diff.pages.inc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <?php
  2. /**
  3. * @file
  4. * Menu callbacks for hook_menu().
  5. */
  6. /**
  7. * Menu callback - show latest diff for a given node.
  8. */
  9. function diff_latest($node) {
  10. $revisions = node_revision_list($node);
  11. if (count($revisions) < 2 || !diff_node_revision_access($node, 'view')) {
  12. drupal_goto('node/' . $node->nid);
  13. }
  14. $new = array_shift($revisions);
  15. $old = array_shift($revisions);
  16. drupal_goto("node/{$node->nid}/revisions/view/{$old->vid}/{$new->vid}");
  17. }
  18. /**
  19. * Menu callback - an overview table of older revisions.
  20. *
  21. * Generate an overview table of older revisions of a node and provide
  22. * an input form to select two revisions for a comparison.
  23. */
  24. function diff_diffs_overview($node) {
  25. drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH);
  26. return drupal_get_form('diff_node_revisions', $node);
  27. }
  28. /**
  29. * Input form to select two revisions.
  30. */
  31. function diff_node_revisions($form, $form_state, $node) {
  32. $form['nid'] = array(
  33. '#type' => 'hidden',
  34. '#value' => $node->nid,
  35. );
  36. $revision_list = diff_node_revision_list($node);
  37. $revision_count = count($revision_list);
  38. if ($revision_count > REVISION_LIST_SIZE) {
  39. // If the list of revisions is longer than the number shown on one page
  40. // split the array.
  41. $page = isset($_GET['page']) ? $_GET['page'] : '0';
  42. $revision_chunks = array_chunk($revision_list, REVISION_LIST_SIZE);
  43. $revisions = $revision_chunks[$page];
  44. // Set up global pager variables as would 'pager_query' do.
  45. // These variables are then used in the theme('pager') call later.
  46. global $pager_page_array, $pager_total, $pager_total_items;
  47. $pager_total_items[0] = $revision_count;
  48. $pager_total[0] = ceil($revision_count / REVISION_LIST_SIZE);
  49. $pager_page_array[0] = max(0, min($page, ((int) $pager_total[0]) - 1));
  50. }
  51. else {
  52. $revisions = $revision_list;
  53. }
  54. $revert_permission = FALSE;
  55. if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
  56. $revert_permission = TRUE;
  57. }
  58. $delete_permission = FALSE;
  59. if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
  60. $delete_permission = TRUE;
  61. }
  62. foreach ($revisions as $revision) {
  63. $operations = array();
  64. $revision_ids[$revision->vid] = '';
  65. $revision_log = ($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : '';
  66. if ($revision->current_vid > 0) {
  67. $form['info'][$revision->vid] = array(
  68. '#markup' => t('!date by !username', array(
  69. '!date' => l(format_date($revision->timestamp, 'small'), "node/$node->nid"),
  70. '!username' => theme('username', array('account' => $revision)))) . $revision_log,
  71. '#revision' => $revision,
  72. );
  73. }
  74. else {
  75. $diff_date = l(format_date($revision->timestamp, 'small'), "node/$node->nid/revisions/$revision->vid/view");
  76. $form['info'][$revision->vid] = array(
  77. '#markup' => t('!date by !username', array(
  78. '!date' => $diff_date,
  79. '!username' => theme('username', array('account' => $revision)))
  80. ) . $revision_log,
  81. '#revision' => $revision,
  82. );
  83. if ($revert_permission) {
  84. $operations[] = array(
  85. '#markup' => l(t('Revert'), "node/$node->nid/revisions/$revision->vid/revert"),
  86. );
  87. }
  88. if ($delete_permission) {
  89. $operations[] = array(
  90. '#markup' => l(t('Delete'), "node/$node->nid/revisions/$revision->vid/delete"),
  91. );
  92. }
  93. // Set a dummy, even if the user has no permission for the other
  94. // operations, so that we can check if the operations array is
  95. // empty to know if the row denotes the current revision.
  96. $operations[] = array();
  97. }
  98. $form['operations'][$revision->vid] = $operations;
  99. }
  100. $new_vid = key($revision_ids);
  101. next($revision_ids);
  102. $old_vid = key($revision_ids);
  103. $form['diff']['old'] = array(
  104. '#type' => 'radios',
  105. '#options' => $revision_ids,
  106. '#default_value' => $old_vid,
  107. );
  108. $form['diff']['new'] = array(
  109. '#type' => 'radios',
  110. '#options' => $revision_ids,
  111. '#default_value' => $new_vid,
  112. );
  113. $form['submit'] = array('#type' => 'submit', '#value' => t('Compare'));
  114. if ($revision_count > REVISION_LIST_SIZE) {
  115. $form['#suffix'] = theme('pager');
  116. }
  117. $form['#attached'] = diff_build_attachments(TRUE);
  118. return $form;
  119. }
  120. /**
  121. * Validation for input form to select two revisions.
  122. */
  123. function diff_node_revisions_validate($form, &$form_state) {
  124. $old_vid = $form_state['values']['old'];
  125. $new_vid = $form_state['values']['new'];
  126. if ($old_vid == $new_vid || !$old_vid || !$new_vid) {
  127. form_set_error('diff', t('Select different revisions to compare.'));
  128. }
  129. }
  130. /**
  131. * Submit code for input form to select two revisions.
  132. */
  133. function diff_node_revisions_submit($form, &$form_state) {
  134. // The ids are ordered so the old revision is always on the left.
  135. $old_vid = min($form_state['values']['old'], $form_state['values']['new']);
  136. $new_vid = max($form_state['values']['old'], $form_state['values']['new']);
  137. if (isset($_GET['destination'])) {
  138. unset($_GET['destination']);
  139. }
  140. $form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/revisions/view/' . $old_vid . '/' . $new_vid;
  141. }
  142. /**
  143. * Create a comparison for the node between versions 'old_vid' and 'new_vid'.
  144. *
  145. * @param object $node
  146. * Node on which to perform comparison.
  147. * @param int $old_vid
  148. * Version ID of the old revision.
  149. * @param int $new_vid
  150. * Version ID of the new revision.
  151. */
  152. function diff_diffs_show($node, $old_vid, $new_vid, $state = NULL) {
  153. // Attaches the CSS.
  154. $build['#attached'] = diff_build_attachments();
  155. $default_state = variable_get('diff_default_state_node', 'raw');
  156. if (empty($state)) {
  157. $state = $default_state;
  158. }
  159. $state = str_replace('-', '_', $state);
  160. if (!array_key_exists($state, diff_available_states())) {
  161. $state = $default_state;
  162. }
  163. // Same title as the 'Revisions' tab. Blocked by non-page requests.
  164. if (node_is_page($node)) {
  165. drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH);
  166. }
  167. $node_revisions = node_revision_list($node);
  168. $old_node = node_load($node->nid, $old_vid);
  169. $new_node = node_load($node->nid, $new_vid);
  170. // Generate table header (date, username, log message).
  171. $old_header = t('!date by !username', array(
  172. '!date' => l(format_date($old_node->revision_timestamp), "node/$node->nid/revisions/$old_node->vid/view", array('absolute' => 1)),
  173. '!username' => theme('username', array('account' => $node_revisions[$old_vid])),
  174. ));
  175. $new_header = t('!date by !username', array(
  176. '!date' => l(format_date($new_node->revision_timestamp), "node/$node->nid/revisions/$new_node->vid/view", array('absolute' => 1)),
  177. '!username' => theme('username', array('account' => $node_revisions[$new_vid])),
  178. ));
  179. $old_log = $old_node->log != '' ? '<p class="revision-log">' . filter_xss($old_node->log) . '</p>' : '';
  180. $new_log = $new_node->log != '' ? '<p class="revision-log">' . filter_xss($new_node->log) . '</p>' : '';
  181. // Generate previous diff/next diff links.
  182. $nav_suffix = ($default_state != $state) ? '/' . str_replace('_', '-', $state) : '';
  183. $next_vid = _diff_get_next_vid($node_revisions, $new_vid);
  184. if ($next_vid) {
  185. $next_link = l(t('Next difference >'), 'node/' . $node->nid . '/revisions/view/' . $new_vid . '/' . $next_vid . $nav_suffix, array('absolute' => 1));
  186. }
  187. else {
  188. $next_link = '';
  189. }
  190. $prev_vid = _diff_get_previous_vid($node_revisions, $old_vid);
  191. if ($prev_vid) {
  192. $prev_link = l(t('< Previous difference'), 'node/' . $node->nid . '/revisions/view/' . $prev_vid . '/' . $old_vid . $nav_suffix, array('absolute' => 1));
  193. }
  194. else {
  195. $prev_link = '';
  196. }
  197. $header = _diff_default_header($old_header, $new_header);
  198. $rows = array();
  199. if ($old_log || $new_log) {
  200. $rows['logs'] = array(
  201. array(
  202. 'data' => $old_log,
  203. 'colspan' => 2,
  204. ),
  205. array(
  206. 'data' => $new_log,
  207. 'colspan' => 2,
  208. ),
  209. );
  210. }
  211. $rows['navigation'] = array(
  212. array(
  213. 'data' => $prev_link,
  214. 'class' => array('diff-prevlink'),
  215. 'colspan' => 2,
  216. ),
  217. array(
  218. 'data' => $next_link,
  219. 'class' => array('diff-nextlink'),
  220. 'colspan' => 2,
  221. ),
  222. );
  223. $links = array();
  224. foreach (diff_available_states('node') as $alternative_state => $label) {
  225. if ($alternative_state == $state) {
  226. // @todo: Should we show both or just alternatives?
  227. }
  228. $links[$alternative_state] = array(
  229. 'title' => $label,
  230. 'href' => "node/{$node->nid}/revisions/view/{$old_vid}/{$new_vid}" . ($alternative_state == $default_state ? '' : '/' . str_replace('_', '-', $alternative_state)),
  231. );
  232. }
  233. if (count($links) > 1) {
  234. $state_links = theme('links', array(
  235. 'links' => $links,
  236. 'attributes' => array('class' => array('links', 'inline')),
  237. ));
  238. $rows['states'] = array(
  239. array(
  240. 'data' => $state_links,
  241. 'class' => 'diff-links',
  242. 'colspan' => 4,
  243. ),
  244. );
  245. }
  246. $rows = array_merge($rows, _diff_body_rows($old_node, $new_node, $state));
  247. $build['diff_table'] = array(
  248. '#theme' => 'table__diff__standard',
  249. '#header' => $header,
  250. '#rows' => $rows,
  251. '#attributes' => array('class' => array('diff')),
  252. '#colgroups' => _diff_default_cols(),
  253. '#sticky' => FALSE,
  254. );
  255. // Allow users to hide or set the display mode of the preview.
  256. if (node_is_page($node) && $view_mode = variable_get('diff_view_mode_preview_node_' . $new_node->type, 'full')) {
  257. $header = '';
  258. if ($node->vid == $new_vid) {
  259. $header .= '<div class="diff-section-title">' . t('Current revision:') . '</div>';
  260. }
  261. else {
  262. $header .= '<div class="diff-section-title">' . t('Revision of @new_date:', array('@new_date' => format_date($new_node->revision_timestamp))) . '</div>';
  263. }
  264. $build['diff_preview']['header']['#markup'] = $header;
  265. // Don't include node links or comments when viewing the diff.
  266. $build['diff_preview']['content'] = function_exists('entity_view') ? entity_view('node', array($new_node), $view_mode) : node_view($new_node, $view_mode);
  267. if (isset($build['diff_preview']['content']['links'])) {
  268. unset($build['diff_preview']['content']['links']);
  269. }
  270. if (isset($build['diff_preview']['content']['comments'])) {
  271. unset($build['diff_preview']['content']['comments']);
  272. }
  273. }
  274. return $build;
  275. }
  276. /**
  277. * Creates an array of rows which represent the difference between two entities.
  278. *
  279. * @param object $left_entity
  280. * Entity for comparison which will be displayed on the left side.
  281. * @param object $right_entity
  282. * Entity for comparison which will be displayed on the right side.
  283. * @param array $context
  284. * The context used to render the diff.
  285. */
  286. function diff_entity_body_rows($entity_type, $left_entity, $right_entity, $context = array()) {
  287. // This is an unique index only, so no need for drupal_static().
  288. static $table_row_counter = 0;
  289. if ($theme = variable_get('diff_theme', 'default')) {
  290. drupal_add_css(drupal_get_path('module', 'diff') . "/css/diff.{$theme}.css");
  291. }
  292. $rows = array();
  293. $any_visible_change = FALSE;
  294. $context += array(
  295. 'entity_type' => $entity_type,
  296. 'states' => array('raw'),
  297. 'view_mode' => 'diff_standard',
  298. );
  299. $state = current($context['states']);
  300. $entity_diffs = diff_compare_entities($left_entity, $right_entity, $context);
  301. // Track line numbers between multiple diffs.
  302. $line_stats = array(
  303. 'counter' => array('x' => 0, 'y' => 0),
  304. 'offset' => array('x' => 0, 'y' => 0),
  305. );
  306. // Render diffs for each.
  307. foreach ($entity_diffs as $entity_diff) {
  308. $show_header = !empty($entity_diff['#name']);
  309. // These are field level settings.
  310. if ($show_header && isset($entity_diff['#settings']['show_header'])) {
  311. $show_header = $show_header && $entity_diff['#settings']['show_header'];
  312. }
  313. // Line counting and line header options.
  314. if (empty($entity_diff['#settings']['line_counter'])) {
  315. $line_counter = FALSE;
  316. }
  317. else {
  318. $line_counter = $entity_diff['#settings']['line_counter'];
  319. }
  320. // Every call to 'line' resets the counters.
  321. if ($line_counter) {
  322. $line_stats['counter']['x'] = 0;
  323. $line_stats['counter']['y'] = 0;
  324. if ($line_counter == 'line' && 0) {
  325. $line_stats['offset']['x'] = 0;
  326. $line_stats['offset']['y'] = 0;
  327. }
  328. $line_stats_ref = $line_stats;
  329. }
  330. else {
  331. $line_stats_ref = NULL;
  332. }
  333. list($left, $right) = diff_extract_state($entity_diff, $state);
  334. if ($entity_diff_rows = diff_get_rows($left, $right, $line_counter && $line_counter != 'hidden', $line_stats_ref)) {
  335. if ($line_counter && $line_counter != 'line') {
  336. $line_stats['offset']['x'] += $line_stats_ref['counter']['x'];
  337. $line_stats['offset']['y'] += $line_stats_ref['counter']['y'];
  338. }
  339. if ($show_header) {
  340. $rows['diff-header-' . $table_row_counter++] = array(
  341. array(
  342. 'data' => t('Changes to %name', array('%name' => $entity_diff['#name'])),
  343. 'class' => 'diff-section-title',
  344. 'colspan' => 4,
  345. ),
  346. );
  347. }
  348. // To avoid passing counter to the Diff engine, index rows manually here
  349. // to allow modules to interact with the table. i.e. no array_merge().
  350. foreach ($entity_diff_rows as $row) {
  351. $rows['diff-row-' . $table_row_counter++] = $row;
  352. }
  353. $any_visible_change = TRUE;
  354. }
  355. }
  356. if (!$any_visible_change) {
  357. $rows['diff-empty-' . $table_row_counter++] = array(
  358. array(
  359. 'data' => t('No visible changes'),
  360. 'class' => 'diff-section-title',
  361. 'colspan' => 4,
  362. ),
  363. );
  364. }
  365. return $rows;
  366. }
  367. /**
  368. * Creates an array of rows which represent the difference between nodes.
  369. *
  370. * @param object $old_node
  371. * Node for comparison which will be displayed on the left side.
  372. * @param object $new_node
  373. * Node for comparison which will be displayed on the right side.
  374. * @param bool $state
  375. * The state to render for the diff.
  376. */
  377. function _diff_body_rows($old_node, $new_node, $state = 'raw') {
  378. $context = array(
  379. 'states' => array($state),
  380. 'view_mode' => 'diff_standard',
  381. );
  382. return diff_entity_body_rows('node', $old_node, $new_node, $context);
  383. }
  384. /**
  385. * Generic callback to compare two entities.
  386. */
  387. function diff_compare_entities($left_entity, $right_entity, $context) {
  388. $entity_type = $context['entity_type'];
  389. list(, , $bundle) = entity_extract_ids($entity_type, $right_entity);
  390. $context['bundle'] = $bundle;
  391. $context['old_entity'] = $left_entity;
  392. $context['new_entity'] = $right_entity;
  393. $context += array(
  394. 'states' => array('raw'),
  395. 'view_mode' => FALSE,
  396. 'language' => LANGUAGE_NONE,
  397. );
  398. $diff = module_invoke_all('entity_diff', $left_entity, $right_entity, $context);
  399. // Allow other modules to interact directly with the results.
  400. drupal_alter('entity_diff', $diff, $context);
  401. // We start off assuming all form elements are in the correct order.
  402. $diff['#sorted'] = TRUE;
  403. // Field rows. Recurse through all child elements.
  404. $count = 0;
  405. foreach (element_children($diff) as $key) {
  406. if (!isset($diff[$key]['#states'])) {
  407. $diff[$key]['#states'] = array();
  408. }
  409. // Ensure that the element follows the new #states format.
  410. if (isset($diff[$key]['#old'])) {
  411. $diff[$key]['#states']['raw']['#old'] = $diff[$key]['#old'];
  412. unset($diff[$key]['#old']);
  413. }
  414. if (isset($diff[$key]['#new'])) {
  415. $diff[$key]['#states']['raw']['#new'] = $diff[$key]['#new'];
  416. unset($diff[$key]['#new']);
  417. }
  418. // If requested, we can convert the .
  419. foreach (array('raw', 'rendered') as $state) {
  420. if (in_array($state . '_plain', $context['states'])) {
  421. diff_markdown_state($diff[$key], $state);
  422. }
  423. }
  424. // Assign a decimal placeholder weight to preserve original array order.
  425. if (!isset($diff[$key]['#weight'])) {
  426. $diff[$key]['#weight'] = $count / 1000;
  427. }
  428. else {
  429. // If one child element has a weight then we will need to sort later.
  430. unset($diff['#sorted']);
  431. }
  432. $count++;
  433. }
  434. // One of the children has a #weight.
  435. if (!isset($diff['#sorted'])) {
  436. uasort($diff, 'element_sort');
  437. }
  438. else {
  439. unset($diff['#sorted']);
  440. }
  441. // Process the array and get line counts per field.
  442. array_walk($diff, 'diff_process_state_lines');
  443. return $diff;
  444. }
  445. /**
  446. * Helper function to get line counts per field.
  447. */
  448. function diff_process_state_lines(&$diff, $key) {
  449. foreach ($diff['#states'] as $state => $data) {
  450. if (isset($data['#old'])) {
  451. if (is_string($data['#old'])) {
  452. $diff['#states'][$state]['#old'] = explode("\n", $data['#old']);
  453. }
  454. $diff['#states'][$state]['#count_old'] = count($diff['#states'][$state]['#old']);
  455. }
  456. else {
  457. $diff['#states'][$state]['#count_old'] = 0;
  458. }
  459. if (isset($data['#new'])) {
  460. if (is_string($data['#new'])) {
  461. $diff['#states'][$state]['#new'] = explode("\n", $data['#new']);
  462. }
  463. $diff['#states'][$state]['#count_new'] = count($diff['#states'][$state]['#new']);
  464. }
  465. else {
  466. $diff['#states'][$state]['#count_new'] = 0;
  467. }
  468. }
  469. }
  470. /**
  471. * Helper function to render plain states from the corresponding raw state.
  472. *
  473. * @param array $diff
  474. * The Diff Engine output array.
  475. * @param string $state
  476. * The state to markdown.
  477. */
  478. function diff_markdown_state(&$diff, $state) {
  479. list($plain_old, $plain_new) = diff_extract_state($diff, $state . '_plain');
  480. list($old, $new) = diff_extract_state($diff, $state);
  481. $markdown = FALSE;
  482. if (isset($diff['#settings']) && !empty($diff['#settings']['markdown'])) {
  483. if (function_exists($diff['#settings']['markdown'])) {
  484. $markdown = $diff['#settings']['markdown'];
  485. }
  486. }
  487. if (!isset($plain_old) && isset($old)) {
  488. $diff['#states'][$state . '_plain']['#old'] = _diff_apply_markdown($markdown, $old);
  489. }
  490. if (!isset($plain_new) && isset($new)) {
  491. $diff['#states'][$state . '_plain']['#new'] = _diff_apply_markdown($markdown, $new);
  492. }
  493. }
  494. /**
  495. * Helper function to clear newlines from the content.
  496. */
  497. function _diff_apply_markdown($markdown, $items) {
  498. if (!$markdown) {
  499. return $items;
  500. }
  501. if (is_array($items)) {
  502. $items = array_map($markdown, $items);
  503. foreach ($items as &$item) {
  504. $item = trim($item, "\n");
  505. }
  506. return $items;
  507. }
  508. else {
  509. return trim($markdown($items), "\n");
  510. }
  511. }
  512. /**
  513. * Get the entry in the revisions list after $vid.
  514. *
  515. * @param array $node_revisions
  516. * Array of node revision IDs in descending order.
  517. * @param int $vid
  518. * Version ID to look for.
  519. *
  520. * @return bool|int
  521. * Returns FALSE if $vid is the last entry.
  522. */
  523. function _diff_get_next_vid($node_revisions, $vid) {
  524. $previous = NULL;
  525. foreach ($node_revisions as $revision) {
  526. if ($revision->vid == $vid) {
  527. return ($previous ? $previous->vid : FALSE);
  528. }
  529. $previous = $revision;
  530. }
  531. return FALSE;
  532. }
  533. /**
  534. * Get the entry in the revision list before $vid.
  535. *
  536. * @param array $node_revisions
  537. * Array of node revision IDs in descending order.
  538. * @param int $vid
  539. * Version ID to look for.
  540. *
  541. * @return bool|int
  542. * Returns FALSE if $vid is the first entry.
  543. */
  544. function _diff_get_previous_vid($node_revisions, $vid) {
  545. $previous = NULL;
  546. foreach ($node_revisions as $revision) {
  547. if ($previous && $previous->vid == $vid) {
  548. return $revision->vid;
  549. }
  550. $previous = $revision;
  551. }
  552. return FALSE;
  553. }
  554. /**
  555. * Helper function to create default 'cols' array for diff table.
  556. */
  557. function _diff_default_cols() {
  558. return array(
  559. array(
  560. array(
  561. 'class' => 'diff-marker',
  562. ),
  563. array(
  564. 'class' => 'diff-content',
  565. ),
  566. array(
  567. 'class' => 'diff-marker',
  568. ),
  569. array(
  570. 'class' => 'diff-content',
  571. ),
  572. ),
  573. );
  574. }
  575. /**
  576. * Helper function to create default 'header' array for diff table.
  577. */
  578. function _diff_default_header($old_header = '', $new_header = '') {
  579. return array(
  580. array(
  581. 'data' => $old_header,
  582. 'colspan' => 2,
  583. ),
  584. array(
  585. 'data' => $new_header,
  586. 'colspan' => 2,
  587. ),
  588. );
  589. }
  590. /**
  591. * Show the inline diff for a given node, vid.
  592. *
  593. * If vid = 0 or no previous vid exists for the given revision returns the
  594. * normally rendered content of the specified revision.
  595. */
  596. function diff_inline_show($node, $vid = 0, $metadata = TRUE) {
  597. $new_node = $vid ? node_load($node->nid, $vid) : clone $node;
  598. node_build_content($new_node);
  599. $new = drupal_render($new_node->content);
  600. $old = $vid ? _diff_get_previous_vid(node_revision_list($node), $vid) : 0;
  601. if ($old) {
  602. $old_node = node_load($node->nid, $old);
  603. node_build_content($old_node);
  604. $old = drupal_render($old_node->content);
  605. $output = $metadata ? theme('diff_inline_metadata', array('node' => $new_node)) : '';
  606. $output .= diff_get_inline($old, $new);
  607. return $output;
  608. }
  609. return $new;
  610. }