move to todo, and mark done rather than just deleting
[ikiwiki] / doc / patchqueue / calendar_--_archive_browsing_via_a_calendar_frontend.mdwn
1 I am serving notice that I am starting work on a calendar plugin inspired by Blosxom's calendar plugin. The current plan is to create a plugin that looks through all the source files matching a certain pagespec, and optionally spit out a month view for the specified month (default to current), or spit out a year view for a given year (defaulting to the current year), of a list  of year with posts in them. The output would be a table, with the same CSS directives that the Blosxom plugin used to use (so that I can just reuse my css file).  The links would be created to a $config{archivedir}/$year or $config{archivedir}/$year-$month file, which can just have 
2
3      \[[inline pages="blog/* and !*/Discussion and creation_year($year) and creation_month($month)" rss="no" atom="no" show="0"]]
4
5 or some thing to generate a archive of postings. 
6
7 Roland Mas suggested a separate cron job to generate these archive indices automatically, but that is another thread.
8
9 ManojSrivastava
10
11 This plugin is inspired by the calendar plugin for Blosxom, but derivesno 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. To invoke the calendar, just use the preprocessor directive:
12
13      \[[calendar ]]
14
15 or
16
17       \[[calendar type="month" pages="blog/* and !*/Discussion"]]
18
19 or
20
21        \[[calendar type="year"  year="2005" pages="blog/* and !*/Discussion"]]
22
23
24 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, archivebase. archivebase defaults to "archives".  Links are created to pages "$archivebase/$year" and "$archivebase/$year/$month". The idea is to create annual and monthly indices, for example, by using something like this sample from my archives/2006/01.mdwn 
25
26           \[[meta title="Archives for 2006/01"]]
27           \[[inline rootpage="blog" atom="no" rss="no" show="0" pages="blog/* and !*/Discussion and creation_year(2006) and creation_month(01)" ]]
28
29 I'll send in the patch via email.
30
31
32 ManojSrivastava
33
34 ------
35
36 Since this is a little bit er, stalled, I'll post here the stuff Manoj
37 mailed me, and my response to it. --[[Joey]]
38
39 <pre>
40 #! /usr/bin/perl
41 #                              -*- Mode: Cperl -*- 
42 # calendar.pm --- 
43 # Author           : Manoj Srivastava ( srivasta@glaurung.internal.golden-gryphon.com ) 
44 # Created On       : Fri Dec  8 16:05:48 2006
45 # Created On Node  : glaurung.internal.golden-gryphon.com
46 # Last Modified By : Manoj Srivastava
47 # Last Modified On : Sun Dec 10 01:53:22 2006
48 # Last Machine Used: glaurung.internal.golden-gryphon.com
49 # Update Count     : 139
50 # Status           : Unknown, Use with caution!
51 # HISTORY          : 
52 # Description      : 
53
54 # arch-tag: 2aa737c7-3d62-4918-aaeb-fd85b4b1384c
55 #
56 # Copyright (c) 2006 Manoj Srivastava <srivasta@debian.org>
57 #
58 # This program is free software; you can redistribute it and/or modify
59 # it under the terms of the GNU General Public License as published by
60 # the Free Software Foundation; either version 2 of the License, or
61 # (at your option) any later version.
62 #
63 # This program is distributed in the hope that it will be useful,
64 # but WITHOUT ANY WARRANTY; without even the implied warranty of
65 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
66 # GNU General Public License for more details.
67 #
68 # You should have received a copy of the GNU General Public License
69 # along with this program; if not, write to the Free Software
70 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
71 #
72
73 require 5.002;
74 package IkiWiki::Plugin::calendar;
75
76 use warnings;
77 use strict;
78 use IkiWiki '1.00';
79 use Time::Local;
80
81 our $VERSION = "0.1";
82 my $file = __FILE__;
83
84 my %calpages;
85 my %cache;
86 my %linkcache;
87
88 my $index=1;
89 my @now=localtime();
90
91 =head1 NAME
92
93 calendar - Add links for the current month's, current year's, and older archived postings
94
95 =cut
96
97 =head1 SYNOPSIS
98
99 To invoke the calendar, just use the preprocessor directive (options
100 and variations are detailed below):
101
102   [[calendar ]]
103
104 or
105
106   [[calendar type="month" pages="blog/* and !*/Discussion"]]
107
108 or
109
110   [[calendar type="year"  year="2005" pages="blog/* and !*/Discussion"]]
111
112 =cut
113
114
115 =head1 DESCRIPTION
116
117 This plugin is inspired by the calendar plugin for Blosxom, but
118 derives no code from it. This plugin is essentially a fancy front end
119 to archives of previous pages, usually used for blogs. It can produce
120 a calendar for a given month, or a list of months for a given year. 
121
122 The year and month entities in the out put have links to archive index
123 pages, which are supposed to exist already. The idea is to create an
124 archives hierarchy, rooted in the subdirectory specified in the
125 site wide customization variable, I<archivebase>. I<archivebase>
126 defaults to C<archives>.  Links are created to pages
127 C<$archivebase/$year> and C<$archivebase/$year/$month>. If one creates
128 annual and monthly indices, for example, by using something like this
129 sample from my I<archives/2006/01.mdwn> (warning: line split for
130 readability):
131
132    \[[meta title="Archives for 2006/01"]]
133    \[[inline rootpage="blog" atom="no" rss="no" show="0"
134      pages="blog/* and !*/Discussion and creation_year(2006)
135             and creation_month(01)"
136    ]]
137
138 =cut
139
140 =head1 OPTIONS
141
142 =over
143
144 =item B<type>
145
146 Used to specify the type of calendar wanted. Can be one of C<month> or
147 C<year>. The default is a month view calendar.
148
149 =item B<pages>
150
151 Specifies the C<pagespec> used to get pages to match for
152 linking. Usually this should be something like C<blog/* and !*/Discussion>.
153 Defaults to C<*>.
154
155 =item B<year>
156
157 The year for which the calendar is requested. Defaults to the current year.
158
159 =item B<month>
160
161 The numeric month for which the calendar is requested, in the range
162 1..12. Used only for the month view calendar, and defaults to the
163 current month.
164
165 =item B<week_start_day>
166
167 A number, in the range 0..6, which represents the day of the week that
168 the month calendar starts with. 0 is Sunday, 1 is Monday, and so
169 on. Defaults to 0, which is Sunday.
170
171 =item B<months_per_row>
172
173 In the annual calendar, number of months to place in each row. Defaults to 3.
174
175 =back
176
177 =cut
178
179 =head1 Classes for CSS control
180
181 The output is liberally sprinkled with classes, for fine grained CSS
182 customization.
183
184 =over
185
186 =item C<month-calendar> 
187
188 The month calendar as a whole
189
190 =item C<month-calendar-head>
191
192 The head of the month calendar (ie,"March"), localized to the environment.
193
194 =item C<month-calendar-day-head>
195
196 A column head in the month calendar (ie, a day-of-week abbreviation),
197 localized.
198
199 =item C<month-calendar-day-noday>, C<month-calendar-day-link>,
200   C<month-calendar-day-nolink>, C<month-calendar-day-future>,
201   C<month-calendar-day-this-day> 
202
203 The day squares on the month calendar, for days that don't exist
204 (before or after the month itself), that don't have stories, that do
205 have stories, that are in the future, or are that currently selected,
206 respectively (today).
207
208 =item Day-of-week-name
209
210 Each day square is also given a class matching its day of week, this
211 can be used to high light weekends. This is also localized.
212
213 =item C<year-calendar>
214
215 The year calendar as a whole
216
217 =item C<year-calendar-head>
218
219 The head of the year calendar (ie, "2006")
220
221 =item C<year-calendar-subhead>
222
223 For example, "Months"
224
225 =item C<year-calendar-month-link>, C<year-calendar-month-nolink>,
226   C<year-calendar-month-future>, C<year-calendar-this-month>
227
228 The month squares on the year calendar, for months with stories,
229 without, in the future, and currently selected, respectively.
230
231 =back
232
233 =cut
234
235
236 sub import {
237   hook(type => "preprocess", id => "calendar", call => \&preprocess);
238   hook(type => "format", id => "calendar", call => \&format);
239 }
240
241 sub preprocess (@) {
242   my %params=@_;
243   $params{pages} = "*"            unless defined $params{pages};
244   $params{type}  = "month"        unless defined $params{type};
245   $params{year}  = 1900 + $now[5] unless defined $params{year};
246   $params{month} = sprintf("%02d", $params{month}) if defined  $params{month};
247   $params{month} = 1    + $now[4] unless defined $params{month};
248   $params{week_start_day} = 0     unless defined $params{week_start_day};
249   $params{months_per_row} = 3     unless defined $params{months_per_row};
250
251   # Store parameters (could be multiple calls per page)
252   $calpages{$params{destpage}}{$index} = \%params;
253
254   return "\n<div class=\"calendar\">" . $index++ . "</div><!-- calendar -->\n";
255 }
256
257 sub is_leap_year (@) {
258   my %params=@_;
259   return ($params{year} % 4 == 0 && (($params{year} % 100 != 0) || $params{year} % 400 ==0)) ;
260 }
261
262
263 sub month_days {
264   my %params=@_;
265   my $days_in_month = (31,28,31,30,31,30,31,31,30,31,30,31)[$params{month}-1];
266   if ($params{month} == 2 && is_leap_year(%params)) {
267     $days_in_month++;
268   }
269   return $days_in_month;
270 }
271
272
273 sub format_month (@) {
274   my %params=@_;
275   my $pagespec = $params{pages};
276   my $year     = $params{year};
277   my $month    = $params{month};
278
279   my $calendar="\n";
280
281   # When did this month start?
282   my @monthstart = localtime(timelocal(0,0,0,1,$month-1,$year-1900));
283
284   my $future_dom = 0;
285   my $today      = 0;
286   $future_dom = $now[3]+1 if ($year == $now[5]+1900 && $month == $now[4]+1);
287   $today      = $now[3]   if ($year == $now[5]+1900 && $month == $now[4]+1);
288
289   # Calculate month names for next month, and previous months
290   my $pmonth = $month - 1;
291   my $nmonth = $month + 1;
292   my $pyear  = $year;
293   my $nyear  = $year;
294
295   # Adjust for January and December
296   if ($month == 1)  { $pmonth = 12; $pyear--;  }
297   if ($month == 12) { $nmonth = 1;  $nyear++;  }
298
299   # Find out month names for this, next, and previous months
300   my $monthname=POSIX::strftime("%B", @monthstart);
301   my $pmonthname=
302     POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900)));
303   my $nmonthname=
304     POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900)));
305
306   # Calculate URL's for monthly archives, and article counts
307   my $archivebase = 'archives';
308   $archivebase = $config{archivebase} if defined $config{archivebase};
309
310   my ($url, $purl, $nurl)=("$monthname",'','');
311   my ($count, $pcount, $ncount) = (0,0,0);
312
313   if (exists $cache{$pagespec}{"$year/$month"}) {
314     $url = htmllink($params{page}, $params{destpage}, 
315                     "$archivebase/$year/" . sprintf("%02d", $month),
316                     0,0," $monthname ");
317   }
318
319   if (exists $cache{$pagespec}{"$pyear/$pmonth"}) {
320     $purl = htmllink($params{page}, $params{destpage}, 
321                      "$archivebase/$pyear/" . sprintf("%02d", $pmonth),
322                      0,0," $pmonthname ");
323   }
324   if (exists $cache{$pagespec}{"$nyear/$nmonth"}) {
325     $nurl = htmllink($params{page}, $params{destpage}, 
326                      "$archivebase/$nyear/" . sprintf("%02d", $nmonth),
327                      0,0," $nmonthname ");
328   }
329
330   # Start producing the month calendar
331   $calendar=<<EOF;
332 <table class="month-calendar">
333    <caption class="month-calendar-head">
334       $purl
335       $url
336       $nurl
337    </caption>
338    <tr>
339 EOF
340   # Suppose we want to start the week with day $week_start_day
341   # If $monthstart[6] == 1
342   my $week_start_day = $params{week_start_day};
343
344   my $start_day = 1 + (7 - $monthstart[6] + $week_start_day) % 7;
345   my %downame;
346   my %dowabbr;
347   for my $dow ($week_start_day..$week_start_day+6) {
348     my @day=localtime(timelocal(0,0,0,$start_day++,$month-1,$year-1900));
349     my $downame = POSIX::strftime("%A", @day);
350     my $dowabbr = POSIX::strftime("%a", @day);
351     $downame{$dow % 7}=$downame;
352     $dowabbr{$dow % 7}=$dowabbr;
353     $calendar.=
354       qq{     <th class="month-calendar-day-head $downame">$dowabbr</th>\n};
355   }
356
357   $calendar.=<<EOF;
358    </tr>
359 EOF
360
361   my $wday;
362   # we start with a week_start_day, and skip until we get to the first
363   for ($wday=$week_start_day; $wday != $monthstart[6]; $wday++, $wday %= 7) {
364     $calendar.=qq{   <tr>\n} if $wday == $week_start_day;
365     $calendar.=
366       qq{     <td class="month-calendar-day-noday $downame{$wday}">&nbsp;</td>\n};
367   }
368
369   # At this point, either the first is a week_start_day, in which case nothing
370   # has been printed, or else we are in the middle of a row.
371   for (my $day = 1; $day <= month_days(year => $year, month => $month); 
372        $day++, $wday++, $wday %= 7) {
373     # At tihs point, on a week_start_day, we close out a row, and start a new
374     # one -- unless it is week_start_day on the first, where we do not close a
375     # row -- since none was started.
376     if ($wday == $week_start_day) {
377       $calendar.=qq{   </tr>\n} unless $day == 1;
378       $calendar.=qq{   <tr>\n};
379     }
380     my $tag;
381     my $mtag = sprintf("%02d", $month);
382     if (defined $cache{$pagespec}{"$year/$mtag/$day"}) {
383       if ($day == $today)         { $tag='month-calendar-day-this-day'; }
384       else                        { $tag='month-calendar-day-link';     }
385       $calendar.=qq{     <td class="$tag $downame{$wday}">};
386       $calendar.=
387         htmllink($params{page}, $params{destpage}, 
388                  pagename($linkcache{"$year/$mtag/$day"}),
389                  0,0,"$day");
390       $calendar.=qq{</td>\n};
391     }
392     else {
393       if ($day == $today)         { $tag='month-calendar-day-this-day'; }
394       elsif ($day == $future_dom) { $tag='month-calendar-day-future';   }
395       else                        { $tag='month-calendar-day-nolink';   }
396       $calendar.=qq{     <td class="$tag $downame{$wday}">$day</td>\n};
397     }
398   }
399   # finish off the week
400   for (; $wday != $week_start_day; $wday++, $wday %= 7) {
401     $calendar.=qq{     <td class="month-calendar-day-noday $downame{$wday}">&nbsp;</td>\n};
402   }
403   $calendar.=<<EOF;
404    </tr>
405 </table>
406 EOF
407
408   return $calendar;
409 }
410
411 sub format_year (@) {
412   my %params=@_;
413   my $pagespec = $params{pages};
414   my $year     = $params{year};
415   my $month    = $params{month};
416   my $calendar="\n";
417   my $pyear  = $year - 1;
418   my $nyear  = $year + 1;
419   my $future_month = 0;
420   $future_month = $now[4]+1 if ($year == $now[5]+1900);
421
422   #  calculate URL's for previous and next years
423   my $archivebase = 'archives';
424   $archivebase = $config{archivebase} if defined $config{archivebase};
425   my ($url, $purl, $nurl)=("$year",'','');
426   if (exists $cache{$pagespec}{"$year"}) {
427     $url = htmllink($params{page}, $params{destpage}, 
428                     "$archivebase/$year",
429                     0,0,"$year");
430   }
431
432   if (exists $cache{$pagespec}{"$pyear"}) {
433     $purl = htmllink($params{page}, $params{destpage}, 
434                      "$archivebase/$pyear",
435                      0,0,"\&larr;");
436   }
437   if (exists $cache{$pagespec}{"$nyear"}) {
438     $nurl = htmllink($params{page}, $params{destpage}, 
439                      "$archivebase/$nyear",
440                      0,0,"\&rarr;");
441   }
442   # Start producing the year calendar
443   $calendar=<<EOF;
444 <table class="year-calendar">
445   <caption class="year-calendar-head">
446    $purl
447    $url
448    $nurl
449   </caption>
450   <tr>
451     <th class="year-calendar-subhead" colspan="$params{months_per_row}">Months</th>
452   </tr>
453 EOF
454
455   for ($month = 1; $month <= 12; $month++) {
456     my @day=localtime(timelocal(0,0,0,15,$month-1,$year-1900));
457     my $murl;
458     my $monthname = POSIX::strftime("%B", @day);
459     my $monthabbr = POSIX::strftime("%b", @day);
460     $calendar.=qq{   <tr>\n}  if ($month % $params{months_per_row} == 1);
461     my $tag;
462     my $mtag=sprintf("%02d", $month);
463     if ($month == $params{month}) {
464       if ($cache{$pagespec}{"$year/$mtag"})        {$tag = 'this_month_link'}
465       else                                         {$tag = 'this_month_nolink'}
466     }
467     elsif ($cache{$pagespec}{"$year/$mtag"})       {$tag = 'month_link'} 
468     elsif ($future_month && $month >=$future_month){$tag = 'month_future'} 
469     else                                           {$tag = 'month_nolink'}
470     if ($cache{$pagespec}{"$year/$mtag"}) {
471       $murl =  htmllink($params{page}, $params{destpage}, 
472                         "$archivebase/$year/$mtag",
473                         0,0,"$monthabbr");
474       $calendar.=qq{     <td class="$tag">};
475       $calendar.=$murl;
476       $calendar.=qq{</td>\n};
477     }
478     else {
479       $calendar.=qq{     <td class="$tag">$monthabbr</td>\n};
480     }
481     $calendar.=qq{   </tr>\n} if ($month % $params{months_per_row} == 0);
482   }
483   $calendar.=<<EOF;
484 </table>
485 EOF
486
487   return $calendar;
488 }
489
490
491 sub format (@) {
492   my %params=@_;
493   my $content=$params{content};
494   return $content unless exists $calpages{$params{page}};
495
496   # Restore parameters for each invocation
497   foreach my $index (keys %{$calpages{$params{page}}}) {
498     my $calendar="\n";
499     my %saved = %{$calpages{$params{page}}{$index}};
500     my $pagespec=$saved{pages};
501
502     if (! defined $cache{$pagespec}) {
503       for my $page (sort keys %pagesources) {
504         next unless pagespec_match($page,$pagespec);
505         my $mtime;
506         my $src = $pagesources{$page};
507         if (! exists $IkiWiki::pagectime{$page}) {
508           $mtime=(stat(srcfile($src)))[9];
509         }
510         else {
511           $mtime=$IkiWiki::pagectime{$page}
512         }
513         my @date  = localtime($mtime);
514         my $mday  = $date[3];
515         my $month = $date[4] + 1;
516         my $year  = $date[5] + 1900;
517         my $mtag  = sprintf("%02d", $month);
518         $linkcache{"$year/$mtag/$mday"} = "$src";
519         $cache{$pagespec}{"$year"}++;
520         $cache{$pagespec}{"$year/$mtag"}++;
521         $cache{$pagespec}{"$year/$mtag/$mday"}++;
522       }
523     }
524     # So, we have cached data for the current pagespec at this point
525     if ($saved{type} =~ /month/i) {
526       $calendar=format_month(%saved);
527     }
528     elsif ($saved{type} =~ /year/i) {
529       $calendar=format_year(%saved);
530     }
531     $content =~ s/(<div class=\"calendar\">\s*.?\s*$index\b)/<div class=\"calendar\">$calendar/ms;
532   }
533   return $content;
534 }
535
536
537
538 =head1 CAVEATS
539
540 In the month calendar, for days in which there is more than one
541 posting, the link created randomly selects one of them. Since there is
542 no easy way in B<IkiWiki> to automatically generate index pages, and
543 pregenerating daily index pages seems too much of an overhead, we have
544 to live with this.  All postings can still be viewed in the monthly or
545 annual indices, of course. This can be an issue for very prolific
546 scriveners.
547
548 =cut
549
550 =head1 BUGS
551
552 None Known so far.
553
554 =head1 BUGS
555
556 Since B<IkiWiki> eval's the configuration file, the values have to all
557 on a single physical line. This is the reason we need to use strings
558 and eval, instead of just passing in real anonymous sub references,
559 since the eval pass converts the coderef into a string of the form
560 "(CODE 12de345657)" which can't be dereferenced.
561
562 =cut
563
564 =head1 AUTHOR
565
566 Manoj Srivastava <srivasta@debian.org>
567
568 =head1 COPYRIGHT AND LICENSE
569
570 This script is a part of the Devotee package, and is 
571
572 Copyright (c) 2002 Manoj Srivastava <srivasta@debian.org>
573
574 This program is free software; you can redistribute it and/or modify
575 it under the terms of the GNU General Public License as published by
576 the Free Software Foundation; either version 2 of the License, or
577 (at your option) any later version.
578
579 This program is distributed in the hope that it will be useful,
580 but WITHOUT ANY WARRANTY; without even the implied warranty of
581 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
582 GNU General Public License for more details.
583
584 You should have received a copy of the GNU General Public License
585 along with this program; if not, write to the Free Software
586 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
587
588 =cut
589
590 1;
591
592 __END__
593 </pre>
594
595 ------
596
597 I've been looking over the calendar plugin. Some items:
598
599 * Why did you need to use a two-stage generation with a format hook?
600   That approach should only be needed if adding something to a page that
601   would be removed by the htmlscrubber, and as far as I can tell, the
602   calendars don't involve anything that would be a problem. It seems
603   that emitting the whole calendar in the preprocess hook would simplify
604   things and you'd not need to save state about calendars.
605
606 > I am scared of the html scrubber, and have never turned it on,
607 >        and did not look too deeply into what would be scrubbed out --ManojSrivastava
608 >> Unless you're using javascript, a few annoyances link <blink>, or inline
609 >> css, it's unlikly to object to any html you might write. The list of
610 >> allowed tags and attributes is easy to find near the top of the plugin.
611
612 > In case the option that gets the ctime of the pages from the
613 > SCM itself, %IkiWiki::pagectime  is not populated that early,
614 > is it? So I waited until the last possible moment to look at
615 > the time information.
616 >
617 >> Actually, since my big rewrite of the rendering path a few months ago,
618 >> ikiwiki scans and populates almost all page information before starting
619 >> to render any page. This includes %pagectime, and even %links. So you
620 >> shouldn't need to worry about running it late.
621
622 * The way that it defaults to the current year and current month
623   is a little bit tricky, because of course the wiki might not get
624   updated in a particular time period, and even if it is updated, only
625   iff a page containing a calendar is rebuilt for some other reason will
626   the calendar get updated, and change what year or month it shows. This
627   is essentially the same problem described in
628   [[todo/tagging_with_a_publication_date]],
629   although I don't think it will affect the calendar plugin very badly.
630   Still, the docs probably need to be clear about this.
631
632 > I use it on the sidebar; and the blog pages are almost always
633 > rebuilt, which is where the calendar is  looked at most often. Oh,
634 > and I also cheat, I have ikiwiki  --setup foo as a @daily cronjob, so
635 > my wiki is always built daily from scratch.
636
637 > I think it should be mentioned, yes.
638
639 * There seems to be something a bit wrong with the year-to-year
640   navigation in the calendar, based on the example in your blog. If I'm
641   on the page for 2006, there's an arrow pointing left which takes me to
642   2005. If I'm on 2005, the arrow points left, but goes to 2006, not
643   2004.
644
645 > I need to look into this.
646
647 * AIUI, the archivebase setting makes a directory rooted at the top of
648   the wiki, so you can have only one set of archives per wiki, in
649   /archives/. It would be good if it were possible to have multiple
650   archived for different blogs in the same wiki at multiple locations.
651   Though since the archives contain calendars, the archive location
652   can't just be relative to the page with the calendar. But perhaps
653   archivebase could be a configurable parameter that can be specified in
654   the directive for the calendar? (It would be fine to keep the global
655   location as a default.)
656
657 > OK, this is simple enough to implement. I'll do that (well,
658 > perhaps not before Xmas, I have a family dinner to cook) and send in
659 > another patch.
660
661
662 ----
663
664 And that's all I've heard so far. Hoping I didn't miss another patch?
665
666 --[[Joey]]