Index: path_redirect.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/path_redirect/path_redirect.admin.inc,v
retrieving revision 1.1.2.44
diff -u -p -r1.1.2.44 path_redirect.admin.inc
--- path_redirect.admin.inc	27 Jun 2009 22:24:27 -0000	1.1.2.44
+++ path_redirect.admin.inc	29 Oct 2009 04:13:14 -0000
@@ -182,24 +182,24 @@ function path_redirect_edit_form(&$form_
 }
 
 function path_redirect_edit_validate($form, &$form_state) {
-  if (strpos($form_state['values']['path'], '#') !== FALSE) {
+  if ($error_anchor = path_redirect_check_anchor($form_state['values']['path'])) {
     // Check that the "from" path is valid and contains no # fragment
-    form_set_error('path', t('You cannot redirect from a fragment anchor.'));
+    form_set_error('path', $error_anchor);
   }
   //elseif (!valid_url($form_state['values']['path'])) {
   //  //Make sure "from" has the form of a local Drupal path
   //  form_set_error('path', t('The redirect <strong>from</strong> path does not appear valid. This must be a local Drupal path.'));
   //}
-  elseif (($result = path_redirect_load(array('path' => $form_state['values']['path']))) && $form_state['values']['rid'] !== $result->rid) {
+  elseif ($error_uniqueness = path_redirect_check_uniqueness($form_state['values']['path'], $form_state['values']['rid'])) {
     // The "from" path should not conflict with another redirect
-    form_set_error('path', t('The <strong>from</strong> path you entered is already redirected. You can <a href="@edit-page">edit this redirect</a> instead.', array('@edit-page' => url('admin/build/path-redirect/edit/'. $result->rid))));
+    form_set_error('path', $error_uniqueness);
   }
-  elseif ($pid = db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", array(':dst' => $form_state['values']['path'])))) {
+  elseif ($error_existing_url_alias = path_redirect_check_existing_url_alias($form_state['values']['path'])) {
     // A redirect's 'from' cannot match any values from url_alias, it will cause an infinite loop.
-    form_set_error('path', t('You cannot add an existing alias as a redirect as it will not work. You must <a href="@alias-delete">delete the alias</a> first.', array('@alias-delete' => url('admin/build/path/delete/' . $pid, array('query' => drupal_get_destination())))));
+    form_set_error('path', $error_existing_url_alias);
   }
-  elseif (menu_get_item($form_state['values']['path'])) {
-    form_set_error('path', t('You cannot create a redirect from a currently valid path.'));
+  elseif ($error_existing_menu  = path_redirect_check_existing_menu($form_state['values']['path'])) {
+    form_set_error('path', $error_existing_menu);
   }
 
   if (!valid_url($form_state['values']['redirect']) && !valid_url($form_state['values']['redirect'], TRUE) && $form_state['values']['redirect'] != '<front>') {
@@ -248,6 +248,23 @@ function path_redirect_delete_confirm_su
  * @see system_settings_form()
  */
 function path_redirect_settings_form() {
+  $form['path_redirect_nodeapi_enabled'] = array(
+    '#type' => 'radios',
+    '#title' => t('Enable in node edit form'),
+    '#default_value' => path_redirect_var('nodeapi_enabled'),
+    '#options' => array(t('Disabled'), t('Enabled')),
+    '#description' => t('Enable management of URL redirects directly on content editing pages.'),
+  );
+
+  $form['path_redirect_nodeapi_error_links'] = array(
+    '#type' => 'radios',
+    '#title' => t('Link to fix existing redirects or paths in node edit form error messages'),
+    '#default_value' => path_redirect_var('nodeapi_error_links'),
+    '#options' => array(t('Disabled'), t('Enabled')),
+    '#disabled' => !path_redirect_var('nodeapi_enabled'),
+    '#description' => t('In the node edit form, if a user submits a redirect that already exists, or is an existing path alias, an error message will appear. Enable this option to show a link in the error message to edit the existing redirect or delete the existing path alias if the user has access to do so in the first place. Leaving this option disabled prevents users from being tempted to click away from the node edit form and losing unsaved changes to the node.'),
+  );
+
   $form['path_redirect_redirect_warning'] = array(
     '#type' => 'checkbox',
     '#title' => t('Display a warning message to users when they are redirected.'),
@@ -291,42 +308,6 @@ function path_redirect_settings_form() {
   return system_settings_form($form);
 }
 
-/**
- * Return an array of 300-range status codes.
- */
-function path_redirect_status_codes() {
-  return array(
-    300 => array(
-      'title' => t('300 Multiple Choices'),
-      'description' => t('The request is ambiguous and needs clarification as to which resource was requested.'),
-    ),
-    301 => array(
-      'title' => t('301 Moved Permanently'),
-      'description' => t('Moved Permanently. The resource has permanently moved elsewhere, the response indicates where it has gone to. <strong>Recommended.</strong>'),
-    ),
-    302 => array(
-      'title' => t('302 Found'),
-      'description' => t("The resource has temporarily moved elsewhere, the response indicates where it is at present. <strong>This is Drupal's default redirect type.</strong>"),
-    ),
-    303 => array(
-      'title' => t('303 See Other'),
-      'description' => t('See Other/Redirect. A preferred alternative source should be used at present.'),
-    ),
-    304 => array(
-      'title' => t('304 Not Modified'),
-      'description' => t("The server has identified from the request information that the client's copy of the information is up-to-date and the requested information does not need to be sent again."),
-    ),
-    305 => array(
-      'title' => t('305 Use Proxy'),
-      'description' => t('The request must be sent through the indicated proxy server.'),
-    ),
-    307 => array(
-      'title' => t('307 Temporary Redirect'),
-      'description' => t('The resource has temporarily moved elsewhere, the response indicates where it is at present. Client should still use this URL.'),
-    ),
-  );
-}
-
 /*
  * Extract part of the current Drupal path from a certain 'argument' onward.
  *
Index: path_redirect.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/path_redirect/path_redirect.module,v
retrieving revision 1.3.2.7.2.49
diff -u -p -r1.3.2.7.2.49 path_redirect.module
--- path_redirect.module	27 Jun 2009 21:57:20 -0000	1.3.2.7.2.49
+++ path_redirect.module	29 Oct 2009 04:13:14 -0000
@@ -25,6 +25,7 @@ function path_redirect_help($path, $arg)
 function path_redirect_perm() {
   return array(
     'administer redirects',
+    'set per-node redirects',
   );
 }
 
@@ -213,6 +214,107 @@ function _path_redirect_check() {
 }
 
 /**
+ * Implementation of hook_form_alter().
+ */
+function path_redirect_form_alter(&$form, $form_state, $form_id) {
+  if (variable_get('path_redirect_nodeapi_enabled', 0) && isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
+    $form['redirects'] = array(
+      '#type' => 'fieldset',
+      '#access' => (user_access('administer redirects') || user_access('set per-node redirects')),
+      '#title' => t('URL redirects'),
+      '#collapsible' => TRUE,
+      '#collapsed' => !db_affected_rows(path_redirect_node_redirects($form['#node']->nid)),
+      '#prefix' => '<div class="path-redirects">',
+      '#suffix' => '</div>',
+      '#weight' => 31,
+    );
+    $form['redirects']['list'] = _path_redirect_node_form_list($form['#node']);
+    $form['redirects']['path_redirect_add'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Add a redirect from'),
+      '#description' => t('Path from which to redirect to this node. Do not include the leading slash.  Example: To redirect http://www.arapahoe.edu/courses/test.html, you would simply enter courses/test.html'),
+      '#maxlength' => 255,
+      '#size' => 42,
+    );
+  }
+}
+
+function path_redirect_node_redirects($nid) {
+  return db_query("
+    SELECT rid, path, redirect, type
+    FROM {path_redirect} pr LEFT JOIN {url_alias} ua ON pr.redirect = ua.dst
+    WHERE pr.redirect = 'node/%d' OR ua.src = 'node/%d'
+    ORDER BY pr.path", $nid, $nid);
+}
+
+function _path_redirect_node_form_list($node) {
+  $form['#theme'] = 'path_redirect_node_form_list';
+  if (!empty($node->nid)) {
+    $result = path_redirect_node_redirects($node->nid);
+    if ($result) {
+      $destination = "destination=admin/build/path-redirect";
+      $types = path_redirect_status_codes();
+      while ($redirect = db_fetch_object($result)) {
+        $form['redirects'][$redirect->rid]['path'] = array(
+          '#value' => $redirect->path,
+        );
+        $form['redirects'][$redirect->rid]['redirect'] = array(
+          '#value' => $redirect->redirect,
+        );
+        $form['redirects'][$redirect->rid]['type'] = array(
+          '#value' => '<span title="'. $types[$redirect->type]['title'] .'">'. $redirect->type .'</span>',
+        );
+        $form['redirects'][$redirect->rid]['test'] = array(
+          '#value' => l(t('test'), $redirect->path),
+        );
+        $form['redirects'][$redirect->rid]['edit'] = array(
+          '#value' => user_access('administer redirects') ? l(t('edit'), 'admin/build/path-redirect/edit/'. $redirect->rid, array('query' => $destination)) : '',
+        );
+        $form['redirects'][$redirect->rid]['delete'] = array(
+          '#value' => user_access('administer redirects') ? l(t('delete'), 'admin/build/path-redirect/delete/'. $redirect->rid, array('query' => $destination)): '',
+        );
+      }
+    }
+  }
+  return $form;
+}
+
+function theme_path_redirect_node_form_list(&$form) {
+  if (count(element_children($form['redirects']))) {
+    $header = array(
+      t('From'),
+      t('To'),
+      t('Type'),
+      array('data' => t('Operations'), 'colspan' => 3),
+    );
+    foreach (element_children($form['redirects']) as $key) {
+      $rows[] = array(
+        drupal_render($form['redirects'][$key]['path']),
+        drupal_render($form['redirects'][$key]['redirect']),
+        drupal_render($form['redirects'][$key]['type']),
+        drupal_render($form['redirects'][$key]['test']),
+        drupal_render($form['redirects'][$key]['edit']),
+        drupal_render($form['redirects'][$key]['delete']),
+      );
+    }
+    return theme('table', $header, $rows);
+  }
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function path_redirect_theme($existing, $type, $theme, $path) {
+  return array(
+    'path_redirect_node_form_list' => array(
+      'arguments' => array(
+        'form' => array(),
+        ),
+      ),
+    );
+}
+
+/**
  * Implementation of hook_nodeapi().
  * @todo Remove in Drupal 7.
  */
@@ -221,12 +323,89 @@ function path_redirect_nodeapi(&$node, $
     case 'presave':
       path_redirect_node_presave($node);
       break;
+    case 'validate':
+      // We're not editing an existing redirect
+      $rid = NULL;
+      // We might now want to take the users away from editing the node -- they will lose their changes
+      $show_link = path_redirect_var('nodeapi_error_links');
+      $path = $node->path_redirect_add;
+      if ($error_anchor = path_redirect_check_anchor($path)) {
+        // Check that the "from" path is valid and contains no # fragment
+        form_set_error('path_redirect_add', $error_anchor);
+      }
+      elseif ($error_uniqueness = path_redirect_check_uniqueness($path, $rid, $show_link)) {
+        // The "from" path should not conflict with another redirect
+        form_set_error('path_redirect_add', $error_uniqueness);
+      }
+      elseif ($error_existing_url_alias = path_redirect_check_existing_url_alias($path, $show_link)) {
+        // A redirect's 'from' cannot match any values from url_alias, it will cause an infinite loop.
+        form_set_error('path_redirect_add', $error_existing_url_alias);
+      }
+      elseif ($error_existing_menu  = path_redirect_check_existing_menu($path)) {
+        form_set_error('path_redirect_add', $error_existing_menu);
+      }
+      break;
+    case 'update':
+    case 'insert':
+      if ($node->path_redirect_add) {
+        path_redirect_save(array(
+          'path' => $node->path_redirect_add,
+          'redirect' => 'node/'. $node->nid,
+          'type' => 301,
+        ));
+      }
+      break;
     case 'delete':
       path_redirect_node_delete($node);
       break;
   }
 }
 
+
+/**
+ * Check that the "from" path is valid and contains no # fragment
+ */
+function path_redirect_check_anchor($path) {
+  if (strpos($path, '#') !== FALSE) {
+    return t('You cannot redirect from a fragment anchor.');
+  }
+}
+
+/**
+ * The "from" path should not conflict with another redirect
+ */
+function path_redirect_check_uniqueness($path, $rid = NULL, $show_link = TRUE) {
+  if (($result = path_redirect_load(array('path' => $path))) && $rid !== $result->rid) {
+    $message = t('The <strong>from</strong> path you entered is already redirected.');
+    if ($show_link && user_access('administer redirects')) {
+      $message .= ' '. t('You can <a href="@edit-page">edit this redirect</a> instead.', array('@edit-page' => url('admin/build/path-redirect/edit/'. $result->rid)));
+    }
+    return $message;
+  }
+}
+
+/**
+ * A redirect's 'from' cannot match any values from url_alias, it will cause an infinite loop.
+ */
+function path_redirect_check_existing_url_alias($path, $show_link = TRUE) {
+  if ($pid = db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", array(':dst' => $path)))) {
+    $message = t('You cannot add an existing alias as a redirect as it will not work.');
+    if ($show_link && user_access('administer url aliases')) {
+      $message .= ' '. t('You must <a href="@alias-delete">delete the alias</a> first.', array('@alias-delete' => url('admin/build/path/delete/'. $pid, array('query' => drupal_get_destination()))));
+    }
+    return $message;
+  }
+}
+
+/**
+ * The path cannot be an existing path
+ */
+function path_redirect_check_existing_menu($path) {
+  if (menu_get_item($path)) {
+    return t('You cannot create a redirect from a currently valid path.');
+  }
+}
+
 /**
  * Implementation of hook_node_presave().
  *
@@ -354,8 +533,8 @@ function path_redirect_variables() {
     'path_redirect_auto_redirect' => 1,
     'path_redirect_purge_inactive' => 4838400,
     'path_redirect_default_status' => 301,
-    // Unused variables set to NULL so they will still be uninstalled.
-    'path_redirect_nodeapi_enabled' => NULL,
+    'path_redirect_nodeapi_enabled' => 0,
+    'path_redirect_nodeapi_error_links' => 0,
   );
 }
 
@@ -377,3 +556,40 @@ function path_redirect_var($name, $defau
 
   return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
 }
+
+/**
+ * Return an array of 300-range status codes.
+ */
+function path_redirect_status_codes() {
+  return array(
+    300 => array(
+      'title' => t('300 Multiple Choices'),
+      'description' => t('The request is ambiguous and needs clarification as to which resource was requested.'),
+    ),
+    301 => array(
+      'title' => t('301 Moved Permanently'),
+      'description' => t('Moved Permanently. The resource has permanently moved elsewhere, the response indicates where it has gone to. <strong>Recommended.</strong>'),
+    ),
+    302 => array(
+      'title' => t('302 Found'),
+      'description' => t("The resource has temporarily moved elsewhere, the response indicates where it is at present. <strong>This is Drupal's default redirect type.</strong>"),
+    ),
+    303 => array(
+      'title' => t('303 See Other'),
+      'description' => t('See Other/Redirect. A preferred alternative source should be used at present.'),
+    ),
+    304 => array(
+      'title' => t('304 Not Modified'),
+      'description' => t("The server has identified from the request information that the client's copy of the information is up-to-date and the requested information does not need to be sent again."),
+    ),
+    305 => array(
+      'title' => t('305 Use Proxy'),
+      'description' => t('The request must be sent through the indicated proxy server.'),
+    ),
+    307 => array(
+      'title' => t('307 Temporary Redirect'),
+      'description' => t('The resource has temporarily moved elsewhere, the response indicates where it is at present. Client should still use this URL.'),
+    ),
+  );
+}
+
