# This patch file was generated by NetBeans IDE
# It uses platform neutral UTF-8 encoding and \n newlines.
--- Base (BASE)
+++ Locally Modified (Based On LOCAL)
@@ -6,7 +6,10 @@
 
 define('USERPOINTS_POST',                'userpoints_post_');
 define('USERPOINTS_POST_COMMENT',        'userpoints_post_comment');
+define('USERPOINTS_POST_UNDO_ON_DELETE', 'userpoints_post__undo_points_on_delete');
 define('USERPOINTS_MODERATE_COMMENT',    'userpoints_moderate_comment');
+define('USERPOINTS_NODE_WORDS',          'userpoints_node_words');
+define('USERPOINTS_COMMENT_WORDS',       'userpoints_comment_words');
 //define a variable to trigger the use of the v2bug please read drupal.org/node/183520
 define('USERPOINTS_USE_V2BUG',    'userpoints_use_v2bug');
 
@@ -28,12 +31,20 @@
         '#title'       => t('!Points for posting nodes', userpoints_translation()),
       );
       
-      $form[$group][USERPOINTS_POST . '_undo_points_on_delete'] = array(
+      $form[$group][USERPOINTS_POST_UNDO_ON_DELETE] = array(
         '#type'          => 'checkbox',
         '#title'         => t('Take away !points on node delete', array_merge(userpoints_translation())),
-        '#default_value' => variable_get(USERPOINTS_POST . '_undo_points_on_delete', true),
+        '#default_value' => variable_get(USERPOINTS_POST_UNDO_ON_DELETE, TRUE),
         );
         
+      $form[$group][USERPOINTS_NODE_WORDS] = array(
+        '#type'          => 'textfield',
+        '#title'         => t('Minimum number of words required to earn !Points for posting a node', userpoints_translation()),
+        '#default_value' => variable_get(USERPOINTS_NODE_WORDS, 1),
+        '#size'          => 5,
+        '#maxlength'     => 5,
+        );
+            
       foreach (node_get_types() as $type => $name) {
         $form[$group][USERPOINTS_POST . $type] = array(
           '#type'          => 'textfield',
@@ -43,6 +54,7 @@
           '#maxlength'     => 5,
           );
       }
+      
       $group = 'comment';
       $form[$group] = array(
         '#type'        => 'fieldset',
@@ -50,6 +62,15 @@
         '#collapsed'   => TRUE,
         '#title'       => t('!Points for posting comments', userpoints_translation()),
       );
+
+      $form[$group][USERPOINTS_COMMENT_WORDS] = array(
+        '#type'          => 'textfield',
+        '#title'         => t('Minimum number of words required to earn !Points for posting a comment', userpoints_translation()),
+        '#default_value' => variable_get(USERPOINTS_COMMENT_WORDS, 1),
+        '#size'          => 5,
+        '#maxlength'     => 5,
+        );
+      
       $form[$group][USERPOINTS_POST_COMMENT] = array(
         '#type'          => 'textfield',
         '#title'         => t('!Points for posting a comment', userpoints_translation()),
@@ -65,6 +86,7 @@
         '#size'          => 5,
         '#maxlength'     => 5,
         );
+      
       $group = 'v2compatibility';
       $form[$group] = array(
         '#type'        => 'fieldset',
@@ -97,210 +119,241 @@
   }
 }
   
+/**
+ * Implementation of hook_nodeapi
+ */
 function userpoints_nodeapi(&$node, $op, $teaser, $page) {
-  //static up_orig_uid please read drupal.org/node/183520 
+  
+  // Static up_orig_uid used by v2 bugfix. See http://drupal.org/node/183520
   static $up_orig_uid;
+  
+  // Get points for this node type
   $points = variable_get(USERPOINTS_POST . $node->type, 0);
+  
   switch ($op) {
     case 'insert':
-      $params = array(
-        'points' => $points,
-        'uid' => $node->uid,
-        'operation' => 'insert',
-        'entity_id' => $node->nid,
-        'entity_type' => 'node'
-      );
-      userpoints_userpointsapi($params);
+      userpoints_nc_entity_insert($node->nid, 'node', $node->uid, $points, $node->body);
       break;
     case 'delete':
-      if (variable_get(USERPOINTS_POST . '_undo_points_on_delete', true)) {
-        $points = -$points;
-        $params = array(
-          'points' => $points,
-          'uid' => $node->uid,
-          'operation' => 'operation',
-          'entity_id' => $node->nid,
-          'entity_type' => 'node',
-        );
-        userpoints_userpointsapi($params);
+      if (variable_get(USERPOINTS_POST_UNDO_ON_DELETE, TRUE)) {
+        userpoints_nc_entity_delete($node->nid, 'node', $node->uid);
+        // TODO: delete points for comments on this node here. See http://drupal.org/node/1216048
       }
       break;
     case 'prepare':
       $up_orig_uid = $node->uid;
       break;
     case 'update':
-      //Find the last points granted on this node inserts and ownership gains
-      $sql = "SELECT points, uid 
-              FROM {userpoints_txn} 
-              WHERE entity_id = %d AND entity_type = '%s' 
-              AND (operation = '%s' OR operation ='%s')
-              ORDER BY time_stamp DESC
-              LIMIT 1
-              ";
-      $last_owner = db_fetch_object(db_query($sql, $node->nid, 'node', 'insert', 'Ownership gain'));
-      
-      //Check the UID of the original to this user, if different add/substract points
-      if ($node->uid != $last_owner->uid && is_numeric($last_owner->uid) ) {
-        //Check to see if this user has already lost the points for
-        // Add to the new node owner
-        $params = array(
-          'points' => $points,
-          'uid' => $node->uid,
-          'operation' => 'Ownership gain',
-          'entity_id' => $node->nid,
-          'entity_type' => 'node'
-        );
-        userpoints_userpointsapi($params);
-        // subtract from the original node owner
-        $params = array(
-          'points' => -$points,
-          'uid' => $up_orig_uid,
-          'operation' => 'Ownership loss',
-          'entity_id' => $node->nid,
-          'entity_type' => 'node'
-        );
-        userpoints_userpointsapi($params);
-      }
-      else {
-        //We failed to pull a matching operation via the DB
-        //If the user wants to use the V2BUG we'll use it..
-        //please read drupal.org/node/183520
-        if (variable_get(USERPOINTS_USE_V2BUG, false)) {
-          if ($node->uid != $up_orig_uid) {
-            // Add to the new node owner
-            $params = array(
-              'points' => $points,
-              'uid' => $node->uid,
-              'operation' => 'Ownership gain',
-              'entity_id' => $node->nid,
-              'entity_type' => 'node'
-            );
-            userpoints_userpointsapi($params);
-            // subtract from the original node owner
-            $params = array(
-              'points' => -$points,
-              'uid' => $up_orig_uid,
-              'operation' => 'Ownership loss',
-              'entity_id' => $node->nid,
-              'entity_type' => 'node'
-            );
-            userpoints_userpointsapi($params);
-          }
-        }
-      }
+      userpoints_nc_entity_update($node->nid, 'node', $node->uid, $up_orig_uid, $node->body, $points);
       break;
   }
 }
 
+/**
+ * Implementation of hook_comment
+ */
 function userpoints_comment($comment, $op) {
-  global $user;
-  //static up_orig_uid, please read this thread http://drupal.org/node/183520
+  
+  // Static $up_orig_com_uid used by v2 bugfix. See http://drupal.org/node/183520
   static $up_orig_com_uid;
 
+  // Get points for a comment
   $points = variable_get(USERPOINTS_POST_COMMENT, 0);
+  
   switch ($op) {
     case 'insert':
-      $params = array(
-        'points' => $points,
-        'uid' => $user->uid,
-        'operation' => 'insert',
-        'entity_id' => $comment['cid'],
-        'entity_type' => 'comment'
-      );
-      userpoints_userpointsapi($params);
+      userpoints_nc_entity_insert($comment['cid'], 'comment', $comment['uid'], $points, $comment['comment']);
       break;
+      
     case 'delete':
-      $points = -$points;
-      $params = array(
-        'points' => $points,
-        'uid' => $comment->uid,
-        'operation' => 'delete',
-        'entity_id' => $comment->cid,
-        'entity_type' => 'comment'
-      );
-      userpoints_userpointsapi($params);
+      userpoints_nc_entity_delete($comment->cid, 'comment', $comment->uid);
       break;
+    
     case 'moderate':
-      $points = variable_get(USERPOINTS_MODERATE_COMMENT, 0);
-      $params = array(
-        'points' => $points,
-        'uid' => $comment->uid,
-        'operation' => 'moderate',
-        'entity_id' => $comment->cid,
-        'entity_type' => 'comment'
-      );
-      userpoints_userpointsapi($params);
+      $modpoints = variable_get(USERPOINTS_MODERATE_COMMENT, 0);
+      userpoints_nc_entity_moderate($comment->cid, 'comment', $comment->uid, $modpoints);
       break;
+    
     case 'form':
       $up_orig_com_uid = $comment['uid']['#value'];
       break;
+    
     case 'update':
-      //Find the last points granted on this node inserts and ownership gains
+      userpoints_nc_entity_update($comment['cid'], 'comment', $comment['uid'], $up_orig_com_uid, $comment['comment'], $points);
+      break;
+  }
+}
+
+/**
+ * Fetches the details of the last set of points granted for an entity
+ * 
+ * @param int $entity_id - $cid or $nid
+ * @param string $entity_type - 'comment' or 'node'
+ * 
+ * @return object 
+ */
+function userpoints_nc_get_last_granted_points($entity_id, $entity_type) {
+
+  if ($entity_type != 'comment' && $entity_type != 'node') {
+    return null;
+  }
+
       $sql = "SELECT points, uid 
               FROM {userpoints_txn} 
               WHERE entity_id = %d AND entity_type = '%s' 
-              AND (operation = '%s' OR operation ='%s')
+          AND (operation = '%s' OR operation = '%s' OR operation = '%s')
               ORDER BY time_stamp DESC
-              LIMIT 1
-              ";
-      $cid = $comment['cid'];
-      $new_uid = $comment['uid'];
-      $last_owner = db_fetch_object(db_query($sql, $cid, 'comment', 'insert', 'Ownership gain'));
+          LIMIT 1";
 
-      //Check the UID of the original to this user, if different add/substract points
-      if ($new_uid != $last_owner->uid && is_numeric($last_owner->uid) ) {
-        //The owner has changed so we're removing from the
-        //the original owner and giving to the new owner
-        //Give to the original owner
-        $points = $last_owner->points;
-        $params = array(
-          'points' => $points,
-          'uid' => $new_uid,
-          'operation' => 'Ownership gain',
-          'entity_id' => $cid,
-          'entity_type' => 'comment'
-        );
-        userpoints_userpointsapi($params);
+  return db_fetch_object(db_query($sql, (int) $entity_id, $entity_type, 'insert', 'update', 'Ownership gain'));
+}
 
-        //Take away from the original owner
-        $params = array(
-          'points' => -$points,
-          'uid' => $last_owner->uid,
-          'operation' => 'Ownership loss',
-          'entity_id' => $cid,
-          'entity_type' => 'comment'
-        );
-        userpoints_userpointsapi($params);
+/**
+ * Checks that the entity passes word length restrictions required to earn points
+ * 
+ * @return boolean
+ */
+function userpoints_nc_check_word_length($content, $entity_type = 'comment') {
 
+  if ($entity_type == 'node') {
+    $minwords = variable_get(USERPOINTS_NODE_WORDS, 1);
       }
       else {
-        //We failed to pull a matching operation via the DB
-        //If the user wants to use the V2BUG we'll use it..
-        //please read drupal.org/node/183520
-        if (variable_get(USERPOINTS_USE_V2BUG, false)) {
-          if ($orig_uid != $comment['uid']) {
-            $params = array(
-              'points' => $points,
-              'uid' => $new_uid,
-              'operation' => 'Ownership gain',
-              'entity_id' => $cid,
-              'entity_type' => 'comment'
-            );
-            userpoints_userpointsapi($params);
+    $minwords = variable_get(USERPOINTS_COMMENT_WORDS, 1);
+  }
 
-            //Take away from the original owner
-            $params = array(
-              'points' => -$points,
-              'uid' => $comment['uid'],
-              'operation' => 'Ownership loss',
-              'entity_id' => $new_uid,
-              'entity_type' => 'comment'
-            );
-            userpoints_userpointsapi($params);
+  // Return early and avoid regex below if check is unnecessary
+  if ($minwords < 2) {
+    return TRUE;
           }
+  
+  $wordcount = 0;
+  
+  // Make sure there is a space between adjacent html tags. Then strip them.
+  $content = strip_tags(str_replace('<', ' <', $content));
+  
+  // Eliminate any quoted text between [quote] [/quote] tags
+  // Regex copied from quote.module
+  $content = preg_replace('#\[(quote.*?)]((?>\[(?!/?quote[^[]*?])|[^[]|(?R))*)\[/quote]#is', '', $content);
+  
+  // Reduce all whitespace characters to a single space between words.
+  // Then split the string at each whitespace and count the words.
+  if (!empty($content)) {
+    $content = trim(preg_replace('/\s+/', ' ', $content));
+    $wordcount = count(explode(' ', $content));
         }
+  
+  return $wordcount >= $minwords;
       }
-      break;
+
+/**
+ * Processes points changes on insertion of a comment or node
+ */
+function userpoints_nc_entity_insert($entity_id, $entity_type, $uid, $points, $content) {
+
+  if ($points != 0 && userpoints_nc_check_word_length($content, $entity_type)) {
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, $points, 'insert');
   }
 }
 
+/**
+ * Processes points changes on deletion of a comment or node
+ */
+function userpoints_nc_entity_delete($entity_id, $entity_type, $uid) {
+
+  // Find the last points granted on this entity for inserts and ownership gains
+  $last_points = userpoints_nc_get_last_granted_points($entity_id, $entity_type);
+  
+  if ($last_points && $last_points->points > 0) {
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, -$last_points->points, 'delete');
+  }  
+}
+
+/**
+ * Processes points changes on moderation of a comment or node
+ */
+function userpoints_nc_entity_moderate($entity_id, $entity_type, $uid, $points) {
+  
+  if ($points != 0) {
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, $points, 'moderate');
+  }
+}
+
+/**
+ * Processes points changes on update of a comment or node
+ */
+function userpoints_nc_entity_update($entity_id, $entity_type, $uid, $orig_uid, $content, $points) {
+
+  // Check if updated comment/node word length qualifies for points
+  $wordsok = userpoints_nc_check_word_length($content, $entity_type);
+  
+  // Find the last points granted on this entity
+  $last_points = userpoints_nc_get_last_granted_points($entity_id, $entity_type);
+
+  // Compare the UID of the last points recorded vs the current $uid
+  $ownerchanged = $last_points && $uid != $last_points->uid && is_numeric($last_points->uid);
+  
+  // Check if ownership change is needed via v2 bugfix setting: http://drupal.org/node/183520
+  $v2bugfix = !$last_points && variable_get(USERPOINTS_USE_V2BUG, FALSE) && $orig_uid != $uid;
+  
+  // Record ownership change.
+  if ($ownerchanged || $v2bugfix) {
+    
+    // Assign points to the new owner
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, $points, 'Ownership gain');
+
+    // If an owner changes but there are not enough words to qualify for points 
+    // then we have a problem. New ownership must be recorded so that 
+    // userpoints_nc_get_last_granted_points() will work next time the comment
+    // is updated. But userpointsapi won't accept 0 points on a transaction. 
+    // So instead we do another transaction to subtract the points gained by
+    // the change of ownership.
+    if (!$wordsok) {
+      _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, -$points, 'update');
+    }
+
+    // Subtract points from the previous owner
+    $uid = $v2bugfix ? $orig_uid : $last_points->uid;
+    $points = $v2bugfix ? $points : $last_points->points;
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, -$points, 'Ownership loss');
+
+  }
+  else if ($wordsok && (!$last_points || $last_points->points < 0)) {
+    
+    // Updating entity that previously did not meet the word requirements.
+    // The last points record either doesn't exist, or it is negative if the
+    // entity was updated and shortened to below the minimum word length.
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, $points, 'update');
+    
+  }
+  else if ($wordsok && $last_points && $last_points->points != $points) {
+    
+    // Updating entity that earns different points now than it did before.
+    // Leave original points intact. Could be exploited by users updating 
+    // all their old posts if admin increases points values.
+    
+  }
+  else if (!$wordsok && $last_points) {
+
+    // Shortened entity content to less than minimum words. Subtract last points.
+    _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, -$last_points->points, 'update');
+    
+  }
+
+}
+
+/**
+ * Inserts a userpointsapi transaction
+ */
+function _userpoints_nc_insert_transaction($entity_id, $entity_type, $uid, $points, $operation) {
+
+    $params = array(
+      'points' => $points,
+      'uid' => $uid,
+      'operation' => $operation,
+      'entity_id' => $entity_id,
+      'entity_type' => $entity_type,
+    );
+
+    userpoints_userpointsapi($params);
+}
