home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / CMS / drupal-6.0.exe / drupal-6.0 / includes / actions.inc next >
Encoding:
Text File  |  2007-12-31  |  12.9 KB  |  366 lines

  1. <?php
  2. // $Id: actions.inc,v 1.8 2007/12/31 14:51:04 goba Exp $
  3.  
  4. /**
  5.  * @file
  6.  * This is the actions engine for executing stored actions.
  7.  */
  8.  
  9. /**
  10.  * Perform a given list of actions by executing their callback functions.
  11.  *
  12.  * Given the IDs of actions to perform, find out what the callbacks
  13.  * for the actions are by querying the database. Then call each callback
  14.  * using the function call $function($object, $context, $a1, $2)
  15.  * where $function is the name of a function written in compliance with
  16.  * the action specification; that is, foo($object, $context).
  17.  *
  18.  * @param $action_ids
  19.  *   The ID of the action to perform. Can be a single action ID or an array
  20.  *   of IDs. IDs of instances will be numeric; IDs of singletons will be
  21.  *   function names.
  22.  * @param $object
  23.  *   Parameter that will be passed along to the callback. Typically the
  24.  *   object that the action will act on; a node, user or comment object.
  25.  *   If the action does not act on an object, pass a dummy object. This
  26.  *   is necessary to support PHP 4 object referencing.
  27.  * @param $context
  28.  *   Parameter that will be passed along to the callback. $context is a
  29.  *   keyed array containing extra information about what is currently
  30.  *   happening at the time of the call. Typically $context['hook'] and
  31.  *   $context['op'] will tell which hook-op combination resulted in this
  32.  *   call to actions_do().
  33.  * @param $a1
  34.  *   Parameter that will be passed along to the callback.
  35.  * @param $a2
  36.  *   Parameter that will be passed along to the callback.
  37.  *
  38.  * @return
  39.  *   An associative array containing the result of the function that
  40.  *   performs the action, keyed on action ID.
  41.  */
  42. function actions_do($action_ids, &$object, $context = NULL, $a1 = NULL, $a2 = NULL) {
  43.   static $stack;
  44.   $stack++;
  45.   if ($stack > variable_get('actions_max_stack', 35)) {
  46.     watchdog('actions', 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.', WATCHDOG_ERROR);
  47.     return;
  48.   }
  49.   $actions = array();
  50.   $available_actions = actions_list();
  51.   $result = array();
  52.   if (is_array($action_ids)) {
  53.     $where = array();
  54.     $where_values = array();
  55.     foreach ($action_ids as $action_id) {
  56.       if (is_numeric($action_id)) {
  57.         $where[] = 'OR aid = %d';
  58.         $where_values[] = $action_id;
  59.       }
  60.       elseif (isset($available_actions[$action_id])) {
  61.         $actions[$action_id] = $available_actions[$action_id];
  62.       }
  63.     }
  64.  
  65.     // When we have action instances we must go to the database to
  66.     // retrieve instance data.
  67.     if ($where) {
  68.       $where_clause = implode(' ', $where);
  69.       // Strip off leading 'OR '.
  70.       $where_clause = '('. strstr($where_clause, " ") .')';
  71.       $result_db = db_query('SELECT * FROM {actions} WHERE '. $where_clause, $where_values);
  72.       while ($action = db_fetch_object($result_db)) {
  73.         $actions[$action->aid] = $action->parameters ? unserialize($action->parameters) : array();
  74.         $actions[$action->aid]['callback'] = $action->callback;
  75.         $actions[$action->aid]['type'] = $action->type;
  76.       }
  77.     }
  78.  
  79.     // Fire actions, in no particular order.
  80.     foreach ($actions as $action_id => $params) {
  81.       if (is_numeric($action_id)) { // Configurable actions need parameters.
  82.         $function = $params['callback'];
  83.         $context = array_merge($context, $params);
  84.         $result[$action_id] = $function($object, $context, $a1, $a2);
  85.       }
  86.       // Singleton action; $action_id is the function name.
  87.       else {
  88.         $result[$action_id] = $action_id($object, $context, $a1, $a2);
  89.       }
  90.     }
  91.   }
  92.   // Optimized execution of single action.
  93.   else {
  94.     // If it's a configurable action, retrieve stored parameters.
  95.     if (is_numeric($action_ids)) {
  96.       $action = db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = %d", $action_ids));
  97.       $function = $action->callback;
  98.       $context = array_merge($context, unserialize($action->parameters));
  99.       $result[$action_ids] = $function($object, $context, $a1, $a2);
  100.     }
  101.     // Singleton action; $action_ids is the function name.
  102.     else {
  103.       $result[$action_ids] = $action_ids($object, $context, $a1, $a2);
  104.     }
  105.   }
  106.   return $result;
  107. }
  108.  
  109.  
  110. /**
  111.  * Discover all action functions by invoking hook_action_info().
  112.  *
  113.  * mymodule_action_info() {
  114.  *   return array(
  115.  *     'mymodule_functiondescription_action' => array(
  116.  *       'type' => 'node',
  117.  *       'description' => t('Save node'),
  118.  *       'configurable' => FALSE,
  119.  *       'hooks' => array(
  120.  *         'nodeapi' => array('delete', 'insert', 'update', 'view'),
  121.  *         'comment' => array('delete', 'insert', 'update', 'view'),
  122.  *       )
  123.  *     )
  124.  *   );
  125.  * }
  126.  *
  127.  * The description is used in presenting possible actions to the user for
  128.  * configuration. The type is used to present these actions in a logical
  129.  * grouping and to denote context. Some types are 'node', 'user', 'comment',
  130.  * and 'system'. If an action is configurable it will provide form,
  131.  * validation and submission functions. The hooks the action supports
  132.  * are declared in the 'hooks' array.
  133.  *
  134.  * @param $reset
  135.  *   Reset the action info static cache.
  136.  *
  137.  * @return
  138.  *   An associative array keyed on function name. The value of each key is
  139.  *   an array containing information about the action, such as type of
  140.  *   action and description of the action, e.g.,
  141.  *
  142.  *   @code
  143.  *   $actions['node_publish_action'] = array(
  144.  *     'type' => 'node',
  145.  *     'description' => t('Publish post'),
  146.  *     'configurable' => FALSE,
  147.  *     'hooks' => array(
  148.  *       'nodeapi' => array('presave', 'insert', 'update', 'view'),
  149.  *       'comment' => array('delete', 'insert', 'update', 'view'),
  150.  *     ),
  151.  *   );
  152.  *   @endcode
  153.  */
  154. function actions_list($reset = FALSE) {
  155.   static $actions;
  156.   if (!isset($actions) || $reset) {
  157.     $actions = module_invoke_all('action_info');
  158.     drupal_alter('action_info', $actions);
  159.   }
  160.  
  161.   // See module_implements for explanations of this cast.
  162.   return (array)$actions;
  163. }
  164.  
  165. /**
  166.  * Retrieve all action instances from the database.
  167.  *
  168.  * Compare with actions_list() which gathers actions by
  169.  * invoking hook_action_info(). The two are synchronized
  170.  * by visiting /admin/build/actions (when actions.module is
  171.  * enabled) which runs actions_synchronize().
  172.  *
  173.  * @return
  174.  *   Associative array keyed by action ID. Each value is
  175.  *   an associative array with keys 'callback', 'description',
  176.  *   'type' and 'configurable'.
  177.  */
  178. function actions_get_all_actions() {
  179.   $actions = array();
  180.   $result = db_query("SELECT * FROM {actions}");
  181.   while ($action = db_fetch_object($result)) {
  182.     $actions[$action->aid] = array(
  183.       'callback' => $action->callback,
  184.       'description' => $action->description,
  185.       'type' => $action->type,
  186.       'configurable' => (bool) $action->parameters,
  187.     );
  188.   }
  189.   return $actions;
  190. }
  191.  
  192. /**
  193.  * Create an associative array keyed by md5 hashes of function names.
  194.  *
  195.  * Hashes are used to prevent actual function names from going out into
  196.  * HTML forms and coming back.
  197.  *
  198.  * @param $actions
  199.  *   An associative array with function names as keys and associative
  200.  *   arrays with keys 'description', 'type', etc. as values. Generally
  201.  *   the output of actions_list() or actions_get_all_actions() is given
  202.  *   as input to this function.
  203.  *
  204.  * @return
  205.  *   An associative array keyed on md5 hash of function name. The value of
  206.  *   each key is an associative array of function, description, and type
  207.  *   for the action.
  208.  */
  209. function actions_actions_map($actions) {
  210.   $actions_map = array();
  211.   foreach ($actions as $callback => $array) {
  212.     $key = md5($callback);
  213.     $actions_map[$key]['callback']     = isset($array['callback']) ? $array['callback'] : $callback;
  214.     $actions_map[$key]['description']  = $array['description'];
  215.     $actions_map[$key]['type']         = $array['type'];
  216.     $actions_map[$key]['configurable'] = $array['configurable'];
  217.   }
  218.   return $actions_map;
  219. }
  220.  
  221. /**
  222.  * Given an md5 hash of a function name, return the function name.
  223.  *
  224.  * Faster than actions_actions_map() when you only need the function name.
  225.  *
  226.  * @param $hash
  227.  *   MD5 hash of a function name
  228.  *
  229.  * @return
  230.  *   Function name
  231.  */
  232. function actions_function_lookup($hash) {
  233.   $actions_list = actions_list();
  234.   foreach ($actions_list as $function => $array) {
  235.     if (md5($function) == $hash) {
  236.       return $function;
  237.     }
  238.   }
  239.  
  240.   // Must be an instance; must check database.
  241.   $aid = db_result(db_query("SELECT aid FROM {actions} WHERE MD5(aid) = '%s' AND parameters != ''", $hash));
  242.   return $aid;
  243. }
  244.  
  245. /**
  246.  * Synchronize actions that are provided by modules.
  247.  *
  248.  * They are synchronized with actions that are stored in the actions table.
  249.  * This is necessary so that actions that do not require configuration can
  250.  * receive action IDs. This is not necessarily the best approach,
  251.  * but it is the most straightforward.
  252.  */
  253. function actions_synchronize($actions_in_code = array(), $delete_orphans = FALSE) {
  254.   if (!$actions_in_code) {
  255.     $actions_in_code = actions_list();
  256.   }
  257.   $actions_in_db = array();
  258.   $result = db_query("SELECT * FROM {actions} WHERE parameters = ''");
  259.   while ($action = db_fetch_object($result)) {
  260.     $actions_in_db[$action->callback] = array('aid' => $action->aid, 'description' => $action->description);
  261.   }
  262.  
  263.   // Go through all the actions provided by modules.
  264.   foreach ($actions_in_code as $callback => $array) {
  265.     // Ignore configurable actions since their instances get put in
  266.     // when the user adds the action.
  267.     if (!$array['configurable']) {
  268.       // If we already have an action ID for this action, no need to assign aid.
  269.       if (array_key_exists($callback, $actions_in_db)) {
  270.         unset($actions_in_db[$callback]);
  271.       }
  272.       else {
  273.         // This is a new singleton that we don't have an aid for; assign one.
  274.         db_query("INSERT INTO {actions} (aid, type, callback, parameters, description) VALUES ('%s', '%s', '%s', '%s', '%s')", $callback, $array['type'], $callback, '', $array['description']);
  275.         watchdog('actions', "Action '%action' added.", array('%action' => filter_xss_admin($array['description'])));
  276.       }
  277.     }
  278.   }
  279.  
  280.   // Any actions that we have left in $actions_in_db are orphaned.
  281.   if ($actions_in_db) {
  282.     $orphaned = array();
  283.     $placeholder = array();
  284.  
  285.     foreach ($actions_in_db as $callback => $array) {
  286.       $orphaned[] = $callback;
  287.       $placeholder[] = "'%s'";
  288.     }
  289.  
  290.     $orphans = implode(', ', $orphaned);
  291.  
  292.     if ($delete_orphans) {
  293.       $placeholders = implode(', ', $placeholder);
  294.       $results = db_query("SELECT a.aid, a.description FROM {actions} a WHERE callback IN ($placeholders)", $orphaned);
  295.       while ($action = db_fetch_object($results)) {
  296.         actions_delete($action->aid);
  297.         watchdog('actions', "Removed orphaned action '%action' from database.", array('%action' => filter_xss_admin($action->description)));
  298.       }
  299.     }
  300.     else {
  301.       $link = l(t('Remove orphaned actions'), 'admin/build/actions/orphan');
  302.       $count = count($actions_in_db);
  303.       watchdog('actions', format_plural($count, 'One orphaned action (%orphans) exists in the actions table. !link', '@count orphaned actions (%orphans) exist in the actions table. !link', array('@count' => $count, '%orphans' => $orphans, '!link' => $link), 'warning'));
  304.     }
  305.   }
  306. }
  307.  
  308. /**
  309.  * Save an action and its associated user-supplied parameter values to the database.
  310.  *
  311.  * @param $function
  312.  *   The name of the function to be called when this action is performed.
  313.  * @param $params
  314.  *   An associative array with parameter names as keys and parameter values
  315.  *   as values.
  316.  * @param $desc
  317.  *   A user-supplied description of this particular action, e.g., 'Send
  318.  *   e-mail to Jim'.
  319.  * @param $aid
  320.  *   The ID of this action. If omitted, a new action is created.
  321.  *
  322.  * @return
  323.  *   The ID of the action.
  324.  */
  325. function actions_save($function, $type, $params, $desc, $aid = NULL) {
  326.   $serialized = serialize($params);
  327.   if ($aid) {
  328.     db_query("UPDATE {actions} SET callback = '%s', type = '%s', parameters = '%s', description = '%s' WHERE aid = %d", $function, $type, $serialized, $desc, $aid);
  329.     watchdog('actions', 'Action %action saved.', array('%action' => $desc));
  330.   }
  331.   else {
  332.     // aid is the callback for singleton actions so we need to keep a
  333.     // separate table for numeric aids.
  334.     db_query('INSERT INTO {actions_aid} VALUES (default)');
  335.     $aid = db_last_insert_id('actions_aid', 'aid');
  336.     db_query("INSERT INTO {actions} (aid, callback, type, parameters, description) VALUES (%d, '%s', '%s', '%s', '%s')", $aid, $function, $type, $serialized, $desc);
  337.     watchdog('actions', 'Action %action created.', array('%action' => $desc));
  338.   }
  339.  
  340.   return $aid;
  341. }
  342.  
  343. /**
  344.  * Retrieve a single action from the database.
  345.  *
  346.  * @param $aid
  347.  *   integer The ID of the action to retrieve.
  348.  *
  349.  * @return
  350.  *   The appropriate action row from the database as an object.
  351.  */
  352. function actions_load($aid) {
  353.   return db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = %d", $aid));
  354. }
  355.  
  356. /**
  357.  * Delete a single action from the database.
  358.  *
  359.  * @param $aid
  360.  *   integer The ID of the action to delete.
  361.  */
  362. function actions_delete($aid) {
  363.   db_query("DELETE FROM {actions} WHERE aid = %d", $aid);
  364.   module_invoke_all('actions_delete', $aid);
  365. }
  366.