X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/af2ea16765ffcae98d2f33daef64aca71e7e199b..2f3a4f689948a9f51699efed4d8c6b9fdc167100:/doc/patchqueue/calendar_--_archive_browsing_via_a_calendar_frontend.mdwn diff --git a/doc/patchqueue/calendar_--_archive_browsing_via_a_calendar_frontend.mdwn b/doc/patchqueue/calendar_--_archive_browsing_via_a_calendar_frontend.mdwn index b971c3839..fe0dba1aa 100644 --- a/doc/patchqueue/calendar_--_archive_browsing_via_a_calendar_frontend.mdwn +++ b/doc/patchqueue/calendar_--_archive_browsing_via_a_calendar_frontend.mdwn @@ -15,3 +15,642 @@ The year and month entities in the out put have links to archive index pages, wh \[[meta title="Archives for 2006/01"]] \[[inline rootpage="blog" atom="no" rss="no" show="0" pages="blog/* and !*/Discussion and creation_year(2006) and creation_month(01)" ]] + +I'll send in the patch via email. + + +ManojSrivastava + +------ + +Since this is a little bit er, stalled, I'll post here the stuff Manoj +mailed me, and my response to it. --[[Joey]] + +
+#! /usr/bin/perl
+#                              -*- Mode: Cperl -*- 
+# calendar.pm --- 
+# Author           : Manoj Srivastava ( srivasta@glaurung.internal.golden-gryphon.com ) 
+# Created On       : Fri Dec  8 16:05:48 2006
+# Created On Node  : glaurung.internal.golden-gryphon.com
+# Last Modified By : Manoj Srivastava
+# Last Modified On : Sun Dec 10 01:53:22 2006
+# Last Machine Used: glaurung.internal.golden-gryphon.com
+# Update Count     : 139
+# Status           : Unknown, Use with caution!
+# HISTORY          : 
+# Description      : 
+# 
+# arch-tag: 2aa737c7-3d62-4918-aaeb-fd85b4b1384c
+#
+# Copyright (c) 2006 Manoj Srivastava 
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+require 5.002;
+package IkiWiki::Plugin::calendar;
+
+use warnings;
+use strict;
+use IkiWiki '1.00';
+use Time::Local;
+
+our $VERSION = "0.1";
+my $file = __FILE__;
+
+my %calpages;
+my %cache;
+my %linkcache;
+
+my $index=1;
+my @now=localtime();
+
+=head1 NAME
+
+calendar - Add links for the current month's, current year's, and older archived postings
+
+=cut
+
+=head1 SYNOPSIS
+
+To invoke the calendar, just use the preprocessor directive (options
+and variations are detailed below):
+
+  [[calendar ]]
+
+or
+
+  [[calendar type="month" pages="blog/* and !*/Discussion"]]
+
+or
+
+  [[calendar type="year"  year="2005" pages="blog/* and !*/Discussion"]]
+
+=cut
+
+
+=head1 DESCRIPTION
+
+This plugin is inspired by the calendar plugin for Blosxom, but
+derives no code from it. This plugin is essentially a fancy front end
+to archives of previous pages, usually used for blogs. It can produce
+a calendar for a given month, or a list of months for a given year. 
+
+The year and month entities in the out put have links to archive index
+pages, which are supposed to exist already. The idea is to create an
+archives hierarchy, rooted in the subdirectory specified in the
+site wide customization variable, I. I
+defaults to C.  Links are created to pages
+C<$archivebase/$year> and C<$archivebase/$year/$month>. If one creates
+annual and monthly indices, for example, by using something like this
+sample from my I (warning: line split for
+readability):
+
+   \[[meta title="Archives for 2006/01"]]
+   \[[inline rootpage="blog" atom="no" rss="no" show="0"
+     pages="blog/* and !*/Discussion and creation_year(2006)
+            and creation_month(01)"
+   ]]
+
+=cut
+
+=head1 OPTIONS
+
+=over
+
+=item B
+
+Used to specify the type of calendar wanted. Can be one of C or
+C. The default is a month view calendar.
+
+=item B
+
+Specifies the C used to get pages to match for
+linking. Usually this should be something like C.
+Defaults to C<*>.
+
+=item B
+
+The year for which the calendar is requested. Defaults to the current year.
+
+=item B
+
+The numeric month for which the calendar is requested, in the range
+1..12. Used only for the month view calendar, and defaults to the
+current month.
+
+=item B
+
+A number, in the range 0..6, which represents the day of the week that
+the month calendar starts with. 0 is Sunday, 1 is Monday, and so
+on. Defaults to 0, which is Sunday.
+
+=item B
+
+In the annual calendar, number of months to place in each row. Defaults to 3.
+
+=back
+
+=cut
+
+=head1 Classes for CSS control
+
+The output is liberally sprinkled with classes, for fine grained CSS
+customization.
+
+=over
+
+=item C 
+
+The month calendar as a whole
+
+=item C
+
+The head of the month calendar (ie,"March"), localized to the environment.
+
+=item C
+
+A column head in the month calendar (ie, a day-of-week abbreviation),
+localized.
+
+=item C, C,
+  C, C,
+  C 
+
+The day squares on the month calendar, for days that don't exist
+(before or after the month itself), that don't have stories, that do
+have stories, that are in the future, or are that currently selected,
+respectively (today).
+
+=item Day-of-week-name
+
+Each day square is also given a class matching its day of week, this
+can be used to high light weekends. This is also localized.
+
+=item C
+
+The year calendar as a whole
+
+=item C
+
+The head of the year calendar (ie, "2006")
+
+=item C
+
+For example, "Months"
+
+=item C, C,
+  C, C
+
+The month squares on the year calendar, for months with stories,
+without, in the future, and currently selected, respectively.
+
+=back
+
+=cut
+
+
+sub import {
+  hook(type => "preprocess", id => "calendar", call => \&preprocess);
+  hook(type => "format", id => "calendar", call => \&format);
+}
+
+sub preprocess (@) {
+  my %params=@_;
+  $params{pages} = "*"            unless defined $params{pages};
+  $params{type}  = "month"        unless defined $params{type};
+  $params{year}  = 1900 + $now[5] unless defined $params{year};
+  $params{month} = sprintf("%02d", $params{month}) if defined  $params{month};
+  $params{month} = 1    + $now[4] unless defined $params{month};
+  $params{week_start_day} = 0     unless defined $params{week_start_day};
+  $params{months_per_row} = 3     unless defined $params{months_per_row};
+
+  # Store parameters (could be multiple calls per page)
+  $calpages{$params{destpage}}{$index} = \%params;
+
+  return "\n
" . $index++ . "
\n"; +} + +sub is_leap_year (@) { + my %params=@_; + return ($params{year} % 4 == 0 && (($params{year} % 100 != 0) || $params{year} % 400 ==0)) ; +} + + +sub month_days { + my %params=@_; + my $days_in_month = (31,28,31,30,31,30,31,31,30,31,30,31)[$params{month}-1]; + if ($params{month} == 2 && is_leap_year(%params)) { + $days_in_month++; + } + return $days_in_month; +} + + +sub format_month (@) { + my %params=@_; + my $pagespec = $params{pages}; + my $year = $params{year}; + my $month = $params{month}; + + my $calendar="\n"; + + # When did this month start? + my @monthstart = localtime(timelocal(0,0,0,1,$month-1,$year-1900)); + + my $future_dom = 0; + my $today = 0; + $future_dom = $now[3]+1 if ($year == $now[5]+1900 && $month == $now[4]+1); + $today = $now[3] if ($year == $now[5]+1900 && $month == $now[4]+1); + + # Calculate month names for next month, and previous months + my $pmonth = $month - 1; + my $nmonth = $month + 1; + my $pyear = $year; + my $nyear = $year; + + # Adjust for January and December + if ($month == 1) { $pmonth = 12; $pyear--; } + if ($month == 12) { $nmonth = 1; $nyear++; } + + # Find out month names for this, next, and previous months + my $monthname=POSIX::strftime("%B", @monthstart); + my $pmonthname= + POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900))); + my $nmonthname= + POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900))); + + # Calculate URL's for monthly archives, and article counts + my $archivebase = 'archives'; + $archivebase = $config{archivebase} if defined $config{archivebase}; + + my ($url, $purl, $nurl)=("$monthname",'',''); + my ($count, $pcount, $ncount) = (0,0,0); + + if (exists $cache{$pagespec}{"$year/$month"}) { + $url = htmllink($params{page}, $params{destpage}, + "$archivebase/$year/" . sprintf("%02d", $month), + 0,0," $monthname "); + } + + if (exists $cache{$pagespec}{"$pyear/$pmonth"}) { + $purl = htmllink($params{page}, $params{destpage}, + "$archivebase/$pyear/" . sprintf("%02d", $pmonth), + 0,0," $pmonthname "); + } + if (exists $cache{$pagespec}{"$nyear/$nmonth"}) { + $nurl = htmllink($params{page}, $params{destpage}, + "$archivebase/$nyear/" . sprintf("%02d", $nmonth), + 0,0," $nmonthname "); + } + + # Start producing the month calendar + $calendar=< + + $purl + $url + $nurl + + +EOF + # Suppose we want to start the week with day $week_start_day + # If $monthstart[6] == 1 + my $week_start_day = $params{week_start_day}; + + my $start_day = 1 + (7 - $monthstart[6] + $week_start_day) % 7; + my %downame; + my %dowabbr; + for my $dow ($week_start_day..$week_start_day+6) { + my @day=localtime(timelocal(0,0,0,$start_day++,$month-1,$year-1900)); + my $downame = POSIX::strftime("%A", @day); + my $dowabbr = POSIX::strftime("%a", @day); + $downame{$dow % 7}=$downame; + $dowabbr{$dow % 7}=$dowabbr; + $calendar.= + qq{ $dowabbr\n}; + } + + $calendar.=< +EOF + + my $wday; + # we start with a week_start_day, and skip until we get to the first + for ($wday=$week_start_day; $wday != $monthstart[6]; $wday++, $wday %= 7) { + $calendar.=qq{ \n} if $wday == $week_start_day; + $calendar.= + qq{  \n}; + } + + # At this point, either the first is a week_start_day, in which case nothing + # has been printed, or else we are in the middle of a row. + for (my $day = 1; $day <= month_days(year => $year, month => $month); + $day++, $wday++, $wday %= 7) { + # At tihs point, on a week_start_day, we close out a row, and start a new + # one -- unless it is week_start_day on the first, where we do not close a + # row -- since none was started. + if ($wday == $week_start_day) { + $calendar.=qq{ \n} unless $day == 1; + $calendar.=qq{ \n}; + } + my $tag; + my $mtag = sprintf("%02d", $month); + if (defined $cache{$pagespec}{"$year/$mtag/$day"}) { + if ($day == $today) { $tag='month-calendar-day-this-day'; } + else { $tag='month-calendar-day-link'; } + $calendar.=qq{ }; + $calendar.= + htmllink($params{page}, $params{destpage}, + pagename($linkcache{"$year/$mtag/$day"}), + 0,0,"$day"); + $calendar.=qq{\n}; + } + else { + if ($day == $today) { $tag='month-calendar-day-this-day'; } + elsif ($day == $future_dom) { $tag='month-calendar-day-future'; } + else { $tag='month-calendar-day-nolink'; } + $calendar.=qq{ $day\n}; + } + } + # finish off the week + for (; $wday != $week_start_day; $wday++, $wday %= 7) { + $calendar.=qq{  \n}; + } + $calendar.=< + +EOF + + return $calendar; +} + +sub format_year (@) { + my %params=@_; + my $pagespec = $params{pages}; + my $year = $params{year}; + my $month = $params{month}; + my $calendar="\n"; + my $pyear = $year - 1; + my $nyear = $year + 1; + my $future_month = 0; + $future_month = $now[4]+1 if ($year == $now[5]+1900); + + # calculate URL's for previous and next years + my $archivebase = 'archives'; + $archivebase = $config{archivebase} if defined $config{archivebase}; + my ($url, $purl, $nurl)=("$year",'',''); + if (exists $cache{$pagespec}{"$year"}) { + $url = htmllink($params{page}, $params{destpage}, + "$archivebase/$year", + 0,0,"$year"); + } + + if (exists $cache{$pagespec}{"$pyear"}) { + $purl = htmllink($params{page}, $params{destpage}, + "$archivebase/$pyear", + 0,0,"\←"); + } + if (exists $cache{$pagespec}{"$nyear"}) { + $nurl = htmllink($params{page}, $params{destpage}, + "$archivebase/$nyear", + 0,0,"\→"); + } + # Start producing the year calendar + $calendar=< + + $purl + $url + $nurl + + + Months + +EOF + + for ($month = 1; $month <= 12; $month++) { + my @day=localtime(timelocal(0,0,0,15,$month-1,$year-1900)); + my $murl; + my $monthname = POSIX::strftime("%B", @day); + my $monthabbr = POSIX::strftime("%b", @day); + $calendar.=qq{ \n} if ($month % $params{months_per_row} == 1); + my $tag; + my $mtag=sprintf("%02d", $month); + if ($month == $params{month}) { + if ($cache{$pagespec}{"$year/$mtag"}) {$tag = 'this_month_link'} + else {$tag = 'this_month_nolink'} + } + elsif ($cache{$pagespec}{"$year/$mtag"}) {$tag = 'month_link'} + elsif ($future_month && $month >=$future_month){$tag = 'month_future'} + else {$tag = 'month_nolink'} + if ($cache{$pagespec}{"$year/$mtag"}) { + $murl = htmllink($params{page}, $params{destpage}, + "$archivebase/$year/$mtag", + 0,0,"$monthabbr"); + $calendar.=qq{ }; + $calendar.=$murl; + $calendar.=qq{\n}; + } + else { + $calendar.=qq{ $monthabbr\n}; + } + $calendar.=qq{ \n} if ($month % $params{months_per_row} == 0); + } + $calendar.=< +EOF + + return $calendar; +} + + +sub format (@) { + my %params=@_; + my $content=$params{content}; + return $content unless exists $calpages{$params{page}}; + + # Restore parameters for each invocation + foreach my $index (keys %{$calpages{$params{page}}}) { + my $calendar="\n"; + my %saved = %{$calpages{$params{page}}{$index}}; + my $pagespec=$saved{pages}; + + if (! defined $cache{$pagespec}) { + for my $page (sort keys %pagesources) { + next unless pagespec_match($page,$pagespec); + my $mtime; + my $src = $pagesources{$page}; + if (! exists $IkiWiki::pagectime{$page}) { + $mtime=(stat(srcfile($src)))[9]; + } + else { + $mtime=$IkiWiki::pagectime{$page} + } + my @date = localtime($mtime); + my $mday = $date[3]; + my $month = $date[4] + 1; + my $year = $date[5] + 1900; + my $mtag = sprintf("%02d", $month); + $linkcache{"$year/$mtag/$mday"} = "$src"; + $cache{$pagespec}{"$year"}++; + $cache{$pagespec}{"$year/$mtag"}++; + $cache{$pagespec}{"$year/$mtag/$mday"}++; + } + } + # So, we have cached data for the current pagespec at this point + if ($saved{type} =~ /month/i) { + $calendar=format_month(%saved); + } + elsif ($saved{type} =~ /year/i) { + $calendar=format_year(%saved); + } + $content =~ s/(
\s*.?\s*$index\b)/
$calendar/ms; + } + return $content; +} + + + +=head1 CAVEATS + +In the month calendar, for days in which there is more than one +posting, the link created randomly selects one of them. Since there is +no easy way in B to automatically generate index pages, and +pregenerating daily index pages seems too much of an overhead, we have +to live with this. All postings can still be viewed in the monthly or +annual indices, of course. This can be an issue for very prolific +scriveners. + +=cut + +=head1 BUGS + +None Known so far. + +=head1 BUGS + +Since B eval's the configuration file, the values have to all +on a single physical line. This is the reason we need to use strings +and eval, instead of just passing in real anonymous sub references, +since the eval pass converts the coderef into a string of the form +"(CODE 12de345657)" which can't be dereferenced. + +=cut + +=head1 AUTHOR + +Manoj Srivastava + +=head1 COPYRIGHT AND LICENSE + +This script is a part of the Devotee package, and is + +Copyright (c) 2002 Manoj Srivastava + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +=cut + +1; + +__END__ +
+ +------ + +I've been looking over the calendar plugin. Some items: + +* Why did you need to use a two-stage generation with a format hook? + That approach should only be needed if adding something to a page that + would be removed by the htmlscrubber, and as far as I can tell, the + calendars don't involve anything that would be a problem. It seems + that emitting the whole calendar in the preprocess hook would simplify + things and you'd not need to save state about calendars. + +> I am scared of the html scrubber, and have never turned it on, +> and did not look too deeply into what would be scrubbed out --ManojSrivastava +>> Unless you're using javascript, a few annoyances link , or inline +>> css, it's unlikly to object to any html you might write. The list of +>> allowed tags and attributes is easy to find near the top of the plugin. + +> In case the option that gets the ctime of the pages from the +> SCM itself, %IkiWiki::pagectime is not populated that early, +> is it? So I waited until the last possible moment to look at +> the time information. +> +>> Actually, since my big rewrite of the rendering path a few months ago, +>> ikiwiki scans and populates almost all page information before starting +>> to render any page. This includes %pagectime, and even %links. So you +>> shouldn't need to worry about running it late. + +* The way that it defaults to the current year and current month + is a little bit tricky, because of course the wiki might not get + updated in a particular time period, and even if it is updated, only + iff a page containing a calendar is rebuilt for some other reason will + the calendar get updated, and change what year or month it shows. This + is essentially the same problem described in + [[todo/tagging_with_a_publication_date]], + although I don't think it will affect the calendar plugin very badly. + Still, the docs probably need to be clear about this. + +> I use it on the sidebar; and the blog pages are almost always +> rebuilt, which is where the calendar is looked at most often. Oh, +> and I also cheat, I have ikiwiki --setup foo as a @daily cronjob, so +> my wiki is always built daily from scratch. +> +> I think it should be mentioned, yes. + +* There seems to be something a bit wrong with the year-to-year + navigation in the calendar, based on the example in your blog. If I'm + on the page for 2006, there's an arrow pointing left which takes me to + 2005. If I'm on 2005, the arrow points left, but goes to 2006, not + 2004. + +> I need to look into this. + +* AIUI, the archivebase setting makes a directory rooted at the top of + the wiki, so you can have only one set of archives per wiki, in + /archives/. It would be good if it were possible to have multiple + archived for different blogs in the same wiki at multiple locations. + Though since the archives contain calendars, the archive location + can't just be relative to the page with the calendar. But perhaps + archivebase could be a configurable parameter that can be specified in + the directive for the calendar? (It would be fine to keep the global + location as a default.) + +> OK, this is simple enough to implement. I'll do that (well, +> perhaps not before Xmas, I have a family dinner to cook) and send in +> another patch. + + +---- + +And that's all I've heard so far. Hoping I didn't miss another patch? + +--[[Joey]]