From: Julien Moutinho
Date: Tue, 11 Feb 2014 03:30:54 +0000 (+0100)
Subject: add user names in polls and poll() pagespec to match against
X-Git-Url: http://git.cyclocoop.org/data/%7B%7Bpath%7D%7Dmw-config/index.%7B%7Bext%7D%7D?a=commitdiff_plain;h=d5fce9d5efc06ee69f7abce3fe64b7c144c9b6c3;p=ikiwiki%2Fpoll.git
add user names in polls and poll() pagespec to match against
---
diff --git a/poll.pm b/poll.pm
index 3bd4af2..612b49d 100644
--- a/poll.pm
+++ b/poll.pm
@@ -7,94 +7,151 @@ use IkiWiki 3.00;
use Encode;
sub import {
- hook(type => "getsetup", id => "poll", call => \&getsetup);
+ hook(type => "getsetup", id => "poll", call => \&getsetup);
hook(type => "preprocess", id => "poll", call => \&preprocess);
+ hook(type => "scan", id => "poll", call => \&scan);
hook(type => "sessioncgi", id => "poll", call => \&sessioncgi);
-}
-
-sub getsetup () {
- return
- plugin => {
- safe => 1,
- rebuild => undef,
- section => "widget",
- },
-}
+ }
my %pagenum;
+sub getsetup () {
+ return
+ plugin =>
+ { safe => 1
+ , rebuild => undef
+ , section => "widget"
+ };
+ }
+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});
+ if (defined $type and $type eq "mdwn") {
+ my %polls = ();
+ while ($content =~ m{(\\?)\[\[\Q$prefix\E(\s+id="([^"]*)")?\s+(.+?)\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'")
+ unless $poll =~ m/^(|[a-z][a-z0-9_-]*)$/;
+ my %poll = ();
+ 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
+ };
+ foreach my $user (@known_votes) {
+ my $userpage = linkpage(($config{userdir}?$config{userdir}.'/':'').$user);
+ add_link($page, $userpage);
+ }
+ }
+ error("poll id=`$poll' already exists on page=`$page'")
+ if exists $polls{$poll};
+ $polls{$poll} = \%poll;
+ }
+ $IkiWiki::pagestate{$page}{poll} = \%polls;
+ }
+ }
sub preprocess (@) {
- my %params=(open => "yes", total => "yes", percent => "yes",
- expandable => "no", @_);
-
+ my %params=
+ ( open => "yes"
+ , total => "yes"
+ , percent => "yes"
+ , expandable => "no"
+ , @_ );
+
my $open=IkiWiki::yesno($params{open});
my $showtotal=IkiWiki::yesno($params{total});
my $showpercent=IkiWiki::yesno($params{percent});
my $expandable=IkiWiki::yesno($params{expandable});
my $num=++$pagenum{$params{page}}{$params{destpage}};
-
+
my %choices;
my @choices;
my $total=0;
while (@_) {
- my $key=shift;
- my $value=shift;
-
- next unless $key =~ /^\d+/;
-
- my $num=$key;
- $key=shift;
- $value=shift;
-
- $choices{$key}=$num;
- push @choices, $key;
- $total+=$num;
- }
-
+ my $unknown_votes = shift;
+ my $known_votes = shift;
+ next
+ unless $unknown_votes =~ /^\d+$/;
+ my @users = $known_votes ? split(/\s+/, $known_votes) : ();
+ my $choice = shift;
+ shift;
+ my $tot = ($unknown_votes + @users);
+ $choices{$choice} =
+ { unknown_votes => $unknown_votes
+ , users => \@users
+ , total => $tot
+ };
+ push @choices, $choice;
+ $total += $tot;
+ }
+ use URI::Escape;
+ my $uri_page = URI::Escape::uri_escape_utf8($params{page}, '^A-Za-z0-9\-\._~/');
my $ret="";
foreach my $choice (@choices) {
if ($open && exists $config{cgiurl}) {
# use POST to avoid robots
$ret.="
\n
\n";
+ }
+ $ret.="$choice";
+ $ret.="";
+ $ret.="";
+ $ret.=$votes;
+ $ret.="
\n";
if ($open && exists $config{cgiurl}) {
$ret.="\n";
- }
- }
+ }
+ $ret.="\n";
+ }
if ($expandable && $open && exists $config{cgiurl}) {
- $ret.="\n";
+ $ret.="
";
$ret.="\n";
+ $ret.="";
+ $ret.="\n";
$ret.="\n";
$ret.="\n";
- }
-
- if ($showtotal) {
- $ret.="".gettext("Total votes:")." $total\n";
- }
- return "$ret
";
-}
-
+ }
+ return "$ret
";
+ }
sub sessioncgi ($$) {
my $cgi=shift;
my $session=shift;
@@ -102,28 +159,27 @@ sub sessioncgi ($$) {
my $choice=decode_utf8($cgi->param('choice'));
if (! defined $choice || not length $choice) {
error("no choice specified");
- }
+ }
my $num=$cgi->param('num');
if (! defined $num) {
error("no num specified");
- }
- my $page=IkiWiki::possibly_foolish_untaint($cgi->param('page'));
+ }
+ my $page=Encode::decode_utf8(URI::Escape::uri_unescape(IkiWiki::possibly_foolish_untaint($cgi->param('page'))));
if (! defined $page || ! exists $pagesources{$page}) {
+ use Data::Dumper;
error("bad page name");
- }
-
+ }
+
# Did they vote before? If so, let them change their vote,
# and check for dups.
my $choice_param="poll_choice_${page}_$num";
my $oldchoice=$session->param($choice_param);
- if (defined $oldchoice && $oldchoice eq $choice) {
- # Same vote; no-op.
- IkiWiki::redirect($cgi, urlto($page));
- exit;
- }
-
+ #if (defined $oldchoice && $oldchoice eq $choice) {
+ # # Same vote; no-op.
+ # IkiWiki::redirect($cgi, urlto($page));
+ # exit;
+ # }
my $prefix=$config{prefix_directives} ? "!poll" : "poll";
-
my $content=readfile(srcfile($pagesources{$page}));
# Now parse the content, find the right poll,
# and find the choice within it, and increment its number.
@@ -131,53 +187,132 @@ sub sessioncgi ($$) {
my $edit=sub {
my $escape=shift;
my $params=shift;
- return "\\[[$prefix $params]]" if $escape;
+ return $params
+ if $escape;
if (--$num == 0) {
- if ($params=~s/(^|\s+)(\d+)\s+"?\Q$choice\E"?(\s+|$)/$1.($2+1)." \"$choice\"".$3/se) {
- }
+ my $vote = sub {
+ my ($action, $unknown_votes, $known_votes) = @_;
+ my $user = $session->param("name");
+ my %users;
+ foreach (split(/\s+/, $known_votes)) {
+ $users{$_} = 1;
+ }
+ if ($action eq 'add') {
+ if (defined $user) {
+ if (exists $users{$user} or (defined $oldchoice and $oldchoice eq $choice)) {
+ delete $users{$user};
+ $known_votes = join(' ', sort {lc $a <=> lc $b} (keys %users));
+ }
+ else {
+ $known_votes = join(' ', sort {lc $a <=> lc $b} ($user, keys %users));
+ }
+ }
+ else {
+ $unknown_votes += 1;
+ }
+ }
+ elsif ($action eq 'del') {
+ if (defined $user) {
+ if (exists $users{$user}) {
+ delete $users{$user};
+ $known_votes = join(' ', sort {lc $a <=> lc $b} (keys %users));
+ }
+ }
+ else {
+ $unknown_votes = ($unknown_votes-1 >=0 ? $unknown_votes-1 : 0);
+ }
+ }
+ return $unknown_votes.($known_votes?"=\"$known_votes\"":"")
+ };
+ if ($params=~s/(^|\s+)(\d+)(="([^"]*)")?(\s+)"?\Q$choice\E"?(\s+|$)/$1.$vote->('add', $2, $4)."$5\"$choice\"".$6/es) {
+ }
elsif ($params=~/expandable=(\w+)/
& &IkiWiki::yesno($1)) {
$choice=~s/["\]\n\r]//g;
$params.=" 1 \"$choice\""
if length $choice;
- }
- if (defined $oldchoice) {
- $params=~s/(^|\s+)(\d+)\s+"?\Q$oldchoice\E"?(\s+|$)/$1.($2-1 >=0 ? $2-1 : 0)." \"$oldchoice\"".$3/se;
- }
- }
- return "[[$prefix $params]]";
- };
- $content =~ s{(\\?)\[\[\Q$prefix\E\s+([^]]+)\s*\]\]}{$edit->($1, $2)}seg;
-
+ }
+ if (defined $oldchoice and not ($oldchoice eq $choice)) {
+ $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;
+
# Store their vote, update the page, and redirect to it.
writefile($pagesources{$page}, $config{srcdir}, $content);
- $session->param($choice_param, $choice);
+ if (defined $oldchoice and $choice eq $oldchoice) {
+ $session->param($choice_param, undef);
+ # TOTRY: $session->clear($choice_param);
+ }
+ else {
+ $session->param($choice_param, $choice);
+ }
IkiWiki::cgi_savesession($session);
- $oldchoice=$session->param($choice_param);
if ($config{rcs}) {
IkiWiki::disable_commit_hook();
- IkiWiki::rcs_commit(
- file => $pagesources{$page},
- message => "poll vote ($choice)",
- token => IkiWiki::rcs_prepedit($pagesources{$page}),
- session => $session,
- );
+ IkiWiki::rcs_commit
+ ( file => $pagesources{$page}
+ , message => "poll vote: id=$id: $choice"
+ , session => $session
+ , token => IkiWiki::rcs_prepedit($pagesources{$page})
+ );
IkiWiki::enable_commit_hook();
IkiWiki::rcs_update();
- }
+ }
require IkiWiki::Render;
IkiWiki::refresh();
IkiWiki::saveindex();
-
- # Need to set cookie in same http response that does the
- # redir.
+ # Need to set cookie in same http response that does the redir.
eval q{use CGI::Cookie};
error($@) if $@;
- my $cookie = CGI::Cookie->new(-name=> $session->name, -value=> $session->id);
- print $cgi->redirect(-cookie => $cookie,
- -url => urlto($page));
+ my $cookie = CGI::Cookie->new
+ ( -name => $session->name
+ , -value => $session->id );
+ print $cgi->redirect
+ ( -cookie => $cookie
+ , -url => urlto($page) );
exit;
- }
-}
+ }
+ }
+package IkiWiki::PageSpec;
+ sub match_poll ($$;@) {
+ 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_user_re = IkiWiki::glob2re($match_user?$match_user:'*');
+ my $match_choice_re = IkiWiki::glob2re($match_choice?$match_choice:'*');
+ while (my ($choice, $data) = each %poll) {
+ next unless $choice =~ $match_choice_re;
+ if ($match_user eq '') {
+ if ($data->{unknown_votes} > 0) {
+ return IkiWiki::SuccessReason->new("unkown user has voted for choice=`$choice'", $page => $IkiWiki::DEPEND_CONTENT);
+ }
+ else {
+ return IkiWiki::FailReason->new("no unkown user has voted for choice=`$choice'", $page => $IkiWiki::DEPEND_CONTENT);
+ }
+ }
+ else {
+ foreach my $user (@{$data->{known_votes}}) {
+ next unless $user =~ $match_user_re;
+ return IkiWiki::SuccessReason->new("user=`$user' has voted for choice=`$choice'", $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 id=`$match_poll'", $page => $IkiWiki::DEPEND_CONTENT);
+ }
+ }
+ else {
+ return IkiWiki::FailReason->new("no poll", $page => $IkiWiki::DEPEND_CONTENT);
+ }
+ }
-1
+1;