From 5800d2160c53113e1d811ce2234608760a3db243 Mon Sep 17 00:00:00 2001
From: joey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>
Date: Sun, 26 Nov 2006 19:46:11 +0000
Subject: [PATCH] add a poll plugin

---
 IkiWiki/Plugin/poll.pm | 160 +++++++++++++++++++++++++++++++++++++++++
 basewiki/style.css     |  13 ++++
 debian/changelog       |   4 +-
 doc/plugins/poll.mdwn  |  24 +++++++
 4 files changed, 200 insertions(+), 1 deletion(-)
 create mode 100644 IkiWiki/Plugin/poll.pm
 create mode 100644 doc/plugins/poll.mdwn

diff --git a/IkiWiki/Plugin/poll.pm b/IkiWiki/Plugin/poll.pm
new file mode 100644
index 000000000..b862ccdaa
--- /dev/null
+++ b/IkiWiki/Plugin/poll.pm
@@ -0,0 +1,160 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::poll;
+
+use warnings;
+use strict;
+use IkiWiki;
+use URI;
+
+sub import { #{{{
+	hook(type => "preprocess", id => "poll", call => \&preprocess);
+	hook(type => "cgi", id => "poll", call => \&cgi);
+} # }}}
+
+sub yesno ($) { #{{{
+	my $val=shift;
+	return (defined $val && lc($val) eq "yes");
+} #}}}
+
+my %pagenum;
+sub preprocess (@) { #{{{
+	my %params=(open => "yes", total => "yes", percent => "yes", @_);
+
+	my $open=yesno($params{open});
+	my $showtotal=yesno($params{total});
+	my $percent=yesno($params{percent});
+	$pagenum{$params{page}}++;
+
+	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 $ret="";
+	foreach my $choice (@choices) {
+		my $percent=int($choices{$choice} / $total * 100);
+		if ($percent) {
+			$ret.="$choice ($percent%) ";
+		}
+		else {
+			$ret.="$choice ($choices{$choice}) ";
+		}
+		if ($open && exists $config{cgiurl}) {
+			my $url=URI->new($config{cgiurl});
+			$url->query_form(
+				"do" => "poll",
+				"num" => $pagenum{$params{page}}, 
+				"page" => $params{page}, 
+				"choice" => $choice,
+			);
+			$ret.="<a class=pollbutton href=\"$url\">vote</a>";
+		}
+		$ret.="<br />\n<hr class=poll align=left width=\"$percent%\"/>\n";
+	}
+	if ($showtotal) {
+		$ret.="<span>Total votes: $total</span>\n";
+	}
+	return "<div class=poll>$ret</div>";
+} # }}}
+
+sub cgi ($) { #{{{
+	my $cgi=shift;
+	if (defined $cgi->param('do') && $cgi->param('do') eq "poll") {
+		my $choice=$cgi->param('choice');
+		if (! defined $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'));
+		if (! defined $page || ! exists $pagesources{$page}) {
+			error("bad page name");
+		}
+
+		# Did they vote before? If so, let them change their vote,
+		# and check for dups.
+		my $session=IkiWiki::cgi_getsession();
+		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, "$config{url}/".htmlpage($page));
+		}
+
+		my $content=readfile(srcfile($pagesources{$page}));
+		# Now parse the content, find the right poll,
+		# and find the choice within it, and increment its number.
+		# If they voted before, decrement that one.
+		my $edit=sub {
+			my $escape=shift;
+			my $params=shift;
+			return "\\[[poll $params]]" if $escape;
+			return $params unless --$num == 0;
+			my @bits=split(' ', $params);
+			my @ret;
+			while (@bits) {
+				my $n=shift @bits;
+				if ($n=~/=/) {
+					# val=param setting
+					push @ret, $n;
+					next;
+				}
+				my $c=shift @bits;
+				$c=~s/^"(.*)"/$1/g;
+				next unless defined $n && defined $c;
+				if ($c eq $choice) {
+					$n++;
+				}
+				if (defined $oldchoice && $c eq $oldchoice) {
+					$n--;
+				}
+				push @ret, $n, "\"$c\"";
+			}
+			return "[[poll ".join(" ", @ret)."]]";
+		};
+		$content =~ s{(\\?)\[\[poll\s+([^]]+)\s*\]\]}{$edit->($1, $2)}seg;
+
+		# Store their vote, update the page, and redirect to it.
+		writefile($pagesources{$page}, $config{srcdir}, $content);
+		$session->param($choice_param, $choice);
+		IkiWiki::cgi_savesession($session);
+		$oldchoice=$session->param($choice_param);
+		if ($config{rcs}) {
+			# prevent deadlock with post-commit hook
+			IkiWiki::unlockwiki();
+			IkiWiki::rcs_commit($pagesources{$page}, "poll vote",
+				IkiWiki::rcs_prepedit($pagesources{$page}),
+				$session->param("name"), $ENV{REMOTE_ADDR});
+		}
+		else {
+			require IkiWiki::Render;
+			IkiWiki::refresh();
+			IkiWiki::saveindex();
+		}
+		# 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 => "$config{url}/".htmlpage($page));
+		exit;
+	}
+} #}}}
+
+1
diff --git a/basewiki/style.css b/basewiki/style.css
index 032665e70..1017e957f 100644
--- a/basewiki/style.css
+++ b/basewiki/style.css
@@ -206,6 +206,19 @@ li.L8 {
 	list-style: upper-alpha;
 }
 
+hr.poll {
+	height: 10pt;
+	color: white !important;
+	background: #eee;
+	border: 2px solid black;
+}
+div.poll {
+	margin-top: 1ex;
+	margin-bottom: 1ex;
+	padding: 1ex 1ex;
+	border: 1px solid #aaa;
+}
+
 input#openid_url {
 	background: url(http://openid.net/login-bg.gif) no-repeat;
 	background-color: #fff;
diff --git a/debian/changelog b/debian/changelog
index 900e62de4..345878cfc 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -44,8 +44,10 @@ ikiwiki (1.34) UNRELEASED; urgency=low
     and their IP address, and needs to construct its own commit message
     containing them, or do something more appropriate for the given RCS.
   * Add softwaresite example.
+  * Add a poll plugin.
+  * Add quick mode for archive page generation.
 
- -- Joey Hess <joeyh@debian.org>  Wed, 22 Nov 2006 09:59:12 -0500
+ -- Joey Hess <joeyh@debian.org>  Thu, 23 Nov 2006 17:13:32 -0500
 
 ikiwiki (1.33) unstable; urgency=low
 
diff --git a/doc/plugins/poll.mdwn b/doc/plugins/poll.mdwn
new file mode 100644
index 000000000..385a78b42
--- /dev/null
+++ b/doc/plugins/poll.mdwn
@@ -0,0 +1,24 @@
+[[template id=plugin name=poll included=1 author="[[Joey]]"]]
+[[tag type/useful]]
+
+This plugin allows you to create online polls in the wiki. Here's an
+example use:
+
+	\[[poll 0 "red" 0 "green" 0 "blue"]]
+
+The numbers indicate how many users voted for that choice. When a user
+votes for a choice in the poll, the page is modified and the number
+incremented.
+
+While some basic precautions are taken to prevent users from accidentially
+voting twice, this sort of poll is should not be counted on the be very
+accurate; all the usual concerns about web based polling apply. Unless the
+page that the poll is in is locked, users can even edit the page and change
+the numbers!
+
+Parameters: 
+
+* `open` - Whether voting is still open. Set to "no" to close the poll to
+  voting.
+* `total` - Show total number of votes at bottom of poll. Default is "yes".
+* `percent` - Whether to display percents. Default is "yes".
-- 
2.39.5