]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/diff.php
Experiment with more compact diff selection: single column of
[SourceForge/phpwiki.git] / lib / diff.php
1 <?php
2 rcs_id('$Id: diff.php,v 1.18 2001-12-13 18:29:24 dairiki Exp $');
3 // diff.php
4 //
5 // PhpWiki diff output code.
6 //
7 // Copyright (C) 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
8 // You may copy this code freely under the conditions of the GPL.
9 //
10
11 require_once('lib/difflib.php');
12
13 class HtmlUnifiedDiffFormatter extends UnifiedDiffFormatter
14 {
15     function HtmlUnifiedDiffFormatter($context_lines = 4) {
16         $this->UnifiedDiffFormatter($context_lines);
17     }
18
19     function _start_diff() {
20         echo "<div class='diff'>\n";
21     }
22     function _end_diff() {
23         echo "</div>\n";
24     }
25     
26     function _start_block($header) {
27         echo "<div class='block'>\n";
28         echo QElement('tt', $header);
29     }
30
31     function _end_block() {
32         echo "</div>\n";
33     }
34
35     function _lines($lines, $class, $prefix = '&nbsp;', $elem = false) {
36         foreach ($lines as $line) {
37             if (!trim($line))
38                 $line = '&nbsp;';
39             elseif ($elem)
40                 $line = QElement($elem, $line);
41                     
42             echo Element('div', array('class' => $class),
43                          Element('tt', array('class' => 'prefix'), $prefix)
44                          . " $line") . "\n";
45         }
46     }
47
48     function _context($lines) {
49         $this->_lines($lines, 'context');
50     }
51     function _deleted($lines) {
52         $this->_lines($lines, 'deleted', '-', 'del');
53     }
54     function _added($lines) {
55         $this->_lines($lines, 'added', '+', 'ins');
56     }
57
58     function _pack_in_span($chars, $elem) {
59         $end_span = "";
60         $packed = '';
61         foreach ($chars as $c) {
62             if ($c == "\n") {
63                 $packed .= $end_span;
64                 $end_span = "";
65             }
66             elseif (!$end_span) {
67                 $packed .= "<$elem>";
68                 $end_span = "</$elem>";
69             }
70             $packed .= htmlspecialchars($c);
71         }
72         return $packed . $end_span;
73     }
74         
75     function _split_to_chars($lines) {
76         // Split into characters --- there must be a better way ...
77         $joined = implode("\n", $lines);
78         $split = array();
79         for ($i = 0; $i < strlen($joined); $i++)
80             $split[$i] = $joined[$i];
81         return $split;
82     }
83     
84             
85     function _changed($orig, $final) {
86         // Compute character-wise diff in changed region.
87         $diff = new Diff($this->_split_to_chars($orig),
88                          $this->_split_to_chars($final));
89         
90         $orig = $final = '';
91         foreach ($diff->edits as $edit) {
92             switch ($edit->type) {
93             case 'copy':
94                 $packed = implode('', $edit->orig);
95                 $orig .= $packed;
96                 $final .= $packed;
97                 break;
98             case 'add':
99                 $final .= $this->_pack_in_span($edit->final, 'ins');
100                 break;
101             case 'delete':
102                 $orig .= $this->_pack_in_span($edit->orig, 'del');
103                 break;
104             case 'change':
105                 $orig .= $this->_pack_in_span($edit->orig, 'del');
106                 $final .= $this->_pack_in_span($edit->final, 'ins');
107                 break;
108             }
109         }
110
111         $this->_lines(explode("\n", $orig),  'changed', '-');
112         $this->_lines(explode("\n", $final), 'changed', '+');
113     }
114 }
115
116 class TableUnifiedDiffFormatter extends HtmlUnifiedDiffFormatter
117 {
118     function TableUnifiedDiffFormatter($context_lines = 4) {
119         $this->HtmlUnifiedDiffFormatter($context_lines);
120     }
121
122     function _start_diff() {
123         echo "\n<table width='100%' class='diff'";
124         echo " cellspacing='1' cellpadding='1' border='1'>\n";
125     }
126     function _end_diff() {
127         echo "</table>\n";
128     }
129     
130     function _start_block($header) {
131         echo "<tr><td><table width='100%' class='block'";
132         echo " cellspacing='0' cellpadding='1' border='0'>\n";
133         echo Element('tr',
134                      Element('td', array('colspan' => 2),
135                              QElement('tt', $header))) . "\n";
136     }
137
138     function _end_block() {
139         echo "</table></td></tr>\n";
140     }
141
142     function _lines($lines, $class, $prefix = '&nbsp;', $elem = false) {
143         $prefix = Element('td', array('class' => 'prefix', 'width' => "1%"), $prefix);
144         foreach ($lines as $line) {
145             if (! trim($line))
146                 $line = '&nbsp';
147             elseif ($elem)
148                 $line = QElement($elem, $line);
149
150             echo Element('tr', array('valign' => 'top'), 
151                          $prefix . Element('td', array('class' => $class),
152                                            $line)) . "\n";
153         }
154     }
155 }
156
157     
158 /////////////////////////////////////////////////////////////////
159
160 function PageInfoRow ($pagename, $label, $rev)
161 {
162    global $datetimeformat;
163    
164    $cols = QElement('td', array('align' => 'right'), $label);
165    
166    if ($rev) {
167        $url = WikiURL($pagename, array('version' => $rev->getVersion()));
168        $linked_version = QElement('a', array('href' => $url), $rev->getVersion());
169        $cols .= Element('td',
170                         gettext("version") . " " . $linked_version);
171
172        $cols .= QElement('td',
173                          sprintf(gettext ("last modified on %s"),
174                                  strftime($datetimeformat, $rev->get('mtime'))));
175        $cols .= QElement('td',
176                          sprintf(gettext ("by %s"), $rev->get('author')));
177    } else {
178        $cols .= QElement('td', array('colspan' => '3'),
179                          gettext ("None"));
180    }
181    return Element('tr', $cols);
182 }
183
184 function showDiff ($dbi, $request) {
185     $pagename = $request->getArg('pagename');
186     if (is_array($versions = $request->getArg('versions'))) {
187         // Version selection from pageinfo.php display:
188         rsort($versions);
189         list ($version, $previous) = $versions;
190     }
191     else {
192         $version = $request->getArg('version');
193         $previous = $request->getArg('previous');
194     }
195     
196     $page = $dbi->getPage($pagename);
197     if ($version) {
198         if (!($new = $page->getRevision($version)))
199             NoSuchRevision($page, $version);
200         $new_version = sprintf(gettext("version %d"), $version);
201     }
202     else {
203         $new = $page->getCurrentRevision();
204         $new_version = gettext('current version');
205     }
206
207     if (preg_match('/^\d+$/', $previous)) {
208         if ( !($old = $page->getRevision($previous)) )
209             NoSuchRevision($page, $previous);
210         $old_version = sprintf(gettext("version %d"), $previous);
211         $others = array('major', 'minor', 'author');
212     }
213     else {
214         switch ($previous) {
215         case 'major':
216             $old = $new;
217             while ($old = $page->getRevisionBefore($old)) {
218                 if (! $old->get('is_minor_edit'))
219                     break;
220             }
221             $old_version = gettext("previous major revision");
222             $others = array('minor', 'author');
223             break;
224         case 'author':
225             $old = $new;
226             while ($old = $page->getRevisionBefore($old)) {
227                 if ($old->get('author') != $new->get('author'))
228                     break;
229             }
230             $old_version = gettext("revision by previous author");
231             $others = array('major', 'minor');
232             break;
233         case 'minor':
234         default:
235             $previous='minor';
236             $old = $page->getRevisionBefore($new);
237             $old_version = gettext("previous revision");
238             $others = array('major', 'author');
239             break;
240         }
241     }
242
243     $new_url = WikiURL($pagename, array('version' => $new->getVersion()));
244     $new_link = QElement('a', array('href' => $new_url), $new_version);
245     $old_url = WikiURL($pagename, array('version' => $old ? $old->getVersion() : 0));
246     $old_link = QElement('a', array('href' => $old_url), $old_version);
247     $page_link = LinkExistingWikiWord($pagename);
248     
249     $html = Element('p',
250                     sprintf(htmlspecialchars(gettext("Differences between %s and %s of %s.")),
251                             $new_link, $old_link, $page_link));
252
253     $otherdiffs='';
254     $label = array('major' => gettext("Previous Major Revision"),
255                    'minor' => gettext("Previous Revision"),
256                    'author'=> gettext("Previous Author"));
257     foreach ($others as $other) {
258         $args = array('action' => 'diff', 'previous' => $other);
259         if ($version)
260             $args['version'] = $version;
261         $otherdiffs .= ' ' . QElement('a', array('href' => WikiURL($pagename, $args),
262                                                  'class' => 'wikiaction'),
263                                       $label[$other]);
264     }
265     $html .= Element('p',
266                      htmlspecialchars(gettext("Other diffs:"))
267                      . $otherdiffs);
268             
269             
270     if ($old and $old->getVersion() == 0)
271         $old = false;
272     
273     $html .= Element('table',
274                     PageInfoRow($pagename, gettext ("Newer page:"), $new)
275                     . PageInfoRow($pagename, gettext ("Older page:"), $old));
276
277
278     if ($new && $old) {
279         $diff = new Diff($old->getContent(), $new->getContent());
280         
281         if ($diff->isEmpty()) {
282             $html .= Element('hr');
283             $html .= QElement('p', '[' . gettext ("Versions are identical") . ']');
284         }
285         else {
286             // New CSS formatted unified diffs (ugly in NS4).
287             $fmt = new HtmlUnifiedDiffFormatter;
288
289             // Use this for old table-formatted diffs.
290             //$fmt = new TableUnifiedDiffFormatter;
291             $html .= $fmt->format($diff);
292         }
293     }
294     
295     include_once('lib/Template.php');
296     echo GeneratePage('MESSAGE', $html,
297                       sprintf(gettext ("Diff: %s"), $pagename));
298 }
299   
300 // Local Variables:
301 // mode: php
302 // tab-width: 8
303 // c-basic-offset: 4
304 // c-hanging-comment-ender-p: nil
305 // indent-tabs-mode: nil
306 // End:   
307 ?>