path_alias_xt.module in Extended Path Aliases 7
Same filename and directory in other branches
Extended Path Aliases.
Automatically generates and recognises aliases beyond the base path, e.g. generates and accepts "about-us/edit" for "node/123/edit" and "users/rik/track" for "user/7/track". These aliases may be used anywhere where you are prompted to enter page specifications, including wildcards, like "about-us*". Examples of modules and pages that particularly benefit are: o any node page displaying a revision or links to revisions o any node or taxonomy term page with View, Edit, Track etc tabs o the tabs on the "My account" page, Edit, Shortcuts etc. o Statistics on top visited pages etc, e.g., those under the Track tab o page-specific block visibility settings o same for any other module that has an include/exclude pages input box.
File
path_alias_xt.moduleView source
<?php
/**
* @file
* Extended Path Aliases.
*
* Automatically generates and recognises aliases beyond the base path, e.g.
* generates and accepts "about-us/edit" for "node/123/edit" and
* "users/rik/track" for "user/7/track".
* These aliases may be used anywhere where you are prompted to enter page
* specifications, including wildcards, like "about-us*".
* Examples of modules and pages that particularly benefit are:
* o any node page displaying a revision or links to revisions
* o any node or taxonomy term page with View, Edit, Track etc tabs
* o the tabs on the "My account" page, Edit, Shortcuts etc.
* o Statistics on top visited pages etc, e.g., those under the Track tab
* o page-specific block visibility settings
* o same for any other module that has an include/exclude pages input box.
*/
define('PATH_ALIAS_XT_DEFAULT_NODE_OR_USER_MATCH', '{(^node|^user|^taxonomy/term)/([0-9]+)/(.+)}');
/**
* Implements hook_help().
*/
function path_alias_xt_help($path, $arg) {
switch ($path) {
case 'admin/help#path_alias_xt':
$s = t('Installation instructions are in the README.txt file. Further documentation is on the <a href="@path_alias_xt">Extended Path Aliases</a> project page.', array(
'@path_alias_xt' => url('http://drupal.org/project/path_alias_xt'),
));
break;
}
return empty($s) ? '' : '<p>' . $s . '</p>';
}
/**
* Implements hook_menu().
*/
function path_alias_xt_menu() {
$items['admin/config/system/path_alias_xt'] = array(
'title' => 'Extended path aliases',
'description' => 'Advanced settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'path_alias_xt_admin_config',
),
'access arguments' => array(
'administer site configuration',
),
);
return $items;
}
/**
* Menu callback for admin settings.
*/
function path_alias_xt_admin_config() {
$form['path_alias_xt_user_special'] = array(
'#type' => 'checkbox',
'#title' => t('For the current user: instead of <em>/user/uid</em> or its alias, apply the alias for <em>/user</em>.'),
'#default_value' => variable_get('path_alias_xt_user_special', TRUE),
'#description' => t('If ticked and the system path <em>/user</em> has an <a target="alias" href="!alias">alias</a>, such as <em>/MyAccount</em>, then <em>/MyAccount</em> will also be applied when a user visits their <em>/user/uid/...</em> pages.<br/>For this feature to work you must complete the full installation procedure outlined in the <a href="!README">README</a>.', array(
'!alias' => url('/admin/config/search/path'),
'!README' => url(drupal_get_path('module', 'path_alias_xt') . '/README.txt'),
)),
);
$form['path_alias_xt_regex_pattern'] = array(
'#type' => 'textfield',
'#size' => 100,
'#title' => t('Regular expression to match system paths for nodes, users and taxonomy terms'),
'#default_value' => variable_get('path_alias_xt_regex_pattern', PATH_ALIAS_XT_DEFAULT_NODE_OR_USER_MATCH),
'#description' => t("While you can always reset this configuration and recover without permanent damage to your site, a change to this expression may temporarily break all extended aliases. Change only when you know what you're doing."),
);
return system_settings_form($form);
}
/**
* Implements hook_url_inbound_alter().
*/
function path_alias_xt_url_inbound_alter(&$path, $original_path, $path_language) {
// While drupal_get_path_alias() can't be overridden, drupal_get_normal_path()
// does let us augment its behaviour by implementing this hook.
// The system path as calculated and passed to us by drupal_get_normal_path(),
// when no alternative system path was found by that function, we apply our
// algorithm to create an system (aka normal) path.
// @see includes/path.inc
// Remove whatever alterations PURL has done to determine the actual path.
if (module_exists('purl') && purl_inited()) {
$original_path = purl_get_normal_path($path);
}
if (!empty($original_path) && $path == $original_path) {
// drupal_get_normal_path() did not find a system path.
// See [#2162621]. This deals with special UTF characters in paths.
// @todo: make this should be configurable?
$candidate_alias = $original_path;
// This needs to use explode() and not strrpos() / drupal_substr(), because
// strrpos() is not unicode safe, so unicode characters can lead to an
// endless loop.
$parts = explode('/', $candidate_alias);
$path_suffix = array();
while (count($parts) > 0) {
// If the truncated path exists as a menu item (incl. paged views), abort.
// E.g.: we won't replace and extend the user alias 'admin' in this path:
// admin/structure/block/manage/system/navigation/configure, because
// admin/structure/block is in the menu-router table.
if ($menu_item_path = _path_alias_xt_get_menu_item($candidate_alias)) {
return;
}
array_unshift($path_suffix, array_pop($parts));
$candidate_alias = implode('/', $parts);
if ($src = drupal_lookup_path('source', $candidate_alias, $path_language)) {
// If 'user' is aliased to MyAccount, then MyAccount/edit needs to
// transform to 'user/123/edit'.
if ($src == 'user') {
global $user;
$src .= '/' . $user->uid;
}
$path = $src . '/' . implode('/', $path_suffix);
return;
}
}
}
}
/**
* Implements hook_url_outbound_alter().
*/
function path_alias_xt_url_outbound_alter(&$path, array &$options, $original_path) {
// This hook implementation gets called from url($path).
// The path as passed to us by the function url(), which we may turn into
// the aliased path or leave unchanged.
// If $options['alias'] is set to TRUE, the path is assumed already to be
// the correct path alias, and the alias is not looked up.
// @see includes/common.inc::url()
if (!empty($options['alias'])) {
return;
}
if ($path == $original_path) {
// This is always the case unless altered by another module implementing
// this hook.
$pattern = variable_get('path_alias_xt_regex_pattern', PATH_ALIAS_XT_DEFAULT_NODE_OR_USER_MATCH);
if (preg_match($pattern, $path, $matches)) {
if ($alias = drupal_lookup_path('alias', "{$matches[1]}/{$matches[2]}")) {
$path = "{$alias}/{$matches[3]}";
}
}
}
}
/**
* Returns FALSE if the supplied path is NOT in the menu_router table.
*
* @param string $path
* The path.
*
* @return bool
* The supplied path or FALSE if it was not found in the router table
*/
function _path_alias_xt_get_menu_item($path) {
return db_query("SELECT path FROM {menu_router} WHERE path = :path", array(
':path' => $path,
))
->fetchField();
}
/**
* Override the call drupal_get_path_alias()
*
* This is used to override the call drupal_get_path_alias(), which occurs
* for instance in the block.module. There is no hook available for this, so
* this function needs to be invoked via a call inserted in function
* drupal_get_path_alias() or by using the PECL runkit.
* Both options are described in detail in the README file.
*
* @param string $path
* If omitted the current path is used.
* @param string $path_language
* The path language.
*
* @return string
* The alias for $path or $path unchanged if no alias was found.
*
* @todo simplify this code w.r.t 'user' exceptions
*/
function path_alias_xt_get_path_alias($path = NULL, $path_language = NULL) {
if ($path == NULL) {
$path = $_GET['q'];
}
// First test for special case user/%.
global $user;
$user_special = variable_get('path_alias_xt_user_special', TRUE);
if ($user_special && preg_match('{^user/([0-9]+)\\z}', $path, $matches) && $matches[1] == $user->uid) {
// For current user rather than applying 'user/%' alias, return
// 'user' alias, if it exists.
if ($user_alias = drupal_lookup_path('alias', 'user', $path_language)) {
return $user_alias;
}
}
if ($alias = drupal_lookup_path('alias', $path, $path_language)) {
return $alias;
}
$pattern = variable_get('path_alias_xt_regex_pattern', PATH_ALIAS_XT_DEFAULT_NODE_OR_USER_MATCH);
if (preg_match($pattern, $path, $matches)) {
// $matches[0] equals $path, eg 'node/123/edit'
// $matches[1] will equal 'node' or 'user' or 'taxonomy/term'
// $matches[2] will be the node, user or term id, e.g '123'
// $matches[3] is the path extension, e.g., 'edit'.
if ($user_special && $matches[1] == 'user' && $matches[2] == $user->uid) {
// For current user rather than applying 'user/%' alias, apply
// 'user' alias, if it exists.
if ($user_alias = drupal_lookup_path('alias', 'user', $path_language)) {
return "{$user_alias}/{$matches[3]}";
}
}
if ($alias = drupal_lookup_path('alias', "{$matches[1]}/{$matches[2]}", $path_language)) {
return "{$alias}/{$matches[3]}";
}
}
return $path;
}
/**
* Implements hook_init().
*/
function path_alias_xt_init() {
// Purists look away...
// There is no suitable hook to override core's drupal_get_path_alias()
// behaviour. So we either take on the impossible task of rewriting all
// modules that call it, or we redefine its body to make a simple call back to
// this module. We can do this programmatically by taking advantage of the
// PECL runkit extension. The runkit needs to be compiled and placed in the
// /extensions (or /ext) directory pointed to by the extension_dir directive
// in php.ini.
// Dynamically load the runkit. This may not be supported on multi-threaded
// web servers.
// If the statement below produces an error on your system, comment it out and
// make sure that you have "extension=runkit.so" in your php.ini.
// Alternatively, apply the simple edit to includes/path.inc as described in
// the README file.
// Dl('runkit.so');.
if (function_exists('runkit_function_redefine')) {
$args = '$path=NULL, $path_language=NULL';
$body = 'return path_alias_xt_get_path_alias($path, $path_language);';
runkit_function_redefine('drupal_get_path_alias', $args, $body);
}
}
Functions
Name | Description |
---|---|
path_alias_xt_admin_config | Menu callback for admin settings. |
path_alias_xt_get_path_alias | Override the call drupal_get_path_alias() |
path_alias_xt_help | Implements hook_help(). |
path_alias_xt_init | Implements hook_init(). |
path_alias_xt_menu | Implements hook_menu(). |
path_alias_xt_url_inbound_alter | Implements hook_url_inbound_alter(). |
path_alias_xt_url_outbound_alter | Implements hook_url_outbound_alter(). |
_path_alias_xt_get_menu_item | Returns FALSE if the supplied path is NOT in the menu_router table. |
Constants
Name | Description |
---|---|
PATH_ALIAS_XT_DEFAULT_NODE_OR_USER_MATCH | @file Extended Path Aliases. |