source: sipes/modules_contrib/revisioning/revisioning.module @ 3514c84

stableversion-3.0
Last change on this file since 3514c84 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 100755
File size: 38.1 KB
Línea 
1<?php
2
3/**
4 * @file
5 * Allows the creation and modification of pre-published as well as live
6 * content while the current revision remains unchanged and publicly visible
7 * until the changes have been reviewed by a moderator.
8 */
9
10define('REVISIONING_LOAD_CURRENT', 0); // node/%node/view, node/%node/edit opens current revision
11define('REVISIONING_LOAD_LATEST',  1); // node/%node/view, node/%node/edit opens latest revison
12
13define('NEW_REVISION_WHEN_NOT_PENDING', 0);
14define('NEW_REVISION_EVERY_SAVE', 1);
15
16define('REVISIONS_BLOCK_OLDEST_AT_TOP', 0);
17define('REVISIONS_BLOCK_NEWEST_AT_TOP', 1);
18
19require_once drupal_get_path('module', 'revisioning') .'/revisioning_api.inc';
20require_once drupal_get_path('module', 'revisioning') .'/revisioning.pages.inc';
21require_once drupal_get_path('module', 'revisioning') .'/revisioning_theme.inc';
22require_once drupal_get_path('module', 'revisioning') .'/revisioning_tokens.inc';
23require_once drupal_get_path('module', 'revisioning') .'/revisioning_triggers_actions.inc';
24// No need to include Rules integration file - Rules module does it for us automatically.
25
26/**
27 * Implementation of hook_help().
28 */
29function revisioning_help($path, $arg) {
30  switch ($path) {
31    case 'admin/help#revisioning':
32      $s = t('For documentation and tutorials see the <a href="@revisioning">Revisioning project page</a>',
33        array('@revisioning' => url('http://drupal.org/project/revisioning')));
34      break;
35    case 'node/%/revisions':
36      $s = t('To edit, publish or delete one of the revisions below, click on its saved date.');
37      break;
38    case 'admin/build/trigger/revisioning':
39      $s = t("Below you can assign actions to run when certain publication-related events happen. For example, you could send an e-mail to an author when their content is pubished.");
40      break;
41    case 'accessible-content/i-created/pending':
42      $s = t('Showing all <em>pending</em> content <em>you created</em> and still have at least view access to.');
43      break;
44    case 'accessible-content/i-last-modified/pending':
45      $s = t('Showing all <em>pending</em> content <em>you last modified</em> and still have at least view access to.');
46      break;
47    case 'accessible-content/i-can-edit/pending':
48      $s = t('Showing all <em>pending</em> content you can <em>edit</em>.');
49      break;
50    case 'accessible-content/i-can-view/pending':
51      $s = t('Showing all <em>pending</em> content you have at least <em>view</em> access to.');
52      break;
53  }
54  return empty($s) ? '' : '<p>'. $s .'</p>';
55}
56
57/**
58 * Implementation of hook_perm().
59 *
60 * Revisioning permissions. Note that permissions to view, revert and delete
61 * revisions already exist in node.module.
62 */
63function revisioning_perm() {
64  $perms = module_exists('module_grants_monitor') ? array('access Pending tab') : array();
65  $perms = array_merge($perms, array('view revision status messages',
66    'edit revisions', 'publish revisions', 'unpublish current revision'));
67  // Add per node-type view perms in same way as edit perms of node module.
68  // TOOD only do this for content types that have the "Create new revision" ticked
69  foreach (node_get_types() as $type) {
70    $name = check_plain($type->type);
71    $perms[] = 'view revisions of own '. $name .' content';
72    $perms[] = 'view revisions of any '. $name .' content';
73    $perms[] = 'publish revisions of own '. $name .' content';
74    $perms[] = 'publish revisions of any '. $name .' content';
75  }
76  return $perms;
77}
78
79/**
80 * Implementation of hook_menu().
81 *
82 * Define new menu items.
83 * Existing menu items are modified through hook_menu_alter().
84 */
85function revisioning_menu() {
86  $items = array();
87
88  if (module_exists('module_grants_monitor')) {
89    // Add a tab to the 'I created' tab (defined in module_grants_monitor.module)
90    $items['accessible-content/i-created/pending'] = array(
91      'title' => 'In draft/Pending publication',
92      'page callback' => '_revisioning_show_pending_nodes',
93      'page arguments' => array('view', I_CREATED),
94      'access callback' => 'revisioning_user_all_access',
95      'access arguments' => array(array('access I Created tab', 'access Pending tab')),
96      'type' => MENU_LOCAL_TASK,
97      'weight' => -1
98    );
99    // Add a tab to the 'I last modified' tab
100    $items['accessible-content/i-last-modified/pending'] = array(
101      'title' => 'In draft/Pending publication',
102      'page callback' => '_revisioning_show_pending_nodes',
103      'page arguments' => array('view', I_LAST_MODIFIED),
104      'access callback' => 'revisioning_user_all_access',
105      'access arguments' => array(array('access I Last Modified tab', 'access Pending tab')),
106      'type' => MENU_LOCAL_TASK,
107      'weight' => -1
108    );
109    // Add a tab to the 'I can edit' tab
110    $items['accessible-content/i-can-edit/pending'] = array(
111      'title' => 'In draft/Pending publication',
112      'page callback' => '_revisioning_show_pending_nodes',
113      'page arguments' => array('update'),
114      'access callback' => 'revisioning_user_all_access',
115      'access arguments' => array(array('access I Can Edit tab', 'access Pending tab')),
116      'type' => MENU_LOCAL_TASK,
117      'weight' => -1
118    );
119    // Add a tab to the 'I can view' tab (defined in module_grants.module)
120    $items['accessible-content/i-can-view/pending'] = array(
121      'title' => 'In draft/Pending publication',
122      'page callback' => '_revisioning_show_pending_nodes',
123      'page arguments' => array('view'),
124      'access callback' => 'revisioning_user_all_access',
125      'access arguments' => array(array('access I Can View tab', 'access Pending tab')),
126      'type' => MENU_LOCAL_TASK,
127      'weight' => -1
128    );
129  }
130
131  // Callback (not a tab) to allow users to unpublish a node.
132  // Note that is a node operation more so than a revision operation, but
133  // we let _revisioning_node_revision_access() handle access anyway.
134  $items['node/%node/unpublish'] = array(
135  //'title' => t(Unpublish current revision'),
136    'page callback' => 'drupal_get_form',
137    'page arguments' => array('revisioning_unpublish_confirm', 1),
138    'access callback' => '_revisioning_node_revision_access', // _revisioning_node_access ?
139    'access arguments' => array('unpublish current revision', 1),
140    'type' => MENU_CALLBACK,
141  );
142
143  // Revision tab local subtasks (i.e. secondary tabs), up to 7 of them:
144  //  view, edit, publish, unpublish, revert, delete and compare.
145  // All revision operations 'node/%node/revisions/%vid/<op>' are defined as
146  // local tasks (tabs) secondary to the primary 'node/%node/revisions' local
147  // task (tab).
148  // The tricky part is to always set "tab_parent", core does NOT figure this
149  // out based on the URL. %vid is optional, see vid_to_arg().
150  // Note: the MENU_DEFAULT_LOCAL_TASK for 'node/%node/revisions' is defined in
151  //       function revisioning_menu_alter()
152
153  // View revision local subtask
154  $items['node/%node/revisions/%vid/view'] = array(
155    'title' => 'View',
156    'load arguments' => array(3),
157    'page callback' => '_revisioning_view_revision',
158    'page arguments' => array(1),
159    'access callback' => '_revisioning_node_revision_access',
160    'access arguments' => array('view revisions', 1),
161    'type' => MENU_LOCAL_TASK,
162    'weight' => -10,
163    'tab_parent' => 'node/%/revisions',
164  );
165  // Edit revision local subtask
166  $items['node/%node/revisions/%vid/edit'] = array(
167    'title' => 'Edit',
168    'load arguments' => array(3),
169    'page callback' => '_revisioning_edit_revision',
170    'page arguments' => array(1),
171    'access callback' => '_revisioning_node_revision_access',
172    'access arguments' => array('edit revisions', 1),
173    'file' => 'node.pages.inc',
174    'file path' => drupal_get_path('module', 'node'),
175    'type' => MENU_LOCAL_TASK,
176    'weight' => -7,
177    'tab_parent' => 'node/%/revisions',
178  );
179  // Publish revision local subtask
180  $items['node/%node/revisions/%vid/publish'] = array(
181    'title' => 'Publish this',
182    'load arguments' => array(3),
183    'page callback' => 'drupal_get_form',
184    'page arguments' => array('revisioning_publish_confirm', 1),
185    'access callback' => '_revisioning_node_revision_access',
186    'access arguments' => array('publish revisions', 1),
187    'type' => MENU_LOCAL_TASK,
188    'weight' => -4,
189    'tab_parent' => 'node/%/revisions',
190  );
191  // Unpublish node local subtask
192  $items['node/%node/revisions/%vid/unpublish'] = array(
193    'title' => 'Unpublish this',
194    'load arguments' => array(3),
195    'page callback' => 'drupal_get_form',
196    'page arguments' => array('revisioning_unpublish_confirm', 1),
197    'access callback' => '_revisioning_node_revision_access',
198    'access arguments' => array('unpublish current revision', 1),
199    'type' => MENU_LOCAL_TASK,
200    'weight' => -3,
201    'tab_parent' => 'node/%/revisions',
202  );
203  // Revert to revision local subtask.
204  // Difference from core version is %vid that's served by vid_to_arg() function.
205  $items['node/%node/revisions/%vid/revert'] = array(
206    'title' => 'Revert to this',
207    'load arguments' => array(3),
208    'page callback' => 'drupal_get_form',
209    'page arguments' => array('node_revision_revert_confirm', 1),
210    'access callback' => '_revisioning_node_revision_access',
211    'access arguments' => array('revert revisions', 1),
212    'file' => 'node.pages.inc',
213    'file path' => drupal_get_path('module', 'node'),
214    'type' => MENU_LOCAL_TASK,
215    'weight' => -2,
216    'tab_parent' => 'node/%/revisions',
217  );
218  // Delete revision local subtask.
219  // Difference from core version is %vid that's served by vid_to_arg() function.
220  $items['node/%node/revisions/%vid/delete'] = array(
221    'title' => 'Delete',
222    'load arguments' => array(3),
223    'page callback' => 'drupal_get_form',
224    'page arguments' => array('node_revision_delete_confirm', 1),
225    'access callback' => '_revisioning_node_revision_access',
226    'access arguments' => array('delete revisions', 1),
227    'file' => 'node.pages.inc',
228    'file path' => drupal_get_path('module', 'node'),
229    'type' => MENU_LOCAL_TASK,
230    'weight' => 10,
231    'tab_parent' => 'node/%/revisions',
232  );
233
234  // If Diff module is enabled, provide a compare local subtask
235  if (module_exists('diff')) {
236    $items['node/%node/revisions/%vid/compare'] = array(
237      'title' => 'Compare to current',
238      'load arguments' => array(3),
239      'page callback' => '_revisioning_compare_to_current_revision',
240      'page arguments' => array(1),
241      'access callback' => '_revisioning_node_revision_access',
242      'access arguments' => array('compare to current', 1),
243      'type' => MENU_LOCAL_TASK,
244      'weight' => 0,
245      'tab_parent' => 'node/%/revisions',
246    );
247  }
248
249  // Finally, the Revisioning configuration menu item
250  $items['admin/settings/revisioning'] = array(
251    'title' => 'Revisioning',
252    'description' => 'Configure how links to view and edit content behave.',
253    'page callback' => 'drupal_get_form',
254    'page arguments' => array('revisioning_admin_configure'),
255    'access arguments' => array('administer site configuration'),
256    'file' => 'revisioning.admin.inc'
257  );
258
259  return $items;
260}
261
262/**
263 * Implementation of hook_menu_alter().
264 *
265 * Modify menu items defined in other modules (in particular the Node and
266 * Module Grants modules).
267 */
268function revisioning_menu_alter(&$items) {
269
270  // Primary tabs for 'node/%node': View tab, Edit tab, Revisions tab ...
271
272  // View tab can be either 'View current' or 'View latest'.
273  // It should be suppressed when the 'Revisions' tab shows the same revision,
274  // so we need a special access callback for this, which expands on the
275  // callback defined in Module Grants.
276  $items['node/%node']['access callback'] = $items['node/%node/view']['access callback'] = '_revisioning_view_edit_access_callback';
277  $items['node/%node']['access arguments']= $items['node/%node/view']['access arguments']= array('view', 1);
278  $items['node/%node']['page callback']   = $items['node/%node/view']['page callback']   = '_revisioning_view';
279  $items['node/%node']['page arguments']  = $items['node/%node/view']['page arguments']  = array(1);
280  // Not applying title callback to 'node/%node', see #782316
281  $items['node/%node/view']['title callback']  = '_revisioning_title_for_tab';
282  $items['node/%node/view']['title arguments'] = array(1, FALSE);
283
284  // Edit tab can be either 'Edit current' or 'Edit latest'.
285  // It should be suppressed when the 'Revisions' tab shows the same revision,
286  // so we need a special access callback for this, which expands on the
287  // callback defined in Module Grants.
288  $items['node/%node/edit']['access callback'] = '_revisioning_view_edit_access_callback';
289  $items['node/%node/edit']['access arguments']= array('edit', 1);
290  $items['node/%node/edit']['page callback']   = '_revisioning_edit';
291  $items['node/%node/edit']['title callback']  = '_revisioning_title_for_tab';
292  $items['node/%node/edit']['title arguments'] = array(1, TRUE);
293
294  // 'Revisions' tab remains but points to new page callback, allowing users to
295  // pick the revision to view, edit, publish, revert, unpublish, delete.
296  // Need to override _node_revision_access() call back as it disallows access
297  // to the 'Revisions' tab when there's only one revision, which will prevent
298  // users from getting to the publish/unpublish links.
299  $items['node/%node/revisions']['access callback'] = '_revisioning_node_revision_access';
300  $items['node/%node/revisions']['access arguments'] = array('view revision list', 1);
301  $items['node/%node/revisions']['page callback'] = '_revisioning_present_node';
302  $items['node/%node/revisions']['page arguments'] = array(1);
303
304  // Unset old menu items defined in node.module (or module_grants.module), as
305  // these are replaced by ones that use the %vid wildcard instead of % and
306  // come with the appropriate callbacks.
307  unset($items['node/%node/revisions/%/view']);
308  unset($items['node/%node/revisions/%/revert']);
309  unset($items['node/%node/revisions/%/delete']);
310
311  if (module_exists('diff')) {
312    // If Diff module is enabled, make sure it uses correct access callback
313    $items['node/%node/revisions/view/%/%']['access callback'] = '_revisioning_node_revision_access';
314    $items['node/%node/revisions/view/%/%']['access arguments'] = array('view revisions', 1);
315  }
316  // This is here rather than in revisioning_menu() as Diff may redefine
317  // the node/%node/revisions/list item.
318  $items['node/%node/revisions/list'] = array(
319    'title' => t('List all revisions'),
320    'access callback' => '_revisioning_node_revision_access',
321    'access arguments' => array('view revision list', 1),
322    'file' => 'node.pages.inc',
323    'module' => 'node',
324  //'file path' => drupal_get_path('module', 'node'),
325    'type' => MENU_LOCAL_TASK, // was: MENU_DEFAULT_LOCAL_TASK; changed for Smart tabs
326    'weight' => -20,
327  );
328
329  $items['node/%node/revisions/delete-archived'] = array(
330    'title' => t('Delete archived revisions'),
331    'page callback' => 'drupal_get_form',
332    'page arguments' => array('revisioning_delete_archived_confirm', 1),
333    'access callback' => '_revisioning_node_revision_access',
334    'access arguments' => array('delete archived revisions', 1),
335    'type' => MENU_CALLBACK,
336  );
337
338  // Apart from administrators, allow those that pass the 'trigger_access_check'
339  // to configure the revisioning triggers. This means that users must have at
340  // least 'administer actions' and 'access administration pages' (the latter is
341  // to allow them to navigate to the trigger page via the menu).
342  if (module_exists('trigger')) {
343    $items['admin/build/trigger/revisioning']['access callback'] = 'trigger_access_check';
344  }
345
346  // [#1024864]: Allow other modules to make further alterations
347  drupal_alter('revisioning_menu', $items);
348}
349
350/**
351 * Implementation of hook_nodeapi().
352 *
353 * This function is called serveral times during the node's life cycle, with
354 * different node operations passed in.
355 *
356 * Typically when loading a node for viewing, the order is:
357 *   'load', 'view', 'alter'
358 *
359 * When creating new content:
360 *   Before displaying the creation form: 'prepare'
361 *   When saving: 'validate', 'presave', 'insert'
362 *
363 * When editing an existing node:
364 *   Before displaying the edit form: 'load', 'prepare'
365 *   When saving: 'load', 'validate', 'presave', 'update'
366 *
367 * The same $op may be requested multiple times during the same HTTP request,
368 * especially 'load'.
369 */
370function revisioning_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
371  if (!empty($node->bypass_nodeapi)) {
372    return;
373  }
374
375  switch ($op) {
376    case 'load': // called at the end of node_load()
377      // The revision_moderation flag may be overridden on the node edit form
378      // by users with the "administer nodes" permission
379      $node->revision_moderation = node_tools_content_is_moderated($node->type);
380      $node->is_pending = _revisioning_node_is_pending($node);
381
382      // Could use following, but this seems old style, i.e. when $node is not a &ref
383      //$node_extras['revision_moderation'] = $node->revision_moderation;
384      //$node_extras['is_pending'] = $node->is_pending;
385      break;
386
387    case 'view': // called from called from node_view() before $node is fully built
388      break;
389
390    case 'alter': // called from node_view() after $node is fully built for display
391      if (!$teaser && $node->nid == arg(1) && // don't show msg on page with many nodes
392        !empty($node->revision_moderation) && user_access('view revision status messages')) {
393        drupal_set_message(_revisioning_node_info_msg($node));
394      }
395      break;
396
397    case 'prepare': // presenting edit form
398      // Check that we're dealing with the current node, not its translation source
399      $current_nid = arg(1);
400      if (is_numeric($current_nid) && $current_nid == $node->nid) {
401        _revisioning_prepare_msg($node);
402      }
403      break;
404
405    case 'presave': // for edits, called from node_save(), prior to _node_save_revision()
406      if (!empty($node->revision_moderation)) { // Tick-box on edit form
407        $node->is_pending = _revisioning_node_is_pending($node);
408        if ($node->revision && $node->is_pending && variable_get('new_revisions_'. $node->type, NEW_REVISION_WHEN_NOT_PENDING) == NEW_REVISION_WHEN_NOT_PENDING) {
409          drupal_set_message(t('Updating existing copy, not creating new revision as this one is still pending.'));
410          // Update the $node object just before it is saved to the db
411          $node->revision = FALSE;
412        }
413        // Save title of current revision to restore at $op=='update' time
414        $node->current_title = db_result(db_query('SELECT title FROM {node} WHERE nid=%d', $node->nid));
415
416        if (variable_get('revisioning_auto_publish_'. $node->type, FALSE)) {
417          if (user_access('publish revisions') ||
418              user_access("publish revisions of any $node->type content") ||
419             (user_access("publish revisions of own $node->type content") && $node->revision_uid == $user->uid)) {
420            if (!$node->status) {
421              // Fix for [#751092] (thanks naquah and mirgorod_a).
422              // If the Publish box has just been unticked, do not auto-publish.
423              if (isset($node->nid)) {
424                 // Existing published node for which Publish box was unticked.
425                if (db_result(db_query("SELECT status FROM {node} WHERE nid = %d", $node->nid))) {
426                  break;
427                }
428              }
429              else {
430                $node_options = variable_get('node_options_'. $node->type, array());
431                if (in_array('status', $node_options)) {
432                  // New node for which Publish box is ticked by default but was
433                  // unticked on the edit form.
434                  break;
435                }
436              }
437            }
438            drupal_set_message(t('Auto-publishing this revision.'));
439            $node->status = TRUE;
440            // Make sure the 'update' does NOT reset vid, so that new revision becomes current
441            unset($node->current_revision_id);
442          }
443        }
444      }
445      break;
446
447    case 'insert': // new node, called from node_save(), after _node_save_revision()
448      if (!empty($node->revision_moderation)) {
449        _revisioning_insert_msg($node);
450      }
451      break;
452
453    case 'update':  // edited node, called from node_save(), after _node_save_revision()
454      if (!empty($node->revision_moderation) && $node->current_revision_id // i.e. set and not 0
455          && $node->current_revision_id != $node->vid) {
456        // Resetting title and vid back to their originial values, thus creating pending revision.
457        db_query("UPDATE {node} SET vid=%d, title='%s' WHERE nid=%d", $node->current_revision_id, $node->current_title, $node->nid);
458      //$node->is_pending = TRUE;
459      }
460      break;
461
462    case 'delete revision':
463      module_invoke_all('revisionapi', 'post delete', $node);
464      break;
465  }
466}
467
468/**
469 * Implementation of hook_block().
470 *
471 * A block that may be placed on all or selected pages, alerting the user
472 * (moderator) when new content has been submitted for review. Shows titles
473 * of pending revisions as a series of links (max. number configurable).
474 * Clicking a link takes the moderator straight to the revision in question.
475 */
476function revisioning_block($op = 'list', $delta = 0, $edit = array()) {
477  switch ($op) {
478    case 'list':
479      // Set up the defaults for the Site configuration>>Blocks page
480      // Return a list of (1) block(s) and the default values
481      $blocks[0]['info'] = t('Pending revisions');
482      $blocks[0]['cache'] = BLOCK_NO_CACHE;
483      $blocks[0]['weight'] = -10; // top of whatever region is chosen
484      $blocks[0]['custom'] = FALSE; // block is implemented by this module;
485      return $blocks;
486
487    case 'configure':
488      $form['revisioning_block_num_pending'] = array(
489        '#type' => 'textfield',
490        '#title' => t('Maximum number of pending revisions displayed'),
491        '#default_value' => variable_get('revisioning_block_num_pending', 5),
492        '#description' => t('Note: the title of this block mentions the total number of revisions pending, which may be greater than the number of revisions displayed.')
493      );
494      $form['revisioning_block_order'] = array(
495        '#type' => 'radios',
496        '#title' => t('Order in which pending revisions are displayed'),
497        '#options' => array(
498          REVISIONS_BLOCK_OLDEST_AT_TOP => t('Oldest at top'),
499          REVISIONS_BLOCK_NEWEST_AT_TOP => t('Newest at top')),
500        '#default_value' => variable_get('revisioning_block_order', REVISIONS_BLOCK_OLDEST_AT_TOP),
501        '#description' => t('Note: order is based on revision timestamps.')
502      );
503      $form['revisioning_content_summary_page'] = array(
504        '#type' => 'textfield',
505        '#title' => t('Page to go to when the block title is clicked'),
506        '#default_value' => variable_get('revisioning_content_summary_page', ''),
507        '#description' => t('When left blank this will default to %accessible_content, provided Module Grants Montior is enabled and the user has sufficient permissions. Otherwise %admin_content is used, subject to permissions. For any of this to work the above <strong>Block title</strong> field must be left blank.',
508          array('%accessible_content' => 'accessible-content', '%admin_content' => 'admin/content/node'))
509      );
510      return $form;
511
512    case 'save':
513      variable_set('revisioning_block_num_pending', (int)$edit['revisioning_block_num_pending']);
514      variable_set('revisioning_block_order', (int)$edit['revisioning_block_order']);
515      variable_set('revisioning_content_summary_page', $edit['revisioning_content_summary_page']);
516      break;
517
518    case 'view':
519      $max_nodes = variable_get('revisioning_block_num_pending', 100);
520      $order = variable_get('revisioning_block_order', REVISIONS_BLOCK_OLDEST_AT_TOP) == REVISIONS_BLOCK_OLDEST_AT_TOP ? 'ASC' : 'DESC';
521      $nodes = node_tools_get_nodes('update', NO_FILTER, NO_FILTER, NO_FILTER, TRUE, TRUE, $max_nodes, 'timestamp '. $order);
522      if (!empty($nodes)) {
523        return _theme_revisions_pending_block($nodes);
524      }
525  }
526}
527
528/**
529 * Implementation of hook_views_api().
530 */
531function revisioning_views_api() {
532  return array(
533    'api' => views_api_version(),
534    'path' => drupal_get_path('module', 'revisioning') .'/views'
535  );
536}
537
538/**
539 * Perform path manipulations for menu items containing %vid wildcard. $map
540 * contains what arg() function returns, eg. $map[0]=='node', $map[1]=='123'.
541 *
542 * When vid is absent, return $map as empty array. This seems to disable menu
543 * items which require a vid context to work. So on the page
544 * "node/123/revisions" we won't see tasks like "node/123/revisions/456/edit".
545 *
546 * An alternative implementation would be to substitute an empty vid with
547 * current revision id. In that case we should also change the tab titles
548 * (via title callbacks) for an enhanced user experience. For example: we'd
549 * change "Edit" to "Edit current".
550 *
551 * See http://drupal.org/node/500864
552 */
553function vid_to_arg($arg, &$map, $index) {
554  if (empty($arg)) {
555    //return node_tools_get_current_node_revision_id($nid = $map[1]);
556    $map = array();
557    return '';
558  }
559  return $arg;
560}
561
562/**
563 * Implementation of hook_user_node_access().
564 *
565 * @see module_grants_node_revision_access()
566 *
567 * @param $revision_op
568 *   node or revision operation e.g. 'view revisions'
569 * @param $node
570 * @return the associated node operation required for this revision_op, or
571 *   FALSE if access to the node is to be denied.
572 *   Valid node operations to return are 'view', 'update', 'delete'.
573 */
574function revisioning_user_node_access($revision_op, $node) {
575  global $user;
576
577  $type = check_plain($node->type);
578
579  switch ($revision_op) {
580    case 'view current':
581      break;
582    case 'compare to current':
583    case 'view revisions':
584    case 'view revision list':
585      if (user_access('view revisions', $user)) { // node.module
586        break;
587      }
588      if (user_access('view revisions of any '. $type .' content', $user)) {
589        break;
590      }
591      if (($node->uid == $user->uid) && user_access('view revisions of own '. $type .' content', $user)) {
592        break;
593      }
594      return FALSE;
595
596    case 'edit current':
597      return 'update';
598
599    case 'edit revisions':
600    case 'revert revisions':
601      return user_access($revision_op, $user) ? 'update' : FALSE;
602
603    case 'publish revisions':
604      if (user_access('publish revisions of any '. $type .' content', $user)) {
605        break;
606      }
607      if (($node->uid == $user->uid) && user_access('publish revisions of own '. $type .' content', $user)) {
608        break;
609      }
610    case 'unpublish current revision':
611      return user_access($revision_op, $user) ? 'view' : FALSE;
612
613    case 'delete revisions':
614    case 'delete archived revisions':
615      if (!user_access('delete revisions', $user)) {
616        return FALSE;
617      }
618    case 'delete node':
619      return 'delete';
620
621    default:
622      drupal_set_message(t("Unknown Revisioning operation '%op'. Treating as 'view'.", array('%op' => $revision_op)), 'warning', FALSE);
623  }
624  return 'view';
625}
626
627/**
628 * Test whether the supplied revision operation is appropriate for the node.
629 * This is irrespective of user permissions, e.g. even for an administrator it
630 * doesn't make sense to publish a node that is already published or to
631 * "revert" to the current revision.
632 *
633 * @param $revision_op
634 * @param $node
635 * @return TRUE if the operation is appropriate for this node at this point
636 */
637function _revisioning_operation_appropriate($revision_op, $node) {
638  switch ($revision_op) {
639
640    case 'compare to current':
641      // Can't compare against itself
642    case 'delete revisions':
643      // If the revision is the current one, suppress the delete operation
644      // @TODO ...unless it's the only revision, in which case delete the
645      // entire node; however this requires a different URL.
646      return !$node->is_current;
647
648    case 'delete archived revisions':
649      break;
650
651    case 'view revision list': // i.e. node revisions summary
652      if ($node->num_revisions == 1 && !$node->revision_moderation
653        /* && (module_exists('module_grants') ? !module_grants_node_access('delete', $node) : !node_access('delete', $node))*/) {
654        // Suppress Revisions tab when when there's only 1 revision -- consistent with core.
655        // However, when content is moderated (i.e. "New revision in draft,
656        // pending moderation" is ticked) we want to be able to get to the
657        // 'Unpublish current' link on this page and the 'Publish this' tab on
658        // the next. Also when user has permission to delete node, we need to
659        // present the Delete link, unless we assume that this privilege
660        // assumes the 'edit' permission.
661        return FALSE;
662      }
663      break;
664
665    case 'publish revisions':
666      // If the node isn't meant to be moderated and the user is not an admin,
667      // or the revision is not either pending or current but not published,
668      // then disallow publication.
669      if ((!$node->revision_moderation && !user_access('administer nodes'))
670        || !($node->is_pending || ($node->is_current && !$node->status))) {
671        return FALSE;
672      }
673      break;
674
675    case 'unpublish current revision':
676      // If the node isn't meant to be moderated and the user is not an admin,
677      // or it is unpublished already or we're not looking at the current
678      // revision, then unpublication is not an option.
679      if ((!$node->revision_moderation && !user_access('administer nodes'))
680        || !$node->status || !$node->is_current) {
681        return FALSE;
682      }
683      break;
684
685    case 'revert revisions':
686      // If this revision is pending or current, suppress the reversion
687      if ($node->is_pending || $node->is_current) {
688        return FALSE;
689      }
690      break;
691  }
692  return TRUE;
693}
694
695/**
696 * Determine whether the supplied revision operation is permitted on the node.
697 * This requires getting through three levels of defence:
698 * o Is the operation appropriate for this node at this time, e.g. a node must
699 *   not be published if it already is or if it isn't under moderation control
700 * o Does the user have permissions to operations of this kind in general?
701 * o Does the user have the node access rights (view/update/delete) required
702 *   for this operation?
703 *
704 * @param $revision_op
705 *   For instance 'publish revisions', 'delete revisions'
706 * @param $node
707 * @return bool
708 */
709function _revisioning_node_revision_access($revision_op, $node) {
710
711  if (!isset($node->num_revisions) || !isset($node->is_current)) {
712    drupal_set_message(t('Node object data incomplete -- have you enabled the Node Tools submodule?'), 'warning', FALSE);
713  }
714  if (!_revisioning_operation_appropriate($revision_op, $node)) {
715    return FALSE;
716  }
717  if (module_exists('module_grants')) {
718    $access = module_grants_node_revision_access($revision_op, $node);
719  }
720  else {
721    // Fall back to core to assess permissions (even though they suck)
722    $access = ($node_op = revisioning_user_node_access($revision_op, $node)) &&
723      node_access($node_op, $node);
724  }
725  return $access;
726}
727
728/**
729 * Access callback for 'node/%', 'node/%/view' and 'node/%/edit' links that
730 * may appear anywhere on the site.
731 * At the time that this function is called the CURRENT revision will already
732 * have been loaded by the system. However depending on the value of the
733 * 'revisioning_view_callback' and 'revisioning_edit_callback' variables (as
734 * set on the admin/settings/revisioning page), this may not be the desired
735 * revision.
736 * If these variables state that the LATEST revision should be loaded, we need
737 * to check at this point whether the user has permission to view this revision.
738 *
739 * The 'View current' and/or 'Edit current' tabs are suppressed when the current
740 * revision is already displayed via one of the Revisions subtabs.
741 * The 'View latest' and/or 'Edit latest' tabs are suppressed when the latest
742 * revision is already displayed via one of the Revisions subtabs.
743 *
744 * @param op, must be one of 'view' or 'edit'
745 * @param $node
746 * @return FALSE if access to the desired revision is denied
747 *
748 */
749function _revisioning_view_edit_access_callback($op, $node) {
750
751  $load_op = _revisioning_load_op($node, $op);
752
753  $vid = arg(3);
754  if (/*!empty($node->revision_moderation) && */is_numeric($vid)) {
755    // The View, Edit primary tabs are requested indirectly, in the context of
756    // the secondary tabs under Revisions, e.g. node/%/revisions/%
757    if ($load_op == REVISIONING_LOAD_CURRENT && $vid == $node->current_revision_id) {
758      // Suppress 'View current' and 'Edit current' primary tabs when viewing current
759      return FALSE;
760    }
761    if ($load_op == REVISIONING_LOAD_LATEST && $vid == revisioning_get_latest_revision_id($node->nid)) {
762      // Suppress 'View latest' and 'Edit latest' primary tabs when viewing latest
763      return FALSE;
764    }
765  }
766  if ($load_op == REVISIONING_LOAD_LATEST) {
767    // _revisioning_load_op has already checked permission to view latest
768    return TRUE;
769  }
770  $revision_op = ($op == 'view') ? 'view current' : 'edit current';
771  return _revisioning_node_revision_access($revision_op, $node);
772}
773
774function _revisioning_load_op($node, $op, $check_access = TRUE) {
775  if ($node->revision_moderation) {
776    $view_mode = (int)variable_get('revisioning_view_callback', REVISIONING_LOAD_CURRENT);
777    $edit_mode = (int)variable_get('revisioning_edit_callback', REVISIONING_LOAD_CURRENT);
778    $load_op = ($op == 'edit') ? $edit_mode : $view_mode;
779    if ($load_op == REVISIONING_LOAD_LATEST) {
780      // Site is configured to load latest revision, but we'll only do this if
781      // the latest isn't loaded already and the user has the permission to do so.
782      $latest_vid = revisioning_get_latest_revision_id($node->nid);
783      if ($latest_vid != $node->current_revision_id) {
784        if (!$check_access) {
785          return REVISIONING_LOAD_LATEST;
786        }
787        $original_vid = $node->vid;
788        $node->vid = $latest_vid;
789        $node->is_current = node_tools_revision_is_current($node);
790        $revision_op = ($op == 'view') ? 'view revisions' : 'edit revisions';
791        $access = _revisioning_node_revision_access($revision_op, $node);
792        // Restore $node (even though called by value), so that it remains consistent
793        $node->vid = $original_vid;
794        $node->is_current = node_tools_revision_is_current($node);
795        if ($access) {
796          return REVISIONING_LOAD_LATEST;
797        }
798      }
799    }
800  }
801  return REVISIONING_LOAD_CURRENT;
802}
803
804function _revisioning_prepare_msg($node) {
805  if (!$node->nid) { // new node
806    return;
807  }
808  $count = _revisioning_get_number_of_revisions_newer_than($node->vid, $node->nid);
809  if ($count == 1) {
810    drupal_set_message(t('Please note there is one revision more recent than the one you are about to edit.'), 'warning');
811  }
812  elseif ($count > 1) {
813    drupal_set_message(t('Please note there are !count revisions more recent than the one you are about to edit.', array('!count' => $count)), 'warning');
814  }
815}
816
817function _revisioning_insert_msg($node) {
818  if ($node->status) {
819    drupal_set_message(t('Initial revision created and published.'));
820  }
821  else {
822    drupal_set_message(t('Initial draft created, pending publication.'));
823  }
824}
825
826/**
827 * Display all revisions of the supplied node in a themed table with links for
828 * the permitted operations above it.
829 */
830function _revisioning_present_node($node, $op = 'any') {
831  return ($op  == 'edit' && !$node->revision_moderation) ? node_page_edit($node) :_theme_revisions_summary($node);
832}
833
834/**
835 * Menu callback for the primary View tab.
836 */
837function _revisioning_view($node) {
838  if (_revisioning_load_op($node, 'view') == REVISIONING_LOAD_LATEST) {
839    $vid_to_load = revisioning_get_latest_revision_id($node->nid);
840    $node = node_load($node->nid, $vid_to_load);
841  }
842  // In node.module, node_page_view() is used to display the current, while
843  // node_show() is used for any other revision. The difference between the
844  // two is that node_page_view() surpresses the message that tells us we're
845  // viewing a revision. That's what we use here because we have our own
846  // configurable message.
847  return node_page_view($node);
848}
849
850/**
851 * Callback for the primary Edit tab.
852 */
853function _revisioning_edit($node) {
854  // Use the admin theme if the user specified this for node edit pages
855  if (variable_get('node_admin_theme', FALSE)) {
856    global $theme, $custom_theme;
857    $custom_theme = variable_get('admin_theme', $theme);
858  }
859  if (_revisioning_load_op($node, 'edit') == REVISIONING_LOAD_LATEST) {
860    $vid_to_load = revisioning_get_latest_revision_id($node->nid);
861    $node = node_load($node->nid, $vid_to_load);
862  }
863  // Following is the same as node_page_edit().
864  drupal_set_title(check_plain($node->title));
865  return drupal_get_form($node->type .'_node_form', $node);
866}
867
868/**
869 * Callback for the primary View and Edit tabs.
870 * @param $node
871 * @param $edit, bool
872 * @return string
873 */
874function _revisioning_title_for_tab($node, $edit = FALSE) {
875  if ($node->num_revisions <= 1 || !$node->revision_moderation) {
876    return $edit ? t('Edit') : t('View');
877  }
878  if (_revisioning_load_op($node, $edit ? 'edit' : 'view') == REVISIONING_LOAD_LATEST) {
879    return $edit ? t('Edit latest') : t('View latest');
880  }
881  return $edit ? t('Edit current') : t('View current');
882}
883
884/**
885 * Callback to view a particular revision.
886 */
887function _revisioning_view_revision($node) {
888  if (isset($node->nid)) {
889    $router_item = menu_get_item('node/' . $node->nid);
890    if (!empty($router_item['file'])) {
891      $path = $_SERVER['DOCUMENT_ROOT'] . base_path();
892      require_once $path . $router_item['file'];
893    }
894    // Call whatever function is assigned to the main node path but pass the
895    // current node as an argument. This approach allows for the reuse of Panel
896    // definition acting on node/%node. See [#1567880].
897    if (isset($router_item['page_callback'])) {
898      return $router_item['page_callback']($node);
899    }
900  }
901  return node_page_view($node);
902}
903
904/**
905 * Callback to edit a particular revision.
906 */
907function _revisioning_edit_revision($node) {
908  // Use the admin theme if the user specified this for node edit pages
909  if (variable_get('node_admin_theme', FALSE)) {
910    global $theme, $custom_theme;
911    $custom_theme = variable_get('admin_theme', $theme);
912  }
913  drupal_set_title(check_plain($node->title));
914  return drupal_get_form($node->type .'_node_form', $node);
915}
916
917/**
918 * Use diff's compare callback to compare specific revision to the current one
919 */
920if (module_exists('diff')) {
921  function _revisioning_compare_to_current_revision($node) {
922    module_load_include('inc', 'diff', 'diff.pages'); // for diff_diffs_show()
923    // Make sure that latest of the two revisions is on the right
924    if ($node->is_pending) {
925      return diff_diffs_show($node, $node->current_revision_id, $node->vid);
926    }
927    return diff_diffs_show($node, $node->vid, $node->current_revision_id);
928  }
929}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.