2008-09-21 20:33:45 +02:00
|
|
|
#!/usr/bin/perl
|
|
|
|
# Copyright © 2006-2008 Joey Hess <joey@ikiwiki.info>
|
|
|
|
# Copyright © 2008 Simon McVittie <http://smcv.pseudorandom.co.uk/>
|
|
|
|
# Licensed under the GNU GPL, version 2, or any later version published by the
|
|
|
|
# Free Software Foundation
|
2008-11-18 11:17:18 +01:00
|
|
|
package IkiWiki::Plugin::comments;
|
2008-09-21 20:33:45 +02:00
|
|
|
|
|
|
|
use warnings;
|
|
|
|
use strict;
|
2008-12-23 22:34:19 +01:00
|
|
|
use IkiWiki 3.00;
|
2008-12-11 00:53:04 +01:00
|
|
|
use Encode;
|
2008-12-12 12:02:41 +01:00
|
|
|
use POSIX qw(strftime);
|
2008-09-21 20:33:45 +02:00
|
|
|
|
|
|
|
use constant PREVIEW => "Preview";
|
|
|
|
use constant POST_COMMENT => "Post comment";
|
|
|
|
use constant CANCEL => "Cancel";
|
|
|
|
|
2008-12-12 21:05:26 +01:00
|
|
|
my $postcomment;
|
2008-12-21 02:55:38 +01:00
|
|
|
my %commentstate;
|
2008-12-12 21:05:26 +01:00
|
|
|
|
2008-12-17 21:22:16 +01:00
|
|
|
sub import {
|
2008-11-23 18:42:50 +01:00
|
|
|
hook(type => "checkconfig", id => 'comments', call => \&checkconfig);
|
2008-11-18 11:17:18 +01:00
|
|
|
hook(type => "getsetup", id => 'comments', call => \&getsetup);
|
2011-06-04 17:55:02 +02:00
|
|
|
hook(type => "preprocess", id => 'comment', call => \&preprocess,
|
|
|
|
scan => 1);
|
2010-07-06 02:19:31 +02:00
|
|
|
hook(type => "preprocess", id => 'commentmoderation', call => \&preprocess_moderation);
|
2009-06-02 23:06:46 +02:00
|
|
|
# here for backwards compatability with old comments
|
2008-12-11 22:23:55 +01:00
|
|
|
hook(type => "preprocess", id => '_comment', call => \&preprocess);
|
2008-11-18 11:17:18 +01:00
|
|
|
hook(type => "sessioncgi", id => 'comment', call => \&sessioncgi);
|
2008-11-18 11:43:11 +01:00
|
|
|
hook(type => "htmlize", id => "_comment", call => \&htmlize);
|
2010-05-07 01:19:51 +02:00
|
|
|
hook(type => "htmlize", id => "_comment_pending",
|
|
|
|
call => \&htmlize_pending);
|
2008-11-23 13:04:00 +01:00
|
|
|
hook(type => "pagetemplate", id => "comments", call => \&pagetemplate);
|
2010-05-07 01:19:51 +02:00
|
|
|
hook(type => "formbuilder_setup", id => "comments",
|
|
|
|
call => \&formbuilder_setup);
|
2009-02-01 00:01:10 +01:00
|
|
|
# Load goto to fix up user page links for logged-in commenters
|
|
|
|
IkiWiki::loadplugin("goto");
|
2008-12-11 00:53:04 +01:00
|
|
|
IkiWiki::loadplugin("inline");
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-11-17 11:41:50 +01:00
|
|
|
|
2008-12-17 21:22:16 +01:00
|
|
|
sub getsetup () {
|
2008-12-12 20:59:23 +01:00
|
|
|
return
|
|
|
|
plugin => {
|
|
|
|
safe => 1,
|
|
|
|
rebuild => 1,
|
2010-02-12 10:22:15 +01:00
|
|
|
section => "web",
|
2008-12-12 20:59:23 +01:00
|
|
|
},
|
2008-12-18 00:50:04 +01:00
|
|
|
comments_pagespec => {
|
2008-12-12 20:59:23 +01:00
|
|
|
type => 'pagespec',
|
2008-12-18 02:22:42 +01:00
|
|
|
example => 'blog/* and !*/Discussion',
|
2008-12-18 00:50:04 +01:00
|
|
|
description => 'PageSpec of pages where comments are allowed',
|
2008-12-12 20:59:23 +01:00
|
|
|
link => 'ikiwiki/PageSpec',
|
|
|
|
safe => 1,
|
|
|
|
rebuild => 1,
|
|
|
|
},
|
2008-12-18 00:50:04 +01:00
|
|
|
comments_closed_pagespec => {
|
2008-12-12 20:59:23 +01:00
|
|
|
type => 'pagespec',
|
2008-12-18 00:50:04 +01:00
|
|
|
example => 'blog/controversial or blog/flamewar',
|
|
|
|
description => 'PageSpec of pages where posting new comments is not allowed',
|
2008-12-12 20:59:23 +01:00
|
|
|
link => 'ikiwiki/PageSpec',
|
|
|
|
safe => 1,
|
|
|
|
rebuild => 1,
|
|
|
|
},
|
|
|
|
comments_pagename => {
|
|
|
|
type => 'string',
|
|
|
|
default => 'comment_',
|
|
|
|
description => 'Base name for comments, e.g. "comment_" for pages like "sandbox/comment_12"',
|
|
|
|
safe => 0, # manual page moving required
|
|
|
|
rebuild => undef,
|
|
|
|
},
|
|
|
|
comments_allowdirectives => {
|
|
|
|
type => 'boolean',
|
|
|
|
example => 0,
|
|
|
|
description => 'Interpret directives in comments?',
|
|
|
|
safe => 1,
|
|
|
|
rebuild => 0,
|
|
|
|
},
|
|
|
|
comments_allowauthor => {
|
|
|
|
type => 'boolean',
|
|
|
|
example => 0,
|
|
|
|
description => 'Allow anonymous commenters to set an author name?',
|
|
|
|
safe => 1,
|
|
|
|
rebuild => 0,
|
|
|
|
},
|
|
|
|
comments_commit => {
|
|
|
|
type => 'boolean',
|
|
|
|
example => 1,
|
|
|
|
description => 'commit comments to the VCS',
|
|
|
|
# old uncommitted comments are likely to cause
|
|
|
|
# confusion if this is changed
|
|
|
|
safe => 0,
|
|
|
|
rebuild => 0,
|
|
|
|
},
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-12-12 20:59:23 +01:00
|
|
|
|
2008-12-17 23:05:49 +01:00
|
|
|
sub checkconfig () {
|
|
|
|
$config{comments_commit} = 1
|
|
|
|
unless defined $config{comments_commit};
|
2008-12-18 00:50:04 +01:00
|
|
|
$config{comments_pagespec} = ''
|
|
|
|
unless defined $config{comments_pagespec};
|
|
|
|
$config{comments_closed_pagespec} = ''
|
|
|
|
unless defined $config{comments_closed_pagespec};
|
2008-12-17 23:05:49 +01:00
|
|
|
$config{comments_pagename} = 'comment_'
|
|
|
|
unless defined $config{comments_pagename};
|
|
|
|
}
|
|
|
|
|
2008-12-17 21:22:16 +01:00
|
|
|
sub htmlize {
|
2008-12-11 00:53:04 +01:00
|
|
|
my %params = @_;
|
|
|
|
return $params{content};
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-12-11 00:53:04 +01:00
|
|
|
|
2010-05-07 01:19:51 +02:00
|
|
|
sub htmlize_pending {
|
|
|
|
my %params = @_;
|
2010-05-07 19:55:08 +02:00
|
|
|
return sprintf(gettext("this comment needs %s"),
|
2010-05-07 01:19:51 +02:00
|
|
|
'<a href="'.
|
|
|
|
IkiWiki::cgiurl(do => "commentmoderation").'">'.
|
|
|
|
gettext("moderation").'</a>');
|
|
|
|
}
|
|
|
|
|
2008-12-11 02:33:43 +01:00
|
|
|
# FIXME: copied verbatim from meta
|
2008-12-17 21:22:16 +01:00
|
|
|
sub safeurl ($) {
|
2008-12-11 02:33:43 +01:00
|
|
|
my $url=shift;
|
|
|
|
if (exists $IkiWiki::Plugin::htmlscrubber::{safe_url_regexp} &&
|
|
|
|
defined $IkiWiki::Plugin::htmlscrubber::safe_url_regexp) {
|
|
|
|
return $url=~/$IkiWiki::Plugin::htmlscrubber::safe_url_regexp/;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 1;
|
|
|
|
}
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-12-11 02:33:43 +01:00
|
|
|
|
2008-12-17 21:22:16 +01:00
|
|
|
sub preprocess {
|
2008-12-11 00:53:04 +01:00
|
|
|
my %params = @_;
|
|
|
|
my $page = $params{page};
|
|
|
|
|
|
|
|
my $format = $params{format};
|
2008-12-12 21:13:07 +01:00
|
|
|
if (defined $format && ! exists $IkiWiki::hooks{htmlize}{$format}) {
|
2008-12-11 00:53:04 +01:00
|
|
|
error(sprintf(gettext("unsupported page format %s"), $format));
|
|
|
|
}
|
|
|
|
|
|
|
|
my $content = $params{content};
|
2008-12-12 21:13:07 +01:00
|
|
|
if (! defined $content) {
|
2008-12-11 00:53:04 +01:00
|
|
|
error(gettext("comment must have content"));
|
|
|
|
}
|
|
|
|
$content =~ s/\\"/"/g;
|
|
|
|
|
2011-06-04 17:55:02 +02:00
|
|
|
if (defined wantarray) {
|
|
|
|
if ($config{comments_allowdirectives}) {
|
|
|
|
$content = IkiWiki::preprocess($page, $params{destpage},
|
|
|
|
$content);
|
|
|
|
}
|
2008-12-11 00:53:04 +01:00
|
|
|
|
2011-06-04 17:55:02 +02:00
|
|
|
# no need to bother with htmlize if it's just HTML
|
|
|
|
$content = IkiWiki::htmlize($page, $params{destpage}, $format, $content)
|
|
|
|
if defined $format;
|
|
|
|
|
|
|
|
IkiWiki::run_hooks(sanitize => sub {
|
|
|
|
$content = shift->(
|
|
|
|
page => $page,
|
|
|
|
destpage => $params{destpage},
|
|
|
|
content => $content,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
IkiWiki::preprocess($page, $params{destpage}, $content, 1);
|
|
|
|
}
|
2008-12-11 00:53:04 +01:00
|
|
|
|
2008-12-11 02:33:43 +01:00
|
|
|
# set metadata, possibly overriding [[!meta]] directives from the
|
|
|
|
# comment itself
|
|
|
|
|
|
|
|
my $commentuser;
|
|
|
|
my $commentip;
|
|
|
|
my $commentauthor;
|
|
|
|
my $commentauthorurl;
|
2008-12-18 20:56:36 +01:00
|
|
|
my $commentopenid;
|
2008-12-11 00:53:04 +01:00
|
|
|
if (defined $params{username}) {
|
2008-12-11 02:33:43 +01:00
|
|
|
$commentuser = $params{username};
|
2008-12-18 20:56:36 +01:00
|
|
|
|
|
|
|
my $oiduser = eval { IkiWiki::openiduser($commentuser) };
|
|
|
|
|
|
|
|
if (defined $oiduser) {
|
|
|
|
# looks like an OpenID
|
|
|
|
$commentauthorurl = $commentuser;
|
2010-06-24 02:12:26 +02:00
|
|
|
$commentauthor = (defined $params{nickname} && length $params{nickname}) ? $params{nickname} : $oiduser;
|
2008-12-18 20:56:36 +01:00
|
|
|
$commentopenid = $commentuser;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$commentauthorurl = IkiWiki::cgiurl(
|
2009-01-31 15:49:12 +01:00
|
|
|
do => 'goto',
|
2010-02-05 00:24:15 +01:00
|
|
|
page => IkiWiki::userpage($commentuser)
|
|
|
|
);
|
2008-12-18 20:56:36 +01:00
|
|
|
|
|
|
|
$commentauthor = $commentuser;
|
|
|
|
}
|
2008-12-11 00:53:04 +01:00
|
|
|
}
|
|
|
|
else {
|
2008-12-11 03:29:11 +01:00
|
|
|
if (defined $params{ip}) {
|
|
|
|
$commentip = $params{ip};
|
|
|
|
}
|
2008-12-11 02:33:43 +01:00
|
|
|
$commentauthor = gettext("Anonymous");
|
2008-12-11 00:53:04 +01:00
|
|
|
}
|
|
|
|
|
2008-12-21 02:55:38 +01:00
|
|
|
$commentstate{$page}{commentuser} = $commentuser;
|
|
|
|
$commentstate{$page}{commentopenid} = $commentopenid;
|
|
|
|
$commentstate{$page}{commentip} = $commentip;
|
|
|
|
$commentstate{$page}{commentauthor} = $commentauthor;
|
|
|
|
$commentstate{$page}{commentauthorurl} = $commentauthorurl;
|
2011-03-30 17:13:31 +02:00
|
|
|
$commentstate{$page}{commentauthoravatar} = $params{avatar};
|
2008-12-12 21:13:07 +01:00
|
|
|
if (! defined $pagestate{$page}{meta}{author}) {
|
2008-12-11 02:33:43 +01:00
|
|
|
$pagestate{$page}{meta}{author} = $commentauthor;
|
|
|
|
}
|
2008-12-12 21:13:07 +01:00
|
|
|
if (! defined $pagestate{$page}{meta}{authorurl}) {
|
2008-12-11 02:33:43 +01:00
|
|
|
$pagestate{$page}{meta}{authorurl} = $commentauthorurl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($config{comments_allowauthor}) {
|
|
|
|
if (defined $params{claimedauthor}) {
|
|
|
|
$pagestate{$page}{meta}{author} = $params{claimedauthor};
|
|
|
|
}
|
|
|
|
|
2008-12-26 20:07:19 +01:00
|
|
|
if (defined $params{url}) {
|
|
|
|
my $url=$params{url};
|
|
|
|
|
|
|
|
eval q{use URI::Heuristic};
|
2011-03-30 16:48:57 +02:00
|
|
|
if (! $@) {
|
2008-12-26 20:07:19 +01:00
|
|
|
$url=URI::Heuristic::uf_uristr($url);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safeurl($url)) {
|
|
|
|
$pagestate{$page}{meta}{authorurl} = $url;
|
|
|
|
}
|
2008-12-11 02:33:43 +01:00
|
|
|
}
|
2008-12-11 00:53:04 +01:00
|
|
|
}
|
|
|
|
else {
|
2008-12-11 02:33:43 +01:00
|
|
|
$pagestate{$page}{meta}{author} = $commentauthor;
|
|
|
|
$pagestate{$page}{meta}{authorurl} = $commentauthorurl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (defined $params{subject}) {
|
2010-04-06 02:31:38 +02:00
|
|
|
# decode title the same way meta does
|
2010-04-05 22:34:49 +02:00
|
|
|
eval q{use HTML::Entities};
|
2010-04-06 02:31:38 +02:00
|
|
|
$pagestate{$page}{meta}{title} = decode_entities($params{subject});
|
2008-12-11 00:53:04 +01:00
|
|
|
}
|
|
|
|
|
2010-01-05 02:51:40 +01:00
|
|
|
if ($params{page} =~ m/\/\Q$config{comments_pagename}\E\d+_/) {
|
2011-01-05 21:26:09 +01:00
|
|
|
$pagestate{$page}{meta}{permalink} = urlto(IkiWiki::dirname($params{page})).
|
2009-03-26 21:45:53 +01:00
|
|
|
"#".page_to_id($params{page});
|
2008-12-11 00:53:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
eval q{use Date::Parse};
|
|
|
|
if (! $@) {
|
|
|
|
my $time = str2time($params{date});
|
|
|
|
$IkiWiki::pagectime{$page} = $time if defined $time;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $content;
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-09-21 20:33:45 +02:00
|
|
|
|
2010-07-06 02:19:31 +02:00
|
|
|
sub preprocess_moderation {
|
|
|
|
my %params = @_;
|
|
|
|
|
|
|
|
$params{desc}=gettext("Comment Moderation")
|
|
|
|
unless defined $params{desc};
|
|
|
|
|
|
|
|
if (length $config{cgiurl}) {
|
|
|
|
return '<a href="'.
|
|
|
|
IkiWiki::cgiurl(do => 'commentmoderation').
|
|
|
|
'">'.$params{desc}.'</a>';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return $params{desc};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-17 21:22:16 +01:00
|
|
|
sub sessioncgi ($$) {
|
2008-09-21 20:33:45 +02:00
|
|
|
my $cgi=shift;
|
|
|
|
my $session=shift;
|
|
|
|
|
|
|
|
my $do = $cgi->param('do');
|
2009-01-26 00:49:57 +01:00
|
|
|
if ($do eq 'comment') {
|
|
|
|
editcomment($cgi, $session);
|
|
|
|
}
|
|
|
|
elsif ($do eq 'commentmoderation') {
|
|
|
|
commentmoderation($cgi, $session);
|
|
|
|
}
|
2010-05-07 23:11:23 +02:00
|
|
|
elsif ($do eq 'commentsignin') {
|
|
|
|
IkiWiki::cgi_signin($cgi, $session);
|
|
|
|
exit;
|
|
|
|
}
|
2009-01-26 00:49:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# Mostly cargo-culted from IkiWiki::plugin::editpage
|
|
|
|
sub editcomment ($$) {
|
|
|
|
my $cgi=shift;
|
|
|
|
my $session=shift;
|
2008-09-21 20:33:45 +02:00
|
|
|
|
|
|
|
IkiWiki::decode_cgi_utf8($cgi);
|
|
|
|
|
|
|
|
eval q{use CGI::FormBuilder};
|
|
|
|
error($@) if $@;
|
|
|
|
|
|
|
|
my @buttons = (POST_COMMENT, PREVIEW, CANCEL);
|
|
|
|
my $form = CGI::FormBuilder->new(
|
2008-12-11 02:33:43 +01:00
|
|
|
fields => [qw{do sid page subject editcontent type author url}],
|
2008-09-21 20:33:45 +02:00
|
|
|
charset => 'utf-8',
|
|
|
|
method => 'POST',
|
2008-12-11 01:22:34 +01:00
|
|
|
required => [qw{editcontent}],
|
2008-09-21 20:33:45 +02:00
|
|
|
javascript => 0,
|
|
|
|
params => $cgi,
|
2010-11-23 01:12:17 +01:00
|
|
|
action => IkiWiki::cgiurl(),
|
2008-09-21 20:33:45 +02:00
|
|
|
header => 0,
|
|
|
|
table => 0,
|
2010-04-24 22:11:33 +02:00
|
|
|
template => { template('editcomment.tmpl') },
|
2008-09-21 20:33:45 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
IkiWiki::decode_form_utf8($form);
|
|
|
|
IkiWiki::run_hooks(formbuilder_setup => sub {
|
2008-11-18 11:17:18 +01:00
|
|
|
shift->(title => "comment", form => $form, cgi => $cgi,
|
2008-09-21 20:33:45 +02:00
|
|
|
session => $session, buttons => \@buttons);
|
|
|
|
});
|
|
|
|
IkiWiki::decode_form_utf8($form);
|
|
|
|
|
2008-12-11 01:19:39 +01:00
|
|
|
my $type = $form->param('type');
|
|
|
|
if (defined $type && length $type && $IkiWiki::hooks{htmlize}{$type}) {
|
2008-12-11 03:22:20 +01:00
|
|
|
$type = IkiWiki::possibly_foolish_untaint($type);
|
2008-12-11 01:19:39 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$type = $config{default_pageext};
|
|
|
|
}
|
2009-05-21 21:50:25 +02:00
|
|
|
|
|
|
|
|
2008-12-11 01:19:39 +01:00
|
|
|
my @page_types;
|
|
|
|
if (exists $IkiWiki::hooks{htmlize}) {
|
2009-05-21 21:50:25 +02:00
|
|
|
foreach my $key (grep { !/^_/ } keys %{$IkiWiki::hooks{htmlize}}) {
|
|
|
|
push @page_types, [$key, $IkiWiki::hooks{htmlize}{$key}{longname} || $key];
|
|
|
|
}
|
2008-12-11 01:19:39 +01:00
|
|
|
}
|
2009-05-21 21:50:25 +02:00
|
|
|
@page_types=sort @page_types;
|
2008-12-11 01:19:39 +01:00
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
$form->field(name => 'do', type => 'hidden');
|
|
|
|
$form->field(name => 'sid', type => 'hidden', value => $session->id,
|
|
|
|
force => 1);
|
|
|
|
$form->field(name => 'page', type => 'hidden');
|
2008-11-16 19:11:55 +01:00
|
|
|
$form->field(name => 'subject', type => 'text', size => 72);
|
2008-12-11 01:22:34 +01:00
|
|
|
$form->field(name => 'editcontent', type => 'textarea', rows => 10);
|
2008-12-11 01:19:39 +01:00
|
|
|
$form->field(name => "type", value => $type, force => 1,
|
|
|
|
type => 'select', options => \@page_types);
|
2008-09-21 20:33:45 +02:00
|
|
|
|
2008-12-11 02:33:43 +01:00
|
|
|
$form->tmpl_param(username => $session->param('name'));
|
|
|
|
|
2008-12-12 21:38:23 +01:00
|
|
|
if ($config{comments_allowauthor} and
|
|
|
|
! defined $session->param('name')) {
|
2008-12-11 02:33:43 +01:00
|
|
|
$form->tmpl_param(allowauthor => 1);
|
|
|
|
$form->field(name => 'author', type => 'text', size => '40');
|
|
|
|
$form->field(name => 'url', type => 'text', size => '40');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$form->tmpl_param(allowauthor => 0);
|
|
|
|
$form->field(name => 'author', type => 'hidden', value => '',
|
|
|
|
force => 1);
|
|
|
|
$form->field(name => 'url', type => 'hidden', value => '',
|
|
|
|
force => 1);
|
|
|
|
}
|
|
|
|
|
2009-04-23 20:56:10 +02:00
|
|
|
if (! defined $session->param('name')) {
|
|
|
|
# Make signinurl work and return here.
|
2010-05-07 23:11:23 +02:00
|
|
|
$form->tmpl_param(signinurl => IkiWiki::cgiurl(do => 'commentsignin'));
|
2009-04-23 20:56:10 +02:00
|
|
|
$session->param(postsignin => $ENV{QUERY_STRING});
|
|
|
|
IkiWiki::cgi_savesession($session);
|
|
|
|
}
|
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
# The untaint is OK (as in editpage) because we're about to pass
|
2011-01-22 15:15:33 +01:00
|
|
|
# it to file_pruned and wiki_file_regexp anyway.
|
2011-01-24 21:56:28 +01:00
|
|
|
my ($page) = $form->field('page')=~/$config{wiki_file_regexp}/;
|
2008-09-21 20:33:45 +02:00
|
|
|
$page = IkiWiki::possibly_foolish_untaint($page);
|
2008-12-12 21:13:07 +01:00
|
|
|
if (! defined $page || ! length $page ||
|
2010-04-18 01:05:40 +02:00
|
|
|
IkiWiki::file_pruned($page)) {
|
2008-11-15 15:11:24 +01:00
|
|
|
error(gettext("bad page name"));
|
2008-09-21 20:33:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$form->title(sprintf(gettext("commenting on %s"),
|
2010-12-25 18:38:26 +01:00
|
|
|
IkiWiki::pagetitle(IkiWiki::basename($page))));
|
2008-09-21 20:33:45 +02:00
|
|
|
|
|
|
|
$form->tmpl_param('helponformattinglink',
|
|
|
|
htmllink($page, $page, 'ikiwiki/formatting',
|
|
|
|
noimageinline => 1,
|
2008-11-17 10:08:54 +01:00
|
|
|
linktext => 'FormattingHelp'),
|
2008-12-12 21:38:23 +01:00
|
|
|
allowdirectives => $config{allow_directives});
|
2008-09-21 20:33:45 +02:00
|
|
|
|
2008-11-23 18:10:44 +01:00
|
|
|
if ($form->submitted eq CANCEL) {
|
|
|
|
# bounce back to the page they wanted to comment on, and exit.
|
2011-01-05 18:49:04 +01:00
|
|
|
IkiWiki::redirect($cgi, urlto($page));
|
2008-11-23 18:10:44 +01:00
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
if (not exists $pagesources{$page}) {
|
2008-11-15 15:11:24 +01:00
|
|
|
error(sprintf(gettext(
|
|
|
|
"page '%s' doesn't exist, so you can't comment"),
|
|
|
|
$page));
|
2008-09-21 20:33:45 +02:00
|
|
|
}
|
2008-11-23 18:10:44 +01:00
|
|
|
|
2008-12-18 00:50:04 +01:00
|
|
|
if (pagespec_match($page, $config{comments_closed_pagespec},
|
2008-11-23 18:10:44 +01:00
|
|
|
location => $page)) {
|
2008-11-17 10:09:52 +01:00
|
|
|
error(sprintf(gettext(
|
2008-11-23 18:10:44 +01:00
|
|
|
"comments on page '%s' are closed"),
|
2008-11-17 10:09:52 +01:00
|
|
|
$page));
|
|
|
|
}
|
2008-09-21 20:33:45 +02:00
|
|
|
|
2008-12-12 21:05:26 +01:00
|
|
|
# Set a flag to indicate that we're posting a comment,
|
|
|
|
# so that postcomment() can tell it should match.
|
|
|
|
$postcomment=1;
|
|
|
|
IkiWiki::check_canedit($page, $cgi, $session);
|
|
|
|
$postcomment=0;
|
2008-11-16 19:23:23 +01:00
|
|
|
|
2009-06-02 23:06:46 +02:00
|
|
|
my $content = "[[!comment format=$type\n";
|
2008-12-11 00:53:04 +01:00
|
|
|
|
|
|
|
if (defined $session->param('name')) {
|
|
|
|
my $username = $session->param('name');
|
|
|
|
$username =~ s/"/"/g;
|
|
|
|
$content .= " username=\"$username\"\n";
|
|
|
|
}
|
2010-06-24 02:12:26 +02:00
|
|
|
if (defined $session->param('nickname')) {
|
|
|
|
my $nickname = $session->param('nickname');
|
|
|
|
$nickname =~ s/"/"/g;
|
|
|
|
$content .= " nickname=\"$nickname\"\n";
|
|
|
|
}
|
2010-06-23 22:32:20 +02:00
|
|
|
elsif (defined $session->remote_addr()) {
|
|
|
|
my $ip = $session->remote_addr();
|
2008-12-11 00:53:04 +01:00
|
|
|
if ($ip =~ m/^([.0-9]+)$/) {
|
|
|
|
$content .= " ip=\"$1\"\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-12 21:38:23 +01:00
|
|
|
if ($config{comments_allowauthor}) {
|
2008-12-11 02:33:43 +01:00
|
|
|
my $author = $form->field('author');
|
2009-01-23 02:58:49 +01:00
|
|
|
if (defined $author && length $author) {
|
2008-12-11 02:33:43 +01:00
|
|
|
$author =~ s/"/"/g;
|
|
|
|
$content .= " claimedauthor=\"$author\"\n";
|
|
|
|
}
|
|
|
|
my $url = $form->field('url');
|
2009-01-23 02:58:49 +01:00
|
|
|
if (defined $url && length $url) {
|
2008-12-11 02:33:43 +01:00
|
|
|
$url =~ s/"/"/g;
|
|
|
|
$content .= " url=\"$url\"\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-30 17:13:31 +02:00
|
|
|
my $avatar=getavatar($session->param('name'));
|
|
|
|
if (defined $avatar && length $avatar) {
|
|
|
|
$avatar =~ s/"/"/g;
|
|
|
|
$content .= " avatar=\"$avatar\"\n";
|
|
|
|
}
|
|
|
|
|
2008-12-11 00:53:04 +01:00
|
|
|
my $subject = $form->field('subject');
|
2009-01-23 02:58:49 +01:00
|
|
|
if (defined $subject && length $subject) {
|
2008-12-11 03:03:07 +01:00
|
|
|
$subject =~ s/"/"/g;
|
|
|
|
}
|
2009-12-30 21:41:17 +01:00
|
|
|
else {
|
|
|
|
$subject = "comment ".(num_comments($page, $config{srcdir}) + 1);
|
|
|
|
}
|
|
|
|
$content .= " subject=\"$subject\"\n";
|
2008-12-11 00:53:04 +01:00
|
|
|
|
2008-12-12 12:02:41 +01:00
|
|
|
$content .= " date=\"" . decode_utf8(strftime('%Y-%m-%dT%H:%M:%SZ', gmtime)) . "\"\n";
|
2008-11-18 11:29:16 +01:00
|
|
|
|
2010-06-09 23:47:49 +02:00
|
|
|
my $editcontent = $form->field('editcontent');
|
|
|
|
$editcontent="" if ! defined $editcontent;
|
2008-12-12 21:19:01 +01:00
|
|
|
$editcontent =~ s/\r\n/\n/g;
|
|
|
|
$editcontent =~ s/\r/\n/g;
|
|
|
|
$editcontent =~ s/"/\\"/g;
|
2008-12-11 01:22:34 +01:00
|
|
|
$content .= " content=\"\"\"\n$editcontent\n\"\"\"]]\n";
|
2008-09-21 20:33:45 +02:00
|
|
|
|
2009-12-30 21:41:17 +01:00
|
|
|
my $location=unique_comment_location($page, $content, $config{srcdir});
|
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
# This is essentially a simplified version of editpage:
|
|
|
|
# - the user does not control the page that's created, only the parent
|
|
|
|
# - it's always a create operation, never an edit
|
|
|
|
# - this means that conflicts should never happen
|
|
|
|
# - this means that if they do, rocks fall and everyone dies
|
|
|
|
|
|
|
|
if ($form->submitted eq PREVIEW) {
|
2009-01-26 04:30:28 +01:00
|
|
|
my $preview=previewcomment($content, $location, $page, time);
|
|
|
|
IkiWiki::run_hooks(format => sub {
|
|
|
|
$preview = shift->(page => $page,
|
|
|
|
content => $preview);
|
|
|
|
});
|
|
|
|
$form->tmpl_param(page_preview => $preview);
|
2008-09-21 20:33:45 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$form->tmpl_param(page_preview => "");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($form->submitted eq POST_COMMENT && $form->validate) {
|
2008-12-17 20:26:08 +01:00
|
|
|
IkiWiki::checksessionexpiry($cgi, $session);
|
2009-01-17 02:46:55 +01:00
|
|
|
|
2009-01-17 03:58:05 +01:00
|
|
|
$postcomment=1;
|
2009-01-25 21:42:13 +01:00
|
|
|
my $ok=IkiWiki::check_content(content => $form->field('editcontent'),
|
2009-01-17 02:46:55 +01:00
|
|
|
subject => $form->field('subject'),
|
|
|
|
$config{comments_allowauthor} ? (
|
|
|
|
author => $form->field('author'),
|
|
|
|
url => $form->field('url'),
|
|
|
|
) : (),
|
|
|
|
page => $location,
|
2009-01-25 21:42:13 +01:00
|
|
|
cgi => $cgi,
|
|
|
|
session => $session,
|
|
|
|
nonfatal => 1,
|
2009-01-17 03:58:05 +01:00
|
|
|
);
|
|
|
|
$postcomment=0;
|
2009-01-25 21:42:13 +01:00
|
|
|
|
|
|
|
if (! $ok) {
|
2010-05-07 19:47:29 +02:00
|
|
|
$location=unique_comment_location($page, $content, $config{srcdir}, "._comment_pending");
|
2010-05-07 01:19:51 +02:00
|
|
|
writefile("$location._comment_pending", $config{srcdir}, $content);
|
|
|
|
|
|
|
|
# Refresh so anything that deals with pending
|
|
|
|
# comments can be updated.
|
|
|
|
require IkiWiki::Render;
|
|
|
|
IkiWiki::refresh();
|
|
|
|
IkiWiki::saveindex();
|
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
IkiWiki::printheader($session);
|
2011-01-05 22:15:38 +01:00
|
|
|
print IkiWiki::cgitemplate($cgi, gettext(gettext("comment stored for moderation")),
|
2009-01-25 21:42:13 +01:00
|
|
|
"<p>".
|
2009-01-27 02:33:55 +01:00
|
|
|
gettext("Your comment will be posted after moderator review").
|
2009-01-25 21:42:13 +01:00
|
|
|
"</p>");
|
|
|
|
exit;
|
|
|
|
}
|
2008-11-23 18:52:30 +01:00
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
# FIXME: could probably do some sort of graceful retry
|
2008-11-23 18:10:44 +01:00
|
|
|
# on error? Would require significant unwinding though
|
2009-01-25 21:42:13 +01:00
|
|
|
my $file = "$location._comment";
|
2008-09-21 20:33:45 +02:00
|
|
|
writefile($file, $config{srcdir}, $content);
|
|
|
|
|
|
|
|
my $conflict;
|
|
|
|
|
2008-12-12 21:38:23 +01:00
|
|
|
if ($config{rcs} and $config{comments_commit}) {
|
2008-11-15 15:11:24 +01:00
|
|
|
my $message = gettext("Added a comment");
|
2008-09-21 20:33:45 +02:00
|
|
|
if (defined $form->field('subject') &&
|
|
|
|
length $form->field('subject')) {
|
2008-11-23 18:10:44 +01:00
|
|
|
$message = sprintf(
|
|
|
|
gettext("Added a comment: %s"),
|
|
|
|
$form->field('subject'));
|
2008-09-21 20:33:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
IkiWiki::rcs_add($file);
|
|
|
|
IkiWiki::disable_commit_hook();
|
2010-06-23 23:35:21 +02:00
|
|
|
$conflict = IkiWiki::rcs_commit_staged(
|
|
|
|
message => $message,
|
|
|
|
session => $session,
|
|
|
|
);
|
2008-09-21 20:33:45 +02:00
|
|
|
IkiWiki::enable_commit_hook();
|
|
|
|
IkiWiki::rcs_update();
|
|
|
|
}
|
|
|
|
|
|
|
|
# Now we need a refresh
|
|
|
|
require IkiWiki::Render;
|
|
|
|
IkiWiki::refresh();
|
|
|
|
IkiWiki::saveindex();
|
|
|
|
|
|
|
|
# this should never happen, unless a committer deliberately
|
|
|
|
# breaks it or something
|
|
|
|
error($conflict) if defined $conflict;
|
|
|
|
|
2008-12-19 02:58:16 +01:00
|
|
|
# Jump to the new comment on the page.
|
2008-12-29 04:20:22 +01:00
|
|
|
# The trailing question mark tries to avoid broken
|
|
|
|
# caches and get the most recent version of the page.
|
2010-11-29 20:07:26 +01:00
|
|
|
IkiWiki::redirect($cgi, urlto($page).
|
2009-03-26 21:45:53 +01:00
|
|
|
"?updated#".page_to_id($location));
|
2008-12-29 04:20:22 +01:00
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
}
|
|
|
|
else {
|
2011-01-05 21:58:27 +01:00
|
|
|
IkiWiki::showform($form, \@buttons, $session, $cgi,
|
2011-01-05 18:49:04 +01:00
|
|
|
page => $page);
|
2008-09-21 20:33:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
exit;
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-09-21 20:33:45 +02:00
|
|
|
|
2011-03-30 17:13:31 +02:00
|
|
|
sub getavatar ($) {
|
|
|
|
my $user=shift;
|
|
|
|
|
|
|
|
my $avatar;
|
|
|
|
eval q{use Libravatar::URL};
|
|
|
|
if (! $@) {
|
|
|
|
my $oiduser = eval { IkiWiki::openiduser($user) };
|
|
|
|
my $https=defined $config{url} && $config{url}=~/^https:/;
|
|
|
|
|
|
|
|
if (defined $oiduser) {
|
|
|
|
eval {
|
|
|
|
$avatar = libravatar_url(openid => $user, https => $https);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! defined $avatar &&
|
|
|
|
(my $email = IkiWiki::userinfo_get($user, 'email'))) {
|
|
|
|
eval {
|
|
|
|
$avatar = libravatar_url(email => $email, https => $https);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $avatar;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
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);
|
|
|
|
|
2009-01-26 04:25:45 +01:00
|
|
|
my $rejectalldefer=$cgi->param('rejectalldefer');
|
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
my %vars=$cgi->Vars;
|
|
|
|
my $added=0;
|
|
|
|
foreach my $id (keys %vars) {
|
2010-05-07 19:44:24 +02:00
|
|
|
if ($id =~ /(.*)\._comment(?:_pending)?$/) {
|
2010-07-04 22:19:22 +02:00
|
|
|
$id=decode_utf8($id);
|
2009-01-26 00:49:57 +01:00
|
|
|
my $action=$cgi->param($id);
|
2009-01-26 04:25:45 +01:00
|
|
|
next if $action eq 'Defer' && ! $rejectalldefer;
|
2009-01-26 00:49:57 +01:00
|
|
|
|
|
|
|
# Make sure that the id is of a legal
|
2010-05-07 01:19:51 +02:00
|
|
|
# pending comment.
|
|
|
|
my ($f) = $id =~ /$config{wiki_file_regexp}/;
|
2009-01-26 00:49:57 +01:00
|
|
|
if (! defined $f || ! length $f ||
|
2010-04-18 01:05:40 +02:00
|
|
|
IkiWiki::file_pruned($f)) {
|
2009-01-26 00:49:57 +01:00
|
|
|
error("illegal file");
|
|
|
|
}
|
|
|
|
|
2010-05-07 01:19:51 +02:00
|
|
|
my $page=IkiWiki::dirname($f);
|
|
|
|
my $file="$config{srcdir}/$f";
|
|
|
|
if (! -e $file) {
|
|
|
|
# old location
|
|
|
|
$file="$config{wikistatedir}/comments_pending/".$f;
|
|
|
|
}
|
2009-01-26 00:49:57 +01:00
|
|
|
|
|
|
|
if ($action eq 'Accept') {
|
|
|
|
my $content=eval { readfile($file) };
|
|
|
|
next if $@; # file vanished since form was displayed
|
2009-12-30 21:41:17 +01:00
|
|
|
my $dest=unique_comment_location($page, $content, $config{srcdir})."._comment";
|
2009-01-26 00:49:57 +01:00
|
|
|
writefile($dest, $config{srcdir}, $content);
|
|
|
|
if ($config{rcs} and $config{comments_commit}) {
|
|
|
|
IkiWiki::rcs_add($dest);
|
|
|
|
}
|
|
|
|
$added++;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2010-06-23 23:35:21 +02:00
|
|
|
$conflict=IkiWiki::rcs_commit_staged(
|
|
|
|
message => $message,
|
|
|
|
session => $session,
|
|
|
|
);
|
2009-01-26 00:49:57 +01:00
|
|
|
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 {
|
2010-05-07 01:19:51 +02:00
|
|
|
my ($id, $dir, $ctime)=@{$_};
|
|
|
|
my $content=readfile("$dir/$id");
|
2009-01-26 04:30:28 +01:00
|
|
|
my $preview=previewcomment($content, $id,
|
2010-05-07 01:19:51 +02:00
|
|
|
$id, $ctime);
|
2009-01-26 00:49:57 +01:00
|
|
|
{
|
|
|
|
id => $id,
|
2009-01-26 04:30:28 +01:00
|
|
|
view => $preview,
|
2010-05-07 01:19:51 +02:00
|
|
|
}
|
2010-05-07 19:44:24 +02:00
|
|
|
} sort { $b->[2] <=> $a->[2] } comments_pending();
|
2009-01-26 00:49:57 +01:00
|
|
|
|
|
|
|
my $template=template("commentmoderation.tmpl");
|
|
|
|
$template->param(
|
|
|
|
sid => $session->id,
|
|
|
|
comments => \@comments,
|
2010-11-23 01:20:57 +01:00
|
|
|
cgiurl => IkiWiki::cgiurl(),
|
2009-01-26 00:49:57 +01:00
|
|
|
);
|
|
|
|
IkiWiki::printheader($session);
|
2009-01-26 04:30:28 +01:00
|
|
|
my $out=$template->output;
|
|
|
|
IkiWiki::run_hooks(format => sub {
|
|
|
|
$out = shift->(page => "", content => $out);
|
|
|
|
});
|
2011-01-05 22:15:38 +01:00
|
|
|
print IkiWiki::cgitemplate($cgi, gettext("comment moderation"), $out);
|
2009-01-26 00:49:57 +01:00
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2009-01-26 01:04:45 +01:00
|
|
|
sub formbuilder_setup (@) {
|
|
|
|
my %params=@_;
|
|
|
|
|
|
|
|
my $form=$params{form};
|
2009-02-26 08:31:13 +01:00
|
|
|
if ($form->title eq "preferences" &&
|
|
|
|
IkiWiki::is_admin($params{session}->param("name"))) {
|
2009-01-26 01:04:45 +01:00
|
|
|
push @{$params{buttons}}, "Comment Moderation";
|
|
|
|
if ($form->submitted && $form->submitted eq "Comment Moderation") {
|
|
|
|
commentmoderation($params{cgi}, $params{session});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
sub comments_pending () {
|
|
|
|
my @ret;
|
2010-05-07 01:19:51 +02:00
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
eval q{use File::Find};
|
|
|
|
error($@) if $@;
|
Fix issues with combining unicode srcdirs and source files.
A short story:
Once there was a unicode string, let's call him Srcdir.
Along came a crufy old File::Find, who went through a tree and pasted each
of the leaves in turn onto Srcdir. But this 90's relic didn't decode the
leaves -- despite some of them using unicode! Poor Srcdir, with these
leaves stuck on him, tainted them with his nice unicode-ness. They didn't
look like leaves at all, but instead garbage.
(In other words, perl's unicode support sucks mightily, and drives
us all to drink and bad storytelling. But we knew that..)
So, srcdir is not normally flagged as unicode, because typically it's pure
ascii. And in that case, things work ok; File::Find finds filenames, which
are not yet decoded to unicode, and appends them to the srcdir, and then
decode_utf8 happily converts the whole thing.
But, if the srcdir does contain utf8 characters, that breaks. Or, if a Yaml
setup file is used, Yaml::Syck's implicitunicode sets the unicode flag of
*all* strings, even those containing only ascii. In either case, srcdir
has the unicode flag set; a non-decoded filename is appended, and the flag
remains set; and decode_utf8 sees the flag and does *nothing*. The result
is that the filename is not decoded, so looks valid and gets skipped.
File::Find only sticks the directory and filenames together in no_chdir
mode .. but we need that mode for security. In order to retain the
security, and avoid the problem, I made it not pass srcdir to File::Find.
Instead, chdir to the srcdir, and pass ".". Since "." is ascii, the problem
is avoided.
Note that chdir srcdir is safe because we check for symlinks in the srcdir
path.
Note that it takes care to chdir back to the starting location. Because
the user may have specified relative paths and so staying in the srcdir
might break. A relative path could even be specifed for an underlay dir, so
it chdirs back after each.
2010-06-15 22:40:37 +02:00
|
|
|
eval q{use Cwd};
|
|
|
|
error($@) if $@;
|
|
|
|
my $origdir=getcwd();
|
2010-05-07 01:19:51 +02:00
|
|
|
|
|
|
|
my $find_comments=sub {
|
|
|
|
my $dir=shift;
|
|
|
|
my $extension=shift;
|
|
|
|
return unless -d $dir;
|
Fix issues with combining unicode srcdirs and source files.
A short story:
Once there was a unicode string, let's call him Srcdir.
Along came a crufy old File::Find, who went through a tree and pasted each
of the leaves in turn onto Srcdir. But this 90's relic didn't decode the
leaves -- despite some of them using unicode! Poor Srcdir, with these
leaves stuck on him, tainted them with his nice unicode-ness. They didn't
look like leaves at all, but instead garbage.
(In other words, perl's unicode support sucks mightily, and drives
us all to drink and bad storytelling. But we knew that..)
So, srcdir is not normally flagged as unicode, because typically it's pure
ascii. And in that case, things work ok; File::Find finds filenames, which
are not yet decoded to unicode, and appends them to the srcdir, and then
decode_utf8 happily converts the whole thing.
But, if the srcdir does contain utf8 characters, that breaks. Or, if a Yaml
setup file is used, Yaml::Syck's implicitunicode sets the unicode flag of
*all* strings, even those containing only ascii. In either case, srcdir
has the unicode flag set; a non-decoded filename is appended, and the flag
remains set; and decode_utf8 sees the flag and does *nothing*. The result
is that the filename is not decoded, so looks valid and gets skipped.
File::Find only sticks the directory and filenames together in no_chdir
mode .. but we need that mode for security. In order to retain the
security, and avoid the problem, I made it not pass srcdir to File::Find.
Instead, chdir to the srcdir, and pass ".". Since "." is ascii, the problem
is avoided.
Note that chdir srcdir is safe because we check for symlinks in the srcdir
path.
Note that it takes care to chdir back to the starting location. Because
the user may have specified relative paths and so staying in the srcdir
might break. A relative path could even be specifed for an underlay dir, so
it chdirs back after each.
2010-06-15 22:40:37 +02:00
|
|
|
|
2010-06-17 22:54:03 +02:00
|
|
|
chdir($dir) || die "chdir $dir: $!";
|
Fix issues with combining unicode srcdirs and source files.
A short story:
Once there was a unicode string, let's call him Srcdir.
Along came a crufy old File::Find, who went through a tree and pasted each
of the leaves in turn onto Srcdir. But this 90's relic didn't decode the
leaves -- despite some of them using unicode! Poor Srcdir, with these
leaves stuck on him, tainted them with his nice unicode-ness. They didn't
look like leaves at all, but instead garbage.
(In other words, perl's unicode support sucks mightily, and drives
us all to drink and bad storytelling. But we knew that..)
So, srcdir is not normally flagged as unicode, because typically it's pure
ascii. And in that case, things work ok; File::Find finds filenames, which
are not yet decoded to unicode, and appends them to the srcdir, and then
decode_utf8 happily converts the whole thing.
But, if the srcdir does contain utf8 characters, that breaks. Or, if a Yaml
setup file is used, Yaml::Syck's implicitunicode sets the unicode flag of
*all* strings, even those containing only ascii. In either case, srcdir
has the unicode flag set; a non-decoded filename is appended, and the flag
remains set; and decode_utf8 sees the flag and does *nothing*. The result
is that the filename is not decoded, so looks valid and gets skipped.
File::Find only sticks the directory and filenames together in no_chdir
mode .. but we need that mode for security. In order to retain the
security, and avoid the problem, I made it not pass srcdir to File::Find.
Instead, chdir to the srcdir, and pass ".". Since "." is ascii, the problem
is avoided.
Note that chdir srcdir is safe because we check for symlinks in the srcdir
path.
Note that it takes care to chdir back to the starting location. Because
the user may have specified relative paths and so staying in the srcdir
might break. A relative path could even be specifed for an underlay dir, so
it chdirs back after each.
2010-06-15 22:40:37 +02:00
|
|
|
|
2010-05-07 01:19:51 +02:00
|
|
|
find({
|
|
|
|
no_chdir => 1,
|
|
|
|
wanted => sub {
|
|
|
|
my $file=decode_utf8($_);
|
Fix issues with combining unicode srcdirs and source files.
A short story:
Once there was a unicode string, let's call him Srcdir.
Along came a crufy old File::Find, who went through a tree and pasted each
of the leaves in turn onto Srcdir. But this 90's relic didn't decode the
leaves -- despite some of them using unicode! Poor Srcdir, with these
leaves stuck on him, tainted them with his nice unicode-ness. They didn't
look like leaves at all, but instead garbage.
(In other words, perl's unicode support sucks mightily, and drives
us all to drink and bad storytelling. But we knew that..)
So, srcdir is not normally flagged as unicode, because typically it's pure
ascii. And in that case, things work ok; File::Find finds filenames, which
are not yet decoded to unicode, and appends them to the srcdir, and then
decode_utf8 happily converts the whole thing.
But, if the srcdir does contain utf8 characters, that breaks. Or, if a Yaml
setup file is used, Yaml::Syck's implicitunicode sets the unicode flag of
*all* strings, even those containing only ascii. In either case, srcdir
has the unicode flag set; a non-decoded filename is appended, and the flag
remains set; and decode_utf8 sees the flag and does *nothing*. The result
is that the filename is not decoded, so looks valid and gets skipped.
File::Find only sticks the directory and filenames together in no_chdir
mode .. but we need that mode for security. In order to retain the
security, and avoid the problem, I made it not pass srcdir to File::Find.
Instead, chdir to the srcdir, and pass ".". Since "." is ascii, the problem
is avoided.
Note that chdir srcdir is safe because we check for symlinks in the srcdir
path.
Note that it takes care to chdir back to the starting location. Because
the user may have specified relative paths and so staying in the srcdir
might break. A relative path could even be specifed for an underlay dir, so
it chdirs back after each.
2010-06-15 22:40:37 +02:00
|
|
|
$file=~s/^\.\///;
|
2010-05-07 01:19:51 +02:00
|
|
|
return if ! length $file || IkiWiki::file_pruned($file)
|
|
|
|
|| -l $_ || -d _ || $file !~ /\Q$extension\E$/;
|
|
|
|
my ($f) = $file =~ /$config{wiki_file_regexp}/; # untaint
|
|
|
|
if (defined $f) {
|
|
|
|
my $ctime=(stat($_))[10];
|
|
|
|
push @ret, [$f, $dir, $ctime];
|
|
|
|
}
|
2009-01-26 00:49:57 +01:00
|
|
|
}
|
Fix issues with combining unicode srcdirs and source files.
A short story:
Once there was a unicode string, let's call him Srcdir.
Along came a crufy old File::Find, who went through a tree and pasted each
of the leaves in turn onto Srcdir. But this 90's relic didn't decode the
leaves -- despite some of them using unicode! Poor Srcdir, with these
leaves stuck on him, tainted them with his nice unicode-ness. They didn't
look like leaves at all, but instead garbage.
(In other words, perl's unicode support sucks mightily, and drives
us all to drink and bad storytelling. But we knew that..)
So, srcdir is not normally flagged as unicode, because typically it's pure
ascii. And in that case, things work ok; File::Find finds filenames, which
are not yet decoded to unicode, and appends them to the srcdir, and then
decode_utf8 happily converts the whole thing.
But, if the srcdir does contain utf8 characters, that breaks. Or, if a Yaml
setup file is used, Yaml::Syck's implicitunicode sets the unicode flag of
*all* strings, even those containing only ascii. In either case, srcdir
has the unicode flag set; a non-decoded filename is appended, and the flag
remains set; and decode_utf8 sees the flag and does *nothing*. The result
is that the filename is not decoded, so looks valid and gets skipped.
File::Find only sticks the directory and filenames together in no_chdir
mode .. but we need that mode for security. In order to retain the
security, and avoid the problem, I made it not pass srcdir to File::Find.
Instead, chdir to the srcdir, and pass ".". Since "." is ascii, the problem
is avoided.
Note that chdir srcdir is safe because we check for symlinks in the srcdir
path.
Note that it takes care to chdir back to the starting location. Because
the user may have specified relative paths and so staying in the srcdir
might break. A relative path could even be specifed for an underlay dir, so
it chdirs back after each.
2010-06-15 22:40:37 +02:00
|
|
|
}, ".");
|
|
|
|
|
2010-06-17 22:54:03 +02:00
|
|
|
chdir($origdir) || die "chdir $origdir: $!";
|
2010-05-07 01:19:51 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
$find_comments->($config{srcdir}, "._comment_pending");
|
|
|
|
# old location
|
|
|
|
$find_comments->("$config{wikistatedir}/comments_pending/",
|
|
|
|
"._comment");
|
2009-01-26 00:49:57 +01:00
|
|
|
|
|
|
|
return @ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub previewcomment ($$$) {
|
|
|
|
my $content=shift;
|
|
|
|
my $location=shift;
|
|
|
|
my $page=shift;
|
|
|
|
my $time=shift;
|
|
|
|
|
2010-11-12 05:25:31 +01:00
|
|
|
# Previewing a comment should implicitly enable comment posting mode.
|
|
|
|
my $oldpostcomment=$postcomment;
|
|
|
|
$postcomment=1;
|
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
my $preview = IkiWiki::htmlize($location, $page, '_comment',
|
|
|
|
IkiWiki::linkify($location, $page,
|
2009-01-26 04:25:45 +01:00
|
|
|
IkiWiki::preprocess($location, $page,
|
|
|
|
IkiWiki::filter($location, $page, $content), 0, 1)));
|
2009-01-26 00:49:57 +01:00
|
|
|
|
|
|
|
my $template = template("comment.tmpl");
|
|
|
|
$template->param(content => $preview);
|
2010-05-02 19:44:13 +02:00
|
|
|
$template->param(ctime => displaytime($time, undef, 1));
|
2010-05-02 22:12:08 +02:00
|
|
|
$template->param(html5 => $config{html5});
|
2009-01-26 00:49:57 +01:00
|
|
|
|
|
|
|
IkiWiki::run_hooks(pagetemplate => sub {
|
|
|
|
shift->(page => $location,
|
|
|
|
destpage => $page,
|
|
|
|
template => $template);
|
|
|
|
});
|
|
|
|
|
2009-01-26 00:56:47 +01:00
|
|
|
$template->param(have_actions => 0);
|
|
|
|
|
2010-11-12 05:25:31 +01:00
|
|
|
$postcomment=$oldpostcomment;
|
|
|
|
|
2009-01-26 00:49:57 +01:00
|
|
|
return $template->output;
|
|
|
|
}
|
|
|
|
|
2008-12-18 01:38:02 +01:00
|
|
|
sub commentsshown ($) {
|
|
|
|
my $page=shift;
|
|
|
|
|
2011-03-28 17:53:55 +02:00
|
|
|
return pagespec_match($page, $config{comments_pagespec},
|
|
|
|
location => $page);
|
2008-12-18 01:38:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub commentsopen ($) {
|
|
|
|
my $page = shift;
|
|
|
|
|
|
|
|
return length $config{cgiurl} > 0 &&
|
|
|
|
(! length $config{comments_closed_pagespec} ||
|
|
|
|
! pagespec_match($page, $config{comments_closed_pagespec},
|
|
|
|
location => $page));
|
|
|
|
}
|
|
|
|
|
2008-12-17 21:22:16 +01:00
|
|
|
sub pagetemplate (@) {
|
2008-11-23 13:04:00 +01:00
|
|
|
my %params = @_;
|
|
|
|
|
|
|
|
my $page = $params{page};
|
|
|
|
my $template = $params{template};
|
2008-12-19 19:55:41 +01:00
|
|
|
my $shown = ($template->query(name => 'commentslink') ||
|
2008-12-21 18:15:49 +01:00
|
|
|
$template->query(name => 'commentsurl') ||
|
|
|
|
$template->query(name => 'atomcommentsurl') ||
|
2008-12-19 19:55:41 +01:00
|
|
|
$template->query(name => 'comments')) &&
|
|
|
|
commentsshown($page);
|
2008-11-23 13:04:00 +01:00
|
|
|
|
|
|
|
if ($template->query(name => 'comments')) {
|
2008-12-18 01:38:02 +01:00
|
|
|
my $comments = undef;
|
2008-11-23 18:11:14 +01:00
|
|
|
if ($shown) {
|
2008-12-12 21:08:06 +01:00
|
|
|
$comments = IkiWiki::preprocess_inline(
|
2011-03-28 17:53:55 +02:00
|
|
|
pages => "comment($page) and !comment($page/*)",
|
2008-12-19 20:03:26 +01:00
|
|
|
template => 'comment',
|
2008-11-23 18:11:14 +01:00
|
|
|
show => 0,
|
|
|
|
reverse => 'yes',
|
|
|
|
page => $page,
|
|
|
|
destpage => $params{destpage},
|
2008-12-11 22:19:50 +01:00
|
|
|
feedfile => 'comments',
|
|
|
|
emptyfeeds => 'no',
|
2008-11-23 18:11:14 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2008-11-23 13:04:00 +01:00
|
|
|
if (defined $comments && length $comments) {
|
2008-11-23 18:11:14 +01:00
|
|
|
$template->param(comments => $comments);
|
|
|
|
}
|
|
|
|
|
2008-12-18 01:38:02 +01:00
|
|
|
if ($shown && commentsopen($page)) {
|
2010-02-15 00:27:47 +01:00
|
|
|
$template->param(addcommenturl => addcommenturl($page));
|
2008-11-23 13:04:00 +01:00
|
|
|
}
|
|
|
|
}
|
2008-12-11 02:33:43 +01:00
|
|
|
|
2010-02-15 00:09:28 +01:00
|
|
|
if ($shown) {
|
|
|
|
if ($template->query(name => 'commentsurl')) {
|
2008-12-21 18:15:49 +01:00
|
|
|
$template->param(commentsurl =>
|
2010-11-29 20:07:26 +01:00
|
|
|
urlto($page).'#comments');
|
2008-12-21 18:15:49 +01:00
|
|
|
}
|
|
|
|
|
2010-02-15 00:09:28 +01:00
|
|
|
if ($template->query(name => 'atomcommentsurl') && $config{usedirs}) {
|
2008-12-21 18:15:49 +01:00
|
|
|
# This will 404 until there are some comments, but I
|
|
|
|
# think that's probably OK...
|
|
|
|
$template->param(atomcommentsurl =>
|
2010-11-29 20:07:26 +01:00
|
|
|
urlto($page).'comments.atom');
|
2008-12-21 18:15:49 +01:00
|
|
|
}
|
|
|
|
|
2010-02-15 00:09:28 +01:00
|
|
|
if ($template->query(name => 'commentslink')) {
|
2010-02-15 00:27:47 +01:00
|
|
|
my $num=num_comments($page, $config{srcdir});
|
|
|
|
my $link;
|
|
|
|
if ($num > 0) {
|
|
|
|
$link = htmllink($page, $params{destpage}, $page,
|
|
|
|
linktext => sprintf(ngettext("%i comment", "%i comments", $num), $num),
|
2008-12-18 01:38:02 +01:00
|
|
|
anchor => "comments",
|
2010-02-15 00:27:47 +01:00
|
|
|
noimageinline => 1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
elsif (commentsopen($page)) {
|
|
|
|
$link = "<a href=\"".addcommenturl($page)."\">".
|
|
|
|
#translators: Here "Comment" is a verb;
|
|
|
|
#translators: the user clicks on it to
|
|
|
|
#translators: post a comment.
|
|
|
|
gettext("Comment").
|
|
|
|
"</a>";
|
|
|
|
}
|
|
|
|
$template->param(commentslink => $link)
|
|
|
|
if defined $link;
|
2008-12-18 01:38:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-10 12:29:56 +01:00
|
|
|
# everything below this point is only relevant to the comments
|
|
|
|
# themselves
|
|
|
|
if (!exists $commentstate{$page}) {
|
|
|
|
return;
|
|
|
|
}
|
2009-03-26 21:45:53 +01:00
|
|
|
|
|
|
|
if ($template->query(name => 'commentid')) {
|
|
|
|
$template->param(commentid => page_to_id($page));
|
|
|
|
}
|
2009-01-10 12:29:56 +01:00
|
|
|
|
2008-12-11 02:33:43 +01:00
|
|
|
if ($template->query(name => 'commentuser')) {
|
|
|
|
$template->param(commentuser =>
|
2008-12-21 02:55:38 +01:00
|
|
|
$commentstate{$page}{commentuser});
|
2008-12-11 02:33:43 +01:00
|
|
|
}
|
|
|
|
|
2008-12-18 20:57:25 +01:00
|
|
|
if ($template->query(name => 'commentopenid')) {
|
|
|
|
$template->param(commentopenid =>
|
2008-12-21 02:55:38 +01:00
|
|
|
$commentstate{$page}{commentopenid});
|
2008-12-18 20:57:25 +01:00
|
|
|
}
|
|
|
|
|
2008-12-11 02:33:43 +01:00
|
|
|
if ($template->query(name => 'commentip')) {
|
|
|
|
$template->param(commentip =>
|
2008-12-21 02:55:38 +01:00
|
|
|
$commentstate{$page}{commentip});
|
2008-12-11 02:33:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($template->query(name => 'commentauthor')) {
|
|
|
|
$template->param(commentauthor =>
|
2008-12-21 02:55:38 +01:00
|
|
|
$commentstate{$page}{commentauthor});
|
2008-12-11 02:33:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($template->query(name => 'commentauthorurl')) {
|
|
|
|
$template->param(commentauthorurl =>
|
2008-12-21 02:55:38 +01:00
|
|
|
$commentstate{$page}{commentauthorurl});
|
2008-12-11 02:33:43 +01:00
|
|
|
}
|
2009-01-10 12:31:24 +01:00
|
|
|
|
2011-03-05 08:33:07 +01:00
|
|
|
if ($template->query(name => 'commentauthoravatar')) {
|
|
|
|
$template->param(commentauthoravatar =>
|
|
|
|
$commentstate{$page}{commentauthoravatar});
|
|
|
|
}
|
|
|
|
|
2009-01-10 12:31:24 +01:00
|
|
|
if ($template->query(name => 'removeurl') &&
|
|
|
|
IkiWiki::Plugin::remove->can("check_canremove") &&
|
|
|
|
length $config{cgiurl}) {
|
|
|
|
$template->param(removeurl => IkiWiki::cgiurl(do => 'remove',
|
|
|
|
page => $page));
|
|
|
|
$template->param(have_actions => 1);
|
|
|
|
}
|
2008-12-17 21:22:16 +01:00
|
|
|
}
|
2008-11-23 13:04:00 +01:00
|
|
|
|
2010-02-15 00:27:47 +01:00
|
|
|
sub addcommenturl ($) {
|
|
|
|
my $page=shift;
|
|
|
|
|
|
|
|
return IkiWiki::cgiurl(do => 'comment', page => $page);
|
|
|
|
}
|
|
|
|
|
2009-12-30 21:41:17 +01:00
|
|
|
sub num_comments ($$) {
|
2009-01-25 21:42:13 +01:00
|
|
|
my $page=shift;
|
|
|
|
my $dir=shift;
|
|
|
|
|
2009-12-30 21:41:17 +01:00
|
|
|
my @comments=glob("$dir/$page/$config{comments_pagename}*._comment");
|
2010-05-17 23:06:13 +02:00
|
|
|
return int @comments;
|
2009-12-30 21:41:17 +01:00
|
|
|
}
|
|
|
|
|
2010-05-07 19:47:29 +02:00
|
|
|
sub unique_comment_location ($$$$) {
|
2009-12-30 21:41:17 +01:00
|
|
|
my $page=shift;
|
|
|
|
eval q{use Digest::MD5 'md5_hex'};
|
|
|
|
error($@) if $@;
|
2010-03-10 01:55:19 +01:00
|
|
|
my $content_md5=md5_hex(Encode::encode_utf8(shift));
|
2009-12-30 21:41:17 +01:00
|
|
|
my $dir=shift;
|
2010-05-07 19:47:29 +02:00
|
|
|
my $ext=shift || "._comment";
|
2009-12-30 21:41:17 +01:00
|
|
|
|
2009-01-25 21:42:13 +01:00
|
|
|
my $location;
|
2009-12-30 21:41:17 +01:00
|
|
|
my $i = num_comments($page, $dir);
|
2009-01-25 21:42:13 +01:00
|
|
|
do {
|
|
|
|
$i++;
|
2009-12-30 21:41:17 +01:00
|
|
|
$location = "$page/$config{comments_pagename}${i}_${content_md5}";
|
2010-05-07 19:47:29 +02:00
|
|
|
} while (-e "$dir/$location$ext");
|
2009-01-25 21:42:13 +01:00
|
|
|
|
|
|
|
return $location;
|
|
|
|
}
|
|
|
|
|
2009-03-26 21:45:53 +01:00
|
|
|
sub page_to_id ($) {
|
|
|
|
# Converts a comment page name into a unique, legal html id
|
2010-03-10 01:55:50 +01:00
|
|
|
# attribute value, that can be used as an anchor to link to the
|
2009-03-26 21:45:53 +01:00
|
|
|
# comment.
|
|
|
|
my $page=shift;
|
|
|
|
|
2009-03-27 18:44:31 +01:00
|
|
|
eval q{use Digest::MD5 'md5_hex'};
|
|
|
|
error($@) if $@;
|
2009-03-26 21:45:53 +01:00
|
|
|
|
2010-03-12 21:01:24 +01:00
|
|
|
return "comment-".md5_hex(Encode::encode_utf8(($page)));
|
2009-03-26 21:45:53 +01:00
|
|
|
}
|
|
|
|
|
2008-11-16 19:23:23 +01:00
|
|
|
package IkiWiki::PageSpec;
|
|
|
|
|
2008-11-17 12:16:31 +01:00
|
|
|
sub match_postcomment ($$;@) {
|
2008-11-16 19:23:23 +01:00
|
|
|
my $page = shift;
|
|
|
|
my $glob = shift;
|
|
|
|
|
2008-12-12 21:05:26 +01:00
|
|
|
if (! $postcomment) {
|
2008-11-16 19:23:23 +01:00
|
|
|
return IkiWiki::FailReason->new("not posting a comment");
|
|
|
|
}
|
2010-05-07 01:19:51 +02:00
|
|
|
return match_glob($page, $glob, @_);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub match_comment ($$;@) {
|
|
|
|
my $page = shift;
|
|
|
|
my $glob = shift;
|
|
|
|
|
2010-11-12 05:36:03 +01:00
|
|
|
if (! $postcomment) {
|
|
|
|
# To see if it's a comment, check the source file type.
|
|
|
|
# Deal with comments that were just deleted.
|
|
|
|
my $source=exists $IkiWiki::pagesources{$page} ?
|
|
|
|
$IkiWiki::pagesources{$page} :
|
|
|
|
$IkiWiki::delpagesources{$page};
|
|
|
|
my $type=defined $source ? IkiWiki::pagetype($source) : undef;
|
|
|
|
if (! defined $type || $type ne "_comment") {
|
|
|
|
return IkiWiki::FailReason->new("$page is not a comment");
|
|
|
|
}
|
2010-05-07 01:19:51 +02:00
|
|
|
}
|
2010-05-07 19:59:08 +02:00
|
|
|
|
2011-03-28 17:41:13 +02:00
|
|
|
return match_glob($page, "$glob/*", internal => 1, @_);
|
2010-05-07 01:19:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sub match_comment_pending ($$;@) {
|
|
|
|
my $page = shift;
|
|
|
|
my $glob = shift;
|
2010-05-07 19:59:08 +02:00
|
|
|
|
2010-05-18 19:28:35 +02:00
|
|
|
my $source=exists $IkiWiki::pagesources{$page} ?
|
|
|
|
$IkiWiki::pagesources{$page} :
|
|
|
|
$IkiWiki::delpagesources{$page};
|
2010-05-22 00:03:21 +02:00
|
|
|
my $type=defined $source ? IkiWiki::pagetype($source) : undef;
|
2010-05-18 20:16:58 +02:00
|
|
|
if (! defined $type || $type ne "_comment_pending") {
|
2010-05-07 19:59:08 +02:00
|
|
|
return IkiWiki::FailReason->new("$page is not a pending comment");
|
|
|
|
}
|
|
|
|
|
2011-03-28 17:41:13 +02:00
|
|
|
return match_glob($page, "$glob/*", internal => 1, @_);
|
2008-11-16 19:23:23 +01:00
|
|
|
}
|
|
|
|
|
2008-09-21 20:33:45 +02:00
|
|
|
1
|