summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/poll.pm
blob: e92d0292f317d90eee975bcdb6d5aaf995632934 (plain)
  1. #!/usr/bin/perl
  2. package IkiWiki::Plugin::poll;
  3. use warnings;
  4. use strict;
  5. use IkiWiki;
  6. use URI;
  7. sub import { #{{{
  8. hook(type => "preprocess", id => "poll", call => \&preprocess);
  9. hook(type => "cgi", id => "poll", call => \&cgi);
  10. } # }}}
  11. sub yesno ($) { #{{{
  12. my $val=shift;
  13. return (defined $val && lc($val) eq "yes");
  14. } #}}}
  15. my %pagenum;
  16. sub preprocess (@) { #{{{
  17. my %params=(open => "yes", total => "yes", percent => "yes", @_);
  18. my $open=yesno($params{open});
  19. my $showtotal=yesno($params{total});
  20. my $percent=yesno($params{percent});
  21. $pagenum{$params{page}}++;
  22. my %choices;
  23. my @choices;
  24. my $total=0;
  25. while (@_) {
  26. my $key=shift;
  27. my $value=shift;
  28. next unless $key =~ /^\d+/;
  29. my $num=$key;
  30. $key=shift;
  31. $value=shift;
  32. $choices{$key}=$num;
  33. push @choices, $key;
  34. $total+=$num;
  35. }
  36. my $ret="";
  37. foreach my $choice (@choices) {
  38. my $percent=$total > 0 ? int($choices{$choice} / $total * 100) : 0;
  39. if ($percent) {
  40. $ret.="$choice ($percent%) ";
  41. }
  42. else {
  43. $ret.="$choice ($choices{$choice}) ";
  44. }
  45. if ($open && exists $config{cgiurl}) {
  46. my $url=URI->new($config{cgiurl});
  47. $url->query_form(
  48. "do" => "poll",
  49. "num" => $pagenum{$params{page}},
  50. "page" => $params{page},
  51. "choice" => $choice,
  52. );
  53. $ret.="<a class=pollbutton href=\"$url\">vote</a>";
  54. }
  55. $ret.="<br />\n<hr class=poll align=left width=\"$percent%\"/>\n";
  56. }
  57. if ($showtotal) {
  58. $ret.="<span>Total votes: $total</span>\n";
  59. }
  60. return "<div class=poll>$ret</div>";
  61. } # }}}
  62. sub cgi ($) { #{{{
  63. my $cgi=shift;
  64. if (defined $cgi->param('do') && $cgi->param('do') eq "poll") {
  65. my $choice=$cgi->param('choice');
  66. if (! defined $choice) {
  67. error("no choice specified");
  68. }
  69. my $num=$cgi->param('num');
  70. if (! defined $num) {
  71. error("no num specified");
  72. }
  73. my $page=IkiWiki::possibly_foolish_untaint($cgi->param('page'));
  74. if (! defined $page || ! exists $pagesources{$page}) {
  75. error("bad page name");
  76. }
  77. # Did they vote before? If so, let them change their vote,
  78. # and check for dups.
  79. my $session=IkiWiki::cgi_getsession();
  80. my $choice_param="poll_choice_${page}_$num";
  81. my $oldchoice=$session->param($choice_param);
  82. if (defined $oldchoice && $oldchoice eq $choice) {
  83. # Same vote; no-op.
  84. IkiWiki::redirect($cgi, "$config{url}/".htmlpage($page));
  85. }
  86. my $content=readfile(srcfile($pagesources{$page}));
  87. # Now parse the content, find the right poll,
  88. # and find the choice within it, and increment its number.
  89. # If they voted before, decrement that one.
  90. my $edit=sub {
  91. my $escape=shift;
  92. my $params=shift;
  93. return "\\[[poll $params]]" if $escape;
  94. return $params unless --$num == 0;
  95. my @bits=split(' ', $params);
  96. my @ret;
  97. while (@bits) {
  98. my $n=shift @bits;
  99. if ($n=~/=/) {
  100. # val=param setting
  101. push @ret, $n;
  102. next;
  103. }
  104. my $c=shift @bits;
  105. $c=~s/^"(.*)"/$1/g;
  106. next unless defined $n && defined $c;
  107. if ($c eq $choice) {
  108. $n++;
  109. }
  110. if (defined $oldchoice && $c eq $oldchoice) {
  111. $n--;
  112. }
  113. push @ret, $n, "\"$c\"";
  114. }
  115. return "[[poll ".join(" ", @ret)."]]";
  116. };
  117. $content =~ s{(\\?)\[\[poll\s+([^]]+)\s*\]\]}{$edit->($1, $2)}seg;
  118. # Store their vote, update the page, and redirect to it.
  119. writefile($pagesources{$page}, $config{srcdir}, $content);
  120. $session->param($choice_param, $choice);
  121. IkiWiki::cgi_savesession($session);
  122. $oldchoice=$session->param($choice_param);
  123. if ($config{rcs}) {
  124. # prevent deadlock with post-commit hook
  125. IkiWiki::unlockwiki();
  126. IkiWiki::rcs_commit($pagesources{$page}, "poll vote",
  127. IkiWiki::rcs_prepedit($pagesources{$page}),
  128. $session->param("name"), $ENV{REMOTE_ADDR});
  129. }
  130. else {
  131. require IkiWiki::Render;
  132. IkiWiki::refresh();
  133. IkiWiki::saveindex();
  134. }
  135. # Need to set cookie in same http response that does the
  136. # redir.
  137. eval q{use CGI::Cookie};
  138. error($@) if $@;
  139. my $cookie = CGI::Cookie->new(-name=> $session->name, -value=> $session->id);
  140. print $cgi->redirect(-cookie => $cookie,
  141. -url => "$config{url}/".htmlpage($page));
  142. exit;
  143. }
  144. } #}}}
  145. 1