Making use of the transient plugin
[ikiwiki.git] / IkiWiki / Plugin / calendar.pm
1 #! /usr/bin/perl
2 # Copyright (c) 2006, 2007 Manoj Srivastava <srivasta@debian.org>
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require 5.002;
19 package IkiWiki::Plugin::calendar;
20
21 use warnings;
22 use strict;
23 use IkiWiki 3.00;
24 use Time::Local;
25
26 my $time=time;
27 my @now=localtime($time);
28
29 sub import {
30         hook(type => "checkconfig", id => "calendar", call => \&checkconfig);
31         hook(type => "getsetup", id => "calendar", call => \&getsetup);
32         hook(type => "needsbuild", id => "calendar", call => \&needsbuild);
33         hook(type => "preprocess", id => "calendar", call => \&preprocess);
34         hook(type => "scan", id => "calendar", call => \&scan);
35
36         IkiWiki::loadplugin("transient");
37 }
38
39 sub getsetup () {
40         return
41                 plugin => {
42                         safe => 1,
43                         rebuild => undef,
44                         section => "widget",
45                 },
46                 archivebase => {
47                         type => "string",
48                         example => "archives",
49                         description => "base of the archives hierarchy",
50                         safe => 1,
51                         rebuild => 1,
52                 },
53                 archive_pagespec => {
54                         type => "pagespec",
55                         example => "page(posts/*) and !*/Discussion",
56                         description => "PageSpec of pages to include in the archives, if option `calendar_autocreate` is true.",
57                         link => 'ikiwiki/PageSpec',
58                         safe => 1,
59                         rebuild => 0,
60                 },
61                 calendar_autocreate => {
62                         type => "boolean",
63                         example => 1,
64                         description => "autocreate new calendar pages?",
65                         safe => 1,
66                         rebuild => undef,
67                 },
68                 calendar_fill_gaps => {
69                         type => "boolean",
70                         example => 1,
71                         default => 1,
72                         description => "if set, when building calendar pages, also build pages of year and month when no pages were published (building empty calendars).",
73                         safe => 1,
74                         rebuild => 0,
75                 },
76 }
77
78 sub checkconfig () {
79         if (! defined $config{calendar_autocreate}) {
80                 $config{calendar_autocreate} = defined $config{archivebase};
81         }
82         if (! defined $config{archive_pagespec}) {
83                 $config{archive_pagespec} = '*';
84         }
85         if (! defined $config{archivebase}) {
86                 $config{archivebase} = 'archives';
87         }
88         if (! defined $config{calendar_fill_gaps}) {
89                 $config{calendar_fill_gaps} = 1;
90         }
91 }
92
93 sub is_leap_year (@) {
94         my %params=@_;
95         return ($params{year} % 4 == 0 && (($params{year} % 100 != 0) || $params{year} % 400 == 0));
96 }
97
98 sub month_days {
99         my %params=@_;
100         my $days_in_month = (31,28,31,30,31,30,31,31,30,31,30,31)[$params{month}-1];
101         if ($params{month} == 2 && is_leap_year(%params)) {
102                 $days_in_month++;
103         }
104         return $days_in_month;
105 }
106
107 sub autocreate {
108         my ($page, $pagefile, $year, $month) = @_;
109         my $message=sprintf(gettext("creating calendar page %s"), $page);
110         debug($message);
111
112         my $template;
113         if (defined $month) {
114                 $template=template("calendarmonth.tmpl");
115         } else {
116                 $template=template("calendaryear.tmpl");
117         }
118         $template->param(year => $year);
119         $template->param(month => $month) if defined $month;
120         $template->param(pagespec => $config{archive_pagespec});
121
122         my $dir = $IkiWiki::Plugin::transient::transientdir;
123
124         writefile($pagefile, $dir, $template->output);
125 }
126
127 sub calendarlink($;$) {
128         my ($year, $month) = @_;
129         if (defined $month) {
130                 return $config{archivebase} . "/" . $year . "/" . $month;
131         } else {
132                 return $config{archivebase} . "/" . $year;
133         }
134 }
135
136 sub gencalendarmonth{
137         my $year = shift;
138         my $month = sprintf("%02d", shift);
139
140         my $page = calendarlink($year, $month);
141         my $pagefile = newpagefile($page, $config{default_pageext});
142         add_autofile(
143                 $pagefile, "calendar",
144                 sub {return autocreate($page, $pagefile, $year, $month);}
145         );
146 }
147
148 sub gencalendaryear {
149         my $year = shift;
150         my %params = @_;
151
152         return unless $config{calendar_autocreate};
153
154         # Building year page
155         my $page = calendarlink($year);
156         my $pagefile = newpagefile($page, $config{default_pageext});
157         add_autofile(
158                 $pagefile, "calendar",
159                 sub {return autocreate($page, $pagefile, $year);}
160         );
161
162         if (not exists $wikistate{calendar}{minyear}) {
163                 $wikistate{calendar}{minyear} = $year;
164         }
165         if (not exists $wikistate{calendar}{maxyear}) {
166                 $wikistate{calendar}{maxyear} = $year;
167         }
168
169         if ($config{calendar_fill_gaps}) {
170                 # Building month pages
171                 foreach my $month (1 .. 12) {
172                         gencalendarmonth($year, $month);
173                 }
174
175                 # Filling potential gaps in years (e.g. calendar goes from 2010 to 2014,
176                 # and we just added year 2005. We have to had years 2006 to 2009).
177                 return if $params{norecurse};
178                 if ($wikistate{calendar}{minyear} > $year) {
179                         foreach my $other ($year + 1 .. $wikistate{calendar}{minyear} - 1) {
180                                 gencalendaryear($other, norecurse => 1);
181                         }
182                         $wikistate{calendar}{minyear} = $year;
183                 }
184                 if ($wikistate{calendar}{maxyear} < $year) {
185                         foreach my $other ($wikistate{calendar}{maxyear} + 1 .. $year - 1) {
186                                 gencalendaryear($other, norecurse => 1);
187                         }
188                         $wikistate{calendar}{maxyear} = $year;
189                 }
190         }
191         if ($year < $wikistate{calendar}{minyear}) {
192                 $wikistate{calendar}{minyear} = $year;
193         }
194         if ($year >  $wikistate{calendar}{maxyear}) {
195                 $wikistate{calendar}{maxyear} = $year;
196         }
197 }
198
199 sub format_month (@) {
200         my %params=@_;
201
202         my %linkcache;
203         foreach my $p (pagespec_match_list($params{page}, 
204                                 "creation_year($params{year}) and creation_month($params{month}) and ($params{pages})",
205                                 # add presence dependencies to update
206                                 # month calendar when pages are added/removed
207                                 deptype => deptype("presence"))) {
208                 my $mtime = $IkiWiki::pagectime{$p};
209                 my @date  = localtime($mtime);
210                 my $mday  = $date[3];
211                 my $month = $date[4] + 1;
212                 my $year  = $date[5] + 1900;
213                 my $mtag  = sprintf("%02d", $month);
214
215                 if (! $linkcache{"$year/$mtag/$mday"}) {
216                         $linkcache{"$year/$mtag/$mday"} = [];
217                 }
218                 push(@{$linkcache{"$year/$mtag/$mday"}}, $p);
219         }
220                 
221         my $archivebase = 'archives';
222         $archivebase = $config{archivebase} if defined $config{archivebase};
223         $archivebase = $params{archivebase} if defined $params{archivebase};
224   
225         my $pmonth = $params{month};
226         my $pyear  = $params{year};
227         while (((not exists $pagesources{"$archivebase/$pyear/" . sprintf("%02d", $pmonth)}) and ($pyear >= $wikistate{calendar}{minyear})) or ($pmonth == $params{month} and $pyear == $params{year})) {
228                 $pmonth -= 1;
229                 if ($pmonth == 0) {
230                         $pyear -= 1;
231                         $pmonth = 12;
232                 }
233         }
234
235         my $nmonth = $params{month};
236         my $nyear  = $params{year};
237         while (((not exists $pagesources{"$archivebase/$nyear/" . sprintf("%02d", $nmonth)}) and ($nyear <= $wikistate{calendar}{maxyear})) or ($nmonth == $params{month} and $nyear == $params{year})) {
238                 $nmonth += 1;
239                 if ($nmonth == 13) {
240                         $nyear += 1;
241                         $nmonth = 1;
242                 }
243         }
244
245         # Add padding.
246         $pmonth=sprintf("%02d", $pmonth);
247         $nmonth=sprintf("%02d", $nmonth);
248
249         my $calendar="\n";
250
251         # When did this month start?
252         my @monthstart = localtime(timelocal(0,0,0,1,$params{month}-1,$params{year}-1900));
253
254         my $future_dom = 0;
255         my $today      = 0;
256         if ($params{year} == $now[5]+1900 && $params{month} == $now[4]+1) {
257                 $future_dom = $now[3]+1;
258                 $today      = $now[3];
259         }
260
261         # Find out month names for this, next, and previous months
262         my $monthabbrev=strftime_utf8("%b", @monthstart);
263         my $monthname=strftime_utf8("%B", @monthstart);
264         my $pmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900)));
265         my $nmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900)));
266
267         # Calculate URL's for monthly archives.
268         my ($url, $purl, $nurl)=("$monthname $params{year}",'','');
269         if (exists $pagesources{"$archivebase/$params{year}/$params{month}"}) {
270                 $url = htmllink($params{page}, $params{destpage}, 
271                         "$archivebase/$params{year}/".$params{month},
272                         noimageinline => 1,
273                         linktext => "$monthabbrev $params{year}",
274                         title => $monthname);
275         }
276         add_depends($params{page}, "$archivebase/$params{year}/$params{month}",
277                 deptype("presence"));
278         if (exists $pagesources{"$archivebase/$pyear/$pmonth"}) {
279                 $purl = htmllink($params{page}, $params{destpage}, 
280                         "$archivebase/$pyear/$pmonth",
281                         noimageinline => 1,
282                         linktext => "\&larr;",
283                         title => $pmonthname);
284         }
285         add_depends($params{page}, "$archivebase/$pyear/$pmonth",
286                 deptype("presence"));
287         if (exists $pagesources{"$archivebase/$nyear/$nmonth"}) {
288                 $nurl = htmllink($params{page}, $params{destpage}, 
289                         "$archivebase/$nyear/$nmonth",
290                         noimageinline => 1,
291                         linktext => "\&rarr;",
292                         title => $nmonthname);
293         }
294         add_depends($params{page}, "$archivebase/$nyear/$nmonth",
295                 deptype("presence"));
296
297         # Start producing the month calendar
298         $calendar=<<EOF;
299 <table class="month-calendar">
300         <tr>
301         <th class="month-calendar-arrow">$purl</th>
302         <th class="month-calendar-head" colspan="5">$url</th>
303         <th class="month-calendar-arrow">$nurl</th>
304         </tr>
305         <tr>
306 EOF
307
308         # Suppose we want to start the week with day $week_start_day
309         # If $monthstart[6] == 1
310         my $week_start_day = $params{week_start_day};
311
312         my $start_day = 1 + (7 - $monthstart[6] + $week_start_day) % 7;
313         my %downame;
314         my %dowabbr;
315         for my $dow ($week_start_day..$week_start_day+6) {
316                 my @day=localtime(timelocal(0,0,0,$start_day++,$params{month}-1,$params{year}-1900));
317                 my $downame = strftime_utf8("%A", @day);
318                 my $dowabbr = substr($downame, 0, 1);
319                 $downame{$dow % 7}=$downame;
320                 $dowabbr{$dow % 7}=$dowabbr;
321                 $calendar.= qq{\t\t<th class="month-calendar-day-head $downame" title="$downame">$dowabbr</th>\n};
322         }
323
324         $calendar.=<<EOF;
325         </tr>
326 EOF
327
328         my $wday;
329         # we start with a week_start_day, and skip until we get to the first
330         for ($wday=$week_start_day; $wday != $monthstart[6]; $wday++, $wday %= 7) {
331                 $calendar.=qq{\t<tr>\n} if $wday == $week_start_day;
332                 $calendar.=qq{\t\t<td class="month-calendar-day-noday $downame{$wday}">&nbsp;</td>\n};
333         }
334
335         # At this point, either the first is a week_start_day, in which case
336         # nothing has been printed, or else we are in the middle of a row.
337         for (my $day = 1; $day <= month_days(year => $params{year}, month => $params{month});
338              $day++, $wday++, $wday %= 7) {
339                 # At this point, on a week_start_day, we close out a row,
340                 # and start a new one -- unless it is week_start_day on the
341                 # first, where we do not close a row -- since none was started.
342                 if ($wday == $week_start_day) {
343                         $calendar.=qq{\t</tr>\n} unless $day == 1;
344                         $calendar.=qq{\t<tr>\n};
345                 }
346                 
347                 my $tag;
348                 my $key="$params{year}/$params{month}/$day";
349                 if (defined $linkcache{$key}) {
350                         if ($day == $today) {
351                                 $tag='month-calendar-day-this-day';
352                         }
353                         else {
354                                 $tag='month-calendar-day-link';
355                         }
356                         $calendar.=qq{\t\t<td class="$tag $downame{$wday}">};
357                         $calendar.=qq{<div class='popup'>$day<div class='balloon'>};
358                         # Several postings on this page
359                         $calendar.=qq{<ul>};
360                         foreach my $page (@{$linkcache{$key}}) {
361                                 $calendar.= qq{\n\t\t\t<li>};
362                                 my $title;
363                                 if (exists $pagestate{$page}{meta}{title}) {
364                                         $title = "$pagestate{$page}{meta}{title}";
365                                 }
366                                 else {
367                                         $title = pagetitle(IkiWiki::basename($page));
368                                 }
369                                 $calendar.=htmllink($params{page}, $params{destpage}, 
370                                         $page,
371                                         noimageinline => 1,
372                                         linktext => $title,
373                                         title => $title);
374                                 $calendar.= '</li>';
375                         }
376                         $calendar.=qq{\n\t\t</ul>};
377                         $calendar.=qq{</div></div>};
378                         $calendar.=qq{</td>\n};
379                 }
380                 else {
381                         if ($day == $today) {
382                                 $tag='month-calendar-day-this-day';
383                         }
384                         elsif ($day == $future_dom) {
385                                 $tag='month-calendar-day-future';
386                         }
387                         else {
388                                 $tag='month-calendar-day-nolink';
389                         }
390                         $calendar.=qq{\t\t<td class="$tag $downame{$wday}">$day</td>\n};
391                 }
392         }
393
394         # finish off the week
395         for (; $wday != $week_start_day; $wday++, $wday %= 7) {
396                 $calendar.=qq{\t\t<td class="month-calendar-day-noday $downame{$wday}">&nbsp;</td>\n};
397         }
398         $calendar.=<<EOF;
399         </tr>
400 </table>
401 EOF
402
403         return $calendar;
404 }
405
406 sub format_year (@) {
407         my %params=@_;
408
409         my @post_months;
410         foreach my $p (pagespec_match_list($params{page}, 
411                                 "creation_year($params{year}) and ($params{pages})",
412                                 # add presence dependencies to update
413                                 # year calendar's links to months when
414                                 # pages are added/removed
415                                 deptype => deptype("presence"))) {
416                 my $mtime = $IkiWiki::pagectime{$p};
417                 my @date  = localtime($mtime);
418                 my $month = $date[4] + 1;
419                 my $year  = $date[5] + 1900;
420
421                 $post_months[$month]++;
422         }
423                 
424         my $calendar="\n";
425
426         my $archivebase = 'archives';
427         $archivebase = $config{archivebase} if defined $config{archivebase};
428         $archivebase = $params{archivebase} if defined $params{archivebase};
429         
430         my $pyear = $params{year}  - 1;
431         while ((not exists $pagesources{"$archivebase/$pyear"}) and ($pyear > $wikistate{calendar}{minyear})) {
432                 $pyear -= 1;
433         }
434         my $nyear = $params{year}  + 1;
435         while ((not exists $pagesources{"$archivebase/$nyear"}) and ($nyear < $wikistate{calendar}{maxyear})) {
436                 $nyear += 1;
437         }
438
439         my $thisyear = $now[5]+1900;
440         my $future_month = 0;
441         $future_month = $now[4]+1 if $params{year} == $thisyear;
442
443         # calculate URL's for previous and next years
444         my ($url, $purl, $nurl)=("$params{year}",'','');
445         if (exists $pagesources{"$archivebase/$params{year}"}) {
446                 $url = htmllink($params{page}, $params{destpage}, 
447                         "$archivebase/$params{year}",
448                         noimageinline => 1,
449                         linktext => $params{year},
450                         title => $params{year});
451         }
452         add_depends($params{page}, "$archivebase/$params{year}", deptype("presence"));
453         if (exists $pagesources{"$archivebase/$pyear"}) {
454                 $purl = htmllink($params{page}, $params{destpage}, 
455                         "$archivebase/$pyear",
456                         noimageinline => 1,
457                         linktext => "\&larr;",
458                         title => $pyear);
459         }
460         add_depends($params{page}, "$archivebase/$pyear", deptype("presence"));
461         if (exists $pagesources{"$archivebase/$nyear"}) {
462                 $nurl = htmllink($params{page}, $params{destpage}, 
463                         "$archivebase/$nyear",
464                         noimageinline => 1,
465                         linktext => "\&rarr;",
466                         title => $nyear);
467         }
468         add_depends($params{page}, "$archivebase/$nyear", deptype("presence"));
469
470         # Start producing the year calendar
471         my $m=$params{months_per_row}-2;
472         $calendar=<<EOF;
473 <table class="year-calendar">
474         <tr>
475         <th class="year-calendar-arrow">$purl</th>
476         <th class="year-calendar-head" colspan="$m">$url</th>
477         <th class="year-calendar-arrow">$nurl</th>
478         </tr>
479         <tr>
480                 <th class="year-calendar-subhead" colspan="$params{months_per_row}">Months</th>
481         </tr>
482 EOF
483
484         for (my $month = 1; $month <= 12; $month++) {
485                 my @day=localtime(timelocal(0,0,0,15,$month-1,$params{year}-1900));
486                 my $murl;
487                 my $monthname = strftime_utf8("%B", @day);
488                 my $monthabbr = strftime_utf8("%b", @day);
489                 $calendar.=qq{\t<tr>\n}  if ($month % $params{months_per_row} == 1);
490                 my $tag;
491                 my $mtag=sprintf("%02d", $month);
492                 if ($month == $params{month} && $thisyear == $params{year}) {
493                         $tag = 'year-calendar-this-month';
494                 }
495                 elsif ($pagesources{"$archivebase/$params{year}/$mtag"}) {
496                         $tag = 'year-calendar-month-link';
497                 } 
498                 elsif ($future_month && $month >= $future_month) {
499                         $tag = 'year-calendar-month-future';
500                 } 
501                 else {
502                         $tag = 'year-calendar-month-nolink';
503                 }
504
505                 if ($pagesources{"$archivebase/$params{year}/$mtag"} &&
506                     $post_months[$mtag]) {
507                         $murl = htmllink($params{page}, $params{destpage}, 
508                                 "$archivebase/$params{year}/$mtag",
509                                 noimageinline => 1,
510                                 linktext => $monthabbr,
511                                 title => $monthname);
512                         $calendar.=qq{\t<td class="$tag">};
513                         $calendar.=$murl;
514                         $calendar.=qq{\t</td>\n};
515                 }
516                 else {
517                         $calendar.=qq{\t<td class="$tag">$monthabbr</td>\n};
518                 }
519                 add_depends($params{page}, "$archivebase/$params{year}/$mtag",
520                         deptype("presence"));
521
522                 $calendar.=qq{\t</tr>\n} if ($month % $params{months_per_row} == 0);
523         }
524
525         $calendar.=<<EOF;
526 </table>
527 EOF
528
529         return $calendar;
530 }
531
532 sub setnextchange ($$) {
533         my $page=shift;
534         my $timestamp=shift;
535
536         if (! exists $pagestate{$page}{calendar}{nextchange} ||
537             $pagestate{$page}{calendar}{nextchange} > $timestamp) {
538                 $pagestate{$page}{calendar}{nextchange}=$timestamp;
539         }
540 }
541
542 sub preprocess (@) {
543         my %params=@_;
544
545         my $thisyear=1900 + $now[5];
546         my $thismonth=1 + $now[4];
547
548         $params{pages} = "*"            unless defined $params{pages};
549         $params{type}  = "month"        unless defined $params{type};
550         $params{week_start_day} = 0     unless defined $params{week_start_day};
551         $params{months_per_row} = 3     unless defined $params{months_per_row};
552         $params{year}  = $thisyear      unless defined $params{year};
553         $params{month} = $thismonth     unless defined $params{month};
554
555         my $relativeyear=0;
556         if ($params{year} < 1) {
557                 $relativeyear=1;
558                 $params{year}=$thisyear+$params{year};
559         }
560         my $relativemonth=0;
561         if ($params{month} < 1) {
562                 $relativemonth=1;
563                 my $monthoff=$params{month};
564                 $params{month}=($thismonth+$monthoff) % 12;
565                 $params{month}=12 if $params{month}==0;
566                 my $yearoff=POSIX::ceil(($thismonth-$params{month}) / -12)
567                         - int($monthoff / 12);
568                 $params{year}-=$yearoff;
569         }
570         
571         $params{month} = sprintf("%02d", $params{month});
572         
573         if ($params{type} eq 'month' && $params{year} == $thisyear
574             && $params{month} == $thismonth) {
575                 # calendar for current month, updates next midnight
576                 setnextchange($params{destpage}, ($time
577                         + (60 - $now[0])                # seconds
578                         + (59 - $now[1]) * 60           # minutes
579                         + (23 - $now[2]) * 60 * 60      # hours
580                 ));
581         }
582         elsif ($params{type} eq 'month' &&
583                (($params{year} == $thisyear && $params{month} > $thismonth) ||
584                 $params{year} > $thisyear)) {
585                 # calendar for upcoming month, updates 1st of that month
586                 setnextchange($params{destpage},
587                         timelocal(0, 0, 0, 1, $params{month}-1, $params{year}));
588         }
589         elsif (($params{type} eq 'year' && $params{year} == $thisyear) ||
590                $relativemonth) {
591                 # Calendar for current year updates 1st of next month.
592                 # Any calendar relative to the current month also updates
593                 # then.
594                 if ($thismonth < 12) {
595                         setnextchange($params{destpage},
596                                 timelocal(0, 0, 0, 1, $thismonth+1-1, $params{year}));
597                 }
598                 else {
599                         setnextchange($params{destpage},
600                                 timelocal(0, 0, 0, 1, 1-1, $params{year}+1));
601                 }
602         }
603         elsif ($relativeyear) {
604                 # Any calendar relative to the current year updates 1st
605                 # of next year.
606                 setnextchange($params{destpage},
607                         timelocal(0, 0, 0, 1, 1-1, $thisyear+1));
608         }
609         elsif ($params{type} eq 'year' && $params{year} > $thisyear) {
610                 # calendar for upcoming year, updates 1st of that year
611                 setnextchange($params{destpage},
612                         timelocal(0, 0, 0, 1, 1-1, $params{year}));
613         }
614         else {
615                 # calendar for past month or year, does not need
616                 # to update any more
617                 delete $pagestate{$params{destpage}}{calendar};
618         }
619
620         my $calendar="";
621         if ($params{type} eq 'month') {
622                 $calendar=format_month(%params);
623         }
624         elsif ($params{type} eq 'year') {
625                 $calendar=format_year(%params);
626         }
627
628         return "\n<div><div class=\"calendar\">$calendar</div></div>\n";
629 } #}}
630
631 sub needsbuild (@) {
632         my $needsbuild=shift;
633         foreach my $page (keys %pagestate) {
634                 if (exists $pagestate{$page}{calendar}{nextchange}) {
635                         if ($pagestate{$page}{calendar}{nextchange} <= $time) {
636                                 # force a rebuild so the calendar shows
637                                 # the current day
638                                 push @$needsbuild, $pagesources{$page};
639                         }
640                         if (exists $pagesources{$page} && 
641                             grep { $_ eq $pagesources{$page} } @$needsbuild) {
642                                 # remove state, will be re-added if
643                                 # the calendar is still there during the
644                                 # rebuild
645                                 delete $pagestate{$page}{calendar};
646                         }
647                 }
648         }
649
650         return $needsbuild;
651 }
652
653 sub scan (@) {
654         my %params=@_;
655         my $page=$params{page};
656
657         # Check if year pages have to be generated
658         if (pagespec_match($page, $config{archive_pagespec})) {
659                 my @ctime = localtime($IkiWiki::pagectime{$page});
660                 gencalendaryear($ctime[5]+1900);
661                 gencalendarmonth($ctime[5]+1900, $ctime[4]+1);
662         }
663 }
664
665 1