X-Git-Url: http://git.cyclocoop.org/?p=ikiwiki%2Fpoll.git;a=blobdiff_plain;f=poll.pm;h=4a7b79b3f1565c381da74ed6d3dd791d37f3867a;hp=612b49d1fb2bc4505cf349207517fe7dea494851;hb=HEAD;hpb=d5fce9d5efc06ee69f7abce3fe64b7c144c9b6c3 diff --git a/poll.pm b/poll.pm index 612b49d..4a7b79b 100644 --- a/poll.pm +++ b/poll.pm @@ -22,38 +22,56 @@ sub getsetup () { , section => "widget" }; } +my $params_re + = qr{ + (?> + (?>(?:[^\[\]]|\[[^\[]|\][^\]])+) + | + (?'loop' + \[\[ + (?> + (?>(?:[^\[\]]|\[[^\[]|\][^\]])+) + | + (?&loop) + )* + \]\] + ) + )* + }x; sub scan (@) { my %params = @_; - my $page = $params{page}; my $content = $params{content}; my $prefix = $config{prefix_directives} ? "!poll" : "poll"; - my $type = IkiWiki::pagetype($pagesources{$page}); + my $type = IkiWiki::pagetype($pagesources{$params{page}}); if (defined $type and $type eq "mdwn") { my %polls = (); - while ($content =~ m{(\\?)\[\[\Q$prefix\E(\s+id="([^"]*)")?\s+(.+?)\s*\]\]}gs) { + while ($content =~ m{(\\?)\[\[\Q$prefix\E(\s+id="([^"]*)")?\s+($params_re)\s*\]\]}gs) { my ($escape, $poll, $directive) = ($1, $3, $4); next if $escape; $poll = '' unless defined $poll; - error("poll id=`$poll' must match (|[a-z][a-z0-9_-]*) on page=`$page'") + error("poll id=`$poll' must match (|[a-z][a-z0-9_-]*) on page=`$params{page}'") unless $poll =~ m/^(|[a-z][a-z0-9_-]*)$/; my %poll = (); + my $userdir = defined $config{userdir} ? $config{userdir}.'/' : ''; while ($directive =~ m/(^|\s+)(\d+)(="([^"]*)")?\s+"?([^"]*)"?/gs) { my ($unknown_votes, $known_votes, $choice) = ($2, $4, $5); - my @known_votes = defined $known_votes ? split(/\s+/, $known_votes) : (); - $poll{$choice} = - { unknown_votes => $unknown_votes - , known_votes => \@known_votes - }; + my @known_votes = defined $known_votes ? grep {length $_ > 0} (split(/\s+/, $known_votes)) : (); + $poll{$choice} = + { unknown_votes => $unknown_votes + , known_votes => \@known_votes + }; foreach my $user (@known_votes) { - my $userpage = linkpage(($config{userdir}?$config{userdir}.'/':'').$user); - add_link($page, $userpage); + my $userpage_best = bestlink($params{page}, $user); + $userpage_best = (length $userpage_best > 0 ? $userpage_best : bestlink($userdir, $user)); + $userpage_best = (length $userpage_best > 0 ? $userpage_best : ($user =~ m{/} ? '/' : $userdir).$user); + add_link($params{page}, $userpage_best); } } - error("poll id=`$poll' already exists on page=`$page'") + error("poll id=`$poll' already exists on page=`$params{page}'") if exists $polls{$poll}; $polls{$poll} = \%poll; } - $IkiWiki::pagestate{$page}{poll} = \%polls; + $IkiWiki::pagestate{$params{page}}{poll} = \%polls; } } sub preprocess (@) { @@ -78,7 +96,7 @@ sub preprocess (@) { my $known_votes = shift; next unless $unknown_votes =~ /^\d+$/; - my @users = $known_votes ? split(/\s+/, $known_votes) : (); + my @users = $known_votes ? grep {length $_ > 0} (split(/\s+/, $known_votes)) : (); my $choice = shift; shift; my $tot = ($unknown_votes + @users); @@ -92,6 +110,7 @@ sub preprocess (@) { } use URI::Escape; my $uri_page = URI::Escape::uri_escape_utf8($params{page}, '^A-Za-z0-9\-\._~/'); + my $userdir = defined $config{userdir} ? $config{userdir}.'/' : ''; my $ret=""; foreach my $choice (@choices) { if ($open && exists $config{cgiurl}) { @@ -107,8 +126,17 @@ sub preprocess (@) { if $showpercent; if (@{$choices{$choice}{users}} > 0) { $votes .= " : ".join(', ', map { - my $userpage = linkpage(($config{userdir}?$config{userdir}.'/':'').$_); - htmllink($params{page}, $params{destpage}, '/'.$userpage, linktext => pagetitle($_)) + my $user = $_; + #my $userpage = linkpage(($config{userdir}?$config{userdir}.'/':'').$_); + #htmllink($params{page}, $params{destpage}, $userpage, linktext => pagetitle($_)) + my $userpage_best = bestlink($userdir, $user); + $userpage_best = (length $userpage_best > 0 ? $userpage_best : bestlink($userdir, $user)); + $userpage_best = (length $userpage_best > 0 ? $userpage_best : ($user =~ m{/} ? '/' : $userdir).$user); + htmllink + ( $params{page} + , $params{destpage} + , $userpage_best + , noimageinline => 1 ) } @{$choices{$choice}{users}}); $votes .= " + ".$choices{$choice}{unknown_votes}." " . ($choices{$choice}{unknown_votes} > 1 ? gettext("unknowns") : gettext("unknown")) @@ -119,10 +147,11 @@ sub preprocess (@) { if $choices{$choice}{unknown_votes}; } if ($open && exists $config{cgiurl}) { + my $choice_escaped = URI::Escape::uri_escape_utf8($choice, '^A-Za-z0-9\ \-\._~/'); $ret.="\n"; $ret.="\n"; $ret.="\n"; - $ret.="\n"; + $ret.="\n"; $ret.="\n"; } $ret.="$choice"; @@ -156,7 +185,8 @@ sub sessioncgi ($$) { my $cgi=shift; my $session=shift; if (defined $cgi->param('do') && $cgi->param('do') eq "poll") { - my $choice=decode_utf8($cgi->param('choice')); + my $choice = Encode::decode_utf8(URI::Escape::uri_unescape(IkiWiki::possibly_foolish_untaint($cgi->param('choice')))); + if (! defined $choice || not length $choice) { error("no choice specified"); } @@ -169,6 +199,7 @@ sub sessioncgi ($$) { use Data::Dumper; error("bad page name"); } + &IkiWiki::check_canedit($page, $cgi, $session); # Did they vote before? If so, let them change their vote, # and check for dups. @@ -195,7 +226,8 @@ sub sessioncgi ($$) { my $user = $session->param("name"); my %users; foreach (split(/\s+/, $known_votes)) { - $users{$_} = 1; + $users{$_} = 1 + if length $_ > 0; } if ($action eq 'add') { if (defined $user) { @@ -232,14 +264,33 @@ sub sessioncgi ($$) { $params.=" 1 \"$choice\"" if length $choice; } - if (defined $oldchoice and not ($oldchoice eq $choice)) { + if (defined $oldchoice and not ($oldchoice eq $choice) + and not ($params=~/multiple=(\w+)/ & &IkiWiki::yesno($1))) { $params=~s/(^|\s+)(\d+)(="([^"]*)")?(\s+)"?\Q$oldchoice\E"?(\s+|$)/$1.$vote->('del', $2, $4)."$5\"$oldchoice\"".$6/es; } } return "$params"; }; my $id=''; - $content =~ s{(\\?)\[\[\Q$prefix\E(\s+id="([^"]*)")?(\s+)(.+?)(\s*)\]\]}{$id=$3;$1.'[['.$prefix.$2.$4.$edit->($1, $5).$6.']]'}gse; + $content =~ + s{ + (?\\?) + \[\[\Q$prefix\E + (?:(?\s+)id="(?[^"]*)")? + (?\s+) + (?$params_re) + (?\s*) + \]\] + } + {$id=$+{id}; + $+{escape} + .'[['.$prefix + .($+{id} eq ''?'':$+{id_space}.'id="'.$+{id}.'"') + .$+{params_space} + .$edit->($+{escape}, $+{params}) + .$+{end_space} + .']]' + }egsx; # Store their vote, update the page, and redirect to it. writefile($pagesources{$page}, $config{srcdir}, $content); @@ -282,9 +333,13 @@ package IkiWiki::PageSpec; my ($page, $match, %params) = @_; my $polls = $IkiWiki::pagestate{$page}{poll}; if (defined $polls and %$polls) { - my ($match_poll, $match_user, $match_choice) = $match =~ m/^id=(.*?) user=(.*?) choice=(.*?)$/; - if (exists $polls->{$match_poll}) { - my %poll = %{$polls->{$match_poll}}; + my ($match_id, $match_user, $match_choice) = $match =~ m/^id=(.*?) user=(.*?) choice=(.*?)$/; + my $match_id_re = IkiWiki::glob2re($match_id?$match_id:'*'); + my @polls = grep {$_ =~ $match_id_re} (keys %$polls); + return IkiWiki::FailReason->new("no poll match id=`$match_id'", $page => $IkiWiki::DEPEND_CONTENT) + unless @polls > 0; + foreach my $poll (@polls) { + my %poll = %{$polls->{$poll}}; my $match_user_re = IkiWiki::glob2re($match_user?$match_user:'*'); my $match_choice_re = IkiWiki::glob2re($match_choice?$match_choice:'*'); while (my ($choice, $data) = each %poll) { @@ -304,11 +359,8 @@ package IkiWiki::PageSpec; } } } - return IkiWiki::FailReason->new("no user=`$match_user' has voted for choice=`$match_choice'", $page => $IkiWiki::DEPEND_CONTENT); - } - else { - return IkiWiki::FailReason->new("no poll id=`$match_poll'", $page => $IkiWiki::DEPEND_CONTENT); } + return IkiWiki::FailReason->new("no user=`$match_user' has voted for choice=`$match_choice'", $page => $IkiWiki::DEPEND_CONTENT); } else { return IkiWiki::FailReason->new("no poll", $page => $IkiWiki::DEPEND_CONTENT);