#!/usr/bin/perl # TmNetWiki version 0.4 (September 11, 2002) # Copyright (C) 2002 Adam Brate # # Based on the GPLed UseModWiki 0.92 # Copyright (C) 2000-2001 Clifford A. Adams # or # Based on the GPLed AtisWiki 0.3 (C) 1998 Markus Denker # # ...which was based on # the LGPLed CVWiki CVS-patches (C) 1997 Peter Merel # and The Original WikiWikiWeb (C) Ward Cunningham # (code reused with permission) # Email and ThinLine options by Jim Mahoney # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc. # 59 Temple Place, Suite 330 # Boston, MA 02111-1307 USA package Wiki::diff; use strict; # needs subs Wiki::template: ScriptLinkDiff, # Wiki::translate: Ts, T # Wiki::file: CreateDir, WriteStringToFile, # Wiki::page: GetPageCache, SetPageCache, # Wiki::keep: OpenKeptRevisions, # Wiki::lock: RequestLockDir, ReleaseLockDir, # Wiki::wiki: CommonMarkup # Wiki::utility: QuoteHtml # needs consts $TempDir, $useDiffLog, $DataDir # needs vars %KeptRevisions, %SaveUrl, %SaveNumUrl, $SaveUrlIndex, $SaveNumUrlIndex use base 'Exporter'; use Wiki::consts; use Wiki::template; use Wiki::translate; use Wiki::file; use Wiki::page; use Wiki::keep; use Wiki::lock; use Wiki::wiki; use Wiki::utility; #if ($^V and $^V gt v5.6.0) { # our @EXPORT; #else { use vars qw(@EXPORT); #} @EXPORT = qw( GetDiffHTML UpdateDiffs ); sub DiffToHTML; sub ColorDiff; # diff utilities sub GetDiff; sub GetCacheDiff; sub GetKeptDiff; sub WriteDiff; sub RequestDiffLock; sub ReleaseDiffLock; # ==== Difference markup and HTML ==== sub GetDiffHTML { my ($diffType, $id, $rev, $newText) = @_; my ($html, $diffText, $diffTextTwo, $priorName, $links, $usecomma); my ($major, $minor, $author, $useMajor, $useMinor, $useAuthor, $cacheName); $links = "("; $usecomma = 0; $major = &ScriptLinkDiff(1, $id, T('major diff'), ""); $minor = &ScriptLinkDiff(2, $id, T('minor diff'), ""); $author = &ScriptLinkDiff(3, $id, T('author diff'), ""); $useMajor = 1; $useMinor = 1; $useAuthor = 1; if ($diffType == 1) { $priorName = T('major'); $cacheName = 'major'; $useMajor = 0; } elsif ($diffType == 2) { $priorName = T('minor'); $cacheName = 'minor'; $useMinor = 0; } elsif ($diffType == 3) { $priorName = T('author'); $cacheName = 'author'; $useAuthor = 0; } if ($rev ne "") { # Note: OpenKeptRevisions must have been done by caller. # Later optimize if same as cached revision $diffText = &GetKeptDiff($newText, $rev, 1); # 1 = get lock if ($diffText eq "") { $diffText = T('(The revisions are identical or unavailable.)'); } } else { $diffText = &GetCacheDiff($cacheName); } $useMajor = 0 if ($useMajor && ($diffText eq &GetCacheDiff("major"))); $useMinor = 0 if ($useMinor && ($diffText eq &GetCacheDiff("minor"))); $useAuthor = 0 if ($useAuthor && ($diffText eq &GetCacheDiff("author"))); $useMajor = 0 if ((!defined(&GetPageCache('oldmajor'))) || (&GetPageCache("oldmajor") < 1)); $useAuthor = 0 if ((!defined(&GetPageCache('oldauthor'))) || (&GetPageCache("oldauthor") < 1)); if ($useMajor) { $links .= $major; $usecomma = 1; } if ($useMinor) { $links .= ", " if ($usecomma); $links .= $minor; $usecomma = 1; } if ($useAuthor) { $links .= ", " if ($usecomma); $links .= $author; } if (!($useMajor || $useMinor || $useAuthor)) { $links .= T('no other diffs'); } $links .= ")"; if ((!defined($diffText)) || ($diffText eq "")) { $diffText = T('No diff available.'); } if ($rev ne "") { $html = '' . Ts('Difference (from revision %s to current revision)', $rev) . "\n" . "$links
" . &DiffToHTML($diffText) . "
\n"; } else { if (($diffType != 2) && ((!defined(&GetPageCache("old$cacheName"))) || (&GetPageCache("old$cacheName") < 1))) { $html = '' . Ts('No diff available--this is the first %s revision.', $priorName) . "\n$links
"; } else { $html = '' . Ts('Difference (from prior %s revision)', $priorName) . "\n$links
" . &DiffToHTML($diffText) . "
\n"; } } return $html; } sub DiffToHTML { my ($html) = @_; my ($tChanged, $tRemoved, $tAdded); $tChanged = T('Changed:'); $tRemoved = T('Removed:'); $tAdded = T('Added:'); $html =~ s/\n--+//g; # Note: Need spaces before
to be different from diff section. $html =~ s/(^|\n)(\d+.*c.*)/$1
$tChanged $2<\/strong>
/g; $html =~ s/(^|\n)(\d+.*d.*)/$1
$tRemoved $2<\/strong>
/g; $html =~ s/(^|\n)(\d+.*a.*)/$1
$tAdded $2<\/strong>
/g; $html =~ s/\n((<.*\n)+)/&ColorDiff($1,"ffffaf")/ge; $html =~ s/\n((>.*\n)+)/&ColorDiff($1,"cfffcf")/ge; return $html; } sub ColorDiff { my ($diff, $color) = @_; $diff =~ s/(^|\n)[<>]/$1/g; $diff = &QuoteHtml($diff); # Do some of the Wiki markup rules: %SaveUrl = (); %SaveNumUrl = (); $SaveUrlIndex = 0; $SaveNumUrlIndex = 0; $diff =~ s/$Consts->{FS}//g; $diff = &CommonMarkup($diff, 0, 1, 0); # No images, all patterns, no special $diff =~ s/$Consts->{FS}(\d+)$Consts->{FS}/$SaveUrl{$1}/ge; # Restore saved text $diff =~ s/$Consts->{FS}(\d+)$Consts->{FS}/$SaveUrl{$1}/ge; # Restore nested saved text $diff =~ s/\r?\n/
/g; return "
\n" . $diff . "
\n"; } sub GetDiff { my ($old, $new, $lock) = @_; my ($diff_out, $oldName, $newName); &CreateDir($TempDir); $oldName = "$TempDir/old_diff"; $newName = "$TempDir/new_diff"; if ($lock) { &RequestDiffLock() or return ""; $oldName .= "_locked"; $newName .= "_locked"; } &WriteStringToFile($oldName, $old); &WriteStringToFile($newName, $new); $diff_out = `diff $oldName $newName`; &ReleaseDiffLock() if ($lock); $diff_out =~ s/\\ No newline.*\n//g; # Get rid of common complaint. # No need to unlink temp files--next diff will just overwrite. return $diff_out; } sub GetCacheDiff { my ($type) = @_; my ($diffText); $diffText = &GetPageCache("diff_default_$type"); $diffText = &GetCacheDiff('minor') if ($diffText eq "1"); $diffText = &GetCacheDiff('major') if ($diffText eq "2"); return $diffText; } sub UpdateDiffs { my ($id, $editTime, $old, $new, $isEdit, $newAuthor) = @_; my ($editDiff, $oldMajor, $oldAuthor); $editDiff = &GetDiff($old, $new, 0); # 0 = already in lock $oldMajor = &GetPageCache('oldmajor'); $oldAuthor = &GetPageCache('oldauthor'); if ($UseDiffLog) { &WriteDiff($id, $editTime, $editDiff); } &SetPageCache('diff_default_minor', $editDiff); if ($isEdit || !$newAuthor) { &OpenKeptRevisions('text_default'); } if (!$isEdit) { &SetPageCache('diff_default_major', "1"); } else { &SetPageCache('diff_default_major', &GetKeptDiff($new, $oldMajor, 0)); } if ($newAuthor) { &SetPageCache('diff_default_author', "1"); } elsif ($oldMajor == $oldAuthor) { &SetPageCache('diff_default_author', "2"); } else { &SetPageCache('diff_default_author', &GetKeptDiff($new, $oldAuthor, 0)); } } # Must be done after minor diff is set and OpenKeptRevisions called sub GetKeptDiff { my ($newText, $oldRevision, $lock) = @_; my (%sect, %data, $oldText); $oldText = ""; if (defined($KeptRevisions{$oldRevision})) { %sect = split(/$Consts->{FS2}/, $KeptRevisions{$oldRevision}, -1); %data = split(/$Consts->{FS3}/, $sect{'data'}, -1); $oldText = $data{'text'}; } return "" if ($oldText eq ""); # Old revision not found return &GetDiff($oldText, $newText, $lock); } sub WriteDiff { my ($id, $editTime, $diffString) = @_; open (OUT, ">>$DataDir/diff_log") or die(T('can not write diff_log')); print OUT "------\n" . $id . "|" . $editTime . "\n"; print OUT $diffString; close(OUT); } sub RequestDiffLock { # 4 tries, 2 second wait, do not die on error return &RequestLockDir('diff', 4, 2, 0); } sub ReleaseDiffLock { &ReleaseLockDir('diff'); }