init
authorJulien Moutinho <julm+ikiwiki+newevent@autogeree.net>
Tue, 11 Feb 2014 03:18:31 +0000 (04:18 +0100)
committerJulien Moutinho <julm+ikiwiki+newevent@autogeree.net>
Wed, 12 Feb 2014 02:45:40 +0000 (03:45 +0100)
newevent.pm [new file with mode: 0644]

diff --git a/newevent.pm b/newevent.pm
new file mode 100644 (file)
index 0000000..2d60517
--- /dev/null
@@ -0,0 +1,622 @@
+#!/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"
+                }
+        , newevent_base =>
+                { type        => "string"
+                , example     => "Agenda"
+                , description => "prefix of the agenda hierarchy"
+                , safe        => 1
+                , rebuild     => 1
+                }
+        );
+ }
+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, $newevent_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
+        ( $newevent_base
+        . ($newevent_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{newevent_base} ? $config{newevent_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 $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 $page = Encode::decode_utf8($cgi->param("page"));
+               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
+                        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 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 => '/^.+$/'
+                        , 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());
+                                }
+                        }
+                );
+               $form->title(sprintf(gettext("creating new events"), pagetitle(IkiWiki::basename($page))));
+               $form->field(name => "do", type => "hidden", value => 'newevent', force => 1);
+               $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 = template("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'));
+                       my $newevent_base = $cgi->param('base');
+                       $newevent_base = $config{newevent_base}
+                               unless defined $newevent_base;
+                       $newevent_base = gettext('Agenda')
+                               unless defined $newevent_base;
+                       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, $newevent_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 = template("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, $newevent_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, $newevent_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 =
+                ( $newevent_base
+                . ($newevent_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 = template("neweventmonth.tmpl");
+                       $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 = template("neweventday.tmpl");
+                       $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;