diff options
-rw-r--r-- | IkiWiki/Plugin/calendar.pm | 270 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rw-r--r-- | doc/ikiwiki-calendar.mdwn | 7 | ||||
-rw-r--r-- | doc/ikiwiki/directive/calendar.mdwn | 21 | ||||
-rw-r--r-- | doc/plugins/calendar.mdwn | 23 | ||||
-rw-r--r-- | doc/todo/calendar_autocreate.mdwn | 2 | ||||
-rwxr-xr-x | t/img.t | 3 |
7 files changed, 298 insertions, 34 deletions
diff --git a/IkiWiki/Plugin/calendar.pm b/IkiWiki/Plugin/calendar.pm index 4df7070ac..23246757b 100644 --- a/IkiWiki/Plugin/calendar.pm +++ b/IkiWiki/Plugin/calendar.pm @@ -25,11 +25,17 @@ use Time::Local; my $time=time; my @now=localtime($time); +my %changed; sub import { + hook(type => "checkconfig", id => "calendar", call => \&checkconfig); hook(type => "getsetup", id => "calendar", call => \&getsetup); hook(type => "needsbuild", id => "calendar", call => \&needsbuild); hook(type => "preprocess", id => "calendar", call => \&preprocess); + hook(type => "scan", id => "calendar", call => \&scan); + hook(type => "build_affected", id => "calendar", call => \&build_affected); + + IkiWiki::loadplugin("transient"); } sub getsetup () { @@ -49,11 +55,41 @@ sub getsetup () { archive_pagespec => { type => "pagespec", example => "page(posts/*) and !*/Discussion", - description => "PageSpec of pages to include in the archives; used by ikiwiki-calendar command", + description => "PageSpec of pages to include in the archives, if option `calendar_autocreate` is true.", link => 'ikiwiki/PageSpec', safe => 1, rebuild => 0, }, + calendar_autocreate => { + type => "boolean", + example => 1, + description => "autocreate new calendar pages?", + safe => 1, + rebuild => undef, + }, + calendar_fill_gaps => { + type => "boolean", + example => 1, + default => 1, + description => "if set, when building calendar pages, also build pages of year and month when no pages were published (building empty calendars).", + safe => 1, + rebuild => 0, + }, +} + +sub checkconfig () { + if (! defined $config{calendar_autocreate}) { + $config{calendar_autocreate} = defined $config{archivebase}; + } + if (! defined $config{archive_pagespec}) { + $config{archive_pagespec} = '*'; + } + if (! defined $config{archivebase}) { + $config{archivebase} = 'archives'; + } + if (! defined $config{calendar_fill_gaps}) { + $config{calendar_fill_gaps} = 1; + } } sub is_leap_year (@) { @@ -70,6 +106,184 @@ sub month_days { return $days_in_month; } +sub build_affected { + my %affected; + my ($ayear, $amonth, $valid); + + foreach my $year (keys %changed) { + ($ayear, $valid) = nextyear($year, $config{archivebase}); + $affected{calendarlink($ayear)} = sprintf(gettext("building calendar for %s, its previous or next year has changed"), $ayear) if ($valid); + ($ayear, $valid) = previousyear($year, $config{archivebase}); + $affected{calendarlink($ayear)} = sprintf(gettext("building calendar for %s, its previous or next year has changed"), $ayear) if ($valid); + foreach my $month (keys $changed{$year}) { + ($ayear, $amonth, $valid) = nextmonth($year, $month, $config{archivebase}); + $affected{calendarlink($ayear, sprintf("%02d", $amonth))} = sprintf(gettext("building calendar for %s/%s, its previous or next month has changed"), $amonth, $ayear) if ($valid); + ($ayear, $amonth, $valid) = previousmonth($year, $month, $config{archivebase}); + $affected{calendarlink($ayear, sprintf("%02d", $amonth))} = sprintf(gettext("building calendar for %s/%s, its previous or next month has changed"), $amonth, $ayear) if ($valid); + } + } + + return %affected; +} + +sub autocreate { + my ($page, $pagefile, $year, $month) = @_; + my $message=sprintf(gettext("creating calendar page %s"), $page); + debug($message); + + my $template; + if (defined $month) { + $template=template("calendarmonth.tmpl"); + } else { + $template=template("calendaryear.tmpl"); + } + $template->param(year => $year); + $template->param(month => $month) if defined $month; + $template->param(pagespec => $config{archive_pagespec}); + + my $dir = $IkiWiki::Plugin::transient::transientdir; + + writefile($pagefile, $dir, $template->output); +} + +sub calendarlink($;$) { + my ($year, $month) = @_; + if (defined $month) { + return $config{archivebase} . "/" . $year . "/" . $month; + } else { + return $config{archivebase} . "/" . $year; + } +} + +sub gencalendarmonth{ + my $year = shift; + my $month = sprintf("%02d", shift); + + my $page = calendarlink($year, $month); + my $pagefile = newpagefile($page, $config{default_pageext}); + add_autofile( + $pagefile, "calendar", + sub {return autocreate($page, $pagefile, $year, $month);} + ); +} + +sub gencalendaryear { + my $year = shift; + my %params = @_; + + # Building year page + my $page = calendarlink($year); + my $pagefile = newpagefile($page, $config{default_pageext}); + add_autofile( + $pagefile, "calendar", + sub {return autocreate($page, $pagefile, $year);} + ); + + if (not exists $wikistate{calendar}{minyear}) { + $wikistate{calendar}{minyear} = $year; + } + if (not exists $wikistate{calendar}{maxyear}) { + $wikistate{calendar}{maxyear} = $year; + } + + if ($config{calendar_fill_gaps}) { + # Building month pages + foreach my $month (1 .. 12) { + gencalendarmonth($year, $month); + } + + # Filling potential gaps in years (e.g. calendar goes from 2010 to 2014, + # and we just added year 2005. We have to add years 2006 to 2009). + return if $params{norecurse}; + if ($wikistate{calendar}{minyear} > $year) { + foreach my $other ($year + 1 .. $wikistate{calendar}{minyear} - 1) { + gencalendaryear($other, norecurse => 1); + } + $wikistate{calendar}{minyear} = $year; + } + if ($wikistate{calendar}{maxyear} < $year) { + foreach my $other ($wikistate{calendar}{maxyear} + 1 .. $year - 1) { + gencalendaryear($other, norecurse => 1); + } + $wikistate{calendar}{maxyear} = $year; + } + } + if ($year < $wikistate{calendar}{minyear}) { + $wikistate{calendar}{minyear} = $year; + } + if ($year > $wikistate{calendar}{maxyear}) { + $wikistate{calendar}{maxyear} = $year; + } +} + +sub previousmonth($$$) { + my $year = shift; + my $month = shift; + my $archivebase = shift; + + if (not exists $wikistate{calendar}{minyear}) { + $wikistate{calendar}{minyear} = $year; + } + + my $pmonth = $month; + my $pyear = $year; + while ((not exists $pagesources{"$archivebase/$pyear/" . sprintf("%02d", $pmonth)}) or ($pmonth == $month and $pyear == $year)) { + $pmonth -= 1; + if ($pmonth == 0) { + $pyear -= 1; + $pmonth = 12; + return ($pyear, $pmonth, 0) unless $pyear >= $wikistate{calendar}{minyear}; + } + } + return ($pyear, $pmonth, 1); +} + +sub nextmonth($$$) { + my $year = shift; + my $month = shift; + my $archivebase = shift; + + if (not exists $wikistate{calendar}{maxyear}) { + $wikistate{calendar}{maxyear} = $year; + } + + my $nmonth = $month; + my $nyear = $year; + while ((not exists $pagesources{"$archivebase/$nyear/" . sprintf("%02d", $nmonth)}) or ($nmonth == $month and $nyear == $year)) { + $nmonth += 1; + if ($nmonth == 13) { + $nyear += 1; + $nmonth = 1; + return ($nyear, $nmonth, 0) unless $nyear <= $wikistate{calendar}{maxyear}; + } + } + return ($nyear, $nmonth, 1); +} + +sub previousyear($$) { + my $year = shift; + my $archivebase = shift; + + my $pyear = $year - 1; + while (not exists $pagesources{"$archivebase/$pyear"}) { + $pyear -= 1; + return ($pyear, 0) unless ($pyear >= $wikistate{calendar}{minyear}); + } + return ($pyear, 1); +} + +sub nextyear($$) { + my $year = shift; + my $archivebase = shift; + + my $nyear = $year + 1; + while (not exists $pagesources{"$archivebase/$nyear"}) { + $nyear += 1; + return ($nyear, 0) unless ($nyear <= $wikistate{calendar}{maxyear}); + } + return ($nyear, 1); +} + sub format_month (@) { my %params=@_; @@ -92,20 +306,12 @@ sub format_month (@) { push(@{$linkcache{"$year/$mtag/$mday"}}, $p); } - my $pmonth = $params{month} - 1; - my $nmonth = $params{month} + 1; - my $pyear = $params{year}; - my $nyear = $params{year}; - - # Adjust for January and December - if ($params{month} == 1) { - $pmonth = 12; - $pyear--; - } - if ($params{month} == 12) { - $nmonth = 1; - $nyear++; - } + my $archivebase = 'archives'; + $archivebase = $config{archivebase} if defined $config{archivebase}; + $archivebase = $params{archivebase} if defined $params{archivebase}; + + my ($pyear, $pmonth, $pvalid) = previousmonth($params{year}, $params{month}, $archivebase); + my ($nyear, $nmonth, $nvalid) = nextmonth($params{year}, $params{month}, $archivebase); # Add padding. $pmonth=sprintf("%02d", $pmonth); @@ -129,10 +335,6 @@ sub format_month (@) { my $pmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900))); my $nmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900))); - my $archivebase = 'archives'; - $archivebase = $config{archivebase} if defined $config{archivebase}; - $archivebase = $params{archivebase} if defined $params{archivebase}; - # Calculate URL's for monthly archives. my ($url, $purl, $nurl)=("$monthname $params{year}",'',''); if (exists $pagesources{"$archivebase/$params{year}/$params{month}"}) { @@ -274,7 +476,7 @@ EOF sub format_year (@) { my %params=@_; - + my @post_months; foreach my $p (pagespec_match_list($params{page}, "creation_year($params{year}) and ($params{pages})", @@ -290,18 +492,18 @@ sub format_year (@) { } my $calendar="\n"; + + my $archivebase = 'archives'; + $archivebase = $config{archivebase} if defined $config{archivebase}; + $archivebase = $params{archivebase} if defined $params{archivebase}; - my $pyear = $params{year} - 1; - my $nyear = $params{year} + 1; + my ($pyear, $pvalid) = previousyear($params{year}, $archivebase); + my ($nyear, $nvalid) = nextyear($params{year}, $archivebase); my $thisyear = $now[5]+1900; my $future_month = 0; $future_month = $now[4]+1 if $params{year} == $thisyear; - my $archivebase = 'archives'; - $archivebase = $config{archivebase} if defined $config{archivebase}; - $archivebase = $params{archivebase} if defined $params{archivebase}; - # calculate URL's for previous and next years my ($url, $purl, $nurl)=("$params{year}",'',''); if (exists $pagesources{"$archivebase/$params{year}"}) { @@ -431,6 +633,7 @@ sub preprocess (@) { } $params{month} = sprintf("%02d", $params{month}); + $changed{$params{year}}{$params{month}} = 1; if ($params{type} eq 'month' && $params{year} == $thisyear && $params{month} == $thismonth) { @@ -508,7 +711,22 @@ sub needsbuild (@) { } } } + return $needsbuild; } +sub scan (@) { + my %params=@_; + my $page=$params{page}; + + return unless $config{calendar_autocreate}; + + # Check if year pages have to be generated + if (pagespec_match($page, $config{archive_pagespec})) { + my @ctime = localtime($IkiWiki::pagectime{$page}); + gencalendaryear($ctime[5]+1900); + gencalendarmonth($ctime[5]+1900, $ctime[4]+1); + } +} + 1 diff --git a/debian/changelog b/debian/changelog index 014066e32..1cf3dce8b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ ikiwiki (3.20141017) UNRELEASED; urgency=medium + [ Joey Hess ] * Added ikiwiki-comment program. * Add missing build-depends on libcgi-formbuilder-perl, needed for t/relativity.t @@ -7,6 +8,11 @@ ikiwiki (3.20141017) UNRELEASED; urgency=medium * Set Debian package maintainer to Simon McVittie as I'm retiring from Debian. + [ Simon McVittie ] + * calendar: add calendar_autocreate option, with which "ikiwiki --refresh" + can mostly supersede the ikiwiki-calendar command. + Thanks, Louis Paternault + -- Joey Hess <joeyh@debian.org> Mon, 20 Oct 2014 12:04:49 -0400 ikiwiki (3.20141016) unstable; urgency=medium diff --git a/doc/ikiwiki-calendar.mdwn b/doc/ikiwiki-calendar.mdwn index d311a1859..fd3244694 100644 --- a/doc/ikiwiki-calendar.mdwn +++ b/doc/ikiwiki-calendar.mdwn @@ -46,6 +46,13 @@ An example crontab: This command uses two [[templates]] to generate the pages, `calendarmonth.tmpl` and `calendaryear.tmpl`. +# [[plugins/calendar]] setup option + +Most of the goals of this command can be replaced by setting up +`calendar_autocreate` setup option (of plugin [[plugins/calendar]]), and +running `ikiwiki -setup you.setup`. The only thing that `ikiwiki-calendar` can +do and that `ikiwiki` cannot is forcing page generation (using `-f` switch). + # AUTHOR Joey Hess <joey@ikiwiki.info> diff --git a/doc/ikiwiki/directive/calendar.mdwn b/doc/ikiwiki/directive/calendar.mdwn index cb40f884e..4c2b99ccb 100644 --- a/doc/ikiwiki/directive/calendar.mdwn +++ b/doc/ikiwiki/directive/calendar.mdwn @@ -25,14 +25,23 @@ in the sidebar, you'll also need to create these archive pages. They typically use this directive to display a calendar, and also use [[inline]] to display or list pages created in the given time frame. -The `ikiwiki-calendar` command can be used to automatically generate the -archive pages. It also refreshes the wiki, updating the calendars to -highlight the current day. This command is typically run at midnight from -cron. +## Generating archive pages + +If [[!iki plugins/calendar desc=option]] `calendar_autocreate` is not set, the +[[!iki ikiwiki-calendar]] command can be used to automatically generate the archive +pages. It also refreshes the wiki, updating the calendars to highlight the +current day. This command is typically run at midnight from cron. An example crontab: - 0 0 * * * ikiwiki-calendar ~/ikiwiki.setup "posts/* and !*/Discussion" + 0 0 * * * ikiwiki-calendar ~/ikiwiki.setup "posts/* and !*/Discussion" + + +With [[!iki plugins/calendar desc="setup option"]] `calendar_autocreate`, +all this work is done by `ikiwiki` itself. Thus, the crontab command can be +replaced by: + + 0 0 * * * ikiwiki --setup ~/ikiwiki.setup --refresh ## usage @@ -45,7 +54,7 @@ An example crontab: for the whole wiki by setting `archivebase` in ikiwiki's setup file. Calendars link to pages under here, with names like "2010/04" and "2010". These pages can be automatically created using the - `ikiwiki-calendar` program. + `calendar_autocreate` [[!iki plugins/calendar desc="setup option"]]. * `year` - The year for which the calendar is requested. Defaults to the current year. Can also use -1 to refer to last year, and so on. * `month` - The numeric month for which the calendar is requested, in the diff --git a/doc/plugins/calendar.mdwn b/doc/plugins/calendar.mdwn index 76e718a3b..8d18ed081 100644 --- a/doc/plugins/calendar.mdwn +++ b/doc/plugins/calendar.mdwn @@ -5,7 +5,28 @@ This plugin provides a [[ikiwiki/directive/calendar]] [[ikiwiki/directive]]. The directive displays a calendar, similar to the typical calendars shown on some blogs. -The [[ikiwiki-calendar]] command is used to keep the calendar up-to-date. +The [[ikiwiki-calendar]] command is used to force generating year and month +pages from templates (overriding the existing ones). + +## Setup options + +* `archivebase` - Default value for [[ikiwiki/directive/calendar]] directive + option of the same name. +* `archive_pagespec` - [[ikiwiki/PageSpec]] of pages to include in the + archives, if option `calendar_autocreate` is on. It defaults to `*`. +* `calendar_autocreate` - Control whether new archive pages are created as + needed. It defaults to being done only if option `archivebase` is set. +* `calendar_fill_gaps` - If set (and `calendar_autocreate` is set as well), + build calendar pages of empty years and months (but does not build pages older + than the older page, and younger than the younger page of the pagespec). If + not, those empty calendar pages will be skipped. *Please note:* + * The archive pages will not be automatically updated if this option changes. + It is up to the user to delete relevant pages, and rebuild the wiki. + * When `calendar_fill_gaps` is set, and a post is deleted, making the + corresponding year/month empty, the corresponding page is left, and shows + an empty calendar. This is on purpose, not to break any external link + pointing to this particular page. If you do not like it, delete the + relevant pages, and rebuild the wiki. ## CSS diff --git a/doc/todo/calendar_autocreate.mdwn b/doc/todo/calendar_autocreate.mdwn index 371079d68..c1f9c454e 100644 --- a/doc/todo/calendar_autocreate.mdwn +++ b/doc/todo/calendar_autocreate.mdwn @@ -1,5 +1,7 @@ Here is a patch that makes [[ikiwiki-calendar]] almost useless. +> [[merged|done]], thanks! --[[smcv]] + It adds some options, the main one being `calendar_autocreate`, which is similar to the `tag_autocreate` option of the [[tag|plugins/tag]]: it create archive pages when needed. @@ -26,7 +26,8 @@ ok(! system("rm -rf t/tmp; mkdir -p t/tmp/in")); ok(! system("cp t/img/redsquare.png t/tmp/in/redsquare.png")); if ($SVGS_WORK) { - writefile("emptysquare.svg", "t/tmp/in", '<svg width="30" height="30"/>'); + writefile("emptysquare.svg", "t/tmp/in", + '<svg width="30" height="30"><rect x="0" y="0" width="30" height="30" fill="blue"/></svg>'); } # using different image sizes for different pages, so the pagenumber selection can be tested easily |