View source
<?php
require_once drupal_get_path('module', 'revisioning') . '/revisioning_triggers_actions.inc';
function revisioning_help($path, $arg) {
switch ($path) {
case 'node/%/revisions':
return t('To edit, publish or delete one of the revisions below, click on its saved date.');
}
}
function revisioning_menu() {
$items = array();
$items['content/pending'] = array(
'title' => 'Pending',
'page callback' => 'revisioning_pending_nodes',
'access arguments' => array(
'access content summary',
),
'weight' => -20,
);
$items['node/%node/revisions/%/edit'] = array(
'load arguments' => array(
3,
),
'page callback' => 'revisioning_edit',
'page arguments' => array(
1,
),
'access callback' => 'module_grants_node_revision_access',
'access arguments' => array(
'edit revisions',
1,
),
'file' => 'node.pages.inc',
'file path' => drupal_get_path('module', 'node'),
'type' => MENU_CALL_BACK,
);
$items['node/%node/revisions/%/publish'] = array(
'load arguments' => array(
3,
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_publish_confirm',
1,
),
'access callback' => 'module_grants_node_revision_access',
'access arguments' => array(
'publish revisions',
1,
),
'type' => MENU_CALLBACK,
);
$items['node/%node/unpublish'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revisioning_unpublish_confirm',
1,
),
'access callback' => 'module_grants_node_revision_access',
'access arguments' => array(
'unpublish current revision',
1,
),
'type' => MENU_CALLBACK,
);
return $items;
}
function revisioning_menu_alter(&$items) {
$items['content']['page callback'] = 'revisioning_pending_nodes';
$items['content/editable']['type'] = MENU_LOCAL_TASK;
$items['content/pending']['type'] = MENU_DEFAULT_LOCAL_TASK;
$items['node/%node/view']['title'] = 'View current';
$items['node/%node/edit']['page callback'] = 'revisioning_node_revisions';
$items['node/%node/edit']['page arguments'] = array(
1,
);
$items['node/%node/edit']['type'] = MENU_CALLBACK;
if (!module_exists("diff")) {
$items['node/%node/revisions']['page callback'] = 'revisioning_node_revisions';
$items['node/%node/revisions']['page arguments'] = array(
1,
);
}
$items['node/%node/revisions/%/view']['page callback'] = 'node_page_view';
$items['node/%node/revisions/%/view']['page arguments'] = array(
1,
);
$items['node/%node/revisions/%/revert']['page callback'] = 'drupal_get_form';
$items['node/%node/revisions/%/revert']['page arguments'] = array(
'revisioning_revert_confirm',
1,
);
}
function revisioning_perm() {
return array(
'edit revisions',
'publish revisions',
'unpublish current revision',
);
}
function revisioning_form_alter(&$form, $form_state, $form_id) {
if (isset($form['#id']) && $form['#id'] == 'node-form') {
$default_value = in_array('revision_moderation', variable_get("node_options_{$form['type']['#value']}", array(
'status',
'promote',
)));
if (!empty($node->revision) || user_access('administer nodes')) {
$form['revision_information']['revision_moderation'] = array(
'#type' => 'checkbox',
'#title' => t('New revisions in moderation'),
'#default_value' => $default_value,
);
}
else {
$form['revision_moderation'] = array(
'#type' => 'value',
'#value' => $default_value,
);
}
}
elseif ($form_id == 'node_type_form') {
$form['workflow']['node_options']['#options']['revision_moderation'] = t('New revisions in moderation');
}
}
function revisioning_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
$args = arg();
if ($args[0] != 'node' || !is_numeric($args[1])) {
return;
}
switch ($op) {
case 'alter':
case 'delete':
case 'load':
case 'validate':
return;
case 'insert':
if ($node->status) {
drupal_set_message(t('Initial revision created and published.'));
}
else {
drupal_set_message(t('Initial revision created, pending publication.'));
}
return;
case 'view':
if (!$teaser) {
_handle_view_op($node);
}
return;
}
if (end($args) == 'edit') {
if ($op == 'prepare') {
$count = _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');
}
}
if ($node->revision_moderation) {
switch ($op) {
case 'presave':
$current_revision = _get_current_revision($node->nid);
$node->original_vid = $current_revision->vid;
$node->original_revision = $node->revision;
$pending = $node->vid > $current_revision->vid || $node->vid == $current_revision->vid && !$node->status;
if ($node->revision && $pending) {
$node->revision = FALSE;
}
break;
case 'update':
$node->revision = $node->original_revision;
if (isset($node->original_vid)) {
db_query("UPDATE {node} SET vid=%d WHERE nid=%d", $node->original_vid, $node->nid);
}
break;
}
}
elseif ($op == 'update') {
drupal_set_message(t('Your changes are now current as moderation is switched off for this content.'), 'warning');
}
}
}
function revisioning_edit($node) {
drupal_set_title(check_plain($node->title));
return drupal_get_form($node->type . '_node_form', $node);
}
function revisioning_publish_confirm($form_state, $node) {
$form['node_id'] = array(
'#type' => 'value',
'#value' => $node->nid,
);
$form['title'] = array(
'#type' => 'value',
'#value' => $node->title,
);
$form['revision'] = array(
'#type' => 'value',
'#value' => $node->vid,
);
$form['type'] = array(
'#type' => 'value',
'#value' => $node->type,
);
return confirm_form($form, t('Are you sure you want to publish this revision of %title?', array(
'%title' => $node->title,
)), 'node/' . $node->nid . '/revisions', t('Publishing this revision will make it visible to the public.'), t('Publish'), t('Cancel'));
}
function revisioning_publish_confirm_submit($form, &$form_state) {
$nid = $form_state['values']['node_id'];
$title = $form_state['values']['title'];
$vid = $form_state['values']['revision'];
$type = $form_state['values']['type'];
_revisioning_publish_revision($nid, $vid, $title, $type);
$form_state['redirect'] = "node/{$nid}/revisions";
}
function revisioning_unpublish_confirm($form_state, $node) {
$form['node_id'] = array(
'#type' => 'value',
'#value' => $node->nid,
);
$form['title'] = array(
'#type' => 'value',
'#value' => $node->title,
);
$form['type'] = array(
'#type' => 'value',
'#value' => $node->type,
);
return confirm_form($form, t('Are you sure you want to unpublish %title?', array(
'%title' => $node->title,
)), "node/{$node->nid}/revisions", t('Unpublishing will remove this content from public view.'), t('Unpublish'), t('Cancel'));
}
function revisioning_unpublish_confirm_submit($form, &$form_state) {
$nid = $form_state['values']['node_id'];
$title = check_plain($form_state['values']['title']);
$type = check_plain($form_state['values']['type']);
db_query("UPDATE {node} SET status=0 WHERE nid=%d", $nid);
cache_clear_all();
drupal_set_message(t('%title has been unpublished.', array(
'%title' => $title,
)));
watchdog('content', 'Unpublished @type %title', array(
'@type' => $type,
'%title' => $title,
), WATCHDOG_NOTICE, l(t('view'), "node/{$nid}"));
$form_state['redirect'] = "node/{$nid}/revisions";
module_invoke_all('revisioning', 'unpublish');
}
function _revisioning_publish_revision($nid, $vid, $title, $type) {
db_query("UPDATE {node} SET vid=%d, title='%s', status=1 WHERE nid=%d", $vid, $title, $nid);
cache_clear_all();
drupal_set_message(t('Revision has been published.'));
watchdog('content', 'Published rev #%revision of @type %title', array(
'@type' => check_plain($type),
'%title' => check_plain($title),
'%revision' => $vid,
), WATCHDOG_NOTICE, l(t('view'), "node/{$nid}/revisions/{$vid}/view"));
module_invoke_all('revisioning', 'publish');
}
function revisioning_publish_latest_revision($node) {
$latest_pending = array_shift(_get_pending_revisions($node->nid));
if (!$latest_pending) {
$current_revision = _get_current_revision($node->nid);
if ($node->vid == $current_revision->vid && !$node->status) {
$latest_pending = $node;
}
}
if ($latest_pending) {
_revisioning_publish_revision($node->nid, $latest_pending->vid, $latest_pending->title, $latest_pending->type);
}
else {
drupal_set_message(t('"!title" has no pending revision to be published.', array(
'!title' => $node->title,
)), 'warning');
}
}
function revisioning_revert_confirm($form_state, $node) {
if (_number_of_pending_revisions($node->nid) > 0) {
drupal_set_message(t('There is a pending revision. Are you sure you want to revert to an older revision?'), 'warning');
}
return node_revision_revert_confirm($form_state, $node);
}
function revisioning_revert_confirm_submit($form, &$form_state) {
$node = $form['#node_revision'];
node_revision_revert_confirm_submit($form, $form_state);
db_query("UPDATE {node} SET status=1 WHERE nid=%d", $node->nid);
cache_clear_all();
module_invoke_all('revisioning', 'revert');
}
function revisioning_pending_nodes() {
return theme('revisioning_pending_nodes');
}
function revisioning_theme() {
return array(
'revisioning_pending_nodes' => array(
'arguments' => array(),
),
);
}
function theme_revisioning_pending_nodes() {
$nodes = get_nodes('update', TRUE);
return _theme_nodes($nodes);
}
function revisioning_node_revisions($node) {
return _theme_node_revisions($node);
}
function _theme_node_revisions($node) {
drupal_set_title(t('Revisions of %title', array(
'%title' => $node->title,
)));
$show_taxonomy_terms = module_exists('taxonomy');
$revisions = _get_all_revisions_for_node($node->nid, $show_taxonomy_terms);
$message = format_plural(count($revisions), 'This content has only one revision', 'This content has @count revisions.');
$links = array();
if ($node->status && module_grants_node_revision_access('unpublish current revision', $node)) {
$links[] = l(t('Unpublish current revision'), "node/{$node->nid}/unpublish");
}
if (module_grants_node_revision_access('delete revisions', $node)) {
$links[] = l(t('Delete all revisions'), "node/{$node->nid}/delete");
}
drupal_set_message(empty($links) ? $message : $message . theme('item_list', $links));
$header = array(
t('Revision'),
);
if ($show_taxonomy_terms) {
$header[] = t('Term');
}
$header[] = t('Status');
$rows = array();
foreach ($revisions as $revision) {
$row = array();
$base_url = "node/{$node->nid}/revisions/{$revision->vid}";
$t = t(' Saved !date by !username', array(
'!date' => l(format_date($revision->timestamp, 'small'), "{$base_url}/view"),
'!username' => theme('username', $revision),
)) . ($revision->log != '' ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : '');
if ($revision->vid == $node->vid) {
$row[] = array(
'data' => $t,
'class' => 'revision-current',
);
if ($show_taxonomy_terms) {
$row[] = array(
'data' => $revision->term,
'class' => 'revision-current',
);
}
$row[] = array(
'data' => theme('placeholder', $revision->status ? t('current revision (published)') : t('current revision (unpublished)')),
'class' => 'revision-current',
);
}
else {
$row[] = array(
'data' => $t,
);
if ($show_taxonomy_terms) {
$row[] = array(
'data' => $revision->term,
);
}
$row[] = array(
'data' => $revision->vid > $node->vid ? t('pending moderation') : t('old'),
);
}
$rows[] = $row;
}
return theme('table', $header, $rows);
}
function _number_of_revisions_newer_than($vid, $nid) {
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));
}
function _number_of_pending_revisions($nid) {
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));
}
function _get_pending_revisions($nid) {
$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";
$result = db_query($sql, $nid);
$revisions = array();
while ($revision = db_fetch_object($result)) {
$revisions[$revision->vid] = $revision;
}
return $revisions;
}
function _get_all_revisions_for_node($nid, $include_taxonomy_terms = FALSE) {
$sql_select = 'SELECT n.status, r.vid, r.title, r.log, r.uid, r.timestamp, u.name';
$sql_from = ' FROM {node_revisions} r LEFT JOIN {node} n ON n.vid=r.vid INNER JOIN {users} u ON u.uid=r.uid';
$sql_where = ' WHERE r.nid=%d ORDER BY r.vid DESC';
if ($include_taxonomy_terms) {
$sql_select .= ', td.name AS term';
$sql_from .= ' LEFT JOIN {term_node} tn ON r.vid=tn.vid LEFT JOIN {term_data} td ON tn.tid=td.tid';
}
$sql = $sql_select . $sql_from . $sql_where;
$result = db_query($sql, $nid);
$revisions = array();
while ($revision = db_fetch_object($result)) {
if (empty($revisions[$revision->vid])) {
$revisions[$revision->vid] = $revision;
}
elseif ($include_taxonomy_terms) {
$existing_revision = $revisions[$revision->vid];
$existing_revision->term .= '/' . $revision->term;
}
}
return $revisions;
}
function _handle_view_op($node) {
$args = arg();
$access_view = module_grants_node_revision_access('view revisions', $node);
$access_edit = module_grants_node_revision_access('edit revisions', $node);
$access_delete = module_grants_node_revision_access('delete revisions', $node);
$access_publish = $access_view && user_access('publish revisions');
$access_revert = $access_view && user_access('revert revisions');
$access_unpublish = $access_view && user_access('unpublish current revision');
$current_revision = _get_current_revision($node->nid);
$is_current = $node->vid == $current_revision->vid;
$is_pending = $node->vid > $current_revision->vid;
$links = array();
if ($access_view) {
$revision_author = user_load($node->revision_uid);
$published = $node->status ? t('current, published') : t('current, unpublished');
$placeholder_data = array(
'%current' => $published,
'%title' => check_plain($node->title),
'!author' => theme('username', $revision_author),
'@date' => format_date($node->revision_timestamp, 'small'),
);
if ($is_current) {
$message = t('Displaying %current revision of %title, last modified by !author on @date', $placeholder_data);
}
else {
$message = $is_pending ? t('Displaying pending revision of %title, last modified by !author on @date', $placeholder_data) : t('Displaying old revision of %title, last modified by !author on @date', $placeholder_data);
}
if (!$is_current && module_exists('diff')) {
$comparison_url = "node/{$node->nid}/revisions/view/";
if ($is_pending) {
$comparison_url .= "{$current_revision->vid}/{$node->vid}";
}
else {
$comparison_url .= "{$node->vid}/{$current_revision->vid}";
}
$links[] = l(t('Compare to current'), $comparison_url);
}
}
$base_url = "node/{$node->nid}/revisions/{$node->vid}";
if ($access_edit) {
$links[] = l(t('Edit this revision'), "{$base_url}/edit");
}
if ($access_publish && ($is_pending || $is_current && !$node->status)) {
$links[] = l(t('Publish this revision'), "{$base_url}/publish");
}
elseif ($access_revert && !$is_pending && !$is_current) {
$links[] = l(t('Revert to this revision'), "{$base_url}/revert");
}
if ($access_unpublish && $is_current && $node->status) {
$links[] = l(t('Unpublish this revision'), "node/{$node->nid}/unpublish");
}
if ($access_delete && !$is_current) {
$links[] = l(t('Delete this revision'), "{$base_url}/delete");
}
if ($access_view && $args[2] == 'revisions') {
$links[] = l(t('Show all revisions'), "node/{$node->nid}/revisions");
}
if ($message) {
drupal_set_message($message . theme('item_list', $links));
}
}
function _get_current_revision($nid) {
return db_fetch_object(db_query('SELECT r.vid, r.timestamp FROM {node} n INNER JOIN {node_revisions} r ON n.vid=r.vid WHERE n.nid=%d', $nid));
}