summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/poll.pm
blob: 320d2fa775e6660736608b999a202d3c664e5cbb (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 $showpercent=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 ($showpercent) {
  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. exit;
  86. }
  87. my $content=readfile(srcfile($pagesources{$page}));
  88. # Now parse the content, find the right poll,
  89. # and find the choice within it, and increment its number.
  90. # If they voted before, decrement that one.
  91. my $edit=sub {
  92. my $escape=shift;
  93. my $params=shift;
  94. return "\\[[poll $params]]" if $escape;
  95. if (--$num == 0) {
  96. $params=~s/(^|\s+)(\d+)\s+"?\Q$choice\E"?(\s+|$)/$1.($2+1)." \"$choice\"".$3/se;
  97. if (defined $oldchoice) {
  98. $params=~s/(^|\s+)(\d+)\s+"?\Q$oldchoice\E"?(\s+|$)/$1.($2-1 >=0 ? $2-1 : 0)." \"$oldchoice\"".$3/se;
  99. }
  100. }
  101. return "[[poll $params]]";
  102. };
  103. $content =~ s{(\\?)\[\[poll\s+([^]]+)\s*\]\]}{$edit->($1, $2)}seg;
  104. # Store their vote, update the page, and redirect to it.
  105. writefile($pagesources{$page}, $config{srcdir}, $content);
  106. $session->param($choice_param, $choice);
  107. IkiWiki::cgi_savesession($session);
  108. $oldchoice=$session->param($choice_param);
  109. if ($config{rcs}) {
  110. # prevent deadlock with post-commit hook
  111. IkiWiki::unlockwiki();
  112. IkiWiki::rcs_commit($pagesources{$page}, "poll vote",
  113. IkiWiki::rcs_prepedit($pagesources{$page}),
  114. $session->param("name"), $ENV{REMOTE_ADDR});
  115. }
  116. else {
  117. require IkiWiki::Render;
  118. IkiWiki::refresh();
  119. IkiWiki::saveindex();
  120. }
  121. # Need to set cookie in same http response that does the
  122. # redir.
  123. eval q{use CGI::Cookie};
  124. error($@) if $@;
  125. my $cookie = CGI::Cookie->new(-name=> $session->name, -value=> $session->id);
  126. print $cgi->redirect(-cookie => $cookie,
  127. -url => "$config{url}/".htmlpage($page));
  128. exit;
  129. }
  130. } #}}}
  131. 1