Kaynağa Gözat

updated flag module to 7.x-3.6 (2015-mar-02)

Bachir Soussi Chiadmi 9 yıl önce
ebeveyn
işleme
eeb0df349d
41 değiştirilmiş dosya ile 2017 ekleme ve 514 silme
  1. 15 14
      sites/all/modules/contrib/flag/flag/README.txt
  2. 29 9
      sites/all/modules/contrib/flag/flag/flag.api.php
  3. 6 3
      sites/all/modules/contrib/flag/flag/flag.info
  4. 2 1
      sites/all/modules/contrib/flag/flag/flag.info.inc
  5. 59 14
      sites/all/modules/contrib/flag/flag/flag.install
  6. 200 98
      sites/all/modules/contrib/flag/flag/flag.module
  7. 54 18
      sites/all/modules/contrib/flag/flag/flag.rules.inc
  8. 48 24
      sites/all/modules/contrib/flag/flag/flag.tokens.inc
  9. 3 3
      sites/all/modules/contrib/flag/flag/flag_actions.info
  10. 6 7
      sites/all/modules/contrib/flag/flag/flag_actions.module
  11. 3 3
      sites/all/modules/contrib/flag/flag/flag_bookmark/flag_bookmark.info
  12. 21 21
      sites/all/modules/contrib/flag/flag/flag_bookmark/flag_bookmark.install
  13. 1 1
      sites/all/modules/contrib/flag/flag/includes/flag.actions.inc
  14. 52 29
      sites/all/modules/contrib/flag/flag/includes/flag.admin.inc
  15. 1 1
      sites/all/modules/contrib/flag/flag/includes/flag.cookie_storage.inc
  16. 5 3
      sites/all/modules/contrib/flag/flag/includes/flag.export.inc
  17. 37 3
      sites/all/modules/contrib/flag/flag/includes/flag.pages.inc
  18. 1 1
      sites/all/modules/contrib/flag/flag/includes/flag/flag_comment.inc
  19. 47 3
      sites/all/modules/contrib/flag/flag/includes/flag/flag_entity.inc
  20. 234 175
      sites/all/modules/contrib/flag/flag/includes/flag/flag_flag.inc
  21. 8 1
      sites/all/modules/contrib/flag/flag/includes/flag/flag_node.inc
  22. 1 1
      sites/all/modules/contrib/flag/flag/includes/flag/flag_user.inc
  23. 21 2
      sites/all/modules/contrib/flag/flag/includes/views/flag.views.inc
  24. 5 0
      sites/all/modules/contrib/flag/flag/includes/views/flag.views_convert.inc
  25. 3 2
      sites/all/modules/contrib/flag/flag/includes/views/flag_handler_argument_entity_id.inc
  26. 8 4
      sites/all/modules/contrib/flag/flag/includes/views/flag_handler_field_ops.inc
  27. 5 1
      sites/all/modules/contrib/flag/flag/includes/views/flag_handler_filter_flagged.inc
  28. 23 4
      sites/all/modules/contrib/flag/flag/includes/views/flag_handler_relationships.inc
  29. 2 1
      sites/all/modules/contrib/flag/flag/includes/views/flag_handler_sort_flagged.inc
  30. 11 8
      sites/all/modules/contrib/flag/flag/includes/views/flag_plugin_argument_validate_flaggability.inc
  31. 35 8
      sites/all/modules/contrib/flag/flag/plugins/access/flag_is_flagged/flag_is_flagged.inc
  32. 10 7
      sites/all/modules/contrib/flag/flag/plugins/content_types/flag_link/flag_link.inc
  33. 715 28
      sites/all/modules/contrib/flag/flag/tests/flag.test
  34. 12 0
      sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.info
  35. 67 0
      sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.install
  36. 45 0
      sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.module
  37. 13 0
      sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.info
  38. 184 0
      sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.module
  39. 3 3
      sites/all/modules/contrib/flag/flag/tests/flagaccesstest/flagaccesstest.info
  40. 1 1
      sites/all/modules/contrib/flag/flag/theme/flag.js
  41. 21 12
      sites/all/modules/contrib/flag/flag/theme/flag.tpl.php

+ 15 - 14
sites/all/modules/contrib/flag/flag/README.txt

@@ -7,13 +7,13 @@ The Flag module is a flexible flagging system whose primary goal is
 to give all the control to the administrator. Using this module, the
 site administrator can provide an arbitrary number of 'flags'.
 
-A flag is really just a boolean toggle that is set on a node, comment,
-or user. Flags may be per-user, meaning that each user can flag an item
+A flag is really just a boolean toggle that is set on an entity such as a node,
+comment, or user. Flags may be per-user, meaning that each user can flag an item
 individually, or global, meaning that the item is either flagged or it
 is not flagged, and any user who changes that changes it for everyone.
 
-In this way, additional flags (similar to 'published' and 'sticky') can 
-be put on nodes, or other items, and dealt with by the system however 
+In this way, additional flags (similar to 'published' and 'sticky') can
+be put on nodes, or other items, and dealt with by the system however
 the administration likes.
 
 Each flag allows the administrator to choose the 'flag this' text, and
@@ -25,19 +25,19 @@ Each flag can be restricted to use only by certain roles. Each
 flag provides data to the Views module, and provides a default
 view to list 'My bookmarks'. These default views are somewhat crude,
 but are easily tailored to whatever the system administrator would like
-it to do. 
+it to do.
 
-Each flag also provides an 'argument' to the Views module that can be 
-used to allow a user to view other people's flagged content. This isn't 
-turned on by default anywhere, though, and the administrator will need 
+Each flag also provides an 'argument' to the Views module that can be
+used to allow a user to view other people's flagged content. This isn't
+turned on by default anywhere, though, and the administrator will need
 to construct a view in order to take advantage of it.
 
-The module will come installed with a simple flag called "bookmarks" and 
-a simple view for 'My bookmarks'. This is a default view provided by the 
-Flag module, but can be customized to fit the needs of your site. To 
-customize this view, go to admin/structure/views and find the 
-'flags_bookmarks' view. Click the 'Add' action to customize the view. 
-Once saved, the new version of the view will be used rather than the one 
+The Flag Bookmark module provides a simple flag called "bookmarks" and
+a simple view for 'My bookmarks'. This is a default view provided by the
+Flag module, but can be customized to fit the needs of your site. To
+customize this view, go to admin/structure/views and find the
+'flags_bookmarks' view. Click the 'Add' action to customize the view.
+Once saved, the new version of the view will be used rather than the one
 provided by Flag.
 
 Besides editing the default view that comes with the module, Flag
@@ -53,6 +53,7 @@ Recommended Modules
 -------------------
 - Views
 - Session API
+- Token, which is required for Flag to provide tokens on flagged entities.
 
 Installation
 ------------

+ 29 - 9
sites/all/modules/contrib/flag/flag/flag.api.php

@@ -58,11 +58,11 @@ function hook_flag_type_info_alter(&$definitions) {
  */
 function hook_flag_default_flags() {
   $flags = array();
-  $flags['bookmarks'] = array (
+  $flags['bookmarks'] = array(
     'entity_type' => 'node',
     'title' => 'Bookmarks',
     'global' => FALSE,
-    'types' => array (
+    'types' => array(
       0 => 'article',
       1 => 'blog',
     ),
@@ -75,7 +75,7 @@ function hook_flag_default_flags() {
     'unflag_denied_text' => '',
     'link_type' => 'toggle',
     'weight' => 0,
-    'show_in_links' => array (
+    'show_in_links' => array(
       'full' => TRUE,
       'token' => FALSE,
     ),
@@ -90,10 +90,22 @@ function hook_flag_default_flags() {
   return $flags;
 }
 
+/**
+ * Alter the definition of default flags.
+ *
+ * @param array &$flags
+ *   An array keyed by flag machine name containing flag definitions.
+ */
+function hook_flag_default_flags_alter(&$flags) {
+  if (!empty($flags['bookmark'])) {
+    $flags['bookmark']['title'] = 'Bananana Bookmark';
+  }
+}
+
 /**
  * Allow modules to alter a flag when it is initially loaded.
  *
- * @see flag_get_flags().
+ * @see flag_get_flags()
  */
 function hook_flag_alter(&$flag) {
 
@@ -183,7 +195,7 @@ function hook_flag_validate($action, $flag, $entity_id, $account, $skip_permissi
       $count = count($flags[$flag->name]);
       if ($count >= 2) {
         // Users may flag only 2 nodes with this flag.
-        return(array('access-denied' => t('You may only flag 2 nodes with the test flag.')));
+        return (array('access-denied' => t('You may only flag 2 nodes with the test flag.')));
       }
     }
   }
@@ -350,15 +362,23 @@ function hook_flag_reset($flag, $entity_id, $rows) {
 /**
  * Alter the javascript structure that describes the flag operation.
  *
+ * @param $info
+ *   The info array before it is returned from flag_build_javascript_info().
  * @param $flag
  *   The full flag object.
- * @param $entity_id
- *   The ID of the node, comment, user or other object being flagged.
  *
  * @see flag_build_javascript_info()
  */
-function hook_flag_javascript_info_alter() {
-
+function hook_flag_javascript_info_alter(&$info, $flag) {
+  if ($flag->name === 'test') {
+    $info['newLink'] = $flag->theme($flag->is_flagged($info['contentId']) ? 'unflag' : 'flag', $info['contentId'], array(
+      'after_flagging' => TRUE,
+      'errors' => $flag->get_errors(),
+      // Additional options to pass to theme's preprocess function/template.
+      'icon' => TRUE,
+      'hide_text' => TRUE,
+    ));
+  }
 }
 
 /**

+ 6 - 3
sites/all/modules/contrib/flag/flag/flag.info

@@ -4,6 +4,9 @@ core = 7.x
 package = Flags
 configure = admin/structure/flags
 
+test_dependencies[] = token
+test_dependencies[] = rules
+
 ; Files that contain classes.
 ; Flag classes
 files[] = includes/flag/flag_flag.inc
@@ -26,9 +29,9 @@ files[] = includes/views/flag_handler_relationships.inc
 files[] = includes/views/flag_plugin_argument_validate_flaggability.inc
 files[] = tests/flag.test
 
-; Information added by drupal.org packaging script on 2013-09-13
-version = "7.x-3.2"
+; Information added by Drupal.org packaging script on 2015-03-02
+version = "7.x-3.6"
 core = "7.x"
 project = "flag"
-datestamp = "1379063829"
+datestamp = "1425327793"
 

+ 2 - 1
sites/all/modules/contrib/flag/flag/flag.info.inc

@@ -1,7 +1,8 @@
 <?php
 
 /**
- * @file Contains Entity API property information.
+ * @file
+ * Contains Entity API property information.
  */
 
 /**

+ 59 - 14
sites/all/modules/contrib/flag/flag/flag.install

@@ -100,7 +100,7 @@ function flag_schema() {
         'default' => 0,
       ),
       'sid' => array(
-        'description' => "The user's session id as stored in the session table.",
+        'description' => "The user's numeric sid from the session_api table.",
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -113,7 +113,7 @@ function flag_schema() {
         'not null' => TRUE,
         'default' => 0,
         'disp-size' => 11,
-      )
+      ),
     ),
     'primary key' => array('flagging_id'),
     'unique keys' => array(
@@ -121,7 +121,12 @@ function flag_schema() {
     ),
     'indexes' => array(
       'entity_type_uid_sid' => array('entity_type', 'uid', 'sid'),
-      'entity_type_entity_id_uid_sid' => array('entity_type', 'entity_id', 'uid', 'sid'),
+      'entity_type_entity_id_uid_sid' => array(
+        'entity_type',
+        'entity_id',
+        'uid',
+        'sid',
+      ),
       'entity_id_fid' => array('entity_id', 'fid'),
     ),
   );
@@ -190,7 +195,7 @@ function flag_schema() {
         'not null' => TRUE,
         'default' => 0,
         'disp-size' => 11,
-      )
+      ),
     ),
     'primary key' => array('fid', 'entity_id'),
     'indexes' => array(
@@ -296,7 +301,11 @@ function flag_update_6200() {
 function flag_update_6201() {
   // Remove "content type" from one key, see http://drupal.org/node/612602.
   db_drop_unique_key('flag_content', 'fid_content_type_content_id_uid');
-  db_add_unique_key('flag_content', 'fid_content_id_uid', array('fid', 'content_id', 'uid'));
+  db_add_unique_key('flag_content', 'fid_content_id_uid', array(
+    'fid',
+    'content_id',
+    'uid',
+  ));
 
   // Add a count index, see http://drupal.org/node/489610.
   db_add_index('flag_counts', 'count', array('count'));
@@ -311,11 +320,25 @@ function flag_update_6202() {
   db_drop_index('flag_content', 'content_type_uid');
 
   // Add the column.
-  db_add_field('flag_content', 'sid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0));
+  db_add_field('flag_content', 'sid', array(
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+  ));
 
   // Re-add the removed keys.
-  db_add_unique_key('flag_content', 'fid_content_id_uid_sid', array('fid', 'content_id', 'uid', 'sid'));
-  db_add_index('flag_content', 'content_type_uid_sid', array('content_type', 'uid', 'sid'));
+  db_add_unique_key('flag_content', 'fid_content_id_uid_sid', array(
+    'fid',
+    'content_id',
+    'uid',
+    'sid',
+  ));
+  db_add_index('flag_content', 'content_type_uid_sid', array(
+    'content_type',
+    'uid',
+    'sid',
+  ));
 }
 
 /**
@@ -357,14 +380,25 @@ function flag_update_6206() {
     db_drop_unique_key('flag_content', 'content_type_content_id_uid_sid');
   }
 
-  db_add_index('flag_content', 'content_type_content_id_uid_sid', array('content_type', 'content_id', 'uid', 'sid'));
+  db_add_index('flag_content', 'content_type_content_id_uid_sid', array(
+    'content_type',
+    'content_id',
+    'uid',
+    'sid',
+  ));
 }
 
 /**
  * Adds column last_updated to flag_counts table.
  */
 function flag_update_6207() {
-  db_add_field('flag_counts', 'last_updated', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'disp-size' => 11), array('indexes' => array('last_updated' => array('last_updated'))));
+  db_add_field('flag_counts', 'last_updated', array(
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+    'disp-size' => 11),
+    array('indexes' => array('last_updated' => array('last_updated'))));
 }
 
 /**
@@ -482,7 +516,12 @@ function flag_update_7303() {
       ),
       'indexes' => array(
         'entity_type_uid_sid' => array('entity_type', 'uid', 'sid'),
-        'entity_type_content_id_uid_sid' => array('entity_type', 'content_id', 'uid', 'sid'),
+        'entity_type_content_id_uid_sid' => array(
+          'entity_type',
+          'content_id',
+          'uid',
+          'sid',
+        ),
         'content_id_fid' => array('content_id', 'fid'),
       ),
     )
@@ -510,7 +549,12 @@ function flag_update_7303() {
         'fid_entity_id_uid_sid' => array('fid', 'entity_id', 'uid', 'sid'),
       ),
       'indexes' => array(
-        'entity_type_entity_id_uid_sid' => array('entity_type', 'entity_id', 'uid', 'sid'),
+        'entity_type_entity_id_uid_sid' => array(
+          'entity_type',
+          'entity_id',
+          'uid',
+          'sid',
+        ),
         'entity_id_fid' => array('entity_id', 'fid'),
       ),
     )
@@ -569,7 +613,7 @@ function flag_update_7304() {
       'indexes' => array(
         'fid_entity_type' => array('fid', 'entity_type'),
         'entity_type_content_id' => array('entity_type', 'content_id'),
-      )
+      ),
     )
   );
 
@@ -671,7 +715,8 @@ function flag_update_7306() {
     }
 
     // Update show_on_comment and show_on_entity properties to use new view
-    // mode settings. Since the old logic was to show on all view modes, do that.
+    // mode settings. Since the old logic was to show on all view modes, do
+    // that.
     if (!empty($flag->show_on_entity) || !empty($flag->show_on_comment)) {
       if ($entity_info = entity_get_info($flag->entity_type)) {
         foreach ($entity_info['view modes'] as $view_mode => $value) {

+ 200 - 98
sites/all/modules/contrib/flag/flag/flag.module

@@ -33,6 +33,7 @@ function flag_entity_info() {
       // The following tells EntityAPI how to save flaggings, thus allowing use
       // of Entity metadata wrappers (if present).
       'save callback' => 'flagging_save',
+      'creation callback' => 'flagging_create',
     ),
   );
 
@@ -72,6 +73,37 @@ function flagging_load($flagging_id, $reset = FALSE) {
   return reset($result);
 }
 
+/**
+ * Entity API creation callback.
+ *
+ * Creates an unsaved flagging object for use with $flag->flag().
+ *
+ * @param $values
+ *   An array of values as described by the entity's property info. Only
+ *   'flag_name' or 'fid' must be specified, since $flag->flag() does the rest.
+ *
+ * @return
+ *   An unsaved flagging object containing the property values.
+ */
+function flagging_create($values = array()) {
+  $flagging = (object) array();
+
+  if (!isset($values['flag_name'])) {
+    if (isset($values['fid'])) {
+      // Add flag_name, determined from fid.
+      $flag = flag_get_flag(NULL, $values['fid']);
+      $values['flag_name'] = $flag->name;
+    }
+  }
+
+  // Apply the given values.
+  foreach ($values as $key => $value) {
+    $flagging->$key = $value;
+  }
+
+  return $flagging;
+}
+
 /**
  * Saves a flagging entity.
  *
@@ -128,14 +160,15 @@ function flagging_save($flagging) {
 // @todo: flag_reset_flag():
 // - it should delete the flaggings.
 // - (it has other issues; see http://drupal.org/node/894992.)
-// - (is problematic: it might not be possible to delete all data in a single page request.)
+// - (is problematic: it might not be possible to delete all data in a single
+//   page request.)
 
 // @todo: Discuss: Note that almost all functions/identifiers dealing with
 // flaggings *aren't* prefixed by "flag_". For example:
-//  - The menu argument is %flagging, not %flag_flagging.
-//  - The entity type is "flagging", not "flag_flagging".
-// On the one hand this succinct version is readable and nice. On the other hand, it isn't
-// very "correct".
+// - The menu argument is %flagging, not %flag_flagging.
+// - The entity type is "flagging", not "flag_flagging".
+// On the one hand this succinct version is readable and nice. On the other
+// hand, it isn't very "correct".
 
 /**
  * Implements hook_entity_query_alter().
@@ -150,7 +183,8 @@ function flag_entity_query_alter(EntityFieldQuery $query) {
 
   // Alter only flagging queries with bundle conditions.
   if (isset($conditions['entity_type']) && $conditions['entity_type']['value'] == 'flagging' && isset($conditions['bundle'])) {
-    $query->addTag('flagging_flag_names'); // Add tag to alter query.
+    // Add tag to alter query.
+    $query->addTag('flagging_flag_names');
     // Make value and operator of the bundle condition accessible
     // in hook_query_TAG_alter.
     $query->addMetaData('flag_name_value', $conditions['bundle']['value']);
@@ -185,7 +219,6 @@ function flag_menu() {
     'access arguments' => array('administer flags'),
     'description' => 'Configure flags for marking content with arbitrary information (such as <em>offensive</em> or <em>bookmarked</em>).',
     'file' => 'includes/flag.admin.inc',
-    'type' => MENU_NORMAL_ITEM,
   );
   $items[FLAG_ADMIN_PATH . '/list'] = array(
     'title' => 'List',
@@ -207,7 +240,7 @@ function flag_menu() {
     'page arguments' => array('flag_import_form'),
     'access arguments' => array('use flag import'),
     'file' => 'includes/flag.export.inc',
-    'type' => MENU_LOCAL_TASK,
+    'type' => MENU_LOCAL_ACTION,
     'weight' => 2,
   );
   $items[FLAG_ADMIN_PATH . '/export'] = array(
@@ -216,24 +249,26 @@ function flag_menu() {
     'page arguments' => array('flag_export_form'),
     'access arguments' => array('administer flags'),
     'file' => 'includes/flag.export.inc',
-    'type' => MENU_LOCAL_TASK,
+    'type' => MENU_LOCAL_ACTION,
     'weight' => 3,
   );
 
   $items[FLAG_ADMIN_PATH . '/manage/%flag'] = array(
-    'load arguments' => array(TRUE), // Allow for disabled flags.
+    // Allow for disabled flags.
+    'load arguments' => array(TRUE),
     'page callback' => 'drupal_get_form',
     'page arguments' => array('flag_form', FLAG_ADMIN_PATH_START + 1),
     'access callback' => 'user_access',
     'access arguments' => array('administer flags'),
     'file' => 'includes/flag.admin.inc',
-    'type' => MENU_CALLBACK,
+    'type' => MENU_LOCAL_TASK,
     // Make the flag title the default title for descendant menu items.
     'title callback' => '_flag_menu_title',
     'title arguments' => array(FLAG_ADMIN_PATH_START + 1),
   );
   $items[FLAG_ADMIN_PATH . '/manage/%flag/edit'] = array(
-    'load arguments' => array(TRUE), // Allow for disabled flags.
+    // Allow for disabled flags.
+    'load arguments' => array(TRUE),
     'title' => 'Edit flag',
     'type' => MENU_DEFAULT_LOCAL_TASK,
     'weight' => -10,
@@ -257,7 +292,8 @@ function flag_menu() {
     'type' => MENU_CALLBACK,
   );
   $items[FLAG_ADMIN_PATH . '/manage/%flag/update'] = array(
-    'load arguments' => array(TRUE), // Allow for disabled flags.
+    // Allow for disabled flags.
+    'load arguments' => array(TRUE),
     'title' => 'Update',
     'page callback' => 'flag_update_page',
     'page arguments' => array(FLAG_ADMIN_PATH_START + 1),
@@ -306,31 +342,6 @@ function flag_admin_menu_map() {
     ),
   );
 
-  if (module_exists('field_ui')) {
-    foreach (entity_get_info() as $obj_type => $info) {
-      if ($obj_type == 'flagging') {
-        foreach ($info['bundles'] as $bundle_name => $bundle_info) {
-          if (isset($bundle_info['admin'])) {
-            $fields = array();
-
-            foreach (field_info_instances($obj_type, $bundle_name) as $field) {
-              $fields[] = $field['field_name'];
-            }
-
-            $arguments = array(
-              '%flag' => array($bundle_name),
-              '%field_ui_menu' => $fields,
-            );
-
-            $path = $bundle_info['admin']['path'];
-            $map["$path/fields/%field_ui_menu"]['parent'] = "$path/fields";
-            $map["$path/fields/%field_ui_menu"]['arguments'][] = $arguments;
-          }
-        }
-      }
-    }
-  }
-
   return $map;
 }
 
@@ -384,9 +395,11 @@ function flag_help($path, $arg) {
     case FLAG_ADMIN_PATH:
       $output = '<p>' . t('This page lists all the <em>flags</em> that are currently defined on this system.') . '</p>';
       return $output;
+
     case FLAG_ADMIN_PATH . '/add':
       $output = '<p>' . t('Select the type of flag to create. An individual flag can only affect one type of object. This cannot be changed once the flag is created.') . '</p>';
       return $output;
+
     case FLAG_ADMIN_PATH . '/manage/%/fields':
       // Get the existing link types that provide a flagging form.
       $link_types = flag_get_link_types();
@@ -540,10 +553,10 @@ function flag_get_types() {
 function flag_create_handler($entity_type) {
   $definition = flag_fetch_definition($entity_type);
   if (isset($definition) && class_exists($definition['handler'])) {
-    $handler = new $definition['handler'];
+    $handler = new $definition['handler']();
   }
   else {
-    $handler = new flag_broken;
+    $handler = new flag_broken();
   }
   $handler->entity_type = $entity_type;
   $handler->construct();
@@ -566,6 +579,8 @@ function flag_permission() {
     ),
   );
 
+  // Reset static cache to ensure all flag permissions are available.
+  drupal_static_reset('flag_get_flags');
   $flags = flag_get_flags();
   // Provide flag and unflag permissions for each flag.
   foreach ($flags as $flag_name => $flag) {
@@ -616,12 +631,19 @@ function flag_field_extra_fields() {
       continue;
     }
 
-    foreach ($flag->types as $bundle_name) {
+    $applicable_bundles = $flag->types;
+    // If the list of bundles is empty, it indicates all bundles apply.
+    if (empty($applicable_bundles)) {
+      $entity_info = entity_get_info($flag->entity_type);
+      $applicable_bundles = array_keys($entity_info['bundles']);
+    }
+
+    foreach ($applicable_bundles as $bundle_name) {
       if ($flag->show_on_form) {
         $extra[$flag->entity_type][$bundle_name]['form']['flag'] = array(
           'label' => t('Flags'),
           'description' => t('Checkboxes for toggling flags'),
-          'weight' => 10
+          'weight' => 10,
         );
       }
 
@@ -689,7 +711,7 @@ function flag_form_node_type_form_alter(&$form, &$form_state, $form_id) {
  * Warning: will not work on entity types that are not fieldable, as this relies
  * on a field module hook.
  *
- * @see flag_field_attach_submit().
+ * @see flag_field_attach_submit()
  */
 function flag_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
   list($id) = entity_extract_ids($entity_type, $entity);
@@ -698,6 +720,10 @@ function flag_field_attach_form($entity_type, $entity, &$form, &$form_state, $la
     $id = NULL;
   }
 
+  // Keep track of whether the entity is new or not, as we're about to fiddle
+  // with the entity id for the flag's entity cache.
+  $is_existing_entity = !empty($id);
+
   // Get all possible flags for this entity type.
   $flags = flag_get_flags($entity_type);
 
@@ -710,7 +736,7 @@ function flag_field_attach_form($entity_type, $entity, &$form, &$form_state, $la
     }
 
     // Get the flag status.
-    if (isset($id)) {
+    if ($is_existing_entity) {
       $flag_status = $flag->is_flagged($id);
     }
     else {
@@ -719,7 +745,8 @@ function flag_field_attach_form($entity_type, $entity, &$form, &$form_state, $la
       $flag_status = FALSE;
       // Apply the per-bundle defaults for nodes.
       if ($entity_type == 'node') {
-        $flag_status = variable_get('flag_' . $flag->name . '_default_' . $form['type']['#value'], 0);
+        $node_type = $entity->type;
+        $flag_status = variable_get('flag_' . $flag->name . '_default_' . $node_type, 0);
       }
 
       // For a new, unsaved entity, make a dummy entity ID so that the flag
@@ -793,7 +820,7 @@ function flag_field_attach_form($entity_type, $entity, &$form, &$form_state, $la
 /**
  * Implements hook_field_attach_submit().
  *
- * @see flag_field_attach_form().
+ * @see flag_field_attach_form()
  */
 function flag_field_attach_submit($entity_type, $entity, $form, &$form_state) {
   // This is invoked for each flag_field_attach_form(), but possibly more than
@@ -831,7 +858,7 @@ function flag_field_attach_update($entity_type, $entity) {
 /**
  * Shared saving routine between flag_field_attach_insert/update().
  *
- * @see flag_field_attach_form().
+ * @see flag_field_attach_form()
  */
 function flag_field_attach_save($entity_type, $entity) {
   list($id) = entity_extract_ids($entity_type, $entity);
@@ -872,13 +899,14 @@ function flag_contextual_links_view_alter(&$element, $items) {
 
       list($entity_id) = entity_extract_ids($entity_type, $entity);
       if (!$flag->access($entity_id) && (!$flag->is_flagged($entity_id) || !$flag->access($entity_id, 'flag'))) {
-        // User has no permission to use this flag or flag does not apply to this
-        // object. The link is not skipped if the user has "flag" access but
-        // not "unflag" access (this way the unflag denied message is shown).
+        // User has no permission to use this flag or flag does not apply to
+        // this object. The link is not skipped if the user has "flag" access
+        // but not "unflag" access (this way the unflag denied message is
+        // shown).
         continue;
       }
 
-      $element['#links']['flag-'. $name] = array(
+      $element['#links']['flag-' . $name] = array(
         'title' => $flag->theme($flag->is_flagged($entity_id) ? 'unflag' : 'flag', $entity_id),
         'html' => TRUE,
       );
@@ -935,7 +963,7 @@ function flag_entity_view($entity, $type, $view_mode, $langcode) {
     // The pseudofield output.
     if ($flag->show_as_field) {
       $entity->content['flag_' . $flag->name] = array(
-       '#markup' => $flag->theme($flag->is_flagged($entity_id) ? 'unflag' : 'flag', $entity_id, array('needs_wrapping_element' => TRUE)),
+        '#markup' => $flag->theme($flag->is_flagged($entity_id) ? 'unflag' : 'flag', $entity_id, array('needs_wrapping_element' => TRUE)),
       );
     }
   }
@@ -1136,7 +1164,7 @@ function flag_user_delete($account) {
 function flag_user_account_removal($account) {
   // Remove flags by this user.
   $query = db_select('flagging', 'fc');
-  $query->leftJoin('flag_counts', 'c', 'fc.entity_id = c.entity_id AND fc.entity_type = c.entity_type');
+  $query->leftJoin('flag_counts', 'c', 'fc.entity_id = c.entity_id AND fc.entity_type = c.entity_type AND fc.fid = c.fid');
   $result = $query
     ->fields('fc', array('fid', 'entity_id'))
     ->fields('c', array('count'))
@@ -1313,8 +1341,9 @@ function flag_flag_trigger($action, $flag, $entity_id, $account, $flagging) {
   $object = $flag->fetch_entity($entity_id);
 
   // Generic "all flags" actions.
-  foreach (trigger_get_assigned_actions('flag_' . $action)  as $aid => $action_info) {
-    // The 'if ($aid)' is a safeguard against http://drupal.org/node/271460#comment-886564
+  foreach (trigger_get_assigned_actions('flag_' . $action) as $aid => $action_info) {
+    // The 'if ($aid)' is a safeguard against
+    // http://drupal.org/node/271460#comment-886564
     if ($aid) {
       actions_do($aid, $object, $context);
     }
@@ -1513,8 +1542,8 @@ function template_preprocess_flag(&$variables) {
   $flag =& $variables['flag'];
   $action = $variables['action'];
   $entity_id = $variables['entity_id'];
-  $errors = join('<br />', $variables['errors']);
-  $flag_css_name = str_replace('_', '-', $flag->name);
+  $errors = implode('<br />', $variables['errors']);
+  $flag_name_css = str_replace('_', '-', $flag->name);
 
   // Generate the link URL.
   $link_type = $flag->get_link_type();
@@ -1550,12 +1579,12 @@ function template_preprocess_flag(&$variables) {
   $variables['link_text'] = isset($link['title']) ? $link['title'] : $flag->get_label($action . '_short', $entity_id);
   $variables['link_title'] = isset($link['attributes']['title']) ? check_plain($link['attributes']['title']) : check_plain(strip_tags($flag->get_label($action . '_long', $entity_id)));
   $variables['status'] = ($action == 'flag' ? 'unflagged' : 'flagged');
-  $variables['flag_name_css'] = $flag_css_name;
+  $variables['flag_name_css'] = $flag_name_css;
 
   $variables['flag_wrapper_classes_array'] = array();
   $variables['flag_wrapper_classes_array'][] = 'flag-wrapper';
-  $variables['flag_wrapper_classes_array'][] = 'flag-' . $flag_css_name;
-  $variables['flag_wrapper_classes_array'][] = 'flag-' . $flag_css_name . '-' . $entity_id;
+  $variables['flag_wrapper_classes_array'][] = 'flag-' . $flag_name_css;
+  $variables['flag_wrapper_classes_array'][] = 'flag-' . $flag_name_css . '-' . $entity_id;
 
   $variables['flag_classes_array'] = array();
   $variables['flag_classes_array'][] = 'flag';
@@ -1583,7 +1612,8 @@ function template_preprocess_flag(&$variables) {
       $variables['message_classes_array'][] = 'flag-' . $variables['status'] . '-message';
       $variables['message_text'] = $flag->get_label($inverse_action . '_message', $entity_id);
       $variables['flag_classes_array'][] = $variables['status'];
-      // By default we make our JS code remove, after a few seconds, only success messages.
+      // By default we make our JS code remove, after a few seconds, only
+      // success messages.
       $variables['message_classes_array'][] = 'flag-auto-remove';
     }
   }
@@ -1646,12 +1676,21 @@ function _flag_link_type_descriptions() {
 // Non-Views public API
 
 /**
- * Get the count of flags for a certain entity.
+ * Get the count of flags for a particular entity type.
+ *
+ * When called during a flagging or unflagging (such as from a hook
+ * implementation or from Rules), the flagging or unflagging that is in the
+ * process of being performed:
+ *  - will be included during a flagging operation
+ *  - will STILL be included during an unflagging operation. That is, the count
+ *    will not yet have been decreased.
+ * This is because this queries the {flagging} table, which only has its record
+ * deleted at the very end of the unflagging process.
  *
  * @param $flag
  *   The flag.
  * @param $entity_type
- *   The entity type (usually 'node').
+ *   The entity type. For example, 'node'.
  *
  * @return
  *   The flag count with the flag name and entity type as the array key.
@@ -1670,7 +1709,7 @@ function flag_get_entity_flag_counts($flag, $entity_type) {
       ->countQuery()
       ->execute()
       ->fetchField();
-    $counts[$flag->name][$entity_type] =  $result;
+    $counts[$flag->name][$entity_type] = $result;
   }
 
   return $counts[$flag->name][$entity_type];
@@ -1679,6 +1718,15 @@ function flag_get_entity_flag_counts($flag, $entity_type) {
 /**
  * Get the user's flag count.
  *
+ * When called during a flagging or unflagging (such as from a hook
+ * implementation or from Rules), the flagging or unflagging that is in the
+ * process of being performed:
+ *  - will be included during a flagging operation
+ *  - will STILL be included during an unflagging operation. That is, the count
+ *    will not yet have been decreased.
+ * This is because this queries the {flagging} table, which only has its record
+ * deleted at the very end of the unflagging process.
+ *
  * @param $flag
  *   The flag.
  * @param $user
@@ -1710,13 +1758,17 @@ function flag_get_user_flag_counts($flag, $user) {
 /**
  * Get flag counts for all flags on a node.
  *
+ * When called during a flagging or unflagging (such as from a hook
+ * implementation or from Rules), the count this returns takes into account the
+ * the flagging or unflagging that is in the process of being performed.
+ *
  * @param $entity_type
  *   The entity type (usually 'node').
  * @param $entity_id
  *   The entity ID (usually the node ID).
  *
  * @return
- *   The flag count with the entity type and id as array keys.
+ *   The flag count with the flag names as array keys.
  */
 function flag_get_counts($entity_type, $entity_id) {
   $counts = &drupal_static(__FUNCTION__);
@@ -1742,6 +1794,10 @@ function flag_get_counts($entity_type, $entity_id) {
 /**
  * Get the total count of items flagged within a flag.
  *
+ * When called during a flagging or unflagging (such as from a hook
+ * implementation or from Rules), the count this returns takes into account the
+ * the flagging or unflagging that is in the process of being performed.
+ *
  * @param $flag_name
  *   The flag name for which to retrieve a flag count.
  * @param $reset
@@ -1756,7 +1812,7 @@ function flag_get_flag_counts($flag_name, $reset = FALSE) {
   if (!isset($counts[$flag_name])) {
     $flag = flag_get_flag($flag_name);
     $counts[$flag_name] = db_select('flag_counts', 'fc')
-      ->fields('fc', array('flagging_id'))
+      ->fields('fc', array('fid'))
       ->condition('fid', $flag->fid)
       ->countQuery()
       ->execute()
@@ -1822,7 +1878,14 @@ function flag_get_flags($entity_type = NULL, $content_subtype = NULL, $account =
     $query = db_select('flag', 'f');
     $query->leftJoin('flag_types', 'fn', 'fn.fid = f.fid');
     $result = $query
-      ->fields('f', array('fid', 'entity_type', 'name', 'title', 'global', 'options'))
+      ->fields('f', array(
+        'fid',
+        'entity_type',
+        'name',
+        'title',
+        'global',
+        'options',
+      ))
       ->fields('fn', array('type'))
       ->execute();
     foreach ($result as $row) {
@@ -1847,7 +1910,8 @@ function flag_get_flags($entity_type = NULL, $content_subtype = NULL, $account =
         // Ensure overridden flags are associated with their parent module.
         $flags[$name]->module = $default_flag->module;
 
-        // Update the flag with any properties that are "locked" by the code version.
+        // Update the flag with any properties that are "locked" by the code
+        // version.
         if (isset($default_flag->locked)) {
           $flags[$name]->locked = $default_flag->locked;
           foreach ($default_flag->locked as $property) {
@@ -1916,32 +1980,39 @@ function flag_get_default_flags($include_disabled = FALSE) {
   $default_flags = array();
   $flag_status = variable_get('flag_default_flag_status', array());
 
+  $default_flags_info = array();
   foreach (module_implements('flag_default_flags') as $module) {
     $function = $module . '_flag_default_flags';
     foreach ($function() as $flag_name => $flag_info) {
       // Backward compatibility: old exported default flags have their names
-      // in $flag_info instead, so we use the += operator to not overwrite it.
-      $flag_info += array(
+      // in $flag_info instead, so we use the + operator to not overwrite it.
+      $default_flags_info[$flag_name] = $flag_info + array(
         'name' => $flag_name,
         'module' => $module,
       );
-      $flag = flag_flag::factory_by_array($flag_info);
+    }
+  }
 
-      // Disable flags that are not at the current API version.
-      if (!$flag->is_compatible()) {
-        $flag->status = FALSE;
-      }
+  // Allow modules to alter definitions using hook_flag_default_flags_alter().
+  drupal_alter('flag_default_flags', $default_flags_info);
 
-      // Add flags that have been enabled.
-      if ((!isset($flag_status[$flag->name]) && (!isset($flag->status) || $flag->status)) || !empty($flag_status[$flag->name])) {
-        $flag->status = TRUE;
-        $default_flags[$flag->name] = $flag;
-      }
-      // Add flags that have been disabled.
-      elseif ($include_disabled) {
-        $flag->status = FALSE;
-        $default_flags[$flag->name] = $flag;
-      }
+  foreach ($default_flags_info as $flag_info) {
+    $flag = flag_flag::factory_by_array($flag_info);
+
+    // Disable flags that are not at the current API version.
+    if (!$flag->is_compatible()) {
+      $flag->status = FALSE;
+    }
+
+    // Add flags that have been enabled.
+    if ((!isset($flag_status[$flag->name]) && (!isset($flag->status) || $flag->status)) || !empty($flag_status[$flag->name])) {
+      $flag->status = TRUE;
+      $default_flags[$flag->name] = $flag;
+    }
+    // Add flags that have been disabled.
+    elseif ($include_disabled) {
+      $flag->status = FALSE;
+      $default_flags[$flag->name] = $flag;
     }
   }
 
@@ -1958,7 +2029,6 @@ function flag_get_default_flags($include_disabled = FALSE) {
  *   An array of flagging data, keyed by the flagging ID.
  */
 function flag_get_flag_flagging_data($flag_name) {
-  $return = array();
   $flag = flag_get_flag($flag_name);
   $result = db_select('flagging', 'fc')
     ->fields('fc')
@@ -1970,6 +2040,15 @@ function flag_get_flag_flagging_data($flag_name) {
 /**
  * Find what a user has flagged, either a single entity or on the entire site.
  *
+ * When called during a flagging or unflagging (such as from a hook
+ * implementation or from Rules), the flagging or unflagging that is in the
+ * process of being performed:
+ *  - will be included during a flagging operation
+ *  - will STILL be included during an unflagging operation. That is, the count
+ *    will not yet have been decreased.
+ * This is because this queries the {flagging} table, which only has its record
+ * deleted at the very end of the unflagging process.
+ *
  * @param $entity_type
  *   The type of entity that will be retrieved. Usually 'node'.
  * @param $entity_id
@@ -1986,11 +2065,15 @@ function flag_get_flag_flagging_data($flag_name) {
  * @return
  *   If returning a single item's flags (that is, when $entity_id isn't NULL),
  *   an array of the structure
- *   [flag_name] => (flagging_id => [flagging_id], uid => [uid], entity_id => [entity_id], timestamp => [timestamp], ...)
+ *   [flag_name] => (
+ *     flagging_id => [flagging_id],
+ *     uid => [uid],
+ *     entity_id => [entity_id],
+ *     timestamp => [timestamp],
+ *     ...)
  *
  *   If returning all items' flags, an array of arrays for each flag:
  *   [flag_name] => [entity_id] => Object from above.
- *
  */
 function flag_get_user_flags($entity_type, $entity_id = NULL, $uid = NULL, $sid = NULL) {
   $flagged_content = &drupal_static(__FUNCTION__);
@@ -2045,6 +2128,15 @@ function flag_get_user_flags($entity_type, $entity_id = NULL, $uid = NULL, $sid
 /**
  * Return a list of users who have flagged an entity.
  *
+ * When called during a flagging or unflagging (such as from a hook
+ * implementation or from Rules), the flagging or unflagging that is in the
+ * process of being performed:
+ *  - will be included during a flagging operation
+ *  - will STILL be included during an unflagging operation. That is, the count
+ *    will not yet have been decreased.
+ * This is because this queries the {flagging} table, which only has its record
+ * deleted at the very end of the unflagging process.
+ *
  * @param $entity_type
  *   The type of entity that will be retrieved. Usually 'node'.
  * @param $entity_id
@@ -2053,10 +2145,14 @@ function flag_get_user_flags($entity_type, $entity_id = NULL, $uid = NULL, $sid
  *   (optional) The name of a flag if wanting a list specific to a single flag.
  *
  * @return
- *   If no flag name is given, an array of flagging data, keyed by the user
- *   ID that flagged the entity. Each flagging array is structured as
- *   an array of flag information for each flag, keyed by the flag name. If
- *   a flag name is specified, only the information for that flag is returned.
+ *   A nested array of flagging records (i.e. rows from the {flagging} table,
+ *   rather than complete Flagging entities). The structure depends on the
+ *   presence of the $flag_name parameter:
+ *    - if $flag_name is omitted, the array is keyed first by the user ID of
+ *      the users that flagged the entity, then by flag name. Each value is
+ *      then the flagging record.
+ *    - if $flag_name is given, the array is keyed only by user ID. Each value
+ *      is the flagging record.
  *   If no flags were found an empty array is returned.
  */
 function flag_get_entity_flags($entity_type, $entity_id, $flag_name = NULL) {
@@ -2104,11 +2200,17 @@ function flag_get_entity_flags($entity_type, $entity_id, $flag_name = NULL) {
  *   The "machine readable" name of the flag; e.g. 'bookmarks'.
  * @param $entity_id
  *   The entity ID to check for flagging, for example a node ID.
+ * @param $variables
+ *  An array of further variables to pass to theme('flag'). For the full list
+ *  of parameters, see flag.tpl.php. Of particular interest:
+ *  - after_flagging: Set to TRUE if this flag link is being displayed as the
+ *    result of a flagging action.
+ *  - errors: An array of error messages.
  *
  * @return
  *   The HTML for the themed flag link.
  */
-function flag_create_link($flag_name, $entity_id) {
+function flag_create_link($flag_name, $entity_id, $variables = array()) {
   $flag = flag_get_flag($flag_name);
   if (!$flag) {
     // Flag does not exist.
@@ -2118,7 +2220,7 @@ function flag_create_link($flag_name, $entity_id) {
     // User has no permission to use this flag.
     return;
   }
-  return $flag->theme($flag->is_flagged($entity_id) ? 'unflag' : 'flag', $entity_id);
+  return $flag->theme($flag->is_flagged($entity_id) ? 'unflag' : 'flag', $entity_id, $variables);
 }
 
 /**
@@ -2150,10 +2252,10 @@ function flag_trim_flag($flag, $account, $cutoff_size, $trim_newest, $permission
     $query->orderBy('timestamp', 'ASC');
   }
   else {
-    $query->orderBy('timestamp', 'DESC') ;
+    $query->orderBy('timestamp', 'DESC');
   }
 
-  // Execute the query
+  // Execute the query.
   $result = $query->execute();
 
   $i = 1;
@@ -2189,7 +2291,7 @@ function flag_reset_flag($flag, $entity_id = NULL) {
   }
   module_invoke_all('flag_reset', $flag, $entity_id, $rows);
 
-  $query = db_delete('flagging')->condition('fid' , $flag->fid);
+  $query = db_delete('flagging')->condition('fid', $flag->fid);
   // Update the flag_counts table.
   $count_query = db_delete('flag_counts')->condition('fid', $flag->fid);
   if ($entity_id) {
@@ -2508,6 +2610,6 @@ function flag_properties_get_flagging_users($entity, array $options, $name, $ent
  * @ingroup callbacks
  */
 function flag_properties_get_user_sid($entity, array $options, $name, $entity_type, $property_info) {
-  $sid =  flag_get_sid($entity->uid, FALSE);
+  $sid = flag_get_sid($entity->uid, FALSE);
   return $sid;
 }

+ 54 - 18
sites/all/modules/contrib/flag/flag/flag.rules.inc

@@ -81,7 +81,7 @@ class FlagRulesUIClass extends RulesDataUI implements RulesDataDirectInputFormIn
       '#required' => empty($info['optional']),
       '#multiple' => FALSE,
       '#default_value' => $settings[$name],
-      '#empty' => t('There is no suiting flag available.')
+      '#empty' => t('There is no suiting flag available.'),
     );
     return $form;
   }
@@ -221,6 +221,8 @@ function flag_rules_action_info() {
       'provides' => array(
         'overall_flag_count' => array(
           'label' => t('Overall flag count'),
+          'description' => t('During a flagging/unflagging event the count
+            will take into account the current flagging/unflagging procedure.'),
           'type' => 'integer',
         ),
       ),
@@ -245,6 +247,10 @@ function flag_rules_action_info() {
       'provides' => array(
         'entity_flag_count' => array(
           'label' => t('Entity flag count'),
+          'description' => t('During a flagging event, the count
+            will take into account the current flagging procedure. For
+            an unflagging event, the count will NOT yet be decreased for the
+            current unflagging procedure.'),
           'type' => 'integer',
         ),
       ),
@@ -267,6 +273,10 @@ function flag_rules_action_info() {
       'provides' => array(
         'user_flag_count' => array(
           'label' => t('User flag count'),
+          'description' => t('During a flagging event, the count
+            will take into account the current flagging procedure. For
+            an unflagging event, the count will NOT yet be decreased for the
+            current unflagging procedure.'),
           'type' => 'integer',
         ),
       ),
@@ -365,9 +375,10 @@ function flag_rules_action_info() {
       'access callback' => 'flag_rules_integration_access',
     );
   }
-  // For backward compatibility sake. This was the original name of the 'fetch node by user'.
+  // For backward compatibility sake. This was the original name of the
+  // 'fetch node by user'.
   $items['flag_fetch_entity_by_user'] = $items['flag_fetch_node_by_user'];
-  $items['flag_fetch_entity_by_user']['label'] .= ' '. t('(Legacy)');
+  $items['flag_fetch_entity_by_user']['label'] .= ' ' . t('(Legacy)');
   return $items;
 }
 
@@ -414,19 +425,23 @@ function flag_rules_action_fetch_users($flag, $entity) {
 }
 
 /**
- * Base action implementation: Fetch entities who were flagged a user.
+ * Base action implementation: Fetch entities flagged by a user.
  */
 function flag_rules_action_fetch_entity_by_user($flag, $entity) {
-  $user = entity_metadata_wrapper('user', $entity);
-  $sid = $user->flag_sid->value();
   $query = db_select('flagging', 'fc')
     ->fields('fc', array('entity_id'))
     ->condition('entity_type', $flag->entity_type)
-    ->condition('uid', $user->uid->value())
     ->condition('fid', $flag->fid);
-  // Filter out any bad session ids and any users that aren't anonymous.
-  if (!empty($sid) && $sid != -1) {
-    $query->condition('sid', $sid);
+  // For global flags the user parameter is ignored, so we add the
+  // extra 'uid' condition when the flag is NOT global.
+  if (!$flag->global) {
+    $user = entity_metadata_wrapper('user', $entity);
+    $sid = $user->flag_sid->value();
+    $query = $query->condition('uid', $user->uid->value());
+    // Filter out any bad session ids and any users that aren't anonymous.
+    if (!empty($sid) && $sid != -1) {
+      $query->condition('sid', $sid);
+    }
   }
   $result = $query->execute();
   $flagged = $result->fetchCol();
@@ -435,6 +450,9 @@ function flag_rules_action_fetch_entity_by_user($flag, $entity) {
 
 /**
  * Base action implementation: Fetch overall count for a particular flag.
+ *
+ * The count that is returned during a flagging or an unflagging will take into
+ * account the current flag/unflag process.
  */
 function flag_rules_action_fetch_overall_flag_count($flag) {
   $count = flag_get_flag_counts($flag->name);
@@ -456,7 +474,12 @@ function flag_rules_get_flag_types() {
 }
 
 /**
- * Base action implementation: Fetch count of flags for a particular entity.
+ * Base action implementation: Fetch count of flags for a particular entity
+ * type.
+ *
+ * During a flagging, the current flagging will be included in the count.
+ * During an unflagging, the current flagging being removed will not yet have
+ * been removed from the count.
  */
 function flag_rules_action_fetch_entity_flag_count($flag, $entity_type) {
   $count = flag_get_entity_flag_counts($flag, $entity_type);
@@ -465,6 +488,10 @@ function flag_rules_action_fetch_entity_flag_count($flag, $entity_type) {
 
 /**
  * Base action implementation: Fetch user's flag count.
+ *
+ * During a flagging, the current flagging will be included in the count.
+ * During an unflagging, the current flagging will not yet have been removed
+ * from the count.
  */
 function flag_rules_action_fetch_user_flag_count($flag, $user) {
   $count = flag_get_user_flag_counts($flag, $user);
@@ -488,7 +515,7 @@ function flag_rules_condition_info() {
             'type' => 'flag',
             'label' => t('Flag'),
             'flag_type' => $type,
-            'description' => t('The flag to check for.')
+            'description' => t('The flag to check for.'),
           ),
           $type => array(
             'type' => $type,
@@ -497,7 +524,12 @@ function flag_rules_condition_info() {
           'number' => array(
             'type' => 'integer',
             'label' => t('Number'),
-            'description' => t('The number against which to test the number of times the object is flagged. For example, if you type "3" here, and choose "Greater than" for the operator, then this condition will return TRUE if the object is flagged more than three times.'),
+            'description' => t('The number against which to test the number of
+              times the object is flagged. For example, if you type "3" here,
+              and choose "Greater than" for the operator, then this condition
+              will return TRUE if the object is flagged more than three times.
+              During a flagging or an unflagging event the count will take into
+              account the current flag/unflag process.'),
           ),
           'operator' => array(
             'type' => 'text',
@@ -519,7 +551,7 @@ function flag_rules_condition_info() {
             'type' => 'flag',
             'label' => t('Flag'),
             'flag_type' => $type,
-            'description' => t('The flag to check for.')
+            'description' => t('The flag to check for.'),
           ),
           $type => array(
             'type' => $type,
@@ -540,7 +572,8 @@ function flag_rules_condition_info() {
 }
 
 /**
- * Options list callback for the operator parameter of the flagging threshold condition.
+ * Options list callback for the operator parameter of the flagging threshold
+ * condition.
  */
 function flag_rules_condition_threshold_operator_options() {
   return array(
@@ -554,15 +587,18 @@ function flag_rules_condition_threshold_operator_options() {
 
 /**
  * Condition: Check flagging count.
+ *
+ * The count that is returned during a flagging or an unflagging will take into
+ * acount the current flag/unflag process.
  */
 function flag_rules_condition_threshold($flag, $entity, $number, $operator = '=') {
   $count = $flag->get_count($flag->get_entity_id($entity));
 
   switch ($operator) {
-    case '>' : return $count >  $number;
+    case '>': return $count > $number;
     case '>=': return $count >= $number;
-    case '=' : return $count == $number;
-    case '<' : return $count <  $number;
+    case '=': return $count == $number;
+    case '<': return $count < $number;
     case '<=': return $count <= $number;
   }
 }

+ 48 - 24
sites/all/modules/contrib/flag/flag/flag.tokens.inc

@@ -7,6 +7,8 @@
 
 /**
  * Implements of hook_token_info().
+ *
+ * The tokens we provide on generic entities require token module.
  */
 function flag_token_info() {
   $types = array();
@@ -74,18 +76,29 @@ function flag_token_info() {
     'description' => t('The current count total for this flag.'),
   );
 
-  // Add tokens for the flag count available at the node/comment/user level.
-  foreach (flag_get_types() as $flag_type) {
-    $flags = flag_get_flags($flag_type);
-    foreach ($flags as $flag) {
-      $tokens[$flag_type]['flag-' . str_replace('_', '-', $flag->name) . '-count'] = array(
-        'name' => t('@flag flag count', array('@flag' => $flag->get_title())),
-        'description' => t('Total flag count for flag @flag', array('@flag' => $flag->get_title())),
-      );
-      $tokens[$flag_type]['flag-' . str_replace('_', '-', $flag->name) . '-link'] = array(
-        'name' => t('@flag flag link', array('@flag' => $flag->get_title())),
-        'description' => t('Flag/unflag link for @flag', array('@flag' => $flag->get_title())),
-      );
+  // Add tokens for the flag count available at the entity level.
+  // These require token module because we need its helper data and functions
+  // to deal with token types that are not the same as the entity types they are
+  // for (in particular, terms and vocabularies).
+  if (module_exists('token')) {
+    $entity_info = entity_get_info();
+    foreach (flag_get_types() as $flag_type) {
+      // The flag type is the entity type, but this is not necessarily the same
+      // as the entity's token type.
+      $token_type = $entity_info[$flag_type]['token type'];
+      $flags = flag_get_flags($flag_type);
+      foreach ($flags as $flag) {
+        $tokens[$token_type]['flag-' . str_replace('_', '-', $flag->name) . '-count'] = array(
+          'name' => t('@flag flag count', array('@flag' => $flag->get_title())),
+          'description' => t('Total flag count for flag @flag', array('@flag' => $flag->get_title())),
+          'flag-type' => $flag_type,
+        );
+        $tokens[$token_type]['flag-' . str_replace('_', '-', $flag->name) . '-link'] = array(
+          'name' => t('@flag flag link', array('@flag' => $flag->get_title())),
+          'description' => t('Flag/unflag link for @flag', array('@flag' => $flag->get_title())),
+          'flag-type' => $flag_type,
+        );
+      }
     }
   }
 
@@ -110,6 +123,7 @@ function flag_tokens($type, $tokens, array $data = array(), array $options = arr
         case 'name':
           $replacements[$original] = $sanitize ? check_plain($flag->name) : $flag->name;
           break;
+
         case 'title':
           $replacements[$original] = $sanitize ? check_plain($flag->get_title()) : $flag->get_title();
           break;
@@ -136,18 +150,23 @@ function flag_tokens($type, $tokens, array $data = array(), array $options = arr
         case 'action':
           $replacements[$original] = $action->action;
           break;
+
         case 'entity-url':
           $replacements[$original] = $sanitize ? check_url($action->entity_url) : $action->entity_url;
           break;
+
         case 'entity-title':
           $replacements[$original] = $sanitize ? check_plain($action->entity_title) : $action->entity_title;
           break;
+
         case 'entity-type':
           $replacements[$original] = $action->entity_type;
           break;
+
         case 'entity-id':
           $replacements[$original] = $action->entity_id;
           break;
+
         case 'count':
           $replacements[$original] = $action->count;
           break;
@@ -155,18 +174,23 @@ function flag_tokens($type, $tokens, array $data = array(), array $options = arr
     }
   }
 
-  if (isset($data[$type]) && in_array($type, flag_get_types())) {
-    $flags = flag_get_flags($type);
-    $object = $data[$type];
-    foreach ($flags as $flag) {
-      foreach ($tokens as $name => $original) {
-        $flag_count_token = 'flag-' . str_replace('_', '-', $flag->name) . '-count';
-        $flag_link_token = 'flag-' . str_replace('_', '-', $flag->name) . '-link';
-        if ($name == $flag_count_token) {
-          $replacements[$original] = $flag->get_count($flag->get_entity_id($object));
-        }
-        elseif ($name == $flag_link_token) {
-          $replacements[$original] = flag_create_link($flag->name, $flag->get_entity_id($object));
+  // We only provide tokens on entity types if we have token module's helper
+  // methods available.
+  if (isset($data[$type]) && module_exists('token')) {
+    $entity_type = token_get_entity_mapping('token', $type);
+    if ($entity_type && in_array($entity_type, flag_get_types())) {
+      $flags = flag_get_flags($entity_type);
+      $object = $data[$type];
+      foreach ($flags as $flag) {
+        foreach ($tokens as $name => $original) {
+          $flag_count_token = 'flag-' . str_replace('_', '-', $flag->name) . '-count';
+          $flag_link_token = 'flag-' . str_replace('_', '-', $flag->name) . '-link';
+          if ($name == $flag_count_token) {
+            $replacements[$original] = $flag->get_count($flag->get_entity_id($object));
+          }
+          elseif ($name == $flag_link_token) {
+            $replacements[$original] = flag_create_link($flag->name, $flag->get_entity_id($object));
+          }
         }
       }
     }

+ 3 - 3
sites/all/modules/contrib/flag/flag/flag_actions.info

@@ -7,9 +7,9 @@ configure = admin/structure/flags/actions
 
 files[] = flag.install
 files[] = flag_actions.module
-; Information added by drupal.org packaging script on 2013-09-13
-version = "7.x-3.2"
+; Information added by Drupal.org packaging script on 2015-03-02
+version = "7.x-3.6"
 core = "7.x"
 project = "flag"
-datestamp = "1379063829"
+datestamp = "1425327793"
 

+ 6 - 7
sites/all/modules/contrib/flag/flag/flag_actions.module

@@ -379,7 +379,7 @@ function theme_flag_actions_add_form($variables) {
   $fieldset = array(
     '#type' => 'fieldset',
     '#title' => t('Add a new flag action'),
-    '#children' => '<div class="container-inline">'. drupal_render($form['flag']) . drupal_render($form['submit']) .'</div>',
+    '#children' => '<div class="container-inline">' . drupal_render($form['flag']) . drupal_render($form['submit']) . '</div>',
     '#parents' => array('add_action'),
     '#attributes' => array(),
     '#groups' => array('add_action' => array()),
@@ -397,7 +397,6 @@ function theme_flag_actions_add_form($variables) {
  *   If editing an action, an action ID must be passed in.
  * @param $flag_name
  *   If adding a new action to a flag, a flag name must be specified.
- *
  */
 function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL) {
   // This is a multistep form. Get the callback value if set and continue.
@@ -422,7 +421,7 @@ function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL)
 
   $form['new'] = array(
     '#type' => 'value',
-    '#value' => isset($callback) ? FALSE: TRUE,
+    '#value' => isset($callback) ? FALSE : TRUE,
   );
 
   if (!isset($callback)) {
@@ -430,7 +429,7 @@ function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL)
 
     $actions = $flag->get_valid_actions();
     $options = array();
-    foreach($actions as $key => $action) {
+    foreach ($actions as $key => $action) {
       $options[$key] = $action['label'];
     }
 
@@ -449,7 +448,7 @@ function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL)
   }
   elseif (!isset($action)) {
     $actions = $flag->get_valid_actions();
-    $action = (object)$actions[$callback];
+    $action = (object) $actions[$callback];
     $action->parameters = array();
     $action->event = 'flag';
     $action->threshold = 10;
@@ -512,7 +511,7 @@ function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL)
   }
 
   // Merge in the standard flag action form.
-  $action_form = $callback .'_form';
+  $action_form = $callback . '_form';
   $edit = array();
   if (function_exists($action_form)) {
     $edit += $action->parameters;
@@ -524,7 +523,7 @@ function flag_actions_form($form, &$form_state, $aid = NULL, $flag_name = NULL)
   }
 
   // Add a few customizations to existing flag actions.
-  $flag_actions_form = 'flag_actions_'. $callback .'_form';
+  $flag_actions_form = 'flag_actions_' . $callback . '_form';
   if (function_exists($flag_actions_form)) {
     $flag_actions_form($form, $flag, $edit);
   }

+ 3 - 3
sites/all/modules/contrib/flag/flag/flag_bookmark/flag_bookmark.info

@@ -4,9 +4,9 @@ core = 7.x
 dependencies[] = flag
 package = Flags
 
-; Information added by drupal.org packaging script on 2013-09-13
-version = "7.x-3.2"
+; Information added by Drupal.org packaging script on 2015-03-02
+version = "7.x-3.6"
 core = "7.x"
 project = "flag"
-datestamp = "1379063829"
+datestamp = "1425327793"
 

+ 21 - 21
sites/all/modules/contrib/flag/flag/flag_bookmark/flag_bookmark.install

@@ -13,7 +13,7 @@
  */
 function flag_bookmark_enable() {
   // Load the flag API in case we want to use it when enabling.
-  include_once(drupal_get_path('module', 'flag') . '/flag.module');
+  include_once drupal_get_path('module', 'flag') . '/flag.module';
 
   if (!flag_get_flags()) {
     // Install a demonstration flag only if no flag exists. This is to prevent
@@ -21,24 +21,24 @@ function flag_bookmark_enable() {
     // flag is overwritten or re-created.
     $flag = flag_flag::factory_by_entity_type('node');
     $configuration = array(
-        'name' => 'bookmarks',
-        'global' => 0,
-        'show_in_links' => array(
-          'full' => 1,
-          'teaser' => 1,
-        ),
-        'show_on_form' => 1,
-        // The following UI labels aren't wrapped in t() because they are written
-        // to the DB in English. They are passed to t() later, thus allowing for
-        // multilingual sites.
-        'title' => 'Bookmarks',
-        'flag_short' => 'Bookmark this',
-        'flag_long' => 'Add this post to your bookmarks',
-        'flag_message' => 'This post has been added to your bookmarks',
-        'unflag_short' => 'Unbookmark this',
-        'unflag_long' => 'Remove this post from your bookmarks',
-        'unflag_message' => 'This post has been removed from your bookmarks',
-        'types' => _flag_bookmark_install_get_suggested_node_types(),
+      'name' => 'bookmarks',
+      'global' => 0,
+      'show_in_links' => array(
+        'full' => 1,
+        'teaser' => 1,
+      ),
+      'show_on_form' => 1,
+      // The following UI labels aren't wrapped in t() because they are written
+      // to the DB in English. They are passed to t() later, thus allowing for
+      // multilingual sites.
+      'title' => 'Bookmarks',
+      'flag_short' => 'Bookmark this',
+      'flag_long' => 'Add this post to your bookmarks',
+      'flag_message' => 'This post has been added to your bookmarks',
+      'unflag_short' => 'Unbookmark this',
+      'unflag_long' => 'Remove this post from your bookmarks',
+      'unflag_message' => 'This post has been removed from your bookmarks',
+      'types' => _flag_bookmark_install_get_suggested_node_types(),
     );
     $flag->form_input($configuration);
     $flag->save();
@@ -53,7 +53,8 @@ function flag_bookmark_enable() {
 }
 
 /**
- * Returns some node types to which the demonstration 'bookmarks' flag will apply.
+ * Returns some node types to which the demonstration 'bookmarks' flag will
+ * apply.
  */
 function _flag_bookmark_install_get_suggested_node_types() {
   $preferred = array('article', 'story', 'forum', 'blog');
@@ -64,4 +65,3 @@ function _flag_bookmark_install_get_suggested_node_types() {
   }
   return $existing;
 }
-

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/flag.actions.inc

@@ -15,7 +15,7 @@ function flag_trigger_info() {
         'label' => t('Object has been flagged with any flag'),
       ),
       'flag_unflag' => array(
-        'label' => t('Object has been unflagged with any flag')
+        'label' => t('Object has been unflagged with any flag'),
       ),
     ),
   );

+ 52 - 29
sites/all/modules/contrib/flag/flag/includes/flag.admin.inc

@@ -77,10 +77,10 @@ function theme_flag_admin_listing($variables) {
 
   foreach ($flags as $flag) {
     $ops = array(
-      'flags_edit' =>  array('title' => t('edit'), 'href' => $flag->admin_path('edit')),
-      'flags_fields' =>  array('title' => t('manage fields'), 'href' => $flag->admin_path('fields')),
-      'flags_delete' =>  array('title' => t('delete'), 'href' => $flag->admin_path('delete')),
-      'flags_export' =>  array('title' => t('export'), 'href' => $flag->admin_path('export')),
+      'flags_edit' => array('title' => t('edit'), 'href' => $flag->admin_path('edit')),
+      'flags_fields' => array('title' => t('manage fields'), 'href' => $flag->admin_path('fields')),
+      'flags_delete' => array('title' => t('delete'), 'href' => $flag->admin_path('delete')),
+      'flags_export' => array('title' => t('export'), 'href' => $flag->admin_path('export')),
     );
     if (!module_exists('field_ui')) {
       unset($ops['flags_fields']);
@@ -116,7 +116,13 @@ function theme_flag_admin_listing($variables) {
   if (count($flags) > 1) {
     $header[] = t('Weight');
   }
-  $header = array_merge($header, array(t('Flag type'), t('Roles'), t('Entity bundles'), t('Global?'), t('Operations')));
+  $header = array_merge($header, array(
+    t('Flag type'),
+    t('Roles'),
+    t('Entity bundles'),
+    t('Global?'),
+    t('Operations'),
+  ));
   $output .= theme('table', array(
     'header' => $header,
     'rows' => $rows,
@@ -142,7 +148,11 @@ function theme_flag_admin_listing_disabled($variables) {
       $ops = array();
       if (!$flag->is_compatible()) {
         $flag_updates_needed = TRUE;
-        $ops['flags_update'] = array('title' => '<strong>' . t('update code') . '</strong>', 'href' => $flag->admin_path('update'), 'html' => TRUE);
+        $ops['flags_update'] = array(
+          'title' => '<strong>' . t('update code') . '</strong>',
+          'href' => $flag->admin_path('update'),
+          'html' => TRUE,
+        );
       }
       else {
         $ops['flags_enable'] = array('title' => t('enable'), 'href' => $flag->admin_path('edit'));
@@ -163,7 +173,12 @@ function theme_flag_admin_listing_disabled($variables) {
   }
 
   if (!empty($rows)) {
-    $header = array(t('Disabled Flags'), t('Module'), t('Flag type'), t('Operations'));
+    $header = array(
+      t('Disabled Flags'),
+      t('Module'),
+      t('Flag type'),
+      t('Operations'),
+    );
     $output .= theme('table', array('header' => $header, 'rows' => $rows));
   }
 
@@ -379,17 +394,17 @@ function flag_form($form, &$form_state, $flag) {
     '#title' => t('Token replacement'),
     '#type' => 'fieldset',
     '#description' =>
-      '<p>' . t('The above six texts may contain any of the tokens listed below. For example, <em>"Flag link text"</em> could be entered as:') . '</p>' .
-      theme('item_list', array(
-        'items' => array(
-          t('Add &lt;em&gt;[node:title]&lt;/em&gt; to your favorites'),
-          t('Add this [node:type] to your favorites'),
-          t('Vote for this proposal ([node:flag-vote-count] people have already done so)'),
-        ),
-        'attributes' => array('class' => 'token-examples'),
-      )) .
-      '<p>' . t('These tokens will be replaced with the appropriate fields from the node (or user, or comment).') . '</p>' .
-      theme('flag_tokens_browser', array('types' => $flag->get_labels_token_types())),
+    '<p>' . t('The above six texts may contain any of the tokens listed below. For example, <em>"Flag link text"</em> could be entered as:') . '</p>' .
+    theme('item_list', array(
+      'items' => array(
+        t('Add &lt;em&gt;[node:title]&lt;/em&gt; to your favorites'),
+        t('Add this [node:type] to your favorites'),
+        t('Vote for this proposal ([node:flag-vote-count] people have already done so)'),
+      ),
+      'attributes' => array('class' => 'token-examples'),
+    )) .
+    '<p>' . t('These tokens will be replaced with the appropriate fields from the node (or user, or comment).') . '</p>' .
+    theme('flag_tokens_browser', array('types' => $flag->get_labels_token_types())),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
   );
@@ -529,6 +544,8 @@ function flag_form($form, &$form_state, $flag) {
     '#title' => t('Flag confirmation message'),
     '#default_value' => isset($flag->flag_confirmation) ? $flag->flag_confirmation : '',
     '#description' => t('Message displayed if the user has clicked the "flag this" link and confirmation is required. Usually presented in the form of a question such as, "Are you sure you want to flag this content?"'),
+    // This will get changed to a state by flag_link_type_options_states().
+    '#required' => TRUE,
   );
 
   $form['display']['link_options_confirm']['unflag_confirmation'] = array(
@@ -536,6 +553,8 @@ function flag_form($form, &$form_state, $flag) {
     '#title' => t('Unflag confirmation message'),
     '#default_value' => isset($flag->unflag_confirmation) ? $flag->unflag_confirmation : '',
     '#description' => t('Message displayed if the user has clicked the "unflag this" link and confirmation is required. Usually presented in the form of a question such as, "Are you sure you want to unflag this content?"'),
+    // This will get changed to a state by flag_link_type_options_states().
+    '#required' => TRUE,
   );
 
   $form['actions'] = array(
@@ -561,7 +580,7 @@ function flag_form($form, &$form_state, $flag) {
 }
 
 /**
- * FormAPI after_build function set states on link type options fieldsets.
+ * FormAPI after_build function to set states on link type options fieldsets.
  *
  * We do this in an after build so we handle further link types fieldsets from
  * other modules that provide link types.
@@ -581,6 +600,20 @@ function flag_link_type_options_states($element) {
           ':input[name="link_type"]' => array('value' => $radio_value),
         ),
       );
+
+      // If an element in a link type options fieldset is required, then we
+      // remove this, as this would break the form, by demanding the user
+      // enter a value for a form element they possibly can't see!
+      // Instead, we set the required property as a state.
+      foreach (element_children($element[$key]) as $child_key) {
+        if (!empty($element[$key][$child_key]['#required'])) {
+          $element[$key][$child_key]['#required'] = FALSE;
+          $element[$key][$child_key]['#states']['required'] = array(
+            ':input[name="link_type"]' => array('value' => $radio_value),
+          );
+        }
+      }
+
       // Gather up the radio values for the format we need for a multiple
       // value state.
       $intro_element_values_array[] = array('value' => $radio_value);
@@ -635,16 +668,6 @@ function flag_form_validate($form, &$form_state) {
   $form_state['values']['title'] = trim($form_state['values']['title']);
   $form_values = $form_state['values'];
 
-  if ($form_values['link_type'] == 'confirm') {
-    if (empty($form_values['flag_confirmation'])) {
-      form_set_error('flag_confirmation', t('A flag confirmation message is required when using the confirmation link type.'));
-    }
-    if (empty($form_values['unflag_confirmation'])) {
-      form_set_error('unflag_confirmation', t('An unflag confirmation message is required when using the confirmation link type.'));
-    }
-  }
-
-
   $flag = $form['#flag'];
   $flag->form_input($form_values);
   $errors = $flag->validate();

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/flag.cookie_storage.inc

@@ -2,7 +2,7 @@
 
 /**
  * @file
- *   Contains the FlagCookieStorage class.
+ * Contains the FlagCookieStorage class.
  */
 
 /**

+ 5 - 3
sites/all/modules/contrib/flag/flag/includes/flag.export.inc

@@ -15,7 +15,8 @@
  *   in hook_flag_default_flags().
  */
 function flag_export_flags($flags = array(), $module = '', $indent = '') {
-  module_load_include('inc', 'features', 'features.export'); // For features_var_export() (optional).
+  // For features_var_export() (optional).
+  module_load_include('inc', 'features', 'features.export');
   $output = $indent . '$flags = array();' . "\n";
   foreach ($flags as $item) {
     if (is_object($item)) {
@@ -269,7 +270,7 @@ function flag_update_export(&$flag) {
     if (substr($class, 0, 11) == 'FlagUpdate_') {
       // @todo: change this to work with the static class when we drop support
       // for PHP 5.2: see commit d5b517.
-      $update_handler = new $class;
+      $update_handler = new $class();
       // Cast to string, as decimals as array keys seem to be rounded down to
       // ints, WTF PHP?
       $version = (string) $update_handler->old_api_version;
@@ -364,7 +365,8 @@ class FlagUpdate_3 {
     }
 
     // Update show_on_comment and show_on_entity properties to use new view
-    // mode settings. Since the old logic was to show on all view modes, do that.
+    // mode settings. Since the old logic was to show on all view modes, do
+    // that.
     if (!empty($flag->show_on_entity) || !empty($flag->show_on_comment)) {
       if ($entity_info = entity_get_info($flag->entity_type)) {
         foreach ($entity_info['view modes'] as $view_mode => $value) {

+ 37 - 3
sites/all/modules/contrib/flag/flag/includes/flag.pages.inc

@@ -11,7 +11,11 @@
  * Used both for the regular callback as well as the JS version.
  *
  * @param $action
- *   Either 'flag' or 'unflag'.
+ *   The action about to be performed. One of 'flag' or 'unflag'.
+ * @param $flag
+ *   The flag object.
+ * @param $entity_id
+ *   The ID of the entity to be acted upon. The type is implicit in the flag.
  */
 function flag_page($action, $flag, $entity_id) {
   global $user;
@@ -93,7 +97,7 @@ function flag_confirm($form, &$form_state, $action, $flag, $entity_id) {
 
   $question = $flag->get_label($action . '_confirmation', $entity_id);
   $path = isset($_GET['destination']) ? $_GET['destination'] : '<front>';
-  $yes = $flag->get_label($action . '_short', $entity_id);
+  $yes = strip_tags($flag->get_label($action . '_short', $entity_id));
 
   if ($action == 'flag') {
     // If the action 'flag', we're potentially about to create a new
@@ -101,11 +105,41 @@ function flag_confirm($form, &$form_state, $action, $flag, $entity_id) {
     $flagging = $flag->new_flagging($entity_id);
     field_attach_form('flagging', $flagging, $form, $form_state);
     $form['#flagging'] = $flagging;
+
+    // Take the same approach as Core entity forms: shove all the entity
+    // properties into the form as values so that entity_form_field_validate()
+    // can build a pseudoentity from $form_values in the validate handler.
+    foreach (array(
+      'flag_name',
+      'entity_type',
+      'entity_id',
+      'uid',
+    ) as $key) {
+      $form[$key] = array(
+        '#type' => 'value',
+        '#value' => isset($flagging->$key) ? $flagging->$key : NULL,
+      );
+    }
   }
 
   return confirm_form($form, $question, $path, '', $yes);
 }
 
+/**
+ * Validate handler for the flag confirm form.
+ *
+ * Validate any Field API fields on the Flagging.
+ *
+ * @see flag_confirm()
+ */
+function flag_confirm_validate($form, &$form_state) {
+  // Only validate the entity fields when we're saving an entity.
+  $action = $form_state['values']['action'];
+  if ($action == 'flag') {
+    entity_form_field_validate('flagging', $form, $form_state);
+  }
+}
+
 /**
  * Submit handler for the flag confirm form.
  *
@@ -160,6 +194,6 @@ function flag_build_javascript_info($flag, $entity_id) {
     'flagName' => $flag->name,
     'flagStatus' => $flag->is_flagged($entity_id) ? 'flagged' : 'unflagged',
   );
-  drupal_alter('flag_javascript_info', $info);
+  drupal_alter('flag_javascript_info', $info, $flag);
   return $info;
 }

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/flag/flag_comment.inc

@@ -2,7 +2,7 @@
 
 /**
  * @file
- *   Contains the flag_comment class.
+ * Contains the flag_comment class.
  */
 
 /**

+ 47 - 3
sites/all/modules/contrib/flag/flag/includes/flag/flag_entity.inc

@@ -2,7 +2,7 @@
 
 /**
  * @file
- *   Contains the flag_entity class.
+ * Contains the flag_entity class.
  */
 
 /**
@@ -51,10 +51,17 @@ class flag_entity extends flag_flag {
     // Add checkboxes to show flag link on each entity view mode.
     $options = array();
     $defaults = array();
-    foreach ($entity_info['view modes'] as $name => $view_mode) {
+    $entity_view_modes = $entity_info['view modes'];
+    foreach ($entity_view_modes as $name => $view_mode) {
       $options[$name] = t('Display on @name view mode', array('@name' => $view_mode['label']));
       $defaults[$name] = !empty($this->show_in_links[$name]) ? $name : 0;
     }
+    // Select the first display option by default if this is a new flag.
+    if (empty($this->fid)) {
+      $first_view_mode_keys = array_keys($entity_view_modes);
+      $first_view_mode = reset($first_view_mode_keys);
+      $defaults[$first_view_mode] = $first_view_mode;
+    }
 
     $form['display']['show_in_links'] = array(
       '#type' => 'checkboxes',
@@ -153,6 +160,37 @@ class flag_entity extends flag_flag {
     );
   }
 
+  /**
+   * Invoke a Rules event in reaction to a flagging or unflagging.
+   *
+   * @param $action
+   *   Either 'flag' or 'unflag'.
+   * @param $flagging
+   *  The flagging entity that is either newly created or about to be deleted.
+   * @param $entity_id
+   *  The entity ID of entity being flagged or unflagged.
+   * @param $account
+   *  The account performing the action.
+   */
+  protected function invoke_rules_event($action, $flagging, $entity_id, $account) {
+    switch ($action) {
+      case 'flag':
+        $event_name = 'flag_flagged_' . $this->name;
+        break;
+      case 'unflag':
+        $event_name = 'flag_unflagged_' . $this->name;
+        break;
+    }
+
+    $variables = array(
+      'flag' => $this,
+      'flagged_' . $this->entity_type => $entity_id,
+      'flagging_user' => $account,
+      'flagging' => $flagging,
+    );
+    rules_invoke_event_by_args($event_name, $variables);
+  }
+
   /**
    * Returns the entity id, if it already exists.
    */
@@ -180,7 +218,13 @@ class flag_entity extends flag_flag {
   function get_labels_token_types() {
     // The token type name might be different to the entity type name. If so,
     // an own flag entity handler can be used for overriding this.
-    return array_merge(array($this->entity_type), parent::get_labels_token_types());
+    $entity_info = entity_get_info($this->entity_type);
+    if (isset($entity_info['token type'])) {
+      return array_merge(array($entity_info['token type']), parent::get_labels_token_types());
+    }
+    else {
+      return array_merge(array($this->entity_type), parent::get_labels_token_types());
+    }
   }
 
   /**

+ 234 - 175
sites/all/modules/contrib/flag/flag/includes/flag/flag_flag.inc

@@ -2,13 +2,14 @@
 
 /**
  * @file
- *  Contains the flag_flag class.
- *  Flag type classes use an object oriented style inspired by that
- *  of Views 2.
+ * Contains the flag_flag class.
+ * Flag type classes use an object oriented style inspired by that
+ * of Views 2.
  */
 
 /**
- * This abstract class represents a flag, or, in Views 2 terminology, "a handler".
+ * This abstract class represents a flag, or, in Views 2 terminology,
+ * "a handler".
  *
  * This is the base class for all flag implementations. Notable derived
  * classes are flag_node and flag_comment.
@@ -29,8 +30,8 @@
  * - flag_flag::factory_by_array(), creates a flag handler from a configuration
  *   array. This is used by flag_get_default_flags() and the flag import form.
  * - flag_flag::factory_by_entity_type(), creates an empty flag handler for the
- *   given entity type. This is used when a new or dummy flag handler is required
- *   and there is no configuration yet.
+ *   given entity type. This is used when a new or dummy flag handler is
+ *   required and there is no configuration yet.
  *
  * The factory methods in turn all call the low-level function
  * flag_create_handler(), which obtains the correct handler for the flag, or if
@@ -94,7 +95,8 @@ class flag_flag {
   );
 
   /**
-   * An associative array containing textual errors that may be created during validation.
+   * An associative array containing textual errors that may be created during
+   * validation.
    *
    * The array keys should reflect the type of error being set. At this time,
    * the only "special" behavior related to the array keys is that
@@ -103,7 +105,7 @@ class flag_flag {
    *
    * @var array
    */
-  var $errors = array();
+  public $errors = array();
 
   /**
    * Creates a flag from a database row. Returns it.
@@ -135,7 +137,8 @@ class flag_flag {
     }
 
     if (!empty($row->type)) {
-      // The loop loading from the database should further populate this property.
+      // The loop loading from the database should further populate this
+      // property.
       $flag->types[] = $row->type;
     }
 
@@ -189,7 +192,8 @@ class flag_flag {
       'unflag_long' => '',
       'unflag_message' => '',
       'unflag_denied_text' => '',
-      // The link type used by the flag, as defined in hook_flag_link_type_info().
+      // The link type used by the flag, as defined in
+      // hook_flag_link_type_info().
       'link_type' => 'toggle',
       'weight' => 0,
     );
@@ -245,7 +249,8 @@ class flag_flag {
    */
   function form_input($form_values) {
     // Load the form fields indiscriminately unto the flag (we don't care about
-    // stray FormAPI fields because we aren't touching unknown properties anyway.
+    // stray FormAPI fields because we aren't touching unknown properties
+    // anyway).
     foreach ($form_values as $field => $value) {
       $this->$field = $value;
     }
@@ -626,18 +631,18 @@ class flag_flag {
    *   TRUE if the flag is enabled for this type and subtype.
    */
   function access_entity_enabled($entity_type, $content_subtype = NULL) {
-   $entity_type_matches = ($this->entity_type == $entity_type);
-   $sub_type_matches = FALSE;
-   if (!isset($content_subtype) || !count($this->types)) {
-     // Subtype automatically matches if we're not asked about it,
-     // or if the flag applies to all subtypes.
-     $sub_type_matches = TRUE;
-   }
-   else {
-     $sub_type_matches = in_array($content_subtype, $this->types);
-   }
-   return $entity_type_matches && $sub_type_matches;
- }
+    $entity_type_matches = ($this->entity_type == $entity_type);
+    $sub_type_matches = FALSE;
+    if (!isset($content_subtype) || !count($this->types)) {
+      // Subtype automatically matches if we're not asked about it,
+      // or if the flag applies to all subtypes.
+      $sub_type_matches = TRUE;
+    }
+    else {
+      $sub_type_matches = in_array($content_subtype, $this->types);
+    }
+    return $entity_type_matches && $sub_type_matches;
+  }
 
   /**
    * Determine whether the flag should show a flag link in entity links.
@@ -676,7 +681,19 @@ class flag_flag {
    *   Flag the item even if the $account user don't have permission to do so.
    * @param $flagging
    *   (optional) This method works in tandem with Drupal's Field subsystem.
-   *   Pass in a flagging entity if you want operate on it as well.
+   *   Pass in a Flagging entity if you want operate on it as well. This may be
+   *   used either of the following cases:
+   *   - to save field data on a new Flagging entity at the same time as
+   *     flagging an entity. In this case, using Entity API's entity_create()
+   *     is recommended, although the Flagging entity may also be created
+   *     directly as a new stdClass object.
+   *   - to update field data an existing flagging. The $action parameter should
+   *     be set to 'flag'. The Flagging entity will need to be loaded first with
+   *     flagging_load().
+   *  As with Drupal core API functions for saving entities, no validation of
+   *  Field API fields is performed here. It is the responsibility of the caller
+   *  to take care of Field API validation, using either
+   *  field_attach_form_validate() or field_attach_validate().
    *
    * @return
    *   FALSE if some error occured (e.g., user has no permission, flag isn't
@@ -687,9 +704,6 @@ class flag_flag {
     if (!isset($account)) {
       $account = $GLOBALS['user'];
     }
-    if (!$account) {
-      return FALSE;
-    }
 
     // Check access and applicability.
     if (!$skip_permission_check) {
@@ -712,12 +726,6 @@ class flag_flag {
       return FALSE;
     }
 
-    // Clear various caches; We don't want code running after us to report
-    // wrong counts or false flaggings.
-    drupal_static_reset('flag_get_counts');
-    drupal_static_reset('flag_get_user_flags');
-    drupal_static_reset('flag_get_entity_flags');
-
     // Find out which user id to use.
     $uid = $this->global ? 0 : $account->uid;
 
@@ -734,105 +742,47 @@ class flag_flag {
       }
     }
 
-    // Set our uid and sid to the flagging object.
+    // @todo: Discuss: Core wraps everything in a try { }, should we?
+
+    $existing_flagging_id = $this->_is_flagged($entity_id, $uid, $sid);
+    $flagged = (bool) $existing_flagging_id;
+
+    // Ensure we have a Flagging entity and it is correctly formed.
     if (isset($flagging)) {
+      // We were given a Flagging entity.
+      // Ensure that it has the uid and sid that we were also given.
       $flagging->uid = $uid;
       $flagging->sid = $sid;
-    }
 
-    // @todo: Discuss: Should we call field_attach_validate()? None of the
-    // entities in core does this (fields entered through forms are already
-    // validated).
-    //
-    // @todo: Discuss: Core wraps everything in a try { }, should we?
+      // This is an ugly hack to preserve previous behaviour.
+      $flagging->given_as_parameter = TRUE;
+    }
+    else {
+      // We were not given a Flagging entity.
+      if ($flagged) {
+        // Load the existing Flagging entity.
+        $flagging = flagging_load($existing_flagging_id);
+      }
+      else {
+        // Construct a new Flagging entity to flag with.
+        $flagging = $this->new_flagging($entity_id, $uid, $sid);
+      }
+    }
 
     // Perform the flagging or unflagging of this flag.
-    $existing_flagging_id = $this->_is_flagged($entity_id, $uid, $sid);
-    $flagged = (bool) $existing_flagging_id;
     if ($action == 'unflag') {
-      if ($this->uses_anonymous_cookies()) {
-        $this->_unflag_anonymous($entity_id);
-      }
       if ($flagged) {
-        if (!isset($flagging)) {
-          $flagging = flagging_load($existing_flagging_id);
-        }
-        $transaction = db_transaction();
-        try {
-          // Note the order: We decrease the count first so hooks have accurate
-          // data, then invoke hooks, then delete the flagging entity.
-          $this->_decrease_count($entity_id);
-          module_invoke_all('flag_unflag', $this, $entity_id, $account, $flagging);
-          // Invoke Rules event.
-          if (module_exists('rules')) {
-            $event_name = 'flag_unflagged_' . $this->name;
-            // We only support flags on entities.
-            if (entity_get_info($this->entity_type)) {
-              $variables = array(
-                'flag' => $this,
-                'flagged_' . $this->entity_type => $entity_id,
-                'flagging_user' => $account,
-                'flagging' => $flagging,
-              );
-              rules_invoke_event_by_args($event_name, $variables);
-            }
-          }
-          $this->_delete_flagging($flagging);
-          $this->_unflag($entity_id, $flagging->flagging_id);
-        }
-        catch (Exception $e) {
-          $transaction->rollback();
-          watchdog_exception('flag', $e);
-          throw $e;
-        }
+        $this->flagging_delete($flagging, $entity_id, $account);
       }
+      // We do nothing in the case of an attempt to unflag something that isn't
+      // actually flagged.
     }
     elseif ($action == 'flag') {
-      if ($this->uses_anonymous_cookies()) {
-        $this->_flag_anonymous($entity_id);
-      }
       if (!$flagged) {
-        // The entity is unflagged. By definition there is no flagging entity,
-        // but we may have been passed one in to save.
-        if (!isset($flagging)) {
-          // Construct a new flagging object if we don't have one.
-          $flagging = $this->new_flagging($entity_id, $uid, $sid);
-        }
-        // Save the flagging entity (just our table).
-        $flagging_id = $this->_flag($entity_id, $uid, $sid);
-        // The _flag() method is a plain DB record writer, so it's a bit
-        // antiquated. We have to explicitly get our new ID out.
-        $flagging->flagging_id = $flagging_id;
-        $this->_increase_count($entity_id);
-        // We're writing out a flagging entity even when we aren't passed one
-        // (e.g., when flagging via JavaScript toggle links); in this case
-        // Field API will assign the fields their default values.
-        $this->_insert_flagging($flagging);
-        module_invoke_all('flag_flag', $this, $entity_id, $account, $flagging);
-        // Invoke Rules event.
-        if (module_exists('rules')) {
-          $event_name = 'flag_flagged_' . $this->name;
-          // We only support flags on entities.
-          if (entity_get_info($this->entity_type)) {
-            $variables = array(
-              'flag' => $this,
-              'flagged_' . $this->entity_type => $entity_id,
-              'flagging_user' => $account,
-              'flagging' => $this->get_flagging($entity_id, $account->uid),
-            );
-            rules_invoke_event_by_args($event_name, $variables);
-          }
-        }
+        $this->flagging_insert($flagging, $entity_id, $account);
       }
       else {
-        // Nothing to do. Item is already flagged.
-        //
-        // Except in the case a $flagging object is passed in: in this case
-        // we're, for example, arriving from an editing form and need to update
-        // the entity.
-        if ($flagging) {
-          $this->_update_flagging($flagging);
-        }
+        $this->flagging_update($flagging, $entity_id, $account);
       }
     }
 
@@ -840,27 +790,159 @@ class flag_flag {
   }
 
   /**
-   * The entity CRUD methods _{insert,update,delete}_flagging() are for private
-   * use by the flag() method.
+   * Create a new Flagging to flag an entity.
    *
-   * The reason programmers should not call them directly is because a flagging
-   * operation is also accompanied by some bookkeeping (calling hooks, updating
-   * counters) or access control. These tasks are handled by the flag() method.
+   * @param $flagging
+   *  The flagging entity that is to be saved.
+   * @param $entity_id
+   *  The entity ID of entity being flagged.
+   * @param $account
+   *  The account performing the flagging.
    */
-  private function _insert_flagging($flagging) {
+  private function flagging_insert($flagging, $entity_id, $account) {
+    if ($this->uses_anonymous_cookies()) {
+      $this->_flag_anonymous($entity_id);
+    }
+
+    // Invoke presave hooks.
     field_attach_presave('flagging', $flagging);
+    // Invoke hook_entity_presave().
+    module_invoke_all('entity_presave', $flagging, 'flagging');
+
+    // Set the timestamp.
+    $flagging->timestamp = REQUEST_TIME;
+
+    // Save the flagging entity.
+    drupal_write_record('flagging', $flagging);
+
+    // Clear various caches; we don't want code running after us to report
+    // wrong counts or false flaggings.
+    drupal_static_reset('flag_get_user_flags');
+    drupal_static_reset('flag_get_entity_flags');
+    // Despite being named in the same pattern as the count API functions, these
+    // query the {flagging} table, so are reset here.
+    drupal_static_reset('flag_get_entity_flag_counts');
+    drupal_static_reset('flag_get_user_flag_counts');
+
+    $this->_increase_count($entity_id);
+    // We're writing out a flagging entity even when we aren't passed one
+    // (e.g., when flagging via JavaScript toggle links); in this case
+    // Field API will assign the fields their default values.
+
+    // Invoke insert hooks.
     field_attach_insert('flagging', $flagging);
+    // Invoke hook_entity_insert().
+    module_invoke_all('entity_insert', $flagging, 'flagging');
+
+    module_invoke_all('flag_flag', $this, $entity_id, $account, $flagging);
+    // Invoke Rules event.
+    if (module_exists('rules')) {
+      $this->invoke_rules_event('flag', $flagging, $entity_id, $account);
+    }
   }
-  private function _update_flagging($flagging) {
+
+  /**
+   * Update a Flagging.
+   *
+   * @param $flagging
+   *  The flagging entity that is being updated.
+   * @param $entity_id
+   *  The entity ID of entity the flagging is on.
+   * @param $account
+   *  The account performing the action.
+   */
+  private function flagging_update($flagging, $entity_id, $account) {
+    // Invoke presave hooks.
+    // This is technically still a presave, even though the {flagging} table
+    // itself is not changed.
     field_attach_presave('flagging', $flagging);
-    field_attach_update('flagging', $flagging);
-    // Update the cache.
-    entity_get_controller('flagging')->resetCache();
+    // Invoke hook_entity_presave().
+    module_invoke_all('entity_presave', $flagging, 'flagging');
+
+    // This check exists solely to preserve previous behaviour with re-flagging.
+    // TODO: consider removing it.
+    if (!empty($flagging->given_as_parameter)) {
+      field_attach_update('flagging', $flagging);
+      // Update the cache.
+      entity_get_controller('flagging')->resetCache();
+      // Invoke hook_entity_update().
+      // Since there are no fields on the {flagging} table that can be
+      // meaningfully changed, we don't perform an update on it. However, this
+      // technically still counts as updating the flagging entity, since we update
+      // its fields.
+      module_invoke_all('entity_update', $flagging, 'flagging');
+    }
+  }
+
+  /**
+   * Unflag an entity by deleting a Flagging.
+   *
+   * @param $flagging
+   *  The flagging entity that is to be removed.
+   * @param $entity_id
+   *  The entity ID of entity being unflagged.
+   * @param $account
+   *  The account performing the unflagging.
+   */
+  private function flagging_delete($flagging, $entity_id, $account) {
+    if ($this->uses_anonymous_cookies()) {
+      $this->_unflag_anonymous($entity_id);
+    }
+
+    $transaction = db_transaction();
+    try {
+      // Note the order: We decrease the count first so hooks have accurate
+      // data, then invoke hooks, then delete the flagging entity.
+      $this->_decrease_count($entity_id);
+      module_invoke_all('flag_unflag', $this, $entity_id, $account, $flagging);
+      // Invoke Rules event.
+      if (module_exists('rules')) {
+        $this->invoke_rules_event('unflag', $flagging, $entity_id, $account);
+      }
+
+      // Invoke hook_entity_delete().
+      module_invoke_all('entity_delete', $flagging, 'flagging');
+      // Delete field data.
+      field_attach_delete('flagging', $flagging);
+
+      // Delete the flagging entity.
+      db_delete('flagging')->condition('flagging_id', $flagging->flagging_id)->execute();
+
+      // Remove from the cache.
+      entity_get_controller('flagging')->resetCache();
+
+      // Clear various caches; we don't want code running after us to report
+      // wrong counts or false flaggings.
+      drupal_static_reset('flag_get_user_flags');
+      drupal_static_reset('flag_get_entity_flags');
+      // Despite being named in the same pattern as the count API functions, these
+      // query the {flagging} table, so are reset here.
+      drupal_static_reset('flag_get_entity_flag_counts');
+      drupal_static_reset('flag_get_user_flag_counts');
+    }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception('flag', $e);
+      throw $e;
+    }
   }
-  private function _delete_flagging($flagging) {
-    field_attach_delete('flagging', $flagging);
-    // Remove from the cache.
-    entity_get_controller('flagging')->resetCache();
+
+  /**
+   * Invoke a Rules event in reaction to a flagging or unflagging.
+   *
+   * @param $action
+   *   Either 'flag' or 'unflag'.
+   * @param $flagging
+   *  The flagging entity that is either newly created or about to be deleted.
+   * @param $entity_id
+   *  The entity ID of entity being flagged or unflagged.
+   * @param $account
+   *  The account performing the action.
+   */
+  protected function invoke_rules_event($action, $flagging, $entity_id, $account) {
+    // We only support flags on entities: do nothing in this class.
+    // See flag_entity::invoke_rules_event().
+    return;
   }
 
   /**
@@ -883,9 +965,12 @@ class flag_flag {
     return (object) array(
       'flagging_id' => NULL,
       'flag_name' => $this->name,
+      'fid' => $this->fid,
+      'entity_type' => $this->entity_type,
       'entity_id' => $entity_id,
       'uid' => $uid,
       'sid' => $sid,
+      // The timestamp is not set until this is saved.
     );
   }
 
@@ -965,44 +1050,7 @@ class flag_flag {
   }
 
   /**
-   * A low-level method to flag an object.
-   *
-   * You probably shouldn't call this raw private method: call the flag()
-   * function instead.
-   *
-   * @return
-   *   The 'flagging_id' column of the new {flagging} record.
-   *
-   * @private
-   */
-  function _flag($entity_id, $uid, $sid) {
-    $flagging_id = db_insert('flagging')
-      ->fields(array(
-        'fid' => $this->fid,
-        'entity_type' => $this->entity_type,
-        'entity_id' => $entity_id,
-        'uid' => $uid,
-        'sid' => $sid,
-        'timestamp' => REQUEST_TIME,
-      ))
-      ->execute();
-    return $flagging_id;
-  }
-
-  /**
-   * A low-level method to unflag an object.
-   *
-   * You probably shouldn't call this raw private method: call the flag()
-   * function instead.
-   *
-   * @private
-   */
-  function _unflag($entity_id, $flagging_id) {
-    db_delete('flagging')->condition('flagging_id', $flagging_id)->execute();
-  }
-
-  /**
-   * Increases the flag count for an object.
+   * Increases the flag count for an object and clears the static counts cache.
    *
    * @param $entity_id
    *   For which item should the count be increased.
@@ -1010,7 +1058,7 @@ class flag_flag {
    *   The amount of counts to increasing. Defaults to 1.
    *
    * @private
-  */
+   */
   function _increase_count($entity_id, $number = 1) {
     db_merge('flag_counts')
       ->key(array(
@@ -1027,10 +1075,15 @@ class flag_flag {
       ))
       ->expression('count', 'count + :inc', array(':inc' => $number))
       ->execute();
+
+    // Reset the static cache of flag counts, so code running after this gets
+    // correct counts.
+    drupal_static_reset('flag_get_counts');
+    drupal_static_reset('flag_get_flag_counts');
   }
 
   /**
-   * Decreases the flag count for an object.
+   * Decreases the flag count for an object and clears the static counts cache.
    *
    * @param $entity_id
    *   For which item should the count be descreased.
@@ -1038,7 +1091,7 @@ class flag_flag {
    *   The amount of counts to decrease. Defaults to 1.
    *
    * @private
-  */
+   */
   function _decrease_count($entity_id, $number = 1) {
     // Delete rows with count 0, for data consistency and space-saving.
     // Done before the db_update() to prevent out-of-bounds errors on "count".
@@ -1057,6 +1110,11 @@ class flag_flag {
       ->condition('fid', $this->fid)
       ->condition('entity_id', $entity_id)
       ->execute();
+
+    // Reset the static cache of flag counts, so code running after this gets
+    // correct counts.
+    drupal_static_reset('flag_get_counts');
+    drupal_static_reset('flag_get_flag_counts');
   }
 
   /**
@@ -1121,6 +1179,7 @@ class flag_flag {
    * @param $entity_id
    *   The ID in whose context to interpret tokens. If not given, only global
    *   tokens will be substituted.
+   *
    * @return
    *   The processed label.
    */
@@ -1468,8 +1527,8 @@ class flag_flag {
    * @param $variables = array()
    *  An array of further variables to pass to theme('flag'). For the full list
    *  of parameters, see flag.tpl.php. Of particular interest:
-   *  - after_flagging: Set to TRUE if this flag link is being displayed as the result
-   *    of a flagging action.
+   *  - after_flagging: Set to TRUE if this flag link is being displayed as the
+   *    result of a flagging action.
    *  - errors: An array of error messages.
    *
    * @return

+ 8 - 1
sites/all/modules/contrib/flag/flag/includes/flag/flag_node.inc

@@ -2,7 +2,7 @@
 
 /**
  * @file
- *   Contains the flag_node class.
+ * Contains the flag_node class.
  */
 
 /**
@@ -55,6 +55,12 @@ class flag_node extends flag_entity {
       '#title' => t('Display checkbox on node edit form'),
       '#description' => t('If you elect to have a checkbox on the node edit form, you may specify its initial state in the settings form <a href="@content-types-url">for each content type</a>.', array('@content-types-url' => url('admin/structure/types'))),
     ) + $form['display']['show_on_form'];
+
+    // Add the 'teaser' view mode as a default value for the entity link display
+    // option if this is a new flag.
+    if (empty($this->fid)) {
+      $form['display']['show_in_links']['#default_value']['teaser'] = 'teaser';
+    }
   }
 
   function type_access_multiple($entity_ids, $account) {
@@ -83,6 +89,7 @@ class flag_node extends flag_entity {
    *
    * @param $entity_id
    *   The nid for the content.
+   *
    * @return
    *   The tnid if available, the nid otherwise.
    */

+ 1 - 1
sites/all/modules/contrib/flag/flag/includes/flag/flag_user.inc

@@ -2,7 +2,7 @@
 
 /**
  * @file
- *   Contains the flag_user class.
+ * Contains the flag_user class.
  */
 
 /**

+ 21 - 2
sites/all/modules/contrib/flag/flag/includes/views/flag.views.inc

@@ -85,7 +85,7 @@ function flag_views_data() {
 
   // Argument for content ID, used for "Who's flagged this" views.
   $data['flagging']['entity_id'] = array(
-    'title' => t('Content ID'),
+    'title' => t('Entity ID'),
     'help' => t('The unique ID of the object that has been flagged.'),
     'argument' => array(
       'handler' => 'flag_handler_argument_entity_id',
@@ -122,6 +122,24 @@ function flag_views_data() {
     ),
   );
 
+  $data['flagging']['sid'] = array(
+    'title' => t('Flagging session ID'),
+    'help' => t('The session ID of the flagging.'),
+    'field' => array(
+      'handler' => 'views_handler_field_numeric',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'views_handler_filter_numeric',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_numeric',
+    ),
+  );
+
   $data['flag_counts']['count'] = array(
     'title' => t('Flag counter'),
     'help' => t('The number of times a piece of content is flagged by any user.'),
@@ -264,7 +282,8 @@ function flag_views_flag_default($entity_type) {
   $default_flag = &drupal_static(__FUNCTION__, array());
 
   if (!array_key_exists($entity_type, $default_flag)) {
-    $flag = array_shift(flag_get_flags($entity_type));
+    $flags = flag_get_flags($entity_type);
+    $flag = array_shift($flags);
     $default_flag[$entity_type] = $flag ? $flag->name : NULL;
   }
 

+ 5 - 0
sites/all/modules/contrib/flag/flag/includes/views/flag.views_convert.inc

@@ -57,6 +57,7 @@ function flag_views_convert($display, $type, &$view, $field, $id) {
           $new_rel = 'flag_content_rel';
           $flag_content_rel_user = 'current';
           break;
+
         case 'count':
           $new_field = array(
             'label' => $field['label'],
@@ -67,6 +68,7 @@ function flag_views_convert($display, $type, &$view, $field, $id) {
           );
           $new_rel = 'flag_counts_rel';
           break;
+
         case 'name':
           $new_field = array(
             'label' => $field['label'],
@@ -95,6 +97,7 @@ function flag_views_convert($display, $type, &$view, $field, $id) {
           // Remove the old filter.
           $view->set_item('default', $type, $id, NULL);
           break;
+
         case 'timestamp':
           $new_field = array(
             'operator' => $field['operator'],
@@ -109,6 +112,7 @@ function flag_views_convert($display, $type, &$view, $field, $id) {
           $new_rel = 'flag_content_rel';
           drupal_set_message(t('Flag is not able to convert the <em>Flagged time</em> filter. It\'s value is currently empty, but needs to be populated to work properly.'), 'warning');
           break;
+
         case 'count':
           $new_field = array(
             'operator' => $field['operator'],
@@ -151,6 +155,7 @@ function flag_views_convert($display, $type, &$view, $field, $id) {
           );
           $new_rel = 'flag_counts_rel';
           break;
+
         case 'timestamp':
           $new_field = array(
             'order' => $field['sortorder'],

+ 3 - 2
sites/all/modules/contrib/flag/flag/includes/views/flag_handler_argument_entity_id.inc

@@ -27,12 +27,13 @@ class flag_handler_argument_entity_id extends views_handler_argument_numeric {
    */
   function title_query() {
     if (!($flag = $this->get_flag())) {
-      return array(); // Error message is printed by get_flag().
+      // Error message is printed by get_flag().
+      return array();
     }
     $views_info = $flag->get_views_info();
 
     $titles = array();
-    $placeholders = implode(', ', array_fill(0, sizeof($this->value), '%d'));
+    $placeholders = implode(', ', array_fill(0, count($this->value), '%d'));
 
     $result = db_select($views_info['views table'], 'o')
       ->fields('o', array($views_info['title field']))

+ 8 - 4
sites/all/modules/contrib/flag/flag/includes/views/flag_handler_field_ops.inc

@@ -35,7 +35,8 @@ class flag_handler_field_ops extends views_handler_field {
   function get_parent_relationship() {
     $parent = $this->view->relationship[$this->options['relationship']]->options['relationship'];
     if (!$parent || $parent == 'none') {
-      return NULL; // Base query table.
+      // Base query table.
+      return NULL;
     }
     else {
       return $this->view->relationship[$parent]->alias;
@@ -66,7 +67,8 @@ class flag_handler_field_ops extends views_handler_field {
    */
   function query() {
     if (!($flag = $this->get_flag())) {
-      return; // Error message is printed in render().
+      // Error message is printed in render().
+      return;
     }
     $info = $flag->get_views_info();
     $parent = $this->get_parent_relationship();
@@ -128,7 +130,8 @@ class flag_handler_field_ops extends views_handler_field {
    */
   function pre_render(&$values) {
     if (!($flag = $this->get_flag())) {
-      return; // Error message is printed in render().
+      // Error message is printed in render().
+      return;
     }
 
     $ids = array();
@@ -144,7 +147,8 @@ class flag_handler_field_ops extends views_handler_field {
 
   function render($values) {
     if (!($flag = $this->get_flag())) {
-      return t('Missing flag'); // get_flag() itself will print a more detailed message.
+      // get_flag() itself will print a more detailed message.
+      return t('Missing flag');
     }
 
     $entity_id = $values->{$this->aliases['entity_id']};

+ 5 - 1
sites/all/modules/contrib/flag/flag/includes/views/flag_handler_filter_flagged.inc

@@ -21,7 +21,11 @@ class flag_handler_filter_flagged extends views_handler_filter_boolean_operator
     parent::options_form($form, $form_state);
     $form['value']['#type'] = 'radios';
     $form['value']['#title'] = t('Status');
-    $form['value']['#options'] = array(1 => t('Flagged'), 0 => t('Not flagged'), 'All' => t('All'));
+    $form['value']['#options'] = array(
+      1 => t('Flagged'),
+      0 => t('Not flagged'),
+      'All' => t('All'),
+    );
     $form['value']['#default_value'] = empty($this->options['value']) ? '0' : $this->options['value'];
     $form['value']['#description'] = '<p>' . t('This filter is only needed if the relationship used has the "Include only flagged content" option <strong>unchecked</strong>. Otherwise, this filter is useless, because all records are already limited to flagged content.') . '</p><p>' . t('By choosing <em>Not flagged</em>, it is possible to create a list of content <a href="@unflagged-url">that is specifically not flagged</a>.', array('@unflagged-url' => 'http://drupal.org/node/299335')) . '</p>';
   }

+ 23 - 4
sites/all/modules/contrib/flag/flag/includes/views/flag_handler_relationships.inc

@@ -44,6 +44,17 @@ abstract class flag_handler_relationship extends views_handler_relationship {
     return $errors;
   }
 
+  /**
+   * Get the type of the flag this relationship uses.
+   *
+   * This looks at the data set in the relationship definition in Views data.
+   *
+   * @return
+   *  The flag's type, e.g., 'node' or 'taxonomy_term', or NULL if this is not
+   *  set in data from hook_views_data().
+   *
+   * @see flag_views_data_alter()
+   */
   function get_flag_type() {
     return isset($this->definition['flag type']) ? $this->definition['flag type'] : NULL;
   }
@@ -77,7 +88,11 @@ abstract class flag_handler_relationship extends views_handler_relationship {
 }
 
 /**
- * Specialized relationship handler associating flags and content.
+ * Views relationship handler associating flags and content.
+ *
+ * This forms a bridge from the entity table to the {flagging} table, with
+ * options to restrict the join to only flagged content, and to flagged content
+ * by the current user.
  *
  * @ingroup views
  */
@@ -172,7 +187,10 @@ class flag_handler_relationship_content extends flag_handler_relationship {
 }
 
 /**
- * Specialized relationship handler associating flag counts and content.
+ * Views relationship handler associating flag counts and content.
+ *
+ * This forms a bridge from the entity table to the {flag_counts} table, with
+ * the option to restrict the join to include only flagged content.
  *
  * @ingroup views
  */
@@ -227,7 +245,9 @@ class flag_handler_relationship_counts extends flag_handler_relationship {
 }
 
 /**
- * Specialized relationship handler associating flags and users.
+ * Views relationship handler associating flags and users.
+ *
+ * This forms a bridge from the the {users} table to the {flagging} table.
  *
  * @ingroup views
  */
@@ -264,4 +284,3 @@ class flag_handler_relationship_user_content extends flag_handler_relationship {
     parent::query();
   }
 }
-

+ 2 - 1
sites/all/modules/contrib/flag/flag/includes/views/flag_handler_sort_flagged.inc

@@ -14,7 +14,8 @@ class flag_handler_sort_flagged extends views_handler_sort {
 
   /**
    * Provide a list of options for the default sort form.
-   * Should be overridden by classes that don't override sort_form
+   *
+   * Should be overridden by classes that don't override sort_form.
    */
   function sort_options() {
     return array(

+ 11 - 8
sites/all/modules/contrib/flag/flag/includes/views/flag_plugin_argument_validate_flaggability.inc

@@ -66,8 +66,9 @@ class flag_plugin_argument_validate_flaggability extends views_plugin_argument_v
   }
 
   /**
-   * Returns form #options for the flags. Returns empty array if no flags were
-   * found.
+   * Returns form #options for the flags.
+   *
+   * Returns empty array if no flags were found.
    */
   function flags_options() {
     $flags = flag_get_flags($this->flag_type);
@@ -96,8 +97,9 @@ class flag_plugin_argument_validate_flaggability extends views_plugin_argument_v
   }
 
   /**
-   * Declares all tests. This scheme makes it easy for derived classes to add
-   * and remove tests.
+   * Declares all tests.
+   *
+   * This scheme makes it easy for derived classes to add and remove tests.
    */
   function tests_info($which = NULL) {
     return array(
@@ -130,7 +132,8 @@ class flag_plugin_argument_validate_flaggability extends views_plugin_argument_v
     if ($flag_name == '*relationship*') {
       // Pick the first flag mentioned in the relationships.
       foreach ($this->view->relationship as $id => $handler) {
-        // Note: we can't do $handler->field, because the relationship handler's init() may overwrite it.
+        // Note: we can't do $handler->field, because the relationship handler's
+        // init() may overwrite it.
         if (strpos($handler->options['field'], 'flag') !== FALSE && !empty($handler->options['flag'])) {
           $flag = flag_get_flag($handler->options['flag']);
           if ($flag && $flag->entity_type == $this->flag_type) {
@@ -198,7 +201,8 @@ class flag_plugin_argument_validate_flaggability extends views_plugin_argument_v
   }
 
   function test_flagged($ids, $flag) {
-    // view_break_phrase() is guaranteed to return only integers, so this is SQL safe.
+    // view_break_phrase() is guaranteed to return only integers, so this is SQL
+    // safe.
     $flattened_ids = implode(',', $ids);
     return $this->_test_by_sql("SELECT entity_id FROM {flag_counts} WHERE fid = :fid AND entity_id IN ($flattened_ids) AND count > 0", array(':fid' => $flag->fid));
   }
@@ -206,7 +210,7 @@ class flag_plugin_argument_validate_flaggability extends views_plugin_argument_v
   function test_flagged_by_current_user($ids, $flag) {
     global $user;
     if (!$user->uid) {
-      // Anonymous user
+      // Anonymous user.
       return array();
     }
     $flattened_ids = implode(',', $ids);
@@ -223,4 +227,3 @@ class flag_plugin_argument_validate_flaggability extends views_plugin_argument_v
     return $passed;
   }
 }
-

+ 35 - 8
sites/all/modules/contrib/flag/flag/plugins/access/flag_is_flagged/flag_is_flagged.inc

@@ -31,7 +31,7 @@ function flag_flag_is_flagged_access_get_children($plugin, $parent) {
 
   foreach ($entities as $entity_type => $info) {
     if (entity_type_supports($entity_type, 'view')) {
-      $plugin['title'] =  t('@entity_type is flagged', array('@entity_type' => $info['label']));
+      $plugin['title'] = t('@entity_type is flagged', array('@entity_type' => $info['label']));
       $plugin['keyword'] = $entity_type;
       $plugin['name'] = $parent . ':' . $entity_type;
       $plugin['required context'] = new ctools_context_required(t('Entity'), $entity_type);
@@ -46,23 +46,36 @@ function flag_flag_is_flagged_access_get_children($plugin, $parent) {
  * Settings form.
  */
 function flag_flag_is_flagged_access_settings(&$form, &$form_state, $conf) {
-  $options = array();
+  $flag_name_options = array();
 
   $plugin = $form_state['plugin'];
   $entity_type = explode(':', $plugin['name']);
   $entity_type = $entity_type[1];
 
   foreach (flag_get_flags($entity_type) as $flag) {
-    $options[$flag->name] = check_plain($flag->title);
+    $flag_name_options[$flag->name] = check_plain($flag->title);
   }
 
+  $flag_user_options = array(
+    'any' => t('Flagged by anyone'),
+    'user' => t('Flagged by current user'),
+  );
+  $flag_user_default = isset($conf['flag_user']) ? $conf['flag_user'] : 'user';
+
   $form['settings']['flag_name'] = array(
     '#title' => t('Flag name'),
     '#type' => 'radios',
-    '#options' => $options,
+    '#options' => $flag_name_options,
     '#description' => t('Include only flagged content.'),
     '#default_value' => $conf['flag_name'],
   );
+  $form['settings']['flag_user'] = array(
+    '#title' => t('Filter by flag owner'),
+    '#type' => 'radios',
+    '#options' => $flag_user_options,
+    '#description' => t('Show content flagged by anyone or only by current user.'),
+    '#default_value' => $flag_user_default,
+  );
   return $form;
 }
 
@@ -78,19 +91,33 @@ function flag_flag_is_flagged_access_check($conf, $context) {
   // Get the ID of the entity.
   list($id) = entity_extract_ids($flag->entity_type, $context->data);
 
-  return $flag->is_flagged($id);
+  // Get either the count of users who have flagged this entity or find out
+  // whether the current user has flagged this node, depending on settings.
+  if (isset($conf['flag_user']) && $conf['flag_user'] == 'any') {
+    $count = count(flag_get_entity_flags($flag->entity_type, $id, $conf['flag_name']));
+    return $count;
+  }
+  else {
+    return $flag->is_flagged($id);
+  }
 }
 
 /**
- * Provide a summary description based upon the specified context
+ * Provide a summary description based upon the specified context.
  */
 function flag_flag_is_flagged_access_summary($conf, $context) {
   $flag = flag_get_flag($conf['flag_name']);
 
   if ($flag) {
-    return t('@identifier is flagged with "@flag"', array('@flag' => check_plain($flag->title), '@identifier' => $context->identifier));
+    $flag_limit_by = '';
+    if (isset($conf['flag_user']) && $conf['flag_user'] == 'user') {
+      return t('@identifier is flagged with "@flag" by current user.', array('@flag' => $flag->title, '@identifier' => $context->identifier));
+    }
+    elseif (isset($conf['flag_user']) && $conf['flag_user'] == 'any') {
+      return t('@identifier is flagged with "@flag" by anyone.', array('@flag' => $flag->title, '@identifier' => $context->identifier));
+    }
   }
   else {
-    return t('Missing/ deleted flag "@flag"', array('@flag' => $conf['flag_name']));
+    return t('Missing/deleted flag "@flag"', array('@flag' => $conf['flag_name']));
   }
 }

+ 10 - 7
sites/all/modules/contrib/flag/flag/plugins/content_types/flag_link/flag_link.inc

@@ -24,17 +24,20 @@ function flag_flag_link_content_type_info($entity_type) {
  * Implements hook_PLUGIN_content_type_content_types().
  */
 function flag_flag_link_content_type_content_types() {
+  $types = &drupal_static(__FUNCTION__);
+  if (isset($types)) {
+    return $types;
+  }
+
   $types = array();
   $entities = entity_get_info();
 
   foreach ($entities as $entity_type => $info) {
-    if (entity_type_supports($entity_type, 'view')) {
-      $types[$entity_type] = array(
-        'title' => t('Flag for @entity_type', array('@entity_type' => $info['label'])),
-        'category' => t('Entity'),
-        'required context' => new ctools_context_required(t('Entity'), $entity_type),
-      );
-    }
+    $types[$entity_type] = array(
+      'title' => t('Flag for @entity_type', array('@entity_type' => $info['label'])),
+      'category' => t('Entity'),
+      'required context' => new ctools_context_required(t('Entity'), $entity_type),
+    );
   }
 
   return $types;

+ 715 - 28
sites/all/modules/contrib/flag/flag/tests/flag.test

@@ -43,7 +43,7 @@ class FlagFlaggingCRUDTestCase extends FlagTestCaseBase {
    */
   public static function getInfo() {
     return array(
-      'name' => 'Flagging CRUD',
+      'name' => 'CRUD API',
       'description' => 'Basic CRUD operations on flagging entities.',
       'group' => 'Flag',
     );
@@ -216,14 +216,14 @@ class FlagFlaggingCRUDTestCase extends FlagTestCaseBase {
  * Test Flag admin UI.
  */
 class FlagAdminTestCase extends FlagTestCaseBase {
-  var $_flag = FALSE;
+  public $_flag = FALSE;
 
   /**
    * Implements getInfo().
    */
   public static function getInfo() {
     return array(
-      'name' => 'Flag admin tests',
+      'name' => 'Admin UI',
       'description' => 'Add, edit and delete flags.',
       'group' => 'Flag',
     );
@@ -235,7 +235,7 @@ class FlagAdminTestCase extends FlagTestCaseBase {
   function setUp() {
     parent::setUp('flag');
 
-    // Create and login user
+    // Create and login user.
     $admin_user = $this->drupalCreateUser(array('access administration pages', 'administer flags'));
     $this->drupalLogin($admin_user);
   }
@@ -245,6 +245,14 @@ class FlagAdminTestCase extends FlagTestCaseBase {
    */
   function testFlagAdmin() {
     // Add a new flag using the UI.
+    $this->drupalGet(FLAG_ADMIN_PATH . '/add/node');
+
+    // Check the form has the expected defaults.
+    $this->assertFieldByName('flag_short', 'Flag this item', "The flag message default value shows in the form.");
+    $this->assertFieldByName('unflag_short', 'Unflag this item', "The unflag message default value shows in the form.");
+    $this->assertFieldByName('show_in_links[full]', 'full', "The view mode option is set to the node 'full' view mode by default.");
+    $this->assertFieldByName('show_in_links[teaser]', 'teaser', "The view mode option is set to the node 'teaser' view mode by default.");
+
     $edit = array(
       'name' => drupal_strtolower($this->randomName()),
       'title' => $this->randomName(),
@@ -269,7 +277,13 @@ class FlagAdminTestCase extends FlagTestCaseBase {
     $saved = $edit;
     $saved['roles'] = array('flag' => array(2), 'unflag' => array(2));
     $saved['types'] = array('page');
-    $saved['show_in_links'] = array('full' => 0, 'teaser' => 0, 'rss' => 0, 'search_index' => 0, 'search_result' => 0);
+    $saved['show_in_links'] = array(
+      'full' => 0,
+      'teaser' => 0,
+      'rss' => 0,
+      'search_index' => 0,
+      'search_result' => 0,
+    );
     unset($saved['roles[flag][2]'], $saved['roles[unflag][2]'], $saved['types[article]'], $saved['types[page]'], $saved['show_in_links[full]'], $saved['show_in_links[teaser]'], $saved['show_in_links[rss]'], $saved['show_in_links[search_index]'], $saved['show_in_links[search_result]']);
 
     $this->drupalPost(FLAG_ADMIN_PATH . '/add/node', $edit, t('Save flag'));
@@ -291,7 +305,7 @@ class FlagAdminTestCase extends FlagTestCaseBase {
     $permissions = user_role_permissions(user_roles());
     foreach ($saved['roles'] as $action => $rids) {
       foreach ($rids as $rid) {
-        $permission_string = "$action ". $saved['name'];
+        $permission_string = "$action " . $saved['name'];
         $this->assertTrue(isset($permissions[$rid][$permission_string]), t('Permission %perm set for flag.', array(
           '%perm' => $permission_string,
         )));
@@ -323,7 +337,13 @@ class FlagAdminTestCase extends FlagTestCaseBase {
     $saved = $edit;
     $saved['roles'] = array('flag' => array(2), 'unflag' => array(2));
     $saved['types'] = array('article');
-    $saved['show_in_links'] = array('full' => TRUE, 'teaser' => TRUE, 'rss' => 0, 'search_index' => 0, 'search_result' => 0);
+    $saved['show_in_links'] = array(
+      'full' => TRUE,
+      'teaser' => TRUE,
+      'rss' => 0,
+      'search_index' => 0,
+      'search_result' => 0,
+    );
     unset($saved['roles[flag][2]'], $saved['roles[unflag][2]'], $saved['types[article]'], $saved['types[page]'], $saved['show_in_links[full]'], $saved['show_in_links[teaser]'], $saved['show_in_links[rss]'], $saved['show_in_links[search_index]'], $saved['show_in_links[search_result]']);
 
     $this->drupalPost(FLAG_ADMIN_PATH . '/manage/' . $flag->name, $edit, t('Save flag'));
@@ -350,7 +370,7 @@ class FlagAdminTestCase extends FlagTestCaseBase {
 
     foreach ($saved['roles'] as $action => $rids) {
       foreach ($rids as $rid) {
-        $permission_string = "$action ". $saved['name'];
+        $permission_string = "$action " . $saved['name'];
         $this->assertTrue(isset($permissions[$rid][$permission_string]), t('Permission %perm set for flag.', array(
           '%perm' => $permission_string,
         )));
@@ -377,7 +397,7 @@ class FlagAccessFormTestCase extends FlagTestCaseBase {
    */
   public static function getInfo() {
     return array(
-      'name' => 'Flag access: entity forms',
+      'name' => 'Access to flags via entity forms',
       'description' => 'Access to flag and unflag entities via entity forms.',
       'group' => 'Flag',
     );
@@ -431,7 +451,7 @@ class FlagAccessFormTestCase extends FlagTestCaseBase {
 
     $this->drupalGet('node/add/article');
 
-    // Check that the flag form element cannot be seen
+    // Check that the flag form element cannot be seen.
     $this->assertNoText('Flag this item', t('Flag form element was not found.'));
 
     // Have the user create a node.
@@ -463,6 +483,161 @@ class FlagAccessFormTestCase extends FlagTestCaseBase {
 
 }
 
+/**
+ * Tokens we provide on generic entities.
+ */
+class FlagEntityTokensTestCase extends FlagTestCaseBase {
+
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity tokens',
+      'description' => 'Tokens for flag count on entities.',
+      'group' => 'Flag',
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    // Our entity tokens require token module.
+    parent::setUp('flag', 'token');
+  }
+
+  /**
+   * Test tokens on nodes.
+   */
+  function testNodeFlagToken() {
+    // Create a flag on article nodes.
+    $flag_data = array(
+      'entity_type' => 'node',
+      'name' => 'node_flag',
+      'title' => 'Node Flag',
+      'global' => 0,
+      'types' => array(
+        0 => 'article',
+      ),
+      'flag_short' => 'Flag this item',
+      'flag_long' => '',
+      'flag_message' => '',
+      'unflag_short' => 'Unflag this item',
+      'unflag_long' => '',
+      'unflag_message' => '',
+      'unflag_denied_text' => 'You may not unflag this item',
+      'link_type' => 'normal',
+      'weight' => 0,
+      // Show the flag on the form.
+      'show_on_form' => 1,
+      'access_author' => '',
+      'show_contextual_link' => 0,
+      'show_in_links' => array(
+        'full' => 1,
+        'teaser' => 1,
+      ),
+      'i18n' => 0,
+      'api_version' => 3,
+    );
+    $flag = $this->createFlag($flag_data);
+
+    // Create a node to flag.
+    $node = (object) array(
+      'type' => 'article',
+      'title' => $this->randomName(),
+    );
+    node_save($node);
+
+    // Flag it by several users.
+    $flag_user_1 = $this->drupalCreateUser(array('flag node_flag',));
+
+    // Flag the node as the user.
+    $flag = flag_get_flag('node_flag');
+    $flag->flag('flag', $node->nid, $flag_user_1);
+
+    $flag_user_2 = $this->drupalCreateUser(array('flag node_flag',));
+
+    // Flag the node as the user.
+    $flag->flag('flag', $node->nid, $flag_user_2);
+
+    $text = '[node:flag-node-flag-count]';
+
+    $replaced_text = token_replace($text, array('node' => $node));
+
+    $this->assertEqual($replaced_text, 2, "The flag count token for the node is correct.");
+  }
+
+  /**
+   * Test tokens on taxonomy terms.
+   *
+   * These are worthy of a separate test, as the token type is a special case.
+   */
+  function testTaxonomyTermFlagToken() {
+    // Create a flag on tag terms.
+    $flag_data = array(
+      'entity_type' => 'taxonomy_term',
+      'name' => 'term_flag',
+      'title' => 'Term Flag',
+      'global' => 0,
+      'types' => array(
+        0 => 'tags',
+      ),
+      'flag_short' => 'Flag this item',
+      'flag_long' => '',
+      'flag_message' => '',
+      'unflag_short' => 'Unflag this item',
+      'unflag_long' => '',
+      'unflag_message' => '',
+      'unflag_denied_text' => 'You may not unflag this item',
+      'link_type' => 'normal',
+      'weight' => 0,
+      // Show the flag on the form.
+      'show_on_form' => 1,
+      'access_author' => '',
+      'show_contextual_link' => 0,
+      'show_in_links' => array(
+        'full' => 1,
+        'teaser' => 1,
+      ),
+      'i18n' => 0,
+      'api_version' => 3,
+    );
+    $flag = $this->createFlag($flag_data);
+
+    $vocabulary = taxonomy_vocabulary_load(1);
+
+    // Create a term to flag.
+    $term = (object) array(
+      'name' => $this->randomName(),
+      'vid' => 1,
+    );
+    taxonomy_term_save($term);
+
+    // Flag it by several users.
+    $flag_user_1 = $this->drupalCreateUser(array('flag term_flag',));
+
+    // Flag the term as the user.
+    $flag = flag_get_flag('term_flag');
+    $flag->flag('flag', $term->tid, $flag_user_1);
+
+    $flag_user_2 = $this->drupalCreateUser(array('flag term_flag',));
+
+    // Flag the term as the user.
+    $flag = flag_get_flag('term_flag');
+    $flag->flag('flag', $term->tid, $flag_user_2);
+
+    $text = '[term:flag-term-flag-count]';
+
+    $replaced_text = token_replace($text, array('term' => $term));
+
+    debug($replaced_text);
+
+    $this->assertEqual($replaced_text, 2, "The flag count token for the term is correct.");
+  }
+
+}
+
 /**
  * Access to flags using the basic flag link.
  */
@@ -473,7 +648,7 @@ class FlagAccessLinkTestCase extends FlagTestCaseBase {
    */
   public static function getInfo() {
     return array(
-      'name' => 'Flag access tests',
+      'name' => 'Access to flags via basic link',
       'description' => 'Access to flag and unflag entities using the basic link.',
       'group' => 'Flag',
     );
@@ -599,7 +774,7 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
    */
   public static function getInfo() {
     return array(
-      'name' => 'Flag confirm link tests',
+      'name' => 'Confirm form',
       'description' => 'Flag confirm form link type.',
       'group' => 'Flag',
     );
@@ -621,7 +796,7 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
       'types' => array(
         0 => 'article',
       ),
-      'flag_short' => 'Flag this item',
+      'flag_short' => 'Flag <em>this</em> item',
       'flag_long' => '',
       'flag_message' => 'You have flagged this item.',
       'unflag_short' => 'Unflag this item',
@@ -642,8 +817,8 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
       'i18n' => 0,
       'api_version' => 3,
     );
-    $flag = flag_flag::factory_by_array($this->flag_data);
-    $flag->save();
+    $this->flag = flag_flag::factory_by_array($this->flag_data);
+    $this->flag->save();
     // Reset our cache so our permissions show up.
     drupal_static_reset('flag_get_flags');
 
@@ -651,8 +826,8 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
     $this->checkPermissions(array(), TRUE);
 
     // Create test user who can flag and unflag.
-    $flag_unflag_user = $this->drupalCreateUser(array('flag test_flag', 'unflag test_flag'));
-    $this->drupalLogin($flag_unflag_user);
+    $this->flag_unflag_user = $this->drupalCreateUser(array('flag test_flag', 'unflag test_flag'));
+    $this->drupalLogin($this->flag_unflag_user);
 
     // Create an article node to flag and unflag.
     $title = $this->randomName(8);
@@ -669,16 +844,27 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Test that a user sees the flag confirm form.
+   * Test usage of the flag confirm form.
    */
   function testFlag() {
     // Look at our node.
     $this->drupalGet('node/' . $this->nid);
 
-    $this->assertLink($this->flag_data['flag_short'], 0, 'The flag link appears on the page');
+    $flag_short_label = strip_tags($this->flag_data['flag_short']);
+
+    $this->assertRaw($this->flag_data['flag_short'], 'The flag text, including HTML, appears on the page.');
+
+    // assertLink() appears to have interesting problems dealing with an A
+    // element which contains other tags. However, the xpath it uses appears to
+    // be fine with being given just the portion of the link text that comes
+    // before the interior tag.
+    // Fortunately, there will be no other text on the page that matches 'Flag'.
+    $this->assertLink('Flag', 0, 'The flag link appears on the page.');
 
     // Click the link to flag the node.
-    $this->clickLink($this->flag_data['flag_short']);
+    // clickLink() has the same problem, as it uses the same xpath expression as
+    // assertLink().
+    $this->clickLink('Flag');
 
     $this->assertUrl('flag/confirm/flag/test_flag/' . $this->nid, array(
       'query' => array(
@@ -688,12 +874,18 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
 
     $this->assertText($this->flag_data['flag_confirmation'], 'The flag confirmation text appears as the confirmation page title.');
 
+    $this->assertPattern('@<input [^>]* value="' . $flag_short_label . '" [^>]*>@', 'The flag text, excluding HTML, is shown in the button.');
+
     // Click the button to confirm the flagging.
-    $this->drupalPost(NULL, array(), $this->flag_data['flag_short']);
+    $this->drupalPost(NULL, array(), $flag_short_label);
 
     $this->assertText($this->flag_data['flag_message'], 'The flag message appears once the item has been flagged.');
     $this->assertLink($this->flag_data['unflag_short'], 0, 'The unflag link appears once the item has been flagged.');
 
+    // Reset the static cache before we get data from it.
+    drupal_static_reset('flag_get_user_flags');
+    $this->assertTrue($this->flag->is_flagged($this->nid, $this->flag_unflag_user->uid), "The node is recorded as flagged by the user.");
+
     // Click the link to unflag the node.
     $this->clickLink($this->flag_data['unflag_short']);
 
@@ -709,6 +901,10 @@ class FlagLinkTypeConfirmTestCase extends DrupalWebTestCase {
     $this->drupalPost(NULL, array(), $this->flag_data['unflag_short']);
 
     $this->assertText($this->flag_data['unflag_message'], 'The unflag message appears once the item has been flagged.');
+
+    // Reset the static cache before we get data from it.
+    drupal_static_reset('flag_get_user_flags');
+    $this->assertFalse($this->flag->is_flagged($this->nid, $this->flag_unflag_user->uid), "The node is recorded as not flagged by the user.");
   }
 
 }
@@ -723,7 +919,7 @@ class FlagHookFlagAccessTestCase extends FlagTestCaseBase {
    */
   public static function getInfo() {
     return array(
-      'name' => 'Flag hook_flag_access() tests',
+      'name' => 'hook_flag_access()',
       'description' => 'Checks the ability of modules to use hook_flag_access().',
       'group' => 'Flag',
     );
@@ -831,7 +1027,8 @@ class FlagHookFlagAccessTestCase extends FlagTestCaseBase {
   }
 
   /**
-   * Verifies that the user sees the flag if a module returns TRUE (Allow) to override default access check.
+   * Verifies that the user sees the flag if a module returns TRUE (Allow) to
+   * override default access check.
    */
   function testFlagAccessAllowOverride() {
     variable_set('FlagHookFlagAccessTestCaseMode', 'allow');
@@ -855,7 +1052,8 @@ class FlagHookFlagAccessTestCase extends FlagTestCaseBase {
   }
 
   /**
-   * Verifies that the user does not see the flag if a module returns FALSE (Deny).
+   * Verifies that the user does not see the flag if a module returns FALSE
+   * (Deny).
    */
   function testFlagAccessDeny() {
     variable_set('FlagHookFlagAccessTestCaseMode', 'deny');
@@ -870,6 +1068,83 @@ class FlagHookFlagAccessTestCase extends FlagTestCaseBase {
 
 }
 
+/**
+ * Test use of fields on flagging entities.
+ */
+class FlagFlaggingFieldTestCase extends FlagTestCaseBase {
+
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Flagging fields',
+      'description' => 'Fields on Flagging entities.',
+      'group' => 'Flag',
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp('flag', 'flag_fields_test');
+  }
+
+  function testFlaggingFields() {
+    $this->assertTrue(1);
+
+    $flag_user = $this->drupalCreateUser(array(
+      'flag flag_fields_test_flag',
+      'unflag flag_fields_test_flag',
+    ));
+    $this->drupalLogin($flag_user);
+
+    $node = $this->drupalCreateNode();
+
+    $this->drupalGet('node/' . $node->nid);
+
+    $this->clickLink('Flag with the test flag');
+
+    // Try to fail the form validation by providing something non-numeric.
+    // This validation is only present in the widget validation: this is a core
+    // bug, but lets us test widget validation works correctly until it's fixed.
+    $edit = array(
+      'flag_fields_test_integer[und][0][value]' => 'banana',
+    );
+    $this->drupalPost(NULL, $edit, 'Flag with the test flag');
+
+    $this->assertText("Only numbers are allowed in Test integer.", "Form validation correctly failed the input.");
+
+    // Try to fail the form validation by a number that's out of bounds.
+    $edit = array(
+      'flag_fields_test_integer[und][0][value]' => 12,
+    );
+    $this->drupalPost(NULL, $edit, 'Flag with the test flag');
+
+    $this->assertText("Test integer: the value may be no greater than 11.", "Form validation correctly failed the input.");
+
+    $edit = array(
+      'flag_fields_test_integer[und][0][value]' => 6,
+    );
+    $this->drupalPost(NULL, $edit, 'Flag with the test flag');
+
+    $this->assertUrl('node/' . $node->nid, array(), "The flagging form submission succeeded.");
+
+    // Try to load the flagging, querying for the field value.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'flagging')
+      ->entityCondition('bundle', 'flag_fields_test_flag')
+      ->propertyCondition('entity_id', $node->nid)
+      ->fieldCondition('flag_fields_test_integer', 'value', 6);
+
+    $result = $query->execute();
+
+    $this->assertEqual(count($result['flagging']), 1, "The Flagging entity was found with the correct field values.");
+  }
+
+}
+
 /**
  * Test use of EntityFieldQueries with flagging entities.
  */
@@ -879,8 +1154,8 @@ class FlagEntityFieldQueryTestCase extends FlagTestCaseBase {
    * Implements getInfo().
    */
   public static function getInfo() {
-     return array(
-      'name' => 'Flagging Entity Field Query Extension',
+    return array(
+      'name' => 'Entity Field Query',
       'description' => 'Entity Field Query for flagging entities.',
       'group' => 'Flag',
     );
@@ -928,7 +1203,12 @@ class FlagEntityFieldQueryTestCase extends FlagTestCaseBase {
     $this->flag3 = $this->createFlag($flag_data);
 
     // Create test user who can flag and unflag.
-    $this->flag_unflag_user = $this->drupalCreateUser(array('flag test_flag_1', 'unflag test_flag_1', 'flag test_flag_2', 'unflag test_flag_2'));
+    $this->flag_unflag_user = $this->drupalCreateUser(array(
+      'flag test_flag_1',
+      'unflag test_flag_1',
+      'flag test_flag_2',
+      'unflag test_flag_2',
+    ));
     $this->drupalLogin($this->flag_unflag_user);
 
   }
@@ -937,7 +1217,7 @@ class FlagEntityFieldQueryTestCase extends FlagTestCaseBase {
    * Test use of EntityFieldQuery with flagging entities.
    */
   function testEntityFieldQuery() {
-     $node_settings = array(
+    $node_settings = array(
       'title' => $this->randomName(),
       'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(32)))),
       'uid' => 1,
@@ -969,3 +1249,410 @@ class FlagEntityFieldQueryTestCase extends FlagTestCaseBase {
   }
 
 }
+
+/**
+ * Verifies the invocation of hooks when performing flagging and unflagging.
+ */
+class FlagHookInvocationsTestCase extends FlagTestCaseBase {
+
+  /**
+   * Implements getInfo().
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Hook invocations',
+      'description' => 'Invocation of flag and entity hooks and rules during flagging and unflagging.',
+      'group' => 'Flag',
+    );
+  }
+
+  /**
+   * Implements setUp().
+   */
+  function setUp() {
+    parent::setUp('flag', 'rules', 'flag_hook_test');
+
+    // Note the test module contains our test flag.
+
+    // Create test user who can flag and unflag.
+    $this->flag_unflag_user = $this->drupalCreateUser(array('flag flag_hook_test_flag', 'unflag flag_hook_test_flag'));
+    $this->drupalLogin($this->flag_unflag_user);
+  }
+
+  /**
+   * Test invocation of hooks and their data during flagging and unflagging.
+   *
+   * For each operation (flagging, re-flagging, unflagging) we test:
+   *  - the order in which Flag hooks, entity hooks, and rules are invoked.
+   *  - the parameters each hook receives
+   *  - the data that a hook implementation obtains when it calls the Flag data
+   *    API functions.
+   */
+  function testHookInvocation() {
+    // Create an article node that we try to create a flagging entity for.
+    $title = $this->randomName(8);
+    $node = array(
+      'title' => $title,
+      'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(32)))),
+      'uid' => 1,
+      'type' => 'article',
+      'is_new' => TRUE,
+    );
+    $node = node_submit((object) $node);
+    node_save($node);
+
+    // Initialize a tracking variable. The test module will update this when
+    // its hooks are invoked.
+    variable_set('flag_hook_test_hook_tracking', array());
+
+    // Flag the node as the user.
+    $flag = flag_get_flag('flag_hook_test_flag');
+    $flag->flag('flag', $node->nid, $this->flag_unflag_user);
+
+    // Get the variable the test module sets the hook order into.
+    $hook_data_variable = variable_get('flag_hook_test_hook_tracking', array());
+    //debug($hook_data_variable['hook_entity_presave']);
+    //debug($hook_data_variable['hook_entity_insert']);
+
+    $expected_hook_order = array(
+      'hook_entity_presave',
+      'hook_entity_insert',
+      'hook_flag_flag',
+      'rules_event',
+    );
+
+    // Check the hooks are invoked in the correct order.
+    $this->assertIdentical($expected_hook_order, array_keys($hook_data_variable), "The hooks are invoked in the correct order when flagging a node.");
+
+    // Check the parameters received by hook_entity_presave().
+    // Param 0: $flagging.
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->flag_name, $flag->name, "The Flagging entity passed to hook_entity_presave() has the expected flag name property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->fid, $flag->fid, "The Flagging entity passed to hook_entity_presave() has the expected fid property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->entity_type, 'node', "The Flagging entity passed to hook_entity_presave() has the expected entity type property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->entity_id, $node->nid, "The Flagging entity passed to hook_entity_presave() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_entity_presave() has the expected uid property.");
+    // Param 1: $entity_type.
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][1], 'flagging', "hook_entity_presave() is passed the correct entity type.");
+
+    // Check the API data available to hook_entity_presave().
+    //debug($hook_data_variable['hook_entity_presave']['api_calls']);
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['api_calls']['flag_get_entity_flags'], array(), "hook_entity_presave() gets no data from from flag_get_entity_flags(), as the flagging has not yet taken place.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['api_calls']['flag_get_user_flags'], array(), "hook_entity_presave() gets no data from from flag_get_user_flags(), as the flagging has not yet taken place.");
+    // The flag counts have not yet been increased.
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['api_calls']['flag_get_counts'], array(), "hook_entity_presave() gets no data from from flag_get_counts(), as the flagging has not yet taken place.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['api_calls']['flag_get_flag_counts'], 0, "hook_entity_presave() gets no data from from flag_get_flag_counts(), as the flagging has not yet taken place.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['api_calls']['flag_get_entity_flag_counts'], 0, "hook_entity_presave() gets no data from from flag_get_entity_flag_counts(), as the flagging has not yet taken place.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['api_calls']['flag_get_user_flag_counts'], 0, "hook_entity_presave() gets no data from from flag_get_user_flag_counts(), as the flagging has not yet taken place.");
+
+    // Check the parameters received by hook_entity_insert().
+    // Param 0: $flagging.
+    $this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->flag_name, $flag->name, "The Flagging entity passed to hook_entity_insert() has the expected flag name property.");
+    $this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->fid, $flag->fid, "The Flagging entity passed to hook_entity_insert() has the expected fid property.");
+    $this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->entity_type, 'node', "The Flagging entity passed to hook_entity_insert() has the expected entity type property.");
+    $this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->entity_id, $node->nid, "The Flagging entity passed to hook_entity_insert() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_entity_insert() has the expected uid property.");
+    $this->assertTrue(!empty($hook_data_variable['hook_entity_insert']['parameters'][0]->flagging_id), "The Flagging entity passed to hook_entity_insert() has the flagging ID set.");
+    // Param 1: $entity_type.
+    $this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][1], 'flagging', "hook_entity_insert() is passed the correct entity type.");
+
+    // Check the API data available to hook_entity_insert().
+    //debug($hook_data_variable['hook_entity_insert']['api_calls']);
+    $flag_get_entity_flags = $hook_data_variable['hook_entity_insert']['api_calls']['flag_get_entity_flags'];
+    $flag_get_entity_flags_uids = array_keys($flag_get_entity_flags);
+    $this->assertEqual($flag_get_entity_flags_uids, array($this->flag_unflag_user->uid), "hook_entity_insert() gets correct data for flagging users from flag_get_entity_flags()");
+    $flag_get_entity_flags_flagging = $flag_get_entity_flags[$this->flag_unflag_user->uid];
+    $this->assertEqual($flag_get_entity_flags_flagging->fid, $flag->fid, "hook_entity_insert() gets a correct fid on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_type, 'node', "hook_entity_insert() gets a correct entity type on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_id, $node->nid, "hook_entity_insert() gets a correct entity ID on the Flagging obtained from flag_get_entity_flags()");
+
+    $flag_get_user_flags = $hook_data_variable['hook_entity_insert']['api_calls']['flag_get_user_flags'];
+    $flag_get_user_flags_flagging = $flag_get_user_flags[$flag->name];
+    $this->assertEqual($flag_get_user_flags_flagging->fid, $flag->fid, "hook_entity_insert() gets a correct fid on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_type, 'node', "hook_entity_insert() gets a correct entity type on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_id, $node->nid, "hook_entity_insert() gets a correct entity ID on the Flagging obtained from flag_get_user_flags()");
+
+    // The flag counts have been increased.
+    $flag_get_counts = $hook_data_variable['hook_entity_insert']['api_calls']['flag_get_counts'];
+    $this->assertEqual($flag_get_counts[$flag->name], 1, "hook_entity_insert() gets a correct flag count from flag_get_counts().");
+
+    $flag_get_flag_counts = $hook_data_variable['hook_entity_insert']['api_calls']['flag_get_flag_counts'];
+    $this->assertEqual($flag_get_flag_counts, 1, "hook_entity_insert() gets a correct flag count from flag_get_flag_counts().");
+
+    $flag_get_entity_flag_counts = $hook_data_variable['hook_entity_insert']['api_calls']['flag_get_entity_flag_counts'];
+    $this->assertEqual($flag_get_entity_flag_counts, 1, "hook_entity_insert() gets a correct flag count from flag_get_entity_flag_counts().");
+
+    $flag_get_user_flag_counts = $hook_data_variable['hook_entity_insert']['api_calls']['flag_get_user_flag_counts'];
+    $this->assertEqual($flag_get_user_flag_counts, 1, "hook_entity_insert() gets a correct flag count from flag_get_user_flag_counts().");
+
+    // Check the parameters received by hook_flag_flag().
+    // Param 0: $flag.
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][0], $flag, "The flag object is passed to hook_flag_flag().");
+    // Param 1: $entity_id.
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][1], $node->nid, "The entity ID is passed to hook_flag_flag().");
+    // Param 2: $account.
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][2]->uid, $this->flag_unflag_user->uid, "The user account is passed to hook_flag_flag().");
+    // Param 3: $flagging.
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][3]->flag_name, $flag->name, "The Flagging entity passed to hook_flag_flag() has the expected flag name property.");
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][3]->fid, $flag->fid, "The Flagging entity passed to hook_flag_flag() has the expected fid property.");
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][3]->entity_type, 'node', "The Flagging entity passed to hook_flag_flag() has the expected entity type property.");
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][3]->entity_id, $node->nid, "The Flagging entity passed to hook_flag_flag() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_flag_flag']['parameters'][3]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_flag_flag() has the expected uid property.");
+
+    // Check the API data available to hook_flag_flag().
+    //debug($hook_data_variable['hook_flag_flag']['api_calls']);
+    $flag_get_entity_flags = $hook_data_variable['hook_flag_flag']['api_calls']['flag_get_entity_flags'];
+    $flag_get_entity_flags_uids = array_keys($flag_get_entity_flags);
+    $this->assertEqual($flag_get_entity_flags_uids, array($this->flag_unflag_user->uid), "hook_flag_flag() gets correct data for flagging users from flag_get_entity_flags()");
+    $flag_get_entity_flags_flagging = $flag_get_entity_flags[$this->flag_unflag_user->uid];
+    $this->assertEqual($flag_get_entity_flags_flagging->fid, $flag->fid, "hook_flag_flag() gets a correct fid on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_type, 'node', "hook_flag_flag() gets a correct entity type on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_id, $node->nid, "hook_flag_flag() gets a correct entity ID on the Flagging obtained from flag_get_entity_flags()");
+
+    $flag_get_user_flags = $hook_data_variable['hook_flag_flag']['api_calls']['flag_get_user_flags'];
+    $flag_get_user_flags_flagging = $flag_get_user_flags[$flag->name];
+    $this->assertEqual($flag_get_user_flags_flagging->fid, $flag->fid, "hook_flag_flag() gets a correct fid on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_type, 'node', "hook_flag_flag() gets a correct entity type on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_id, $node->nid, "hook_flag_flag() gets a correct entity ID on the Flagging obtained from flag_get_user_flags()");
+
+    // The flag counts have been increased.
+    $flag_get_counts = $hook_data_variable['hook_flag_flag']['api_calls']['flag_get_counts'];
+    $this->assertEqual($flag_get_counts[$flag->name], 1, "hook_flag_flag() gets a correct flag count from flag_get_counts().");
+
+    $flag_get_flag_counts = $hook_data_variable['hook_flag_flag']['api_calls']['flag_get_flag_counts'];
+    $this->assertEqual($flag_get_flag_counts, 1, "hook_flag_flag() gets a correct flag count from flag_get_flag_counts().");
+
+    $flag_get_entity_flag_counts = $hook_data_variable['hook_flag_flag']['api_calls']['flag_get_entity_flag_counts'];
+    $this->assertEqual($flag_get_entity_flag_counts, 1, "hook_flag_flag() gets a correct flag count from flag_get_entity_flag_counts().");
+
+    $flag_get_user_flag_counts = $hook_data_variable['hook_flag_flag']['api_calls']['flag_get_user_flag_counts'];
+    $this->assertEqual($flag_get_user_flag_counts, 1, "hook_flag_flag() gets a correct flag count from flag_get_user_flag_counts().");
+
+    // Clear the tracking variable.
+    variable_set('flag_hook_test_hook_tracking', array());
+
+    // Get the Flagging, and re-flag the node.
+    // This does nothing in our case, but is the API for updating a Flagging
+    // entity with changes to its Field API fields.
+    // Query the database to get the Flagging ID rather than Flag API so we
+    // don't interefere with any caches.
+    $query = db_select('flagging', 'f');
+    $query->addField('f', 'flagging_id');
+    $query->condition('fid', $flag->fid)
+      ->condition('entity_id', $node->nid);
+    $flagging_id = $query
+      ->execute()
+      ->fetchField();
+    $flagging = flagging_load($flagging_id);
+
+    // Re-flag the node passing in the flagging entity.
+    $flag->flag('flag', $node->nid, $this->flag_unflag_user, FALSE, $flagging);
+
+    // Get the variable the test module sets the hook order into.
+    $hook_data_variable = variable_get('flag_hook_test_hook_tracking', array());
+    //debug($hook_data_variable);
+
+    $expected_hook_order = array(
+      'hook_entity_presave',
+      'hook_entity_update',
+      // Note that hook_flag() and the rule are not invoked, as this is not a
+      // new act of flagging.
+    );
+
+    // Check the hooks are invoked in the correct order.
+    $this->assertIdentical($expected_hook_order, array_keys($hook_data_variable), "The hooks are invoked in the correct order when re-flagging a node.");
+
+    // Check the parameters received by hook_entity_presave().
+    // Param 0: $flagging.
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->flag_name, $flag->name, "The Flagging entity passed to hook_entity_presave() has the expected flag name property.");
+    //$this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->fid, $flag->fid);
+    //$this->assertEqual($hook_data_variable['hook_entity_insert']['parameters'][0]->entity_type, 'node');
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->entity_id, $node->nid, "The Flagging entity passed to hook_entity_presave() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_entity_presave() has the expected uid property.");
+    // Param 1: $entity_type.
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][1], 'flagging', "hook_entity_presave() is passed the correct entity type.");
+
+    // Check the API data available to hook_entity_presave().
+    //debug($hook_data_variable['hook_entity_presave']['api_calls']);
+    $flag_get_entity_flags = $hook_data_variable['hook_entity_presave']['api_calls']['flag_get_entity_flags'];
+    $flag_get_entity_flags_uids = array_keys($flag_get_entity_flags);
+    $this->assertEqual($flag_get_entity_flags_uids, array($this->flag_unflag_user->uid), "hook_entity_presave() gets correct data for flagging users from flag_get_entity_flags()");
+    $flag_get_entity_flags_flagging = $flag_get_entity_flags[$this->flag_unflag_user->uid];
+    $this->assertEqual($flag_get_entity_flags_flagging->fid, $flag->fid, "hook_entity_presave() gets a correct fid on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_type, 'node', "hook_entity_presave() gets a correct entity type on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_id, $node->nid, "hook_entity_presave() gets a correct entity ID on the Flagging obtained from flag_get_entity_flags()");
+
+    $flag_get_user_flags = $hook_data_variable['hook_entity_presave']['api_calls']['flag_get_user_flags'];
+    $flag_get_user_flags_flagging = $flag_get_user_flags[$flag->name];
+    $this->assertEqual($flag_get_user_flags_flagging->fid, $flag->fid, "hook_entity_presave() gets a correct fid on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_type, 'node', "hook_entity_presave() gets a correct entity type on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_id, $node->nid, "hook_entity_presave() gets a correct entity ID on the Flagging obtained from flag_get_user_flags()");
+
+    // The flag counts have not changed.
+    $flag_get_counts = $hook_data_variable['hook_entity_presave']['api_calls']['flag_get_counts'];
+    $this->assertEqual($flag_get_counts[$flag->name], 1, "hook_entity_presave() gets a correct flag count from flag_get_counts().");
+
+    $flag_get_flag_counts = $hook_data_variable['hook_entity_presave']['api_calls']['flag_get_flag_counts'];
+    $this->assertEqual($flag_get_flag_counts, 1, "hook_entity_presave() gets a correct flag count from flag_get_flag_counts().");
+
+    $flag_get_entity_flag_counts = $hook_data_variable['hook_entity_presave']['api_calls']['flag_get_entity_flag_counts'];
+    $this->assertEqual($flag_get_entity_flag_counts, 1, "hook_entity_presave() gets a correct flag count from flag_get_entity_flag_counts().");
+
+    $flag_get_user_flag_counts = $hook_data_variable['hook_entity_presave']['api_calls']['flag_get_user_flag_counts'];
+    $this->assertEqual($flag_get_user_flag_counts, 1, "hook_entity_presave() gets a correct flag count from flag_get_user_flag_counts().");
+
+    // Check the parameters received by hook_entity_update().
+    // Param 0: $flagging.
+    $this->assertEqual($hook_data_variable['hook_entity_update']['parameters'][0]->flag_name, $flag->name, "The Flagging entity passed to hook_entity_update() has the expected flag name property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->fid, $flag->fid, "The Flagging entity passed to hook_entity_presave() has the expected fid property.");
+    $this->assertEqual($hook_data_variable['hook_entity_presave']['parameters'][0]->entity_type, 'node', "The Flagging entity passed to hook_entity_presave() has the expected entity type property.");
+    $this->assertEqual($hook_data_variable['hook_entity_update']['parameters'][0]->entity_id, $node->nid, "The Flagging entity passed to hook_entity_update() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_entity_update']['parameters'][0]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_entity_update() has the expected uid property.");
+    $this->assertTrue(!empty($hook_data_variable['hook_entity_update']['parameters'][0]->flagging_id), "The Flagging entity passed to hook_entity_update() has the flagging ID set.");
+    // Param 1: $entity_type.
+    $this->assertEqual($hook_data_variable['hook_entity_update']['parameters'][1], 'flagging', "hook_entity_update() is passed the correct entity type.");
+
+    // Check the API data available to hook_entity_update().
+    //debug($hook_data_variable['hook_entity_update']['api_calls']);
+    $flag_get_entity_flags = $hook_data_variable['hook_entity_update']['api_calls']['flag_get_entity_flags'];
+    $flag_get_entity_flags_uids = array_keys($flag_get_entity_flags);
+    $this->assertEqual($flag_get_entity_flags_uids, array($this->flag_unflag_user->uid), "hook_entity_update() gets correct data for flagging users from flag_get_entity_flags()");
+    $flag_get_entity_flags_flagging = $flag_get_entity_flags[$this->flag_unflag_user->uid];
+    $this->assertEqual($flag_get_entity_flags_flagging->fid, $flag->fid, "hook_entity_update() gets a correct fid on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_type, 'node', "hook_entity_update() gets a correct entity type on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_id, $node->nid, "hook_entity_update() gets a correct entity ID on the Flagging obtained from flag_get_entity_flags()");
+
+    $flag_get_user_flags = $hook_data_variable['hook_entity_update']['api_calls']['flag_get_user_flags'];
+    $flag_get_user_flags_flagging = $flag_get_user_flags[$flag->name];
+    $this->assertEqual($flag_get_user_flags_flagging->fid, $flag->fid, "hook_entity_update() gets a correct fid on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_type, 'node', "hook_entity_update() gets a correct entity type on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_id, $node->nid, "hook_entity_update() gets a correct entity ID on the Flagging obtained from flag_get_user_flags()");
+
+    // The flag counts have not changed.
+    $flag_get_counts = $hook_data_variable['hook_entity_update']['api_calls']['flag_get_counts'];
+    $this->assertEqual($flag_get_counts[$flag->name], 1, "hook_entity_update() gets a correct flag count from flag_get_counts().");
+
+    $flag_get_flag_counts = $hook_data_variable['hook_entity_update']['api_calls']['flag_get_flag_counts'];
+    $this->assertEqual($flag_get_flag_counts, 1, "hook_entity_update() gets a correct flag count from flag_get_flag_counts().");
+
+    $flag_get_entity_flag_counts = $hook_data_variable['hook_entity_update']['api_calls']['flag_get_entity_flag_counts'];
+    $this->assertEqual($flag_get_entity_flag_counts, 1, "hook_entity_update() gets a correct flag count from flag_get_entity_flag_counts().");
+
+    $flag_get_user_flag_counts = $hook_data_variable['hook_entity_update']['api_calls']['flag_get_user_flag_counts'];
+    $this->assertEqual($flag_get_user_flag_counts, 1, "hook_entity_update() gets a correct flag count from flag_get_user_flag_counts().");
+
+    // Clear the tracking variable.
+    variable_set('flag_hook_test_hook_tracking', array());
+
+    // Unflag the node as the user.
+    $flag->flag('unflag', $node->nid, $this->flag_unflag_user);
+
+    // Get the variable the test module sets the hook order into.
+    $hook_data_variable = variable_get('flag_hook_test_hook_tracking', array());
+    //debug($hook_data_variable);
+
+    $expected_hook_order = array(
+      'hook_flag_unflag',
+      'rules_event',
+      'hook_entity_delete',
+    );
+
+    // Check the hooks are invoked in the correct order.
+    $this->assertIdentical($expected_hook_order, array_keys($hook_data_variable), "The hooks are invoked in the correct order when unflagging a node.");
+
+    // Check the parameters received by hook_flag_unflag().
+    // Param 0: $flag.
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][0], $flag, "The flag object is passed to hook_flag_unflag().");
+    // Param 1: $entity_id.
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][1], $node->nid, "The entity ID is passed to hook_flag_unflag().");
+    // Param 2: $account.
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][2]->uid, $this->flag_unflag_user->uid, "The user account is passed to hook_flag_unflag().");
+    // Param 3: $flagging.
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][3]->flag_name, $flag->name, "The Flagging entity passed to hook_flag_unflag() has the expected flag name property.");
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][3]->fid, $flag->fid, "The Flagging entity passed to hook_entity_presave() has the expected fid property.");
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][3]->entity_type, 'node', "The Flagging entity passed to hook_entity_presave() has the expected entity type property.");
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][3]->entity_id, $node->nid, "The Flagging entity passed to hook_flag_unflag() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_flag_unflag']['parameters'][3]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_flag_unflag() has the expected uid property.");
+
+    // Check the API data available to hook_flag_unflag().
+    //debug($hook_data_variable['hook_flag_unflag']['api_calls']);
+    // The unflagging is not yet done, so flag_get_entity_flags() will find the
+    // flagging data.
+    $flag_get_entity_flags = $hook_data_variable['hook_flag_unflag']['api_calls']['flag_get_entity_flags'];
+    $flag_get_entity_flags_uids = array_keys($flag_get_entity_flags);
+    $this->assertEqual($flag_get_entity_flags_uids, array($this->flag_unflag_user->uid), "hook_flag_unflag() gets correct data for flagging users from flag_get_entity_flags()");
+    $flag_get_entity_flags_flagging = $flag_get_entity_flags[$this->flag_unflag_user->uid];
+    $this->assertEqual($flag_get_entity_flags_flagging->fid, $flag->fid, "hook_flag_unflag() gets a correct fid on the Flagging obtained from flag_get_entity_flags(): the Flagging has not yet been deleted.");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_type, 'node', "hook_flag_unflag() gets a correct entity type on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_id, $node->nid, "hook_flag_unflag() gets a correct entity ID on the Flagging obtained from flag_get_entity_flags()");
+
+    $flag_get_user_flags = $hook_data_variable['hook_flag_unflag']['api_calls']['flag_get_user_flags'];
+    $flag_get_user_flags_flagging = $flag_get_user_flags[$flag->name];
+    $this->assertEqual($flag_get_user_flags_flagging->fid, $flag->fid, "hook_flag_unflag() gets a correct fid on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_type, 'node', "hook_flag_unflag() gets a correct entity type on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_id, $node->nid, "hook_flag_unflag() gets a correct entity ID on the Flagging obtained from flag_get_user_flags()");
+
+    // The flag counts have already been decreased.
+    $flag_get_counts = $hook_data_variable['hook_flag_unflag']['api_calls']['flag_get_counts'];
+    $this->assertEqual($flag_get_counts, array(), "hook_flag_unflag() gets a correct flag count from flag_get_counts().");
+
+    $flag_get_flag_counts = $hook_data_variable['hook_flag_unflag']['api_calls']['flag_get_flag_counts'];
+    $this->assertEqual($flag_get_flag_counts, 0, "hook_flag_unflag() gets a correct flag count from flag_get_flag_counts().");
+
+    // flag_get_entity_flag_counts() queries the {flagging} table, so is not
+    // updated yet.
+    $flag_get_entity_flag_counts = $hook_data_variable['hook_flag_unflag']['api_calls']['flag_get_entity_flag_counts'];
+    $this->assertEqual($flag_get_entity_flag_counts, 1, "hook_flag_unflag() gets a correct flag count from flag_get_entity_flag_counts().");
+
+    // flag_get_user_flag_counts() queries the {flagging} table, so is not
+    // updated yet.
+    $flag_get_user_flag_counts = $hook_data_variable['hook_flag_unflag']['api_calls']['flag_get_user_flag_counts'];
+    $this->assertEqual($flag_get_user_flag_counts, 1, "hook_flag_unflag() gets a correct flag count from flag_get_user_flag_counts().");
+
+    // Check the parameters received by hook_entity_delete().
+    // Param 0: $flagging.
+    $this->assertEqual($hook_data_variable['hook_entity_delete']['parameters'][0]->flag_name, $flag->name, "The Flagging entity passed to hook_entity_delete() has the expected flag name property.");
+    $this->assertEqual($hook_data_variable['hook_entity_delete']['parameters'][0]->fid, $flag->fid, "The Flagging entity passed to hook_entity_presave() has the expected fid property.");
+    $this->assertEqual($hook_data_variable['hook_entity_delete']['parameters'][0]->entity_type, 'node', "The Flagging entity passed to hook_entity_presave() has the expected entity type property.");
+    $this->assertEqual($hook_data_variable['hook_entity_delete']['parameters'][0]->entity_id, $node->nid, "The Flagging entity passed to hook_entity_delete() has the expected entity ID property.");
+    $this->assertEqual($hook_data_variable['hook_entity_delete']['parameters'][0]->uid, $this->flag_unflag_user->uid, "The Flagging entity passed to hook_entity_delete() has the expected uid property.");
+    $this->assertTrue(!empty($hook_data_variable['hook_entity_delete']['parameters'][0]->flagging_id), "The Flagging entity passed to hook_entity_delete() has the flagging ID set.");
+    // Param 1: $entity_type.
+    $this->assertEqual($hook_data_variable['hook_entity_delete']['parameters'][1], 'flagging', "hook_entity_delete() is passed the correct entity type.");
+
+    // Check the API data available to hook_entity_delete().
+    //debug($hook_data_variable['hook_entity_delete']['api_calls']);
+    // The unflagging is not yet done, so hook_entity_delete() will find the
+    // flagging data.
+    $flag_get_entity_flags = $hook_data_variable['hook_entity_delete']['api_calls']['flag_get_entity_flags'];
+    $flag_get_entity_flags_uids = array_keys($flag_get_entity_flags);
+    $this->assertEqual($flag_get_entity_flags_uids, array($this->flag_unflag_user->uid), "hook_entity_delete() gets correct data for flagging users from flag_get_entity_flags()");
+    $flag_get_entity_flags_flagging = $flag_get_entity_flags[$this->flag_unflag_user->uid];
+    $this->assertEqual($flag_get_entity_flags_flagging->fid, $flag->fid, "hook_entity_delete() gets a correct fid on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_type, 'node', "hook_entity_delete() gets a correct entity type on the Flagging obtained from flag_get_entity_flags()");
+    $this->assertEqual($flag_get_entity_flags_flagging->entity_id, $node->nid, "hook_entity_delete() gets a correct entity ID on the Flagging obtained from flag_get_entity_flags()");
+
+    $flag_get_user_flags = $hook_data_variable['hook_entity_delete']['api_calls']['flag_get_user_flags'];
+    $flag_get_user_flags_flagging = $flag_get_user_flags[$flag->name];
+    $this->assertEqual($flag_get_user_flags_flagging->fid, $flag->fid, "hook_entity_delete() gets a correct fid on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_type, 'node', "hook_entity_delete() gets a correct entity type on the Flagging obtained from flag_get_user_flags()");
+    $this->assertEqual($flag_get_user_flags_flagging->entity_id, $node->nid, "hook_entity_delete() gets a correct entity ID on the Flagging obtained from flag_get_user_flags()");
+
+    // The flag counts have already been decreased.
+    $flag_get_counts = $hook_data_variable['hook_entity_delete']['api_calls']['flag_get_counts'];
+    $this->assertEqual($flag_get_counts, array(), "hook_entity_delete() gets a correct flag count from flag_get_counts().");
+
+    $flag_get_flag_counts = $hook_data_variable['hook_entity_delete']['api_calls']['flag_get_flag_counts'];
+    $this->assertEqual($flag_get_flag_counts, 0, "hook_entity_delete() gets a correct flag count from flag_get_flag_counts().");
+
+    // flag_get_entity_flag_counts() queries the {flagging} table, so is not
+    // updated yet.
+    $flag_get_entity_flag_counts = $hook_data_variable['hook_entity_delete']['api_calls']['flag_get_entity_flag_counts'];
+    $this->assertEqual($flag_get_entity_flag_counts, 1, "hook_entity_delete() gets a correct flag count from flag_get_entity_flag_counts().");
+
+    // flag_get_user_flag_counts() queries the {flagging} table, so is not
+    // updated yet.
+    $flag_get_user_flag_counts = $hook_data_variable['hook_entity_delete']['api_calls']['flag_get_user_flag_counts'];
+    $this->assertEqual($flag_get_user_flag_counts, 1, "hook_entity_delete() gets a correct flag count from flag_get_user_flag_counts().");
+  }
+
+}

+ 12 - 0
sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.info

@@ -0,0 +1,12 @@
+name = Flag Fields Test
+description = Test module for fields on Flagging entities.
+dependencies[] = flag
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-03-02
+version = "7.x-3.6"
+core = "7.x"
+project = "flag"
+datestamp = "1425327793"
+

+ 67 - 0
sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.install

@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file flag_fields_test.install
+ * Contains install hooks.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function flag_fields_test_install() {
+  // Add a number field to the flagging.
+  $field = array(
+    'field_name' => 'flag_fields_test_integer',
+    'type' => 'number_integer',
+    'cardinality' => '1',
+    'settings' => array(),
+  );
+  field_create_field($field);
+
+  $instance = array(
+    'field_name' => 'flag_fields_test_integer',
+    'entity_type' => 'flagging',
+    'bundle' => 'flag_fields_test_flag',
+    'label' => 'Test integer',
+    'widget' => array(
+      'weight' => 0,
+      'type' => 'number',
+      'module' => 'number',
+      'active' => 0,
+      'settings' => array(),
+    ),
+    'settings' => array(
+      'min' => '',
+      'max' => '11',
+      'prefix' => '',
+      'suffix' => '',
+      'user_register_form' => FALSE,
+    ),
+    'display' => array(
+      'default' => array(
+        'label' => 'above',
+        'type' => 'number_integer',
+        'settings' => array(
+          'thousand_separator' => ' ',
+          'decimal_separator' => '.',
+          'scale' => 0,
+          'prefix_suffix' => TRUE,
+        ),
+        'module' => 'number',
+        'weight' => 1,
+      ),
+    ),
+  );
+  field_create_instance($instance);
+}
+
+/**
+ * Implements hook_uninstall().
+ *
+ * Not needed for testing, but useful for when developing tests as it allows
+ * use of Devel module's reinstall tool.
+ */
+function flag_fields_test_uninstall() {
+  // Delete our fields.
+  field_delete_field('flag_fields_test_integer');
+}

+ 45 - 0
sites/all/modules/contrib/flag/flag/tests/flag_fields_test/flag_fields_test.module

@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file flag_fields_test.module
+ * Test module for fields on flagging entities.
+ */
+
+/**
+ * Implements hook_flag_default_flags().
+ */
+function flag_fields_test_flag_default_flags() {
+  $flags = array();
+  // Exported flag: "Flag fields test".
+  $flags['flag_fields_test_flag'] = array(
+    'entity_type' => 'node',
+    'title' => 'Flag fields test',
+    'global' => 1,
+    'types' => array(),
+    'flag_short' => 'Flag with the test flag',
+    'flag_long' => '',
+    'flag_message' => '',
+    'unflag_short' => 'Unflag',
+    'unflag_long' => '',
+    'unflag_message' => '',
+    'unflag_denied_text' => '',
+    'link_type' => 'confirm',
+    'weight' => -15,
+    'show_in_links' => array(
+      'full' => 'full',
+      'teaser' => 'teaser',
+      'rss' => 0,
+      'token' => 0,
+      'revision' => 0,
+    ),
+    'show_as_field' => 0,
+    'show_on_form' => 0,
+    'access_author' => '',
+    'show_contextual_link' => 0,
+    'i18n' => 0,
+    'flag_confirmation' => 'Confirm flagging this node',
+    'unflag_confirmation' => 'Confirm unflagging this node',
+    'api_version' => 3,
+  );
+  return $flags;
+}

+ 13 - 0
sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.info

@@ -0,0 +1,13 @@
+name = Flag Hooks Test
+description = Test module for invocation of flag hooks.
+dependencies[] = flag
+dependencies[] = rules
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-03-02
+version = "7.x-3.6"
+core = "7.x"
+project = "flag"
+datestamp = "1425327793"
+

+ 184 - 0
sites/all/modules/contrib/flag/flag/tests/flag_hook_test/flag_hook_test.module

@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * @file flag_hook_test.module
+ * Test module for the hooks that Flag invokes.
+ */
+
+/**
+ * Store the hook name and parameters into a variable for retrieval by the test.
+ *
+ * Hook implementations should call this with their hook name and parameters.
+ *
+ * @param $hook_name
+ *  The name of the hook invoked.
+ * @param $function_parameters
+ *  The array of parameters the hook received.
+ * @param $flagging
+ *  (optional) The flagging entity that the hook received. If this is given,
+ *  then various flag API functions have their data set into the tracking
+ *  variable for verification by the test case.
+ */
+function _flag_hook_test_record_invocation($hook_name, $function_parameters, $flagging = NULL) {
+  $variable = variable_get('flag_hook_test_hook_tracking', array());
+
+  $variable[$hook_name] = array();
+  $variable[$hook_name]['parameters'] = $function_parameters;
+
+  // If a Flagging entity was passed in, call API functions and store their data
+  // for the test case to check.
+  if (isset($flagging)) {
+    $flag = flag_get_flag(NULL, $flagging->fid);
+
+    $variable[$hook_name]['api_calls'] = array();
+
+    $variable[$hook_name]['api_calls']['flag_get_entity_flags'] = flag_get_entity_flags('node', $flagging->entity_id, $flag->name);
+
+    $variable[$hook_name]['api_calls']['flag_get_user_flags'] = flag_get_user_flags('node', $flagging->entity_id, $flagging->uid);
+
+    $variable[$hook_name]['api_calls']['flag_get_counts'] = flag_get_counts('node', $flagging->entity_id);
+
+    $variable[$hook_name]['api_calls']['flag_get_flag_counts'] = flag_get_flag_counts($flag->name);
+
+    $variable[$hook_name]['api_calls']['flag_get_entity_flag_counts'] = flag_get_entity_flag_counts($flag, 'node');
+
+    $account = user_load($flagging->uid);
+    $variable[$hook_name]['api_calls']['flag_get_user_flag_counts'] = flag_get_user_flag_counts($flag, $account);
+  }
+
+  variable_set('flag_hook_test_hook_tracking', $variable);
+}
+
+/**
+ * Implements hook_flag_flag().
+ */
+function flag_hook_test_flag_flag($flag, $entity_id, $account, $flagging) {
+  _flag_hook_test_record_invocation('hook_flag_flag', func_get_args(), $flagging);
+}
+
+/**
+ * Implements hook_flag_unflag().
+ */
+function flag_hook_test_flag_unflag($flag, $entity_id, $account, $flagging) {
+  _flag_hook_test_record_invocation('hook_flag_unflag', func_get_args(), $flagging);
+}
+
+/**
+ * Implements hook_entity_presave().
+ */
+function flag_hook_test_entity_presave($entity, $type) {
+  if ($type == 'flagging') {
+    _flag_hook_test_record_invocation('hook_entity_presave', func_get_args(), $entity);
+  }
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function flag_hook_test_entity_insert($entity, $type) {
+  if ($type == 'flagging') {
+    _flag_hook_test_record_invocation('hook_entity_insert', func_get_args(), $entity);
+  }
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function flag_hook_test_entity_update($entity, $type) {
+  if ($type == 'flagging') {
+    _flag_hook_test_record_invocation('hook_entity_update', func_get_args(), $entity);
+  }
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function flag_hook_test_entity_delete($entity, $type) {
+  if ($type == 'flagging') {
+    _flag_hook_test_record_invocation('hook_entity_delete', func_get_args(), $entity);
+  }
+}
+
+// ========================================================= Configuration
+
+/**
+ * Implements hook_flag_default_flags().
+ */
+function flag_hook_test_flag_default_flags() {
+  $flags = array();
+  $flags['flag_hook_test_flag'] = array(
+    'entity_type' => 'node',
+    'title' => 'Test Flag',
+    'global' => FALSE,
+    'types' => array(
+      0 => 'article',
+    ),
+    'flag_short' => 'Flag this',
+    'flag_long' => 'Flag this post',
+    'flag_message' => 'This post has been flagged',
+    'unflag_short' => 'Unflag this',
+    'unflag_long' => 'Remove this post from your flagged items',
+    'unflag_message' => 'This post has been unflagged',
+    'unflag_denied_text' => 'You may not unflag this item',
+    'link_type' => 'normal',
+    'weight' => 0,
+    'show_in_links' => array(
+      'full' => TRUE,
+      'teaser' => TRUE,
+    ),
+    'show_as_field' => FALSE,
+    'show_on_form' => FALSE,
+    'access_author' => '',
+    'show_contextual_link' => TRUE,
+    'show_on_profile' => FALSE,
+    'access_uid' => '',
+    'api_version' => 3,
+  );
+  return $flags;
+}
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function flag_hook_test_rules_action_info() {
+  return array(
+    'flag_test_action' => array(
+      'label' => t('Flag test action'),
+      'group' => t('Flag test'),
+    ),
+  );
+}
+
+/**
+ * Test action for flagging.
+ */
+function flag_test_action() {
+  _flag_hook_test_record_invocation('rules_event', func_get_args());
+}
+
+/**
+ * Implements hook_default_rules_configuration().
+ */
+function flag_hook_test_default_rules_configuration() {
+  $configs['flag_test_rule_flag'] = rules_import('{ "flag_test_rule" : {
+      "LABEL" : "Flag test rule",
+      "PLUGIN" : "reaction rule",
+      "OWNER" : "rules",
+      "REQUIRES" : [ "flag_hook_test", "flag" ],
+      "ON" : { "flag_flagged_flag_hook_test_flag" : [] },
+      "DO" : [ { "flag_test_action" : [] } ]
+    }
+  }');
+
+  $configs['flag_test_rule_unflag'] = rules_import('{ "flag_test_rule" : {
+      "LABEL" : "Flag test rule",
+      "PLUGIN" : "reaction rule",
+      "OWNER" : "rules",
+      "REQUIRES" : [ "flag_hook_test", "flag" ],
+      "ON" : { "flag_unflagged_flag_hook_test_flag" : [] },
+      "DO" : [ { "flag_test_action" : [] } ]
+    }
+  }');
+
+  return $configs;
+}

+ 3 - 3
sites/all/modules/contrib/flag/flag/tests/flagaccesstest/flagaccesstest.info

@@ -5,9 +5,9 @@ dependencies[] = flag
 package = Flags
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-09-13
-version = "7.x-3.2"
+; Information added by Drupal.org packaging script on 2015-03-02
+version = "7.x-3.6"
 core = "7.x"
 project = "flag"
-datestamp = "1379063829"
+datestamp = "1425327793"
 

+ 1 - 1
sites/all/modules/contrib/flag/flag/theme/flag.js

@@ -98,7 +98,7 @@ Drupal.flagLink = function(context) {
         $wrapper.removeClass('flag-waiting');
       }
     });
-  };
+  }
 
   $('a.flag-link-toggle:not(.flag-processed)', context).addClass('flag-processed').click(flagClick);
 };

+ 21 - 12
sites/all/modules/contrib/flag/flag/theme/flag.tpl.php

@@ -2,41 +2,50 @@
 
 /**
  * @file
- * Default theme implementation to display a flag link, and a message after the action
- * is carried out.
+ * Default theme implementation to display a flag link, and a message after the
+ * action is carried out.
  *
  * Available variables:
  *
  * - $flag: The flag object itself. You will only need to use it when the
  *   following variables don't suffice.
- * - $flag_name_css: The flag name, with all "_" replaced with "-". For use in 'class'
- *   attributes.
- * - $flag_classes: A space-separated list of CSS classes that should be applied to the link.
+ * - $flag_name_css: The flag name, with all "_" replaced with "-". For use in
+ *   'class' attributes.
+ * - $flag_classes: A space-separated list of CSS classes that should be applied
+ *   to the link.
  *
- * - $action: The action the link is about to carry out, either "flag" or "unflag".
+ * - $action: The action the link is about to carry out, either "flag" or
+ *   "unflag".
  * - $status: The status of the item; either "flagged" or "unflagged".
+ * - $entity_id: The id of the entity item.
  *
- * - $link_href: The URL for the flag link.
+ * - $link['href']: The path for the flag link.
+ * - $link['query']: Array of query string parameters, such as "destination".
+ * - $link_href: The URL for the flag link, query string included.
  * - $link_text: The text to show for the link.
  * - $link_title: The title attribute for the link.
  *
- * - $message_text: The long message to show after a flag action has been carried out.
- * - $message_classes: A space-separated list of CSS classes that should be applied to
+ * - $message_text: The long message to show after a flag action has been
+ *   carried out.
+ * - $message_classes: A space-separated list of CSS classes that should be
+ *   applied to
  *   the message.
- * - $after_flagging: This template is called for the link both before and after being
+ * - $after_flagging: This template is called for the link both before and after
+ *   being
  *   flagged. If displaying to the user immediately after flagging, this value
  *   will be boolean TRUE. This is usually used in conjunction with immedate
  *   JavaScript-based toggling of flags.
  * - $needs_wrapping_element: Determines whether the flag displays a wrapping
  *   HTML DIV element.
+ * - $errors: An array of error messages.
  *
  * Template suggestions available, listed from the most specific template to
  * the least. Drupal will use the most specific template it finds:
  * - flag--name.tpl.php
  * - flag--link-type.tpl.php
  *
- * NOTE: This template spaces out the <span> tags for clarity only. When doing some
- * advanced theming you may have to remove all the whitespace.
+ * NOTE: This template spaces out the <span> tags for clarity only. When doing
+ * some advanced theming you may have to remove all the whitespace.
  */
 ?>
 <?php if ($needs_wrapping_element): ?>