| 1 |
#
|
| 2 |
# This patch introduces includes/lock.inc, a generic locking mechanism.
|
| 3 |
# It then wraps the administrative editing of nodes in a lock, preventing
|
| 4 |
# more than one administrator from making changes at the same time.
|
| 5 |
#
|
| 6 |
# To use, you will need to add the following database table:
|
| 7 |
#
|
| 8 |
# CREATE TABLE locks (
|
| 9 |
# name varchar(64) NOT NULL default '',
|
| 10 |
# microtime varchar(64) NOT NULL default '',
|
| 11 |
# ttl int NOT NULL default '0',
|
| 12 |
# status tinyint(1) unsigned NOT NULL default '0',
|
| 13 |
# quantity int(10) unsigned NOT NULL default '0',
|
| 14 |
# PRIMARY KEY (name),
|
| 15 |
# KEY (microtime),
|
| 16 |
# KEY (ttl),
|
| 17 |
# KEY (status),
|
| 18 |
# KEY (quantity)
|
| 19 |
# );
|
| 20 |
#
|
| 21 |
# Then apply this patch.
|
| 22 |
#
|
| 23 |
# Finally, with the patch applied and the database updated, enable content
|
| 24 |
# locking at "administer >> configuration >> modules >> node"
|
| 25 |
#
|
| 26 |
|
| 27 |
--- includes/common.inc.orig 2004-03-27 17:05:57.267404839 -0500
|
| 28 |
+++ includes/common.inc 2004-03-27 17:06:37.062143144 -0500
|
| 29 |
@@ -1225,6 +1225,7 @@ function drupal_xml_parser_create(&$data
|
| 30 |
include_once "includes/xmlrpc.inc";
|
| 31 |
include_once "includes/tablesort.inc";
|
| 32 |
include_once "includes/file.inc";
|
| 33 |
+include_once "includes/lock.inc";
|
| 34 |
|
| 35 |
// set error handler:
|
| 36 |
set_error_handler("error_handler");
|
| 37 |
--- modules/node.module.orig 2004-03-27 17:05:57.266404946 -0500
|
| 38 |
+++ modules/node.module 2004-03-27 17:06:37.061143251 -0500
|
| 39 |
@@ -578,6 +578,12 @@ function node_settings() {
|
| 40 |
$output .= form_select(t('Length of trimmed posts'), 'teaser_length', variable_get('teaser_length', 600), array(0 => t('Unlimited'), 200 => t('200 characters'), 400 => t('400 characters'), 600 => t('600 characters'), 800 => t('800 characters'), 1000 => t('1000 characters'), 1200 => t('1200 characters'), 1400 => t('1400 characters'), 1600 => t('1600 characters'), 1800 => t('1800 characters'), 2000 => t('2000 characters')), t("The maximum number of characters used in the trimmed version of a post. Drupal will use this setting to determine at which offset long posts should be trimmed. The trimmed version of a post is typically used as a teaser when displaying the post on the main page, in XML feeds, etc. To disable teasers, set to 'Unlimited'. Note that this setting will only affect new or updated content and will not affect existing teasers."));
|
| 41 |
$output .= form_radios(t('Preview post'), 'node_preview', variable_get('node_preview', 0), array(t('Optional'), t('Required')), t('Must users preview posts before submitting?'));
|
| 42 |
|
| 43 |
+ // locking:
|
| 44 |
+ // manually formatting time array so the seconds stay seconds...
|
| 45 |
+ $duration = array(0 => t("disabled"), 30 => t("30 sec"), 60 => t("1 min"), 120 => t("2 min"), 180 => t("3 min"), 240 => t("4 min"), 300 => t("5 min"), 600 => t("10 min"), 900 => t("15 min"), 1800 => t("30 min"), 3600 => t("1 hour"));
|
| 46 |
+ $group = form_select(t("Content locking"), "node_lock", variable_get("node_lock", 0), $duration, t("Content locking allows only one administrator at a time to modify any given piece of content. To enable, select how long an administrator can hold a lock on content. If the administrator takes longer than this amount of time to make changes to content, another administrator is allowed to come along and grab the lock, thereby preventing the first from saving changes."));
|
| 47 |
+ $output .= form_group(t("Lock settings"), $group);
|
| 48 |
+
|
| 49 |
return $output;
|
| 50 |
}
|
| 51 |
|
| 52 |
@@ -646,7 +652,76 @@ function node_admin_edit($node) {
|
| 53 |
$output .= implode("\n", module_invoke_all('node_link', $node));
|
| 54 |
|
| 55 |
return $output;
|
| 56 |
+}
|
| 57 |
|
| 58 |
+function node_lock($nid, $lock_type) {
|
| 59 |
+ if($lock->ttl = variable_get("node_lock", 0)) {
|
| 60 |
+ $name="lock_$nid";
|
| 61 |
+
|
| 62 |
+ switch ($lock_type) {
|
| 63 |
+ case "lock":
|
| 64 |
+ // get the lock and store the key in a session variable
|
| 65 |
+ if ($key = lock_lock($name, 0, $lock)) {
|
| 66 |
+ $_SESSION[$name] = $key;
|
| 67 |
+ }
|
| 68 |
+ else {
|
| 69 |
+ return 0;
|
| 70 |
+ }
|
| 71 |
+ break;
|
| 72 |
+ case "renew":
|
| 73 |
+ if($_SESSION[$name]) {
|
| 74 |
+ if($key = lock_renew($name, $_SESSION[$name], $lock)) {
|
| 75 |
+ // we still own the lock
|
| 76 |
+ $_SESSION[$name] = $key;
|
| 77 |
+ }
|
| 78 |
+ else {
|
| 79 |
+ // we don't own the lock
|
| 80 |
+ unset($_SESSION[$name]);
|
| 81 |
+ return 0;
|
| 82 |
+ }
|
| 83 |
+ }
|
| 84 |
+ // we can only try and renew if we have a key
|
| 85 |
+ else {
|
| 86 |
+ return 0;
|
| 87 |
+ }
|
| 88 |
+ break;
|
| 89 |
+ case "edit":
|
| 90 |
+ // if we already have a key, try and renew it
|
| 91 |
+ if ($_SESSION[$name]) {
|
| 92 |
+ // if we fail to renew, the key will be erased
|
| 93 |
+ node_lock($nid, "renew");
|
| 94 |
+ }
|
| 95 |
+ // if we don't have a key, try and get one
|
| 96 |
+ if(!$_SESSION[$name] && !node_lock($nid, "lock")) {
|
| 97 |
+ return 0;
|
| 98 |
+ }
|
| 99 |
+ break;
|
| 100 |
+ case "preview":
|
| 101 |
+ // if we already have a key, try and renew it
|
| 102 |
+ if($_SESSION[$name] && !node_lock($nid, "renew")) {
|
| 103 |
+ return 0;
|
| 104 |
+ }
|
| 105 |
+ // if we don't have a key, we shouldn't be here
|
| 106 |
+ elseif (!$_SESSION[$name]) {
|
| 107 |
+ return 0;
|
| 108 |
+ }
|
| 109 |
+ break;
|
| 110 |
+ case "submit":
|
| 111 |
+ case "delete":
|
| 112 |
+ // verify that our key is still valid
|
| 113 |
+ if ($_SESSION[$name] && !node_lock($nid, "renew")) {
|
| 114 |
+ return 0;
|
| 115 |
+ }
|
| 116 |
+ // display error if we don't have a key
|
| 117 |
+ elseif (!$_SESSION[$name]) {
|
| 118 |
+ return 0;
|
| 119 |
+ }
|
| 120 |
+ lock_unlock($name, $_SESSION[$name]);
|
| 121 |
+ unset($_SESSION[$name]);
|
| 122 |
+ break;
|
| 123 |
+ }
|
| 124 |
+ }
|
| 125 |
+ return 1;
|
| 126 |
}
|
| 127 |
|
| 128 |
function node_admin_nodes() {
|
| 129 |
@@ -947,20 +1022,46 @@ function node_admin() {
|
| 130 |
$output = search_type('node', url('admin/node/search'), $_POST['keys']);
|
| 131 |
break;
|
| 132 |
case 'edit':
|
| 133 |
- $output = node_admin_edit(arg(3));
|
| 134 |
+ if(node_lock(arg(3), "edit")) {
|
| 135 |
+ $output = node_admin_edit(arg(3));
|
| 136 |
+ }
|
| 137 |
+ else {
|
| 138 |
+ $output = node_display_lock_error("admin edit");
|
| 139 |
+ }
|
| 140 |
break;
|
| 141 |
case 'delete':
|
| 142 |
- $output = node_delete(array('nid' => arg(3)));
|
| 143 |
+ if(node_lock(arg(3), "delete")) {
|
| 144 |
+ $output = node_delete(array('nid' => arg(3)));
|
| 145 |
+ }
|
| 146 |
+ else {
|
| 147 |
+ $output = node_display_lock_error("admin delete");
|
| 148 |
+ }
|
| 149 |
break;
|
| 150 |
case t('Preview'):
|
| 151 |
- $edit = node_validate($edit, $error);
|
| 152 |
- $output = node_preview($edit, $error);
|
| 153 |
+ if(node_lock($edit['nid'], "preview")) {
|
| 154 |
+ $edit = node_validate($edit, $error);
|
| 155 |
+ $output = node_preview($edit, $error);
|
| 156 |
+ }
|
| 157 |
+ else {
|
| 158 |
+ $output = node_display_lock_error("admin preview");
|
| 159 |
+ }
|
| 160 |
break;
|
| 161 |
case t('Submit'):
|
| 162 |
- $output = node_submit($edit);
|
| 163 |
+ if(node_lock($edit['nid'], "submit")) {
|
| 164 |
+ $output = node_submit($edit);
|
| 165 |
+ }
|
| 166 |
+ else {
|
| 167 |
+ $output = node_display_lock_error("admin submit");
|
| 168 |
+ }
|
| 169 |
break;
|
| 170 |
case t('Delete'):
|
| 171 |
- $output = node_delete($edit);
|
| 172 |
+ // we are still previewing during this step, not yet deleting
|
| 173 |
+ if(node_lock($edit['nid'], "preview")) {
|
| 174 |
+ $output = node_delete($edit);
|
| 175 |
+ }
|
| 176 |
+ else {
|
| 177 |
+ $output = node_display_lock_error("admin delete");
|
| 178 |
+ }
|
| 179 |
break;
|
| 180 |
case t('Save configuration'):
|
| 181 |
case t('Reset to defaults'):
|
| 182 |
@@ -973,6 +1074,17 @@ function node_admin() {
|
| 183 |
print theme('page', $output);
|
| 184 |
}
|
| 185 |
|
| 186 |
+function node_display_lock_error($type) {
|
| 187 |
+ switch($type) {
|
| 188 |
+ case "admin edit":
|
| 189 |
+ return t("Temporary access failure. Another administrater is currently making changes to this content. Please try again later.");
|
| 190 |
+ case "admin preview":
|
| 191 |
+ case "admin submit":
|
| 192 |
+ case "admin delete":
|
| 193 |
+ return t("Your session has expired. Another administrator is currently making changes to this content. Please try again later.");
|
| 194 |
+ }
|
| 195 |
+}
|
| 196 |
+
|
| 197 |
function node_block($op = 'list', $delta = 0) {
|
| 198 |
|
| 199 |
if ($op == 'list') {
|
| 200 |
@@ -1255,7 +1367,7 @@ function node_add($type) {
|
| 201 |
$edit = $_POST['edit'];
|
| 202 |
|
| 203 |
/*
|
| 204 |
- ** If a node type has been specified, validate it existence. If no
|
| 205 |
+ ** If a node type has been specified, validate its existence. If no
|
| 206 |
** (valid) node type has been provided, display a node type overview.
|
| 207 |
*/
|
| 208 |
|
| 209 |
--- includes/lock.inc.orig 2004-03-27 17:07:45.005864995 -0500
|
| 210 |
+++ includes/lock.inc 2004-03-27 17:06:37.062143144 -0500
|
| 211 |
@@ -0,0 +1,286 @@
|
| 212 |
+<?php
|
| 213 |
+
|
| 214 |
+/* ***********************************
|
| 215 |
+ * external functions (lock API)
|
| 216 |
+ * ***********************************/
|
| 217 |
+
|
| 218 |
+/**
|
| 219 |
+ * This function allows you to (attempt to) grab a lock. If you succesfully
|
| 220 |
+ * grab a lock, you now own it and you can be sure that nobody else can get
|
| 221 |
+ * it until you free it (or it times out).
|
| 222 |
+ * This function is actually little more than a wrapper, allowing you to easily
|
| 223 |
+ * develop your own locking mechanisms if you so require. For example, you
|
| 224 |
+ * may wish to implement row-level locking, or file-based locking, neither of
|
| 225 |
+ * which is currently provided by this include file.
|
| 226 |
+ * The built in mechanisms are quite powerful, however, so the vast majority
|
| 227 |
+ * of users will not need to write their own locking mechanisms.
|
| 228 |
+ *
|
| 229 |
+ * Built in lock types:
|
| 230 |
+ * db - generic locking in a single database table
|
| 231 |
+ * db_rw - more complex read-many write-one locking (**currently disabled**)
|
| 232 |
+ *
|
| 233 |
+ * @param string $name the name of the lock
|
| 234 |
+ * @param string $block 0 = don't block (return immediately),
|
| 235 |
+ * 1 = block (don't return until we have the lock)
|
| 236 |
+ * @param object $attributes opptional items used by some types of lock
|
| 237 |
+ * @param string $type specify what type of locking mechanism to use
|
| 238 |
+ *
|
| 239 |
+ * @return int # = key to the lock we own; 0 = we do not own the lock
|
| 240 |
+ */
|
| 241 |
+function lock_lock($name, $block = 0, $attributes = NULL, $type = "db") {
|
| 242 |
+ $lock = "lock_$type"."_lock";
|
| 243 |
+ return $lock($name, $block, $attributes);
|
| 244 |
+}
|
| 245 |
+
|
| 246 |
+/**
|
| 247 |
+ * This function allows you to renew a lock if you own it. This is useful for
|
| 248 |
+ * lengthy interactive tasks with users. If renew lock returns with a value,
|
| 249 |
+ * you know that you still own the lock.
|
| 250 |
+ * This function is actually just a wrapper, allowing you to easily use the
|
| 251 |
+ * built in locking mechanisms, or even to develop your own. See the comments
|
| 252 |
+ * in 'lock_lock()' for more information.
|
| 253 |
+ *
|
| 254 |
+ * @param string $name the name of the lock
|
| 255 |
+ * @param int $key the key for the lock
|
| 256 |
+ * @param object $attributes opptional items used by some types of lock
|
| 257 |
+ * @param string $type specify what type of locking mechanism to use
|
| 258 |
+ *
|
| 259 |
+ * @return int # = new key to the renewed lock; 0 = we lost the lock
|
| 260 |
+ */
|
| 261 |
+
|
| 262 |
+function lock_renew($name, $key, $attributes = NULL, $type = "db") {
|
| 263 |
+ $lock = "lock_$type"."_renew";
|
| 264 |
+ return $lock($name, $key, $attributes);
|
| 265 |
+}
|
| 266 |
+
|
| 267 |
+/**
|
| 268 |
+ * This function allows you to check the status of a named lock. If the lock
|
| 269 |
+ * is held by you are another process, it returns 1. If the lock is not
|
| 270 |
+ * held by anyone, it returns 0.
|
| 271 |
+ * This function is actually just a wrapper, allowing you to easily use the
|
| 272 |
+ * built in locking mechanisms, or even to develop your own. See the comments
|
| 273 |
+ * in 'lock_lock()' for more information.
|
| 274 |
+ *
|
| 275 |
+
|
| 276 |
+ * @param string $name the name of the lock
|
| 277 |
+ * @param object $attributes opptional items used by some types of lock
|
| 278 |
+ * @param string $type specify what type of locking mechanism to use
|
| 279 |
+ *
|
| 280 |
+ * @return int 1 = lock is held; 0 = lock is not held
|
| 281 |
+ */
|
| 282 |
+function lock_is_locked($name, $attributes = NULL, $type = "db") {
|
| 283 |
+ $is_locked = "lock_$type"."_is_locked";
|
| 284 |
+ return $is_locked($name, $attributes);
|
| 285 |
+}
|
| 286 |
+
|
| 287 |
+/**
|
| 288 |
+ * This function allows you to unlock a lock. If it succeeds in unlocking the
|
| 289 |
+ * lock, it returns a 1. If it fails, it returns a 0.
|
| 290 |
+ * This function is actually just a wrapper, allowing you to easily use the
|
| 291 |
+ * built in locking mechanisms, or even to develop your own. See the comments
|
| 292 |
+ * in 'lock_lock()' for more information.
|
| 293 |
+ *
|
| 294 |
+ * @param string $name the name of the lock
|
| 295 |
+ * @param object $attributes opptional items used by some types of lock
|
| 296 |
+ * @param string $type specify what type of locking mechanism to use
|
| 297 |
+ *
|
| 298 |
+ * @return int 1 = succesfully unlocked lock; 0 = failed to unlock lock
|
| 299 |
+ */
|
| 300 |
+function lock_unlock($name, $key, $attributes = NULL, $type = "db") {
|
| 301 |
+ $unlock = "lock_$type"."_unlock";
|
| 302 |
+ return $unlock($name, $key, $attributes);
|
| 303 |
+}
|
| 304 |
+
|
| 305 |
+/* **********************************************
|
| 306 |
+ * internal functions (don't call directly)
|
| 307 |
+ * **********************************************/
|
| 308 |
+
|
| 309 |
+/**
|
| 310 |
+ * This is the default lock type. The locks are stored in a database table.
|
| 311 |
+ * You can optionally pass in the following attributes to control locking
|
| 312 |
+ * behavior:
|
| 313 |
+ * - ttl: after this number of seconds the lock will be automatically
|
| 314 |
+ * unlocked without an explicit call to lock_unlock(). This is
|
| 315 |
+ * primarily to handle the inconsistent nature of http. Defaults
|
| 316 |
+ * to 15 seconds.
|
| 317 |
+ * - timeout: if blocking while trying to obtain a lock, you can optionally
|
| 318 |
+ * specify in seconds how long before we give up. (otherwise
|
| 319 |
+ * we'll try forever until we get the lock)
|
| 320 |
+ *
|
| 321 |
+ * @param string $name the name of the lock
|
| 322 |
+ * @param string $block 1 = block, 0 = don't block
|
| 323 |
+ * @param object $attributes optional, defined above
|
| 324 |
+ *
|
| 325 |
+ * @return int 1 = we own the lock; 0 = we failed to obtain the lock
|
| 326 |
+ */
|
| 327 |
+function lock_db_lock($name, $block, $attributes) {
|
| 328 |
+ /* Due to the nature of http, it's very possible that someone will grab a lock
|
| 329 |
+ ** and then fail to free it. Because of this, we have to specify how long
|
| 330 |
+ ** the lock is valid before we determine it to be stale and unlock it.
|
| 331 |
+ */
|
| 332 |
+ if ($attributes->ttl) {
|
| 333 |
+ $ttl = $attributes->ttl;
|
| 334 |
+ }
|
| 335 |
+ else {
|
| 336 |
+ /* We were not explicitly told how long a lock is valid, so we're going to
|
| 337 |
+ ** default to a relatively sane value of 15 seconds. Depending on the
|
| 338 |
+ ** web application, this could be way too long, or way too short...
|
| 339 |
+ */
|
| 340 |
+ $ttl = 15;
|
| 341 |
+ }
|
| 342 |
+ if ($attributes->timeout) {
|
| 343 |
+ // blocking attempt to obtain a lock will timeout after this many seconds
|
| 344 |
+ $timeout = $attributes->timeout;
|
| 345 |
+ }
|
| 346 |
+ else {
|
| 347 |
+ // we'll never timeout...
|
| 348 |
+ $timeout = 0;
|
| 349 |
+ }
|
| 350 |
+ // initialize the loop counter, used to optionally timeout a lock attempt
|
| 351 |
+ $loop = 0;
|
| 352 |
+
|
| 353 |
+ // we enter a loop to try and obtain the lock
|
| 354 |
+ while (1) {
|
| 355 |
+ /* The first step in obtaining a lock is checking to see if it is already
|
| 356 |
+ ** held by another process. Even if the lock exists, it may be stale
|
| 357 |
+ ** in which case we need to unlock it. In other words, we can't skip this
|
| 358 |
+ ** step.
|
| 359 |
+ **/
|
| 360 |
+ if (!lock_db_is_locked($name, $attributes)) {
|
| 361 |
+ /* At this moment, we know that the lock is available, so we're going to
|
| 362 |
+ ** try and grab it...
|
| 363 |
+ */
|
| 364 |
+ $key = microtime();
|
| 365 |
+ if(@db_query("INSERT INTO {locks} VALUES('%s', '%s', %d, 1, 1)", $name, $key, $ttl)) {
|
| 366 |
+ /* The presence of a return code means that the database did not
|
| 367 |
+ ** return an error. In other words, we own the lock.
|
| 368 |
+ */
|
| 369 |
+ return $key;
|
| 370 |
+ }
|
| 371 |
+ else {
|
| 372 |
+ /* The absence of a return code means the database returned an error.
|
| 373 |
+ ** In other words, we failed to get the lock.
|
| 374 |
+ */
|
| 375 |
+ if (!$block) {
|
| 376 |
+ /* We were not told to block, so we need to exit reporting that we
|
| 377 |
+ ** failed to grab the lock.
|
| 378 |
+ */
|
| 379 |
+ return 0;
|
| 380 |
+ }
|
| 381 |
+ /* If we got here, we failed to grab the lock, but we were told to
|
| 382 |
+ ** block so we're going to sleep a moment and try again.
|
| 383 |
+ */
|
| 384 |
+ }
|
| 385 |
+ }
|
| 386 |
+ if (!$block) {
|
| 387 |
+ /* When checking the lock, we learned someone else already has it. We
|
| 388 |
+ ** were not told to block, so we need to exit reporting that we failed
|
| 389 |
+ ** to grab the lock.
|
| 390 |
+ */
|
| 391 |
+ return 0;
|
| 392 |
+ }
|
| 393 |
+ /* If we got here, we're still trying to grab the lock. Let's sleep for
|
| 394 |
+ ** second then we'll loop around and try again.
|
| 395 |
+ */
|
| 396 |
+ if (($timeout) && ($loop >= $timeout)) {
|
| 397 |
+ // We failed to obtain our lock in the allowed time.
|
| 398 |
+ return 0;
|
| 399 |
+ }
|
| 400 |
+ $loop++;
|
| 401 |
+ sleep(1);
|
| 402 |
+ }
|
| 403 |
+}
|
| 404 |
+
|
| 405 |
+/**
|
| 406 |
+ * This is the default db_renew function. If the lock still exists in the
|
| 407 |
+ * database, we update it and issue a new key.
|
| 408 |
+ *
|
| 409 |
+ * @param string $name the name of the lock
|
| 410 |
+ * @param int $key the key for the lock
|
| 411 |
+ * @param object $attributes optional
|
| 412 |
+ *
|
| 413 |
+ * @return int # = new key to the lock we own; 0 = we lost the lock
|
| 414 |
+ */
|
| 415 |
+function lock_db_renew($name, $key, $attributes) {
|
| 416 |
+ if ($attributes->ttl) {
|
| 417 |
+ $ttl = $attributes->ttl;
|
| 418 |
+ }
|
| 419 |
+ else {
|
| 420 |
+ $ttl = 15;
|
| 421 |
+ }
|
| 422 |
+ $newkey = microtime();
|
| 423 |
+ db_query("UPDATE {locks} SET microtime = '%s', ttl = %d WHERE name = '%s' and microtime = '%s'", $newkey, $ttl, $name, $key);
|
| 424 |
+ // Verify that we actually updated a database row.
|
| 425 |
+ if (db_affected_rows() == 1) {
|
| 426 |
+ // Great, our lock is still there, and we've successfully renewed it.
|
| 427 |
+ return ($newkey);
|
| 428 |
+ }
|
| 429 |
+ // Our lock is gone, we can't renew it.
|
| 430 |
+ return 0;
|
| 431 |
+}
|
| 432 |
+
|
| 433 |
+/**
|
| 434 |
+ * This is the default db is_locked function. It checks if the named lock
|
| 435 |
+ * is locked or not. It is very unlikely that you will ever need to call this
|
| 436 |
+ * function directly.
|
| 437 |
+ * You can pass in the following attributes:
|
| 438 |
+ *
|
| 439 |
+ * @param string $name the name of the lock
|
| 440 |
+ * @param object $attributes optional, defined above
|
| 441 |
+ *
|
| 442 |
+ * @return int 1 = the lock is locked; 0 = the lock is not locked
|
| 443 |
+ */
|
| 444 |
+function lock_db_is_locked($name, $attributes) {
|
| 445 |
+ // See if the lock exists in the database.
|
| 446 |
+ $lock = db_fetch_object(db_query("SELECT status,microtime,ttl FROM {locks} WHERE name = '%s'", $name));
|
| 447 |
+ if($lock->status == 1) {
|
| 448 |
+ // The lock is in the database, but it may be stale...
|
| 449 |
+ list($cur_micro, $cur_sec) = explode(" ", microtime());
|
| 450 |
+ list($old_micro, $old_sec) = explode(" ", $lock->microtime);
|
| 451 |
+ $old_sec = $old_sec + $lock->ttl;
|
| 452 |
+ if (($cur_sec > $old_sec) || ($cur_sec == $old_sec && $cur_micro > $old_micro)) {
|
| 453 |
+ /* The lock is stale, meaning that it's been in the database too long.
|
| 454 |
+ ** Pass in the unique tampstamp of the lock, and try to unlock it.
|
| 455 |
+ */
|
| 456 |
+ $key = $lock->microtime;
|
| 457 |
+ lock_db_unlock($name, $key, $attributes);
|
| 458 |
+ return 0;
|
| 459 |
+ }
|
| 460 |
+ return 1;
|
| 461 |
+ }
|
| 462 |
+ // the lock is not currently held
|
| 463 |
+ return 0;
|
| 464 |
+}
|
| 465 |
+
|
| 466 |
+/**
|
| 467 |
+ * This is the default db unlock function. It will attempt to unlock the named
|
| 468 |
+ * lock.
|
| 469 |
+ * It does not recognize any attributes at this time.
|
| 470 |
+ *
|
| 471 |
+ * @param string $name the name of the lock
|
| 472 |
+ * @param int $key the key to unlock (creation timestamp)
|
| 473 |
+ * @param object $attributes optional, defined above
|
| 474 |
+ *
|
| 475 |
+ * @return int 1 = the lock was unlocked; 0 = the lock was not unlocked
|
| 476 |
+ */
|
| 477 |
+function lock_db_unlock($name, $key, $attributes) {
|
| 478 |
+ /* Unlocking a lock is as simple as deleting it. (We want to do that rather
|
| 479 |
+ ** than simply changing the status as this logic is built around the theory
|
| 480 |
+ ** that you can only have one lock in the table by the same name.)
|
| 481 |
+ ** Note: We must delay the deletion of a lock to be sure whatever logic
|
| 482 |
+ ** required the lock in the first place is finished. Hence the sleep before
|
| 483 |
+ ** the delete.
|
| 484 |
+ */
|
| 485 |
+ db_query("DELETE FROM {locks} WHERE name = '%s' and microtime = '%s'", $name, $key);
|
| 486 |
+ if (db_error()) {
|
| 487 |
+ /* Our delete failed. Why? Probably because someone has already freed the
|
| 488 |
+ ** lock. In other words, it doesn't matter. But we'll leave it up to
|
| 489 |
+ ** whomever is actually using the lock to decide if this matters. We'll
|
| 490 |
+ ** return a 0 to say that we failed to unlock the lock. Most likely this
|
| 491 |
+ ** will be ignored.
|
| 492 |
+ */
|
| 493 |
+ return 0;
|
| 494 |
+ }
|
| 495 |
+ // We succesfully unlocked the lock.
|
| 496 |
+ return 1;
|
| 497 |
+}
|