add calendar rendering
authorJulien Moutinho <julm+ikiwiki+events@autogeree.net>
Fri, 4 Apr 2014 23:32:00 +0000 (01:32 +0200)
committerJulien Moutinho <julm+ikiwiki+events@autogeree.net>
Sat, 5 Apr 2014 00:12:40 +0000 (02:12 +0200)
events.pm [new file with mode: 0644]
newevent.pm [deleted file]
templates/neweventday.tmpl
templates/neweventmonth.tmpl

diff --git a/events.pm b/events.pm
new file mode 100644 (file)
index 0000000..e06e2ce
--- /dev/null
+++ b/events.pm
@@ -0,0 +1,1154 @@
+#! /usr/bin/perl
+# Copyright (C) 2014 Julien Moutinho <julm+ikiwiki+events&autogeree.net>
+#
+# 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 3 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+package IkiWiki::Plugin::events;
+
+use strict;
+use warnings;
+use IkiWiki 3.00;
+use Time::Local;
+use DateTime;
+#use Data::Dumper;
+
+sub import {
+       hook(type => "getsetup",   id => "events", call => \&getsetup);
+       hook(type => "needsbuild", id => "events", call => \&needsbuild);
+       hook(type => "preprocess", id => "events", call => \&preprocess);
+       hook(type => "sessioncgi", id => "events", call => \&sessioncgi);
+ }
+sub getsetup () {
+       return
+        { plugin =>
+                { safe => 1
+                , rebuild => undef
+                , section => "widget"
+                }
+        };
+ }
+
+my $now
+ = DateTime->now
+ ( time_zone => 'local'
+ , locale    => $config{locale}
+ )->set_time_zone('floating');
+my @days    = ('01'..'31');
+my @hours   = ('00'..'23');
+my @minutes = ('00'..'59');
+
+# update
+sub set_rendering_expiration ($$) {
+       my ($page, $timestamp) = @_;
+       if (not exists  $pagestate{$page}{events}{expiration}
+       or $timestamp < $pagestate{$page}{events}{expiration}) {
+               my $time = DateTime->from_epoch
+                ( epoch     => $timestamp
+                , time_zone => 'UTC'
+                , locale    => $config{locale}
+                );
+               #debug("events: set_rendering_expiration(): will refresh: page=".$page
+               # . " after: date=".$time->strftime('%Y-%m-%d_%H-%M-%S'));
+               $pagestate{$page}{events}{expiration} = $timestamp;
+        }
+ }
+sub set_next_rendering (%) {
+       my %params = @_;
+       if ($params{type} eq 'month'
+        and $params{focus}->year()  == $now->year()
+        and $params{focus}->month() == $now->month()) {
+               # NOTE: calendar for current month, updates next day
+               my $update = $params{focus}->clone;
+               $update->set_hour(0);
+               $update->set_minute(0);
+               $update->set_second(0);
+               $update->set_nanosecond(0);
+               my $duration = DateTime::Duration->new(days => 1, end_of_month => 'limit');
+               $update->add_duration($duration);
+               set_rendering_expiration($params{destpage}, $update->epoch());
+               #debug("events: will refresh current month: page=".$params{destpage}
+               # . " after: date=".$update->strftime('%Y-%m-%d_%H-%M-%S'));
+        }
+       elsif ($params{type} eq 'month'
+        and ((  $params{focus}->year()  == $now->year()
+                        and $params{focus}->month()  > $now->month())
+                or    $params{focus}->year()   > $now->year())) {
+               # NOTE: calendar for upcoming month, updates 1st of next month
+               my $update = $params{focus}->clone;
+               $update->set_day(1);
+               $update->set_hour(0);
+               $update->set_minute(0);
+               $update->set_second(0);
+               $update->set_nanosecond(0);
+               set_rendering_expiration($params{destpage}, $update->epoch());
+               #debug("events: will refresh upcoming month: page=".$params{destpage}
+               # . " after: date=".$update->strftime('%Y-%m-%d_%H-%M-%S'));
+        }
+       elsif ($params{type} eq 'day'
+        and ($params{focus}->year()     == $now->year()
+                        and $params{focus}->month() == $now->month()
+                        and $params{focus}->day()   == $now->day())) {
+               # NOTE: calendar for current day, updates next day
+               my $update = $params{focus}->clone;
+               $update->set_hour(0);
+               $update->set_minute(0);
+               $update->set_second(0);
+               $update->set_nanosecond(0);
+               my $duration = DateTime::Duration->new(days => 1, end_of_month => 'limit');
+               $update->add_duration($duration);
+               set_rendering_expiration($params{destpage}, $update->epoch());
+               #debug("events: will refresh current day: page=".$params{destpage}
+               # . " after: date=".$update->strftime('%Y-%m-%d_%H-%M-%S'));
+        }
+       elsif ($params{type} eq 'day'
+        and ((    $params{focus}->year()  == $now->year()
+                        and ( $params{focus}->month()  > $now->month()
+                                or ($params{focus}->month() == $now->month()
+                                and $params{focus}->day()    > $now->day() ))
+                or      $params{focus}->year()   > $now->year()))) {
+               # NOTE: calendar for upcoming day, updates that day
+               my $update = $params{focus}->clone;
+               $update->set_hour(0);
+               $update->set_minute(0);
+               $update->set_second(0);
+               $update->set_nanosecond(0);
+               set_rendering_expiration($params{destpage}, $update->epoch());
+               #debug("events: will refresh upcoming day: page=".$params{destpage}
+               # . " after: date=".$update->strftime('%Y-%m-%d_%H-%M-%S'));
+        }
+ }
+sub needsbuild (@) {
+       my $needsbuild = shift;
+       foreach my $page (keys %pagestate) {
+               if (exists $pagestate{$page}{events}{expiration}) {
+                       if ($pagestate{$page}{events}{expiration} <= $now->epoch()) {
+                               # NOTE: force a rebuild so the calendar shows the current day
+                               push @$needsbuild, $pagesources{$page};
+                        }
+                       if (exists $pagesources{$page}
+                        and grep { $_ eq $pagesources{$page} } @$needsbuild) {
+                               # NOTE: remove state, will be re-added
+                               #       if the calendar is still there during the rebuild
+                               delete $pagestate{$page}{events};
+                        }
+                }
+        }
+       return $needsbuild;
+ }
+
+# render
+sub date_of_page ($%) {
+       my ($page, %params) = @_;
+       my $dir = IkiWiki::dirname($page);
+       my ($year, $month, $day, $hour, $hour_begin, $hour_end)
+        = $dir =~ m{
+               .*/
+               (\d+)/
+               (01|02|03|04|05|06|07|08|09|10|11|12)/
+               (01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)/
+               (([0-2][0-9]h[0-5][0-9])(-[0-2][0-9]h[0-5][0-9])?)?
+               $
+        }x;
+       my $r =
+        { year       => $year
+        , month      => $month
+        , day        => $day
+        , hour       => $hour
+        , hour_begin => $hour_begin
+        , hour_end   => $hour_end
+        };
+       #debug("date_of_page: dir=".$dir." ".Dumper($r));
+       return $r;
+ }
+sub event_of_page ($%) {
+       my ($event, %params) = @_;
+       my $title
+        = exists $pagestate{$event}{meta}{title}
+        ? $pagestate{$event}{meta}{title}
+        : pagetitle(IkiWiki::basename($event));
+       my $hour
+        = date_of_page($event)->{hour};
+       my $link
+        = htmllink
+        ( $params{page}
+        , $params{destpage}
+        , $event
+        , linktext => $title
+        , noimageinline => 1
+        , title => $title );
+       my @tags
+        = sort {lc $a cmp lc $b}
+        (grep {
+                       not defined $params{tags}
+                       or pagespec_match($_, $params{tags})
+                }
+                (keys %{$IkiWiki::typedlinks{$event}{tag}}));
+       @tags
+        = map {
+               my $tag = $_;
+               my $title
+                = exists $pagestate{$tag}{meta}{title}
+                ? $pagestate{$tag}{meta}{title}
+                : pagetitle(IkiWiki::basename($tag));
+               my $link
+                = htmllink
+                ( $params{page}
+                , $params{destpage}
+                , $tag
+                , linktext => $title
+                , noimageinline => 1
+                , title => $title );
+               add_depends($params{page}, $event, deptype('content'));
+               #add_depends($params{page}, $tag, deptype('content'));
+                       # XXX: much too heavy :\ and midnight refresh may fix it anyway.
+               my $class = qq{$tag};
+               $class =~ s{[^a-zA-Z0-9-]}{_}g;
+               { class => "tag tag-$class"
+               , link  => $link }
+        } @tags;
+       return
+        { hour => $hour
+        , link => $link
+        , tags => \@tags };
+ }
+sub events_of_pages ($%) {
+       my ($pages, %params) = @_;
+       my @day_events = ();
+       my @hour_events = ();
+       my $pagedir = sub { IkiWiki::basename(IkiWiki::dirname(shift)) };
+       foreach my $page (@$pages) {
+               my $date = date_of_page($page);
+               if (defined $date->{hour}) {
+                       push @hour_events, $page;
+                }
+               else {
+                       push @day_events, $page;
+                }
+        }
+       return
+        map {event_of_page($_, %params)}
+        ( (sort {lc $pagedir->($a) cmp lc $pagedir->($b)} @day_events)
+        , (sort {lc $pagedir->($a) cmp lc $pagedir->($b)} @hour_events) );
+ }
+sub event_html ($$%) {
+       my ($date, $format, %params) = @_;
+       my $day   = sprintf("%02d", $date->day());
+       my $month = sprintf("%02d", $date->month());
+       my $year_html = $date->year();
+       my $month_html
+        = $format->{month_name}
+        ? $date->month_name()
+        : $month;
+       my $day_html
+        = $format->{day_name}
+        ? $date->day_name()." ".$date->day()
+        : $format->{day_abbr}
+        ? $date->day_abbr()." ".$date->day()
+        : $date->day();
+       my $wday_class
+        = "wday-".($date->day_of_week() - 1)
+        . ( ($date->year()    == $now->year()
+                       and $date->month() == $now->month()
+                       and $date->day()   == $now->day())
+                ? " today"
+                : "" );
+       my $new_html = "";
+       if (not defined $params{new} or $params{new} ne 'no') {
+               if ($format->{year}) {
+                       my $year_page
+                        = sprintf
+                        ( '%s/%d'
+                        , $params{base}
+                        , $date->year()
+                        );
+                       add_depends($params{page}, $year_page, deptype("presence"));
+                       if ($pagesources{$year_page}) {
+                               $year_html
+                                = htmllink
+                                ( $params{page}
+                                , $params{destpage}
+                                , $year_page
+                                , linktext => $year_html
+                                , noimageinline => 1 );
+                        }
+                }
+               if ($format->{month}) {
+                       my $month_page
+                        = sprintf
+                        ( '%s/%d/%02d'
+                        , $params{base}
+                        , $date->year()
+                        , $date->month()
+                        );
+                       add_depends($params{page}, $month_page, deptype("presence"));
+                       if ($pagesources{$month_page}) {
+                               $month_html
+                                = htmllink
+                                ( $params{page}
+                                , $params{destpage}
+                                , $month_page
+                                , linktext => $month_html
+                                , noimageinline => 1 );
+                        }
+                }
+               if ($format->{day}) {
+                       my $day_page
+                        = sprintf
+                        ( '%s/%d/%02d/%02d'
+                        , $params{base}
+                        , $date->year()
+                        , $date->month()
+                        , $date->day()
+                        );
+                       add_depends($params{page}, $day_page, deptype("presence"));
+                       if ($pagesources{$day_page}) {
+                               $day_html
+                                = htmllink
+                                ( $params{page}
+                                , $params{destpage}
+                                , $day_page
+                                , linktext => $day_html
+                                , noimageinline => 1 );
+                        }
+                }
+               unless ($params{nonew} or not $format->{new}) {
+                       $new_html
+                        .= qq{<a class='new' href='}
+                        . IkiWiki::cgiurl
+                                ( base  => $params{base}
+                                , ($date->day()
+                                        ?(day => $day):())
+                                , (($date->month() or $date->day())
+                                        ?(month => $month):())
+                                , (($date->year() or $date->month() or $date->day())
+                                        ?(year => $date->year()):())
+                                , do    => 'events'
+                                , page  => $params{destpage}
+                                )
+                        . qq{' rel='nofollow'>+</a>};
+                }
+        }
+       return
+        { new   => $new_html
+        , day   => $day_html
+        , month => $month_html
+        , year  => $year_html
+        , wday  => $wday_class
+        };
+ }
+sub preprocess_day (@) {
+       my %params = @_;
+       my @pages
+        = pagespec_match_list
+        ( $params{page}
+        , $params{pages}
+        , deptype => deptype("presence")
+               # NOTE: add presence dependencies to update calendar when pages are added/removed
+        );
+       my $event_html
+        = event_html
+        ( $params{focus}
+        , {day=>1, day_name=>1, new => 1}
+        , %params );
+       my @events
+        = map {
+               my @tags
+                = map {"<li\n class='".$_->{class}."'>".$_->{link}."</li>"}
+                @{$_->{tags}};
+               "<ul\n class='events'><li class='event'>"
+                . "<span class='head'>"
+                                . (defined $_->{hour} ? "<span class='hour'>$_->{hour}</span>" : "")
+                        . "<span class='link'>$_->{link}</span>"
+                . "</span>"
+                . "<ul\n class='tags'>".join("", @tags)."</ul>"
+                . "</li></ul>"
+        }
+        events_of_pages(\@pages, %params);
+       return
+        "<table\n class='wday'>"
+        . "<thead><tr><th><span\n class='head'><span\n class='day'>"
+                        . $event_html->{day}
+                        . $event_html->{new}
+                . "</span></span></th></tr></thead>"
+        . "<tbody><tr><td\n class='wday $event_html->{wday}'>"
+                . join("", @events)
+                . "</td></tr></tbody>"
+        . "</table>";
+ }
+sub preprocess_month (@) {
+       my %params = @_;
+       my $one_day = DateTime::Duration->new(days => 1, end_of_month => 'limit');
+       my $day = $params{focus}->clone->set_day(1);
+       my $last_day
+         = DateTime->last_day_of_month
+        ( year  => $params{focus}->year()
+        , month => $params{focus}->month() )->day();
+       
+       my @pages
+        = pagespec_match_list
+        ( $params{page}
+        , $params{pages}
+        , deptype => deptype("presence")
+               # NOTE: add presence dependencies to update calendar when pages are added/removed
+        );
+       
+       # NOTE: sort the pages by days of the month
+       my %days = map {($_=>[])} (1 .. $last_day);
+       foreach my $page (@pages) {
+               my $page_ctime = DateTime->from_epoch
+                ( epoch     => $IkiWiki::pagectime{$page}
+                , time_zone => 'local'
+                , locale    => $config{locale}
+                );
+               push @{$days{$page_ctime->day()}}, $page;
+        }
+       
+       my $t='<tr>';
+       my $first_wday = $day->clone();
+       my $last_wday  = ($params{week_start_day} + 6) % 7;
+       while ($first_wday->day_of_week() - 1 != $params{week_start_day}) {
+        # NOTE: pad the begining
+               $first_wday->subtract_duration($one_day);
+               $t.="<td class='no-wday'></td>";
+        }
+       my $month = $day->month();
+       for (; $day->month() == $month; $day->add_duration($one_day)) {
+               my $event_html
+                = event_html
+                ( $day
+                , {day=>1, day_abbr=>1, new => 1}
+                , %params );
+               $t.= "<td class='wday $event_html->{wday}'>";
+               my @events
+                = map {
+                       my @tags
+                        = map {"<li\n class='".$_->{class}."'>".$_->{link}."</li>"}
+                        @{$_->{tags}};
+                       "<li\n class='event'>"
+                        . "<span class='head'>"
+                                . (defined $_->{hour} ? "<span class='hour'>$_->{hour}</span>" : "")
+                                . "<span class='link'>$_->{link}</span>"
+                        . "</span>"
+                        . "<ul class='tags'>".join("", @tags)."</ul>"
+                        . "</li>\n"
+                }
+                events_of_pages($days{$day->day()}, %params);
+               $t .=
+                "<span class='head'>"
+                        . "<span class='day'>"
+                                . $event_html->{day}
+                                . $event_html->{new}
+                        . "</span>"
+                . "</span>"
+                . "<ul class='events'>".join("", @events)."</ul>";
+               $t.='</td>';
+               if ($day->day_of_week() - 1 == $last_wday) {
+                       $t.="</tr>";
+                       $t.="<tr>"
+                               if ($day->day_of_month() < $last_day);
+                }
+        }
+       while ($day->day_of_week() - 1 != $params{week_start_day}) {
+        # NOTE: pad the end
+               $day->add_duration($one_day);
+               $t.="<td class='no-wday'></td>";
+        }
+       $t.='</tr>';
+       my $event_html
+        = event_html
+        ( $params{focus}
+        , {year=>1, month=>1, month_name=>1}
+        , %params );
+       return
+        "<table class='month'>"
+        . "<thead>"
+                . "<tr>"
+                . "<th colspan='7'>"
+                . "<span class='month'>$event_html->{month}</span>"
+                . " <span class='year'>$event_html->{year}</span>"
+                . "</th>"
+                . "</tr>"
+                . "<tr>"
+                . join ("", map {
+                       $_ = "<th><span>".$first_wday->day_name()."</span></th>";
+                       $first_wday->add_duration($one_day);
+                       $_ } (1..7))
+                . "</tr>"
+        . "</thead>"
+        . "<tbody>$t</tbody></table>";
+ }
+sub preprocess (@) {
+       my %params = @_;
+       $params{focus} = $now->clone;
+       
+       $params{pages} = "*"        unless defined $params{pages};
+       $params{type}  = "month"    unless defined $params{type};
+       $params{week_start_day} = 0 unless defined $params{week_start_day};
+       $params{week_start_day} = $params{week_start_day} % 7;
+       
+       unless (defined $params{base}) {
+               $params{base}
+                = defined $config{events_base}
+                ? $config{events_base}
+                : gettext('Agenda');
+        }
+       if (defined $params{day}) {
+               if ($params{day} =~ m/^([+-])(\d+)$/) {
+                       my ($sign, $days) = ($1, $2);
+                       my $duration = DateTime::Duration->new(days => $days, end_of_month => 'limit');
+                       $params{focus}
+                        = $sign eq '+'
+                        ? $params{focus}->add_duration($duration)
+                        : $params{focus}->subtract_duration($duration);
+                }
+               else {
+                       $params{focus}->set(day => $params{day});
+                }
+        }
+       else {
+               #$params{focus}->set(day => 1);
+        }
+       if (defined $params{month}) {
+               if ($params{month} =~ m/^([+-])(\d+)$/) {
+                       my ($sign, $months) = ($1, $2);
+                       my $duration = DateTime::Duration->new(months => $months, end_of_month => 'limit');
+                       $params{focus}
+                        = $sign eq '+'
+                        ? $params{focus}->add_duration($duration)
+                        : $params{focus}->subtract_duration($duration);
+                }
+               else {
+                       $params{focus}->set(month => $params{month});
+                }
+        }
+       if (defined $params{year}) {
+               if ($params{year} =~ m/^([+-])(\d+)$/) {
+                       my ($sign, $years) = ($1, $2);
+                       my $duration = DateTime::Duration->new(years => $years, end_of_month => 'limit');
+                       $params{focus}
+                        = $sign eq '+'
+                        ? $params{focus}->add_duration($duration)
+                        : $params{focus}->subtract_duration($duration);
+                }
+               else {
+                       $params{focus}->set(year => $params{year});
+                }
+        }
+       
+       #debug("events: focus=".$params{focus}->strftime('%Y-%m-%d_%H-%M-%S'));
+       $params{pages} =~ s[%Y][$params{focus}->year()]eg;
+       $params{pages} =~ s[%m][sprintf('%02d', $params{focus}->month())]eg;
+       $params{pages} =~ s[%d][sprintf('%02d', $params{focus}->day())]eg;
+       
+       set_next_rendering(%params);
+       
+       my $calendar = "";
+       if ($params{type} eq 'month') {
+               $calendar = preprocess_month(%params);
+        }
+       elsif ($params{type} eq 'day') {
+               $calendar = preprocess_day(%params);
+        }
+       
+       return "<div class='calendar'>$calendar</div>";
+ }
+
+# new
+sub tmpl ($$) {
+       my ($base, $model) = @_;
+       my $page = IkiWiki::dirname($base).'/'.'templates/'.$model;
+       my $file = defined srcfile($page, 1) ? '/'.$page : $model;
+       return template($file);
+ }
+sub date_of_form ($$;%) {
+       my ($form, $prefix, %default) = @_;
+       %default =
+        ( year   => 0
+        , month  => 1
+        , day    => 1
+        , hour   => 0
+        , minute => 0
+        , %default
+        );
+       my $date;
+       eval { $date = DateTime->new
+        ( year       => ($form->field($prefix.'_year')   ne '' ? $form->field($prefix.'_year')                : $default{year})
+        , month      => ($form->field($prefix.'_month')  ne '' ? substr($form->field($prefix.'_month'), 0, 2) : $default{month})
+        , day        => ($form->field($prefix.'_day')    ne '' ? $form->field($prefix.'_day')                 : $default{day})
+        , hour       => ($form->field($prefix.'_hour')   ne '' ? $form->field($prefix.'_hour')                : $default{hour})
+        , minute     => ($form->field($prefix.'_minute') ne '' ? $form->field($prefix.'_minute')              : $default{minute})
+        , second     => 0
+        , nanosecond => 0
+        , time_zone  => 'local'
+        , locale     => $config{locale}
+        )->set_time_zone('floating') };
+       return $date;
+ };
+sub duration_of_form ($$) {
+       my ($form, $prefix) = @_;
+       my $dur;
+       eval { $dur = DateTime::Duration->new
+        ( years        => $form->field($prefix.'_year')
+        , months       => $form->field($prefix.'_month')
+        , days         => $form->field($prefix.'_day')
+        , weeks        => $form->field($prefix.'_week')
+        , hours        => $form->field($prefix.'_hour')
+        , minutes      => $form->field($prefix.'_minute')
+        , seconds      => 0
+        , nanoseconds  => 0
+        , end_of_month => 'limit'
+        ) };
+       return $dur;
+ };
+sub page_of_event ($$$$$) {
+       my ($form, $from_date, $to_date, $name, $base) = @_;
+       my $time = '';
+       if ($form->field('from_hour')   ne ''
+       or  $form->field('from_minute') ne '') {
+               if ($from_date->hour() == $to_date->hour()
+                and $from_date->minute() == $to_date->minute()) {
+                       $time = sprintf('%02dh%02d', $from_date->hour(), $from_date->minute());
+                }
+               else {
+                       $time = sprintf('%02dh%02d-%02dh%02d'
+                        , $from_date->hour(), $from_date->minute()
+                        , $to_date->hour(), $to_date->minute());
+                }
+        }
+       return
+        ( $base
+        . ($base?'/':'').$from_date->year()
+        . '/'.sprintf('%02d', $from_date->month())
+        . '/'.sprintf('%02d', $from_date->day())
+        . '/'. ($time ne '' ? $time . '/' : '')
+        . $name
+        );
+ }
+sub check_cannewevent ($$$$) {
+       my $dest=shift;
+       my $destfile=shift;
+       my $cgi=shift;
+       my $session=shift;
+       
+       # Must be a legal filename.
+       if (IkiWiki::file_pruned($destfile)) {
+               error(sprintf(gettext("illegal name")));
+        }
+       # Must not be a known source file.
+       if (exists $pagesources{$dest}) {
+               error(sprintf(gettext("%s already exists"),
+                       htmllink("", "", $dest
+                        , linktext => $dest
+                        , noimageinline => 1)));
+        }
+       # Must not exist on disk already.
+       if (-l "$config{srcdir}/$destfile" || -e _) {
+               error(sprintf(gettext("%s already exists on disk"), $destfile));
+        }
+       
+       # Must be editable.
+       IkiWiki::check_canedit($dest, $cgi, $session);
+       
+       my $can_newevent;
+       IkiWiki::run_hooks(can_newevent => sub {
+               return if defined $can_newevent;
+               my $ret=shift->(cgi => $cgi, session => $session, dest => $dest, destfile => $destfile);
+               if (defined $ret) {
+                       if ($ret eq "") {
+                               $can_newevent=1;
+                        }
+                       elsif (ref $ret eq 'CODE') {
+                               $ret->();
+                               $can_newevent=0;
+                        }
+                       elsif (defined $ret) {
+                               error($ret);
+                               $can_newevent=0;
+                        }
+                }
+        });
+       return defined $can_newevent ? $can_newevent : 1;
+ }
+sub post_newevent ($$$) {
+       my $cgi=shift;
+       my $session=shift;
+       my $dest=shift;
+       
+       IkiWiki::redirect($cgi, urlto($dest));
+       exit;
+ }
+sub newevent_hook {
+       my %params  = @_;
+       my @events  = @{$params{events}};
+       my %done    = %{$params{done}};
+       my $cgi     = $params{cgi};
+       my $session = $params{session};
+       return ()
+               unless @events;
+       my @next;
+       foreach my $event (@events) {
+               unless (exists $done{$event->{page}} && $done{$event->{file}}) {
+                       IkiWiki::run_hooks(newevent => sub {
+                               push @next, shift->
+                                ( cgi     => $cgi
+                                , event   => $event
+                                , session => $session
+                                );
+                        });
+                       $done{$event->{page}} = 1;
+                }
+        }
+       push @events, newevent_hook
+        ( cgi     => $cgi
+        , done    => \%done
+        , events  => \@next
+        , session => $session
+        );
+       my %seen; # NOTE: insure unicity
+       return grep { ! $seen{$_->{page}}++ } @events;
+ }
+sub preview($$$$$) {
+       my ($cgi, $session, $form, $events, $months) = @_;
+       $form->tmpl_param(year   => gettext("year"));
+       $form->tmpl_param(month  => gettext("month"));
+       $form->tmpl_param(day    => gettext("day"));
+       $form->tmpl_param(hour   => gettext("hour"));
+       $form->tmpl_param(min    => gettext("min"));
+       $form->tmpl_param(dow    => gettext("day of week"));
+       $form->tmpl_param(page   => gettext("page"));
+       $form->tmpl_param(events => [
+               map {
+                       { from_year      => $_->{from}->year()
+                       , from_month     => sprintf('%02d', $_->{from}->month())
+                       , from_monthname => $months->{$_->{from}->month()}
+                       , from_day       => sprintf('%02d', $_->{from}->day())
+                       , from_hour      => sprintf('%02d', $_->{from}->hour())
+                       , from_minute    => sprintf('%02d', $_->{from}->minute())
+                       , from_dow       => $_->{from}->dow()
+                       , from_downame   => $_->{from}->day_name()
+                       , to_year        => $_->{to}->year()
+                       , to_month       => sprintf('%02d', $_->{to}->month())
+                       , to_monthname   => $months->{$_->{to}->month()}
+                       , to_day         => sprintf('%02d', $_->{to}->day())
+                       , to_hour        => sprintf('%02d', $_->{to}->hour())
+                       , to_minute      => sprintf('%02d', $_->{to}->minute())
+                       , to_dow         => $_->{to}->dow()
+                       , page           =>
+                               htmllink("", "", $_->{page}
+                                , linktext => $_->{page}
+                                , noimageinline => 1)
+                       }
+                } @$events
+        ]);
+       if (@$events > 0) {
+               my $page = @$events[0];
+               # FROM: editpage.pm
+               my $new = not exists $pagesources{$page};
+               # temporarily record its type
+               my $type = $config{default_pageext};
+               $pagesources{$page} = $page.".".$type if $new;
+               my %wasrendered = map { $_ => 1 } @{$renderedfiles{$page}};
+               my $content = @$events[0]->{content};
+               
+               IkiWiki::run_hooks(editcontent => sub {
+                       $content = shift->
+                        ( cgi     => $cgi
+                        , content => $content
+                        , page    => $page
+                        , session => $session
+                        );
+                });
+               my $preview = IkiWiki::htmlize($page, $page, $type,
+                       IkiWiki::linkify($page, $page,
+                       IkiWiki::preprocess($page, $page,
+                       IkiWiki::filter($page, $page, $content), 0, 1)));
+               IkiWiki::run_hooks(format => sub {
+                       $preview = shift->
+                        ( content => $preview
+                        , page    => $page
+                        );
+                });
+               $form->tmpl_param("preview", $preview);
+               
+               # Previewing may have created files on disk.
+               # Keep a list of these to be deleted later.
+               my %previews = map { $_ => 1 } @{$wikistate{editpage}{previews}};
+               foreach my $f (@{$renderedfiles{$page}}) {
+                       $previews{$f} = 1 unless $wasrendered{$f};
+                }
+               
+               # Throw out any other state changes made during previewing,
+               # and save the previews list.
+               IkiWiki::loadindex();
+               @{$wikistate{editpage}{previews}} = keys %previews;
+               IkiWiki::saveindex();
+        }
+       else {
+               $form->tmpl_param("preview", gettext("No event"));
+        }
+ }
+sub create ($$$$$) {
+       my ($event, $cgi, $session, $months, $base) = @_;
+       check_cannewevent
+        ( $event->{page}
+        , $event->{file}
+        , $cgi
+        , $session
+        );
+       my $pageext = $config{default_pageext};
+       
+       $config{cgi} = 0; # NOTE: avoid CGI error message
+       eval { writefile($event->{file}, $config{srcdir}, $event->{content}) };
+       if ($config{rcs}) {
+               IkiWiki::rcs_add($event->{file});
+        }
+       # month page
+               my $monthpage =
+                ( $base
+                . ($base?'/':'').$event->{from}->year()
+                . '/'.sprintf('%02d', $event->{from}->month())
+                );
+               my $monthfile = IkiWiki::newpagefile($monthpage, $pageext);
+               if (not exists $pagesources{$monthpage}
+                and not -l $config{srcdir}.'/'.$monthfile
+                and not -e _) {
+                       my $tmpl_neweventmonth = tmpl($base, 'neweventmonth.tmpl');
+                       $tmpl_neweventmonth->param(base      => $base);
+                       $tmpl_neweventmonth->param(year      => $event->{from}->year());
+                       $tmpl_neweventmonth->param(month     => sprintf('%02d', $event->{from}->month()));
+                       $tmpl_neweventmonth->param(monthname => $months->{$event->{from}->month()});
+                       my $content = $tmpl_neweventmonth->output();
+                       eval { writefile($monthfile, $config{srcdir}, $content) };
+                       if ($config{rcs}) {
+                               IkiWiki::rcs_add($monthfile);
+                        }
+                }
+       # day page
+               my $daypage =
+                ( $monthpage
+                . '/'.sprintf('%02d', $event->{from}->day())
+                );
+               my $dayfile = IkiWiki::newpagefile($daypage, $pageext);
+               if (not exists $pagesources{$daypage}
+                and not -l $config{srcdir}.'/'.$dayfile
+                and not -e _) {
+                       my $tmpl_neweventday = tmpl($base, 'neweventday.tmpl');
+                       $tmpl_neweventday->param(base      => $base);
+                       $tmpl_neweventday->param(year      => $event->{from}->year());
+                       $tmpl_neweventday->param(month     => sprintf('%02d', $event->{from}->month()));
+                       $tmpl_neweventday->param(monthname => $months->{$event->{from}->month()});
+                       $tmpl_neweventday->param(day       => sprintf('%02d', $event->{from}->day()));
+                       $tmpl_neweventday->param(dayname   => $event->{from}->day_name());
+                       my $content = $tmpl_neweventday->output();
+                       eval { writefile($dayfile, $config{srcdir}, $content) };
+                       if ($config{rcs}) {
+                               IkiWiki::rcs_add($dayfile);
+                        }
+                }
+       $config{cgi} = 1;
+ }
+sub sessioncgi ($$) {
+       my ($cgi, $session) = @_;
+       if (defined $cgi->param('do') && $cgi->param('do') eq "newevent") {
+               # TOTRY: decode_cgi_utf8($cgi);
+               my $base = Encode::decode_utf8(URI::Escape::uri_unescape(IkiWiki::possibly_foolish_untaint($cgi->param('base'))));
+               &IkiWiki::check_canedit($base, $cgi, $session);
+               my $page = Encode::decode_utf8(URI::Escape::uri_unescape(IkiWiki::possibly_foolish_untaint($cgi->param('page'))));
+               
+               my $now_date = DateTime->now
+                ( time_zone => 'local'
+                , locale    => $config{locale}
+                )->set_time_zone('floating');
+               my %dows   = map { ($_ => $now_date->{locale}->day_format_wide->[ $_ ]) } (0..6);
+               my %months = map { ($_ => $now_date->{locale}->month_format_wide->[ $_ - 1 ]) } (1..12);
+               my $cgi_date;
+               eval { $cgi_date = DateTime->new
+                ( year       => defined $cgi->param("year")   ? $cgi->param("year")   : $now_date->year()
+                , month      => defined $cgi->param("month")  ? $cgi->param("month")  : $now_date->month()
+                , day        => defined $cgi->param("day")    ? $cgi->param("day")    : $now_date->day()
+                , hour       => defined $cgi->param("hour")   ? $cgi->param("hour")   : $now_date->hour()
+                , minute     => defined $cgi->param("minute") ? $cgi->param("minute") : $now_date->minute()
+                , second     => 0
+                , nanosecond => 0
+                , time_zone  => 'local'
+                , locale     => $config{locale}
+                )->set_time_zone('floating') };
+               error(sprintf(gettext("illegal date")))
+                       unless $cgi_date;
+               
+               my @years = ($cgi_date->year() .. $cgi_date->year()+5);
+               my $week_start_day
+                = (defined $config{week_start_day} and $config{week_start_day} >= 0 and $config{week_start_day} <= 6)
+                ?          $config{week_start_day}
+                : 1;
+               my @dow_order = ($week_start_day .. 6, 0 .. $week_start_day-1);
+               
+               my $tags = $typedlinks{$page}{tag};
+               my $buttons = [qw{Preview Create}];
+               my ($from_date, $to_date, $end_date, $inc_dur);
+               my $form = CGI::FormBuilder->new
+                ( action => IkiWiki::cgiurl()
+                , charset => "utf-8"
+                , fields => [qw{
+                        do base
+                        from_date from_year from_month from_day from_hour from_minute
+                        to_date to_year to_month to_day to_hour to_minute
+                        inc_dur inc_year inc_month inc_week inc_day inc_hour inc_minute
+                        end_times end_date end_year end_month end_day end_hour end_minute
+                        dom name content
+                        }]
+                , header => 0
+                , javascript => 0
+                , messages =>
+                        {
+                       #   form_required_text  => 'form_required_text'
+                       # , form_invalid_text   => 'form_invalid_text'
+                       # , form_invalid_file   => 'form_invalid_file'
+                       # , form_invalid_input  => gettext('allowed characters: ').$config{wiki_file_chars}
+                        form_invalid_select => gettext('invalid selection')
+                        }
+                , method     => 'POST'
+                , name       => "newevent"
+                , stylesheet => 1
+                , params     => $cgi
+                , required   => [qw{do base year month day name from_date to_date end_date inc_dur}]
+                , submit     => [qw{Preview Create}]
+                , title      => gettext("newevent")
+                , template   => { template("newevent.tmpl") }
+                , validate   =>
+                        { from_date => { perl => sub {
+                                       my (undef, $form) = @_;
+                                       $from_date = date_of_form($form, 'from')
+                                               unless defined $from_date;
+                                       defined $from_date
+                                } }
+                        , to_date   => { perl => sub {
+                                       my (undef, $form) = @_;
+                                       $from_date = date_of_form($form, 'from')
+                                               unless defined $from_date;
+                                       if (defined $from_date) {
+                                               $to_date = date_of_form($form, 'to'
+                                                , year   => $from_date->year()
+                                                , month  => $from_date->month()
+                                                , day    => $from_date->day()
+                                                , hour   => $from_date->hour()
+                                                , minute => $from_date->minute());
+                                               defined $to_date
+                                               and (DateTime->compare($from_date, $to_date) <= 0)
+                                        }
+                                       else {return 0;}
+                                } }
+                        , end_date  => { perl => sub {
+                                       my (undef, $form) = @_;
+                                       if ( $form->field('end_year')   ne ''
+                                        or  $form->field('end_month')  ne ''
+                                        or  $form->field('end_day')    ne '' ) {
+                                               $from_date = date_of_form($form, 'from')
+                                                       unless defined $from_date;
+                                               if (defined $from_date) {
+                                                       $end_date = date_of_form($form, 'end'
+                                                        , year   => $from_date->year()
+                                                        , month  => $from_date->month()
+                                                        , day    => $from_date->day()
+                                                        , hour   => $from_date->hour()
+                                                        , minute => $from_date->minute());
+                                                       (defined $from_date and defined $end_date
+                                                       and DateTime->compare($from_date, $end_date) <= 0)
+                                                }
+                                               else {return 0;}
+                                        }
+                                       else {
+                                               1;
+                                        }
+                                } }
+                        , name => '/^.+$/'
+                        , base => '/^.*$/'
+                        , end_times  => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_year   => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_month  => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_week   => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_day    => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_hour   => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_minute => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
+                        , inc_dur    => sub {
+                                       my (undef, $form) = @_;
+                                       $inc_dur = duration_of_form($form, 'inc');
+                                       defined $inc_dur
+                                       and ($inc_dur->is_positive() or $inc_dur->is_zero());
+                                }
+                        }
+                );
+               $base = $form->field('base') ? $form->field('base') : $base;
+               $form->title(sprintf(gettext("creating new events"), pagetitle(IkiWiki::basename($page))));
+               $form->field(name => "do", type => "hidden", value => 'newevent', force => 1);
+               $form->field(name => "base", type => "hidden", force => 1 , value => $base);
+               $form->field(name => "from_date", type => "hidden", value => '1', force => 1);
+               $form->field(name => "to_date", type => "hidden", value => '1', force => 1);
+               $form->field(name => "end_date", type => "hidden", value => '1', force => 1);
+               $form->field(name => "inc_dur", type => "hidden", value => '1', force => 1);
+               $form->field(name => "from_year", type => 'select', value => $cgi_date->year(), options => \@years);
+               $form->field(name => "from_month", type => 'select'
+                , value => sprintf("%02d", $cgi_date->month()).' - '.$months{$cgi_date->month()}
+                , options => [map { sprintf("%02d", $_).' - '.$months{$_} } (1..12)]);
+               $form->field(name => "from_day", type => 'select'
+                , value => sprintf("%02d", $cgi_date->day())
+                , options => \@days);
+               $form->field(name => "from_hour", type => 'select', value => '', options => \@hours);
+               $form->field(name => "from_minute", type => 'select', value => '', options => \@minutes);
+               $form->field(name => "name", type => 'text', size => 60, value => gettext('New event'));
+               $form->field(name => "to_year", type => 'select', value => '', options => \@years);
+               $form->field(name => "to_month", type => 'select'
+                , value => ''
+                , options => [map { sprintf("%02d", $_).' - '.$months{$_} } (1..12)]);
+               $form->field(name => "to_day", type => 'select'
+                , value => ''
+                , options => \@days);
+               $form->field(name => "to_hour", type => 'select', value => '', options => \@hours);
+               $form->field(name => "to_minute", type => 'select', value => '', options => \@minutes);
+               $form->field(name => "end_year", type => 'select', value => '', options => \@years);
+               $form->field(name => "end_month", type => 'select', value => ''
+                , options => [map { sprintf("%02d", $_).' - '.$months{$_} } (1..12)]);
+               $form->field(name => "end_day", type => 'select', value => '', options => \@days);
+               $form->field(name => "end_hour", type => 'select', value => '', options => \@hours);
+               $form->field(name => "end_minute", type => 'select', value => '', options => \@minutes);
+               $form->field(name => "end_times", type => 'text', value => '0', size => 2);
+               $form->field(name => "inc_year", type => 'text', value => '0', size => 2);
+               $form->field(name => "inc_month", type => 'text', value => '0', size => 2);
+               $form->field(name => "inc_week", type => 'text', value => '0', size => 2);
+               $form->field(name => "inc_day", type => 'text', value => '0', size => 2);
+               $form->field(name => "inc_hour", type => 'text', value => '0', size => 2);
+               $form->field(name => "inc_minute", type => 'text', value => '0', size => 2);
+               my $tmpl_neweventcontent = tmpl($base, 'neweventcontent.tmpl');
+               $tmpl_neweventcontent->param(title => gettext('Title of the event'));
+               $tmpl_neweventcontent->param(tags => [map {{name => $_}} (sort keys %$tags)]);
+               $form->field(name => "content", type => "textarea", size => 30, rows => 20, cols => 80
+                , value => $tmpl_neweventcontent->output());
+               $form->field(name => "dom", type => 'select', multiple => 1, size => 35
+                , options => [map { my $n = $_; map {($n.' '.$dows{$_})} (0..6)} ('1°', '2°', '3°', '4°', '5°')]);
+               
+               IkiWiki::decode_form_utf8($form);
+               IkiWiki::run_hooks(formbuilder_setup => sub {
+                       shift->(form => $form, cgi => $cgi, session => $session, buttons => $buttons);
+                });
+               IkiWiki::decode_form_utf8($form);
+               
+               if (($form->submitted eq 'Create' || $form->submitted eq 'Preview') && $form->validate) {
+                       #IkiWiki::checksessionexpiry($cgi, $session, $cgi->param('sid'));
+                       $base
+                        = $form->field('base')
+                        ? $form->field('base')
+                        : (defined $config{base} ? $config{base} : gettext('Agenda'));
+                       my $end_times
+                        = $form->field('end_times') == 0
+                        ? undef : $form->field('end_times');
+                       my $dom;
+                       foreach ($form->field('dom')) {
+                               $dom = {} if not defined $dom;
+                               $dom->{$_} = 1;
+                        }
+                       my $name = $form->field('name');
+                       $name = IkiWiki::possibly_foolish_untaint(IkiWiki::titlepage($name));
+                               # NOTE: these untaints are safe because of the checks
+                               #       performed in check_cannewevent later.
+                       my $content = $form->field('content');
+                       $content =~ s/\r\n/\n/gs;
+                       $content =~ s/\n$//s;
+                       
+                       # Queue of event creations to perfom.
+                       my @events = ();
+                       my $events_try = 0;
+                       my $events_max
+                        = defined $config{newevent_max_per_commit}
+                        ? $config{newevent_max_per_commit} : (2 * 365) ;
+                       my $pageext = $config{default_pageext};
+                       while (++$events_try <= $events_max
+                        and (not defined $end_times or --$end_times >= 0)
+                        and (not defined $end_date or DateTime->compare($from_date, $end_date) <= 0)) {
+                               my $dest = page_of_event($form, $from_date, $to_date, $name, $base);
+                               my $week = $from_date->weekday_of_month();
+                               my $day = $now_date->{locale}->day_format_wide->[$from_date->day_of_week()-1];
+                               if (not defined $dom or exists $dom->{"$week° $day"}) {
+                                       push @events,
+                                        { page => $dest
+                                        , file => IkiWiki::newpagefile($dest, $pageext)
+                                        , from => $from_date
+                                        , to   => $to_date
+                                        , name => $name
+                                        };
+                                }
+                               last unless defined $inc_dur and $inc_dur->is_positive();
+                               $from_date = $from_date->clone->add_duration($inc_dur);
+                               $to_date   = $to_date->clone->add_duration($inc_dur);
+                        }
+                       error("events try per commit overflow: $events_max")
+                               unless $events_try <= $events_max;
+                       my $tmpl_neweventpage = tmpl($base, 'neweventpage.tmpl');
+                       my $i = 0;
+                       foreach (@events) {
+                               $tmpl_neweventpage->clear_params();
+                               $tmpl_neweventpage->param(content => $content);
+                               $tmpl_neweventpage->param(page => $_->{page});
+                               $tmpl_neweventpage->param(event => $i);
+                               $tmpl_neweventpage->param("event_first" => 1)
+                                       if $i == 0;
+                               $tmpl_neweventpage->param("event_last" => 1)
+                                       if $i == @events - 1;
+                               $tmpl_neweventpage->param(events => \@events);
+                               $tmpl_neweventpage->param(from_date => "$_->{from}");
+                               $tmpl_neweventpage->param(name => $_->{name});
+                               $tmpl_neweventpage->param(to_date => "$_->{to}");
+                               $_->{content} = $tmpl_neweventpage->output();
+                               $i++;
+                        }
+                       if ($form->submitted eq 'Create') {
+                               @events = newevent_hook
+                                ( cgi     => $cgi
+                                , done    => {}
+                                , events  => \@events
+                                , session => $session
+                                );
+                               require IkiWiki::Render;
+                               if ($config{rcs}) {
+                                       IkiWiki::disable_commit_hook()
+                                }
+                               foreach my $event (@events) {
+                                       create($event, $cgi, $session, \%months, $base);
+                                }
+                               if ($config{rcs}) {
+                                       IkiWiki::rcs_commit_staged
+                                        ( message => sprintf(gettext("new event"))
+                                        , session => $session );
+                                       IkiWiki::enable_commit_hook();
+                                       IkiWiki::rcs_update();
+                                }
+                               IkiWiki::refresh();
+                               IkiWiki::saveindex();
+                               
+                               post_newevent($cgi, $session, (defined $events[0] ? $events[0]->{page} : ''));
+                        }
+                       elsif ($form->submitted eq 'Preview') {
+                               preview($cgi, $session, $form, \@events, \%months);
+                               IkiWiki::showform($form, $buttons, $session, $cgi);
+                        }
+                }
+               else {
+                       IkiWiki::showform($form, $buttons, $session, $cgi);
+                }
+               
+               exit 0;
+        }
+ }
+
+1;
diff --git a/newevent.pm b/newevent.pm
deleted file mode 100644 (file)
index ec34b72..0000000
+++ /dev/null
@@ -1,634 +0,0 @@
-#!/usr/bin/perl
-package IkiWiki::Plugin::newevent;
-
-use strict;
-use warnings;
-use IkiWiki 3.00;
-use Time::Local;
-use DateTime;
-use CGI::FormBuilder;
-use Data::Dumper;
-
-sub import {
-       #hook(type => "formbuilder",       id => "newevent", call => \&formbuilder);
-       #hook(type => "formbuilder_setup", id => "newevent", call => \&formbuilder_setup);
-       hook(type => "getsetup",          id => "newevent", call => \&getsetup);
-       #hook(type => "preprocess",        id => "newevent", call => \&preprocess);
-       #hook(type => "refresh",           id => "newevent", call => \&refresh);
-       hook(type => "sessioncgi",        id => "newevent", call => \&sessioncgi);
- }
-
-my @days    = ('01'..'31');
-my @hours   = ('00'..'23');
-my @minutes = ('00'..'59');
-
-sub getsetup () {
-       return
-        ( plugin =>
-                { safe    => 1
-                , rebuild => undef
-                , section => "misc"
-                }
-        , base =>
-                { type        => "string"
-                , example     => "Agenda"
-                , description => "prefix of the agenda hierarchy"
-                , safe        => 1
-                , rebuild     => 1
-                }
-        );
- }
-sub tmpl ($$) {
-       my ($base, $model) = @_;
-       my $page = IkiWiki::dirname($base).'/'.'templates/'.$model;
-       my $file = defined srcfile($page, 1) ? '/'.$page : $model;
-       return template($file);
- }
-sub date_of_form ($$;%) {
-       my ($form, $prefix, %default) = @_;
-       %default =
-        ( year   => 0
-        , month  => 1
-        , day    => 1
-        , hour   => 0
-        , minute => 0
-        , %default
-        );
-       my $date;
-       eval { $date = DateTime->new
-        ( year       => ($form->field($prefix.'_year')   ne '' ? $form->field($prefix.'_year')                : $default{year})
-        , month      => ($form->field($prefix.'_month')  ne '' ? substr($form->field($prefix.'_month'), 0, 2) : $default{month})
-        , day        => ($form->field($prefix.'_day')    ne '' ? $form->field($prefix.'_day')                 : $default{day})
-        , hour       => ($form->field($prefix.'_hour')   ne '' ? $form->field($prefix.'_hour')                : $default{hour})
-        , minute     => ($form->field($prefix.'_minute') ne '' ? $form->field($prefix.'_minute')              : $default{minute})
-        , second     => 0
-        , nanosecond => 0
-        , time_zone  => 'local'
-        , locale     => $config{locale}
-        )->set_time_zone('floating') };
-       return $date;
- };
-sub duration_of_form ($$) {
-       my ($form, $prefix) = @_;
-       my $dur;
-       eval { $dur = DateTime::Duration->new
-        ( years        => $form->field($prefix.'_year')
-        , months       => $form->field($prefix.'_month')
-        , days         => $form->field($prefix.'_day')
-        , weeks        => $form->field($prefix.'_week')
-        , hours        => $form->field($prefix.'_hour')
-        , minutes      => $form->field($prefix.'_minute')
-        , seconds      => 0
-        , nanoseconds  => 0
-        , end_of_month => 'limit'
-        ) };
-       return $dur;
- };
-sub page_of_event ($$$$$) {
-       my ($form, $from_date, $to_date, $name, $base) = @_;
-       my $time = '';
-       if ($form->field('from_hour') ne '' or $form->field('from_minute') ne '') {
-               if ($from_date->hour() == $to_date->hour()
-                and $from_date->minute() == $to_date->minute()) {
-                       $time = sprintf('%02dh%02d', $from_date->hour(), $from_date->minute());
-                }
-               else {
-                       $time = sprintf('%02dh%02d-%02dh%02d'
-                        , $from_date->hour(), $from_date->minute()
-                        , $to_date->hour(), $to_date->minute());
-                }
-        }
-       return
-        ( $base
-        . ($base?'/':'').$from_date->year()
-        . '/'.sprintf('%02d', $from_date->month())
-        . '/'.sprintf('%02d', $from_date->day())
-        . '/'. ($time ne '' ? $time . '/' : '')
-        . $name
-        );
- }
-sub check_cannewevent ($$$$) {
-       my $dest=shift;
-       my $destfile=shift;
-       my $cgi=shift;
-       my $session=shift;
-       
-       # Must be a legal filename.
-       if (IkiWiki::file_pruned($destfile)) {
-               error(sprintf(gettext("illegal name")));
-        }
-       # Must not be a known source file.
-       if (exists $pagesources{$dest}) {
-               error(sprintf(gettext("%s already exists"),
-                       htmllink("", "", $dest
-                        , linktext => $dest
-                        , noimageinline => 1)));
-        }
-       # Must not exist on disk already.
-       if (-l "$config{srcdir}/$destfile" || -e _) {
-               error(sprintf(gettext("%s already exists on disk"), $destfile));
-        }
-       
-       # Must be editable.
-       IkiWiki::check_canedit($dest, $cgi, $session);
-       
-       my $can_newevent;
-       IkiWiki::run_hooks(can_newevent => sub {
-               return if defined $can_newevent;
-               my $ret=shift->(cgi => $cgi, session => $session, dest => $dest, destfile => $destfile);
-               if (defined $ret) {
-                       if ($ret eq "") {
-                               $can_newevent=1;
-                        }
-                       elsif (ref $ret eq 'CODE') {
-                               $ret->();
-                               $can_newevent=0;
-                        }
-                       elsif (defined $ret) {
-                               error($ret);
-                               $can_newevent=0;
-                        }
-                }
-        });
-       return defined $can_newevent ? $can_newevent : 1;
- }
-sub post_newevent ($$$) {
-       my $cgi=shift;
-       my $session=shift;
-       my $dest=shift;
-       
-       IkiWiki::redirect($cgi, urlto($dest));
-       exit;
- }
-sub preprocess (@) {
-       #my %params =
-       # ( base => ($config{base} ? $config{base} : gettext('Agenda'))
-       # , @_ );
-       #($form, $buttons) = newevent_form()
-       #       if not defined $form;
-       #my $ret = $form->render();
-       return "<div class='newevent'></div>";
- }
-sub sessioncgi ($$) {
-       my ($cgi, $session) = @_;
-       if (defined $cgi->param('do') && $cgi->param('do') eq "newevent") {
-               # TOTRY: decode_cgi_utf8($cgi);
-               my $base = Encode::decode_utf8(URI::Escape::uri_unescape(IkiWiki::possibly_foolish_untaint($cgi->param('base'))));
-               &IkiWiki::check_canedit($base, $cgi, $session);
-               my $page = Encode::decode_utf8(URI::Escape::uri_unescape(IkiWiki::possibly_foolish_untaint($cgi->param('page'))));
-               
-               my $now_date = DateTime->now
-                ( time_zone => 'local'
-                , locale    => $config{locale}
-                )->set_time_zone('floating');
-               my %dows   = map { ($_ => $now_date->{locale}->day_format_wide->[ $_ ]) } (0..6);
-               my %months = map { ($_ => $now_date->{locale}->month_format_wide->[ $_ - 1 ]) } (1..12);
-               my $cgi_date;
-               eval { $cgi_date = DateTime->new
-                ( year       => defined $cgi->param("year")   ? $cgi->param("year")   : $now_date->year()
-                , month      => defined $cgi->param("month")  ? $cgi->param("month")  : $now_date->month()
-                , day        => defined $cgi->param("day")    ? $cgi->param("day")    : $now_date->day()
-                , hour       => defined $cgi->param("hour")   ? $cgi->param("hour")   : $now_date->hour()
-                , minute     => defined $cgi->param("minute") ? $cgi->param("minute") : $now_date->minute()
-                , second     => 0
-                , nanosecond => 0
-                , time_zone  => 'local'
-                , locale     => $config{locale}
-                )->set_time_zone('floating') };
-               error(sprintf(gettext("illegal date")))
-                       unless $cgi_date;
-               
-               my @years = ($cgi_date->year() .. $cgi_date->year()+5);
-               my $week_start_day
-                = (defined $config{week_start_day} and $config{week_start_day} >= 0 and $config{week_start_day} <= 6)
-                ?          $config{week_start_day}
-                : 1;
-               my @dow_order = ($week_start_day .. 6, 0 .. $week_start_day-1);
-               
-               my $tags = $typedlinks{$page}{tag};
-               my $buttons = [qw{Preview Create}];
-               my ($from_date, $to_date, $end_date, $inc_dur);
-               my $form = CGI::FormBuilder->new
-                ( action => IkiWiki::cgiurl()
-                , charset => "utf-8"
-                , fields => [qw{
-                        do base
-                        from_date from_year from_month from_day from_hour from_minute
-                        to_date to_year to_month to_day to_hour to_minute
-                        inc_dur inc_year inc_month inc_week inc_day inc_hour inc_minute
-                        end_times end_date end_year end_month end_day end_hour end_minute
-                        dom name content
-                        }]
-                , header => 0
-                , javascript => 0
-                , messages =>
-                        {
-                       #   form_required_text  => 'form_required_text'
-                       # , form_invalid_text   => 'form_invalid_text'
-                       # , form_invalid_file   => 'form_invalid_file'
-                       # , form_invalid_input  => gettext('allowed characters: ').$config{wiki_file_chars}
-                        form_invalid_select => gettext('invalid selection')
-                        }
-                , method     => 'POST'
-                , name       => "newevent"
-                , stylesheet => 1
-                , params     => $cgi
-                , required   => [qw{do base year month day name from_date to_date end_date inc_dur}]
-                , submit     => [qw{Preview Create}]
-                , title      => gettext("newevent")
-                , template   => { template("newevent.tmpl") }
-                , validate   =>
-                        { from_date => { perl => sub {
-                                       my (undef, $form) = @_;
-                                       $from_date = date_of_form($form, 'from')
-                                               unless defined $from_date;
-                                       defined $from_date
-                                } }
-                        , to_date   => { perl => sub {
-                                       my (undef, $form) = @_;
-                                       $from_date = date_of_form($form, 'from')
-                                               unless defined $from_date;
-                                       if (defined $from_date) {
-                                               $to_date = date_of_form($form, 'to'
-                                                , year   => $from_date->year()
-                                                , month  => $from_date->month()
-                                                , day    => $from_date->day()
-                                                , hour   => $from_date->hour()
-                                                , minute => $from_date->minute());
-                                               defined $to_date
-                                               and (DateTime->compare($from_date, $to_date) <= 0)
-                                        }
-                                       else {return 0;}
-                                } }
-                        , end_date  => { perl => sub {
-                                       my (undef, $form) = @_;
-                                       if ( $form->field('end_year')   ne ''
-                                        or  $form->field('end_month')  ne ''
-                                        or  $form->field('end_day')    ne '' ) {
-                                               $from_date = date_of_form($form, 'from')
-                                                       unless defined $from_date;
-                                               if (defined $from_date) {
-                                                       $end_date = date_of_form($form, 'end'
-                                                        , year   => $from_date->year()
-                                                        , month  => $from_date->month()
-                                                        , day    => $from_date->day()
-                                                        , hour   => $from_date->hour()
-                                                        , minute => $from_date->minute());
-                                                       (defined $from_date and defined $end_date
-                                                       and DateTime->compare($from_date, $end_date) <= 0)
-                                                }
-                                               else {return 0;}
-                                        }
-                                       else {
-                                               1;
-                                        }
-                                } }
-                        , name => '/^.+$/'
-                        , base => '/^.*$/'
-                        , end_times  => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_year   => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_month  => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_week   => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_day    => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_hour   => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_minute => sub { $_[0] =~ m/^\d+$/ and $_[0] >= 0 }
-                        , inc_dur    => sub {
-                                       my (undef, $form) = @_;
-                                       $inc_dur = duration_of_form($form, 'inc');
-                                       defined $inc_dur
-                                       and ($inc_dur->is_positive() or $inc_dur->is_zero());
-                                }
-                        }
-                );
-               $base = $form->field('base') ? $form->field('base') : $base;
-               $form->title(sprintf(gettext("creating new events"), pagetitle(IkiWiki::basename($page))));
-               $form->field(name => "do", type => "hidden", value => 'newevent', force => 1);
-               $form->field(name => "base", type => "hidden", force => 1 , value => $base);
-               $form->field(name => "from_date", type => "hidden", value => '1', force => 1);
-               $form->field(name => "to_date", type => "hidden", value => '1', force => 1);
-               $form->field(name => "end_date", type => "hidden", value => '1', force => 1);
-               $form->field(name => "inc_dur", type => "hidden", value => '1', force => 1);
-               $form->field(name => "from_year", type => 'select', value => $cgi_date->year(), options => \@years);
-               $form->field(name => "from_month", type => 'select'
-                , value => sprintf("%02d", $cgi_date->month()).' - '.$months{$cgi_date->month()}
-                , options => [map { sprintf("%02d", $_).' - '.$months{$_} } (1..12)]);
-               $form->field(name => "from_day", type => 'select'
-                , value => sprintf("%02d", $cgi_date->day())
-                , options => \@days);
-               $form->field(name => "from_hour", type => 'select', value => '', options => \@hours);
-               $form->field(name => "from_minute", type => 'select', value => '', options => \@minutes);
-               $form->field(name => "name", type => 'text', size => 60, value => gettext('New event'));
-               $form->field(name => "to_year", type => 'select', value => '', options => \@years);
-               $form->field(name => "to_month", type => 'select'
-                , value => ''
-                , options => [map { sprintf("%02d", $_).' - '.$months{$_} } (1..12)]);
-               $form->field(name => "to_day", type => 'select'
-                , value => ''
-                , options => \@days);
-               $form->field(name => "to_hour", type => 'select', value => '', options => \@hours);
-               $form->field(name => "to_minute", type => 'select', value => '', options => \@minutes);
-               $form->field(name => "end_year", type => 'select', value => '', options => \@years);
-               $form->field(name => "end_month", type => 'select', value => ''
-                , options => [map { sprintf("%02d", $_).' - '.$months{$_} } (1..12)]);
-               $form->field(name => "end_day", type => 'select', value => '', options => \@days);
-               $form->field(name => "end_hour", type => 'select', value => '', options => \@hours);
-               $form->field(name => "end_minute", type => 'select', value => '', options => \@minutes);
-               $form->field(name => "end_times", type => 'text', value => '0', size => 2);
-               $form->field(name => "inc_year", type => 'text', value => '0', size => 2);
-               $form->field(name => "inc_month", type => 'text', value => '0', size => 2);
-               $form->field(name => "inc_week", type => 'text', value => '0', size => 2);
-               $form->field(name => "inc_day", type => 'text', value => '0', size => 2);
-               $form->field(name => "inc_hour", type => 'text', value => '0', size => 2);
-               $form->field(name => "inc_minute", type => 'text', value => '0', size => 2);
-               my $tmpl_neweventcontent = tmpl($base, 'neweventcontent.tmpl');
-               $tmpl_neweventcontent->param(title => gettext('Title of the event'));
-               $tmpl_neweventcontent->param(tags => [map {{name => $_}} (sort keys %$tags)]);
-               $form->field(name => "content", type => "textarea", size => 30, rows => 20, cols => 80
-                , value => $tmpl_neweventcontent->output());
-               $form->field(name => "dom", type => 'select', multiple => 1, size => 35
-                , options => [map { my $n = $_; map {($n.' '.$dows{$_})} (0..6)} ('1°', '2°', '3°', '4°', '5°')]);
-               
-               IkiWiki::decode_form_utf8($form);
-               IkiWiki::run_hooks(formbuilder_setup => sub {
-                       shift->(form => $form, cgi => $cgi, session => $session, buttons => $buttons);
-                });
-               IkiWiki::decode_form_utf8($form);
-               
-               if (($form->submitted eq 'Create' || $form->submitted eq 'Preview') && $form->validate) {
-                       #IkiWiki::checksessionexpiry($cgi, $session, $cgi->param('sid'));
-                       $base
-                        = $form->field('base')
-                        ? $form->field('base')
-                        : (defined $config{base} ? $config{base} : gettext('Agenda'));
-                       my $end_times
-                        = $form->field('end_times') == 0
-                        ? undef : $form->field('end_times');
-                       my $dom;
-                       foreach ($form->field('dom')) {
-                               $dom = {} if not defined $dom;
-                               $dom->{$_} = 1;
-                        }
-                       my $name = $form->field('name');
-                       $name = IkiWiki::possibly_foolish_untaint(IkiWiki::titlepage($name));
-                               # NOTE: these untaints are safe because of the checks
-                               #       performed in check_cannewevent later.
-                       my $content = $form->field('content');
-                       $content =~ s/\r\n/\n/gs;
-                       $content =~ s/\n$//s;
-                       
-                       # Queue of event creations to perfom.
-                       my @events = ();
-                       my $events_try = 0;
-                       my $events_max
-                        = defined $config{newevent_max_per_commit}
-                        ? $config{newevent_max_per_commit} : (2 * 365) ;
-                       my $pageext = $config{default_pageext};
-                       while (++$events_try <= $events_max
-                        and (not defined $end_times or --$end_times >= 0)
-                        and (not defined $end_date or DateTime->compare($from_date, $end_date) <= 0)) {
-                               my $dest = page_of_event($form, $from_date, $to_date, $name, $base);
-                               my $week = $from_date->weekday_of_month();
-                               my $day = $now_date->{locale}->day_format_wide->[$from_date->day_of_week()-1];
-                               if (not defined $dom or exists $dom->{"$week° $day"}) {
-                                       push @events,
-                                        { page => $dest
-                                        , file => IkiWiki::newpagefile($dest, $pageext)
-                                        , from => $from_date
-                                        , to   => $to_date
-                                        , name => $name
-                                        };
-                                }
-                               last unless defined $inc_dur and $inc_dur->is_positive();
-                               $from_date = $from_date->clone->add_duration($inc_dur);
-                               $to_date   = $to_date->clone->add_duration($inc_dur);
-                        }
-                       error("events try per commit overflow: $events_max")
-                               unless $events_try <= $events_max;
-                       my $tmpl_neweventpage = tmpl($base, 'neweventpage.tmpl');
-                       my $i = 0;
-                       foreach (@events) {
-                               $tmpl_neweventpage->clear_params();
-                               $tmpl_neweventpage->param(content => $content);
-                               $tmpl_neweventpage->param(page => $_->{page});
-                               $tmpl_neweventpage->param(event => $i);
-                               $tmpl_neweventpage->param("event_first" => 1)
-                                       if $i == 0;
-                               $tmpl_neweventpage->param("event_last" => 1)
-                                       if $i == @events - 1;
-                               $tmpl_neweventpage->param(events => \@events);
-                               $tmpl_neweventpage->param(from_date => "$_->{from}");
-                               $tmpl_neweventpage->param(name => $_->{name});
-                               $tmpl_neweventpage->param(to_date => "$_->{to}");
-                               $_->{content} = $tmpl_neweventpage->output();
-                               $i++;
-                        }
-                       if ($form->submitted eq 'Create') {
-                               @events = newevent_hook
-                                ( cgi     => $cgi
-                                , done    => {}
-                                , events  => \@events
-                                , session => $session
-                                );
-                               require IkiWiki::Render;
-                               if ($config{rcs}) {
-                                       IkiWiki::disable_commit_hook()
-                                }
-                               foreach my $event (@events) {
-                                       create($event, $cgi, $session, \%months, $base);
-                                }
-                               if ($config{rcs}) {
-                                       IkiWiki::rcs_commit_staged
-                                        ( message => sprintf(gettext("new event"))
-                                        , session => $session );
-                                       IkiWiki::enable_commit_hook();
-                                       IkiWiki::rcs_update();
-                                }
-                               IkiWiki::refresh();
-                               IkiWiki::saveindex();
-                               
-                               post_newevent($cgi, $session, (defined $events[0] ? $events[0]->{page} : ''));
-                        }
-                       elsif ($form->submitted eq 'Preview') {
-                               preview($cgi, $session, $form, \@events, \%months);
-                               IkiWiki::showform($form, $buttons, $session, $cgi);
-                        }
-                }
-               else {
-                       IkiWiki::showform($form, $buttons, $session, $cgi);
-                }
-               
-               exit 0;
-        }
- }
-sub preview($$$$) {
-       my ($cgi, $session, $form, $events, $months) = @_;
-       $form->tmpl_param(year   => gettext("year"));
-       $form->tmpl_param(month  => gettext("month"));
-       $form->tmpl_param(day    => gettext("day"));
-       $form->tmpl_param(hour   => gettext("hour"));
-       $form->tmpl_param(min    => gettext("min"));
-       $form->tmpl_param(dow    => gettext("day of week"));
-       $form->tmpl_param(page   => gettext("page"));
-       $form->tmpl_param(events => [
-               map {
-                       { from_year      => $_->{from}->year()
-                       , from_month     => sprintf('%02d', $_->{from}->month())
-                       , from_monthname => $months->{$_->{from}->month()}
-                       , from_day       => sprintf('%02d', $_->{from}->day())
-                       , from_hour      => sprintf('%02d', $_->{from}->hour())
-                       , from_minute    => sprintf('%02d', $_->{from}->minute())
-                       , from_dow       => $_->{from}->dow()
-                       , from_downame   => $_->{from}->day_name()
-                       , to_year        => $_->{to}->year()
-                       , to_month       => sprintf('%02d', $_->{to}->month())
-                       , to_monthname   => $months->{$_->{to}->month()}
-                       , to_day         => sprintf('%02d', $_->{to}->day())
-                       , to_hour        => sprintf('%02d', $_->{to}->hour())
-                       , to_minute      => sprintf('%02d', $_->{to}->minute())
-                       , to_dow         => $_->{to}->dow()
-                       , page           =>
-                               htmllink("", "", $_->{page}
-                                , linktext => $_->{page}
-                                , noimageinline => 1)
-                       }
-                } @$events
-        ]);
-       if (@$events > 0) {
-               my $page = @$events[0];
-               # FROM: editpage.pm
-               my $new = not exists $pagesources{$page};
-               # temporarily record its type
-               my $type = $config{default_pageext};
-               $pagesources{$page} = $page.".".$type if $new;
-               my %wasrendered = map { $_ => 1 } @{$renderedfiles{$page}};
-               my $content = @$events[0]->{content};
-               
-               IkiWiki::run_hooks(editcontent => sub {
-                       $content = shift->
-                        ( cgi     => $cgi
-                        , content => $content
-                        , page    => $page
-                        , session => $session
-                        );
-                });
-               my $preview = IkiWiki::htmlize($page, $page, $type,
-                       IkiWiki::linkify($page, $page,
-                       IkiWiki::preprocess($page, $page,
-                       IkiWiki::filter($page, $page, $content), 0, 1)));
-               IkiWiki::run_hooks(format => sub {
-                       $preview = shift->
-                        ( content => $preview
-                        , page    => $page
-                        );
-                });
-               $form->tmpl_param("preview", $preview);
-               
-               # Previewing may have created files on disk.
-               # Keep a list of these to be deleted later.
-               my %previews = map { $_ => 1 } @{$wikistate{editpage}{previews}};
-               foreach my $f (@{$renderedfiles{$page}}) {
-                       $previews{$f} = 1 unless $wasrendered{$f};
-                }
-               
-               # Throw out any other state changes made during previewing,
-               # and save the previews list.
-               IkiWiki::loadindex();
-               @{$wikistate{editpage}{previews}} = keys %previews;
-               IkiWiki::saveindex();
-        }
-       else {
-               $form->tmpl_param("preview", gettext("No event"));
-        }
- }
-sub create ($$$$$) {
-       my ($event, $cgi, $session, $months, $base) = @_;
-       check_cannewevent
-        ( $event->{page}
-        , $event->{file}
-        , $cgi
-        , $session
-        );
-       my $pageext = $config{default_pageext};
-       
-       $config{cgi} = 0; # NOTE: avoid CGI error message
-       eval { writefile($event->{file}, $config{srcdir}, $event->{content}) };
-       if ($config{rcs}) {
-               IkiWiki::rcs_add($event->{file});
-        }
-       # month page
-               my $monthpage =
-                ( $base
-                . ($base?'/':'').$event->{from}->year()
-                . '/'.sprintf('%02d', $event->{from}->month())
-                );
-               my $monthfile = IkiWiki::newpagefile($monthpage, $pageext);
-               if (not exists $pagesources{$monthpage}
-                and not -l $config{srcdir}.'/'.$monthfile
-                and not -e _) {
-                       my $tmpl_neweventmonth = tmpl($base, 'neweventmonth.tmpl');
-                       $tmpl_neweventmonth->param(base      => $base);
-                       $tmpl_neweventmonth->param(year      => $event->{from}->year());
-                       $tmpl_neweventmonth->param(month     => sprintf('%02d', $event->{from}->month()));
-                       $tmpl_neweventmonth->param(monthname => $months->{$event->{from}->month()});
-                       my $content = $tmpl_neweventmonth->output();
-                       eval { writefile($monthfile, $config{srcdir}, $content) };
-                       if ($config{rcs}) {
-                               IkiWiki::rcs_add($monthfile);
-                        }
-                }
-       # day page
-               my $daypage =
-                ( $monthpage
-                . '/'.sprintf('%02d', $event->{from}->day())
-                );
-               my $dayfile = IkiWiki::newpagefile($daypage, $pageext);
-               if (not exists $pagesources{$daypage}
-                and not -l $config{srcdir}.'/'.$dayfile
-                and not -e _) {
-                       my $tmpl_neweventday = tmpl($base, 'neweventday.tmpl');
-                       $tmpl_neweventday->param(base      => $base);
-                       $tmpl_neweventday->param(year      => $event->{from}->year());
-                       $tmpl_neweventday->param(month     => sprintf('%02d', $event->{from}->month()));
-                       $tmpl_neweventday->param(monthname => $months->{$event->{from}->month()});
-                       $tmpl_neweventday->param(day       => sprintf('%02d', $event->{from}->day()));
-                       $tmpl_neweventday->param(dayname   => $event->{from}->day_name());
-                       my $content = $tmpl_neweventday->output();
-                       eval { writefile($dayfile, $config{srcdir}, $content) };
-                       if ($config{rcs}) {
-                               IkiWiki::rcs_add($dayfile);
-                        }
-                }
-       $config{cgi} = 1;
- }
-sub newevent_hook {
-       my %params  = @_;
-       my @events  = @{$params{events}};
-       my %done    = %{$params{done}};
-       my $cgi     = $params{cgi};
-       my $session = $params{session};
-       return ()
-               unless @events;
-       my @next;
-       foreach my $event (@events) {
-               unless (exists $done{$event->{page}} && $done{$event->{file}}) {
-                       IkiWiki::run_hooks(newevent => sub {
-                               push @next, shift->
-                                ( cgi     => $cgi
-                                , event   => $event
-                                , session => $session
-                                );
-                        });
-                       $done{$event->{page}} = 1;
-                }
-        }
-       push @events, newevent_hook
-        ( cgi     => $cgi
-        , done    => \%done
-        , events  => \@next
-        , session => $session
-        );
-       my %seen; # NOTE: insure unicity
-       return grep { ! $seen{$_->{page}}++ } @events;
- }
-
-1;
index 0180140..25c77b0 100644 (file)
@@ -1,8 +1,6 @@
 [[!meta title="<TMPL_VAR DAY> - <TMPL_VAR DAYNAME>"]]
-[[!meta date="<TMPL_VAR YEAR>-<TMPL_VAR MONTH>-<TMPL_VAR DAY>T00:00:00"]]
 
-# Évènements du jour
-[[!map
- pages="page(./<TMPL_VAR DAY>/*) and !*/Discussion"
- show="title"
+[[!events type="day" day="<TMPL_VAR DAY>"
+ base="<TMPL_VAR BASE>"
+ pages="page(./%d/*) and !*/Discussion"
  ]]
index 2b8f3d4..955ecb7 100644 (file)
@@ -1,12 +1,6 @@
 [[!meta title="<TMPL_VAR MONTH> - <TMPL_VAR MONTHNAME>"]]
-[[!meta date="<TMPL_VAR YEAR>-<TMPL_VAR MONTH>-01T00:00:00"]]
 
-[[!calendar
- month="<TMPL_VAR MONTH>"
- neweventbase="<TMPL_VAR BASE>"
+[[!calendar type="month" month="<TMPL_VAR MONTH>"
+ base="<TMPL_VAR BASE>"
  pages="page(./<TMPL_VAR MONTH>/*/*) and !*/Discussion"
- popup="no"
- type="month"
- year="<TMPL_VAR YEAR>"
- week_start_day="1"
  ]]