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