Index: DiffEngine.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/diff/DiffEngine.php,v retrieving revision 1.1.4.2 diff -U3 -r1.1.4.2 DiffEngine.php --- DiffEngine.php 13 Dec 2007 02:41:24 -0000 1.1.4.2 +++ DiffEngine.php 17 Dec 2007 17:18:59 -0000 @@ -1029,93 +1029,147 @@ } /** - * Wikipedia Table style diff formatter. - * @todo document + * Diff formatter which uses Drupal theme functions. * @private * @subpackage DifferenceEngine */ -class TableDiffFormatter extends DiffFormatter +class DrupalDiffFormatter extends DiffFormatter { - function TableDiffFormatter() { - $this->leading_context_lines = 2; - $this->trailing_context_lines = 2; - } - - function _block_header( $xbeg, $xlen, $ybeg, $ylen ) { - $r = ''.t('Line @line', array('@line' => $xbeg))."\n" . - ''.t('Line @line', array('@line' => $ybeg))."\n"; - return $r; - } - - function _start_block( $header ) { - if ($this->show_header) - echo $header; - } - - function _end_block() { - } - - function _lines( $lines, $prefix=' ', $color='white' ) { - } - - # HTML-escape parameter before calling this - function addedLine( $line ) { - return "+
{$line}
"; - } - - # HTML-escape parameter before calling this - function deletedLine( $line ) { - return "-
{$line}
"; - } - - # HTML-escape parameter before calling this - function contextLine( $line ) { - return "
{$line}
"; - } - - function emptyLine() { - return ' '; - } - - function _added( $lines ) { - foreach ($lines as $line) { - echo '' . $this->emptyLine() . - $this->addedLine( check_plain ( $line ) ) . "\n"; - } - } - - function _deleted($lines) { - foreach ($lines as $line) { - echo '' . $this->deletedLine( check_plain ( $line ) ) . - $this->emptyLine() . "\n"; - } - } - - function _context( $lines ) { - foreach ($lines as $line) { - echo '' . - $this->contextLine( check_plain ( $line ) ) . - $this->contextLine( check_plain ( $line ) ) . "\n"; - } - } - - function _changed( $orig, $closing ) { - - $diff = new WordLevelDiff( $orig, $closing ); - $del = $diff->orig(); - $add = $diff->closing(); - - # Notice that WordLevelDiff returns HTML-escaped output. - # Hence, we will be calling addedLine/deletedLine without HTML-escaping. - - while ( $line = array_shift( $del ) ) { - $aline = array_shift( $add ); - echo '' . $this->deletedLine( $line ) . - $this->addedLine( $aline ) . "\n"; - } - foreach ($add as $line) { # If any leftovers - echo '' . $this->emptyLine() . - $this->addedLine( $line ) . "\n"; - } - } + + var $rows; + + function DrupalDiffFormatter() { + $this->leading_context_lines = 2; + $this->trailing_context_lines = 2; + } + + function _start_diff() { + $this->rows = array(); + } + + function _end_diff() { + return $this->rows; + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) { + return array( + array( + 'data' => theme('diff_header_line', $xbeg), + 'colspan' => 2 + ), + array( + 'data' => theme('diff_header_line', $ybeg), + 'colspan' => 2 + ) + ); + } + + function _start_block($header) { + if ($this->show_header) { + $this->rows[] = $header; + } + } + + function _end_block() { + } + + function _lines($lines, $prefix=' ', $color='white') { + } + + /** + * Note: you should HTML-escape parameter before calling this. + */ + function addedLine($line) { + return array( + array( + 'data' => '+', + ), + array( + 'data' => theme('diff_content_line', $line), + 'class' => 'diff-addedline', + ) + ); + } + + /** + * Note: you should HTML-escape parameter before calling this. + */ + function deletedLine($line) { + return array( + array( + 'data' => '-', + ), + array( + 'data' => theme('diff_content_line', $line), + 'class' => 'diff-deletedline', + ) + ); + } + + /** + * Note: you should HTML-escape parameter before calling this. + */ + function contextLine($line) { + return array( + ' ', + array( + 'data' => theme('diff_content_line', $line), + 'class' => 'diff-context', + ) + ); + } + + function emptyLine() { + return array( + ' ', + theme('diff_empty_line', ' '), + ); + } + + function _added($lines) { + foreach($lines as $line) { + $this->rows[] = array_merge($this->emptyLine(), $this->addedLine(check_plain($line))); + } + } + + function _deleted($lines) { + foreach($lines as $line) { + $this->rows[] = array_merge($this->deletedLine(check_plain($line)), $this->emptyLine()); + } + } + + function _context($lines) { + foreach($lines as $line) { + $this->rows[] = array_merge($this->contextLine(check_plain($line)), $this->contextLine(check_plain($line))); + } + } + + function _changed($orig, $closing) { + $diff = new WordLevelDiff($orig, $closing); + $del = $diff->orig(); + $add = $diff->closing(); + + // Notice that WordLevelDiff returns HTML-escaped output. + // Hence, we will be calling addedLine/deletedLine without HTML-escaping. + + while ($line = array_shift($del)) { + $aline = array_shift( $add ); + $this->rows[] = array_merge($this->deletedLine($line), $this->addedLine($aline)); + } + foreach ($add as $line) { // If any leftovers + $this->rows[] = array_merge($this->emptyLine(), $this->addedLine($line)); + } + } +} + +function theme_diff_header_line($lineno) { + return ''. t('Line %lineno', array('%lineno' => $lineno)) .''; +} + +function theme_diff_content_line($line) { + return '
'. $line .'
'; +} + +function theme_diff_empty_line($line) { + return $line; } Index: diff.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/diff/diff.css,v retrieving revision 1.2.2.4 diff -U3 -r1.2.2.4 diff.css --- diff.css 13 Dec 2007 02:41:24 -0000 1.2.2.4 +++ diff.css 17 Dec 2007 17:18:59 -0000 @@ -6,6 +6,10 @@ table-layout: fixed; width: 100%; } +table.diff tr.even, table.diff tr.odd { + background-color: inherit; + border: none; +} td.diff-prevlink { text-align: left; } Index: diff.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/diff/diff.module,v retrieving revision 1.8.2.14 diff -U3 -r1.8.2.14 diff.module --- diff.module 13 Dec 2007 19:08:25 -0000 1.8.2.14 +++ diff.module 17 Dec 2007 17:18:59 -0000 @@ -330,17 +330,59 @@ $prev_link = ''; } - // display table - $output .= ''; - $output .= ''; - $output .= ''; - if ($new_log || $old_log) { - $output .= ''; - } - $output .= ''; - $output .= _diff_table_body($old_node, $new_node); - $output .= '
'. $old_header .''. $new_header .'
'. $old_log .''. $new_log .'
'; - $output .= '
'; + $cols = array( + array( + array( + 'class' => 'diff-marker', + ), + array( + 'class' => 'diff-content', + ), + array( + 'class' => 'diff-marker', + ), + array( + 'class' => 'diff-content', + ), + ), + ); + $header = array( + array( + 'data' => $old_header, + 'colspan' => 2 + ), + array( + 'data' => $new_header, + 'colspan' => 2 + ) + ); + $rows = array(); + if ($old_log || $new_log) { + $rows[] = array( + array( + 'data' => $old_log, + 'colspan' => 2 + ), + array( + 'data' => $new_log, + 'colspan' => 2 + ) + ); + } + $rows[] = array( + array( + 'data' => $prev_link, + 'class' => 'diff-prevlink', + 'colspan' => 2 + ), + array( + 'data' => $next_link, + 'class' => 'diff-nextlink', + 'colspan' => 2 + ) + ); + $rows = array_merge($rows, _diff_body_rows($old_node, $new_node)); + $output = theme('diff_table', $header, $rows, array('class' => 'diff'), NULL, $cols); if ($node->vid == $new_vid) { $output .= '
'. t('Current revision:') .'
'; @@ -354,18 +396,21 @@ } /** - * Create the table body of the diff between $old_node and $new_node. - * The result is a html table part enclosed in tags. + * Creates an array of rows which represent a diff between $old_node and $new_node. + * The rows can be used via theme('diff_table') to be displayed. * * @param $old_node * Node for comparison which will be displayed on the left side. * @param $new_node * Node for comparison which will be displayed on the right side. */ -function _diff_table_body(&$old_node, &$new_node) { +function _diff_body_rows(&$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('taxonomy')) { + include_once('taxonomy.inc'); + } if (module_exists('upload')) { include_once('upload.inc'); } @@ -373,27 +418,38 @@ include_once('cck.inc'); } - $output = ''; + $rows = array(); $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(); + $formatter = new DrupalDiffFormatter(); if (isset($node_diff['format'])) { $formatter->show_header = $node_diff['format']['show_header']; } - $formatter_output = $formatter->format($diff); - if ($formatter_output) { - $output .= ''. t('Changes to %name', array('%name' => $node_diff['name'])) .''; - $output .= $formatter_output; + $diff_rows = $formatter->format($diff); + if (count($diff_rows)) { + $rows[] = array( + array( + 'data' => t('Changes to %name', array('%name' => $node_diff['name'])), + 'class' => 'diff-section-title', + 'colspan' => 4 + ) + ); + $rows = array_merge($rows, $diff_rows); $any_visible_change = true; } } if (!$any_visible_change) { - $output .= '' .t('No visible changes') .''; + $rows[] = array( + array( + 'data' => t('No visible changes'), + 'class' => 'diff-section-title', + 'colspan' => 4 + ) + ); } - $output .= ''; - return $output; + return $rows; } /** @@ -482,11 +538,12 @@ $op = isset($form_values['op']) ? $form_values['op'] : ''; if ($op == t('Preview changes')) { + // Diff module expects node as object, thus $form_values is cast to an object. $node = (object)$form_values; - $changes = ''; - $changes .= ''; - $changes .= _diff_table_body(node_load($form_values['nid']), $node); - $changes .= '
'; + // Create diff of old node and edited node + $rows = _diff_body_rows(node_load($form_values['nid']), $node); + $changes = theme('table', array(), $rows, array('class' => 'diff')); + // Prepend diff to edit form $form['#prefix'] = isset($form['#prefix']) ? $changes . $form['#prefix'] : $changes; } return $form; @@ -538,3 +595,199 @@ return $output; } + +/** + * Return a themed table. This is a modified version of theme_table, adding + * colgroup tag and col tag options. + * + * @param $header + * An array containing the table headers. Each element of the array can be + * either a localized string or an associative array with the following keys: + * - "data": The localized title of the table column. + * - "field": The database field represented in the table column (required if + * user is to be able to sort on this column). + * - "sort": A default sort order for this column ("asc" or "desc"). + * - Any HTML attributes, such as "colspan", to apply to the column header cell. + * @param $rows + * An array of table rows. Every row is an array of cells, or an associative + * array with the following keys: + * - "data": an array of cells + * - Any HTML attributes, such as "class", to apply to the table row. + * + * Each cell can be either a string or an associative array with the following keys: + * - "data": The string to display in the table cell. + * - "header": Indicates this cell is a header. + * - Any HTML attributes, such as "colspan", to apply to the table cell. + * + * Here's an example for $rows: + * @verbatim + * $rows = array( + * // Simple row + * array( + * 'Cell 1', 'Cell 2', 'Cell 3' + * ), + * // Row with attributes on the row and some of its cells. + * array( + * 'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => 'funky' + * ) + * ); + * @endverbatim + * + * @param $attributes + * An array of HTML attributes to apply to the table tag. + * @param $caption + * A localized string to use for the tag. + * @param $cols + * An array of table colum groups. Every column group is an array of columns, + * or an associative array with the following keys: + * - "data": an array of cells + * - Any HTML attributes, such as "class", to apply to the table column group. + * + * Each column can be either an empty array or associative array with the following keys: + * - Any HTML attributes, such as "class", to apply to the table column group. + * + * Here's an example for $cols: + * @verbatim + * $cols = array( + * // Simple colgroup. + * array(), + * // Simple colgroup with attributes. + * array( + * 'data' => array(), 'colspan' => 2, 'style' => 'color: green;', + * ), + * // Simple colgroup with one col. + * array( + * array(), + * ), + * // Colgroup with attributes on the colgroup and some of its cols. + * array( + * 'data' => array(array('class' => 'diff-marker'), array('colspan' => 2)), 'class' => 'funky', + * ), + * ); + * @endverbatim + * + * The HTML will look as follows: + * @verbatim + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ... + *
+ * @endverbatim + * + * @return + * An HTML string representing the table. + */ +function theme_diff_table($header, $rows, $attributes = array(), $caption = NULL, $cols = array()) { + $output = '\n"; + + if (isset($caption)) { + $output .= ''. $caption ."\n"; + } + + // Format the table columns: + if (count($cols)) { + foreach ($cols as $number => $col) { + $attributes = array(); + + // Check if we're dealing with a simple or complex column + if (isset($col['data'])) { + foreach ($col as $key => $value) { + if ($key == 'data') { + $cells = $value; + } + else { + $attributes[$key] = $value; + } + } + } + else { + $cells = $col; + } + + // Build colgroup + if (is_array($cells) && count($cells)) { + $output .= ' '; + $i = 0; + foreach ($cells as $cell) { + $output .= ' '; + } + $output .= " \n"; + } + else { + $output .= ' \n"; + } + } + } + + // Format the table header: + if (count($header)) { + $ts = tablesort_init($header); + $output .= ' '; + foreach ($header as $cell) { + $cell = tablesort_header($cell, $header, $ts); + $output .= _theme_table_cell($cell, TRUE); + } + $output .= " \n"; + } + + // Format the table rows: + $output .= "\n"; + if (count($rows)) { + $flip = array('even' => 'odd', 'odd' => 'even'); + $class = 'even'; + foreach ($rows as $number => $row) { + $attributes = array(); + + // Check if we're dealing with a simple or complex row + if (isset($row['data'])) { + foreach ($row as $key => $value) { + if ($key == 'data') { + $cells = $value; + } + else { + $attributes[$key] = $value; + } + } + } + else { + $cells = $row; + } + + // Add odd/even class + $class = $flip[$class]; + if (isset($attributes['class'])) { + $attributes['class'] .= ' '. $class; + } + else { + $attributes['class'] = $class; + } + + // Build row + $output .= ' '; + $i = 0; + foreach ($cells as $cell) { + $cell = tablesort_cell($cell, $header, $ts, $i++); + $output .= _theme_table_cell($cell); + } + $output .= " \n"; + } + } + + $output .= "\n"; + return $output; +}