array(),
'args' => array(),
'limit' => 0,
);
// Check if this will be a tableselect element.
$tableselect &= user_access('administer redirects') && module_exists('elements');
// Set up the header.
$header = array(
'source' => array('data' => t('From'), 'field' => 'source', 'sort' => 'asc'),
'redirect' => array('data' => t('To'), 'field' => 'redirect'),
'type' => array('data' => t('Type'), 'field' => 'type'),
'language' => array('data' => t('Language'), 'field' => 'language'),
'last_used' => array('data' => t('Last used'), 'field' => 'last_used'),
'operations' => array('data' => t('Operations')),
);
// Do not include the language column if locale is disabled.
if (!module_exists('locale')) {
unset($header['language']);
}
// Remove any columns that are present in conditions.
_path_redirect_build_conditions($query, NULL, $conditions);
foreach ($conditions as $field => $value) {
unset($header[$field]);
}
// Build the SQL query.
$sql = 'SELECT rid FROM {path_redirect}';
if ($query['conditions']) {
$sql .= ' WHERE (' . implode(') AND (', $query['conditions']). ')';
}
$sql .= tablesort_sql($header);
if ($query['limit']) {
$query = pager_query($sql, $query['limit'], 0, NULL, $query['args']);
}
else {
$query = db_query($sql, $query['args']);
}
// Load the redirects.
$rids = array();
while ($rid = db_result($query)) {
$rids[] = $rid;
}
$redirects = path_redirect_load_multiple($rids);
$destination = drupal_get_destination();
$rows = array();
$weight = 0;
foreach ($rids as $rid) {
$redirect = $redirects[$rid];
$row = array();
if (isset($header['source'])) {
$source_url = path_redirect_build_url($redirect['source'], $redirect['source_query']);
$row['source'] = l($source_url, $redirect['source'], array('query' => $redirect['source_query'], 'alias' => TRUE));
}
if (isset($header['redirect'])) {
$redirect_url = path_redirect_build_url($redirect['redirect'], $redirect['query'], $redirect['fragment'], TRUE);
$row['redirect'] = l($redirect_url, $redirect['redirect'], array('query' => $redirect['query'], 'fragment' => $redirect['fragment']));
}
if (isset($header['type'])) {
$row['type'] = $redirect['type'];
}
if (isset($header['language'])) {
$row['language'] = module_invoke('locale', 'language_name', $redirect['language']);
}
if (isset($header['last_used'])) {
$row['last_used'] = format_date($redirect['last_used'], 'short');
}
if (isset($header['operations'])) {
$operations = array();
$operations['edit'] = array(
'title' => t('Edit'),
'href' => 'admin/build/path-redirect/edit/' . $rid,
'query' => $destination,
);
$operations['delete'] = array(
'title' => t('Delete'),
'href' => 'admin/build/path-redirect/delete/' . $rid,
'query' => $destination,
);
$row['operations'] = theme('links', $operations, array('class' => 'links inline nowrap'));
}
$rows[(string) $rid] = $row;
}
if ($tableselect) {
return array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $rows,
'#empty' => t('No URL redirects available.'),
);
}
else {
if (empty($rows)) {
$rows[] = array(array(
'data' => t('No URL redirects available.'),
'colspan' => count($header),
'class' => 'empty',
));
}
return array(
'#type' => 'markup',
'#value' => theme('table', $header, $rows),
);
}
}
/**
* Render a list of redirects for the main admin page.
*/
function path_redirect_admin_redirects(&$form_state) {
if (isset($form_state['values']['operation']) && empty($form_state['values']['confirm'])) {
return path_redirect_admin_redirects_update_confirm($form_state, $form_state['values']['operation'], array_filter($form_state['values']['rids']));
}
// Get filter key.
$keys = func_get_args();
array_shift($keys); // Offset the $form_state parameter.
$keys = implode('/', $keys);
// Add the local actions and filter form.
$form['actions'] = array(
'#type' => 'markup',
'#value' => path_redirect_local_actions(),
);
$form['filter'] = path_redirect_filter_form($keys);
// Build the 'Update options' form.
$form['options'] = array(
'#type' => 'fieldset',
'#title' => t('Update options'),
'#prefix' => '
',
'#suffix' => '
',
'#access' => user_access('administer redirects') && module_exists('elements'),
);
$options = array();
foreach (module_invoke_all('path_redirect_operations') as $key => $operation) {
$options[$key] = $operation['action'];
}
$form['options']['operation'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => 'delete',
);
$form['options']['submit'] = array(
'#type' => 'submit',
'#value' => t('Update'),
'#validate' => array('path_redirect_admin_redirects_update_validate'),
'#submit' => array('path_redirect_admin_redirects_update_submit'),
);
// Apply the filter conditions.
$query = array('conditions' => array(), 'args' => array(), 'limit' => 50);
path_redirect_filter_query($query, $keys);
$form['rids'] = path_redirect_list_redirects($query, array(), TRUE);
$form['pager'] = array(
'#type' => 'markup',
'#value' => theme('pager', NULL, 50, 0),
);
return $form;
}
function path_redirect_filter_query(&$query, $keys = '') {
if ($keys) {
// Replace wildcards with MySQL/PostgreSQL wildcards.
$wildcard = preg_replace('!\*+!', '%', $keys);
$query['conditions'][] = "(source LIKE '%%%s%%' OR redirect LIKE '%%%s%%' OR query LIKE '%%%s%%' OR fragment LIKE '%%%s%%')";
$query['args'] = array_merge($query['args'], array($wildcard, $wildcard, $wildcard, $wildcard));
}
}
/**
* Return a form to filter URL redirects.
*
* @ingroup forms
* @see path_redirect_filter_form_submit()
*/
function path_redirect_filter_form($keys = '') {
$form['#attributes'] = array('class' => 'search-form');
$form['basic'] = array(
'#type' => 'fieldset',
'#title' => t('Filter redirects'),
);
$form['basic']['inline'] = array(
'#prefix' => '',
'#suffix' => '
',
);
$form['basic']['inline']['filter'] = array(
'#type' => 'textfield',
'#title' => '',
'#default_value' => $keys,
'#maxlength' => 128,
'#size' => 25,
);
$form['basic']['inline']['submit'] = array(
'#type' => 'submit',
'#value' => t('Filter'),
'#submit' => array('path_redirect_filter_form_submit'),
);
if ($keys) {
$form['basic']['inline']['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset'),
'#submit' => array('path_redirect_filter_form_reset'),
);
}
return $form;
}
/**
* Process filter form submission when the Filter button is pressed.
*/
function path_redirect_filter_form_submit($form, &$form_state) {
$form_state['redirect'] = 'admin/build/path-redirect/list/'. trim($form_state['values']['filter']);
}
/**
* Process filter form submission when the Reset button is pressed.
*/
function path_redirect_filter_form_reset($form, &$form_state) {
$form_state['redirect'] = 'admin/build/path-redirect';
}
/**
* Validate node_admin_nodes form submissions.
*
* Check if any nodes have been selected to perform the chosen
* 'Update option' on.
*/
function path_redirect_admin_redirects_update_validate($form, &$form_state) {
// Error if there are no items to select.
if (!is_array($form_state['values']['rids']) || !count(array_filter($form_state['values']['rids']))) {
form_set_error('', t('No redirects selected.'));
}
}
/**
* Process path_redirect_admin_redirects form submissions.
*
* Execute the chosen 'Update option' on the selected redirects.
*/
function path_redirect_admin_redirects_update_submit($form, &$form_state) {
$operations = module_invoke_all('path_redirect_operations');
$operation = $operations[$form_state['values']['operation']];
// Filter out unchecked redirects
$rids = array_filter($form_state['values']['rids']);
if (!empty($operation['confirm']) && empty($form_state['values']['confirm'])) {
// We need to rebuild the form to go to a second step. For example, to
// show the confirmation form for the deletion of redirects.
$form_state['rebuild'] = TRUE;
}
else {
$function = $operation['callback'];
// Add in callback arguments if present.
if (isset($operation['callback arguments'])) {
$args = array_merge(array($rids), $operation['callback arguments']);
}
else {
$args = array($rids);
}
call_user_func_array($function, $args);
path_redirect_clear_cache();
$count = count($form_state['values']['rids']);
watchdog('path_redirect', '@action @count redirects.', array('@action' => $operation['action_past'], '@count' => $count));
drupal_set_message(format_plural(count($rids), '@action @count redirect.', '@action @count redirects.', array('@action' => $operation['action_past'], '@count' => $count)));
//$form_state['redirect'] = 'admin/build/path-redirect';
}
}
function path_redirect_admin_redirects_update_confirm(&$form_state, $operation, $rids) {
$operations = module_invoke_all('path_redirect_operations');
$operation = $operations[$form_state['values']['operation']];
$form['rids'] = array('#prefix' => '', '#tree' => TRUE);
$redirects = path_redirect_load_multiple($rids);
foreach ($rids as $rid) {
$redirect = $redirects[$rid];
$form['rids'][$rid] = array(
'#type' => 'hidden',
'#value' => $rid,
'#prefix' => '',
'#suffix' => path_redirect_build_url($redirect['source'], $redirect['source_query']) . "\n",
);
}
$form['operation'] = array('#type' => 'hidden', '#value' => $form_state['values']['operation']);
$form['#submit'][] = 'path_redirect_admin_redirects_update_submit';
$confirm_question = format_plural(count($rids), 'Are you sure you want to @action this redirect?', 'Are you sure you want to @action these redirects?', array('@action' => drupal_strtolower($operation['action'])));
return confirm_form(
$form,
$confirm_question,
'admin/build/path-redirect', // @todo This does not redirect back to filtered page.
t('This action cannot be undone.'),
$operation['action'],
t('Cancel')
);
}
function path_redirect_edit_form(&$form_state, $redirect = array()) {
// Merge default values.
$redirect += array(
'rid' => NULL,
'source' => isset($_GET['source']) ? urldecode($_GET['source']) : '',
'source_query' => isset($_GET['source_query']) ? path_redirect_get_query_array($_GET['source_query']) : array(),
'redirect' => isset($_GET['redirect']) ? urldecode($_GET['redirect']) : '',
'query' => array(),
'fragment' => '',
'language' => isset($_GET['language']) ? urldecode($_GET['language']) : '',
'type' => variable_get('path_redirect_default_status', 301),
);
$form['rid'] = array(
'#type' => 'value',
'#value' => $redirect['rid'],
);
$form['source'] = array(
'#type' => 'textfield',
'#title' => t('From'),
'#description' => t("Enter an internal Drupal path or path alias to redirect (e.g. %example1 or %example2). Fragment anchors (e.g. %anchor) are not allowed.", array('%example1' => 'node/123', '%example2' => 'taxonomy/term/123', '%anchor' => '#anchor')),
'#size' => 42,
'#maxlength' => 255,
'#default_value' => path_redirect_build_url($redirect['source'], $redirect['source_query']),
'#required' => TRUE,
'#field_prefix' => url('', array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
'#autocomplete_path' => db_table_exists('watchdog') ? 'js/path_redirect/autocomplete_404' : '',
'#element_validate' => array('path_redirect_validate_source_field'),
);
$form['redirect'] = array(
'#type' => 'textfield',
'#title' => t('To'),
'#maxlength' => 560,
'#default_value' => path_redirect_build_url($redirect['redirect'], $redirect['query'], $redirect['fragment'], TRUE),
'#required' => TRUE,
'#description' => t('Enter an internal Drupal path, path alias, or complete external URL (like http://example.com/) to redirect to. Use %front to redirect to the front page.', array('%front' => '')),
'#element_validate' => array('path_redirect_validate_redirect_field'),
);
// This will be a hidden value unless locale module is enabled.
$form['language'] = array(
'#type' => 'value',
'#value' => $redirect['language'],
'#process' => array(),
);
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['advanced']['type'] = array(
'#type' => 'select',
'#title' => t('Redirect status'),
'#description' => t('You can find more information about HTTP redirect status codes at @status-codes.', array('@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection')),
'#default_value' => $redirect['type'],
'#options' => path_redirect_status_code_options(),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
$form['cancel'] = array(
'#value' => l(t('Cancel'), isset($_REQUEST['destination']) ? $_REQUEST['destination'] : 'admin/build/path-redirect'),
);
return $form;
}
/**
* Implement hook_form_FORM_ID_alter().
*/
function locale_form_path_redirect_edit_form_alter(&$form, &$form_state) {
$form['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#options' => array('' => t('All languages')) + locale_language_list('name'),
'#default_value' => $form['language']['#value'],
'#weight' => -10,
'#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 All languages.'),
);
}
/**
* Validate a redirect's source path (from) field.
*/
function path_redirect_validate_source_field($element, &$form_state) {
// Check that the path contains no URL fragment.
if (strpos($element['#value'], '#') !== FALSE) {
form_error($element, t('The source path cannot contain an URL fragment anchor.'));
}
//if (!valid_url($element['#value'])) {
// //Make sure "from" has the form of a local Drupal path
// form_serror($element, t('The source path does not appear valid. This must be a local Drupal path.'));
//}
// @todo Split into source and source_query here.
// A redirect's 'from' cannot match any values from url_alias, it will cause an infinite loop.
if ($pid = db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", $element['#value']))) {
form_error($element, t('You cannot add an existing alias as a redirect as it will not work. You must delete the alias first.', array('@alias-delete' => url('admin/build/path/delete/' . $pid, array('query' => drupal_get_destination())))));
}
// Cannot create redirects for valid paths.
$menu_item = menu_get_item($element['#value']);
if ($menu_item && $menu_item['page_callback'] != 'path_redirect_goto' && $element['#value'] == $menu_item['path']) {
form_error($element, t('The source path %path is a currently valid path. You cannot override existing paths. You can however, create URL aliases for them.', array('%path' => $element['#value'])));
}
return $element;
}
/**
* Validate a redirect's redirect path (to) field.
*/
function path_redirect_validate_redirect_field($element, &$form_state) {
// Split this field into path, query, and fragment.
$parsed = parse_url($element['#value']) + array('query' => '', 'fragment' => '', 'path' => '');
$form_state['values']['query'] = $parsed['query'];
$form_state['values']['fragment'] = $parsed['fragment'];
$form_state['values']['redirect'] = (isset($parsed['scheme']) ? $parsed['scheme'] . '://'. $parsed['host'] : '') . $parsed['path'];
return $element;
}
function path_redirect_edit_form_validate($form, &$form_state) {
$redirect = &$form_state['values'];
if ($existing = path_redirect_load_by_source($redirect['source'], $redirect['language'])) {
if ($redirect['rid'] != $existing['rid'] && $redirect['language'] == $existing['language']) {
// The "from" path should not conflict with another redirect
form_set_error('source', t('The source path %source is already being redirected. Do you want to edit the existing redirect?', array('%source' => $redirect['source'], '@edit-page' => url('admin/build/path-redirect/edit/'. $existing['rid']))));
}
}
if (!valid_url($redirect['redirect']) && !valid_url($redirect['redirect'], TRUE) && $redirect['redirect'] != '') {
//form_set_error('redirect', t('The redirect to path does not appear valid.'));
}
// check that there there are no redirect loops
if (url(drupal_strtolower($redirect['source'])) == url(drupal_strtolower($redirect['redirect']))) {
form_set_error('redirect', t('You are attempting to redirect the page to itself. This will result in an infinite loop.'));
}
}
function path_redirect_edit_form_submit($form, &$form_state) {
path_redirect_save($form_state['values']);
drupal_set_message(t('The redirect has been saved.'));
$form_state['redirect'] = 'admin/build/path-redirect';
}
function path_redirect_delete_form($form_state, $redirect) {
$form['redirect'] = array(
'#type' => 'value',
'#value' => $redirect,
);
return confirm_form(
$form,
t('Are you sure you want to delete the redirect from %source to %redirect?', array('%source' => $redirect['source'], '%redirect' => $redirect['redirect'])),
isset($_REQUEST['destination']) ? $_REQUEST['destination'] : 'admin/build/path-redirect'
);
}
function path_redirect_delete_form_submit($form, &$form_state) {
path_redirect_delete($form_state['values']['redirect']['rid']);
drupal_set_message(t('The redirect has been deleted.'));
$form_state['redirect'] = 'admin/build/path-redirect';
}
/**
* Form builder; administrative settings for the module.
*
* @see system_settings_form()
*/
function path_redirect_settings_form() {
$form['path_redirect_redirect_warning'] = array(
'#type' => 'checkbox',
'#title' => t('Display a warning message to users when they are redirected.'),
'#default_value' => variable_get('path_redirect_redirect_warning', 0),
);
$form['path_redirect_allow_bypass'] = array(
'#type' => 'checkbox',
'#title' => t('Allow users to bypass redirects by adding %code to the URL.', array('%code' => variable_get('clean_url', 0) ? '?redirect=no' : '&redirect=no')),
'#default_value' => variable_get('path_redirect_allow_bypass', 0),
);
$form['path_redirect_auto_redirect'] = array(
'#type' => 'checkbox',
'#title' => t('Automatically create redirects when URL aliases are changed.'),
'#default_value' => variable_get('path_redirect_auto_redirect', 1),
'#access' => module_exists('path'),
);
$form['path_redirect_purge_inactive'] = array(
'#type' => 'select',
'#title' => t('Discard redirects that have not been accessed for'),
'#default_value' => variable_get('path_redirect_purge_inactive', 0),
'#options' => array(0 => t('Never (do not discard)')) + drupal_map_assoc(array(604800, 1209600, 2419200, 4838400, 7257600, 9676800, 31536000), 'format_interval'),
);
$form['path_redirect_default_status'] = array(
'#type' => 'select',
'#title' => t('Default redirect status'),
'#description' => t('You can find more information about HTTP redirect status codes at @status-codes.', array('@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection')),
'#default_value' => variable_get('path_redirect_default_status', 301),
'#options' => path_redirect_status_code_options(),
);
return system_settings_form($form);
}
function path_redirect_status_code_options() {
return 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'),
);
}
/**
* Autocompletion callback for the add/edit redirect form. Returns a list of
* current 404s on the site.
*/
function path_redirect_js_autocomplete_404() {
$args = func_get_args();
$string = drupal_strtolower(implode('/', $args));
$matches = array();
// Get a list of 404s, sorted by the number of times each 404 was processed.
$paths = db_query("SELECT message, COUNT(message) AS count FROM {watchdog} WHERE type = 'page not found' AND LOWER(message) LIKE '%%%s%%' GROUP BY message ORDER BY count DESC", drupal_strtolower($string));
while ($path = db_result($paths)) {
// If the 404 is now a valid path or already has a redirect, discard it.
if (!menu_get_item($path) && !path_redirect_load_by_source($path)) {
$matches[$path] = check_plain($path);
}
}
// Limit the output to 10 results and return the JSON.
$matches = array_slice($matches, 0, 10);
drupal_json($matches);
}