Index: checkout.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/checkout/checkout.module,v
retrieving revision 1.7.2.4
diff -u -r1.7.2.4 checkout.module
--- checkout.module	2 Dec 2007 23:38:43 -0000	1.7.2.4
+++ checkout.module	5 Jan 2008 15:41:08 -0000
@@ -54,7 +54,10 @@
       'type' => MENU_CALLBACK);
   }
   else {
-    if (arg(0) == 'user' && is_numeric(arg(1)) && $user->uid == arg(1)) {
+    if (arg(0) == 'node' && is_numeric(arg(1)) && $user->uid) {
+      checkout_process($user->uid);
+    }
+    else if (arg(0) == 'user' && is_numeric(arg(1)) && $user->uid == arg(1)) {
       $user_access = user_access('check out documents');
       $items[] = array('path' => 'user/'. arg(1) .'/checkout',
         'title' => t('Track check-outs'),
@@ -88,7 +91,7 @@
           '#type' => 'checkbox',
           '#title' => t('Keep document checked out'),
           '#return_value' => 1,
-          '#weight' => 20.1,
+          '#weight' => 21,
           '#default_value' => FALSE,
           '#description' => t('Check this box if you wish to keep this document locked for editing after submit.'),
         );
@@ -114,21 +117,10 @@
  * Implementation of hook_nodeapi().
  */
 function checkout_nodeapi(&$node, $op, $teaser, $page) {
-  global $user;
-
   switch ($op) {
-    case 'prepare':
-      if ($node->nid > 0) {
-        $data = checkout_checkout($node->nid);
-        if ($data && $data->uid != $user->uid) {
-          checkout_message($data); // won't return
-        }
-      }
-      break;
-    
     case 'update':
       if (empty($node->checkout)) {
-        checkout_release($node->nid);
+        checkout_release_node($node->nid);
       }
       else {
         db_query("UPDATE {checkout} SET persistent = 1 WHERE nid = %d", $node->nid);
@@ -136,7 +128,7 @@
       break;
     
     case 'delete':
-      checkout_release($node->nid);
+      checkout_release_node($node->nid);
       break;
   }
 }
@@ -159,97 +151,118 @@
 }
 
 /**
- * Implementation of hook_exit().
+ * Check whether to (un)lock the current or previous node for the current user.
+ * The required action depends on the current and refering uris.
  *
- * Release the node that has been last edited, but only if we're coming
- * directly from an edit page to protect against unlocking nodes while
- * editing/surfing in concurrent browser windows.
- */
-function checkout_exit() {
-  global $user;
-
-  if ($user->uid) {
-    $edit_types = array('edit', 'classify', 'outline');
-    $request = array_reverse(explode('/', request_uri()));
-    $referer = array_reverse(explode('/', referer_uri()));
-  
-    if (!in_array($request[0], $edit_types) && in_array($referer[0], $edit_types)) {
-      $nid = $referer[1];
-      if (!db_result(db_query("SELECT persistent FROM {checkout} WHERE nid = %d", $nid))) {
-        checkout_release($nid);
-      }
+ * @param integer $uid
+ *   A user id to process node (un)locking for.
+ */
+function checkout_process($uid) {
+  // Build referer path
+  $referer_uri = parse_url(referer_uri());
+  if (variable_get('clean_url', 0)) {
+    $referer = $referer_uri['path'];
+  }
+  else {
+    $vars = array();
+    parse_str($referer_uri['query'], $vars);
+    $referer = $vars['q'];
+  }
+  if ($referer) {
+    $referer = drupal_get_normal_path(trim($referer, '/'));
+  }
+
+  $edit_paths = variable_get('checkout_edit_paths', "edit\nrevisions\nrevisions/*\noutline\nclassify");
+  $on_edit_page = checkout_match_path($_GET['q'], $edit_paths);
+  $coming_from_edit_page = checkout_match_path($referer, $edit_paths);
+
+  // Check whether to lock current node
+  if ($on_edit_page && !$coming_from_edit_page) {
+    // Try to lock the node
+    $redirect = checkout_lock_node(arg(1), $uid);
+    if ($redirect) {
+      // Node already locked: send back to refering page
+      drupal_goto(referer_uri());
+    }
+  }
+  // Check whether to release previous node
+  else if (!$on_edit_page && $coming_from_edit_page) {
+    // Get nid of refering page
+    $args = explode('/', $referer);
+    if (!db_result(db_query("SELECT persistent FROM {checkout} WHERE nid = %d", $args[1]))) {
+      checkout_release_node($args[1]);
     }
   }
 }
 
 /**
- * Lock a node for editing.
+ * Match a node path against some patterns.
  *
- * If the document isn't currently checked out the returned info object has
- * just one property: the user id of the current user. Otherwise it contains
- * the information about when and by whom the document was checked out.
+ * @param string $path
+ *   The path to match.
+ * @param string $patterns
+ *   Newline-delimited patterns. Supply only the part after node/123/.
+ *
+ * @return boolean
+ */
+function checkout_match_path($path, $patterns) {
+  static $regexp;
+
+  if (!isset($regexp)) {
+    $regexp = '/^node\/\d+\/('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/'), array('|', '.*'), preg_quote($patterns, '/')) .')$/';
+  }
+  return preg_match($regexp, $path);
+}
+
+/**
+ * Try to lock a node for editing.
  *
  * @param integer $nid
  *   A node id.
+ * @param integer $uid
+ *   The user id to lock the node for.
  *
- * @return object A check-out info object.
+ * @return boolean Whether the user should be redirected away from the current page.
  */
-function checkout_checkout($nid) {
-  global $user;
+function checkout_lock_node($nid, $uid) {
+  $redirect = FALSE;
 
   db_lock_table('checkout');
+  $result = db_query("SELECT uid, timestamp FROM {checkout} WHERE nid = %d", $nid);
 
-  $result = db_query("SELECT nid, uid, timestamp FROM {checkout} WHERE nid = %d", $nid);
   if (db_num_rows($result)) {
+    // Node already locked: display error message
     $info = db_fetch_object($result);
-  }
-  else if (user_access('check out documents')) {
-    db_query("INSERT INTO {checkout} (nid, uid, persistent, timestamp) VALUES (%d, %d, %d, %d)", $nid, $user->uid, 0, time());
-
-    $info = new stdClass;
-    $info->uid = $user->uid;
-  }
-  else {
-    $info = NULL;
-  }
-
-  db_unlock_tables();
-
-  return $info;
-}
+    db_unlock_tables();
 
-/**
- * Display an error message and send the user back to the node view.
- *
- * @param object $info
- *   A check-out info object.
- *
- * @return void This function doesn't return.
- *
- * @see checkout_checkout()
- */
-function checkout_message($info) {
-  $username = theme('username', user_load(array('uid' => $info->uid)));
-  $date = format_date($info->timestamp, 'medium');
-  $message = t('This document is locked for editing by !name since @date.', array('!name' => $username, '@date' => $date));
+    $username = theme('username', user_load(array('uid' => $info->uid)));
+    $date = format_date($info->timestamp, 'medium');
+    $message = t('This document is locked for editing by !name since @date.', array('!name' => $username, '@date' => $date));
+    if (user_access('administer checked out documents')) {
+      $url = url("admin/content/node/checkout/release/$nid", 'destination='. $_GET['q']);
+      $message .= '<br />'. t('Click <a href="!url">here</a> to check back in now. <strong>WARNING:</strong> Any unsaved changes in another window will be overwritten.', array('!url' => $url));
+    }
+    drupal_set_message($message, 'error');
 
-  if (user_access('administer checked out documents')) {
-    $uri = url("admin/content/node/checkout/release/$info->nid", 'destination='. request_uri());
-    $message .= '<br />'. t('Click <a href="!uri">here</a> to check back in now.', array('!uri' => $uri));
+    $redirect = TRUE;
+  }
+  else if (user_access('check out documents')) {
+    // Lock node
+    db_query("INSERT INTO {checkout} (nid, uid, timestamp) VALUES (%d, %d, %d)", $nid, $uid, time());
+    drupal_set_message(t('This document is now locked against simultaneous editing. It will unlock when you navigate elsewhere.'));
+    db_unlock_tables();
   }
 
-  drupal_set_message($message, 'error');
-
-  drupal_goto("node/$info->nid");
+  return $redirect;
 }
 
 /**
- * Release a node.
+ * Release a node lock.
  *
  * @param integer $nid
  *   A node id.
  */
-function checkout_release($nid) {
+function checkout_release_node($nid) {
   db_query('DELETE FROM {checkout} WHERE nid = %d', $nid);
 }
 
@@ -298,7 +311,7 @@
  * @return void This function doesn't return.
  */
 function checkout_admin_release($nid) {
-  checkout_release($nid);
+  checkout_release_node($nid);
   drupal_set_message(t('The editing lock has been released.'));
   drupal_goto('admin/content/node/checkout');
 }
@@ -351,7 +364,7 @@
  * @return void This function doesn't return.
  */
 function checkout_user_release($uid, $nid) {
-  checkout_release($nid);
+  checkout_release_node($nid);
   drupal_set_message(t('The editing lock has been released.'));
   drupal_goto("user/$uid/checkout");
 }

