source: sipes/modules_contrib/revisioning/revisioning_api.inc @ a8b1f3f

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

se agrego el directorio de modulos contribuidos de drupal

  • Propiedad mode establecida a 100644
File size: 20.3 KB
Línea 
1<?php
2
3/**
4 * @file
5 * API functions of Revisioning module
6 *
7 * Reusable functions that do the dirty work.
8 */
9
10define('REVISION_ARCHIVED', 0);
11define('REVISION_CURRENT', 1);
12define('REVISION_PENDING', 2);
13
14/**
15 * Some naming conventions
16 *
17 * Pending:
18 *   - revision with (vid > current_vid) of ANY node
19 *     OR single revision of UNPUBLISHED node
20 * Current, published:
21 *   - revision with (vid == current_vid) of PUBLISHED node
22 * Archived:
23 *   - all other revisions, i.e.
24 *     revision with (vid < current_vid) of ANY node
25 *     OR revision with (vid == current_vid) of UNPUBLISHED node
26 *
27 * Note: these will change when Revisioning is going to store revision states
28 * independently from vid number (e.g. in different table).
29 */
30
31/**
32 * Return a single or all possible revision state names.
33 *
34 * @param $state
35 *  optional state id, as defined in revisioning_api.inc
36 * @return
37 *  if $state is provided, state name. Otherwise, state names array keyed by state id.
38 */
39function revisioning_revision_states($state = NULL) {
40  static $states;
41  $states = array(
42    REVISION_ARCHIVED => t('Archived'),
43    REVISION_CURRENT  => t('Current, published'),
44    REVISION_PENDING  => t('Pending'),
45  );
46  return $state === NULL ? $states : $states[$state];
47}
48
49/**
50 * Return TRUE when either of the following is true:
51 * o the supplied node has at least one revision more recent than the current
52 * o the node is not yet published and consists of a single revision
53 *
54 * Relies on vid, current_revision_id and num_revisions set on the node object,
55 * see function node_tools_nodeapi()
56 *
57 * @param $node
58 * @return TRUE, if node is pending according to the above definition
59 */
60function _revisioning_node_is_pending($node) {
61  return ($node->vid > $node->current_revision_id) || (!$node->status && $node->num_revisions == 1);
62}
63
64/**
65 * Implementation of hook_revisionapi().
66 *
67 * Act on various revision events.
68 *
69 * @param $op
70 *  Operation
71 * @param $node
72 *  Node of current operation (loaded with vid of the operation).
73 *
74 * "Pre" operations can be useful to get values before they are lost or changed,
75 * for example, to save a backup of revision before it's deleted.
76 * Also, for "pre" operations vetoing mechanics could be implemented, so it
77 * would be possible to veto an operation via hook_revisionapi(). For example,
78 * when the hook is returning FALSE, operation will be vetoed.
79 *
80 * @TODO: Add more operations if needed.
81 */
82function revisioning_revisionapi($op, $node) {
83  switch ($op) {
84    case 'pre revert':
85      // Invoke corresponding Rules event
86      if (module_exists('rules')) {
87        rules_invoke_event('revisioning_pre_revert', $node);
88      }
89      break;
90
91    case 'post revert':
92      // Invoke the revisioning trigger passing 'revert' as the operation
93      if (module_exists('trigger')) {
94        module_invoke_all('revisioning', 'revert', $node, $node->vid);
95      }
96      // Invoke corresponding Rules event
97      if (module_exists('rules')) {
98        rules_invoke_event('revisioning_post_revert', $node);
99      }
100      break;
101
102    case 'pre publish':
103      // Invoke corresponding Rules event
104      if (module_exists('rules')) {
105        rules_invoke_event('revisioning_pre_publish', $node);
106      }
107      break;
108
109    case 'post publish':
110      // Invoke the revisioning trigger passing 'publish' as the operation
111      if (module_exists('trigger')) {
112        module_invoke_all('revisioning', 'publish', $node);
113      }
114      // Invoke corresponding Rules event
115      if (module_exists('rules')) {
116        rules_invoke_event('revisioning_post_publish', $node);
117      }
118      break;
119
120  //case 'pre unpublish':
121      // Not implemented: do we really need it ?
122
123    case 'post unpublish':
124      // Invoke the revisioning trigger passing 'unpublish' as the operation
125      if (module_exists('trigger')) {
126        module_invoke_all('revisioning', 'unpublish', $node);
127      }
128      // Invoke corresponding Rules event
129      if (module_exists('rules')) {
130        rules_invoke_event('revisioning_post_unpublish', $node);
131      }
132      break;
133
134    case 'pre delete':
135      // Invoke corresponding Rules event
136      if (module_exists('rules')) {
137        rules_invoke_event('revisioning_pre_delete', $node);
138      }
139      break;
140
141    case 'post delete':
142      break;
143  }
144}
145
146/**
147 * Get the id of the latest revision belonging to a node.
148 * @param
149 *  $nid, id of the node
150 * @return
151 *  ID of the latest revision.
152 */
153function revisioning_get_latest_revision_id($nid) {
154  return db_result(db_query('SELECT MAX(vid) FROM {node_revisions} WHERE nid=%d', $nid));
155}
156
157/**
158 * Get the id of the user who last edited the supplied node, ie. the author
159 * of the latest revision.
160 * This is irrespective of whether this latest revision is pending or not,
161 * unless TRUE is specified for the second argument, in which case the uid
162 * of the creator of the current revision (published or not) is returned.
163 *
164 * @param $nid
165 *  The id of the node whose most recent editor id is to be returned.
166 * @param $current
167 *  Whether the uid of the current or very latest revision should be returned.
168 * @return
169 *  A single number being the user id (uid).
170 */
171function revisioning_get_last_editor($nid, $current = FALSE) {
172  $sql = ($current)
173    ? "SELECT vid FROM {node} WHERE nid = %d"
174    : "SELECT MAX(vid) FROM {node_revisions} WHERE nid = %d";
175  $vid = db_result(db_query($sql, $nid));
176  return db_result(db_query("SELECT uid FROM {node_revisions} WHERE vid = %d", $vid));
177}
178
179/**
180 * Revert node to selected revision without changing its publication status.
181 *
182 * @param $node
183 *  Target $node object (loaded with target revision) or nid of target node
184 * @param $vid
185 *  Optional vid of revision to revert to, if provided $node must not be an object.
186 */
187function _revisioning_revertpublish_revision(&$node, $vid = NULL) {
188  $node_revision = is_object($node) ? $node : node_load($node, $vid);
189  $return = module_invoke_all('revisionapi', 'pre revert', $node_revision);
190  if (in_array(FALSE, $return)) {
191    drupal_goto('node/'. $node_revision->nid .'/revisions/'. $node_revision->vid .'/view');
192    die;
193  }
194  _revisioning_revert_revision($node_revision);
195  module_invoke_all('revisionapi', 'post revert', $node_revision);
196}
197
198/**
199 * Revert node to selected revision without publishing it.
200 *
201 * This is same as node_revision_revert_confirm_submit() in node_pages.inc,
202 * except it doesn't put any messages on screen.
203 *
204 * @param $node
205 *  Target $node object (loaded with target revision) or nid of target node
206 * @param $vid
207 *  optional vid of revision to revert to, if provided $node is not an object.
208 */
209function _revisioning_revert_revision(&$node, $vid = NULL) {
210  $node_revision = is_object($node) ? $node : node_load($node, $vid);
211  $node_revision->revision = 1;
212  $node_revision->log = t('Copy of the revision from %date.', array('%date' => format_date($node_revision->revision_timestamp)));
213  if (module_exists('taxonomy')) {
214    $node_revision->taxonomy = array_keys($node_revision->taxonomy);
215  }
216  node_save($node_revision);
217  watchdog('content', '@type: reverted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid));
218}
219
220/**
221 * Publish node, without calling node_save().
222 * @obsolete
223 *  This function is no longer used. Use _revisioning_publish_revision().
224 *
225 * @param $node
226 *  Target $node object or nid of target node
227 * @param $clear_cache
228 *   Whether to clear the cache afterwards or not. Clearing the cache on every
229 *   node during bulk operations can be time-consuming.
230 *
231function _revisioning_publish_node($node, $clear_cache = TRUE) {
232  if (is_numeric($node)) {
233    $node = node_load($node);
234  }
235  db_query("UPDATE {node} SET status=1 WHERE nid=%d", $node->nid);
236  // Let other modules know there was an update on the node, just like
237  // node_save() does.
238  $node->status = 1;
239  node_invoke_nodeapi($node, 'update');
240  // Update the node access table for this node.
241  node_access_acquire_grants($node);
242  if ($clear_cache) {
243    cache_clear_all();
244  }
245}
246 */
247
248/**
249 * Unpublish node, without calling node_save().
250 *
251 * @param $node
252 *  Target $node object or nid.
253 * @param $clear_cache
254 *   Whether to clear the cache afterwards or not. Clearing the cache on every
255 *   node during bulk operations can be time-consuming.
256 */
257function _revisioning_unpublish_node($node, $clear_cache = TRUE) {
258  if (is_numeric($node)) {
259    $node = node_load($node);
260  }
261  db_query("UPDATE {node} SET status=0 WHERE nid=%d", $node->nid);
262  // Let other modules know there was an update on the node, just like
263  // node_save() does.
264  $node->status = 0;
265  $node->bypass_nodeapi = TRUE; // avoid revisioning_nodeapi() doing stuff
266  $node->pathauto_perform_alias = FALSE; // avoid pathauto_nodeapi() doing stuff
267  node_invoke_nodeapi($node, 'update');
268  // Update the node access table for this node.
269  node_access_acquire_grants($node);
270
271  if ($clear_cache) {
272    cache_clear_all();
273  }
274}
275
276/**
277 * Delete selected revision of node, provided it's not current.
278 *
279 * This is same as node_revision_delete_confirm_submit() in node_pages.inc,
280 * except it doesn't put any messages on the screen. This way it becomes
281 * reusable (eg. in actions).
282 * Since we are calling nodeapi as in node_revision_delete_confirm_submit(), we
283 * invoke our "post delete" revisionapi hook in nodeapi. This way revisionapi
284 * hooks work the same way both with "delete revision" submit handler and when
285 * this function is called, and we don't invoke revisionapi "post delete" hook
286 * twice.
287 *
288 * @param $node
289 *  Target $node object (loaded with target revision) or nid of target node
290 * @param $vid
291 *  optional vid of revision to delete, if provided $node is not object.
292 *
293 * @TODO: Insert check to prevent deletion of current revision of node.
294 */
295function _revisioning_delete_revision(&$node, $vid = NULL) {
296  $node_revision = is_object($node) ? $node : node_load($node, $vid);
297  module_invoke_all('revisionapi', 'pre delete', $node_revision);
298  db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $node_revision->nid, $node_revision->vid);
299  db_query("DELETE FROM {term_node} WHERE nid = %d AND vid = %d", $node_revision->nid, $node_revision->vid);
300  node_invoke_nodeapi($node_revision, 'delete revision');
301  watchdog('content', '@type: deleted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid));
302}
303
304/**
305 * Unpublish revision (i.e. the node).
306 *
307 * Note that no check is made as to whether the initiating user has permission
308 * to unpublish this node.
309 *
310 * @param $node
311 *  Target $node object or nid of target node
312 */
313function _revisioning_unpublish_revision(&$node) {
314  $node_revision = is_object($node) ? $node : node_load($node);
315  module_invoke_all('revisionapi', 'pre unpublish', $node_revision);
316  _revisioning_unpublish_node($node_revision);
317  watchdog('content', 'Unpublished @type %title', array('@type' => $node_revision->type, '%title' => $node_revision->title), WATCHDOG_NOTICE, l(t('view'), "node/$node_revision->nid"));
318  module_invoke_all('revisionapi', 'post unpublish', $node_revision);
319}
320
321/**
322 * Make the supplied revision of the node current and publish it.
323 * It is the caller's responsibility to provide proper revision.
324 * Note that no check is made as to whether the initiating user has permission
325 * to publish this revision.
326 *
327 * @param $node
328 *  Target $node object (loaded with target revision) or nid of target node
329 * @param $vid
330 *  optional vid of revision to make current, if provided $node is not object.
331 * @param $clear_cache
332 *   Whether to clear the cache afterwards or not. Clearing the cache on every
333 *   node during bulk operations can be time-consuming.
334 */
335function _revisioning_publish_revision(&$node, $vid = NULL, $clear_cache = TRUE) {
336  $node_revision = is_object($node) ? $node : node_load($node, $vid);
337  $return = module_invoke_all('revisionapi', 'pre publish', $node_revision);
338  if (in_array(FALSE, $return)) {
339    drupal_goto('node/'. $node_revision->nid .'/revisions/'. $node_revision->vid .'/view');
340    die;
341  }
342  // Update node table, making sure the "published" (ie. status) flag is set
343  db_query("UPDATE {node} SET vid=%d, title='%s', status=1 WHERE nid=%d", $node_revision->vid, $node_revision->title, $node_revision->nid);
344  if ($clear_cache) {
345    cache_clear_all();
346  }
347  $node_revision->status = 1;
348  $node_revision->bypass_nodeapi = TRUE; // avoid revisioning_nodeapi() doing stuff
349  $node_revision->nodewords = FALSE; // avoid nodewords_nodeapi() doing stuff
350  // On the first save of a node we should allow pathauto_nodeapi() to create the
351  // alias as normal. Otherwise, the alias will not be created if a node is set
352  // to be immediately published during creation. See [#1635542].
353  if (empty($node->is_new)) {
354    $node_revision->pathauto_perform_alias = FALSE; // avoid pathauto_nodeapi() doing stuff
355  }
356
357  node_invoke_nodeapi($node_revision, 'update');
358  // Update the node access table for this node.
359  node_access_acquire_grants($node_revision);
360
361  watchdog('content', 'Published rev #%revision of @type %title', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid), WATCHDOG_NOTICE, l(t('view'), "node/$node_revision->nid/revisions/$node_revision->vid/view"));
362  module_invoke_all('revisionapi', 'post publish', $node_revision);
363}
364
365/**
366 * Find the most recent pending revision, make it current, unless it already is
367 * and publish node.
368 * Note that no check is made as to whether the initiating user has permission
369 * to publish this node.
370 *
371 * @param $node
372 *   The node object whose latest pending revision is to be published
373 * @return
374 *   TRUE if operation was successful, FALSE if there is no pending revision to
375 *   publish
376 */
377function _revisioning_publish_latest_revision(&$node) {
378  // Get latest pending revision or take the current provided it's UNpublished
379  $latest_pending = array_shift(_revisioning_get_pending_revisions($node->nid));
380  if (!$latest_pending) {
381    if (!$node->status && $node->is_current) {
382      _revisioning_publish_revision($node);
383      return TRUE;
384    }
385  }
386  else {
387    _revisioning_publish_revision($node->nid, $latest_pending->vid);
388    return TRUE;
389  }
390  return FALSE;
391}
392
393/**
394 * Return a count of the number of revisions newer than the supplied vid.
395 *
396 * @param $vid
397 *  The reference vid.
398 * @param $nid
399 *  The id of the node.
400 * @return
401 *  integer
402 */
403function _revisioning_get_number_of_revisions_newer_than($vid, $nid) {
404  return db_result(db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {node_revisions} r ON n.nid=r.nid WHERE (r.vid>%d AND n.nid=%d)", $vid, $nid));
405}
406
407/**
408 * Return a count of the number of revisions newer than the current revision.
409 *
410 * @param $nid
411 *  The id of the node.
412 * @return
413 *  integer
414 */
415function _revisioning_get_number_of_pending_revisions($nid) {
416  return db_result(db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {node_revisions} r ON n.nid=r.nid WHERE (r.vid>n.vid AND n.nid=%d)", $nid));
417}
418
419/**
420 * Get the number of archived revisions belonging to a node.
421 * @param
422 *  $nid, id of the node
423 * @return
424 *  A count representing the number of archived revisions for the node
425 *  Returns zero if there is only one (i.e. the current) revision.
426 */
427function revisioning_get_number_of_archived_revisions($node) {
428  return db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d AND vid < %d', $node->nid, $node->current_revision_id));
429}
430
431/**
432 * Delete all revisions with a vid less than the current.
433 */
434function revisioning_delete_archived_revisions($node) {
435  db_query('DELETE FROM {term_node} WHERE nid = %d AND vid < %d', $node->nid, $node->current_revision_id);
436  return db_query('DELETE FROM {node_revisions} WHERE nid = %d AND vid < %d', $node->nid, $node->current_revision_id);
437}
438
439/**
440 * Retrieve a list of revisions with a vid greater than the current.
441 *
442 * @param $nid
443 *  The node id to retrieve.
444 * @return
445 *  An array of revisions (latest first), each containing vid, title and
446 *  content type.
447 */
448function _revisioning_get_pending_revisions($nid) {
449  $sql = "SELECT r.vid, r.title, n.type FROM {node} n INNER JOIN {node_revisions} r ON n.nid=r.nid WHERE (r.vid>n.vid AND n.nid=%d) ORDER BY r.vid DESC";
450  $result = db_query($sql, $nid);
451  $revisions = array();
452  while ($revision = db_fetch_object($result)) {
453    $revisions[$revision->vid] = $revision;
454  }
455  return $revisions;
456}
457
458/**
459 * Retrieve a list of all revisions (archive, current, pending) belonging to
460 * the supplied node.
461 *
462 * @param $nid
463 *  The node id to retrieve.
464 * @param $include_taxonomy_terms
465 *  Whether to also retrieve the taxonomy terms for each revision
466 * @return
467 *  An array of revision objects, each with published flag, log message, vid,
468 *  title, timestamp and name of user that created the revision
469 */
470function _revisioning_get_all_revisions_for_node($nid, $include_taxonomy_terms = FALSE) {
471  $sql_select = 'SELECT n.type, n.status, r.vid, r.title, r.log, r.uid, r.timestamp, u.name';
472  $sql_from   = ' FROM {node_revisions} r LEFT JOIN {node} n ON n.vid=r.vid INNER JOIN {users} u ON u.uid=r.uid';
473  $sql_where  = ' WHERE r.nid=%d ORDER BY r.vid DESC';
474  if ($include_taxonomy_terms) {
475    $sql_select .= ', td.name AS term';
476    $sql_from .= ' LEFT JOIN {term_node} tn ON r.vid=tn.vid LEFT JOIN {term_data} td ON tn.tid=td.tid';
477    $sql_where .= ', term ASC';
478  }
479  $sql = $sql_select . $sql_from . $sql_where;
480  $result = db_query($sql, $nid);
481  $revisions = array();
482  while ($revision = db_fetch_object($result)) {
483    if (empty($revisions[$revision->vid])) {
484      $revisions[$revision->vid] = $revision;
485    }
486    elseif ($include_taxonomy_terms) {
487      // If a revision has more than one taxonomy term, these will be returned
488      // by the query as seperate objects differing only in their term fields.
489      $existing_revision = $revisions[$revision->vid];
490      $existing_revision->term .= '/'. $revision->term;
491    }
492  }
493  return $revisions;
494}
495
496/**
497 * Return revision type of the supplied node.
498 *
499 * @param &$node
500 *  Node object to check
501 * @return
502 *  Revision type
503 */
504function _revisioning_revision_is(&$node) {
505  if ($node->is_pending) {
506    return REVISION_PENDING;
507  }
508  return ($node->is_current && $node->status) ? REVISION_CURRENT : REVISION_ARCHIVED;
509}
510
511/**
512 * Return a string with details about the node that is about to be displayed.
513 *
514 * Called from revisioning_nodeapi().
515 *
516 * @param $node
517 *  The node that is about to be viewed
518 * @return
519 *  A translatable message containing details about the node
520 */
521function _revisioning_node_info_msg($node) {
522  // Get username for the revision, not the creator of the node
523  $revision_author = user_load($node->revision_uid);
524  $placeholder_data = array(
525    '@content_type' => $node->type,
526    '%title' => $node->title,
527    '!author' => theme('username', $revision_author),
528    '@date' => format_date($node->revision_timestamp, 'small'),
529  );
530  $revision_type = _revisioning_revision_is($node);
531  switch ($revision_type) {
532    case REVISION_PENDING:
533      return t('Displaying <em>pending</em> revision of @content_type %title, last modified by !author on @date', $placeholder_data);
534
535    case REVISION_CURRENT:
536      return t('Displaying <em>current, published</em> revision of @content_type %title, last modified by !author on @date', $placeholder_data);
537
538    case REVISION_ARCHIVED:
539      return t('Displaying <em>archived</em> revision of @content_type %title, last modified by !author on @date', $placeholder_data);
540  }
541}
542
543/**
544 * Return TRUE only if the user account has ALL of the supplied permissions.
545 *
546 * @param $permissions
547 *  An array of permissions (strings)
548 * @param $account
549 *  The user account object. Defaults to the current user if omitted.
550 * @return bool
551 */
552function revisioning_user_all_access($permissions, $account = NULL) {
553  foreach ($permissions as $permission) {
554    if (!user_access($permission, $account)) {
555      return FALSE;
556    }
557  }
558  return TRUE;
559}
560
561/**
562 * Return an array of names of content types that are subject to moderation.
563 *
564 * @return array of strings, may be empty
565 */
566function revisioning_moderated_content_types() {
567  $moderated_content_types = array();
568  foreach (node_get_types() as $type) {
569    $content_type = check_plain($type->type);
570    if (node_tools_content_is_moderated($content_type)) {
571      $moderated_content_types[] = $content_type;
572    }
573  }
574  return $moderated_content_types;
575}
576
577/**
578 * Return the id of the user who created the revision by the supplied vid.
579 */
580function revisioning_get_revision_uid($vid) {
581  return db_result(db_query('SELECT uid FROM {node_revisions} WHERE vid = %d', $vid));
582}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.