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