web commit by ManojSrivastava

master
joey 2007-08-19 22:00:39 +00:00
parent 6a8c921ee1
commit d8f7706cda
1 changed files with 7 additions and 566 deletions

View File

@ -35,569 +35,14 @@ ManojSrivastava
Since this is a little bit er, stalled, I'll post here the stuff Manoj
mailed me, and my response to it. --[[Joey]]
> > I'm sending in an updated package, and have removed the older version you had here.--ManojSrivastava
[[tag patch]]
<pre>
#! /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 <srivasta@debian.org>
#
# 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<archivebase>. I<archivebase>
defaults to C<archives>. 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<archives/2006/01.mdwn> (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<type>
Used to specify the type of calendar wanted. Can be one of C<month> or
C<year>. The default is a month view calendar.
=item B<pages>
Specifies the C<pagespec> used to get pages to match for
linking. Usually this should be something like C<blog/* and !*/Discussion>.
Defaults to C<*>.
=item B<year>
The year for which the calendar is requested. Defaults to the current year.
=item B<month>
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<week_start_day>
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<months_per_row>
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<month-calendar>
The month calendar as a whole
=item C<month-calendar-head>
The head of the month calendar (ie,"March"), localized to the environment.
=item C<month-calendar-day-head>
A column head in the month calendar (ie, a day-of-week abbreviation),
localized.
=item C<month-calendar-day-noday>, C<month-calendar-day-link>,
C<month-calendar-day-nolink>, C<month-calendar-day-future>,
C<month-calendar-day-this-day>
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<year-calendar>
The year calendar as a whole
=item C<year-calendar-head>
The head of the year calendar (ie, "2006")
=item C<year-calendar-subhead>
For example, "Months"
=item C<year-calendar-month-link>, C<year-calendar-month-nolink>,
C<year-calendar-month-future>, C<year-calendar-this-month>
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<div class=\"calendar\">" . $index++ . "</div><!-- calendar -->\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=<<EOF;
<table class="month-calendar">
<caption class="month-calendar-head">
$purl
$url
$nurl
</caption>
<tr>
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{ <th class="month-calendar-day-head $downame">$dowabbr</th>\n};
}
$calendar.=<<EOF;
</tr>
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{ <tr>\n} if $wday == $week_start_day;
$calendar.=
qq{ <td class="month-calendar-day-noday $downame{$wday}">&nbsp;</td>\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{ </tr>\n} unless $day == 1;
$calendar.=qq{ <tr>\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{ <td class="$tag $downame{$wday}">};
$calendar.=
htmllink($params{page}, $params{destpage},
pagename($linkcache{"$year/$mtag/$day"}),
0,0,"$day");
$calendar.=qq{</td>\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{ <td class="$tag $downame{$wday}">$day</td>\n};
}
}
# finish off the week
for (; $wday != $week_start_day; $wday++, $wday %= 7) {
$calendar.=qq{ <td class="month-calendar-day-noday $downame{$wday}">&nbsp;</td>\n};
}
$calendar.=<<EOF;
</tr>
</table>
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,"\&larr;");
}
if (exists $cache{$pagespec}{"$nyear"}) {
$nurl = htmllink($params{page}, $params{destpage},
"$archivebase/$nyear",
0,0,"\&rarr;");
}
# Start producing the year calendar
$calendar=<<EOF;
<table class="year-calendar">
<caption class="year-calendar-head">
$purl
$url
$nurl
</caption>
<tr>
<th class="year-calendar-subhead" colspan="$params{months_per_row}">Months</th>
</tr>
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{ <tr>\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{ <td class="$tag">};
$calendar.=$murl;
$calendar.=qq{</td>\n};
}
else {
$calendar.=qq{ <td class="$tag">$monthabbr</td>\n};
}
$calendar.=qq{ </tr>\n} if ($month % $params{months_per_row} == 0);
}
$calendar.=<<EOF;
</table>
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/(<div class=\"calendar\">\s*.?\s*$index\b)/<div class=\"calendar\">$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<IkiWiki> 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<IkiWiki> 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 <srivasta@debian.org>
=head1 COPYRIGHT AND LICENSE
This script is a part of the Devotee package, and is
Copyright (c) 2002 Manoj Srivastava <srivasta@debian.org>
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__
</pre>
------
I've been looking over the calendar plugin. Some items:
'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
@ -663,19 +108,15 @@ I've been looking over the calendar plugin. Some items:
----
And that's all I've heard so far. Hoping I didn't miss another patch?
--[[Joey]]
No, you did not. But I am back to hacking on this, and I think I have discovered a major problem with my approach. One of the problems with the current plugin is that the goal of a calendar is to create a calendar, either a month or year based on, that provides links to blogs for all the days in (for the month calendar), and all the months (in a year calendar) in which there have been blog postings. For the monthly calendar, it needs to know the previous and next months where there is a posting, and for the year calendar, it needs to know which of the previous (next) years had entries.
>No, you did not. But I am back to hacking on this, and I think I have discovered a major problem with my approach. One of the problems with the current plugin is that the goal of a calendar is to create a calendar, either a month >or year based on, that provides links to blogs for all the days in (for the month calendar), and all the months (in a year calendar) in which there have been blog postings. For the monthly calendar, it needs to know the previous >and next months where there is a posting, and for the year calendar, it needs to know which of the previous (next) years had entries.
Now, this means that it needs to know about at _all_ pages that meet the pagespec, and stash that information, before it begins generating the calandar in question, in order to calculate how to create the symlinks. And, of course, all pages that have calendars on them might need to change anytime a page that meets the pagespec is added; and again at midnight, when the current day changes.
>Now, this means that it needs to know about at _all_ pages that meet the pagespec, and stash that information, before it begins generating the calandar in question, in order to calculate how to create the symlinks. And, of >course, all pages that have calendars on them might need to change anytime a page that meets the pagespec is added; and again at midnight, when the current day changes.
keys %IkiWiki::pagectime followed by pagespec_match would probably do the trick --
>> I think I have solved the ""Need to look at all pages that match the spec"" issue; but the nightly rebuild to handle the current day changing still remain. I use cron. It is now, however, richly documented :)
@Interesting = grep (pagespec_match($_,$pagespec), keys %IkiWiki::pagectime);
When is the %IkiWiki::pagectime hash created? Can it be used in the preprocess hook?
--[[ManojSrivastava]]
--ManojSrivastava