View source
<?php
define('REVISIONING_LOAD_CURRENT', 0);
define('REVISIONING_LOAD_LATEST', 1);
define('NEW_REVISION_WHEN_NOT_PENDING', 0);
define('NEW_REVISION_EVERY_SAVE', 1);
define('REVISIONS_BLOCK_OLDEST_AT_TOP', 0);
define('REVISIONS_BLOCK_NEWEST_AT_TOP', 1);
require_once drupal_get_path('module', 'revisioning') . '/revisioning_api.inc';
require_once drupal_get_path('module', 'revisioning') . '/revisioning.pages.inc';
require_once drupal_get_path('module', 'revisioning') . '/revisioning_theme.inc';
require_once drupal_get_path('module', 'revisioning') . '/revisioning_tokens.inc';
require_once drupal_get_path('module', 'revisioning') . '/revisioning_triggers_actions.inc';
function revisioning_help($path, $arg) {
switch ($path) {
case 'admin/help#revisioning':
$s = t('For documentation and tutorials see the <a href="@revisioning">Revisioning project page</a>', array(
'@revisioning' => url('http://drupal.org/project/revisioning'),
));
break;
case 'node/%/revisions':
$s = t('To edit, publish or delete one of the revisions below, click on its saved date.');
break;
case 'admin/build/trigger/revisioning':
$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.");
break;
case 'accessible-content/i-created/pending':
$s = t('Showing all <em>pending</em> content <em>you created</em> and still have at least view access to.');
break;
case 'accessible-content/i-last-modified/pending':
$s = t('Showing all <em>pending</em> content <em>you last modified</em> and still have at least view access to.');
break;
case 'accessible-content/i-can-edit/pending':
$s = t('Showing all <em>pending</em> content you can <em>edit</em>.');
break;
case 'accessible-content/i-can-view/pending':
$s = t('Showing all <em>pending</em> content you have at least <em>view</em> access to.');
break;
}
return empty($s) ? '' : '<p>' . $s . '</p>';
}
function revisioning_perm() {
$perms = module_exists('module_grants_monitor') ? array(
'access Pending tab',
) : array();
$perms = array_merge($perms, array(
'view revision status messages',
'edit revisions',
'publish revisions',
'unpublish current revision',
));
foreach (node_get_types() as $type) {
$name = check_plain($type->type);
$perms[] = 'view revisions of own ' . $name . ' content';
$perms[] = 'view revisions of any ' . $name . ' content';
$perms[] = 'publish revisions of own ' . $name . ' content';
$perms[] = 'publish revisions of any ' . $name . ' content';
}
return $perms;
}
function revisioning_menu() {
$items = array();
if (module_exists('module_grants_monitor')) {
$items['accessible-content/i-created/pending'] = array(
'title' => 'In draft/Pending publication',
'page callback' => '_revisioning_show_pending_nodes',
'page arguments' => array(
'view',
I_CREATED,
),
'access callback' => 'revisioning_user_all_access',
'access arguments' => array(
array(
'access I Created tab',
'access Pending tab',
),
),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
$items['accessible-content/i-last-modified/pending'] = array(
'title' => 'In draft/Pending publication',
'page callback' => '_revisioning_show_pending_nodes',
'page arguments' => array(
'view',
I_LAST_MODIFIED,
),
'access callback' => 'revisioning_user_all_access',
'access arguments' => array(
array(
'access I Last Modified tab',
'access Pending tab',
),
),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
$items['accessible-content/i-can-edit/pending'] = array(
'title' => 'In draft/Pending publication',
'page callback' => '_revisioning_show_pending_nodes',
'page arguments' => array(
'update',
),
'access callback' => 'revisioning_user_all_access',
'access arguments' => array(
array(
'access I Can Edit tab',
'access Pending tab',
),
),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
$items['accessible-content/i-can-view/pending'] = array(
'title' => 'In draft/Pending publication',
'page callback' => '_revisioning_show_pending_nodes',
'page arguments' => array(
'view',
),
'access callback' => 'revisioning_user_all_access',
'access arguments' => array(
array(
'access I Can View tab',
'access Pending tab',
),
),
'type' => MENU_LOCAL_TASK,
'weight' => -1,
);
}
$items['node/%node/unpublish'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_unpublish_confirm',
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'unpublish current revision',
1,
),
'type' => MENU_CALLBACK,
);
$items['node/%node/revisions/%vid/view'] = array(
'title' => 'View',
'load arguments' => array(
3,
),
'page callback' => '_revisioning_view_revision',
'page arguments' => array(
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'view revisions',
1,
),
'type' => MENU_LOCAL_TASK,
'weight' => -10,
'tab_parent' => 'node/%/revisions',
);
$items['node/%node/revisions/%vid/edit'] = array(
'title' => 'Edit',
'load arguments' => array(
3,
),
'page callback' => '_revisioning_edit_revision',
'page arguments' => array(
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'edit revisions',
1,
),
'file' => 'node.pages.inc',
'file path' => drupal_get_path('module', 'node'),
'type' => MENU_LOCAL_TASK,
'weight' => -7,
'tab_parent' => 'node/%/revisions',
);
$items['node/%node/revisions/%vid/publish'] = array(
'title' => 'Publish this',
'load arguments' => array(
3,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_publish_confirm',
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'publish revisions',
1,
),
'type' => MENU_LOCAL_TASK,
'weight' => -4,
'tab_parent' => 'node/%/revisions',
);
$items['node/%node/revisions/%vid/unpublish'] = array(
'title' => 'Unpublish this',
'load arguments' => array(
3,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_unpublish_confirm',
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'unpublish current revision',
1,
),
'type' => MENU_LOCAL_TASK,
'weight' => -3,
'tab_parent' => 'node/%/revisions',
);
$items['node/%node/revisions/%vid/revert'] = array(
'title' => 'Revert to this',
'load arguments' => array(
3,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'node_revision_revert_confirm',
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'revert revisions',
1,
),
'file' => 'node.pages.inc',
'file path' => drupal_get_path('module', 'node'),
'type' => MENU_LOCAL_TASK,
'weight' => -2,
'tab_parent' => 'node/%/revisions',
);
$items['node/%node/revisions/%vid/delete'] = array(
'title' => 'Delete',
'load arguments' => array(
3,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'node_revision_delete_confirm',
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'delete revisions',
1,
),
'file' => 'node.pages.inc',
'file path' => drupal_get_path('module', 'node'),
'type' => MENU_LOCAL_TASK,
'weight' => 10,
'tab_parent' => 'node/%/revisions',
);
if (module_exists('diff')) {
$items['node/%node/revisions/%vid/compare'] = array(
'title' => 'Compare to current',
'load arguments' => array(
3,
),
'page callback' => '_revisioning_compare_to_current_revision',
'page arguments' => array(
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'compare to current',
1,
),
'type' => MENU_LOCAL_TASK,
'weight' => 0,
'tab_parent' => 'node/%/revisions',
);
}
$items['admin/settings/revisioning'] = array(
'title' => 'Revisioning',
'description' => 'Configure how links to view and edit content behave.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_admin_configure',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'revisioning.admin.inc',
);
return $items;
}
function revisioning_menu_alter(&$items) {
$items['node/%node']['access callback'] = $items['node/%node/view']['access callback'] = '_revisioning_view_edit_access_callback';
$items['node/%node']['access arguments'] = $items['node/%node/view']['access arguments'] = array(
'view',
1,
);
$items['node/%node']['page callback'] = $items['node/%node/view']['page callback'] = '_revisioning_view';
$items['node/%node']['page arguments'] = $items['node/%node/view']['page arguments'] = array(
1,
);
$items['node/%node/view']['title callback'] = '_revisioning_title_for_tab';
$items['node/%node/view']['title arguments'] = array(
1,
FALSE,
);
$items['node/%node/edit']['access callback'] = '_revisioning_view_edit_access_callback';
$items['node/%node/edit']['access arguments'] = array(
'edit',
1,
);
$items['node/%node/edit']['page callback'] = '_revisioning_edit';
$items['node/%node/edit']['title callback'] = '_revisioning_title_for_tab';
$items['node/%node/edit']['title arguments'] = array(
1,
TRUE,
);
$items['node/%node/revisions']['access callback'] = '_revisioning_node_revision_access';
$items['node/%node/revisions']['access arguments'] = array(
'view revision list',
1,
);
$items['node/%node/revisions']['page callback'] = '_revisioning_present_node';
$items['node/%node/revisions']['page arguments'] = array(
1,
);
unset($items['node/%node/revisions/%/view']);
unset($items['node/%node/revisions/%/revert']);
unset($items['node/%node/revisions/%/delete']);
if (module_exists('diff')) {
$items['node/%node/revisions/view/%/%']['access callback'] = '_revisioning_node_revision_access';
$items['node/%node/revisions/view/%/%']['access arguments'] = array(
'view revisions',
1,
);
}
$items['node/%node/revisions/list'] = array(
'title' => t('List all revisions'),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'view revision list',
1,
),
'file' => 'node.pages.inc',
'module' => 'node',
'type' => MENU_LOCAL_TASK,
'weight' => -20,
);
$items['node/%node/revisions/delete-archived'] = array(
'title' => t('Delete archived revisions'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_delete_archived_confirm',
1,
),
'access callback' => '_revisioning_node_revision_access',
'access arguments' => array(
'delete archived revisions',
1,
),
'type' => MENU_CALLBACK,
);
if (module_exists('trigger')) {
$items['admin/build/trigger/revisioning']['access callback'] = 'trigger_access_check';
}
drupal_alter('revisioning_menu', $items);
}
function revisioning_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
if (!empty($node->bypass_nodeapi)) {
return;
}
switch ($op) {
case 'load':
$node->revision_moderation = node_tools_content_is_moderated($node->type);
$node->is_pending = _revisioning_node_is_pending($node);
break;
case 'view':
break;
case 'alter':
if (!$teaser && $node->nid == arg(1) && !empty($node->revision_moderation) && user_access('view revision status messages')) {
drupal_set_message(_revisioning_node_info_msg($node));
}
break;
case 'prepare':
$current_nid = arg(1);
if (is_numeric($current_nid) && $current_nid == $node->nid) {
_revisioning_prepare_msg($node);
}
break;
case 'presave':
if (!empty($node->revision_moderation)) {
$node->is_pending = _revisioning_node_is_pending($node);
if ($node->revision && $node->is_pending && variable_get('new_revisions_' . $node->type, NEW_REVISION_WHEN_NOT_PENDING) == NEW_REVISION_WHEN_NOT_PENDING) {
drupal_set_message(t('Updating existing copy, not creating new revision as this one is still pending.'));
$node->revision = FALSE;
}
$node->current_title = db_result(db_query('SELECT title FROM {node} WHERE nid=%d', $node->nid));
if (variable_get('revisioning_auto_publish_' . $node->type, FALSE)) {
if (user_access('publish revisions') || user_access("publish revisions of any {$node->type} content") || user_access("publish revisions of own {$node->type} content") && $node->revision_uid == $user->uid) {
if (!$node->status) {
if (isset($node->nid)) {
if (db_result(db_query("SELECT status FROM {node} WHERE nid = %d", $node->nid))) {
break;
}
}
else {
$node_options = variable_get('node_options_' . $node->type, array());
if (in_array('status', $node_options)) {
break;
}
}
}
drupal_set_message(t('Auto-publishing this revision.'));
$node->status = TRUE;
unset($node->current_revision_id);
}
}
}
break;
case 'insert':
if (!empty($node->revision_moderation)) {
_revisioning_insert_msg($node);
}
break;
case 'update':
if (!empty($node->revision_moderation) && $node->current_revision_id && $node->current_revision_id != $node->vid) {
db_query("UPDATE {node} SET vid=%d, title='%s' WHERE nid=%d", $node->current_revision_id, $node->current_title, $node->nid);
}
break;
case 'delete revision':
module_invoke_all('revisionapi', 'post delete', $node);
break;
}
}
function revisioning_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('Pending revisions');
$blocks[0]['cache'] = BLOCK_NO_CACHE;
$blocks[0]['weight'] = -10;
$blocks[0]['custom'] = FALSE;
return $blocks;
case 'configure':
$form['revisioning_block_num_pending'] = array(
'#type' => 'textfield',
'#title' => t('Maximum number of pending revisions displayed'),
'#default_value' => variable_get('revisioning_block_num_pending', 5),
'#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.'),
);
$form['revisioning_block_order'] = array(
'#type' => 'radios',
'#title' => t('Order in which pending revisions are displayed'),
'#options' => array(
REVISIONS_BLOCK_OLDEST_AT_TOP => t('Oldest at top'),
REVISIONS_BLOCK_NEWEST_AT_TOP => t('Newest at top'),
),
'#default_value' => variable_get('revisioning_block_order', REVISIONS_BLOCK_OLDEST_AT_TOP),
'#description' => t('Note: order is based on revision timestamps.'),
);
$form['revisioning_content_summary_page'] = array(
'#type' => 'textfield',
'#title' => t('Page to go to when the block title is clicked'),
'#default_value' => variable_get('revisioning_content_summary_page', ''),
'#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.', array(
'%accessible_content' => 'accessible-content',
'%admin_content' => 'admin/content/node',
)),
);
return $form;
case 'save':
variable_set('revisioning_block_num_pending', (int) $edit['revisioning_block_num_pending']);
variable_set('revisioning_block_order', (int) $edit['revisioning_block_order']);
variable_set('revisioning_content_summary_page', $edit['revisioning_content_summary_page']);
break;
case 'view':
$max_nodes = variable_get('revisioning_block_num_pending', 100);
$order = variable_get('revisioning_block_order', REVISIONS_BLOCK_OLDEST_AT_TOP) == REVISIONS_BLOCK_OLDEST_AT_TOP ? 'ASC' : 'DESC';
$nodes = node_tools_get_nodes('update', NO_FILTER, NO_FILTER, NO_FILTER, TRUE, TRUE, $max_nodes, 'timestamp ' . $order);
if (!empty($nodes)) {
return _theme_revisions_pending_block($nodes);
}
}
}
function revisioning_views_api() {
return array(
'api' => views_api_version(),
'path' => drupal_get_path('module', 'revisioning') . '/views',
);
}
function vid_to_arg($arg, &$map, $index) {
if (empty($arg)) {
$map = array();
return '';
}
return $arg;
}
function revisioning_user_node_access($revision_op, $node) {
global $user;
$type = check_plain($node->type);
switch ($revision_op) {
case 'view current':
break;
case 'compare to current':
case 'view revisions':
case 'view revision list':
if (user_access('view revisions', $user)) {
break;
}
if (user_access('view revisions of any ' . $type . ' content', $user)) {
break;
}
if ($node->uid == $user->uid && user_access('view revisions of own ' . $type . ' content', $user)) {
break;
}
return FALSE;
case 'edit current':
return 'update';
case 'edit revisions':
case 'revert revisions':
return user_access($revision_op, $user) ? 'update' : FALSE;
case 'publish revisions':
if (user_access('publish revisions of any ' . $type . ' content', $user)) {
break;
}
if ($node->uid == $user->uid && user_access('publish revisions of own ' . $type . ' content', $user)) {
break;
}
case 'unpublish current revision':
return user_access($revision_op, $user) ? 'view' : FALSE;
case 'delete revisions':
case 'delete archived revisions':
if (!user_access('delete revisions', $user)) {
return FALSE;
}
case 'delete node':
return 'delete';
default:
drupal_set_message(t("Unknown Revisioning operation '%op'. Treating as 'view'.", array(
'%op' => $revision_op,
)), 'warning', FALSE);
}
return 'view';
}
function _revisioning_operation_appropriate($revision_op, $node) {
switch ($revision_op) {
case 'compare to current':
case 'delete revisions':
return !$node->is_current;
case 'delete archived revisions':
break;
case 'view revision list':
if ($node->num_revisions == 1 && !$node->revision_moderation) {
return FALSE;
}
break;
case 'publish revisions':
if (!$node->revision_moderation && !user_access('administer nodes') || !($node->is_pending || $node->is_current && !$node->status)) {
return FALSE;
}
break;
case 'unpublish current revision':
if (!$node->revision_moderation && !user_access('administer nodes') || !$node->status || !$node->is_current) {
return FALSE;
}
break;
case 'revert revisions':
if ($node->is_pending || $node->is_current) {
return FALSE;
}
break;
}
return TRUE;
}
function _revisioning_node_revision_access($revision_op, $node) {
if (!isset($node->num_revisions) || !isset($node->is_current)) {
drupal_set_message(t('Node object data incomplete -- have you enabled the Node Tools submodule?'), 'warning', FALSE);
}
if (!_revisioning_operation_appropriate($revision_op, $node)) {
return FALSE;
}
if (module_exists('module_grants')) {
$access = module_grants_node_revision_access($revision_op, $node);
}
else {
$access = ($node_op = revisioning_user_node_access($revision_op, $node)) && node_access($node_op, $node);
}
return $access;
}
function _revisioning_view_edit_access_callback($op, $node) {
$load_op = _revisioning_load_op($node, $op);
$vid = arg(3);
if (is_numeric($vid)) {
if ($load_op == REVISIONING_LOAD_CURRENT && $vid == $node->current_revision_id) {
return FALSE;
}
if ($load_op == REVISIONING_LOAD_LATEST && $vid == revisioning_get_latest_revision_id($node->nid)) {
return FALSE;
}
}
if ($load_op == REVISIONING_LOAD_LATEST) {
return TRUE;
}
$revision_op = $op == 'view' ? 'view current' : 'edit current';
return _revisioning_node_revision_access($revision_op, $node);
}
function _revisioning_load_op($node, $op, $check_access = TRUE) {
if ($node->revision_moderation) {
$view_mode = (int) variable_get('revisioning_view_callback', REVISIONING_LOAD_CURRENT);
$edit_mode = (int) variable_get('revisioning_edit_callback', REVISIONING_LOAD_CURRENT);
$load_op = $op == 'edit' ? $edit_mode : $view_mode;
if ($load_op == REVISIONING_LOAD_LATEST) {
$latest_vid = revisioning_get_latest_revision_id($node->nid);
if ($latest_vid != $node->current_revision_id) {
if (!$check_access) {
return REVISIONING_LOAD_LATEST;
}
$original_vid = $node->vid;
$node->vid = $latest_vid;
$node->is_current = node_tools_revision_is_current($node);
$revision_op = $op == 'view' ? 'view revisions' : 'edit revisions';
$access = _revisioning_node_revision_access($revision_op, $node);
$node->vid = $original_vid;
$node->is_current = node_tools_revision_is_current($node);
if ($access) {
return REVISIONING_LOAD_LATEST;
}
}
}
}
return REVISIONING_LOAD_CURRENT;
}
function _revisioning_prepare_msg($node) {
if (!$node->nid) {
return;
}
$count = _revisioning_get_number_of_revisions_newer_than($node->vid, $node->nid);
if ($count == 1) {
drupal_set_message(t('Please note there is one revision more recent than the one you are about to edit.'), 'warning');
}
elseif ($count > 1) {
drupal_set_message(t('Please note there are !count revisions more recent than the one you are about to edit.', array(
'!count' => $count,
)), 'warning');
}
}
function _revisioning_insert_msg($node) {
if ($node->status) {
drupal_set_message(t('Initial revision created and published.'));
}
else {
drupal_set_message(t('Initial draft created, pending publication.'));
}
}
function _revisioning_present_node($node, $op = 'any') {
return $op == 'edit' && !$node->revision_moderation ? node_page_edit($node) : _theme_revisions_summary($node);
}
function _revisioning_view($node) {
if (_revisioning_load_op($node, 'view') == REVISIONING_LOAD_LATEST) {
$vid_to_load = revisioning_get_latest_revision_id($node->nid);
$node = node_load($node->nid, $vid_to_load);
}
return node_page_view($node);
}
function _revisioning_edit($node) {
if (variable_get('node_admin_theme', FALSE)) {
global $theme, $custom_theme;
$custom_theme = variable_get('admin_theme', $theme);
}
if (_revisioning_load_op($node, 'edit') == REVISIONING_LOAD_LATEST) {
$vid_to_load = revisioning_get_latest_revision_id($node->nid);
$node = node_load($node->nid, $vid_to_load);
}
drupal_set_title(check_plain($node->title));
return drupal_get_form($node->type . '_node_form', $node);
}
function _revisioning_title_for_tab($node, $edit = FALSE) {
if ($node->num_revisions <= 1 || !$node->revision_moderation) {
return $edit ? t('Edit') : t('View');
}
if (_revisioning_load_op($node, $edit ? 'edit' : 'view') == REVISIONING_LOAD_LATEST) {
return $edit ? t('Edit latest') : t('View latest');
}
return $edit ? t('Edit current') : t('View current');
}
function _revisioning_view_revision($node) {
if (isset($node->nid) && module_exists('panels')) {
$router_item = menu_get_item('node/' . $node->nid);
if (!empty($router_item['file'])) {
$path = $_SERVER['DOCUMENT_ROOT'] . base_path();
require_once $path . $router_item['file'];
}
if (isset($router_item['page_callback'])) {
return $router_item['page_callback']($node);
}
}
return node_page_view($node);
}
function _revisioning_edit_revision($node) {
if (variable_get('node_admin_theme', FALSE)) {
global $theme, $custom_theme;
$custom_theme = variable_get('admin_theme', $theme);
}
drupal_set_title(check_plain($node->title));
return drupal_get_form($node->type . '_node_form', $node);
}
if (module_exists('diff')) {
function _revisioning_compare_to_current_revision($node) {
module_load_include('inc', 'diff', 'diff.pages');
if ($node->is_pending) {
return diff_diffs_show($node, $node->current_revision_id, $node->vid);
}
return diff_diffs_show($node, $node->vid, $node->current_revision_id);
}
}