From: Joey Hess <joey@gnu.kitenet.net>
Date: Sun, 25 Jan 2009 23:49:57 +0000 (-0500)
Subject: comments: Add a moderation web interface.
X-Git-Tag: 3.03~35
X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/commitdiff_plain/731fc9e7a2ce818f0b7069cf0353931ec2dc8b43?ds=inline;hp=--cc

comments: Add a moderation web interface.
---

731fc9e7a2ce818f0b7069cf0353931ec2dc8b43
diff --git a/IkiWiki/Plugin/comments.pm b/IkiWiki/Plugin/comments.pm
index 4d225b90a..388a983f7 100644
--- a/IkiWiki/Plugin/comments.pm
+++ b/IkiWiki/Plugin/comments.pm
@@ -134,8 +134,8 @@ sub preprocess {
 	}
 
 	# no need to bother with htmlize if it's just HTML
-	$content = IkiWiki::htmlize($page, $params{destpage}, $format,
-		$content) if defined $format;
+	$content = IkiWiki::htmlize($page, $params{destpage}, $format, $content)
+		if defined $format;
 
 	IkiWiki::run_hooks(sanitize => sub {
 		$content = shift->(
@@ -263,13 +263,23 @@ sub linkcgi ($) {
 	}
 }
 
-# Mostly cargo-culted from IkiWiki::plugin::editpage
 sub sessioncgi ($$) {
 	my $cgi=shift;
 	my $session=shift;
 
 	my $do = $cgi->param('do');
-	return unless $do eq 'comment';
+	if ($do eq 'comment') {
+		editcomment($cgi, $session);
+	}
+	elsif ($do eq 'commentmoderation') {
+		commentmoderation($cgi, $session);
+	}
+}
+
+# Mostly cargo-culted from IkiWiki::plugin::editpage
+sub editcomment ($$) {
+	my $cgi=shift;
+	my $session=shift;
 
 	IkiWiki::decode_cgi_utf8($cgi);
 
@@ -431,29 +441,8 @@ sub sessioncgi ($$) {
 	# - this means that if they do, rocks fall and everyone dies
 
 	if ($form->submitted eq PREVIEW) {
-		my $preview = IkiWiki::htmlize($location, $page, '_comment',
-				IkiWiki::linkify($location, $page,
-					IkiWiki::preprocess($location, $page,
-						IkiWiki::filter($location,
-							$page, $content),
-						0, 1)));
-		IkiWiki::run_hooks(format => sub {
-				$preview = shift->(page => $page,
-					content => $preview);
-			});
-
-		my $template = template("comment.tmpl");
-		$template->param(content => $preview);
-		$template->param(title => $form->field('subject'));
-		$template->param(ctime => displaytime(time));
-
-		IkiWiki::run_hooks(pagetemplate => sub {
-			shift->(page => $location,
-				destpage => $page,
-				template => $template);
-		});
-
-		$form->tmpl_param(page_preview => $template->output);
+		$form->tmpl_param(page_preview => 
+			previewcomment($content, $location, $page, time));
 	}
 	else {
 		$form->tmpl_param(page_preview => "");
@@ -480,6 +469,7 @@ sub sessioncgi ($$) {
 			my $penddir=$config{wikistatedir}."/comments_pending";
 			$location=unique_comment_location($page, $penddir);
 			writefile("$location._comment", $penddir, $content);
+			IkiWiki::printheader($session);
 			print IkiWiki::misctemplate(gettext(gettext("comment stored for moderation")),
 				"<p>".
 				gettext("Your comment will be posted after moderator review"),
@@ -534,6 +524,158 @@ sub sessioncgi ($$) {
 	exit;
 }
 
+sub commentmoderation ($$) {
+	my $cgi=shift;
+	my $session=shift;
+
+	IkiWiki::needsignin($cgi, $session);
+	if (! IkiWiki::is_admin($session->param("name"))) {
+		error(gettext("you are not logged in as an admin"));
+	}
+
+	IkiWiki::decode_cgi_utf8($cgi);
+	
+	if (defined $cgi->param('sid')) {
+		IkiWiki::checksessionexpiry($cgi, $session);
+
+		my %vars=$cgi->Vars;
+		my $added=0;
+		foreach my $id (keys %vars) {
+			if ($id =~ /(.*)\Q._comment\E$/) {
+				my $action=$cgi->param($id);
+				next if $action eq 'Defer';
+
+				# Make sure that the id is of a legal
+				# pending comment before untainting.
+				my ($f)= $id =~ /$config{wiki_file_regexp}/;
+				if (! defined $f || ! length $f ||
+				    IkiWiki::file_pruned($f, $config{srcdir})) {
+					error("illegal file");
+				}
+
+				my $page=IkiWiki::possibly_foolish_untaint(IkiWiki::dirname($1));
+				my $file="$config{wikistatedir}/comments_pending/".
+					IkiWiki::possibly_foolish_untaint($id);
+
+				if ($action eq 'Accept') {
+					my $content=eval { readfile($file) };
+					next if $@; # file vanished since form was displayed
+					my $dest=unique_comment_location($page, $config{srcdir})."._comment";
+					writefile($dest, $config{srcdir}, $content);
+					if ($config{rcs} and $config{comments_commit}) {
+						IkiWiki::rcs_add($dest);
+					}
+					$added++;
+				}
+
+				# This removes empty subdirs, so the
+				# .ikiwiki/comments_pending dir will
+				# go away when all are moderated.
+				require IkiWiki::Render;
+				IkiWiki::prune($file);
+			}
+		}
+
+		if ($added) {
+			my $conflict;
+			if ($config{rcs} and $config{comments_commit}) {
+				my $message = gettext("Comment moderation");
+				IkiWiki::disable_commit_hook();
+				$conflict=IkiWiki::rcs_commit_staged($message,
+					$session->param('name'), $ENV{REMOTE_ADDR});
+				IkiWiki::enable_commit_hook();
+				IkiWiki::rcs_update();
+			}
+		
+			# Now we need a refresh
+			require IkiWiki::Render;
+			IkiWiki::refresh();
+			IkiWiki::saveindex();
+		
+			error($conflict) if defined $conflict;
+		}
+	}
+
+	my @comments=map {
+		my $id=$_;
+		my $file="$config{wikistatedir}/comments_pending/$id";
+		my $content=readfile($file);
+		my $ctime=(stat($file))[10];
+		{
+			id => $id,
+			view => previewcomment($content, $id,
+					IkiWiki::dirname($_), $ctime),
+		} 
+	} comments_pending();
+
+	my $template=template("commentmoderation.tmpl");
+	$template->param(
+		sid => $session->id,
+		comments => \@comments,
+	);
+	IkiWiki::printheader($session);
+	print IkiWiki::misctemplate(gettext("comment moderation"), $template->output);
+	exit;
+}
+
+sub comments_pending () {
+	my $dir="$config{wikistatedir}/comments_pending/";
+	return unless -d $dir;
+
+	my @ret;
+	eval q{use File::Find};
+	error($@) if $@;
+	find({
+		no_chdir => 1,
+		wanted => sub {
+			$_=decode_utf8($_);
+			if (IkiWiki::file_pruned($_, $dir)) {
+				$File::Find::prune=1;
+			}
+			elsif (! -l $_ && ! -d _) {
+				$File::Find::prune=0;
+				my ($f)=/$config{wiki_file_regexp}/; # untaint
+				if (defined $f && $f =~ /\Q._comment\E$/) {
+					$f=~s/^\Q$dir\E\/?//;
+                                        push @ret, $f;
+				}
+			}
+		}
+	}, $dir);
+
+	return @ret;
+}
+
+sub previewcomment ($$$) {
+	my $content=shift;
+	my $location=shift;
+	my $page=shift;
+	my $time=shift;
+
+	my $preview = IkiWiki::htmlize($location, $page, '_comment',
+			IkiWiki::linkify($location, $page,
+				IkiWiki::preprocess($location, $page,
+					IkiWiki::filter($location,
+						$page, $content),
+					0, 1)));
+	IkiWiki::run_hooks(format => sub {
+			$preview = shift->(page => $page,
+				content => $preview);
+		});
+
+	my $template = template("comment.tmpl");
+	$template->param(content => $preview);
+	$template->param(ctime => displaytime($time));
+
+	IkiWiki::run_hooks(pagetemplate => sub {
+		shift->(page => $location,
+			destpage => $page,
+			template => $template);
+	});
+
+	return $template->output;
+}
+
 sub commentsshown ($) {
 	my $page=shift;
 
diff --git a/debian/changelog b/debian/changelog
index 002ae12c1..c9ebcd1e9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -8,6 +8,7 @@ ikiwiki (3.03) UNRELEASED; urgency=low
     config settings.
   * comments: If comment content checks fail, store the comment
     (in .ikiwiki/comments_pending) for moderator review.
+  * comments: Add a moderation web interface.
 
  -- Joey Hess <joeyh@debian.org>  Sun, 18 Jan 2009 14:50:57 -0500
 
diff --git a/templates/commentmoderation.tmpl b/templates/commentmoderation.tmpl
new file mode 100644
index 000000000..3dadb791b
--- /dev/null
+++ b/templates/commentmoderation.tmpl
@@ -0,0 +1,23 @@
+<TMPL_IF NAME="COMMENTS">
+<br />
+<form action="<TMPL_VAR CGIURL>" method="post">
+<input type="hidden" name="do" value="commentmoderation" />
+<input type="hidden" name="sid" value="<TMPL_VAR SID>" />
+<TMPL_LOOP NAME="COMMENTS">
+<div>
+<div>
+<TMPL_VAR VIEW>
+</div>
+<input type="radio" value="Defer" name="<TMPL_VAR ID>" checked>Defer</input>
+<input type="radio" value="Accept" name="<TMPL_VAR ID>">Accept</input>
+<input type="radio" value="Reject" name="<TMPL_VAR ID>">Reject</input>
+</div>
+<br />
+</TMPL_LOOP>
+<input type="submit" value="Submit" />
+</form>
+<TMPL_ELSE>
+<p>
+No comments need moderation at this time.
+</p>
+</TMPL_IF>