Index: flag.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.inc,v
retrieving revision 1.1.2.19
diff -u -F^[^a-z]*function -r1.1.2.19 flag.inc
--- flag.inc	16 Oct 2008 16:13:00 -0000	1.1.2.19
+++ flag.inc	30 Oct 2008 15:18:44 -0000
@@ -602,6 +602,30 @@   function get_relevant_action_objects($
    */
 
   /**
+   * @defgroup rules Rules integration
+   * @{
+   * Methods that can be overridden to support the Rules module.
+   */
+
+  /**
+   * Defines the Rules arguments involved in a flag event.
+   */
+  function rules_get_event_arguments_definition() {
+    return array();
+  }
+
+  /**
+   * Populates the Rules arguments involved in a flag event.
+   */
+  function rules_get_event_arguments($content_id) {
+    return array();
+  }
+
+  /**
+   * @} End of "defgroup rules".
+   */
+
+  /**
    * @defgroup views Views 2 integration
    * @{
    * Methods that can be overridden to support the Views module.
@@ -818,6 +842,21 @@   function get_relevant_action_objects($
     );
   }
 
+  function rules_get_event_arguments_definition() {
+    return array(
+      'node' => array(
+        'type' => 'node',
+        'label' => t('flagged node'),
+      ),
+    );
+  }
+
+  function rules_get_event_arguments($content_id) {
+    return array(
+      'node' => $this->fetch_content($content_id),
+    );
+  }
+
   function get_views_info() {
     return array(
       'views table' => 'node',
@@ -913,6 +952,27 @@   function get_relevant_action_objects($
     );
   }
 
+  function rules_get_event_arguments_definition() {
+    return array(
+      'comment' => array(
+        'type' => 'comment',
+        'label' => t('flagged comment'),
+      ),
+      'node' => array(
+        'type' => 'node',
+        'label' => t("the flagged comment's node"),
+      ),
+    );
+  }
+
+  function rules_get_event_arguments($content_id) {
+    $comment = $this->fetch_content($content_id);
+    return array(
+      'comment' => $comment,
+      'node' => node_load($comment->nid),
+    );
+  }
+
   function get_views_info() {
     return array(
       'views table' => 'comments',
@@ -1009,6 +1069,21 @@   function get_relevant_action_objects($
     );
   }
 
+  function rules_get_event_arguments_definition() {
+    return array(
+      'user' => array(
+        'type' => 'user',
+        'label' => t('flagged user'),
+      ),
+    );
+  }
+
+  function rules_get_event_arguments($content_id) {
+    return array(
+      'user' => $this->fetch_content($content_id),
+    );
+  }
+
   function get_views_info() {
     return array(
       'views table' => 'users',
Index: flag.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.module,v
retrieving revision 1.11.2.48
diff -u -F^[^a-z]*function -r1.11.2.48 flag.module
--- flag.module	13 Oct 2008 11:29:01 -0000	1.11.2.48
+++ flag.module	30 Oct 2008 15:18:46 -0000
@@ -69,9 +69,12 @@ function flag_menu() {
  */
 function flag_init() {
   $path = drupal_get_path('module', 'flag');
+  // Comment out Actions support so that it doesn't clash with Rules.
+  /*
   if (module_exists('trigger')) {
     include_once $path .'/flag.actions.inc';
   }
+  */
   if (module_exists('token')) {
     include_once $path .'/includes/flag.token.inc';
   }
@@ -732,6 +735,13 @@ function flag_flag($action, $flag, $cont
     }
 
   }
+
+  if (module_exists('rules')) {
+    $event_name = ($action == 'flag' ? 'flag_flagged_' : 'flag_unflagged_') . $flag->name;
+    $arguments = array('flagging_user' => $account);
+    $arguments += $flag->rules_get_event_arguments($content_id);
+    rules_invoke_event($event_name, $arguments);
+  }
 }
 
 /**
@@ -802,6 +812,9 @@ function flag_theme() {
     'flag_token_help' => array(
       'arguments' => array('types' => NULL, 'prefix' => NULL, 'suffix' => NULL),
     ),
+    'flag_rules_radios' => array(
+      'arguments' => array(),
+    ),
   );
 }
 
Index: flag.rules.inc
===================================================================
RCS file: flag.rules.inc
diff -N flag.rules.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ flag.rules.inc	30 Oct 2008 15:18:46 -0000
@@ -0,0 +1,149 @@
+<?php
+// $Id$
+
+/**
+ * @file flag.rules.inc
+ * Rules integration for the Flag module.
+ */
+
+/**
+ * Implementation of hook_rules_event_info().
+ */
+function flag_rules_event_info() {
+  $items = array();
+
+  $flags = flag_get_flags();
+  foreach ($flags as $flag) {
+    
+    $arguments = array(
+      // First, define ubiquitous arguments.
+      'flagging_user' => array(
+        'type' => 'user',
+        'label' => t('flagging user'),
+      ),
+    );
+    // Then, define flag-specific arguments.
+    $arguments += $flag->rules_get_event_arguments_definition();
+
+    // For each flag we define two events.
+    $items['flag_flagged_' . $flag->name] = array(
+      'module' => 'Flag',
+      'label' => t('A @flag-type has been flagged, under "@flag-title"', array('@flag-title' => $flag->get_title(), '@flag-type' => t($flag->content_type))),
+      'arguments' => $arguments,
+    );
+    $items['flag_unflagged_' . $flag->name] = array(
+      'module' => 'Flag',
+      'label' => t('A @flag-type has been unflagged, under "@flag-title"', array('@flag-title' => $flag->get_title(), '@flag-type' => t($flag->content_type))),
+      'arguments' => $arguments,
+    );
+  }
+
+  return $items;
+}
+
+/**
+ * Implementation of hook_rules_action_info().
+ */
+function flag_rules_action_info() {
+  return array(
+    'flag_rules_action_flag' => array(
+      'label' => t('Flag an object'),
+      'arguments' => array(
+        'flagging_user' => array(
+          'type' => 'user',
+          'label' => t('User on whose behalf to flag'),
+          'description' => t('For non-global flags, this is the user on whose behalf to flag the object. In addition, the access permissions to the flag are checked against this user.'),
+        ),
+      ),
+      'module' => 'Flag',
+    ),
+    'flag_rules_action_unflag' => array(
+      'label' => t('Unflag an object'),
+      'arguments' => array(
+        'flagging_user' => array(
+          'type' => 'user',
+          'label' => t('User on whose behalf to unflag'),
+          'description' => t('For non-global flags, this is the user on whose behalf to unflag the object. In addition, the access permissions to the flag are checked against this user.'),
+        ),
+      ),
+      'module' => 'Flag',
+    ),
+    'flag_rules_action_trim' => array(
+      'label' => t('Trim a flag'),
+      'help' => 'The <em>trim</em> action is used to restrict the number of objects that may be flagged. For example, you may wish your "Editor picks" queue (that is, flag) to contain a maximum of 3 nodes. The trim action is best understood when we think of a flag as a <em>queue</em>. The action works by discarding old flaggings; So newly flagged objects push older ones out of the queue.',
+      'arguments' => array(
+        'flagging_user' => array(
+          'type' => 'user',
+          'label' => t('User whose flag to trim'),
+          'description' => t('For non-global flags, this is the user whose flag to trim. (For global flags, this argument is ignored.)'),
+        ),
+        'cutoff_size' => array(
+          'type' => 'number',
+          'label' => t('Cutoff size'),
+          'description' => t('The maximum number of objects to keep in the queue. Newly flagged objects will be kept; older ones will be removed. Tip: by typing "1" here you implement a <em>singleton</em>.'),
+        ),
+      ),
+      'module' => 'Flag',
+    ),
+  );
+}
+
+function flag_rules_action_flag($flagging_user, $settings, $element, &$state) {
+  $flag = flag_get_flag($settings['flag']);
+  $object =& current( rules_get_variables(array($settings['flagee'][$flag->content_type]), $state) );
+  flag('flag', $flag->name, $flag->get_content_id($object), $flagging_user);
+}
+
+function flag_rules_action_unflag($flagging_user, $settings, $element, &$state) {
+  $flag = flag_get_flag($settings['flag']);
+  $object =& current( rules_get_variables(array($settings['flagee'][$flag->content_type]), $state) );
+  flag('unflag', $flag->name, $flag->get_content_id($object), $flagging_user);
+}
+
+function flag_rules_action_trim($flagging_user, $cutoff_size, $settings, $element, &$state) {
+  $flag = flag_get_flag($settings['flag']);
+  // We can't use db_query_range(), because we can't specify 'infinity'.
+  $result = db_query("SELECT * FROM {flag_content} WHERE fid = %d AND (uid = %d OR uid = 0) ORDER BY timestamp DESC", $flag->fid, $flagging_user->uid);
+  $i = 1;
+  while ($row = db_fetch_object($result)) {
+    if ($i++ > $cutoff_size) {
+      flag('unflag', $flag->name, $row->content_id, $flagging_user);
+    }
+  }
+}
+
+/**
+ * Implementation of hook_rules_condition_info().
+ */
+function flag_rules_condition_info() {
+  return array(
+    'flag_rules_condition_threshold' => array(
+      'label' => t('Flagging count'),
+      'help' => "<p>This condition is used to test the number of times an object is flagged. For example, you may wish to find out if a node is flagged more than three times; Because, if it's a 'spam' flag, you'd then want to delete this node.</p><p>Note that global flags are shared by all users, and therefore an object is flagged a maximum of 1 times using such flag.</p>",
+      'arguments' => array(
+        'number' => array(
+          'type' => 'number',
+          '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.'),
+        ),
+      ),
+      'module' => 'Flag',
+    ),
+  );
+}
+
+function flag_rules_condition_threshold($number, $settings, $element, &$state) {
+  $flag = flag_get_flag($settings['flag']);
+  $object =& current( rules_get_variables(array($settings['flagee'][$flag->content_type]), $state) );
+
+  $count = $flag->get_count($flag->get_content_id($object));
+
+  switch ($settings['operator']) {
+    case '>' : return $count >  $number;
+    case '>=': return $count >= $number;
+    case '=' : return $count == $number;
+    case '<' : return $count <  $number;
+    case '<=': return $count <= $number;
+  }
+}
+
Index: flag.rules_forms.inc
===================================================================
RCS file: flag.rules_forms.inc
diff -N flag.rules_forms.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ flag.rules_forms.inc	30 Oct 2008 15:18:46 -0000
@@ -0,0 +1,206 @@
+<?php
+// $Id$
+
+/**
+ * @file flag.rules_form.inc
+ * Rules integration for the Flag module.
+ */
+
+// -------------------------------------------------------------------------
+// Actions forms
+
+function flag_rules_action_flag_form($settings, &$form, &$form_state) {
+  unset($form['map']['#type']); // Don't render arguments in a fieldset.
+  _flag_rules_add_flag_selector($form, $settings, t('Select the flag with which to flag the object.'));
+  _flag_rules_add_flagee_selector($form, $settings, $form_state, t('Choose the %type to flag'));
+}
+
+function flag_rules_action_flag_label($settings, $argument_labels, $element) {
+  $flag = flag_get_flag($settings['flag']);
+  $object = $settings['flagee'][$flag->content_type];
+  return t('Flag @object, under "@flag-title"', array('@object' => $object, '@flag-title' => $flag->get_title()));
+}
+
+function flag_rules_action_unflag_form($settings, &$form, &$form_state) {
+  unset($form['map']['#type']); // Don't render arguments in a fieldset.
+  _flag_rules_add_flag_selector($form, $settings, t('Select the flag with which to unflag the object.'));
+  _flag_rules_add_flagee_selector($form, $settings, $form_state, t('Choose the %type to unflag'));
+}
+
+function flag_rules_action_unflag_label($settings, $argument_labels, $element) {
+  $flag = flag_get_flag($settings['flag']);
+  $object = $settings['flagee'][$flag->content_type];
+  return t('Unflag @object, under "@flag-title"', array('@object' => $object, '@flag-title' => $flag->get_title()));
+}
+
+function flag_rules_action_trim_form($settings, &$form, &$form_state) {
+  unset($form['map']['#type']); // Don't render arguments in a fieldset.
+  _flag_rules_add_flag_selector($form, $settings, t('Select the flag to trim.'));
+
+  // Move argument to center of form.
+  $form['settings']['cutoff_size']['#weight'] = 2;
+
+  // Move tokens help to bottom of form.
+  $form['settings']['input_help'] = $form['input_help'];
+  $form['settings']['input_help']['#weight'] = 10;
+  unset($form['input_help']);
+}
+
+function flag_rules_action_trim_label($settings, $argument_labels, $element) {
+  $flag = flag_get_flag($settings['flag']);
+  return t('Trim "@flag-title" at @size', array('@flag-title' => $flag->get_title(), '@size' => $settings['cutoff_size']));
+}
+
+// -------------------------------------------------------------------------
+// Conditions forms
+
+function flag_rules_condition_threshold_form($settings, &$form, &$form_state) {
+  _flag_rules_add_flag_selector($form, $settings, t('An object may be flagged using different flags. Select the flag you wish to test against.'));
+  _flag_rules_add_flagee_selector($form, $settings, $form_state, t('Choose the %type whose flaggings to count'));
+
+  $form['settings']['operator'] = array(
+    '#title' => t('Comparison operator'),
+    '#type' => 'select',
+    '#options' => array(
+      '>'  => t('Greater than'),
+      '>=' => t('Greater than or equal'),
+      '='  => t('Equal to'),
+      '<=' => t('Less than or equal'),
+      '<'  => t('Less than'),
+    ),
+    '#default_value' => isset($settings['operator']) ? $settings['operator'] : '>=',
+  );
+
+  // Move argument to bottom of form.
+  $form['settings']['number']['#weight'] = 2;
+
+  // Move tokens help to bottom of form.
+  $form['settings']['input_help'] = $form['input_help'];
+  $form['settings']['input_help']['#weight'] = 3;
+  unset($form['input_help']);
+  
+}
+
+function flag_rules_condition_threshold_label($settings, $argument_labels, $element) {
+  $flag = flag_get_flag($settings['flag']);
+  $object = $settings['flagee'][$flag->content_type];
+  return t('Flagging count @op @number, for @object, under "@flag-title"', array('@object' => $object, '@flag-title' => $flag->get_title(), '@op' => _flag_op_pretty_print($settings['operator']), '@number' => $settings['number']));
+}
+
+/**
+ * "Pretty prints" an operator. Only because ">=" and such are HTML escaped in
+ * labels. So we use some Unicode characters instead.
+ */
+function _flag_op_pretty_print($op) {
+  $op_table = array(
+    '>'  => "\xe2\x89\xbb", // U+227B ≻
+    '>=' => "\xe2\x89\xa5", // U+2265 ≥
+    '='  => '=',
+    '<=' => "\xe2\x89\xa4", // U+2264 ≤
+    '<'  => "\xe2\x89\xba", // U+227A ≺
+  );
+  return $op_table[$op];
+}
+
+// -------------------------------------------------------------------------
+// Form utilities
+//
+// We define two widgets here: a flag selector, and a flagee selector.
+
+/**
+ * Adds a flag selector to the form.
+ *
+ * Its theming function adds a CSS class, and some JS data, to make it work in tandem
+ * with the the "flagee" selector.
+ */
+function _flag_rules_add_flag_selector(&$form, $settings, $description) {
+  $options = _flag_rules_flags_options();
+  $form['settings']['flag'] = array(
+    '#type' => 'radios',
+    '#options' => $options,
+    '#description' => $description,
+    '#required' => TRUE,
+    '#default_value' => isset($settings['flag']) ? $settings['flag'] : key($options),
+    '#theme' => 'flag_rules_radios',
+  );
+}
+
+function theme_flag_rules_radios($element) {
+  $headers = array(t('Flag:'), t('The flag type'), t('Is the flag global?'));
+  $rows = array();
+  foreach (element_children($element) as $flag_name) {
+    $flag = flag_get_flag($flag_name);
+    $rows[] = array(
+      '<div class="flag-radio">' . drupal_render($element[$flag_name]) . '</div>',
+      $flag->content_type,
+      $flag->global ? t('Yes') : t('No'),
+    );
+    drupal_add_js(array('flag_types' => array($flag->name => $flag->content_type)), 'setting');
+  }
+  return theme('table', $headers, $rows);
+}
+
+/**
+ * Adds a "flagee" selector to the form. This is actually a bunch of selectors,
+ * but JavaScript magic is used to show only the relevant selector: the one
+ * compatible with the flag chosen.
+ */
+function _flag_rules_add_flagee_selector(&$form, $settings, &$form_state, $label) {
+  $variables = $form_state['proxy']->get_available_variables($form_state['element']['#id']);
+  $form['settings']['flagee']['#element_validate'] = array('_flag_rules_flagee_selector_validate');
+  foreach (flag_get_types() as $type) {
+    $options = rules_admin_map_get_possible_arguments(array('type' => $type), $variables);
+    $form['settings']['flagee'][$type] = array(
+      '#type' => 'select',
+      '#attributes' => array('class' => 'flag-flagee flag-flagee-' . $type),
+      '#title' => strtr($label, array('%type' => "<em>$type</em>")),
+      '#options' => $options,
+      '#default_value' => isset($settings['flagee'][$type]) ? $settings['flagee'][$type] : '',
+      '#description' => t('The flag you have selected is used for flagging @type objects. This list shows you all compatible objects available, and you should choose one. If the object you want to operate on isn\'t in the list, you may need to load it first, by using the appropriate action.', array('@type' => $type)),
+    );
+    if (!$options) {
+      $form['settings']['flagee'][$type]['#description'] = '<p class="warning">' . t('Error: The flag you have selected is used for flagging %type objects. However, no arguments of type %type exist from which to choose. You may first need to load the desired object, by using the appropriate action.', array('%type' => $type)) . '</p>';
+    }
+  }
+
+  static $js_added = FALSE;
+  if (!$js_added) {
+    $js =<<<EOS
+$(function() {
+
+  $('.flag-radio :radio').click(function() {
+    var flag_name = $(this).attr('value');
+    var flag_type = Drupal.settings.flag_types[flag_name];
+
+    $('.flag-flagee').parents('.form-item').hide();
+    $('.flag-flagee-' + flag_type).parents('.form-item').show();
+  });
+
+  $('.flag-flagee').parents('.form-item').hide();
+  // Trigger a 'click' on the first checked radio:
+  $('.flag-radio :radio:checked').click();
+});
+EOS;
+    drupal_add_js($js, 'inline');
+    $js_added = TRUE;
+  }
+}
+
+function _flag_rules_flagee_selector_validate($element, &$form_state, $complete_form) {
+  $flag_name = $form_state['values']['settings']['flag'];
+  if (($flag = flag_get_flag($flag_name))) {
+    if (empty($element[$flag->content_type]['#value'])) {
+      form_error($element[$flag->content_type], t('This field is required.'));
+    }
+  }
+}
+
+function _flag_rules_flags_options() {
+  $flags = flag_get_flags();
+  $options = array();
+  foreach ($flags as $flag) {
+    $options[$flag->name] = $flag->get_title();
+  }
+  return $options;
+}
+
