source: sipes/cord/includes/actions.inc @ b9d4e2e

stableversion-3.0
Last change on this file since b9d4e2e was d7a822e, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se agrego el directorio del cord

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