' . t('Here you can set up URL redirecting for this site. Any existing or non-existing path within this site can redirect to any internal or external URL.') .'
'; break; case 'admin/build/path-redirect/add': case 'admin/build/path-redirect/edit/%': $output .= '' . t('If you need advanced redirection functionality (i.e. wildcards, etc.), you should be using a webserver rewriting engine rather than this module.') . '
'; break; } return $output; } /** * Implements hook_perm(). */ function path_redirect_perm() { return array( 'administer redirects', ); } /** * Implements hook_menu(). */ function path_redirect_menu() { $items['admin/build/path-redirect'] = array( 'title' => 'URL redirects', 'description' => 'Redirect users from one URL to another.', 'page callback' => 'drupal_get_form', 'page arguments' => array('path_redirect_admin_redirects'), 'access arguments' => array('administer redirects'), 'file' => 'path_redirect.admin.inc', ); $items['admin/build/path-redirect/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/build/path-redirect/add'] = array( 'title' => 'Add redirect', 'description' => 'Add a new URL redirect.', 'page callback' => 'drupal_get_form', 'page arguments' => array('path_redirect_edit_form'), 'access arguments' => array('administer redirects'), 'type' => MENU_LOCAL_TASK, 'file' => 'path_redirect.admin.inc', ); $items['admin/build/path-redirect/edit/%path_redirect'] = array( 'title' => 'Edit redirect', 'description' => 'Edit an existing URL redirect.', 'page callback' => 'drupal_get_form', 'page arguments' => array('path_redirect_edit_form', 4), 'access arguments' => array('administer redirects'), 'type' => MENU_CALLBACK, 'file' => 'path_redirect.admin.inc', ); $items['admin/build/path-redirect/delete/%path_redirect'] = array( 'title' => 'Delete redirect', 'description' => 'Delete an existing URL redirect.', 'page callback' => 'drupal_get_form', 'page arguments' => array('path_redirect_delete_form', 4), 'access arguments' => array('administer redirects'), 'type' => MENU_CALLBACK, 'file' => 'path_redirect.admin.inc', ); $items['admin/build/path-redirect/settings'] = array( 'title' => 'Settings', 'description' => 'Configure behavior for URL redirects.', 'page callback' => 'drupal_get_form', 'page arguments' => array('path_redirect_settings_form'), 'access arguments' => array('administer redirects'), 'type' => MENU_LOCAL_TASK, 'weight' => 10, 'file' => 'path_redirect.admin.inc', ); $items['js/path_redirect/autocomplete_404'] = array( 'page callback' => 'path_redirect_js_autocomplete_404', 'access arguments' => array('administer redirects'), 'type' => MENU_CALLBACK, 'file' => 'path_redirect.admin.inc', ); return $items; } /** * Implements hook_init(). */ function path_redirect_init() { if (defined('MAINTENANCE_MODE')) { return; } path_redirect_goto(); } function path_redirect_goto($redirect = NULL) { if (!isset($redirect)) { $path = path_redirect_get_path(); $language = $GLOBALS['language']->language; $query = path_redirect_get_query(); $redirect = path_redirect_load_by_source($path, $language, $query); } elseif (is_numeric($redirect)) { $redirect = path_redirect_load($redirect); } if ($redirect) { // Create the absolute redirection URL. $redirect['redirect_url'] = url($redirect['redirect'], array('query' => $redirect['query'], 'fragment' => $redirect['fragment'], 'absolute' => TRUE)); // Update the last used timestamp so that unused redirects can be purged. db_query("UPDATE {path_redirect} SET last_used = %d WHERE rid = %d", time(), $redirect['rid']); if (url($redirect['redirect']) == url($_GET['q'])) { // Prevent infinite loop redirection. watchdog('path_redirect', 'Redirect to%redirect
is causing an infinite loop; redirect cancelled.', array('%redirect' => $redirect['redirect_url']), WATCHDOG_WARNING, l(t('Edit'), 'admin/build/path-redirect/edit/'. $redirect['rid']));
}
elseif (variable_get('path_redirect_allow_bypass', 0) && isset($_GET['redirect']) && $_GET['redirect'] === 'no') {
// If the user has requested not to be redirected, show a message.
drupal_set_message(t('This page has been moved to @redirect.', array('@redirect' => $redirect['redirect_url'])));
}
elseif (variable_get('path_redirect_redirect_warning', 0)) {
// Show a message and automatically redirect after 10 seconds.
drupal_set_message(t('This page has been moved to @redirect. You will be automatically redirected in 10 seconds.', array('@redirect' => $redirect['redirect_url'])), 'error');
drupal_set_html_head('');
}
else {
// Perform the redirect.
unset($_REQUEST['destination']);
drupal_goto($redirect['redirect_url'], NULL, NULL, $redirect['type']);
}
}
// If this is being executed as a menu item, return a not found flag.
return MENU_NOT_FOUND;
}
/**
* Implements hook_cron().
*/
function path_redirect_cron() {
// Purge inactive redirects from the database.
if ($purge = variable_get('path_redirect_purge_inactive', 0)) {
$query = db_query("SELECT rid FROM {path_redirect} WHERE last_used < %d", time() - $purge);
$rids = array();
while ($rid = db_result($query)) {
$rids[] = $rid;
}
if ($rids) {
path_redirect_delete_multiple($rids);
watchdog('path_redirect', format_plural(count($rids), 'Removed 1 inactive redirect from the database.', 'Removed @count inactive redirects from the database.'));
}
}
}
/**
* Implements hook_path_redirect_operations().
*/
function path_redirect_path_redirect_operations() {
$operations = array(
'delete' => array(
'action' => t('Delete'),
'action_past' => t('Deleted'),
'callback' => 'path_redirect_delete_multiple',
'confirm' => TRUE,
),
);
return $operations;
}
/**
* Implements hook_content_extra_fields().
*
* This is so that the URL redirect fieldset is sortable on the node edit form.
*/
function path_redirect_content_extra_fields() {
$extras['path_redirect'] = array(
'label' => t('URL redirects'),
'description' => t('Path redirect module listing'),
'weight' => 30,
);
return $extras;
}
/**
* Implements hook_form_alter().
*
* Add a summary of redirects pointing to a node on its edit form.
*/
function path_redirect_form_alter(&$form, $form_state, $form_id) {
if (substr($form_id, -10) == '_node_form' && !empty($form['#node']->nid)) {
$redirect = array(
'redirect' => 'node/' . $form['#node']->nid,
'language' => $form['#node']->language,
);
$form['path_redirect'] = array(
'#type' => 'fieldset',
'#title' => t('URL redirects'),
'#description' => t('The following are a list of URL redirects that point to this location.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#access' => user_access('administer redirects'),
'#weight' => 30,
'#group' => 'additional_settings',
'#attached' => array(
'js' => array(
'vertical-tabs' => drupal_get_path('module', 'path_redirect') . '/path_redirect.js',
),
),
);
module_load_include('inc', 'path_redirect', 'path_redirect.admin');
$form['path_redirect']['table'] = path_redirect_list_redirects(array(), array('redirect' => 'node/' . $form['#node']->nid));
$form['path_redirect']['add'] = array(
'#value' => path_redirect_local_actions($redirect),
);
}
}
function path_redirect_local_actions($redirect = array()) {
$links = array(
'add' => array(
'title' => $redirect ? t('Add redirect to this location') : t('Add redirect'),
'href' => 'admin/build/path-redirect/add',
'query' => drupal_get_destination() . ($redirect ? '&' . drupal_query_string_encode($redirect) : ''),
),
);
return theme('links', $links, array('class' => 'item-list action-links'));
}
/**
* Implements hook_nodeapi().
*/
function path_redirect_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
switch ($op) {
case 'presave':
if (!empty($node->nid) && !empty($node->path)) {
path_redirect_check_alias_changed('node/' . $node->nid, $node->path, $node->language);
}
break;
case 'delete':
// When a node is deleted, also delete the redirects to it (they will result in a 404).
path_redirect_delete_multiple(NULL, array('source' => 'node/'. $node->nid));
path_redirect_delete_multiple(NULL, array('redirect' => 'node/'. $node->nid));
break;
}
}
/**
* Creates a redirect if an URL alias is being changed.
*
* @param $path
* The base (normal) path.
* @param $new_alias
* The new alias for the path.
* @param $language
* The language of the alias being created.
* @return
* TRUE if a redirect was created, or FALSE otherwise.
*/
function path_redirect_check_alias_changed($path, $new_alias, $language = '') {
if (!variable_get('path_redirect_auto_redirect', 1) || empty($new_alias)) {
return FALSE;
}
$old_alias = drupal_get_path_alias($path, $language);
if ($old_alias != $path && $old_alias != $new_alias) {
// If the user is manually changing the path alias, add a redirect from the old alias to the node.
$redirect = array(
'source' => $old_alias,
'redirect' => $path,
//'language' => $language,
);
path_redirect_save($redirect);
return $redirect;
}
}
/**
* Implements hook_taxonomy().
*/
function path_redirect_taxonomy($op, $type, $array = NULL) {
if ($op == 'delete' && $type == 'term') {
// Delete any redirects to valid taxonomy paths.
$term = (object) $array;
$term_uri = taxonomy_term_path($term);
path_redirect_delete_multiple(NULL, array('source' => $term_uri));
path_redirect_delete_multiple(NULL, array('redirect' => $term_uri));
if ($term_uri != "taxonomy/term/{$term->tid}") {
path_redirect_delete_multiple(NULL, array('source' => "taxonomy/term/{$term->tid}"));
path_redirect_delete_multiple(NULL, array('redirect' => "taxonomy/term/{$term->tid}"));
}
}
}
/**
* Implements hook_user().
*/
function path_redirect_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'delete') {
// Delete any redirects to valid user paths.
path_redirect_delete_multiple(NULL, array('source' => 'user/'. $account->uid));
path_redirect_delete_multiple(NULL, array('redirect' => 'user/'. $account->uid));
}
}
/**
* Save an URL redirect to the database.
*/
function path_redirect_save(&$redirect) {
// Merge default values.
$redirect += array(
'rid' => NULL,
'query' => '',
'fragment' => '',
'language' => '',
'type' => variable_get('path_redirect_default_status', 301),
'last_used' => time(),
);
// Convert query arrays into a saveable query string.
if (isset($redirect['source_query']) && is_array($redirect['source_query'])) {
$redirect['source'] .= '?' . drupal_query_string_encode($redirect['source_query']);
}
if (is_array($redirect['query'])) {
$redirect['query'] = drupal_query_string_encode($redirect['query']);
}
// Allow spaces in "from" path
// @todo Move to validation?
$redirect['source'] = str_replace('+', ' ', $redirect['source']);
// Remove beginning and trailing slashes from path.
// @todo Move to validation?
$redirect['source'] = trim($redirect['source'], '\/?');
path_redirect_clear_cache();
if (empty($redirect['rid'])) {
drupal_write_record('path_redirect', $redirect);
}
else {
drupal_write_record('path_redirect', $redirect, array('rid'));
}
return $redirect;
}
/**
* Load a redirect by ID.
*
* @param $rid
* An integer with the redirect ID.
*/
function path_redirect_load($rid) {
$redirect = path_redirect_load_multiple(array($rid));
return $redirect ? reset($redirect) : FALSE;
}
/**
* Load a redirect by incoming path and language.
*
* @param $source
* The incoming path.
* @param $language
* An optional language code. If not provided this will examine all language-
* neutral redirects.
* @param $query
* An optional query string to match.
*/
function path_redirect_load_by_source($source, $language = '', $query = array()) {
$where = $query ? "(source = '%s' OR source LIKE '%s%%')" : "source = '%s'";
$args = $query ? array($source, $source . '?') : array($source);
$args[] = $language;
$rid_query = db_query("SELECT rid FROM {path_redirect} WHERE $where AND language IN ('%s', '') ORDER BY language DESC, source DESC, rid DESC", $args);
$rids = array();
while ($rid = db_result($rid_query)) {
$rids[] = $rid;
}
if ($rids && $redirects = path_redirect_load_multiple($rids)) {
// Narrow down the list of candidates.
foreach ($redirects as $rid => $redirect) {
if (!empty($redirect['source_query'])) {
if (empty($query) || !path_redirect_compare_array($redirect['source_query'], $query)) {
unset($redirects[$rid]);
continue;
}
}
// Add a case sensitive matches condition to be used in sorting.
if ($source !== $redirect['source']) {
$redirects[$rid]['weight'] = 1;
}
}
if (!empty($redirects)) {
// Sort the redirects in the proper order.
uasort($redirects, '_path_redirect_uasort');
// Allow other modules to alter the redirect candidates before selecting the top one.
$context = array('language' => $language, 'query' => $query);
drupal_alter('path_redirect_load_by_source', $redirects, $source, $context);
return !empty($redirects) ? reset($redirects) : FALSE;
}
}
return FALSE;
}
function path_redirect_load_multiple($rids = NULL, $conditions = array()) {
if (isset($rids) && empty($rids)) {
return array();
}
$query = array();
_path_redirect_build_conditions($query, $rids, $conditions);
$sql = "SELECT * FROM {path_redirect} WHERE " . implode(' AND ', $query['conditions']);
$query = db_query($sql, $query['args']);
$redirects = array();
while ($redirect = db_fetch_array($query)) {
// Unpack query strings into arrays.
if (!isset($redirect['source_query']) || !is_array($redirect['source_query'])) {
$parsed = parse_url($redirect['source']) + array('query' => '');
$redirect['source_query'] = path_redirect_get_query_array($parsed['query']);
$redirect['source'] = $parsed['path'];
}
$redirect['query'] = path_redirect_get_query_array($redirect['query']);
$redirects[$redirect['rid']] = $redirect;
}
return $redirects;
}
/**
* uasort callback; Compare redirects based on language neutrality and rids.
*/
function _path_redirect_uasort($a, $b) {
$a_weight = isset($a['weight']) ? $a['weight'] : 0;
$b_weight = isset($b['weight']) ? $b['weight'] : 0;
if ($a_weight != $b_weight) {
// First sort by weight (case sensitivity).
return $a_weight > $b_weight;
}
elseif ($a['language'] != $b['language']) {
// Then sort by language specific over language neutral.
return $a['language'] == '';
}
elseif (empty($a['source_query']) != empty($b['source_query'])) {
// Then sort by redirects that do not have query strings over ones that do.
return empty($a['source_query']);
}
else {
// Lastly sort by the highest redirect ID.
return $a['rid'] < $b['rid'];
}
}
function _path_redirect_build_conditions(&$query, $rids, $conditions) {
static $schema;
if (!isset($schema)) {
$schema = !defined('MAINTENANCE_MODE') ? drupal_get_schema('path_redirect') : drupal_get_schema_unprocessed('path_redirect', 'path_redirect');
}
$query += array(
'conditions' => array(),
'args' => array(),
);
if ($rids) {
$conditions += array('rid' => array());
$conditions['rid'] = array_merge($rids, (array) $conditions['rid']);
}
if ($conditions) {
foreach ($conditions as $field => $value) {
if (!is_string($field) || !isset($schema['fields'][$field])) {
continue;
}
//if ($field == 'langauge' && !is_array($value)) {
// $value = array($value, '');
//}
$type = $schema['fields'][$field]['type'];
if (is_array($value)) {
$conditions[$field] = "$field IN (" . db_placeholders($value, $type) . ')';
$query['args'] = array_merge($query['args'], $value);
}
else {
$conditions[$field] = "$field = " . db_type_placeholder($type);
$query['args'][] = $value;
}
}
}
$query['conditions'] = array_merge($query['conditions'], $conditions);
return $query;
}
function path_redirect_clear_cache() {
cache_clear_all(NULL, 'cache_page');
}
/**
* Delete a redirect.
*
* @param $rid
* The ID of the redirect to delete.
*/
function path_redirect_delete($rid, $deprecated = FALSE) {
// @todo Remove legacy path_redirect_delete support for pathauto.
if (is_string($rid) && is_string($deprecated)) {
return path_redirect_delete_multiple(NULL, array('source' => $rid, 'redirect' => $deprecated));
}
elseif (is_array($rid) && !isset($rid['rid'])) {
$rid = $rid['rid'];
}
return path_redirect_delete_multiple(array($rid));
}
/**
* Delete multiple redirects.
*
* @param $rids
* An optional array or redirect IDs.
* @param $conditions
* An optional array of conditions keyed by field to match.
* @return
* The number of deleted redirects.
*/
function path_redirect_delete_multiple($rids = NULL, $conditions = array()) {
$query = array();
_path_redirect_build_conditions($query, $rids, $conditions);
$sql = 'DELETE FROM {path_redirect} WHERE ' . implode(' AND ', $query['conditions']);
db_query($sql, $query['args']);
$deleted = db_affected_rows();
path_redirect_clear_cache();
return $deleted;
}
function path_redirect_get_path($path = NULL) {
if (!isset($path)) {
if (drupal_is_front_page()) {
$path = '