From: Julien Moutinho Date: Fri, 4 Apr 2014 23:32:00 +0000 (+0200) Subject: add calendar rendering X-Git-Url: http://git.cyclocoop.org/?p=ikiwiki%2Fevents.git;a=commitdiff_plain;h=3624b2511a8ff22fa7020a48437dfaf12605d9f0 add calendar rendering --- diff --git a/events.pm b/events.pm new file mode 100644 index 0000000..e06e2ce --- /dev/null +++ b/events.pm @@ -0,0 +1,1154 @@ +#! /usr/bin/perl +# Copyright (C) 2014 Julien Moutinho +# +# 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{ $params{destpage} + ) + . qq{' rel='nofollow'>+}; + } + } + 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 {"".$_->{link}.""} + @{$_->{tags}}; + "
  • " + . "" + . (defined $_->{hour} ? "$_->{hour}" : "") + . "$_->{link}" + . "" + . "".join("", @tags)."" + . "
  • " + } + events_of_pages(\@pages, %params); + return + "" + . "" + . $event_html->{day} + . $event_html->{new} + . "" + . "" + . join("", @events) + . "" + . ""; + } +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=''; + 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.=""; + } + 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.= ""; + my @events + = map { + my @tags + = map {"".$_->{link}.""} + @{$_->{tags}}; + "" + . "" + . (defined $_->{hour} ? "$_->{hour}" : "") + . "$_->{link}" + . "" + . "
      ".join("", @tags)."
    " + . "\n" + } + events_of_pages($days{$day->day()}, %params); + $t .= + "" + . "" + . $event_html->{day} + . $event_html->{new} + . "" + . "" + . "
      ".join("", @events)."
    "; + $t.=''; + if ($day->day_of_week() - 1 == $last_wday) { + $t.=""; + $t.="" + 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.=""; + } + $t.=''; + my $event_html + = event_html + ( $params{focus} + , {year=>1, month=>1, month_name=>1} + , %params ); + return + "" + . "" + . "" + . "" + . "" + . "" + . join ("", map { + $_ = ""; + $first_wday->add_duration($one_day); + $_ } (1..7)) + . "" + . "" + . "$t
    " + . "$event_html->{month}" + . " $event_html->{year}" + . "
    ".$first_wday->day_name()."
    "; + } +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 "
    $calendar
    "; + } + +# 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 index ec34b72..0000000 --- a/newevent.pm +++ /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 "
    "; - } -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; diff --git a/templates/neweventday.tmpl b/templates/neweventday.tmpl index 0180140..25c77b0 100644 --- a/templates/neweventday.tmpl +++ b/templates/neweventday.tmpl @@ -1,8 +1,6 @@ [[!meta title=" - "]] -[[!meta date="--T00:00:00"]] -# Évènements du jour -[[!map - pages="page(.//*) and !*/Discussion" - show="title" +[[!events type="day" day="" + base="" + pages="page(./%d/*) and !*/Discussion" ]] diff --git a/templates/neweventmonth.tmpl b/templates/neweventmonth.tmpl index 2b8f3d4..955ecb7 100644 --- a/templates/neweventmonth.tmpl +++ b/templates/neweventmonth.tmpl @@ -1,12 +1,6 @@ [[!meta title=" - "]] -[[!meta date="--01T00:00:00"]] -[[!calendar - month="" - neweventbase="" +[[!calendar type="month" month="" + base="" pages="page(.//*/*) and !*/Discussion" - popup="no" - type="month" - year="" - week_start_day="1" ]]