### Eclipse Workspace Patch 1.0
#P drupal5
Index: sites/all/modules/flag/flag.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.module,v
retrieving revision 1.1.2.48
diff -u -r1.1.2.48 flag.module
--- sites/all/modules/flag/flag.module	5 Sep 2008 15:35:57 -0000	1.1.2.48
+++ sites/all/modules/flag/flag.module	5 Sep 2008 17:38:53 -0000
@@ -53,27 +53,10 @@
     );
   }
 
-  if (_flag_have_actions_version_2()) {
-    $items[] = array(
-      'path' => 'admin/build/flags/actions',
-      'title' => t('Actions'),
-      'callback' => 'flag_actions_page',
-      'access' => user_access('administer actions'),
-      'type' => MENU_LOCAL_TASK,
-    );
-  }
-
   return $items;
 }
 
 /**
- * We only support Actions 2.x. Not Actions 1.x. 
- */
-function _flag_have_actions_version_2() {
-  return module_exists('actions') && function_exists('_actions_get_hook_aids');
-}
-
-/**
  * Implementation of hook_init().
  */
 function flag_init() {
@@ -716,12 +699,12 @@
  * Implementation of hook_flag(). Trigger actions if any are available.
  */
 function flag_flag($action, $flag, $content_id, $account) {
-  if (_flag_have_actions_version_2()) {
+  if (module_exists('actions') && function_exists('_actions_get_hook_aids')) {
 
     $flag_action = $flag->get_flag_action($content_id);
-    $flag_action['action'] = $action;
-    $context = $flag_action;
-    $aids = _actions_get_hook_aids('flag', $action);
+    $flag_action->action = $action;
+    $context = (array)$flag_action;
+    $aids = _actions_get_hook_aids($action, $action);
     foreach ($aids as $aid => $action_info) {
       // The 'if ($aid)' is a safeguard against http://drupal.org/node/271460#comment-886564
       if ($aid) {
Index: sites/all/modules/flag/flag.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.inc,v
retrieving revision 1.1.4.11
diff -u -r1.1.4.11 flag.inc
--- sites/all/modules/flag/flag.inc	5 Sep 2008 14:57:22 -0000	1.1.4.11
+++ sites/all/modules/flag/flag.inc	5 Sep 2008 17:38:52 -0000
@@ -446,13 +446,27 @@
    * slots.
    */
   function get_flag_action($content_id) {
-    $flag_action['flag'] = $this->name;
-    $flag_action['content_type'] = $this->content_type;
-    $flag_action['content_id'] = $content_id;
+    $flag_action = new stdClass();
+    $flag_action->flag = $this->name;
+    $flag_action->content_type = $this->content_type;
+    $flag_action->content_id = $content_id;
     return $flag_action;
   }
 
   /**
+   * Returns an array of all actions that are executable with this flag.
+   */
+  function get_valid_actions() {
+    $actions = module_invoke_all('action_info');
+    foreach ($actions as $callback => $action) {
+      if ($action['type'] != $this->content_type && !isset($action['hooks'][$this->content_type])) {
+        unset($actions[$callback]);
+      }
+    }
+    return $actions;
+  }
+
+  /**
    * @defgroup views Views 2 integration
    * @{
    * Methods that can be overridden to support the Views module.
@@ -647,11 +661,21 @@
   function get_flag_action($content_id) {
     $flag_action = parent::get_flag_action($content_id);
     $node = $this->load_content($content_id);
-    $flag_action['content_title'] = $node->title;
-    $flag_action['content_url'] = _flag_url('node/' . $node->nid);
+    $flag_action->content_title = $node->title;
+    $flag_action->content_url = _flag_url('node/' . $node->nid);
     return $flag_action;
   }
 
+  function get_valid_actions() {
+    $actions = module_invoke_all('action_info');
+    foreach ($actions as $callback => $action) {
+      if ($action['type'] != 'node' && !isset($action['hooks']['nodeapi'])) {
+        unset($actions[$callback]);
+      }
+    }
+    return $actions;
+  }
+
   function get_views_info() {
     return array(
       'views table' => 'node',
@@ -737,8 +761,8 @@
   function get_flag_action($content_id) {
     $flag_action = parent::get_flag_action($content_id);
     $comment = $this->load_content($content_id);
-    $flag_action['content_title'] = $comment->subject;
-    $flag_action['content_url'] = _flag_url("node/$comment->nid/$comment->cid", "comment-$comment->cid");
+    $flag_action->content_title = $comment->subject;
+    $flag_action->content_url = _flag_url("node/$comment->nid/$comment->cid", "comment-$comment->cid");
     return $flag_action;
   }
 
@@ -830,8 +854,8 @@
   function get_flag_action($content_id) {
     $flag_action = parent::get_flag_action($content_id);
     $user = $this->load_content($content_id);
-    $flag_action['content_title'] = $user->name;
-    $flag_action['content_url'] = _flag_url('user/' . $user->uid);
+    $flag_action->content_title = $user->name;
+    $flag_action->content_url = _flag_url('user/' . $user->uid);
     return $flag_action;
   }
 
Index: sites/all/modules/flag/flag.actions.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.actions.inc,v
retrieving revision 1.1.2.8
diff -u -r1.1.2.8 flag.actions.inc
--- sites/all/modules/flag/flag.actions.inc	3 Sep 2008 20:59:24 -0000	1.1.2.8
+++ sites/all/modules/flag/flag.actions.inc	5 Sep 2008 17:38:52 -0000
@@ -16,457 +16,3 @@
     ),
   );
 }
-
-/**
- * Implementation of hook_action_info().
- */
-function flag_action_info() {
-  $actions = array(
-    'flag_action_email' => array(
-      'type' => 'flag',
-      'description' => t('Send e-mail after flagging threshold'),
-      'configurable' => TRUE,
-      'hooks' => array(
-        'flag' => array('flag'),
-      ),
-    ),
-    'flag_action_unpublish' => array(
-      'type' => 'flag',
-      'description' => t('Unpublish after flagging threshold'),
-      'configurable' => TRUE,
-      'hooks' => array(
-        'flag' => array('flag'),
-      ),
-    ),
-    'flag_action_delete' => array(
-      'type' => 'flag',
-      'description' => t('Delete after flagging threshold'),
-      'configurable' => TRUE,
-      'hooks' => array(
-        'flag' => array('flag', 'unflag'),
-      ),
-    ),
-  );
-
-  if (module_exists('modr8')) {
-    $actions += array(
-      'flag_action_moderate' => array(
-        'type' => 'node',
-        'description' => t('Moderate after flagging threshold'),
-        'configurable' => TRUE,
-        'hooks' => array(
-          'flag' => array('flag', 'unflag'),
-        ),
-      ),
-    );
-  }
-
-  return $actions;
-}
-
-/**
- * Menu callback for admin/build/flags/actions.
- */
-function flag_actions_page() {
-  $result = db_query("SELECT aid FROM {actions} WHERE type = 'flag'");
-
-  $enabled_actions = _actions_get_hook_aids('flag', 'flag');
-
-  $actions = array();
-  while ($row = db_fetch_object($result)) {
-    $action = actions_load($row->aid);
-    $action->parameters = unserialize($action->parameters);
-    $action->enabled = array_key_exists($row->aid, $enabled_actions);
-    $actions[] = $action;
-  }
-
-  $add_action_form = drupal_get_form('flag_action_add_form');
-
-  return theme('flag_actions_page', $actions, $add_action_form);
-}
-
-/**
- * Theme the list of actions currently in place for flags.
- */
-function theme_flag_actions_page($actions, $add_action_form) {
-  $rows = array();
-  foreach ($actions as $action) {
-    $row = array();
-    $row[] = $action->description;
-    $row[] = $action->parameters['threshold'];
-    $row[] = implode(', ', array_filter($action->parameters['flags']));
-    $row[] = $action->enabled ? t('enabled') : t('disabled');
-    $row[] = l(t('edit'), 'admin/settings/actions/configure/'. $action->aid, array(), drupal_get_destination());
-    $row[] = l(t('delete'), 'admin/settings/actions/delete/'. $action->aid, array(), drupal_get_destination());
-    $rows[] = $row;
-  }
-
-  if (empty($rows)) {
-    $rows[] = array(array('data' => t('Currently no flag actions. Use the <em>Add new flag action</em> form to add an action.'), 'colspan' => 6));
-  }
-
-  $header = array(
-    t('Description'),
-    t('Threshold'),
-    t('Flags'),
-    t('Enabled'),
-    array('data' => t('Operations'), 'colspan' => 2),
-  );
-
-  $output = '';
-  $output .= theme('table', $header, $rows);
-  $output .= $add_action_form;
-  return $output;
-}
-
-/**
- * Check if a flag action should be executed.
- */
-function flag_action_check($context) {
-  // Flagging operations only occur on the 'flag' operation.
-  if ($context['action'] != 'flag') {
-    return FALSE;
-  }
-
-  // Check if this action applies to this flag.
-  if (in_array($context['flag'], $context['flags'], TRUE)) {
-    // Check if the flag is over the threshold.
-    $counts = flag_get_counts($context['content_type'], $context['content_id']);
-    if ($counts[$context['flag']] == $context['threshold']) {
-      return TRUE;
-    }
-  }
-  return FALSE;
-}
-
-/**
- * Modified version of the Add action form that redirects back to the flag list.
- */
-function flag_action_add_form() {
-  $all_actions = actions_list();
-  $options = array(t('Choose an advanced action'));
-  foreach ($all_actions as $aid => $action) {
-    if ($action['type'] == 'flag') {
-      $options[md5($aid)] = $action['description'] .'...';
-    }
-  }
-  $form = actions_manage_form($options);
-  $form['parent']['#title'] = t('Add new flag action');
-  return $form;
-}
-
-function flag_action_add_form_submit($form_id, $form_values) {
-  if ($form_values['action']) {
-    return array('admin/settings/actions/configure/'. $form_values['action'], drupal_get_destination());
-  }
-}
-
-/**
- * Generic configuration form for configuration of flag actions.
- */
-function flag_action_form($context) {
-  $flags = flag_get_flags();
-
-  $options = array();
-  foreach ($flags as $flag) {
-    $options[$flag->name] = $flag->get_title();
-  }
-  $form['flags'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Flags'),
-    '#options' => $options,
-    '#required' => TRUE,
-    '#default_value' => isset($context['flags']) ? $context['flags'] : array(),
-    '#weight' => -10,
-  );
-  $form['threshold'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Flagging threshold'),
-    '#size' => 6,
-    '#maxlength' => 6,
-    '#default_value' => isset($context['threshold']) ? $context['threshold'] : 10,
-    '#required' => TRUE,
-    '#weight' => -9,
-  );
-
-  $form['advanced'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Advanced'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#tree' => FALSE,
-    '#weight' => -1,
-  );
-
-  $form['advanced']['actions_description'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Action description'),
-    '#default_value' => $context['actions_description'],
-    '#maxlength' => '255',
-    '#description' => t('A unique description for this action.'),
-    '#weight' => 1,
-  );
-
-  $form['advanced']['add_flag_trigger'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Include this action in the "Flag" trigger'),
-    '#default_value' => isset($context['add_flag_trigger']) ? $context['add_flag_trigger'] : 1,
-    '#description' => t('This checkbox can be used to easily add or remove this action from the "Flag" trigger.'),
-    '#weight' => 1,
-  );
-  $form['actions_description'] = array();
-
-  $form['#submit'] = array('actions_configure_submit' => array(), 'flag_action_form_submit' => array());
-
-  return $form;
-}
-
-/**
- * Generic submit handler for saving flag actions.
- */
-function flag_action_form_submit($form_id, $form_values) {
-  // TODO: Find a better way of finding the last added action.
-  if (!isset($form_values['actions_aid'])) {
-    $form_values['actions_aid'] = db_result(db_query("SELECT MAX(aid) as aid FROM {actions_aid}"));
-  }
-
-  // Build a set of values to pass to the trigger assign/unassign forms.
-  $trigger_values = array(
-    'operation' => 'flag',
-    'hook' => 'flag',
-    'confirm' => 1,
-    'aid' => md5($form_values['actions_aid']), // Wow.
-  );
-
-  // Check the status of the trigger before adding or removing an action.
-  $aids = _actions_get_hook_aids('flag', 'flag');
-  if ($form_values['add_flag_trigger'] && (!isset($form_values['actions_aid']) || !array_key_exists($form_values['actions_aid'], $aids))) {
-    actions_assign_form_submit('actions_assign_form', $trigger_values);
-  }
-  elseif (!$form_values['add_flag_trigger'] && array_key_exists($form_values['actions_aid'], $aids)) {
-    actions_unassign_submit('actions_unassign_form', $trigger_values);
-  }
-}
-
-function flag_action_email_form($context) {
-  $form = flag_action_form($context);
-  
-  if (!module_exists('token')) {
-    form_set_error('', t('You don\'t have the <a href="http://drupal.org/project/token">Token</a> module installed. Tokens are little pieces of information that can be embedded in text, including emails, and are mandatory for using this action. Unless you install the <a href="http://drupal.org/project/token">Token</a> module, <strong>you won\'t be able to save this form.</strong>'));
-  }
-
-  $form['flags']['#title'] = t('Send when any of the following flags are over the threshold');
-  $form['threshold']['#field_prefix'] = t('Send an e-mail after content has been flagged');
-  $form['threshold']['#field_suffix'] = t('times');
-
-  $form['recipients'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Recipients'),
-    '#default_value' => isset($context['recipients']) ? $context['recipients'] : '[site-mail]',
-    '#description' => t('Comma sepereated list of email addresses to which the message should be sent.'),
-    '#weight' => -7,
-  );
-  $form['subject'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Subject'),
-    '#default_value' => isset($context['subject']) ? $context['subject'] : '[site-name] Content Flagged [flag-title]',
-    '#description' => t('The subject of the message.'),
-    '#weight' => -6,
-  );
-  $form['message'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Message'),
-    '#default_value' => isset($context['message']) ? $context['message'] : t("The [flag-content-type] [flag-content-title] has been flagged [flag-count] times with the [flag-title] flag.\n\nView this [flag-content-type] at [flag-content-url]."),
-    '#description' => t('The message that should be sent.'),
-    '#weight' => -5,
-  );
-  if (module_exists('token')) {
-    $form['tokens'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Tokens'),
-      '#description' => t('The following tokens can be used in the recipients, subject, or message.'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-      '#weight' => -4,
-    );
-    $form['tokens']['basic'] = array(
-      '#value' => theme('flag_token_help', array('flag', 'flag-action')),
-    );
-    $valid_token_types = array();
-    foreach (flag_fetch_definition() as $type => $info) {
-      $flag = flag_flag::factory_by_content_type($type);
-      $valid_token_types = array_merge($valid_token_types, $flag->get_labels_token_types());
-    }
-    $valid_token_types = array_unique($valid_token_types);
-    $form['tokens']['more'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('More tokens'),
-      '#description' => t("Depending on the type of the content being flagged, the following tokens can be used in the recipients, subject, or message. For example, if the content being flagged is a node, you can use any of the node tokens --but you can't use the comment tokens: they won't be recognized. Similarly, if the content being flagged is a user, you can use only the user tokens."),
-      '#value' => theme('flag_token_help', $valid_token_types),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
-    );
-  }
-  return $form; 
-}
-
-function flag_action_email_validate($form_id, $form_values) {
-  // Validate e-mail addresses.
-  $errors = array();
-  $email_tokens = array('[site-mail]');
-  $email_tokens_regexp = '/^\[\S*mail\S*\]$/';
-  $recipients = explode(',', $form_values['recipients']);
-
-  // Validate each recipient.
-  foreach($recipients as $recipient) {
-    $recipient = trim($recipient);
-    if (!valid_email_address($recipient) && !preg_match($email_tokens_regexp, $recipient)) {
-      $errors['recipient'] = t('Please enter a valid email address or email token.');
-    }
-  }
-  foreach ($errors as $name => $message) {
-    form_set_error($name, $message);
-  }
-  return count($errors) == 0;
-}
-
-function flag_action_email_submit($form_id, $form_values) {
-  return array(
-    'flags' => array_filter($form_values['flags']),
-    'threshold' => $form_values['threshold'],
-    'add_flag_trigger' => $form_values['add_flag_trigger'],
-    'recipients' => $form_values['recipients'],
-    'subject' => $form_values['subject'],
-    'message' => $form_values['message'],
-  );
-}
-
-/**
- * Action to send email upon flagging.
- */
-function flag_action_email($flag, $context) {
-  // Check if this action needs to be executed.
-  if (!flag_action_check($context)) {
-    return;
-  }
-
-  $subject     = $context['subject'];
-  $message     = $context['message'];
-  $recipients  = $context['recipients'];
-  $flag_action = (object)$context;
-
-  if (module_exists('token')) {
-    $token_contexts = array('flag' => $flag, 'flag-action' => $flag_action);
-    $subject    = $flag->replace_tokens($subject, $token_contexts, $flag_action->content_id);
-    $message    = $flag->replace_tokens($message, $token_contexts, $flag_action->content_id);
-    $recipients = $flag->replace_tokens($recipients, $token_contexts, $flag_action->content_id);
-  }
-
-  // Remove empty recipients.
-  $recipients = implode(', ', array_filter(explode(',', $recipients)));
-
-  // Ensure there are no new-lines in the subject.
-  $subject = str_replace(array("\r", "\n"), '', $subject);
-
-  if (drupal_mail('flag_over_threshold', $recipients, $subject, $message)) {
-    watchdog('action', t('Sent email to %recipient', array('%recipient' => $recipients)));
-  }
-  else {
-    watchdog('error', t('Unable to send email to %recipient', array('%recipient' => $recipients)));
-  }
-}
-
-function flag_action_unpublish_form($context) {
-  $form = flag_action_form($context);
-
-  $form['flags']['#title'] = t('Unpublish content when any of the following flags are over the threshold');
-  $form['threshold']['#field_prefix'] = t('Unpublish content that has been flagged');
-  $form['threshold']['#field_suffix'] = t('times');
-
-  return $form;
-}
-
-function flag_action_unpublish_submit($form_id, $form_values) {
-  return array(
-    'flags' => array_filter($form_values['flags']),
-    'threshold' => $form_values['threshold'],
-    'add_flag_trigger' => $form_values['add_flag_trigger'],
-  );
-}
-
-/**
- * Action to unpublish content after flagging.
- */
-function flag_action_unpublish($flag, $context) {
-  // Check if this action needs to be executed.
-  if (!flag_action_check($context)) {
-    return;
-  }
-
-  switch ($context['content_type']) {
-    case 'node':
-      $node = node_load($context['content_id']);
-      $node->status = 0;
-      node_save($node);
-      break;
-    case 'user':
-      $account = user_load(array('uid' => $context['content_id']));
-      user_save($account, array('status' => 0));
-      break;
-    case 'comment':
-      // Workaround to disable Drupal's message "Your comment has been queued for moderation ..."
-      $messages = drupal_get_messages();
-      $comment = _comment_load($context['content_id']);
-      $comment->status = COMMENT_NOT_PUBLISHED;
-      comment_save((array)$comment);
-      // Write back the old messages.
-      $_SESSION['messages'] = $messages;
-      break;
-  }
-}
-
-
-function flag_action_delete_form($context) {
-  $form = flag_action_form($context);
-
-  $form['flags']['#title'] = t('Delete content when any of the following flags are over the threshold');
-  $form['threshold']['#field_prefix'] = t('Delete content that has been flagged');
-  $form['threshold']['#field_suffix'] = t('times');
-  $form['threshold']['#default_value'] = isset($context['threshold']) ? $context['threshold'] : 20;
-
-  return $form;
-}
-
-function flag_action_delete_submit($form_id, $form_values) {
-  return array(
-    'flags' => array_filter($form_values['flags']),
-    'threshold' => $form_values['threshold'],
-    'add_flag_trigger' => $form_values['add_flag_trigger'],
-  );
-}
-
-/**
- * Action to delete content after flagging.
- */
-function flag_action_delete($flag, $context) {
-  // Check if this action needs to be executed.
-  if (!flag_action_check($context)) {
-    return;
-  }
-
-  switch ($context['content_type']) {
-    case 'node':
-      node_delete($context['content_id']);
-      break;
-    case 'user':
-      $account = user_load(array('uid' => $context['content_id']));
-      user_delete((array)$account, $context['content_id']);
-      break;
-    case 'comment':
-      $comment = _comment_load($context['content_id']);
-      _comment_delete_thread($comment);
-      _comment_update_node_statistics($comment->nid);
-      cache_clear_all();
-      break;
-  }
-}
Index: sites/all/modules/flag/flag_actions.info
===================================================================
RCS file: sites/all/modules/flag/flag_actions.info
diff -N sites/all/modules/flag/flag_actions.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sites/all/modules/flag/flag_actions.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+; $Id$
+name = Flag actions
+description = Execute actions on Flag events.
+core = 6.x
+dependencies = flag actions
Index: sites/all/modules/flag/flag_actions.install
===================================================================
RCS file: sites/all/modules/flag/flag_actions.install
diff -N sites/all/modules/flag/flag_actions.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sites/all/modules/flag/flag_actions.install	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,40 @@
+<?php
+// $Id$
+
+/**
+ * Implementation of hook_install().
+ */
+function flag_actions_install() {
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      db_query("CREATE TABLE {flag_actions} (
+        aid int(11) NOT NULL auto_increment,
+        fid smallint(6) default NULL,
+        event varchar(255) default NULL,
+        threshold smallint(6) default NULL,
+        callback varchar(255) NOT NULL default '',
+        parameters longtext NOT NULL,
+        PRIMARY KEY (aid)
+      ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
+      break;
+    case 'mysqli':
+      db_query("CREATE TABLE {flag_actions} (
+        aid integer NOT NULL auto_increment,
+        fid smallint default NULL,
+        event varchar(255) default NULL,
+        threshold smallint default NULL,
+        callback varchar(255) NOT NULL default '',
+        parameters text NOT NULL,
+        PRIMARY KEY (aid)
+      )");
+      break;
+  }
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function flag_actions_uninstall() {
+  db_query("DROP TABLE {flag_actions}");
+}
Index: sites/all/modules/flag/flag_actions.module
===================================================================
RCS file: sites/all/modules/flag/flag_actions.module
diff -N sites/all/modules/flag/flag_actions.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sites/all/modules/flag/flag_actions.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,503 @@
+<?php
+// $Id$
+
+/**
+ * Implementation of hook_flag(). Trigger actions if any are available.
+ */
+function flag_actions_flag($event, $flag, $content_id, $account) {
+  flag_actions_do($event, $flag, $content_id, $account);
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function flag_actions_menu($may_cache) {
+  $items = array();
+
+  if ($may_cache) {
+    $items[] = array(
+      'path' => 'admin/build/flags/actions',
+      'title' => t('Actions'),
+      'callback' => 'flag_actions_page',
+      'access' => user_access('administer actions'),
+      'type' => MENU_LOCAL_TASK,
+    );
+    $items[] = array(
+      'path' => 'admin/build/flags/actions/add',
+      'title' => t('Add action'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('flag_actions_form', NULL),
+      'access' => user_access('administer actions'),
+      'type' => MENU_CALLBACK,
+    );
+    $items[] = array(
+      'path' => 'admin/build/flags/actions/delete',
+      'title' => t('Delete action'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('flag_actions_delete_form'),
+      'access' => user_access('administer actions'),
+      'type' => MENU_CALLBACK,
+    );
+    $items[] = array(
+      'path' => 'admin/build/flags/actions/configure',
+      'title' => t('Edit action'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('flag_actions_form'),
+      'access callback' => user_access('administer actions'),
+      'type' => MENU_CALLBACK,
+    );
+  }
+
+  return $items;
+}
+
+function flag_actions_get_action($aid) {
+  $actions = flag_actions_get_actions();
+  return $actions[$aid];
+}
+
+function flag_actions_get_actions($flag_name = NULL, $reset = FALSE) {
+  static $flag_actions;
+
+  // Get a list of all possible actions defined by modules.
+  $actions = module_invoke_all('action_info');
+
+  // Retrieve the list of user-defined flag actions.
+  if (!isset($flag_actions) || $reset) {
+    $flag_actions = array();
+    $result = db_query("SELECT a.*, f.name as flag, f.content_type as type FROM {flag_actions} a INNER JOIN flags f ON a.fid = f.fid");
+    while ($action = db_fetch_object($result)) {
+      $action->parameters = unserialize($action->parameters);
+      $action->description = $actions[$action->callback]['description'];
+      $action->configurable = $actions[$action->callback]['configurable'];
+      $action->behavior = isset($actions[$action->callback]['behavior']) ? $actions[$action->callback]['behavior'] : array();
+
+      $flag_actions[$action->aid] = $action;
+    }
+  }
+
+  // Filter actions to a specified flag.
+  if (isset($flag_name)) {
+    foreach ($flag_actions as $aid => $action) {
+      if ($action->flag != $flag_name) {
+        unset($flag_actions[$aid]);
+      }
+    }
+  }
+
+  return $flag_actions;
+}
+
+/**
+ * Insert a new flag action.
+ *
+ * @param $fid
+ *   The flag object ID.
+ * @param $threshold
+ *   The flagging threshold at which this action will be executed.
+ * @param $callback
+ *   The action callback to be executed.
+ * @param $parameters
+ *   The action parameters.
+ */
+function flag_actions_insert_action($fid, $event, $threshold, $callback, $parameters) {
+  $aid = db_next_id('flag_actions_{aid}');
+  db_query('INSERT INTO {flag_actions} (aid, fid, event, threshold, callback, parameters) VALUES (%d, %d, "%s", %d, "%s", "%s")', $aid, $fid, $event, $threshold, $callback, serialize($parameters));
+  return $aid;
+}
+
+/**
+ * Update an existing flag action.
+ *
+ * @param $aid
+ *   The flag action ID to update.
+ * @param $threshold
+ *   The flagging threshold at which this action will be executed.
+ * @param $parameters
+ *   The action parameters.
+ */
+function flag_actions_update_action($aid, $event, $threshold, $parameters) {
+  return db_query('UPDATE {flag_actions} SET event = "%s", threshold = %d, parameters = "%s" WHERE aid = %d', $event, $threshold, serialize($parameters), $aid);
+}
+
+/**
+ * Delete a flag action.
+ *
+ * @param $aid
+ *   The flag action ID to delete.
+ */
+function flag_actions_delete_action($aid) {
+  return db_query('DELETE FROM {flag_actions} WHERE aid = %d', $aid);
+}
+
+/**
+ * Perform flag actions.
+ */
+function flag_actions_do($event, $flag, $content_id, $account) {
+  $actions = flag_actions_get_actions($flag->name);
+
+  $counts = flag_get_counts($flag->content_type, $content_id);
+  $count = $counts[$flag->name];
+  $flag_action = $flag->get_flag_action($content_id);
+  $object = $flag->load_content($content_id);
+  $node_changed = FALSE;
+  foreach ($actions as $aid => $action) {
+    if (($action->event == $event && $action->event == 'flag' && $count == $action->threshold) || ($action->event == $event && $action->event == 'unflag' && $count == $action->threshold - 1)) {
+      $context = $action->parameters;
+      $context['callback'] = $action->callback;
+      $context['type'] = $action->type;
+      $context['account'] = $account;
+      $context['flag'] = $flag;
+      $context['flag-action'] = $flag_action;
+      $context[$action->type] = &$object;
+      $callback = $action->callback;
+      $callback($object, $context);
+
+      if (is_array($action->behavior) && in_array('changes_node_property', $action->behavior)) {
+        $node_changed = TRUE;
+      }
+    }
+  }
+  if ($node_changed) {
+    node_save_action($object);
+  }
+}
+
+/**
+ * Menu callback for admin/build/flags/actions.
+ */
+function flag_actions_page() {
+  $actions = flag_actions_get_actions();
+  $add_action_form = drupal_get_form('flag_actions_add_form');
+
+  return theme('flag_actions_page', $actions, $add_action_form);
+}
+
+/**
+ * Theme the list of actions currently in place for flags.
+ */
+function theme_flag_actions_page($actions, $add_action_form) {
+  $rows = array();
+  foreach ($actions as $action) {
+    $flag = flag_get_flag($action->flag);
+
+    $row = array();
+    $row[] = $flag->get_title();
+    $row[] = ($action->event == 'flag' ? '≥ ' : '< ') . $action->threshold;
+    $row[] = $action->description;
+    $row[] = l(t('edit'), 'admin/build/flags/actions/configure/'. $action->aid);
+    $row[] = l(t('delete'), 'admin/build/flags/actions/delete/'. $action->aid);
+    $rows[] = $row;
+  }
+
+  if (empty($rows)) {
+    $rows[] = array(array('data' => t('Currently no flag actions. Use the <em>Add new flag action</em> form to add an action.'), 'colspan' => 6));
+  }
+
+  $header = array(
+    t('Flag'),
+    t('Threshold'),
+    t('Action'),
+    array('data' => t('Operations'), 'colspan' => 2),
+  );
+
+  $output = '';
+  $output .= theme('table', $header, $rows);
+  $output .= $add_action_form;
+  return $output;
+}
+
+/**
+ * Modified version of the Add action form that redirects back to the flag list.
+ */
+function flag_actions_add_form() {
+  $flags = flag_get_flags();
+  $options = array();
+  foreach ($flags as $flag) {
+    $options[$flag->name] = $flag->get_title();
+  }
+
+  if (empty($options)) {
+    $options[] = t('No flag available');
+  }
+
+  $form = array();
+
+  $form['flag'] = array(
+    '#type' => 'select',
+    '#options' => empty($options) ? array(t('No flag available')) : $options,
+    '#disabled' => empty($options),
+    '#title' => t('Select a flag'),
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add action'),
+  );
+
+  return $form;
+}
+
+function flag_actions_add_form_submit($form_id, $form_values) {
+  if ($form_values['flag']) {
+    return 'admin/build/flags/actions/add/'. $form_values['flag'];
+  }
+}
+
+function theme_flag_actions_add_form($form) {
+  $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>',
+  );
+
+  return drupal_render($fieldset) . drupal_render($form);
+}
+
+/**
+ * Generic configuration form for configuration of flag actions.
+ *
+ * @param $aid
+ *   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.
+ * @param $form_values
+ *   The current form values of a submission, used in multistep forms.
+ *
+ */
+function flag_actions_form($aid = NULL, $flag_name = NULL, $form_values = NULL) {
+  // This is a multistep form. Get the callback value if set and continue.
+  if (isset($form_values['callback'])) {
+    $callback = $form_values['callback'];
+  }
+
+  if (isset($aid)) {
+    $action = flag_actions_get_action($aid);
+    $callback = $action->callback;
+    $flag = flag_get_flag($action->flag);
+    drupal_set_title(t('Edit the "@action" action for the @title flag', array('@action' => $action->description, '@title' => $flag->get_title())));
+  }
+  elseif (isset($flag_name)) {
+    $flag = flag_get_flag($flag_name);
+  }
+
+  if (empty($flag)) {
+    drupal_not_found();
+  }
+
+  $form = array();
+  $form['#multistep'] = TRUE;
+  if (isset($_POST['op']) && $_POST['op'] == t('Continue')) {
+    $form['#redirect'] = FALSE;
+  }
+
+  $form['new'] = array(
+    '#type' => 'value',
+    '#value' => isset($callback) ? FALSE: TRUE,
+  );
+
+  if (!isset($callback)) {
+    drupal_set_title(t('Add an action to the @title flag', array('@title' => $flag->get_title())));
+
+    $actions = $flag->get_valid_actions();
+    $options = array();
+    foreach($actions as $key => $action) {
+      $options[$key] = $action['description'];
+    }
+
+    $form['callback'] = array(
+      '#title' => t('Select an action'),
+      '#type' => 'select',
+      '#options' => $options,
+    );
+
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Continue'),
+    );
+
+    return $form;
+  }
+  elseif (!isset($action)) {
+    $actions = $flag->get_valid_actions();
+    $action = (object)$actions[$callback];
+    $action->event = 'flag';
+    $action->threshold = 10;
+    drupal_set_title(t('Add "@action" action to the @title flag', array('@action' => $action->description, '@title' => $flag->get_title())));
+  }
+
+  $form['flag'] = array(
+    '#tree' => TRUE,
+    '#weight' => -9,
+    '#theme' => 'flag_actions_flag_form',
+    '#action' => $action,
+  );
+
+  $form['flag']['flag'] = array(
+    '#type' => 'value',
+    '#value' => $flag,
+  );
+
+  $form['flag']['callback'] = array(
+    '#type' => 'value',
+    '#value' => $callback,
+  );
+
+  $form['flag']['aid'] = array(
+    '#type' => 'value',
+    '#value' => $aid,
+  );
+
+  $form['flag']['event'] = array(
+    '#type' => 'select',
+    '#options' => array(
+      'flag' => t('reaches'),
+      'unflag' => t('falls below'),
+    ),
+    '#default_value' => $action->event,
+  );
+
+  $form['flag']['threshold'] = array(
+    '#type' => 'textfield',
+    '#size' => 6,
+    '#maxlength' => 6,
+    '#default_value' => $action->threshold,
+    '#required' => TRUE,
+  );
+
+  $action_form = $callback .'_form';
+  if (function_exists($action_form)) {
+    $edit = $action->parameters;
+    $edit['actions_description'] = $action->description;
+    $edit['actions_type'] = $action->type;
+    $form = array_merge($form, $action_form($edit));
+  }
+
+  $flag_actions_form = 'flag_actions_'. $callback .'_form';
+  if (function_exists($flag_actions_form)) {
+    $flag_actions_form($form, $flag, $edit);
+  }
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+
+  return $form;
+}
+
+/**
+ * Generic submit handler for saving flag actions.
+ */
+function flag_actions_form_submit($form_id, $form_values) {
+  // This is a multistep form. If selecting an action, do not yet process.
+  if ($_POST['op'] == t('Continue')) {
+    return;
+  }
+
+  $aid        = $form_values['flag']['aid'];
+  $flag       = $form_values['flag']['flag'];
+  $event      = $form_values['flag']['event'];
+  $threshold  = $form_values['flag']['threshold'];
+  $callback   = $form_values['flag']['callback'];
+
+  $parameters = $form_values;
+  unset($parameters['flag']);
+
+  if (empty($aid)) {
+    $aid = flag_actions_insert_action($flag->fid, $event, $threshold, $callback, $parameters);
+    $form_values['flag']['aid'] = $aid;
+    $form_values['flag']['is_new'] = TRUE;
+  }
+  else {
+    flag_actions_update_action($aid, $event, $threshold, $parameters);
+  }
+
+  $action = flag_actions_get_action($aid);
+
+  drupal_set_message(t('The "@action" action for the @title flag has been saved.', array('@action' => $action->description, '@title' => $flag->get_title())));
+  return 'admin/build/flags/actions';
+}
+
+function theme_flag_actions_flag_form($form) {
+  $event = drupal_render($form['event']);
+  $threshold = drupal_render($form['threshold']);
+  $action = $form['#action']->description;
+
+  $output  = '';
+  $output .= '<div class="container-inline">';
+  $output .= t('Perform action when content !event !threshold flags', array('!event' => $event, '!threshold' => $threshold));
+  $output .= '</div>';
+
+  $element = array(
+    '#title' => t('Flagging threshold'),
+    '#description' => t('Set the event for which this action should be executed.'),
+    '#required' => TRUE,
+  );
+
+  $output = theme('form_element', $element, $output);
+
+  return $output . drupal_render($form);
+}
+
+function flag_actions_delete_form($aid) {
+  $action = flag_actions_get_action($aid);
+  $flag = flag_get_flag($action->flag);
+
+  $form = array();
+
+  $form['action'] = array(
+    '#type' => 'value',
+    '#value' => $action,
+  );
+
+  $form['flag'] = array(
+    '#type' => 'value',
+    '#value' => $flag,
+  );
+
+  $question = t('Delete the "@action" action for the @title flag?', array('@action' => $action->description, '@title' => $flag->get_title()));
+  $path = 'admin/build/flags/actions';
+
+  return confirm_form($form, $question, $path, NULL, t('Delete'));
+}
+
+function flag_actions_delete_form_submit($form_id, $form_values) {
+  flag_actions_delete_action($form_values['action']->aid);
+  drupal_set_message(t('The "@action" action for the @title flag has been deleted.', array('@action' => $form_values['action']->description, '@title' => $form_values['flag']->get_title())));
+  return 'admin/build/flags/actions';
+}
+
+function flag_actions_token_actions_send_email_action_form(&$form, &$flag, $context) {
+  if (!isset($context['recipient'])) {
+    $form['recipient']['#default_value'] = '[site-mail]';
+  }
+
+  if (!isset($context['subject'])) {
+    $form['subject']['#default_value'] = t('Content Flagged @flag_title', array('@flag_title' => $flag->get_title()));
+  }
+
+  if (!isset($context['message'])) {
+    $form['message']['#default_value'] = t("The @flag_content_type [flag-content-title] has been flagged [flag-count] times with the @flag_title flag.\n\nView this @flag_content_type at [flag-content-url].", array('@flag_content_type' => $flag->content_type, '@flag_title' => $flag->get_title()));
+  }
+
+  $form['help'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Tokens'),
+    '#description' => t('The following tokens can be used in the recipient, subject, or message.'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['help']['basic'] = array(
+    '#value' => theme('flag_token_help', array('flag', 'flag-action')),
+  );
+
+  $form['help']['tokens'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('More tokens'),
+    '#description' => t("Depending on the type of the content being flagged, the following tokens can be used in the recipients, subject, or message. For example, if the content being flagged is a node, you can use any of the node tokens --but you can't use the comment tokens: they won't be recognized. Similarly, if the content being flagged is a user, you can use only the user tokens."),
+    '#value' => theme('flag_token_help', $flag->get_labels_token_types()),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+}
