From bba3baad93ff3a4b63198012f24b25effdce593c Mon Sep 17 00:00:00 2001
From: David Barbarisi <davidbarbarisi@gmail.com>
Date: Thu, 24 Mar 2011 17:22:02 -0400
Subject: [PATCH] initial attempt at a drupal 7 version of content_lock

---
 content_lock.admin.inc                             |    9 +-
 content_lock.info                                  |    9 +-
 content_lock.install                               |    9 -
 content_lock.module                                |  266 +++++++++++---------
 js/content_lock_init.js                            |    5 -
 .../content_lock_timeout/content_lock_timeout.info |    2 +-
 6 files changed, 166 insertions(+), 134 deletions(-)

diff --git a/content_lock.admin.inc b/content_lock.admin.inc
index 13f893d..7c2e81f 100644
--- a/content_lock.admin.inc
+++ b/content_lock.admin.inc
@@ -7,6 +7,7 @@
  * Providing an administration interface for content_lock.
  */
 function content_lock_admin_settings() {
+/*
   $form['content_lock_unload_js'] = array(
       '#type' => 'checkbox',
       '#title' => t('Use javascript to detect leaving the node form'),
@@ -19,6 +20,12 @@ function content_lock_admin_settings() {
       '#description' => t('This option allows you to modify the text the user sees when navigating away from an edited node.'),
       '#default_value' => variable_get('content_lock_unload_js_message', 'If you proceed, ALL of your changes will be lost.'),
   );
+*/
+  /**
+   * @todo Add the unload JS back in.
+   */
+  variable_set('content_lock_unload_js', FALSE);
+  variable_set('content_lock_unload_js_message', 'If you proceed, ALL of your changes will be lost.');
 
   $form['content_lock_admin_verbose'] = array(
       '#type' => 'checkbox',
@@ -38,7 +45,7 @@ function content_lock_admin_settings() {
     '#type' => 'checkboxes',
     '#title' => t('Lockable content types'),
     '#description' => t('Apply lock to the selected content types. Leave blank for all node types to be lockable.'),
-    '#options' => node_get_types('names'),
+    '#options' => node_type_get_names(),
     '#default_value' => variable_get('content_lock_allowed_node_types', array()),
   );
 
diff --git a/content_lock.info b/content_lock.info
index 3209d0f..89033a2 100644
--- a/content_lock.info
+++ b/content_lock.info
@@ -1,5 +1,10 @@
 name = "Content locking (edit lock)"
 description = "Prevents multiple users from trying to edit a single node simultaneously to prevent edit conflicts."
-core = 6.x
+core = 7.x
 project = "content_lock"
-package = "Drupal Wiki"
+package = "Content Locking"
+files[] = content_lock.module
+files[] = content_lock.install
+files[] = content_lock.admin.inc
+dependencies[] = node
+configure = admin/config/content/content_lock
diff --git a/content_lock.install b/content_lock.install
index b5e3d10..3b56dc9 100644
--- a/content_lock.install
+++ b/content_lock.install
@@ -1,18 +1,9 @@
 <?php
 
 /**
- * Implementation of hook_install().
- */
-function content_lock_install() {
-  drupal_install_schema('content_lock');
-}
-
-/**
  * Implementation of hook_uninstall().
  */
 function content_lock_uninstall() {
-  drupal_uninstall_schema('content_lock');
-
   variable_del('content_lock_clear');
   variable_del('content_lock_unload_js');
   variable_del('content_lock_unload_js_message');
diff --git a/content_lock.module b/content_lock.module
index ec96b31..8480d1c 100644
--- a/content_lock.module
+++ b/content_lock.module
@@ -6,10 +6,19 @@
  */
 
 /**
- * Implementation of hook_perm().
+ * Implements hook_permission().
  */
-function content_lock_perm() {
-  return array('check out documents', 'administer checked out documents');
+function content_lock_permission() {
+  return array(
+    'check out documents' => array(
+      'title' => t('Check out Documents'),
+      'description' => t('Lock document editing.'),
+    ),
+    'administer checked out documents' => array(
+      'title' => t('Administer checked out documents'),
+      'description' => t('Administer locked doecuments.'),
+    ),
+  );
 }
 
 /**
@@ -35,13 +44,12 @@ function content_lock_help($path, $arg) {
  * Implementation of hook_menu().
  */
 function content_lock_menu() {
-  $items['admin/content/node/content_lock'] = array(
+  $items = array();
+  $items['admin/config/content/content_lock_documents'] = array(
     'title' => 'Locked documents',
     'page callback' => 'content_lock_overview',
-    'access callback' => 'user_access',
     'access arguments' => array('administer checked out documents'),
-    'weight' => 5,
-    'type' => MENU_LOCAL_TASK,
+    'type' => MENU_NORMAL_ITEM,
   );
   $items['admin/content/node/content_lock/release'] = array(
     'page callback' => 'content_lock_release_item',
@@ -69,7 +77,7 @@ function content_lock_menu() {
     'page arguments' => array(2,false),
     'access callback' => true
   );
-  $items['admin/settings/content_lock'] = array (
+  $items['admin/config/content/content_lock'] = array (
     'type' => MENU_NORMAL_ITEM,
     'title' => 'Content lock',
     'description' => 'Configuration options for the Content lock module',
@@ -83,57 +91,65 @@ function content_lock_menu() {
 }
 
 /**
- * Implementation of hook_nodeapi().
+ * Implements hook_node_validate().
  */
-function content_lock_nodeapi(&$node, $op, $teaser, $page) {
+function content_lock_node_validate($node, $form, &$form_state) {
   global $user;
-  static $messages_shown = false;
-
-  switch ($op) {
-    case 'validate':
-      if (isset($node->nid) && _content_lock_is_lockable_node($node) && user_access('check out documents')) {
-        // Existing node. Check if we still own the lock.
-        if ($lock = content_lock_fetch_lock($node->nid)) {
-          if ($lock->uid != $user->uid) {
-            // Lock is no longer ours.
-            form_set_error('changed', t('Your lock has been removed!') .'<br />'. content_lock_lock_owner($lock) .'<br />'. t('You can still save the content if this user aborts the edit operation without saving changes.'));
-          }
-        }
-        else {
-          // Node is not locked. Try to re-lock if node is unchanged.
-          if (node_last_changed($node->nid) > $node->changed || !content_lock_node($node->nid, $user->uid)) {
-            form_set_error('alsochanged', t('Your lock has been removed due to inactivity or by an administrator. Failed to regain the lock since the document has been changed since.'));
-          }
-        }
-      }
-      break;
-    case 'update':
-      if (_content_lock_is_lockable_node($node)) {
-        content_lock_release($node->nid, $user->uid);
+  if (isset($node->nid) && _content_lock_is_lockable_node($node) && user_access('check out documents')) {
+    // Existing node. Check if we still own the lock.
+    if ($lock = content_lock_fetch_lock($node->nid)) {
+      if ($lock->uid != $user->uid) {
+        // Lock is no longer ours.
+        form_set_error('changed', t('Your lock has been removed!') .'<br />'. content_lock_lock_owner($lock) .'<br />'. t('You can still save the content if this user aborts the edit operation without saving changes.'));
       }
-      break;
-    case 'delete':
-      content_lock_release($node->nid, NULL);
-      break;
-    case 'view':
-      if (!_content_lock_is_lockable_node($node)) {
-        break;
+    }
+    else {
+      // Node is not locked. Try to re-lock if node is unchanged.
+      if (node_last_changed($node->nid) > $node->changed || !content_lock_node($node->nid, $user->uid)) {
+        form_set_error('alsochanged', t('Your lock has been removed due to inactivity or by an administrator. Failed to regain the lock since the document has been changed since.'));
       }
+    }
+  }
+}
 
-      if(!$messages_shown) {
-        _content_lock_show_warnings();
-        $messages_shown = true;
-      }
+/**
+ * Implements hook_node_update().
+ */
+function content_lock_node_update($node) {
+  global $user;
+  if (_content_lock_is_lockable_node($node)) {
+    content_lock_release($node->nid, $user->uid);
+  }
+}
 
-      if($node->build_mode != NODE_BUILD_PREVIEW) {
-        content_lock_warn_pending_locks($user->uid); // check if the user has pending locks and warn him
-      }
-      break;
+/**
+ * Implements hook_node_delete().
+ */
+function content_lock_node_delete($node) {
+  content_lock_release($node->nid, NULL);
+}
+
+/**
+ * Implements hook_node_view().
+ */
+function content_lock_node_view($node, $view_mode, $langcode) {
+  global $user;
+  static $messages_shown = false;
+
+  if (_content_lock_is_lockable_node($node)) {
+    if(!$messages_shown) {
+      _content_lock_show_warnings();
+      $messages_shown = true;
+    }
+
+    if(empty($node->in_preview)) {
+      content_lock_warn_pending_locks($user->uid); // check if the user has pending locks and warn him
+    }
   }
 }
 
 /**
- * Implementation of hook_form_alter().
+ * Implements hook_form_alter().
  */
 function content_lock_form_alter(&$form, &$form_state, $form_id) {
   global $user;
@@ -146,22 +162,22 @@ function content_lock_form_alter(&$form, &$form_state, $form_id) {
   // initialize it here.
   // Only touch node edit forms:
   if (is_object($node) && is_numeric($nid) && $node->type . '_node_form' == $form_id) {
-      $old_format = $node->format;
-      if (!empty($node->content_lock_old_format)) {
-	$old_format = $node->content_lock_old_format;
-      }
-      if (!empty($form_state['values']['content_lock_old_format'])) {
-	$old_format = $form_state['values']['content_lock_old_format'];
-      }
-      // Needs to be manually set before first form submission.
-      // We set this in the $node-> namespace because content_lock_nodeapi()
-      // doesn't see $form_state['values'].
-      $node->content_lock_old_format = $old_format;
-
-      $form['content_lock_old_format'] = array(
-        '#type' => 'hidden',
-        '#value' => $node->content_lock_old_format,
-      );
+    $old_format = $node->body[$node->language][0]['format'];
+    if (!empty($node->content_lock_old_format)) {
+      $old_format = $node->content_lock_old_format;
+    }
+    if (!empty($form_state['values']['content_lock_old_format'])) {
+      $old_format = $form_state['values']['content_lock_old_format'];
+    }
+    // Needs to be manually set before first form submission.
+    // We set this in the $node-> namespace because content_lock_nodeapi()
+    // doesn't see $form_state['values'].
+    $node->content_lock_old_format = $old_format;
+
+    $form['content_lock_old_format'] = array(
+      '#type' => 'hidden',
+      '#value' => $node->content_lock_old_format,
+    );
   }
 
   /** ******************* General preconditions for locking ***************** */
@@ -204,11 +220,11 @@ function content_lock_form_alter(&$form, &$form_state, $form_id) {
       }
       // else if($form_state['submitted'] === TRUE)
       // if it is a submission, we would not need to lock once again, as we had before.
-      // as nodeapi insert/update are not called on preview, the node should stay locked until saved or canceled.  
+      // as nodeapi insert/update are not called on preview, the node should stay locked until saved or canceled.
   }
 }
 
-/* 
+/**
  *  Implementation of our own skip_locking api to implement our logic to skip locks
  */
 function content_lock_content_lock_skip_locking($node, $form_id, $form, $form_state) {
@@ -219,9 +235,9 @@ function content_lock_content_lock_skip_locking($node, $form_id, $form, $form_st
   $node_type_blacklist = array(
     'user' => TRUE   // we are not allowed to lock on users form edit, as it always returns to the edit form..
   );
-  
+
   // Form ids listed here will not be locked
-  $form_id_blacklist = array( 
+  $form_id_blacklist = array(
     'comment_form' => TRUE,    // dont lock on comment forms
   );
 
@@ -242,18 +258,19 @@ function content_lock_content_lock_skip_locking($node, $form_id, $form, $form_st
     // Preconditions failed, skip the lock
     return TRUE;
   }
-  
+
   // Check if the current node type and format type is configured to be locked
   // $node->content_lock_old_format has been set in content_lock_form_alter().
   if (!_content_lock_is_lockable_node($node)) {
     // It should not be locked, so skip the lock
     return TRUE;
   }
-  
+
   // we have no veto, so lock the node
   return FALSE;
 }
 
+/*
 function _content_lock_add_unload_js(&$form, $form_state) {
   $m = drupal_get_path('module','content_lock');
   drupal_add_js("$m/js/jquery.url.packed.js",'module');
@@ -263,6 +280,7 @@ function _content_lock_add_unload_js(&$form, $form_state) {
   drupal_add_js(array('content_lock' => array('nid' => $form['#node']->nid, 'ajax_key' => $lock->ajax_key, 'unload_js_message' => variable_get('content_lock_unload_js_message', 'If you proceed, ALL of your changes will be lost.'))), 'setting');
   return $form;
 }
+*/
 
 function _content_lock_verbose() {
   return variable_get('content_lock_admin_verbose', true);
@@ -284,7 +302,7 @@ function _content_lock_add_cancelbutton(&$form, $form_state, $form_id) {
         '#value' => t('Cancel'),
         '#validate' => array('content_lock_cancel_submit'),
       );
-    
+
       if (isset($form['buttons']['delete'])) {
         $form['buttons']['delete']['#weight'] = 2001;
       }
@@ -309,7 +327,7 @@ function content_lock_cancel_submit(&$form, &$form_state) {
  *   The lock for the node. FALSE, if the document is not locked.
  */
 function content_lock_fetch_lock($nid) {
-  return db_fetch_object(db_query("SELECT c.*, u.name FROM {content_lock} c LEFT JOIN {users} u ON u.uid = c.uid WHERE c.nid = %d", $nid));
+  return db_query("SELECT c.*, u.name FROM {content_lock} c LEFT JOIN {users} u ON u.uid = c.uid WHERE c.nid = :nid", array(':nid' => $nid))->fetchObject();
 }
 
 /**
@@ -321,18 +339,17 @@ function content_lock_fetch_lock($nid) {
  *   String with the message.
  */
 function content_lock_lock_owner($lock) {
-  $username = theme('username', $lock);
+  $username = theme('username', array(user_load($lock->uid)));
   $date = format_date($lock->timestamp, 'medium');
   return t('This document is locked for editing by !name since @date.', array('!name' => $username, '@date' => $date));
 }
 
-function content_lock_user($op, $edit, $account) {
-  switch($op) {
-    case 'logout':
-      // removing all locks, as the user logs out
-      _content_lock_release_all_user_locks($account->uid);
-    break;
-  }
+/**
+ * Implements hook_user_logout().
+ * Removes all locks as the user logs out.
+ */
+function content_lock_user_logout($account) {
+  _content_lock_release_all_user_locks($account->uid);
 }
 
 /**
@@ -358,12 +375,12 @@ function content_lock_node($nid, $uid, $quiet = FALSE) {
     if (user_access('administer checked out documents')) {
       $url = "admin/content/node/content_lock/release/$nid";
     }
-    
+
     if (isset($url)) {
       $message .= '<br />'. t('Click <a href="!release-url">here</a> to check back in now.', array('!release-url' => url($url, array('query' => 'destination='. $_GET['q']))));
     }
     if(!empty($message)) {
-      drupal_set_message($message, 'error');
+      drupal_set_message(filter_xss($message), 'error');
     }
     return FALSE;
   }
@@ -374,7 +391,7 @@ function content_lock_node($nid, $uid, $quiet = FALSE) {
       $data = array(
         'nid' => $nid,
         'uid' => $uid,
-        'timestamp' => time(),
+        'timestamp' => REQUEST_TIME,
         'ajax_key' => rand(),
       );
       drupal_write_record(
@@ -390,16 +407,15 @@ function content_lock_node($nid, $uid, $quiet = FALSE) {
       /* A lock already exists: update its AJAX key */
       $lock->ajax_key = rand();
       if (!drupal_write_record('content_lock', $lock, array('nid'))) {
-	/*
-	 * we encountered a race condition where the lock was deleted
-	 * between when we loaded it and when we tried to update it
-	 * with a new key. Recreate the lock then:
-	 */
-	drupal_write_record('content_lock', $lock);
+        /*
+         * we encountered a race condition where the lock was deleted
+         * between when we loaded it and when we tried to update it
+         * with a new key. Recreate the lock then:
+         */
+        drupal_write_record('content_lock', $lock);
       }
     }
   }
-
   return TRUE;
 }
 
@@ -411,19 +427,17 @@ function content_lock_node($nid, $uid, $quiet = FALSE) {
  * @param $uid
  *   If set, verify that a lock belongs to this user prior to release.
  */
-function content_lock_release($nid, $uid = NULL) {
-  $add_sql = '';
-  $args = array($nid);
+function content_lock_release($nid, $uid = NULL) {  
+  $query = db_delete('content_lock')->condition('nid', $nid);
   if (isset($uid)) {
-    $add_sql = " AND uid = %d";
-    $args[] = $uid;
+    $query->condition('uid', $uid);
   }
-  db_query("DELETE FROM {content_lock} WHERE nid = %d". $add_sql, $args);
+  $query->execute();  
   module_invoke_all('content_lock_released', $nid);
 }
 
 function _content_lock_release_all_user_locks($uid) {
-  db_query("DELETE FROM {content_lock} WHERE uid = %d", $uid);
+  db_delete('content_lock')->condition('uid', $uid)->execute();
 }
 
 /**
@@ -445,21 +459,35 @@ function content_lock_overview($account = NULL) {
   else {
     $uid = $account->uid;
   }
-  $header[] = array('data' => t('Locked since'), 'field' => 'c.timestamp');
+  $header[] = array('data' => t('Locked since'), 'field' => 'cl.timestamp');
   if($uid == $user->uid || user_access('administer checked out documents')) {
     $header[] = t('Operations');
   }
 
   $rows = array();
-  $add_sql = $uid ? " WHERE c.uid = %d" : '';
-  $result = pager_query('SELECT c.*, n.title, u.name FROM {content_lock} c INNER JOIN {node} n ON n.nid = c.nid INNER JOIN {users} u ON u.uid = c.uid'. $add_sql . tablesort_sql($header), 50, 0, NULL, $uid);
+//  $add_sql = $uid ? " WHERE c.uid = %d" : '';
+//  $result = pager_query('SELECT c.*, n.title, u.name FROM {content_lock} c INNER JOIN {node} n ON n.nid = c.nid INNER JOIN {users} u ON u.uid = c.uid'. $add_sql . tablesort_sql($header), 50, 0, NULL, $uid);
 
-  while ($data = db_fetch_object($result)) {
+  $query = db_select('content_lock', 'cl')->extend('PagerDefault')->extend('TableSort');
+  $query->innerJoin('node', 'n', 'n.nid = cl.nid');
+  $query->fields('cl', array());
+  $query->fields('n', array('title', 'uid'));
+  
+  if (!empty($uid)) {
+    $query->condition('cl.uid', $uid); // Only load nodes with the type page.
+  }
+
+  $result = $query
+    ->limit(50)
+    ->orderByHeader($header)
+    ->execute();
+
+  foreach ($result as $data) {
     $url = $uid ? "admin/content/".$data->nid."/content_lock/releaseown" : 'admin/content/node/content_lock/release/'.$data->nid;
     $row = array();
     $row[] = l($data->title, "node/$data->nid");
     if (!$uid) {
-      $row[] = theme('username', user_load(array('uid' => $data->uid)));
+      $row[] = theme('username', array('account' => user_load($data->uid)));
     }
     $row[] = format_date($data->timestamp, 'small');
     if($uid == $user->uid || user_access('administer checked out documents')) {
@@ -468,11 +496,17 @@ function content_lock_overview($account = NULL) {
     $rows[] = $row;
   }
 
-  $output = theme('table', $header, $rows, array('id' => 'content_lock'));
+  $variables = array(
+    'header' => $header,
+    'rows' => $rows,
+    'attributes' => array('id' => 'content_lock'),
+  );
+  $output = theme('table', $variables);
+
   if (!$rows) {
     $output .= t('No locked documents.');
   }
-  else if ($pager = theme('pager', array(), 50, 0)) {
+  else if ($pager = theme('pager', array())) {
     $output .= $pager;
   }
 
@@ -516,8 +550,8 @@ function content_lock_warn_pending_locks($uid) {
   else {
     // load form db
     $warned_nodes[$uid] = array();
-    $result = db_query("SELECT cl.nid,n.title FROM {content_lock} as cl LEFT JOIN {node} as n on cl.nid=n.nid WHERE cl.uid = %d", $uid);
-    while($lock = db_fetch_object($result)) {
+    $result = db_query("SELECT cl.nid,n.title FROM {content_lock} cl LEFT JOIN {node} n ON cl.nid = n.nid WHERE cl.uid = :uid", array(':uid' => $uid));
+    foreach ($result as $lock) {
       $warned_nodes[$uid][] = $lock;
     }
   }
@@ -558,14 +592,14 @@ function _content_lock_show_warnings() {
   }
   foreach($data as $nid => $messsage) {
     if(_content_lock_still_locked($user->uid,$nid) > 0){
-      drupal_set_message($messsage,'warning');
+      drupal_set_message(filter_xss($messsage),'warning');
     }
   }
   $_SESSION['content_lock'] = '';
 }
 
 function _content_lock_still_locked($uid,$nid) {
-  $result = db_result(db_query("SELECT count(cl.nid) FROM {content_lock} as cl  WHERE cl.uid = %d AND cl.nid=%d", $uid,$nid));
+  $result = db_query("SELECT COUNT(cl.nid) FROM {content_lock} cl WHERE cl.uid = :uid AND cl.nid = :nid", array(':uid' => $uid, ':nid' => $nid))->fetchField();
   return $result;
 }
 
@@ -591,12 +625,12 @@ function content_lock_release_own_item($nid, $response = true) {
     if (!$response) {
       $lock = content_lock_fetch_lock($nid);
       if (strcmp($_GET['k'], $lock->ajax_key)) {
-	/* the key doesn't match, don't unlock the node */
-	if ($response) {
-	  drupal_set_message('Trounced AJAX unlock requeset.');
-	}
-	exit();
-	}
+  /* the key doesn't match, don't unlock the node */
+  if ($response) {
+    drupal_set_message('Trounced AJAX unlock requeset.');
+  }
+  exit();
+  }
     }
     content_lock_release($nid,$user->uid);
    // drupal_get_messages();
@@ -632,7 +666,7 @@ function content_lock_content_lock_node_lockable($node) {
 
   // To catch the case where the user is changing the input format,
   // we store the original input format.
-  $format = $node->format;
+  $format = $node->body[$node->language][0]['format'];
   if (!empty($node->content_lock_old_format)) {
     $format = $node->content_lock_old_format;
   }
diff --git a/js/content_lock_init.js b/js/content_lock_init.js
index 35a328c..e7f0f2b 100644
--- a/js/content_lock_init.js
+++ b/js/content_lock_init.js
@@ -1,8 +1,3 @@
-/**
- * @file
- *   Initialize onUnload scripts.
- */
-
 (function($) {
   window.content_lock_onleave = function  () {
     var nid = Drupal.settings.content_lock.nid;
diff --git a/modules/content_lock_timeout/content_lock_timeout.info b/modules/content_lock_timeout/content_lock_timeout.info
index 161256c..703d12c 100644
--- a/modules/content_lock_timeout/content_lock_timeout.info
+++ b/modules/content_lock_timeout/content_lock_timeout.info
@@ -2,5 +2,5 @@ name = "Content locking (edit lock) timeouts"
 description = "Provides mechanisms for automatically unlocking nodes that have been locked for a certain length of time."
 core = 6.x
 project = "content_lock"
-package = "Drupal Wiki"
+package = "Content Locking"
 dependencies[] = content_lock
-- 
1.7.3.4

