1# See bottom of file for license and copyright information 2 3package Foswiki::Plugins::HistoryPlugin; 4 5use strict; 6use warnings; 7use Foswiki::Func (); 8use Error qw(:try); 9use Foswiki::AccessControlException (); 10 11# ========================= 12# Simple decimal version, no leading "v" 13our $VERSION = "1.14"; 14our $RELEASE = '22 Jan 2018'; 15our $NO_PREFS_IN_TOPIC = 1; 16our $SHORTDESCRIPTION = 'Shows a complete history of a topic'; 17 18# ========================= 19sub initPlugin { 20 21 # check for Plugins.pm versions 22 if ( $Foswiki::Plugins::VERSION < 1.021 ) { 23 Foswiki::Func::writeWarning( 24 "Version mismatch between HistoryPlugin and Plugins.pm"); 25 return 0; 26 } 27 28 Foswiki::Func::registerTagHandler( 'HISTORY', \&_handleHistory ); 29 30 return 1; 31} 32 33sub _handleHistory { 34 my ( $session, $params, $theTopic, $theWeb ) = @_; 35 36 my $web = $params->{web} || $theWeb; 37 my $topic = $params->{topic} || $theTopic; 38 ( $web, $topic ) = Foswiki::Func::normalizeWebTopicName( $web, $topic ); 39 40 # check topic exists 41 unless ( Foswiki::Func::topicExists( $web, $topic ) ) { 42 return 43"<noautolink><span class='foswikiAlert'>HistoryPlugin error: Topic $web.$topic does not exist</noautolink>"; 44 } 45 46 # check access permissions 47 unless ( 48 Foswiki::Func::checkAccessPermission( 49 "VIEW", $session->{user}, undef, $topic, $web 50 ) 51 ) 52 { 53 throw Foswiki::AccessControlException( "VIEW", $session->{user}, $web, 54 $topic, $Foswiki::Meta::reason ); 55 } 56 57 my $reverse = Foswiki::Func::isTrue( $params->{reverse}, 1 ); 58 59 my $versions = $params->{versions}; 60 my $versionStart; 61 my $versionEnd; 62 my $maxrev = ( Foswiki::Func::getRevisionInfo( $web, $topic ) )[2]; 63 my $rev1 = $params->{rev1} ? $params->{rev1} : 1; 64 my $rev2 = $params->{rev2} ? $params->{rev2} : $maxrev; 65 my $nrev = $params->{nrev} ? $params->{nrev} : 10; 66 67 if ($versions) { 68 $versions =~ m/([0-9\-]*)(\.\.)*([0-9\-]*)/; 69 if ( defined $1 && length $1 ) { 70 $rev1 = $1; 71 } 72 if ( defined $2 && length $2 ) { 73 74 # dots 75 $rev2 = $3 if ( defined $3 && length $3 ); 76 } 77 else { 78 $rev2 = $rev1; 79 } 80 81 # wrap 82 $rev1 = $maxrev + $rev1 if $rev1 < 0; 83 $rev2 = $maxrev + $rev2 if $rev2 < 0; 84 85 # normalize 86 $rev1 = 1 if $rev1 == 0; 87 $rev2 = 1 if $rev2 == 0; 88 $rev1 = $maxrev if $rev1 > $maxrev; 89 $rev2 = $maxrev if $rev2 > $maxrev; 90 if ( $rev1 > $rev2 ) { 91 $reverse = 0; 92 ( $rev1, $rev2 ) = ( $rev2, $rev1 ); 93 } 94 } 95 else { 96 97 # deprecated syntax 98 99 $rev1 = $params->{rev1}; 100 $rev1 =~ s/1\.// if $rev1; 101 $rev2 = $params->{rev2}; 102 $rev2 =~ s/1\.// if $rev2; 103 $nrev = $params->{nrev} || 10; 104 105 $rev2 ||= $rev1 ? $rev1 + $nrev - 1 : $maxrev; 106 $rev1 ||= $rev2 - $nrev + 1; 107 108 ( $rev1, $rev2 ) = ( $rev2, $rev1 ) if $rev1 > $rev2; 109 $rev1 = $maxrev if $rev1 > $maxrev; 110 $rev1 = 1 if $rev1 < 1; 111 $rev2 = $maxrev if $rev2 > $maxrev; 112 $rev2 = 1 if $rev2 < 1; 113 } 114 115 my $format = 116 $params->{format} 117 || $params->{_DEFAULT} 118 || 'r$rev - $date - $wikiusername%BR%'; 119 my $header = $params->{header}; 120 $header = "\$next{'...'}%BR%" unless defined($header); 121 my $footer = $params->{footer}; 122 $footer = "\$previous{'...'}" unless defined($footer); 123 124 Foswiki::Func::setPreferencesValue( "HISTORY_MAXREV", $maxrev ); 125 Foswiki::Func::setPreferencesValue( "HISTORY_REV1", $rev1 ); 126 Foswiki::Func::setPreferencesValue( "HISTORY_REV2", $rev2 ); 127 Foswiki::Func::setPreferencesValue( "HISTORY_NREV", $nrev ); 128 129 # Start the output 130 my $out = _handleHeadFoot( $header, $rev1, $rev2, $nrev, $maxrev ); 131 132 # Print revision info 133 134 my @revs = ( $rev1 .. $rev2 ); 135 136 @revs = reverse(@revs) if $reverse; 137 my $mixedAlphaNum = Foswiki::Func::getRegularExpression('mixedAlphaNum'); 138 my $checkFlag = 0; 139 140 foreach my $rev (@revs) { 141 142 my ( $date, $user, $revout, $comment ) = 143 Foswiki::Func::getRevisionInfo( $web, $topic, $rev ); 144 145 my $wikiName = Foswiki::Func::userToWikiName( $user, 1 ); 146 my $wikiUserName = Foswiki::Func::userToWikiName( $user, 0 ); 147 148 my $revinfo = $format; 149 my $checked1 = ''; 150 my $checked2 = ''; 151 $checked1 = 'checked' if $checkFlag == 0; 152 $checked2 = 'checked' if $checkFlag == 1; 153 $checkFlag++; 154 $revinfo =~ s/\$web/$web/g; 155 $revinfo =~ s/\$topic/$topic/g; 156 $revinfo =~ s/\$rev/$rev/g; 157 $revinfo =~ s/\$date/Foswiki::Func::formatTime($date)/ge; 158 $revinfo =~ 159s/\$(year|ye|week|web|wday|tz|topic|time|seconds|rev|rcs|month|mo|minutes|longdate|isotz|iso|http|hours|epoch|email|dow|day)/_formatTime("\$$1", $web, $topic, $rev)/ge; 160 $revinfo =~ s/\$username/$user/g; 161 $revinfo =~ s/\$wikiname/$wikiName/g; 162 $revinfo =~ s/\$wikiusername/$wikiUserName/g; 163 $revinfo =~ s/\$checked1/$checked1/g; 164 $revinfo =~ s/\$checked2/$checked2/g; 165 166 # This space to tabs conversion must be for Cairo compatibility 167 $revinfo =~ s|^(( )+)|"\t" x (length($1)/3)|e; 168 169 $out .= $revinfo . "\n"; 170 171 $rev--; 172 } 173 $out .= _handleHeadFoot( $footer, $rev1, $rev2, $nrev, $maxrev ); 174 $out = Foswiki::Func::decodeFormatTokens($out); 175 176 return $out; 177} 178 179sub _formatTime { 180 my ( $format, $web, $topic, $rev ) = @_; 181 182 return Foswiki::Func::expandCommonVariables( 183 '%REVINFO{"' . $format . '" rev="' . $rev . '"}%', 184 $topic, $web ); 185} 186 187sub _handleHeadFoot { 188 189 my ( $text, $rev1, $rev2, $nrev, $maxrev ) = @_; 190 191 if ( $rev2 >= $maxrev ) { 192 $text =~ s/\$next(\{.*?\})//g; 193 } 194 else { 195 while ( $text =~ m/\$next(\{(.*?)\})/ ) { 196 my $args = $2 || ''; 197 198 my $newrev1 = $rev2 < $maxrev ? $rev2 + 1 : $rev2; 199 my $newrev2 = $newrev1 + $nrev - 1; 200 $newrev2 = $maxrev if $newrev2 > $maxrev; 201 202 $args =~ s/'/"/g; 203 $args =~ s/\$rev1/$newrev1/g; 204 $args =~ s/\$rev2/$newrev2/g; 205 $args =~ s/\$nrev/$nrev/g; 206 207 my %params = Foswiki::Func::extractParameters($args); 208 my $newtext = $params{text} || $params{_DEFAULT} || ''; 209 my $url = $params{url} || ''; 210 my $replace = 211 $url 212 ? "<a href='$url' class='foswikiButton'>$newtext</a>" 213 : $newtext; 214 $text =~ s/\$next(\{.*?\})/$replace/; 215 } 216 } 217 218 if ( $rev1 <= 1 ) { 219 $text =~ s/\$previous(\{.*?\})//g; 220 } 221 else { 222 while ( $text =~ m/\$previous(\{(.*?)\})/ ) { 223 my $args = $2 || ''; 224 225 my $newrev2 = $rev1 > 1 ? $rev1 - 1 : 1; 226 my $newrev1 = $newrev2 - $nrev + 1; 227 $newrev1 = 1 if $newrev1 < 1; 228 229 $args =~ s/'/"/g; 230 $args =~ s/\$rev1/$newrev1/g; 231 $args =~ s/\$rev2/$newrev2/g; 232 $args =~ s/\$nrev/$nrev/g; 233 234 my %params = Foswiki::Func::extractParameters($args); 235 my $newtext = $params{text} || $params{_DEFAULT} || ''; 236 my $url = $params{url} || ''; 237 my $replace = 238 $url 239 ? "<a href='$url' class='foswikiButton'>$newtext</a>" 240 : $newtext; 241 $text =~ s/\$previous(\{.*?\})/$replace/; 242 } 243 } 244 245 return $text; 246} 247 2481; 249__END__ 250Foswiki - The Free and Open Source Wiki, http://foswiki.org/ 251 252Copyright (C) 2008-2017 Foswiki Contributors. Foswiki Contributors 253are listed in the AUTHORS file in the root of this distribution. 254NOTE: Please extend that file, not this notice. 255 256Additional copyrights apply to some or all of the code in this 257file as follows: 258Copyright (C) 2000-2003 Andrea Sterbini, a.sterbini@flashnet.it 259Copyright (C) 2001-2007 Peter Thoeny, peter@thoeny.com 260 261This program is free software; you can redistribute it and/or 262modify it under the terms of the GNU General Public License 263as published by the Free Software Foundation; either version 2 264of the License, or (at your option) any later version. For 265more details read LICENSE in the root of this distribution. 266 267This program is distributed in the hope that it will be useful, 268but WITHOUT ANY WARRANTY; without even the implied warranty of 269MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 270 271As per the GPL, removal of this notice is prohibited. 272