'item', '#title' => t('You are not authorized to access the pathauto settings.')); return system_settings_form($form); } $output = ''; // 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 system_settings_form($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) { // 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); $url = $string; // substitutes anything but letters, numbers and '_' with separator $url = preg_replace('~[^\\pL0-9_]+~u', $separator, $url); // TRANSLIT now does the whole transliteration work for us $url = iconv("utf-8", "us-ascii//TRANSLIT", $url); $url = drupal_strtolower($url); // keep only letters, numbers, '_' and separator $url = preg_replace('~[^-a-z0-9_]+~', '', $url); // trim off any leading or trailing separators or underscores $url = trim($url, $separator); // Enforce the maximum component length //we use 128 as max length because that's all that url_alias db table can store $maxlength = min(variable_get('pathauto_max_component_length', 128), 128); $url = drupal_substr($url, 0, $maxlength); return $url; } /** * 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(t('Ignoring alias '). $dst .t(' due to existing path conflict')); } 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' => $dst, '%src' => $src, '%oldalias' => $oldalias))); } else { drupal_set_message(t('Created new alias %dst for %src', array('%dst' => $dst, '%src' => $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(); if ($may_cache) { $items[] = array( 'path' => 'admin/settings/pathauto', 'title' => t('pathauto'), 'description' => t('Configure how pathauto generates clean URLs for your content.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('pathauto_admin_settings'), 'access' => user_access('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); } $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_CALLBACK); } } } 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