View source
<?php
define('REVISION_LIST_SIZE', 50);
function diff_help($path, $arg) {
switch ($path) {
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('View changes'),
'%view_revisions' => t('view revisions'),
)) . '</p>';
return $output;
case 'node/%/revisions/%/view':
return '<p>' . t('The revisions let you track differences between multiple versions of a post.') . '</p>';
case 'node/%/revisions/view/%/%':
return '<p>' . t('Comparing two revisions:') . '</p>';
}
}
function diff_menu() {
$items = array();
$items['node/%node/revisions/list'] = array(
'title' => 'List revisions',
'page callback' => 'diff_diffs_overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'access callback' => 'diff_node_revision_access',
'access arguments' => array(
1,
),
'file' => 'diff.pages.inc',
);
$items['node/%node/revisions/view/%/%'] = array(
'title' => 'Diff',
'page callback' => 'diff_diffs_show',
'page arguments' => array(
1,
4,
5,
),
'type' => MENU_LOCAL_TASK,
'access callback' => 'diff_node_revision_access',
'access arguments' => array(
1,
),
'tab_parent' => 'node/%/revisions/list',
'file' => 'diff.pages.inc',
);
$items['node/%node/revisions/view/latest'] = array(
'title' => 'Show latest diff',
'page callback' => 'diff_latest',
'page arguments' => array(
1,
),
'type' => MENU_LOCAL_TASK,
'access callback' => '_node_revision_access',
'access arguments' => array(
1,
),
'tab_parent' => 'node/%/revisions/view',
'file' => 'diff.pages.inc',
);
$items['node/%node/revisions/diff-inline'] = array(
'page callback' => 'diff_inline_ahah',
'page arguments' => array(
1,
),
'type' => MENU_CALLBACK,
'access callback' => 'diff_node_revision_access',
'access arguments' => array(
1,
),
'file' => 'diff.pages.inc',
);
return $items;
}
function diff_menu_alter(&$callbacks) {
$callbacks['node/%node/revisions']['page callback'] = 'diff_diffs_overview';
$callbacks['node/%node/revisions']['module'] = 'diff';
$callbacks['node/%node/revisions']['file'] = 'diff.pages.inc';
$callbacks['node/%node/revisions/%/view']['tab_parent'] = 'node/%/revisions/list';
$callbacks['node/%node/revisions/%/revert']['tab_parent'] = 'node/%/revisions/%/view';
$callbacks['node/%node/revisions/%/delete']['tab_parent'] = 'node/%/revisions/%/view';
return;
}
function diff_node_revision_access($node, $op = 'view') {
$may_revision_this_type = variable_get('enable_revisions_page_' . $node->type, TRUE) || user_access('administer nodes');
return $may_revision_this_type && _node_revision_access($node, $op);
}
function diff_block($op = 'list', $delta = 0, $edit = array()) {
if ($op === 'list') {
return array(
'inline' => array(
'info' => t('Inline diff'),
),
);
}
elseif ($op === 'view' && $delta === 'inline' && user_access('view revisions') && ($node = menu_get_object())) {
$block = array();
$revisions = node_revision_list($node);
if (count($revisions) > 1) {
$block['subject'] = t('Highlight changes');
$block['content'] = drupal_get_form('diff_inline_form', $node, $revisions);
}
return $block;
}
}
function diff_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
if ($page && $op == 'view' && user_access('view revisions') && variable_get('show_diff_inline_' . $node->type, FALSE)) {
if (arg(2) === 'revisions' && arg(3) === $node->vid) {
module_load_include('inc', 'diff', 'diff.pages');
$node->content = array(
'#value' => diff_inline_show($node, $node->vid),
);
}
$node->content['#prefix'] = isset($node->content['#prefix']) ? "<div id='diff-inline-{$node->nid}'>" . $node->content['#prefix'] : "<div id='diff-inline-{$node->nid}'>";
$node->content['#suffix'] = isset($node->content['#suffix']) ? $node->content['#suffix'] . "</div>" : "</div>";
}
}
function diff_form_alter(&$form, $form_state, $form_id) {
if (isset($form['type']['#value']) && $form['type']['#value'] . '_node_form' == $form_id) {
if (variable_get('show_preview_changes_' . $form['type']['#value'], TRUE) && $form['nid']['#value'] > 0) {
$form['buttons']['preview_changes'] = array(
'#type' => 'submit',
'#value' => t('View changes'),
'#weight' => 12,
'#submit' => array(
'diff_node_form_build_preview_changes',
),
);
}
}
elseif ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
$form['workflow']['diff'] = array(
'#title' => t('Diff'),
'#type' => 'item',
'#tree' => FALSE,
);
$form['workflow']['diff']['show_preview_changes'] = array(
'#type' => 'checkbox',
'#title' => t('Show %preview_changes button on node edit form', array(
'%preview_changes' => t('View changes'),
)),
'#weight' => 10,
'#default_value' => variable_get('show_preview_changes_' . $form['#node_type']->type, TRUE),
);
$form['workflow']['diff']['remove_markup_default'] = array(
'#type' => 'checkbox',
'#title' => t('Remove markup by default when comparing body text'),
'#weight' => 10,
'#default_value' => variable_get('remove_markup_default_' . $form['#node_type']->type, FALSE),
);
$form['workflow']['diff']['show_diff_inline'] = array(
'#type' => 'checkbox',
'#title' => t('Show diffs inline for this content type'),
'#description' => t("You must enable the 'Inline diff' block to use this feature"),
'#weight' => 10,
'#default_value' => variable_get('show_diff_inline_' . $form['#node_type']->type, FALSE),
);
$form['workflow']['diff']['enable_revisions_page'] = array(
'#type' => 'checkbox',
'#title' => t('Enable the %revisions page for this content type', array(
'%revisions' => t('Revisions'),
)),
'#weight' => 11,
'#default_value' => variable_get('enable_revisions_page_' . $form['#node_type']->type, TRUE),
);
}
}
function diff_node_form_build_preview_changes($form, &$form_state) {
module_load_include('inc', 'diff', 'diff.pages');
$node = node_form_submit_build_node($form, $form_state);
$rows = _diff_body_rows(node_load($form_state['values']['nid']), $node, variable_get('remove_markup_default_' . $node->type, FALSE));
$cols = _diff_default_cols();
$header = _diff_default_header();
$changes = theme('diff_table', $header, $rows, array(
'class' => 'diff',
), NULL, $cols);
$form_state['node_preview'] = isset($form_state['node_preview']) ? $changes . $form_state['node_preview'] : $changes;
}
function diff_theme() {
return array(
'diff_node_revisions' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'diff.theme.inc',
),
'diff_table' => array(
'arguments' => array(
'header' => NULL,
'rows' => NULL,
'attributes' => array(),
'caption' => NULL,
'cols' => array(),
),
'file' => 'diff.theme.inc',
),
'diff_header_line' => array(
'arguments' => array(
'lineno' => NULL,
),
'file' => 'diff.theme.inc',
),
'diff_content_line' => array(
'arguments' => array(
'line' => NULL,
),
'file' => 'diff.theme.inc',
),
'diff_empty_line' => array(
'arguments' => array(
'line' => NULL,
),
'file' => 'diff.theme.inc',
),
'diff_inline_form' => array(
'arguments' => array(
'form' => array(),
),
'file' => 'diff.theme.inc',
),
'diff_inline_metadata' => array(
'arguments' => array(
'node' => NULL,
),
'file' => 'diff.theme.inc',
),
'diff_inline_chunk' => array(
'arguments' => array(
'text' => '',
'type' => NULL,
),
'file' => 'diff.theme.inc',
),
);
}
function diff_get_rows($a, $b, $show_header = FALSE) {
$a = is_array($a) ? $a : explode("\n", $a);
$b = is_array($b) ? $b : explode("\n", $b);
module_load_include('php', 'diff', 'DiffEngine');
$formatter = new DrupalDiffFormatter();
$formatter->show_header = $show_header;
$diff = new Diff($a, $b);
return $formatter
->format($diff);
}
function diff_get_inline($a, $b) {
module_load_include('php', 'diff', 'DiffEngine');
$diff = new DrupalDiffInline($a, $b);
return $diff
->render();
}
function diff_inline_form($form_state, $node, $revisions) {
$form = array();
$form['node'] = array(
'#type' => 'value',
'#value' => $node,
);
$form['revision'] = array(
'#type' => 'select',
'#options' => array(
0 => '< ' . t('No highlighting') . ' >',
),
'#default_value' => arg(2) === 'revisions' && arg(3) === $node->vid ? $node->vid : 0,
'#ahah' => array(
'path' => "node/{$node->nid}/revisions/diff-inline",
'wrapper' => "diff-inline-{$node->nid}",
'method' => 'replace',
),
);
foreach ($revisions as $revision) {
$form['revision']['#options'][$revision->vid] = t('@revision by @name', array(
'@revision' => format_date($revision->timestamp, 'small'),
'@name' => $revision->name,
));
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('View'),
'#submit' => array(
'diff_inline_form_submit',
),
'#attributes' => array(
'class' => 'diff-js-hidden',
),
);
return $form;
}
function diff_inline_form_submit(&$form, &$form_state) {
if (isset($form_state['values']['revision'], $form_state['values']['node'])) {
$node = $form_state['values']['node'];
$vid = $form_state['values']['revision'];
$form_state['redirect'] = "node/{$node->nid}/revisions/{$vid}/view";
}
}