'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