'item', '#title' => t('You are not authorized to access the pathauto settings.')); return $form; } $output = ''; // Insist that path.module be enabled if (!module_exist('path')) { $form['error'] = array('#type' => 'item', '#title' => t('The pathauto module is an extension of the path module - you must enable path to use it.', array('%admin-modules' => url('admin/modules')))); return $form; } // Check for any updates _pathauto_update(); // Generate the form - settings applying to all patterns first $group_weight = -20; $form['general'] = array('#type' => 'fieldset', '#weight' => $group_weight, '#title' => t('General settings'), '#collapsible' => TRUE, '#collapsed' => TRUE); $group_weight++; $form['general']['pathauto_verbose'] = array('#type' => 'checkbox', '#title' => t('Verbose'), '#default_value' => variable_get('pathauto_verbose', FALSE), '#description' => t('Display alias changes (except during bulk updates).')); $form['general']['pathauto_separator'] = array('#type' => 'textfield', '#title' => t('Separator'), '#size' => 1, '#maxlength' => 1, '#default_value' => variable_get('pathauto_separator', '_'), '#description' => t('Character used to separate words in titles. This will replace any spaces and punctuation characters.')); $form['general']['pathauto_quotes'] = array('#type' => 'radios', '#title' => t('Quotation marks'), '#default_value' => variable_get('pathauto_quotes', 0), '#options' => array(t('Remove'), t('Replace by separator')), ); $form['general']['pathauto_max_length'] = array('#type' => 'textfield', '#title' => t('Maximum alias length'), '#size' => 3, '#maxlength' => 3, '#default_value' => variable_get('pathauto_max_length', 128), '#description' => t('Maximum text length of aliases to generate. 128 is the maximum permissible.')); $form['general']['pathauto_max_component_length'] = array('#type' => 'textfield', '#title' => t('Maximum component length'), '#size' => 3, '#maxlength' => 3, '#default_value' => variable_get('pathauto_max_component_length', 128), '#description' => t('Maximum text length of any single component in the alias (e.g., [title]). 128 is the maximum permissible.')); $form['general']['pathauto_indexaliases'] = array('#type' => 'checkbox', '#title' => t('Create index aliases'), '#default_value' => variable_get('pathauto_indexaliases', FALSE), '#description' => t('When a pattern generates a hierarchical alias (i.e., any alias containing a slash), generate aliases for each step of the hierarchy which can be used to list content within that hierarchy. For example, if a node alias "music/concert/beethoven" is created, also create an alias "music/concert" which will list all concert nodes, and an alias "music" which will list all music nodes.')); // If requested, do a bulk generation of index aliases $do_index_bulkupdate = variable_get('pathauto_indexaliases_bulkupdate', FALSE); variable_set('pathauto_indexaliases_bulkupdate', FALSE); $form['general']['pathauto_indexaliases_bulkupdate'] = array('#type' => 'checkbox', '#title' => t('Bulk generate index aliases'), '#default_value' => FALSE, '#description' => t('Generate index aliases based on all pre-existing aliases.')); $form['general']['pathauto_update_action'] = array('#type' => 'radios', '#title' => t('Update action'), '#default_value' => variable_get('pathauto_update_action', 0), '#options' => array(t('Do nothing, leaving the old alias intact'), t('Create a new alias in addition to the old alias'), t('Create a new alias, replacing the old one')), '#description' => t('What should pathauto do when updating an existing content item which already has an alias?')); // Call the hook on all modules - an array of 'settings' objects is returned $all_settings = module_invoke_all('pathauto', 'settings'); $modulelist = ''; foreach ($all_settings as $settings) { $items = ''; $module = $settings->module; $modulelist[] = $module; $patterndescr = $settings->patterndescr; $patterndefault = $settings->patterndefault; $groupheader = $settings->groupheader; $supportsfeeds = $settings->supportsfeeds; variable_set('pathauto_'.$module.'_supportsfeeds', $supportsfeeds); $form[$module] = array('#type' => 'fieldset', '#title' => $groupheader, '#weight' => $group_weight, '#collapsible' => TRUE, '#collapsed' => TRUE); $group_weight++; // Prompt for the default pattern for this module $variable = 'pathauto_'.$module.'_pattern'; $form[$module][$variable] = array('#type' => 'textfield', '#title' => $patterndescr, '#default_value' => variable_get($variable,$patterndefault), '#size' => 65, '#maxlength' => 128); // If the module supports a set of specialized patterns, set // them up here if ($settings->patternitems) { foreach ($settings->patternitems as $itemname => $itemlabel) { $variable = 'pathauto_'.$module.'_'.$itemname.'_pattern'; $form[$module][$variable] = array('#type' => 'textfield', '#title' => $itemlabel, '#default_value' => variable_get($variable,''), '#size' => 65, '#maxlength' => 128); } } // Display the user documentation of placeholders supported by // this module, as a description on the last pattern $doc = "
\n"; foreach ($settings->placeholders as $name => $description) { $doc .= '
'.$name.'
'; $doc .= '
'.$description.'
'; } $doc .= "
\n"; $form[$module][$variable]['#description'] = $doc; // If the module supports bulk updates, offer the update action here if ($settings->bulkname) { $variable = 'pathauto_'.$module.'_bulkupdate'; if (variable_get($variable, FALSE)) { variable_set($variable, FALSE); $function = $module.'_pathauto_bulkupdate'; call_user_func($function); } $form[$module][$variable] = array('#type' => 'checkbox', '#title' => $settings->bulkname, '#default_value' => FALSE, '#description' => $settings->bulkdescr); } // Perform bulk updates of indexes for this module, if asked if ($do_index_bulkupdate) { $function = $module.'_pathauto_bulkupdate_indexes'; if (is_callable($function)) { $indexcount += call_user_func($function); } } // If the module supports feeds, offer to generate aliases for them if ($settings->supportsfeeds) { $variable = 'pathauto_'.$module.'_applytofeeds'; $form[$module][$variable] = array('#type' => 'checkbox', '#title' => t('Create feed aliases'), '#default_value' => variable_get($variable, FALSE), '#description' => t('Also generate aliases for RSS feeds.')); } } if ($do_index_bulkupdate) { drupal_set_message(format_plural($indexcount, "Bulk update of index aliases completed, one alias generated.", "Bulk update of index aliases completed, %count aliases generated.")); } // Keep track of which modules currently support pathauto variable_set('pathauto_modulelist', $modulelist); return $form; } // Make sure there isn't already an alias pointing to a different item function _pathauto_alias_exists($alias, $src) { return db_result(db_query( "SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s' AND src != '%s'", $alias, $src)); } // Clean up a string value provided by a module, resulting in a // string containing only alphanumerics and separators function pathauto_cleanstring($string) { static $translations = array( 'À'=>'A','Á'=>'A','Â'=>'A','Ã'=>'A','Ä'=>'A','Å'=>'A','Ā'=>'A','Ą'=>'A','Ă'=>'A', 'à'=>'a','á'=>'a','â'=>'a','ã'=>'a','ä'=>'a','å'=>'a','ā'=>'a','ą'=>'a','ă'=>'a', 'Æ'=>'Ae', 'æ'=>'ae', 'Ç'=>'C','Ć'=>'C','Č'=>'C','Ĉ'=>'C','Ċ'=>'C', 'ç'=>'c','ć'=>'c','č'=>'c','ĉ'=>'c','ċ'=>'c', 'Ď'=>'D','Đ'=>'D','Ð'=>'D', 'ď'=>'d','đ'=>'d','ð'=>'d', 'È'=>'E','É'=>'E','Ê'=>'E','Ë'=>'E','Ē'=>'E','Ę'=>'E','Ě'=>'E','Ĕ'=>'E','Ė'=>'E', 'è'=>'e','é'=>'e','ê'=>'e','ë'=>'e','ē'=>'e','ę'=>'e','ě'=>'e','ĕ'=>'e','ė'=>'e', 'ƒ'=>'f', 'Ĝ'=>'G','Ğ'=>'G','Ġ'=>'G','Ģ'=>'G', 'ĝ'=>'g','ğ'=>'g','ġ'=>'g','ģ'=>'g', 'Ĥ'=>'H','Ħ'=>'H', 'ĥ'=>'h','ħ'=>'h', 'Ì'=>'I','Í'=>'I','Î'=>'I','Ï'=>'I','Ī'=>'I','Ĩ'=>'I','Ĭ'=>'I','Į'=>'I','İ'=>'I', 'ì'=>'i','í'=>'i','î'=>'i','ï'=>'i','ī'=>'i','ĩ'=>'i','ĭ'=>'i','į'=>'i','ı'=>'i', 'IJ'=>'Ij', 'ij'=>'ij', 'Ĵ'=>'J', 'ĵ'=>'j', 'Ķ'=>'K', 'ķ'=>'k','ĸ'=>'k', 'Ł'=>'L','Ľ'=>'L','Ĺ'=>'L','Ļ'=>'L','Ŀ'=>'L', 'ł'=>'l','ľ'=>'l','ĺ'=>'l','ļ'=>'l','ŀ'=>'l', 'Ñ'=>'N','Ń'=>'N','Ň'=>'N','Ņ'=>'N','Ŋ'=>'N', 'ñ'=>'n','ń'=>'n','ň'=>'n','ņ'=>'n','ʼn'=>'n','ŋ'=>'n', 'Ò'=>'O','Ó'=>'O','Ô'=>'O','Õ'=>'O','Ö'=>'O','Ø'=>'O','Ō'=>'O','Ő'=>'O','Ŏ'=>'O', 'ò'=>'o','ó'=>'o','ô'=>'o','õ'=>'o','ö'=>'o','ø'=>'o','ō'=>'o','ő'=>'o','ŏ'=>'o', 'Œ'=>'Oe', 'œ'=>'oe', 'Ŕ'=>'R','Ř'=>'R','Ŗ'=>'R', 'ŕ'=>'r','ř'=>'r','ŗ'=>'r', 'Ś'=>'S','Š'=>'S','Ş'=>'S','Ŝ'=>'S','Ș'=>'S', 'Ť'=>'T','Ţ'=>'T','Ŧ'=>'T','Ț'=>'T','Þ'=>'T', 'þ'=>'t', 'Ù'=>'U','Ú'=>'U','Û'=>'U','Ü'=>'U','Ū'=>'U','Ů'=>'U','Ű'=>'U','Ŭ'=>'U','Ũ'=>'U','Ų'=>'U', 'ú'=>'u','û'=>'u','ü'=>'u','ū'=>'u','ů'=>'u','ű'=>'u','ŭ'=>'u','ũ'=>'u','ų'=>'u', 'Ŵ'=>'W', 'ŵ'=>'w', 'Ý'=>'Y','Ŷ'=>'Y','Ÿ'=>'Y','Y'=>'Y', 'ý'=>'y','ÿ'=>'y','ŷ'=>'y', 'Ź'=>'Z','Ž'=>'Z','Ż'=>'Z', 'ž'=>'z','ż'=>'z','ź'=>'z', 'ß'=>'ss','ſ'=>'ss'); // Replace or drop apostrophes based on user settings $separator = variable_get('pathauto_separator', '_'); $quotes = variable_get('pathauto_quotes', 0); $output = str_replace("'", ($quotes ? $separator : ''), $string); // Convert accented characters to their ASCII counterparts... /* $output = strtr(utf8_decode($output), "\xA1\xAA\xBA\xBF". "\xC0\xC1\xC2\xC3\xC5\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF". "\xD0\xD1\xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD". "\xE0\xE1\xE2\xE3\xE5\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF". "\xF0\xF1\xF2\xF3\xF4\xF5\xF8\xF9\xFA\xFB\xFD\xFF", "!ao?AAAAACEEEEIIIIDNOOOOOUUUYaaaaaceeeeiiiidnooooouuuyy"); // ...and ligatures too $output = utf8_encode(strtr($output, array("\xC4"=>"Ae", "\xC6"=>"AE", "\xD6"=>"Oe", "\xDC"=>"Ue", "\xDE"=>"TH", "\xDF"=>"ss", "\xE4"=>"ae", "\xE6"=>"ae", "\xF6"=>"oe", "\xFC"=>"ue", "\xFE"=>"th")));*/ $output = strtr($output, $translations); // Preserve alphanumerics, everything else becomes a separator $pattern = '/[^a-zA-Z0-9]+/ '; $output = preg_replace($pattern, $separator, $output); // Trim any leading or trailing separators (note the need to // escape the separator if and only if it is not alphanumeric) if ($separator) { if (ctype_alnum($separator)) { $seppattern = $separator; } else { $seppattern = '\\'.$separator; } $output = preg_replace("/^$seppattern+|$seppattern+$/", "", $output); } // Enforce the maximum component length $maxlength = min(variable_get('pathauto_max_component_length', 128), 128); $output = drupal_substr($output, 0, $maxlength); return $output; } /** * Apply patterns to create an alias * * @param $module * The name of your module (e.g., 'node') * @param $op * Operation being performed on the content being aliased ('insert', * 'update', or 'bulkupdate') * @param $placeholders * An array whose keys consist of the translated placeholders * which appear in patterns (e.g., t('[title]')) and values are the * actual values to be substituted into the pattern (e.g., $node->title) * @param $src * The "real" URI of the content to be aliased (e.g., "node/$node->nid") * @param $type * For modules which provided patternitems in hook_autopath(), * the relevant identifier for the specific item to be aliased (e.g., * $node->type) * @return * The alias that was created */ function pathauto_create_alias($module, $op, $placeholders, $src, $type=NULL) { if (($op != 'bulkupdate') and variable_get('pathauto_verbose', FALSE)) { $verbose = TRUE; } else { $verbose = FALSE; } // Retrieve and apply the pattern for this content type $pattern = ''; if ($type) { $pattern = drupal_strtolower(variable_get('pathauto_'.$module.'_'.$type.'_pattern', '')); } if (!trim($pattern)) { $pattern = drupal_strtolower(variable_get('pathauto_'.$module.'_pattern', '')); } // No pattern? Do nothing (otherwise we may blow away existing aliases...) if (!trim($pattern)) { return ''; } // Special handling when updating an item which is already aliased $pid = NULL; if ($op == 'update' or $op == 'bulkupdate') { $result = db_query("SELECT pid,dst FROM {url_alias} WHERE src='%s'", $src); if ($data = db_fetch_object($result)) { // The item is already aliased, check what to do... switch (variable_get('pathauto_update_action', 0)) { // Do nothing case 0: return ''; // Add new alias in addition to old one case 1: $oldalias = $data->dst; break; // Replace old alias - remember the pid to update case 2: $pid = $data->pid; $oldalias = $data->dst; break; default: break; } } } // Replace the placeholders with the values provided by the module, // and lower-case the result $alias = str_replace(array_keys($placeholders), $placeholders, $pattern); $alias = drupal_strtolower($alias); // Two or more slashes should be collapsed into one $alias = preg_replace("/\/+/", "/", $alias); // Trim any leading or trailing slashes $alias = preg_replace("/^\/|\/+$/", "", $alias); $maxlength = min(variable_get('pathauto_max_length', 128), 128); $alias = drupal_substr($alias, 0, $maxlength); // If the alias already exists, generate a new variant $separator = variable_get('pathauto_separator', '_'); if (_pathauto_alias_exists($alias, $src)) { for ($i=0; _pathauto_alias_exists($alias.$separator.$i, $src); $i++) { } // Make room for the sequence number $alias = drupal_substr($alias, 0, $maxlength-1-($i/10+1)); $alias = $alias.$separator.$i; } // If $pid is NULL, a new alias is created - otherwise, the existing // alias for the designated src is replaced _pathauto_set_alias($src, $alias, $pid, $verbose, $oldalias); // Also create a related feed alias if requested, and if supported // by the module if (variable_get('pathauto_'.$module.'_applytofeeds', FALSE)) { $feedappend = variable_get('pathauto_'.$module.'_supportsfeeds', ''); // Handle replace case (get existing pid) _pathauto_set_alias("$src/$feedappend", "$alias/feed", NULL, $verbose); } // Create any relevant index aliases if requested if (variable_get('pathauto_indexaliases', FALSE)) { pathauto_create_index_alias($alias, $module); } return $alias; } /** * Verifies if the given path is a valid menu callback. * Taken from menu_execute_active_handler(). * * @param $path * A string containing a relative path. * * @return * TRUE if the path is already */ function _pathauto_path_is_callback($path) { static $menu = NULL; if (is_null($menu)) { $menu = menu_get_menu(); } // Determine the menu item containing the callback. return isset($menu['callbacks'][$path]); } function _pathauto_set_alias($src, $dst, $pid = NULL, $verbose = FALSE, $oldalias = NULL) { // Alert users that an existing callback cannot be overridden automatically // if (_pathauto_path_is_callback($dst)) { if ($verbose and user_access('create url aliases')) { drupal_set_message("Ignoring alias $dst"); } return; } // Skip replacing the current alias with an identical alias if ($oldalias != $dst) { path_set_alias($src, $dst, $pid, 1, 10); if ($verbose and user_access('create url aliases')) { if ($pid) { drupal_set_message(t('Created new alias %dst for %src, replacing %oldalias', array('%dst' => theme('placeholder', $dst), '%src' => theme('placeholder', $src), '%oldalias' => theme('placeholder', $oldalias)))); } else { drupal_set_message(t('Created new alias %dst for %src', array('%dst' => theme('placeholder', $dst), '%src' => theme('placeholder', $src)))); } } } } function pathauto_create_index_alias($alias, $module) { $count = 0; $components = explode('/', $alias); // Not interested in the trailing component array_pop($components); $alias = ''; foreach ($components as $component) { if ($alias) { $alias .= '/'.$component; } else { $alias .= $component; } $alias_count = db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s'", $alias)); if (!$alias_count) { $src = "pathauto/$module/$alias"; _pathauto_set_alias($src, $alias); $count++; } } return $count; } function pathauto_menu($may_cache) { // Look for any extensions installed in the pathauto directory $path = drupal_get_path('module', 'pathauto'); $files = file_scan_directory($path, '^pathauto_.*\.inc$'); foreach ($files as $filename => $file) { include_once($filename); } $items = array(); $modulelist = variable_get('pathauto_modulelist', array()); if (is_array($modulelist)) { foreach ($modulelist as $module) { $indexfunc = $module.'_pathauto_page'; if (function_exists($indexfunc)) { $items[] = array('path' => 'pathauto/'.$module, 'title' => t('Pathauto'), 'callback' => $indexfunc, 'access' => 1, 'type' => MENU_HIDE); } } } return $items; } /** * Returns the version of this release of the pathauto module. * * @return array An array with keys 'text' and 'build' containing the * text version and build ID of this release, respectively. */ function _pathauto_version() { return array("text" => "2005-9-18", "build" => 5); } // function _pathauto_version /** * Makes updates to saved variables and the database structure. **/ function _pathauto_update() { $installed_version = variable_get('pathauto_version', array('text'=> 'Unknown', 'build' => 1)); $current_version = _pathauto_version(); if ( $installed_version['build'] < $current_version['build']) { // Upgrading from original version - variable names were changed if ( $installed_version['build'] <= 1 ) { // Remove obsolete bulkupdate variables variable_del('pathauto_bulkupdate'); variable_del('pathauto_cat_bulkupdate'); // The original global node and taxonomy patterns got renamed $old_pattern = variable_get('pathauto_pattern', 0); if ($old_pattern != 0) { variable_set('pathauto_node_pattern', $old_pattern); variable_del('pathauto_pattern'); } $old_pattern = variable_get('pathauto_cat_pattern', 0); if ($old_pattern != 0) { variable_set('pathauto_taxonomy_pattern', $old_pattern); variable_del('pathauto_cat_pattern'); } // And the form of the type-specific patterns was changed $query = 'SELECT name,value FROM {variable} '. "WHERE name LIKE '%_pathauto_pattern'"; $result = db_query($query); $var = db_fetch_object($result); while ($var) { $type = drupal_substr($var->name, 0, drupal_strlen($var->name)-drupal_strlen('_pathauto_pattern')); $old_pattern = variable_get($var->name, ''); $new_name = 'pathauto_node_'.$type.'_pattern'; variable_set($new_name, $old_pattern); variable_del($var->name); $var = db_fetch_object($result); } $query = 'SELECT name,value FROM {variable} '. "WHERE name LIKE '%_pathauto_cat_pattern'"; $result = db_query($query); $var = db_fetch_object($result); while ($var) { $type = drupal_substr($var->name, 0, drupal_strlen($var->name)-drupal_strlen('_pathauto_cat_pattern')); $old_pattern = variable_get($var->name, ''); $new_name = 'pathauto_taxonomy_'.$type.'_pattern'; variable_set($new_name, $old_pattern); variable_del($var->name); $var = db_fetch_object($result); } } if ( $installed_version['build'] <= 2 ) { // Change feed support variables from booleans to appended strings $query = 'SELECT name,value FROM {variable} '. "WHERE name LIKE 'pathauto_%_supportsfeeds'"; $result = db_query($query); $var = db_fetch_object($result); while ($var) { $type = drupal_substr($var->name, 9, drupal_strlen($var->name)-23); $old_value = variable_get($var->name, FALSE); if ($old_value) { switch ($type) { case 'blog': case 'node': variable_set($var->name, 'feed'); break; case 'taxonomy': variable_set($var->name, '0/feed'); break; } } else { variable_set($var->name, ''); } $var = db_fetch_object($result); } // Update previously-generated taxonomy aliases to remove the "/0" $query = "SELECT * FROM {url_alias} WHERE src LIKE 'taxonomy/term/%/0'"; $result = db_query($query); $aliasrow = db_fetch_object($result); while ($aliasrow) { $src = drupal_substr($aliasrow->src, 0, -2); _pathauto_set_alias($src, $aliasrow->dst, $aliasrow->pid, FALSE); $aliasrow = db_fetch_object($result); } } if ($installed_version['build'] <= 3) { // Change taxonomy pattern variables to use IDs instead // of names $query = 'SELECT name,value FROM {variable} '. "WHERE name LIKE 'pathauto_taxonomy_%_pattern'"; $result = db_query($query); $var = db_fetch_object($result); while ($var) { $vocabname = drupal_substr($var->name, 18, drupal_strlen($var->name)-26); $query = "SELECT vid FROM {vocabulary} WHERE name='$vocabname'"; $vid = db_result(db_query($query)); variable_set("pathauto_taxonomy_$vid_pattern", $var->value); variable_del($var->name); $var = db_fetch_object($result); } } if ($installed_version['build'] <= 4) { // Make feed generation variables module-specific. $applytofeed = variable_get('pathauto_applytofeed', FALSE); if ($applytofeed) { // Feeds were enabled, enable for each module supporting them $query = 'SELECT name,value FROM {variable} '. "WHERE name LIKE 'pathauto_%_supportsfeeds'"; $result = db_query($query); while ($var = db_fetch_object($result)) { if (variable_get($var->name, FALSE)) { $varname = str_replace('_supportsfeed', '_applytofeed', $var->name); variable_set($varname, TRUE); } } } variable_del('pathauto_applytofeed'); } // Set the current version variable_set('pathauto_version', $current_version); drupal_set_message('Upgraded pathauto from '.$installed_version['build'] . ' to '. $current_version['build']); } } // end function _pathauto_update