summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.15/sites/all/modules/redirect/redirect.module
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.15/sites/all/modules/redirect/redirect.module')
-rw-r--r--kolab.org/www/drupal-7.15/sites/all/modules/redirect/redirect.module1489
1 files changed, 1489 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.15/sites/all/modules/redirect/redirect.module b/kolab.org/www/drupal-7.15/sites/all/modules/redirect/redirect.module
new file mode 100644
index 0000000..b14709b
--- /dev/null
+++ b/kolab.org/www/drupal-7.15/sites/all/modules/redirect/redirect.module
@@ -0,0 +1,1489 @@
+<?php
+
+/**
+ * @defgroup redirect_api Redirection API
+ * @{
+ * Functions related to URL redirects.
+ *
+ * @} End of "defgroup redirect_api".
+ */
+
+/**
+ * Modules should return this value from hook_redirect_access() to allow access
+ * to a redirect.
+ */
+define('REDIRECT_ACCESS_ALLOW', 'allow');
+
+/**
+ * Modules should return this value from hook_redirect_access() to deny access
+ * to a redirect.
+ */
+define('REDIRECT_ACCESS_DENY', 'deny');
+
+/**
+ * Modules should return this value from hook_redirect_access() to not affect
+ * redirect access.
+ */
+define('REDIRECT_ACCESS_IGNORE', NULL);
+
+/**
+ * Implements hook_entity_info().
+ */
+function redirect_entity_info() {
+ $info['redirect'] = array(
+ 'label' => t('Redirect'),
+ 'base table' => 'redirect',
+ 'controller class' => 'RedirectController',
+ 'entity keys' => array(
+ 'id' => 'rid',
+ 'bundle' => 'type',
+ ),
+ 'fieldable' => FALSE,
+ 'uuid' => FALSE,
+ );
+
+ return $info;
+}
+
+/**
+ * Controller class for redirects.
+ *
+ * This extends the DrupalDefaultEntityController class, adding required
+ * special handling for redirect objects.
+ */
+class RedirectController extends DrupalDefaultEntityController {
+
+ protected function attachLoad(&$redirects, $revision_id = FALSE) {
+ // Unserialize the URL option fields.
+ foreach ($redirects as $key => $redirect) {
+ $redirects[$key]->source_options = unserialize($redirect->source_options);
+ $redirects[$key]->redirect_options = unserialize($redirect->redirect_options);
+ }
+ parent::attachLoad($redirects, $revision_id);
+ }
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function redirect_hook_info() {
+ $hooks = array(
+ 'redirect_load',
+ 'redirect_load_by_source_alter',
+ 'redirect_access',
+ 'redirect_prepare',
+ 'redirect_validate',
+ 'redirect_presave',
+ 'redirect_insert',
+ 'redirect_update',
+ 'redirect_delete',
+ 'redirect_alter',
+ );
+
+ $hooks = array_combine($hooks, $hooks);
+ foreach ($hooks as $hook => $info) {
+ $hooks[$hook] = array('group' => 'redirect');
+ }
+ return $hooks;
+}
+
+/**
+ * Implements hook_permission().
+ */
+function redirect_permission() {
+ $permissions['administer redirects'] = array(
+ 'title' => t('Administer URL redirections'),
+ );
+ return $permissions;
+}
+
+/**
+ * Implements hook_help().
+ */
+function redirect_help($path, $arg) {
+ $output = '';
+ switch ($path) {
+ case 'admin/config/search/redirect/404':
+ $output = '<p>' . t('This page lists all paths that have resulted in 404 errors and do not yet have any redirects assigned to them.') . '</p>';
+ break;
+ case 'admin/reports/page-not-found':
+ break;
+ }
+ return $output;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function redirect_menu() {
+ $items['admin/config/search/redirect'] = array(
+ 'title' => 'URL redirects',
+ 'description' => 'Redirect users from one URL to another.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('redirect_list_form'),
+ 'access arguments' => array('administer redirects'),
+ 'file' => 'redirect.admin.inc',
+ );
+ $items['admin/config/search/redirect/list'] = array(
+ 'title' => 'List',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ );
+ $items['admin/config/search/redirect/add'] = array(
+ 'title' => 'Add redirect',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('redirect_edit_form'),
+ 'access callback' => 'redirect_access',
+ 'access arguments' => array('create', 'redirect'),
+ 'file' => 'redirect.admin.inc',
+ 'type' => MENU_LOCAL_ACTION,
+ );
+ $items['admin/config/search/redirect/edit/%redirect'] = array(
+ 'title' => 'Edit redirect',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('redirect_edit_form', 5),
+ 'access callback' => 'redirect_access',
+ 'access arguments' => array('update', 5),
+ 'file' => 'redirect.admin.inc',
+ );
+ $items['admin/config/search/redirect/delete/%redirect'] = array(
+ 'title' => 'Delete redirect',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('redirect_delete_form', 5),
+ 'access callback' => 'redirect_access',
+ 'access arguments' => array('delete', 5),
+ 'file' => 'redirect.admin.inc',
+ );
+ $items['admin/config/search/redirect/settings'] = array(
+ 'title' => 'Settings',
+ 'description' => 'Configure behavior for URL redirects.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('redirect_settings_form'),
+ 'access arguments' => array('administer redirects'),
+ 'file' => 'redirect.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 50,
+ );
+
+ // If the database logging module is enabled, add special 404 listing pages.
+ if (module_exists('dblog')) {
+ $items['admin/config/search/redirect/404'] = array(
+ 'title' => 'Fix 404 pages',
+ 'description' => 'Add redirects for 404 pages.',
+ 'page callback' => 'redirect_404_list',
+ 'access arguments' => array('administer redirects'),
+ 'file' => 'redirect.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 20,
+ );
+ $items['admin/reports/page-not-found/redirect'] = array(
+ 'title' => 'Fix 404 pages with URL redirects',
+ 'page callback' => 'drupal_goto',
+ 'page arguments' => array('admin/config/search/redirect/404'),
+ 'access arguments' => array('administer redirects'),
+ 'type' => MENU_LOCAL_ACTION,
+ );
+ }
+
+ // Devel generate integration.
+ if (module_exists('devel_generate')) {
+ $items['admin/config/development/generate/redirects'] = array(
+ 'title' => 'Generate redirects',
+ 'description' => 'Generate a given number of redirects. Optionally delete current redirects.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('redirect_generate_form'),
+ 'access arguments' => array('administer redirects'),
+ 'file' => 'redirect.generate.inc',
+ );
+ $items['admin/config/search/redirect/generate'] = $items['admin/config/development/generate/redirects'];
+ $items['admin/config/search/redirect/generate']['type'] = MENU_LOCAL_ACTION;
+ }
+
+ return $items;
+}
+
+/**
+ * Implements hook_url_inbound_alter().
+ */
+function redirect_url_inbound_alter(&$path, $original_path, $path_language) {
+ // Redirect to canonical URLs.
+ if ($path && variable_get('redirect_canonical', 1)) {
+ $alias = drupal_get_path_alias($path, $path_language);
+ if ($alias != $path && $alias != $original_path) {
+ //return redirect_redirect(array('redirect' => $alias, 'type' => 'global'));
+ }
+
+ // Redirect from default entity paths to the proper entity path.
+ if ($path_entity = redirect_load_entity_from_path($path)) {
+ if ($uri = entity_uri($path_entity['entity_type'], $path_entity['entity'])) {
+ if ($path != $uri['path']) {
+ //return redirect_redirect(array('redirect' => $uri['path'], 'redirect_options' => $uri['options'], 'type' => 'global'));
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function redirect_entity_info_alter(&$info) {
+ $default_paths = array(
+ 'node' => 'node/%node',
+ 'user' => 'user/%user',
+ 'taxonomy_term' => 'taxonomy/term/%taxonomy_term',
+ );
+
+ foreach ($default_paths as $entity_type => $default_path) {
+ if (isset($info[$entity_type]) && !isset($info[$entity_type]['default path'])) {
+ $info[$entity_type]['default path'] = $default_path;
+ }
+ }
+}
+
+/**
+ * Implements hook_init().
+ */
+function redirect_init() {
+ if (!redirect_can_redirect()) {
+ return;
+ }
+
+ $current_path = current_path();
+ $current_langcode = $GLOBALS['language']->language;
+ $current_query = drupal_get_query_parameters();
+ if ($redirect = redirect_load_by_source($current_path, $current_langcode, $current_query)) {
+ redirect_redirect($redirect);
+ }
+
+ $redirect_global = FALSE;
+ $request_uri = $original_uri = ltrim(request_uri(), '/');
+
+ // Redirect from non-clean URLs to clean URLs.
+ if (variable_get('redirect_global_clean', 1) && variable_get('clean_url', 0) && strpos($request_uri, '?q=') !== FALSE) {
+ //$redirect_global = TRUE;
+ //$request_uri = str_replace('?q=', '', $request_uri);
+ }
+
+ if (strpos($request_uri, 'index.php') !== FALSE) {
+ //$redirect_global = TRUE;
+ //$request_uri = str_replace('index.php', '', $request_uri);
+ }
+
+ //$request_uri = ltrim($request_uri, '/');
+ //$parsed = parse_url($request_uri);
+
+ if ($redirect_global && $request_uri != $original_uri) {
+ redirect_redirect(array(/*'redirect' => $request_uri,*/ 'type' => 'global'));
+ }
+}
+
+/**
+ * Implements hook_cron().
+ */
+function redirect_cron() {
+ // Purge inactive self-managed redirects from the database.
+ redirect_purge_inactive_redirects();
+}
+
+/**
+ * Implements hook_exit().
+ */
+function redirect_exit($destination = NULL) {
+ // If the current page is being cached, track it.
+ if (drupal_get_http_header('Location') && $rid = drupal_get_http_header('X-Redirect-ID')) {
+ // Ensure the database is loaded. This is only the next bootstrap step
+ // after DRUPAL_BOOTSTRAP_DATABASE
+ drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
+ db_update('redirect')
+ ->fields(array('access' => REQUEST_TIME))
+ ->expression('count', 'count + 1')
+ ->condition('rid', $rid)
+ ->execute();
+ }
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function redirect_entity_delete($entity, $entity_type) {
+ if ($entity_type == 'redirect') {
+ return;
+ }
+ redirect_delete_by_entity_path($entity_type, $entity);
+}
+
+/**
+ * Implements hook_path_update().
+ */
+function redirect_path_update(array $path) {
+ if (!variable_get('redirect_auto_redirect', TRUE)) {
+ return;
+ }
+ elseif (isset($path['redirect']) && !$path['redirect']) {
+ return;
+ }
+
+ if (!empty($path['original']['pid']) && $path['original']['pid'] == $path['pid'] && $path['original']['alias'] != $path['alias']) {
+ $redirect = new stdClass();
+ redirect_object_prepare($redirect);
+ $redirect->source = $path['original']['alias'];
+ $redirect->redirect = $path['source'];
+ $redirect->language = $path['original']['language'];
+ // Check if the redirect exists before saving.
+ $hash = redirect_hash($redirect);
+ if (!redirect_load_by_hash($hash)) {
+ redirect_save($redirect);
+ }
+ }
+}
+
+/**
+ * Implements hook_path_delete().
+ */
+function redirect_path_delete($path) {
+ if (!variable_get('redirect_auto_redirect', TRUE)) {
+ return;
+ }
+ elseif (isset($path['redirect']) && !$path['redirect']) {
+ return;
+ }
+ elseif (empty($path)) {
+ // @todo Remove this condition and allow $path to use an array type hint
+ // when http://drupal.org/node/1025904 is fixed.
+ return;
+ }
+
+ // Redirect from a deleted alias to the system path.
+ //if (!redirect_load_by_source($path['alias'], $path['language'])) {
+ // $redirect = new stdClass();
+ // redirect_object_prepare($redirect);
+ // $redirect->source = $path['alias'];
+ // $redirect->redirect = $path['source'];
+ // $redirect->language = $path['language'];
+ // redirect_save($redirect);
+ //}
+}
+
+/**
+ * Implements hook_views_api().
+ */
+function redirect_views_api() {
+ return array(
+ 'api' => 2,
+ 'path' => drupal_get_path('module', 'redirect') . '/views',
+ );
+}
+
+/**
+ * Implements hook_page_build().
+ *
+ * Adds an action on 404 pages to create a redirect.
+ */
+function redirect_page_build(&$page) {
+ if (redirect_is_current_page_404() && user_access('administer redirects')) {
+ if (!isset($page['content']['system_main']['actions'])) {
+ $page['content']['system_main']['actions'] = array(
+ '#theme' => 'links',
+ '#links' => array(),
+ '#attributes' => array('class' => array('action-links')),
+ '#weight' => -100,
+ );
+ }
+ $page['content']['system_main']['actions']['#links']['add_redirect'] = array(
+ 'title' => t('Add URL redirect from this page to another location'),
+ 'href' => 'admin/config/search/redirect/add',
+ 'query' => array('source' => current_path()) + drupal_get_destination(),
+ );
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds support for creating redirects if a node URL alias is changed.
+ */
+function redirect_form_node_form_alter(&$form, $form_state) {
+ if (!empty($form['path']['pid']['#value']) && !isset($form['path']['original'])) {
+ $form['path']['original'] = array('#type' => 'value', '#value' => path_load($form['path']['pid']['#value']));
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds support for creating redirects if a taxonomy term URL alias is changed.
+ */
+function redirect_form_taxonomy_form_term_alter(&$form, $form_state) {
+ if (!empty($form['path']['pid']['#value']) && !isset($form['path']['original'])) {
+ $form['path']['original'] = array('#type' => 'value', '#value' => path_load($form['path']['pid']['#value']));
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds support for creating redirects if an URL alias is changed.
+ */
+function redirect_form_path_admin_form_alter(&$form, $form_state) {
+ if (!empty($form['pid']['#value']) && !isset($form['original'])) {
+ $form['original'] = array('#type' => 'value', '#value' => path_load($form['pid']['#value']));
+ }
+}
+
+/**
+ * Load an URL redirect from the database.
+ *
+ * @param $rid
+ * The URL redirect ID.
+ * @param $reset
+ * Whether to reset the redirect_load_multiple cache.
+ *
+ * @return
+ * An URL redirect object, or FALSE if loading failed.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_load($rid, $reset = FALSE) {
+ $redirects = entity_load('redirect', array($rid), array(), $reset);
+ return !empty($redirects) ? reset($redirects) : FALSE;
+}
+
+/**
+ * Load an URL redirect from the database by {redirect}.hash.
+ *
+ * @param $hash
+ * The hash of the URL redirect.
+ * @param $reset
+ * Whether to reset the redirect_load_multiple cache.
+ *
+ * @return
+ * An URL redirect object, or FALSE if loading failed.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_load_by_hash($hash, $reset = FALSE) {
+ $redirects = entity_load('redirect', FALSE, array('hash' => $hash), $reset);
+ return !empty($redirects) ? reset($redirects) : FALSE;
+}
+
+/**
+ * Load multiple URL redirects from the database by {redirect}.source.
+ *
+ * @param $source
+ * The source of the URL redirect.
+ *
+ * @return
+ * An array of URL redirect objects indexed by redirect IDs.
+ *
+ * @see redirect_load_multiple()
+ * @see _redirect_uasort()
+ * @see redirect_compare_array_recursive()
+ *
+ * @ingroup redirect_api
+ */
+function redirect_load_by_source($source, $language = LANGUAGE_NONE, array $query = array()) {
+ // Run a case-insensitive query for matching RIDs first.
+ $rid_query = db_select('redirect');
+ $rid_query->addField('redirect', 'rid');
+ if ($source != variable_get('site_frontpage', 'node')) {
+ $rid_query->condition('source', db_like($source), 'LIKE');
+ }
+ else {
+ $source_condition = db_or();
+ $source_condition->condition('source', db_like($source), 'LIKE');
+ $source_condition->condition('source', '');
+ $rid_query->condition($source_condition);
+ }
+ $rid_query->condition('language', array($language, LANGUAGE_NONE));
+ $rids = $rid_query->execute()->fetchCol();
+
+ if ($rids && $redirects = redirect_load_multiple($rids)) {
+ // Narrow down the list of candidates.
+ foreach ($redirects as $rid => $redirect) {
+ if (!empty($redirect->source_options['query'])) {
+ if (empty($query) || !redirect_compare_array_recursive($redirect->source_options['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, '_redirect_uasort');
+
+ // Allow other modules to alter the redirect candidates before selecting the top one.
+ $context = array('language' => $language, 'query' => $query);
+ drupal_alter('redirect_load_by_source', $redirects, $source, $context);
+
+ return !empty($redirects) ? reset($redirects) : FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Load multiple URL redirects from the database.
+ *
+ * @param $rids
+ * An array of redirect IDs.
+ * @param $conditions
+ * An array of conditions on the {redirect} table in the form 'field' =>
+ * $value.
+ * @param $reset
+ * Whether to reset the redirect_load_multiple cache.
+ *
+ * @return
+ * An array of URL redirect objects indexed by redirect IDs.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_load_multiple($rids = array(), array $conditions = array(), $reset = FALSE) {
+ return entity_load('redirect', $rids, $conditions, $reset);
+}
+
+/**
+ * Determine whether the current user may perform the given operation on the
+ * specified redirect.
+ *
+ * @param $op
+ * The operation to be performed on the redirect. Possible values are:
+ * - "create"
+ * - "update"
+ * - "delete"
+ * @param $redirect
+ * The redirect object on which the operation is to be performed, or redirect
+ * type (e.g. 'feedburner') for the "create" operation.
+ * @param $account
+ * Optional, a user object representing the user for whom the operation is to
+ * be performed. Determines access for a user other than the current user.
+ *
+ * @return
+ * TRUE if the operation may be performed, FALSE otherwise.
+ */
+function redirect_access($op, $redirect, $account = NULL) {
+ global $user;
+
+ $rights = &drupal_static(__FUNCTION__, array());
+
+ if (!$redirect || !in_array($op, array('create', 'update', 'delete'), TRUE)) {
+ // If there was no redirect to check against, or the $op was not one of the
+ // supported ones, we return access denied.
+ return FALSE;
+ }
+ // If no user object is supplied, the access check is for the current user.
+ if (empty($account)) {
+ $account = $user;
+ }
+
+ $cid = isset($redirect->rid) ? $redirect->rid : $redirect;
+
+ // Return cached value if access already checked for this redirect, user and op.
+ if (isset($rights[$account->uid][$cid][$op])) {
+ return $rights[$account->uid][$cid][$op];
+ }
+
+ // Administrators can access all redirects.
+ if (user_access('administer redirects', $account)) {
+ $rights[$account->uid][$cid][$op] = TRUE;
+ return TRUE;
+ }
+
+ // We grant access to the redirect if both of the following conditions are met:
+ // - No modules say to deny access.
+ // - At least one module says to grant access.
+ $access = module_invoke_all('redirect_access', $op, $redirect, $account);
+ if (in_array(REDIRECT_ACCESS_DENY, $access, TRUE)) {
+ $rights[$account->uid][$cid][$op] = FALSE;
+ return FALSE;
+ }
+ elseif (in_array(REDIRECT_ACCESS_ALLOW, $access, TRUE)) {
+ $rights[$account->uid][$cid][$op] = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Validate a redirect.
+ */
+function redirect_validate($redirect, $form, &$form_state) {
+ $redirect = (object) $redirect;
+
+ // check that there there are no redirect loops
+ if (url($redirect->source) == url($redirect->redirect)) {
+ form_set_error('redirect', t('You are attempting to redirect the page to itself. This will result in an infinite loop.'));
+ }
+
+ redirect_hash($redirect);
+ if ($existing = redirect_load_by_hash($redirect->hash)) {
+ if ($redirect->rid != $existing->rid) {
+ form_set_error('source', t('The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => redirect_url($redirect->source, $redirect->source_options), '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid))));
+ }
+ }
+
+ // Allow other modules to validate the SSH public key.
+ foreach (module_implements('redirect_validate') as $module) {
+ $function = $module . '_redirect_validate';
+ $function($redirect, $form, $form_state);
+ }
+}
+
+function redirect_object_prepare($redirect, $defaults = array()) {
+ $defaults += array(
+ 'rid' => NULL,
+ 'type' => 'redirect',
+ 'uid' => $GLOBALS['user']->uid,
+ 'source_options' => array(),
+ 'redirect_options' => array(),
+ 'language' => LANGUAGE_NONE,
+ 'status_code' => 0,
+ 'count' => 0,
+ 'access' => 0,
+ 'hash' => '',
+ );
+
+ foreach ($defaults as $key => $default) {
+ if (!isset($redirect->{$key})) {
+ $redirect->{$key} = $default;
+ }
+ }
+
+ module_invoke_all('redirect_prepare', $redirect);
+}
+
+/**
+ * Save an URL redirect.
+ *
+ * @param $redirect
+ * The URL redirect object to be saved. If $redirect->rid is omitted (or
+ * $redirect->is_new is TRUE), a new redirect will be added.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_save($redirect) {
+ $transaction = db_transaction();
+
+ try {
+ if (!empty($redirect->rid) && !isset($redirect->original)) {
+ $redirect->original = entity_load_unchanged('redirect', $redirect->rid);
+ }
+
+ // Determine if we will be inserting a new node.
+ if (!isset($redirect->is_new)) {
+ $redirect->is_new = empty($redirect->rid);
+ }
+
+ // The changed timestamp is always updated for bookkeeping purposes.
+ //$redirect->changed = time();
+
+ redirect_hash($redirect);
+ if ($redirect->is_new || $redirect->hash != $redirect->original->hash) {
+ // Only new or changed redirects reset the last used value.
+ $redirect->count = 0;
+ $redirect->access = 0;
+ }
+
+ // Allow other modules to alter the redirect before saving.
+ module_invoke_all('redirect_presave', $redirect);
+ module_invoke_all('entity_presave', $redirect, 'redirect');
+
+ // Save the redirect to the database and invoke the post-save hooks.
+ if ($redirect->is_new) {
+ drupal_write_record('redirect', $redirect);
+ module_invoke_all('redirect_insert', $redirect);
+ module_invoke_all('entity_insert', $redirect, 'redirect');
+ }
+ else {
+ drupal_write_record('redirect', $redirect, array('rid'));
+ module_invoke_all('redirect_update', $redirect);
+ module_invoke_all('entity_update', $redirect, 'redirect');
+ }
+
+ // Clear internal properties.
+ unset($redirect->is_new);
+ unset($redirect->original);
+
+ // Clear the static loading cache.
+ entity_get_controller('redirect')->resetCache(array($redirect->rid));
+
+ // Ignore slave server temporarily to give time for the
+ // saved node to be propagated to the slave.
+ db_ignore_slave();
+ }
+ catch (Exception $e) {
+ $transaction->rollback();
+ watchdog_exception('redirect', $e);
+ throw $e;
+ }
+}
+
+/**
+ * Implements hook_redirect_insert().
+ */
+function redirect_redirect_insert($redirect) {
+ redirect_page_cache_clear($redirect);
+}
+
+/**
+ * Implements hook_redirect_update().
+ */
+function redirect_redirect_update($redirect) {
+ redirect_page_cache_clear($redirect);
+
+ // Clear the page cache for the original redirect as well.
+ if (!empty($redirect->original) && $redirect->original->source != $redirect->source) {
+ redirect_page_cache_clear($redirect->original);
+ }
+}
+
+/**
+ * Implements hook_redirect_delete().
+ */
+function redirect_redirect_delete($redirect) {
+ redirect_page_cache_clear($redirect);
+}
+
+/**
+ * Delete a single URL redirect.
+ *
+ * @param $rid
+ * The ID of the redirect to delete.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_delete($rid) {
+ return redirect_delete_multiple(array($rid));
+}
+
+/**
+ * Delete any redirects associated with a path or any of its sub-paths.
+ *
+ * Given a source like 'node/1' this function will delete any redirects that
+ * have that specific source or any sources that match 'node/1/%'.
+ *
+ * @param $path
+ * An string with an internal Drupal path.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_delete_by_path($path) {
+ $query = db_select('redirect');
+ $query->addField('redirect', 'rid');
+ $query_or = db_or();
+ $query_or->condition('source', db_like($path), 'LIKE');
+ $query_or->condition('source', db_like($path . '/') . '%', 'LIKE');
+ $query_or->condition('redirect', db_like($path), 'LIKE');
+ $query_or->condition('redirect', db_like($path . '/') . '%', 'LIKE');
+ $query->condition($query_or);
+ $rids = $query->execute()->fetchCol();
+
+ if ($rids) {
+ return redirect_delete_multiple($rids);
+ }
+}
+
+/**
+ * Delete an entity URL alias and any of its sub-paths.
+ *
+ * This function also checks to see if the default entity URI is different from
+ * the current entity URI and will delete any of the default aliases.
+ *
+ * @param $entity_type
+ * A string with the entity type.
+ * @param $entity
+ * An entity object.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_delete_by_entity_path($entity_type, $entity) {
+ if ($uri = entity_uri($entity_type, $entity)) {
+ redirect_delete_by_path($uri['path']);
+ }
+
+ //$info = entity_get_info($entity_type);
+ //if (isset($info['default path'])) {
+ // list($id) = entity_extract_ids($entity_type, $entity);
+ // $default_path = str_replace('[id]', $id, $info['default path']);
+ // if ($uri['path'] !== $default_path) {
+ // redirect_delete_by_path($default_path);
+ // }
+ //}
+}
+
+/**
+ * Delete multiple URL redirects.
+ *
+ * @param $rids
+ * An array of redirect IDs to delete.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_delete_multiple(array $rids) {
+ $transaction = db_transaction();
+ if (!empty($rids)) {
+ $redirects = redirect_load_multiple($rids);
+
+ try {
+ // Let modules react to the individual redirects being deleted.
+ foreach ($redirects as $rid => $redirect) {
+ module_invoke_all('redirect_delete', $redirect);
+ module_invoke_all('entity_delete', $redirect, 'redirect');
+ }
+
+ db_delete('redirect')
+ ->condition('rid', $rids, 'IN')
+ ->execute();
+ }
+ catch (Exception $e) {
+ $transaction->rollback();
+ watchdog_exception('redirect', $e);
+ throw $e;
+ }
+
+ // Clear the redirect_load_multiple cache.
+ entity_get_controller('redirect')->resetCache();
+ }
+}
+
+/**
+ * Purge inactive redirects from the database.
+ *
+ * @param $types
+ * An array of redirect types to remove. Default is only the self-managed
+ * 'redirect'. If not provided all redirect types will be eligible for
+ * removal.
+ * @param $interval
+ * The number of seconds to subtract from the current time and used to
+ * find the inactive redirects.
+ *
+ * @return
+ * An array of redirect IDs that were deleted or FALSE if none were.
+ */
+function redirect_purge_inactive_redirects(array $types = array('redirect'), $interval = NULL) {
+ if (!isset($interval)) {
+ $interval = variable_get('redirect_purge_inactive', 0);
+ }
+
+ if (!$interval || !variable_get('redirect_page_cache', 0) || !variable_get('page_cache_invoke_hooks', TRUE)) {
+ // If serving redirects from the page cache is enabled and hooks are not
+ // executed during page caching, then we cannot track when a redirect is
+ // used. Therefore, we cannot remove unused redirects.
+ return FALSE;
+ }
+
+ $query = db_select('redirect');
+ $query->addField('redirect', 'rid');
+ if (!empty($types)) {
+ $query->condition('type', $types);
+ }
+ $query->condition('access', REQUEST_TIME - $interval, '<');
+ $query->addTag('redirect_purge');
+ $rids = $query->execute()->fetchCol();
+
+ if (count($rids)) {
+ redirect_delete_multiple($rids);
+ watchdog('redirect', format_plural(count($rids), 'Removed 1 inactive redirect from the database.', 'Removed @count inactive redirects from the database.'));
+ return $rids;
+ }
+}
+
+/**
+ * Perform an URL redirect.
+ *
+ * @param $redirect
+ * An optional URL redirect array.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_redirect($redirect = NULL) {
+ // First check if we're in an infinite loop.
+ $session_id = session_id();
+ if (flood_is_allowed('redirection', 5, 15, $session_id ? $session_id : NULL)) {
+ flood_register_event('redirection', 60, $session_id ? $session_id : NULL);
+ }
+ else {
+ watchdog('redirect', 'Infinite loop stopped.');
+ drupal_set_message('Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!');
+ return FALSE;
+ }
+
+ if (!isset($redirect)) {
+ $redirect = new stdClass();
+ }
+ redirect_object_prepare($redirect, array('redirect' => current_path(), 'type' => 'manual', 'callback' => 'redirect_goto', 'cache' => TRUE));
+
+ if (empty($redirect->status_code)) {
+ $redirect->status_code = variable_get('redirect_default_status_code', 301);
+ }
+
+ if (variable_get('redirect_passthrough_querystring', 1)) {
+ // Preserve the current query parameters in the redirect.
+ $redirect->redirect_options += array('query' => array());
+ $redirect->redirect_options['query'] += drupal_get_query_parameters();
+ }
+
+ // Prevent the destination query parameter from overriding this redirect.
+ //if (isset($_GET['destination'])) {
+ // Simply unset the parameter since it has already been passed into
+ // $options['query'] in the previous code.
+ // unset($_GET['destination']);
+ //}
+
+ // Allow other modules to alter the redirect before passing to drupal_goto().
+ drupal_alter('redirect', $redirect);
+
+ // Continue if the redirect has not been disabled by hook_redirect_alter().
+ if (isset($redirect->redirect) && isset($redirect->callback) && $redirect->redirect !== FALSE && function_exists($redirect->callback)) {
+ // Perform the actual redirect.
+ $callback = $redirect->callback;
+ $callback($redirect);
+ }
+}
+
+/**
+ * Redirect callback; perform an URL redirect.
+ */
+function redirect_goto($redirect) {
+ $redirect->redirect_options['absolute'] = TRUE;
+ $url = url($redirect->redirect, $redirect->redirect_options);
+ drupal_add_http_header('Location', $url);
+ drupal_add_http_header('Status', redirect_status_code_options($redirect->status_code));
+
+ if (!empty($redirect->rid)) {
+ // Add a custom header for the redirect ID so when the redirect is served
+ // from the page cache, we can track it.
+ drupal_add_http_header('X-Redirect-ID', $redirect->rid);
+ }
+
+ if (!variable_get('redirect_page_cache', 0) || !variable_get('cache', 0) || !drupal_page_is_cacheable() || empty($redirect->cache)) {
+ drupal_exit($url);
+ }
+
+ // @see drupal_exit()
+ if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
+ if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
+ module_invoke_all('exit', $url);
+ }
+ drupal_session_commit();
+ if (variable_get('cache', 0)) {
+ // We must output something to allow the request to be cached.
+ echo ' ';
+ drupal_page_set_cache();
+ }
+ }
+
+ exit;
+}
+
+function redirect_hash($redirect) {
+ $hash = array(
+ 'source' => $redirect->source,
+ 'language' => $redirect->language,
+ );
+ if (!empty($redirect->source_options['query'])) {
+ $hash['source_query'] = $redirect->source_options['query'];
+ }
+ drupal_alter('redirect_hash', $hash, $redirect);
+ redirect_sort_recursive($hash, 'ksort');
+ $redirect->hash = drupal_hash_base64(serialize($hash));
+ return $redirect->hash;
+}
+
+/**
+ * Clear a page from the page cache.
+ */
+function redirect_page_cache_clear($redirect = NULL) {
+ if (!variable_get('redirect_page_cache', 0)) {
+ return;
+ }
+
+ if (isset($redirect)) {
+ $path = url($redirect->source, array('absolute' => TRUE));
+ // Use a wildcard to catch paths with query strings.
+ cache_clear_all($path, 'cache_page', TRUE);
+ }
+ else {
+ // Clear the entire page cache.
+ cache_clear_all('*', 'cache_page', TRUE);
+ }
+}
+
+/**
+ * Given a path determine if it is an entity default path.
+ *
+ * @param $path
+ * The internal path. The id of the entity should be in the string as '[id]'.
+ * @return
+ * An array with the entity type and the loaded entity object.
+ */
+function redirect_load_entity_from_path($path) {
+ $entity_paths = &drupal_static(__FUNCTION__);
+
+ if (!isset($entity_paths)) {
+ $entity_paths = array();
+ foreach (entity_get_info() as $entity_type => $entity_info) {
+ if (isset($entity_info['default path'])) {
+ $default_path = $entity_info['default path'];
+ $default_path = preg_quote($default_path, '/');
+ $default_path = str_replace(preg_quote('%' . $entity_type, '/'), '(\d+)', $default_path);
+ $entity_paths[$entity_type] = $default_path;
+ }
+ }
+ }
+
+ foreach ($entity_paths as $entity_type => $default_path) {
+ if (preg_match("/^{$default_path}$/", $path, $matches)) {
+ if ($entity = entity_load($entity_type, array($matches[1]))) {
+ return array('entity_type' => $entity_type, 'entity' => reset($entity));
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * Check the ability to perform redirects with the current request context.
+ *
+ * This function checks the following conditions:
+ * - If the PHP entry point is the root index.php file.
+ * - If PHP is not running as CLI.
+ * - If the site is not offline or in install/update mode.
+ * - If the curerent page is not an admin page (check can be disabled).
+ * - If the current request does not have any POST data since a redirect
+ * may interrupt form submission.
+ *
+ * @return
+ * TRUE if redirections can be performed, or FALSE otherwise.
+ */
+function redirect_can_redirect() {
+ $can_redirect = &drupal_static(__FUNCTION__);
+
+ if (!isset($can_redirect)) {
+ $path = current_path();
+ $can_redirect = TRUE;
+
+ if ($_SERVER['SCRIPT_NAME'] != $GLOBALS['base_path'] . 'index.php') {
+ // Do not redirect if the root script is not /index.php.
+ $can_redirect = FALSE;
+ }
+ elseif (!empty($_POST)) {
+ // Do not redirect if this is a post request with data.
+ $can_redirect = FALSE;
+ }
+ elseif (drupal_is_cli()) {
+ // If this is a command line request (Drush, etc), skip processing.
+ $can_redirect = FALSE;
+ }
+ elseif (variable_get('maintenance_mode', 0) || defined('MAINTENANCE_MODE')) {
+ // Do not redirect in offline or maintenance mode.
+ $can_redirect = FALSE;
+ }
+ elseif (!variable_get('redirect_global_admin_paths', 0) && path_is_admin($path)) {
+ // Do not redirect on admin paths.
+ $can_redirect = FALSE;
+ }
+ }
+
+ return $can_redirect;
+}
+
+/**
+ * Compare tha all values and associations in one array match another array.
+ *
+ * We cannot use array_diff_assoc() here because we need to be recursive.
+ *
+ * @param $match
+ * The array that has the values.
+ * @param $haystack
+ * The array that will be searched for values.
+ * @return
+ * TRUE if all the elements of $match were found in $haystack, or FALSE
+ * otherwise.
+ */
+function redirect_compare_array_recursive($match, $haystack) {
+ foreach ($match as $key => $value) {
+ if (!array_key_exists($key, $haystack)) {
+ return FALSE;
+ }
+ elseif (is_array($value)) {
+ if (!is_array($haystack[$key])) {
+ return FALSE;
+ }
+ elseif (!redirect_compare_array_recursive($value, $haystack[$key])) {
+ return FALSE;
+ }
+ }
+ elseif ($value != $haystack[$key]) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Sort an array recusively.
+ *
+ * @param $array
+ * The array to sort, by reference.
+ * @param $callback
+ * The sorting callback to use (e.g. 'sort', 'ksort', 'asort').
+ *
+ * @return
+ * TRUE on success or FALSE on failure.
+ */
+function redirect_sort_recursive(&$array, $callback = 'sort') {
+ $result = $callback($array);
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $result &= redirect_sort_recursive($array[$key], $callback);
+ }
+ }
+ return $result;
+}
+
+/**
+ * Load a language object by its language code.
+ *
+ * @todo Remove when http://drupal.org/node/660736 is fixed in Drupal core.
+ *
+ * @param $language
+ * A language code. If not provided the default language will be returned.
+ * @return
+ * A language object.
+ */
+function redirect_language_load($language = LANGUAGE_NONE) {
+ $languages = &drupal_static(__FUNCTION__);
+
+ if (!isset($languages)) {
+ $languages = language_list();
+ $languages[LANGUAGE_NONE] = NULL;
+ }
+
+ return isset($languages[$language]) ? $languages[$language] : NULL;
+}
+
+/**
+ * Build the URL of a redirect for display purposes only.
+ */
+function redirect_url($path, array $options = array(), $clean_url = NULL) {
+ if (!isset($clean_url)) {
+ $clean_url = variable_get('clean_url', 0);
+ }
+
+ if ($path == '') {
+ $path = '<front>';
+ }
+
+ if (!isset($options['alter']) || !empty($options['alter'])) {
+ drupal_alter('redirect_url', $path, $options);
+ }
+
+ // The base_url might be rewritten from the language rewrite in domain mode.
+ if (!isset($options['base_url'])) {
+ if (isset($options['https']) && variable_get('https', FALSE)) {
+ if ($options['https'] === TRUE) {
+ $options['base_url'] = $GLOBALS['base_secure_url'];
+ $options['absolute'] = TRUE;
+ }
+ elseif ($options['https'] === FALSE) {
+ $options['base_url'] = $GLOBALS['base_insecure_url'];
+ $options['absolute'] = TRUE;
+ }
+ }
+ else {
+ $options['base_url'] = $GLOBALS['base_url'];
+ }
+ }
+
+ if (empty($options['absolute']) || url_is_external($path)) {
+ $url = $path;
+ }
+ else {
+ $url = $options['base_url'] . base_path() . $path;
+ }
+
+ if (isset($options['query'])) {
+ $url .= $clean_url ? '?' : '&';
+ $url .= drupal_http_build_query($options['query']);
+ }
+ if (isset($options['fragment'])) {
+ $url .= '#' . $options['fragment'];
+ }
+
+ return $url;
+}
+
+function redirect_variables() {
+ return array(
+ 'redirect_default_status_code' => 301,
+ 'redirect_auto_redirect' => TRUE,
+ 'redirect_warning' => FALSE,
+ 'redirect_passthrough_querystring' => 1,
+ 'redirect_page_cache' => 0,
+ 'redirect_purge_inactive' => 0,
+ 'redirect_global_home' => 1,
+ 'redirect_global_clean' => 1,
+ 'redirect_global_canonical' => 1,
+ 'redirect_global_admin_paths' => 0,
+ );
+}
+
+//function redirect_get_redirect_info() {
+// $info = &drupal_static(__FUNCTION__);
+//
+// if (!isset($info)) {
+// if ($cache = cache_get('redirect:info')) {
+// $info = $cache->data;
+// }
+// else {
+// $info = module_invoke_all('redirect_info');
+// drupal_alter('redirect_info', $info);
+// cache_set('redirect:info', $info);
+// }
+// }
+//
+// return $info;
+//}
+
+function redirect_parse_url($url) {
+ $original_url = $url;
+ $url = trim($url, " \t\n\r\0\x0B\/");
+ $parsed = parse_url($url);
+
+ if (isset($parsed['fragment'])) {
+ $url = substr($url, 0, -strlen($parsed['fragment']));
+ $url = trim($url, '#');
+ }
+ if (isset($parsed['query'])) {
+ $url = substr($url, 0, -strlen($parsed['query']));
+ $url = trim($url, '?&');
+ $parsed['query'] = drupal_get_query_array($parsed['query']);
+ }
+
+ // Convert absolute to relative.
+ if (isset($parsed['scheme']) && isset($parsed['host'])) {
+ $base_secure_url = rtrim($GLOBALS['base_secure_url'] . base_path(), '/');
+ $base_insecure_url = rtrim($GLOBALS['base_insecure_url'] . base_path(), '/');
+ if (strpos($url, $base_secure_url) === 0) {
+ $url = str_replace($base_secure_url, '', $url);
+ $parsed['https'] = TRUE;
+ }
+ elseif (strpos($url, $base_insecure_url) === 0) {
+ $url = str_replace($base_insecure_url, '', $url);
+ }
+ }
+
+ $url = trim($url, '/');
+
+ // Convert to frontpage paths.
+ if ($url == '<front>') {
+ $url = '';
+ }
+
+ //$parsed['url'] = http_build_query($url, HTTP_URL_STRIP_QUERY | HTTP_URL_STRIP_FRAGMENT);
+ $parsed['url'] = $url;
+
+ // Allow modules to alter the parsed URL.
+ drupal_alter('redirect_parse_url', $parsed, $original_url);
+
+ return $parsed;
+}
+
+function redirect_status_code_options($code = NULL) {
+ $codes = array(
+ 300 => t('300 Multiple Choices'),
+ 301 => t('301 Moved Permanently'),
+ 302 => t('302 Found'),
+ 303 => t('303 See Other'),
+ 304 => t('304 Not Modified'),
+ 305 => t('305 Use Proxy'),
+ 307 => t('307 Temporary Redirect'),
+ );
+ return isset($codes[$code]) ? $codes[$code] : $codes;
+}
+
+/**
+ * Returns if the current page request is a page not found (404 status error).
+ *
+ * Why the fuck do we have to do this? Why is there not an easier way???
+ *
+ * @return
+ * TRUE if the current page is a 404, or FALSE otherwise.
+ */
+function redirect_is_current_page_404() {
+ return drupal_get_http_header('Status') == '404 Not Found';
+}
+
+/**
+ * uasort callback; Compare redirects based on language neutrality and rids.
+ */
+function _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 == LANGUAGE_NONE;
+ }
+ elseif (!empty($a->source_options['query']) != !empty($b->source_options['query'])) {
+ // Then sort by redirects that do not have query strings over ones that do.
+ return empty($a->source_options['query']);
+ }
+ else {
+ // Lastly sort by the highest redirect ID.
+ return $a->rid < $b->rid;
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter() on behalf of locale.module.
+ */
+function locale_form_redirect_edit_form_alter(&$form, &$form_state) {
+ $form['language'] = array(
+ '#type' => 'select',
+ '#title' => t('Language'),
+ '#options' => array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'),
+ '#default_value' => $form['language']['#value'],
+ '#description' => t('A redirect set for a specific language will always be used when requesting this page in that language, and takes precedence over redirects set for <em>All languages</em>.'),
+ );
+}
+
+/**
+ * Implements hook_field_attach_form().
+ *
+ * @todo Investigate using hook_entity_load() to load all entity redirects.
+ * @todo Figure out how to support entity URIs that contain query strings.
+ */
+function redirect_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+ list($id) = entity_extract_ids($entity_type, $entity);
+ if (!empty($form['redirect']) || empty($id) || $entity_type == 'comment') {
+ return;
+ }
+
+ // @todo Remove when http://drupal.org/node/1057242 is fixed.
+ if ($entity_type == 'file') {
+ return;
+ }
+
+ $uri = entity_uri($entity_type, $entity);
+ if (empty($uri)) {
+ // If the entity has no source path, then we cannot lookup the existing
+ // redirects.
+ return;
+ }
+
+ $info = entity_get_info($entity_type);
+ $form['redirect'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('URL redirects'),
+ '#description' => t('The following are a list of URL redirects that point to this @entitytype.', array('@entitytype' => drupal_strtolower($info['label']))),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#access' => user_access('administer redirects'),
+ '#weight' => 30,
+ '#attributes' => array('class' => array('redirect-list')),
+ );
+
+ // Only support vertical tabs if there is a vertical tab element.
+ foreach (element_children($form) as $key) {
+ if (isset($form[$key]['#type']) && $form[$key]['#type'] == 'vertical_tabs') {
+ $form['redirect']['#group'] = $key;
+ $form['redirect']['#attached']['js']['vertical-tabs'] = drupal_get_path('module', 'redirect') . '/redirect.js';
+ }
+ }
+
+ // We don't have to put our include in $form_state['build_info']['files']
+ // since the build array will already be cached.
+ module_load_include('inc', 'redirect', 'redirect.admin');
+ $redirects = redirect_load_multiple(FALSE, array('redirect' => $uri['path']));
+ $header = array('source', 'status_code', 'language', 'count', 'access', 'operations');
+ $form['redirect'] += redirect_list_table($redirects, $header);
+
+ $redirect = array(
+ 'redirect' => $uri['path'],
+ 'redirect_options' => array_diff_key($uri['options'], array('entity_type' => '', 'entity' => '')),
+ 'language' => $langcode,
+ );
+
+ $form['redirect']['actions'] = array(
+ '#theme' => 'links',
+ '#links' => array(),
+ '#attributes' => array('class' => array('action-links')),
+ );
+ if (redirect_access('create', 'redirect')) {
+ $form['redirect']['actions']['#links']['add'] = array(
+ 'title' => t('Add URL redirect to this @entitytype', array('@entitytype' => drupal_strtolower($info['label']))),
+ 'href' => 'admin/config/search/redirect/add',
+ 'query' => array_filter($redirect) + drupal_get_destination(),
+ );
+ }
+}
+
+/**
+ * Implements hook_field_extra_fields().
+ */
+function redirect_field_extra_fields() {
+ $entity_info = entity_get_info();
+ foreach (array_keys($entity_info) as $entity_type) {
+ if ($entity_type == 'comment') {
+ // The comment entity type supports URIs, but they're not real.
+ continue;
+ }
+ foreach (array_keys($entity_info[$entity_type]['bundles']) as $bundle) {
+ if (!isset($entity_info[$entity_type]['bundles'][$bundle]['uri callback']) && !isset($entity_info[$entity_type]['uri callback'])) {
+ // The bundle or base entity must have an URI callback defined otherwise
+ // we cannot use the entity_uri() function to lookup the entity's source
+ // path.
+ continue;
+ }
+ $info[$entity_type][$bundle]['form']['redirect'] = array(
+ 'label' => t('URL redirects'),
+ 'description' => t('Redirect module form elements'),
+ 'weight' => 30,
+ );
+ }
+ }
+ return $info;
+}
+
+/**
+ * Fetch an array of redirect bulk operations.
+ *
+ * @see hook_redirect_operations()
+ * @see hook_redirect_operations_alter()
+ */
+function redirect_get_redirect_operations() {
+ $operations = &drupal_static(__FUNCTION__);
+
+ if (!isset($operations)) {
+ $operations = module_invoke_all('redirect_operations');
+ drupal_alter('redirect_operations', $operations);
+ }
+
+ return $operations;
+}
+
+/**
+ * Implements hook_redirect_operations().
+ */
+function redirect_redirect_operations() {
+ $operations['delete'] = array(
+ 'action' => t('Delete'),
+ 'action_past' => t('Deleted'),
+ 'callback' => 'redirect_delete_multiple',
+ 'confirm' => TRUE,
+ );
+ return $operations;
+}