0ee8f0e6671fcb9be708cab7a9e230a9587baded
[ikiwiki.git] / IkiWiki / Plugin / recentchanges.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::recentchanges;
3
4 use warnings;
5 use strict;
6 use IkiWiki 3.00;
7 use Encode;
8 use HTML::Entities;
9
10 sub import {
11         hook(type => "getsetup", id => "recentchanges", call => \&getsetup);
12         hook(type => "checkconfig", id => "recentchanges", call => \&checkconfig);
13         hook(type => "refresh", id => "recentchanges", call => \&refresh);
14         hook(type => "pageactions", id => "recentchanges", call => \&pageactions,
15                 first => 1);
16         hook(type => "pagetemplate", id => "recentchanges", call => \&pagetemplate);
17         hook(type => "htmlize", id => "_change", call => \&htmlize);
18         # Load goto to fix up links from recentchanges
19         IkiWiki::loadplugin("goto");
20 }
21
22 sub getsetup () {
23         return
24                 plugin => {
25                         safe => 1,
26                         rebuild => 1,
27                 },
28                 recentchangespage => {
29                         type => "string",
30                         example => "recentchanges",
31                         description => "name of the recentchanges page",
32                         safe => 1,
33                         rebuild => 1,
34                 },
35                 recentchangesnum => {
36                         type => "integer",
37                         example => 100,
38                         description => "number of changes to track",
39                         safe => 1,
40                         rebuild => 0,
41                 },
42 }
43
44 sub checkconfig () {
45         $config{recentchangespage}='recentchanges' unless defined $config{recentchangespage};
46         $config{recentchangesnum}=100 unless defined $config{recentchangesnum};
47 }
48
49 sub refresh ($) {
50         my %seen;
51
52         # add new changes
53         foreach my $change (IkiWiki::rcs_recentchanges($config{recentchangesnum})) {
54                 $seen{store($change, $config{recentchangespage})}=1;
55         }
56         
57         # delete old and excess changes
58         foreach my $page (keys %pagesources) {
59                 if ($pagesources{$page} =~ /\._change$/ && ! $seen{$page}) {
60                         unlink($config{srcdir}.'/'.$pagesources{$page});
61                 }
62         }
63 }
64
65 # Enable the recentchanges link on wiki pages.
66 sub pageactions (@) {
67         my %params=@_;
68         my $page=$params{page};
69
70         if (defined $config{recentchangespage} && $config{rcs} &&
71             $page ne $config{recentchangespage}) {
72                 return htmllink($page, $page, $config{recentchangespage},
73                         linktext => gettext("RecentChanges"));
74         }
75 }
76
77 # Backwards compatability for templates still using
78 # RECENTCHANGESURL.
79 sub pagetemplate (@) {
80         my %params=@_;
81         my $template=$params{template};
82         my $page=$params{page};
83
84         if (defined $config{recentchangespage} && $config{rcs} &&
85             $template->query(name => "recentchangesurl") &&
86             $page ne $config{recentchangespage}) {
87                 $template->param(recentchangesurl => urlto($config{recentchangespage}, $page));
88                 $template->param(have_actions => 1);
89         }
90 }
91
92 # Pages with extension _change have plain html markup, pass through.
93 sub htmlize (@) {
94         my %params=@_;
95         return $params{content};
96 }
97
98 sub store ($$$) {
99         my $change=shift;
100
101         my $page="$config{recentchangespage}/change_".titlepage($change->{rev});
102
103         # Optimisation to avoid re-writing pages. Assumes commits never
104         # change (or that any changes are not important).
105         return $page if exists $pagesources{$page} && ! $config{rebuild};
106
107         # Limit pages to first 10, and add links to the changed pages.
108         my $is_excess = exists $change->{pages}[10];
109         delete @{$change->{pages}}[10 .. @{$change->{pages}}] if $is_excess;
110         $change->{pages} = [
111                 map {
112                         if (length $config{cgiurl}) {
113                                 $_->{link} = "<a href=\"".
114                                         IkiWiki::cgiurl(
115                                                 do => "goto",
116                                                 page => $_->{page}
117                                         ).
118                                         "\" rel=\"nofollow\">".
119                                         pagetitle($_->{page}).
120                                         "</a>"
121                         }
122                         else {
123                                 $_->{link} = pagetitle($_->{page});
124                         }
125                         $_->{baseurl}="$config{url}/" if length $config{url};
126
127                         $_;
128                 } @{$change->{pages}}
129         ];
130         push @{$change->{pages}}, { link => '...' } if $is_excess;
131
132         # See if the committer is an openid.
133         $change->{author}=$change->{user};
134         my $oiduser=eval { IkiWiki::openiduser($change->{user}) };
135         if (defined $oiduser) {
136                 $change->{authorurl}=$change->{user};
137                 $change->{user}=$oiduser;
138         }
139         elsif (length $config{cgiurl}) {
140                 $change->{authorurl} = IkiWiki::cgiurl(
141                         do => "goto",
142                         page => IkiWiki::userpage($change->{author}),
143                 );
144         }
145
146         if (ref $change->{message}) {
147                 foreach my $field (@{$change->{message}}) {
148                         if (exists $field->{line}) {
149                                 # escape html
150                                 $field->{line} = encode_entities($field->{line});
151                                 # escape links and preprocessor stuff
152                                 $field->{line} = encode_entities($field->{line}, '\[\]');
153                         }
154                 }
155         }
156
157         # Fill out a template with the change info.
158         my $template=template("change.tmpl", blind_cache => 1);
159         $template->param(
160                 %$change,
161                 commitdate => displaytime($change->{when}, "%X %x"),
162                 wikiname => $config{wikiname},
163         );
164         
165         $template->param(permalink => "$config{url}/$config{recentchangespage}/#change-".titlepage($change->{rev}))
166                 if exists $config{url};
167         
168         IkiWiki::run_hooks(pagetemplate => sub {
169                 shift->(page => $page, destpage => $page,
170                         template => $template, rev => $change->{rev});
171         });
172
173         my $file=$page."._change";
174         writefile($file, $config{srcdir}, $template->output);
175         utime $change->{when}, $change->{when}, "$config{srcdir}/$file";
176
177         return $page;
178 }
179
180 1