$details) { $action = variable_get("pathauto_punctuation_". $name, 0); // 2 is the action for "do nothing" with the punctuation if ($action != 2) { // Slightly tricky inline if which either replaces with the separator or nothing $output = str_replace($details['value'], ($action ? $separator : ''), $output); } } // If something is already urlsafe then don't remove slashes if ($clean_slash) { $output = str_replace('/', '', $output); } // Optionally remove accents and transliterate to a subset of ASCII-96 characters. if (variable_get('pathauto_transliterate', FALSE)) { static $i18n_loaded = false; static $translations = array(); if (!$i18n_loaded) { $path = drupal_get_path('module', 'pathauto'); if (is_file($path. '/i18n-ascii.txt')) { $translations = parse_ini_file($path. '/i18n-ascii.txt'); } $i18n_loaded = true; } $output = strtr($output, $translations); } // Reduce to the subset of ASCII128 letters and numbers if (variable_get('pathauto_reduce_ascii', FALSE)) { $pattern = '/[^a-zA-Z0-9\/]+/ '; $output = preg_replace($pattern, $separator, $output); } // Get rid of words that are on the ignore list $ignore_re = "\b". preg_replace("/,/", "\b|\b", variable_get('pathauto_ignore_words', $ignore_words)) ."\b"; $output = preg_replace("/$ignore_re/ie", "", $output); // Always replace whitespace with the separator. $output = preg_replace("/\s+/", $separator, $output); // In preparation for pattern matching, // escape the separator if and only if it is not alphanumeric) if (isset($separator)) { if (preg_match('/^[^'. PREG_CLASS_ALNUM .']+$/uD',$separator)) { $seppattern = $separator; } else { $seppattern = '\\'. $separator; } // Trim any leading or trailing separators (note the need to $output = preg_replace("/^$seppattern+|$seppattern+$/", "", $output); // Replace multiple separators with a single one $output = preg_replace("/$seppattern+/", "$separator", $output); } // Enforce the maximum component length $maxlength = min(variable_get('pathauto_max_component_length', 100), 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_pathauto(), * 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; $old_alias = 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', 2)) { // Do nothing case 0: return ''; // Add new alias in addition to old one case 1: $old_alias = $data->dst; break; // Replace old alias - remember the pid to update case 2: case 3: $pid = $data->pid; $old_alias = $data->dst; break; default: break; } } } // Replace the placeholders with the values provided by the module, // and optionally lower-case the result $alias = str_replace($placeholders['tokens'], $placeholders['values'], $pattern); if (variable_get('pathauto_case',1)) { $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', 100), 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(drupal_substr($alias, 0, $maxlength - strlen($i)) . $separator . $i, $src); $i++) { } // Make room for the sequence number $alias = drupal_substr($alias, 0, $maxlength - strlen($i)); $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, $old_alias); // 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', ''); // If we're in a forum, the src doesn't form the base of the rss feed // TODO refactor this out if ($module == 'forum') { $pieces = explode('/', $src); $tid = $pieces[1]; _pathauto_set_alias("taxonomy/term/$tid/$feedappend", "$alias/feed", NULL, $verbose); } else { _pathauto_set_alias("$src/$feedappend", "$alias/feed", NULL, $verbose); } } 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 already exists. */ 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]); } /** * Private function for pathauto to create an alias * * @param $src * The internal path * @param $dst * The visible externally used path. * @param $pid * If the item is currently aliased, the pid for that item. * @param $verbose * If the admin has enabled verbose, should be TRUE. Else FALSE or NULL. * @param $old_alias * If the item is currently aliased, the existing alias for that item. * */ function _pathauto_set_alias($src, $dst, $pid = NULL, $verbose = FALSE, $old_alias = 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 ($old_alias != $dst) { path_set_alias($src, $dst, $pid, 1, 10); if (variable_get('pathauto_update_action', 2) == 3 && function_exists('path_redirect_save')) { if (isset($old_alias)) { $save['path'] = $old_alias; $save['redirect'] = $src; $save['type'] = 301; //moved permanently path_redirect_save($save); $redirect = TRUE; } } if ($verbose and user_access('create url aliases')) { if ($redirect) { drupal_set_message(t('Created new alias %dst for %src, replacing %old_alias. %old_alias now redirects to %dst', array('%dst' => $dst, '%src' => $src, '%old_alias' => $old_alias))); } elseif ($pid) { drupal_set_message(t('Created new alias %dst for %src, replacing %old_alias', array('%dst' => $dst, '%src' => $src, '%old_alias' => $old_alias))); } else { drupal_set_message(t('Created new alias %dst for %src', array('%dst' => $dst, '%src' => $src))); } } } } /** * Generalized function to get tokens across all Pathauto types * * @param $object * A user, node, or category object. * * @return * tokens for that object formatted in the way that Pathauto expects to see them. * */ function pathauto_get_placeholders($type, $object) { $full = token_get_values($type, $object, TRUE); $tokens = token_prepare_tokens($full->tokens); $values = pathauto_clean_token_values($full); return array('tokens' => $tokens, 'values' => $values); } /** * Cleans tokens so they are URL friendly * * @param $values * An array of token values that need to be "cleaned" for use in the URL * */ function pathauto_clean_token_values($full) { foreach ($full->values as $key => $value) { // If it's a "path" or "url friendly" token don't remove the "/" character if (substr($full->tokens[$key], -4, 4) === 'path' || substr($full->tokens[$key], -8, 8) === 'path-raw') { $full->values[$key] = pathauto_cleanstring($value, FALSE); } else { $full->values[$key] = pathauto_cleanstring($value); } } return $full->values; } /** * Returns an array of arrays for punctuation values keyed by a name * including the value and a textual description * Can and should be expanded to include "all" non text punctuation values * */ function pathauto_punctuation_chars() { $punctuation = array(); // Handle " ' , . - _ : ; | { { } ] + = * & % $ £ # @ ! ~ ( ) ? < > \ ¿ ¡ $punctuation['double_quotes'] = array('value' => '"', 'name' => t('Double quotes "')); $punctuation['quotes'] = array('value' => "'", 'name' => t("Single quotes (apostrophe) '")); $punctuation['backtick'] = array('value' => "`", 'name' => t("Back tick `")); $punctuation['comma'] = array('value' => ",", 'name' => t('Comma ,')); $punctuation['period'] = array('value' => ".", 'name' => t('Period .')); $punctuation['hyphen'] = array('value' => "-", 'name' => t('Hyphen -')); $punctuation['underscore'] = array('value' => "_", 'name' => t('Underscore _')); $punctuation['colon'] = array('value' => ":", 'name' => t('Colon :')); $punctuation['semicolon'] = array('value' => ";", 'name' => t('Semicolon ;')); $punctuation['pipe'] = array('value' => "|", 'name' => t('Pipe |')); $punctuation['left_curly'] = array('value' => "{", 'name' => t('Left curly bracket {')); $punctuation['left_square'] = array('value' => "[", 'name' => t('Left square bracket [')); $punctuation['right_curly'] = array('value' => "}", 'name' => t('Right curly bracket }')); $punctuation['right_square'] = array('value' => "]", 'name' => t('Right square bracket ]')); $punctuation['plus'] = array('value' => "+", 'name' => t('Plus +')); $punctuation['equal'] = array('value' => "=", 'name' => t('Equal =')); $punctuation['asterisk'] = array('value' => "*", 'name' => t('Asterisk *')); $punctuation['ampersand'] = array('value' => "&", 'name' => t('Ampersand &')); $punctuation['percent'] = array('value' => "%", 'name' => t('Percent %')); $punctuation['caret'] = array('value' => "^", 'name' => t('Caret ^')); $punctuation['dollar'] = array('value' => "$", 'name' => t('Dollar $')); $punctuation['hash'] = array('value' => "#", 'name' => t('Hash #')); $punctuation['at'] = array('value' => "@", 'name' => t('At @')); $punctuation['exclamation'] = array('value' => "!", 'name' => t('Exclamation !')); $punctuation['tilde'] = array('value' => "~", 'name' => t('Tilde ~')); $punctuation['left_parenthesis'] = array('value' => "(", 'name' => t('Left parenthesis (')); $punctuation['right_parenthesis'] = array('value' => ")", 'name' => t('right parenthesis )')); $punctuation['question_mark'] = array('value' => "?", 'name' => t('Question mark ?')); $punctuation['less_than'] = array('value' => "<", 'name' => t('Less than <')); $punctuation['greater_than'] = array('value' => ">", 'name' => t('Greater than >')); $punctuation['back_slash'] = array('value' => '\\', 'name' => t('Back slash \\')); return $punctuation; }