Ajout : supporte id= comme expression rationnelle.
[ikiwiki/poll.git] / poll.pm
diff --git a/poll.pm b/poll.pm
index 612b49d..411f3bd 100644 (file)
--- a/poll.pm
+++ b/poll.pm
@@ -22,6 +22,22 @@ sub getsetup () {
                 , section => "widget"
                 };
  }
+my $params_re
+ = qr{
+       (?>
+               (?>(?:[^\[\]]|\[[^\[]|\][^\]])+)
+       |
+               (?'loop'
+                       \[\[
+                       (?>
+                               (?>(?:[^\[\]]|\[[^\[]|\][^\]])+)
+                       |
+                               (?&loop)
+                       )*
+                       \]\]
+               )
+       )*
+ }x;
 sub scan (@) {
        my %params = @_;
        my $page = $params{page};
@@ -30,7 +46,7 @@ sub scan (@) {
        my $type = IkiWiki::pagetype($pagesources{$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;
@@ -119,10 +135,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.="<input type=\"hidden\" name=\"do\" value=\"poll\" />\n";
                        $ret.="<input type=\"hidden\" name=\"num\" value=\"$num\" />\n";
                        $ret.="<input type=\"hidden\" name=\"page\" value=\"$uri_page\" />\n";
-                       $ret.="<input type=\"hidden\" name=\"choice\" value=\"$choice\" />\n";
+                       $ret.="<input type=\"hidden\" name=\"choice\" value=\"$choice_escaped\" />\n";
                        $ret.="<input type=\"submit\" value=\"".gettext("vote")."\" />\n";
                 }
                $ret.="<span class='description'>$choice</span>";
@@ -156,7 +173,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 +187,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.
@@ -239,7 +258,25 @@ sub sessioncgi ($$) {
                        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{
+                       (?<escape>\\?)
+                       \[\[\Q$prefix\E
+                               (?:(?<id_space>\s+)id="(?<id>[^"]*)")?
+                               (?<params_space>\s+)
+                               (?<params>$params_re)
+                               (?<end_space>\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 +319,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 +345,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);