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"); }