View source
<?php
define('REVISION_LIST_SIZE', 50);
function diff_help($section) {
switch ($section) {
case 'admin/help#diff':
$output = '<p>' . t('The diff module overwrites the normal revisions view. The revisions table is enhanced with a possibility to view the difference between two node revisions. Users with the %view_revisions permission will also be able to view the changes between any two selected revisions. You may disable this for individual content types on the content type configuration page. This module also provides a nifty %preview_changes button while editing a post.', array(
'%preview_changes' => t('Preview changes'),
'%view_revisions' => t('view revisions'),
)) . '</p>';
return $output;
}
}
function diff_requirements($phase) {
if ($phase == 'install') {
return;
}
$modules = array_keys(module_list());
if (array_search('diff', $modules) <= array_search('node', $modules)) {
diff_autoadjust();
}
}
function diff_menu($may_cache) {
$items = array();
if (!$may_cache) {
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
if ($node->nid) {
$revisions_access = (user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node) && db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', arg(1))) > 1;
$items[] = array(
'path' => 'node/' . arg(1) . '/revisions',
'title' => t('Revisions'),
'callback' => 'diff_diffs',
'access' => $revisions_access,
'weight' => 4,
'type' => MENU_LOCAL_TASK,
);
}
}
}
return $items;
}
function diff_autoadjust() {
$modules = array_keys(module_list());
if (array_search('diff', $modules) <= array_search('node', $modules)) {
module_load_install('diff');
diff_set_weight();
}
}
function diff_diffs() {
if (is_numeric(arg(1)) && arg(2) == 'revisions') {
$op = arg(3) ? arg(3) : 'overview';
switch ($op) {
case 'overview':
$node = node_load(arg(1));
if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) {
return diff_diffs_overview($node);
}
drupal_access_denied();
return;
case 'view':
if (is_numeric(arg(4)) && is_numeric(arg(5))) {
$node = node_load(arg(1));
if ($node->nid) {
if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) {
drupal_set_title(t('Diff for %title', array(
'%title' => $node->title,
)));
return diff_diffs_show($node, arg(4), arg(5));
}
drupal_access_denied();
return;
}
}
break;
default:
return node_revisions();
break;
}
}
drupal_not_found();
}
function diff_diffs_overview(&$node) {
$output = '';
drupal_set_title(t('Revisions for %title', array(
'%title' => $node->title,
)));
$output .= drupal_get_form('diff_node_revisions', $node);
return $output;
}
function diff_node_revisions(&$node) {
global $form_values;
$form = array();
$form['nid'] = array(
'#type' => 'hidden',
'#value' => $node->nid,
);
$revision_list = node_revision_list($node);
if (count($revision_list) > REVISION_LIST_SIZE) {
$page = isset($_GET['page']) ? $_GET['page'] : '0';
$revision_chunks = array_chunk(node_revision_list($node), REVISION_LIST_SIZE);
$revisions = $revision_chunks[$page];
global $pager_page_array, $pager_total, $pager_total_items;
$pager_total_items[0] = count($revision_list);
$pager_total[0] = ceil(count($revision_list) / REVISION_LIST_SIZE);
$pager_page_array[0] = max(0, min($page, (int) $pager_total[0] - 1));
}
else {
$revisions = $revision_list;
}
$revert_permission = FALSE;
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
$revert_permission = TRUE;
}
$delete_permission = FALSE;
if (user_access('administer nodes')) {
$delete_permission = TRUE;
}
foreach ($revisions as $revision) {
$operations = array();
$revision_ids[$revision->vid] = '';
if ($revision->current_vid > 0) {
$form['info'][$revision->vid] = array(
'#value' => t('!date by !username', array(
'!date' => l(format_date($revision->timestamp, 'small'), "node/{$node->nid}"),
'!username' => theme('username', $revision),
)) . ($revision->log != '' ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
);
}
else {
$form['info'][$revision->vid] = array(
'#value' => t('!date by !username', array(
'!date' => l(format_date($revision->timestamp, 'small'), "node/{$node->nid}/revisions/{$revision->vid}/view"),
'!username' => theme('username', $revision),
)) . ($revision->log != '' ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
);
if ($revert_permission) {
$operations[] = array(
'#value' => l(t('revert'), "node/{$node->nid}/revisions/{$revision->vid}/revert"),
);
}
if ($delete_permission) {
$operations[] = array(
'#value' => l(t('delete'), "node/{$node->nid}/revisions/{$revision->vid}/delete"),
);
}
$operations[] = array();
}
$form['operations'][$revision->vid] = $operations;
}
$new_vid = key($revision_ids);
next($revision_ids);
$old_vid = key($revision_ids);
$form['diff']['old'] = array(
'#type' => 'radios',
'#options' => $revision_ids,
'#default_value' => $old_vid,
);
$form['diff']['new'] = array(
'#type' => 'radios',
'#options' => $revision_ids,
'#default_value' => $new_vid,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Show diff'),
);
if (count($revision_list) > REVISION_LIST_SIZE) {
$form['#suffix'] = theme('pager', NULL, REVISION_LIST_SIZE, 0);
}
return $form;
}
function theme_diff_node_revisions($form) {
$header = array(
t('Revision'),
array(
'data' => drupal_render($form['submit']),
'colspan' => 2,
),
array(
'data' => t('Operations'),
'colspan' => 2,
),
);
if (isset($form['info']) && is_array($form['info'])) {
foreach (element_children($form['info']) as $key) {
$row = array();
if (isset($form['operations'][$key][0])) {
$row[] = drupal_render($form['info'][$key]);
$row[] = drupal_render($form['diff']['old'][$key]);
$row[] = drupal_render($form['diff']['new'][$key]);
$row[] = drupal_render($form['operations'][$key][0]);
$row[] = drupal_render($form['operations'][$key][1]);
$rows[] = $row;
}
else {
$row[] = array(
'data' => drupal_render($form['info'][$key]),
'class' => 'revision-current',
);
$row[] = array(
'data' => drupal_render($form['diff']['old'][$key]),
'class' => 'revision-current',
);
$row[] = array(
'data' => drupal_render($form['diff']['new'][$key]),
'class' => 'revision-current',
);
$row[] = array(
'data' => theme('placeholder', t('current revision')),
'class' => 'revision-current',
'colspan' => '2',
);
$rows[] = array(
'data' => $row,
'class' => 'error',
);
}
}
}
$output .= theme('table', $header, $rows);
$output .= drupal_render($form);
return $output;
}
function diff_node_revisions_submit($form_id, $form_values) {
$old_vid = min($form_values['old'], $form_values['new']);
$new_vid = max($form_values['old'], $form_values['new']);
return 'node/' . $form_values['nid'] . '/revisions/view/' . $old_vid . '/' . $new_vid;
}
function diff_node_revisions_validate($form_id, $form_values) {
$old_vid = $form_values['old'];
$new_vid = $form_values['new'];
if ($old_vid == $new_vid || !$old_vid || !$new_vid) {
form_set_error('diff', t('Select different revisions to compare.'));
}
}
function diff_diffs_show(&$node, $old_vid, $new_vid) {
$lame_revisions = node_revision_list($node);
foreach ($lame_revisions as $revision) {
$node_revisions[$revision->vid] = $revision;
}
$old_node = node_load($node->nid, $old_vid);
$new_node = node_load($node->nid, $new_vid);
$old_header = t('!date by !username', array(
'!date' => l(format_date($old_node->revision_timestamp), "node/{$node->nid}/revisions/{$old_node->vid}/view"),
'!username' => theme('username', $node_revisions[$old_vid]),
));
$new_header = t('!date by !username', array(
'!date' => l(format_date($new_node->revision_timestamp), "node/{$node->nid}/revisions/{$new_node->vid}/view"),
'!username' => theme('username', $node_revisions[$new_vid]),
));
$old_log = $old_node->log != '' ? '<p class="revision-log">' . filter_xss($old_node->log) . '</p>' : '';
$new_log = $new_node->log != '' ? '<p class="revision-log">' . filter_xss($new_node->log) . '</p>' : '';
$next_vid = _diff_get_next_vid($node_revisions, $new_vid);
if ($next_vid) {
$next_link = l(t('next diff >'), 'node/' . $node->nid . '/revisions/view/' . $new_vid . '/' . $next_vid);
}
else {
$next_link = '';
}
$prev_vid = _diff_get_previous_vid($node_revisions, $old_vid);
if ($prev_vid) {
$prev_link = l(t('< previous diff'), 'node/' . $node->nid . '/revisions/view/' . $prev_vid . '/' . $old_vid);
}
else {
$prev_link = '';
}
$output .= '<table class="diff">';
$output .= '<col class="diff-marker" /><col class="diff-content" /><col class="diff-marker" /><col class="diff-content" />';
$output .= '<thead><tr><th></th><th>' . $old_header . '</th><th></th><th>' . $new_header . '</th></tr></thead>';
if ($new_log || $old_log) {
$output .= '<tr><td></td><td>' . $old_log . '</td><td></td><td>' . $new_log . '</td></tr>';
}
$output .= '<tr><td></td><td class="diff-prevlink">' . $prev_link . '</td><td></td><td class="diff-nextlink">' . $next_link . '</td></tr>';
$output .= _diff_table_body($old_node, $new_node);
$output .= '</table>';
$output .= '<hr/>';
if ($node->vid == $new_vid) {
$output .= '<div class="diff-section-title">' . t('Current revision:') . '</div>';
}
else {
$output .= '<div class="diff-section-title">' . t('Revision of !new_date:', array(
'!new_date' => format_date($new_node->revision_timestamp),
)) . '</div>';
}
$output .= node_view($new_node, FALSE, FALSE, FALSE);
return $output;
}
function _diff_table_body(&$old_node, &$new_node) {
drupal_add_css(drupal_get_path('module', 'diff') . '/diff.css', 'module', 'all', FALSE);
include_once 'DiffEngine.php';
include_once 'node.inc';
if (module_exists('upload')) {
include_once 'upload.inc';
}
if (module_exists('content')) {
include_once 'cck.inc';
}
$output = '<tbody>';
$any_visible_change = false;
$node_diffs = module_invoke_all('diff', $old_node, $new_node);
foreach ($node_diffs as $node_diff) {
$diff = new Diff($node_diff['old'], $node_diff['new']);
$formatter = new TableDiffFormatter();
if (isset($node_diff['format'])) {
$formatter->show_header = $node_diff['format']['show_header'];
}
$formatter_output = $formatter
->format($diff);
if ($formatter_output) {
$output .= '<tr><td colspan="4" class="diff-section-title">' . t('Changes to %name', array(
'%name' => $node_diff['name'],
)) . '</td></tr>';
$output .= $formatter_output;
$any_visible_change = true;
}
}
if (!$any_visible_change) {
$output .= '<tr><td colspan="4" class="diff-section-title">' . t('No visible changes') . '</td></tr>';
}
$output .= '</tbody>';
return $output;
}
function _diff_get_next_vid(&$node_revisions, $vid) {
$previous = NULL;
foreach ($node_revisions as $revision) {
if ($revision->vid == $vid) {
return $previous ? $previous->vid : false;
}
$previous = $revision;
}
return false;
}
function _diff_get_previous_vid(&$node_revisions, $vid) {
$previous = NULL;
foreach ($node_revisions as $revision) {
if ($previous && $previous->vid == $vid) {
return $revision->vid;
}
$previous = $revision;
}
return false;
}
function diff_form_alter($form_id, &$form) {
if (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
if (variable_get('show_preview_changes_' . $form['type']['#value'], TRUE) && $form['nid']['#value'] > 0) {
$form['preview_changes'] = array(
'#type' => 'button',
'#value' => t('Preview changes'),
'#weight' => 41,
);
$form['#theme'] = 'diff_node_form';
if (isset($form['#after_build']) && is_array($form['#after_build'])) {
$form['#after_build'][] = 'diff_node_form_add_changes';
}
else {
$form['#after_build'] = array(
'diff_node_form_add_changes',
);
}
}
}
elseif ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
$form['workflow']['show_preview_changes'] = array(
'#type' => 'checkbox',
'#title' => t('Show %preview_changes button on node edit form', array(
'%preview_changes' => t('Preview changes'),
)),
'#prefix' => '<strong>' . t('Preview changes') . '</strong>',
'#weight' => 10,
'#default_value' => variable_get('show_preview_changes_' . $form['#node_type']->type, TRUE),
);
}
}
function diff_node_form_add_changes($form) {
global $form_values;
$op = isset($form_values['op']) ? $form_values['op'] : '';
if ($op == t('Preview changes')) {
$node = (object) $form_values;
$changes = '<table class="diff">';
$changes .= '<col class="diff-marker" /><col class="diff-content" /><col class="diff-marker" /><col class="diff-content" />';
$changes .= _diff_table_body(node_load($form_values['nid']), $node);
$changes .= '</table>';
$form['#prefix'] = isset($form['#prefix']) ? $changes . $form['#prefix'] : $changes;
}
return $form;
}
function theme_diff_node_form($form) {
$output = "\n<div class=\"node-form\">\n";
$admin = '';
if (isset($form['author'])) {
$admin .= " <div class=\"authored\">\n";
$admin .= drupal_render($form['author']);
$admin .= " </div>\n";
}
if (isset($form['options'])) {
$admin .= " <div class=\"options\">\n";
$admin .= drupal_render($form['options']);
$admin .= " </div>\n";
}
$buttons = drupal_render($form['preview']);
$buttons .= isset($form['preview_changes']) ? drupal_render($form['preview_changes']) : '';
$buttons .= drupal_render($form['submit']);
$buttons .= isset($form['delete']) ? drupal_render($form['delete']) : '';
$output .= " <div class=\"standard\">\n";
$output .= drupal_render($form);
$output .= " </div>\n";
if (!empty($admin)) {
$output .= " <div class=\"admin\">\n";
$output .= $admin;
$output .= " </div>\n";
}
$output .= $buttons;
$output .= "</div>\n";
return $output;
}