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 .= '
'. t('Click here to check back in now. WARNING: 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 .= '
'. t('Click here 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");
}