summaryrefslogtreecommitdiff
path: root/IkiWiki
diff options
context:
space:
mode:
authorJoey Hess <joey@gnu.kitenet.net>2009-04-04 17:27:48 -0400
committerJoey Hess <joey@gnu.kitenet.net>2009-04-04 17:27:48 -0400
commit8e92468eae9ac0ab8161a0c71ff6c6a0a8aef07a (patch)
tree9e26465e0ca98a5f3cbc6c72a0cace4bf83b93db /IkiWiki
parent78a69e5bd632eb86ef8135e9c1d05d2c48b43362 (diff)
parent08fda4c9d374de1d3de3172a192d4d915d3dc0c1 (diff)
Merge branch 'master'
Conflicts: doc/ikiwiki-makerepo.mdwn
Diffstat (limited to 'IkiWiki')
-rw-r--r--IkiWiki/CGI.pm133
-rw-r--r--IkiWiki/Plugin/404.pm78
-rw-r--r--IkiWiki/Plugin/aggregate.pm171
-rw-r--r--IkiWiki/Plugin/amazon_s3.pm34
-rw-r--r--IkiWiki/Plugin/anonok.pm14
-rw-r--r--IkiWiki/Plugin/attachment.pm92
-rw-r--r--IkiWiki/Plugin/autoindex.pm18
-rw-r--r--IkiWiki/Plugin/blogspam.pm116
-rw-r--r--IkiWiki/Plugin/brokenlinks.pm14
-rw-r--r--IkiWiki/Plugin/bzr.pm76
-rw-r--r--IkiWiki/Plugin/calendar.pm32
-rw-r--r--IkiWiki/Plugin/camelcase.pm34
-rw-r--r--IkiWiki/Plugin/color.pm22
-rw-r--r--IkiWiki/Plugin/comments.pm840
-rw-r--r--IkiWiki/Plugin/conditional.pm34
-rw-r--r--IkiWiki/Plugin/creole.pm14
-rw-r--r--IkiWiki/Plugin/cutpaste.pm22
-rw-r--r--IkiWiki/Plugin/ddate.pm14
-rw-r--r--IkiWiki/Plugin/editdiff.pm18
-rw-r--r--IkiWiki/Plugin/editpage.pm70
-rw-r--r--IkiWiki/Plugin/edittemplate.pm26
-rw-r--r--IkiWiki/Plugin/embed.pm22
-rw-r--r--IkiWiki/Plugin/external.pm60
-rw-r--r--IkiWiki/Plugin/favicon.pm14
-rw-r--r--IkiWiki/Plugin/filecheck.pm32
-rw-r--r--IkiWiki/Plugin/format.pm30
-rw-r--r--IkiWiki/Plugin/fortune.pm14
-rw-r--r--IkiWiki/Plugin/git.pm228
-rw-r--r--IkiWiki/Plugin/goodstuff.pm12
-rw-r--r--IkiWiki/Plugin/google.pm18
-rw-r--r--IkiWiki/Plugin/googlecalendar.pm55
-rw-r--r--IkiWiki/Plugin/goto.pm75
-rw-r--r--IkiWiki/Plugin/graphviz.pm20
-rw-r--r--IkiWiki/Plugin/haiku.pm14
-rw-r--r--IkiWiki/Plugin/hnb.pm14
-rw-r--r--IkiWiki/Plugin/html.pm14
-rw-r--r--IkiWiki/Plugin/htmlbalance.pm58
-rw-r--r--IkiWiki/Plugin/htmlscrubber.pm18
-rw-r--r--IkiWiki/Plugin/htmltidy.pm14
-rw-r--r--IkiWiki/Plugin/httpauth.pm14
-rw-r--r--IkiWiki/Plugin/img.pm16
-rw-r--r--IkiWiki/Plugin/inline.pm166
-rw-r--r--IkiWiki/Plugin/link.pm26
-rw-r--r--IkiWiki/Plugin/linkmap.pm22
-rw-r--r--IkiWiki/Plugin/listdirectives.pm31
-rw-r--r--IkiWiki/Plugin/lockedit.pm72
-rw-r--r--IkiWiki/Plugin/map.pm14
-rw-r--r--IkiWiki/Plugin/mdwn.pm14
-rw-r--r--IkiWiki/Plugin/mercurial.pm58
-rw-r--r--IkiWiki/Plugin/meta.pm88
-rw-r--r--IkiWiki/Plugin/mirrorlist.pm18
-rw-r--r--IkiWiki/Plugin/monotone.pm79
-rw-r--r--IkiWiki/Plugin/more.pm12
-rw-r--r--IkiWiki/Plugin/norcs.pm48
-rw-r--r--IkiWiki/Plugin/opendiscussion.pm14
-rw-r--r--IkiWiki/Plugin/openid.pm38
-rw-r--r--IkiWiki/Plugin/orphans.pm14
-rw-r--r--IkiWiki/Plugin/otl.pm18
-rw-r--r--IkiWiki/Plugin/pagecount.pm14
-rw-r--r--IkiWiki/Plugin/pagestats.pm14
-rw-r--r--IkiWiki/Plugin/pagetemplate.pm18
-rw-r--r--IkiWiki/Plugin/parentlinks.pm18
-rw-r--r--IkiWiki/Plugin/passwordauth.pm28
-rw-r--r--IkiWiki/Plugin/pingee.pm14
-rw-r--r--IkiWiki/Plugin/pinger.pm20
-rw-r--r--IkiWiki/Plugin/poll.pm18
-rw-r--r--IkiWiki/Plugin/polygen.pm14
-rw-r--r--IkiWiki/Plugin/postsparkline.pm14
-rw-r--r--IkiWiki/Plugin/prettydate.pm18
-rw-r--r--IkiWiki/Plugin/progress.pm18
-rw-r--r--IkiWiki/Plugin/rawhtml.pm10
-rw-r--r--IkiWiki/Plugin/recentchanges.pm81
-rw-r--r--IkiWiki/Plugin/recentchangesdiff.pm19
-rw-r--r--IkiWiki/Plugin/relativedate.pm60
-rw-r--r--IkiWiki/Plugin/remove.pm40
-rw-r--r--IkiWiki/Plugin/rename.pm87
-rw-r--r--IkiWiki/Plugin/repolist.pm51
-rw-r--r--IkiWiki/Plugin/search.pm42
-rw-r--r--IkiWiki/Plugin/shortcut.pm45
-rw-r--r--IkiWiki/Plugin/sidebar.pm18
-rw-r--r--IkiWiki/Plugin/signinedit.pm14
-rw-r--r--IkiWiki/Plugin/skeleton.pm.example113
-rw-r--r--IkiWiki/Plugin/smiley.pm24
-rw-r--r--IkiWiki/Plugin/sparkline.pm16
-rw-r--r--IkiWiki/Plugin/svn.pm54
-rw-r--r--IkiWiki/Plugin/table.pm70
-rw-r--r--IkiWiki/Plugin/tag.pm53
-rw-r--r--IkiWiki/Plugin/template.pm23
-rw-r--r--IkiWiki/Plugin/testpagespec.pm14
-rw-r--r--IkiWiki/Plugin/teximg.pm34
-rw-r--r--IkiWiki/Plugin/textile.pm14
-rw-r--r--IkiWiki/Plugin/tla.pm46
-rw-r--r--IkiWiki/Plugin/toc.pm16
-rw-r--r--IkiWiki/Plugin/toggle.pm95
-rw-r--r--IkiWiki/Plugin/txt.pm8
-rw-r--r--IkiWiki/Plugin/typography.pm18
-rw-r--r--IkiWiki/Plugin/underlay.pm40
-rw-r--r--IkiWiki/Plugin/version.pm18
-rw-r--r--IkiWiki/Plugin/websetup.pm40
-rw-r--r--IkiWiki/Plugin/wikitext.pm14
-rw-r--r--IkiWiki/Plugin/wmd.pm52
-rw-r--r--IkiWiki/Receive.pm135
-rw-r--r--IkiWiki/Render.pm37
-rw-r--r--IkiWiki/Setup.pm12
-rw-r--r--IkiWiki/Setup/Automator.pm40
-rw-r--r--IkiWiki/Setup/Standard.pm18
-rw-r--r--IkiWiki/UserInfo.pm44
-rw-r--r--IkiWiki/Wrapper.pm75
108 files changed, 3376 insertions, 1768 deletions
diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm
index dac522eea..04f24b04f 100644
--- a/IkiWiki/CGI.pm
+++ b/IkiWiki/CGI.pm
@@ -9,7 +9,7 @@ use IkiWiki::UserInfo;
use open qw{:utf8 :std};
use Encode;
-sub printheader ($) { #{{{
+sub printheader ($) {
my $session=shift;
if ($config{sslcookie}) {
@@ -19,9 +19,9 @@ sub printheader ($) { #{{{
print $session->header(-charset => 'utf-8',
-cookie => $session->cookie(-httponly => 1));
}
-} #}}}
+}
-sub showform ($$$$;@) { #{{{
+sub showform ($$$$;@) {
my $form=shift;
my $buttons=shift;
my $session=shift;
@@ -38,7 +38,7 @@ sub showform ($$$$;@) { #{{{
print misctemplate($form->title, $form->render(submit => $buttons), @_);
}
-sub redirect ($$) { #{{{
+sub redirect ($$) {
my $q=shift;
my $url=shift;
if (! $config{w3mmode}) {
@@ -48,9 +48,9 @@ sub redirect ($$) { #{{{
print "Content-type: text/plain\n";
print "W3m-control: GOTO $url\n\n";
}
-} #}}}
+}
-sub decode_cgi_utf8 ($) { #{{{
+sub decode_cgi_utf8 ($) {
# decode_form_utf8 method is needed for 5.10
if ($] < 5.01) {
my $cgi = shift;
@@ -58,9 +58,9 @@ sub decode_cgi_utf8 ($) { #{{{
$cgi->param($f, map { decode_utf8 $_ } $cgi->param($f));
}
}
-} #}}}
+}
-sub decode_form_utf8 ($) { #{{{
+sub decode_form_utf8 ($) {
if ($] >= 5.01) {
my $form = shift;
foreach my $f ($form->field) {
@@ -70,11 +70,11 @@ sub decode_form_utf8 ($) { #{{{
);
}
}
-} #}}}
+}
# Check if the user is signed in. If not, redirect to the signin form and
# save their place to return to later.
-sub needsignin ($$) { #{{{
+sub needsignin ($$) {
my $q=shift;
my $session=shift;
@@ -85,9 +85,9 @@ sub needsignin ($$) { #{{{
cgi_savesession($session);
exit;
}
-} #}}}
+}
-sub cgi_signin ($$) { #{{{
+sub cgi_signin ($$) {
my $q=shift;
my $session=shift;
@@ -127,9 +127,9 @@ sub cgi_signin ($$) { #{{{
}
showform($form, $buttons, $session, $q);
-} #}}}
+}
-sub cgi_postsignin ($$) { #{{{
+sub cgi_postsignin ($$) {
my $q=shift;
my $session=shift;
@@ -142,11 +142,16 @@ sub cgi_postsignin ($$) { #{{{
exit;
}
else {
- error(gettext("login failed, perhaps you need to turn on cookies?"));
+ if ($config{sslcookie} && ! $q->https()) {
+ error(gettext("probable misconfiguration: sslcookie is set, but you are attepting to login via http, not https"));
+ }
+ else {
+ error(gettext("login failed, perhaps you need to turn on cookies?"));
+ }
}
-} #}}}
+}
-sub cgi_prefs ($$) { #{{{
+sub cgi_prefs ($$) {
my $q=shift;
my $session=shift;
@@ -203,25 +208,9 @@ sub cgi_prefs ($$) { #{{{
my $user_name=$session->param("name");
- # XXX deprecated, should be removed eventually
- $form->field(name => "banned_users", size => 50, fieldset => "admin");
- if (! is_admin($user_name)) {
- $form->field(name => "banned_users", type => "hidden");
- }
if (! $form->submitted) {
$form->field(name => "email", force => 1,
value => userinfo_get($user_name, "email"));
- if (is_admin($user_name)) {
- my $value=join(" ", get_banned_users());
- if (length $value) {
- $form->field(name => "banned_users", force => 1,
- value => join(" ", get_banned_users()),
- comment => "deprecated; please move to banned_users in setup file");
- }
- else {
- $form->field(name => "banned_users", type => "hidden");
- }
- }
}
if ($form->submitted eq 'Logout') {
@@ -239,48 +228,48 @@ sub cgi_prefs ($$) { #{{{
error("failed to set email");
}
- # XXX deprecated, should be removed eventually
- if (is_admin($user_name)) {
- set_banned_users(grep { ! is_admin($_) }
- split(' ',
- $form->field("banned_users"))) ||
- error("failed saving changes");
- if (! length $form->field("banned_users")) {
- $form->field(name => "banned_users", type => "hidden");
- }
- }
-
$form->text(gettext("Preferences saved."));
}
showform($form, $buttons, $session, $q);
-} #}}}
+}
-sub check_banned ($$) { #{{{
+sub cgi_custom_failure ($$) {
+ my $header=shift;
+ my $message=shift;
+
+ print $header;
+ print $message;
+
+ # Internet Explod^Hrer won't show custom 404 responses
+ # unless they're >= 512 bytes
+ print ' ' x 512;
+
+ exit;
+}
+
+sub check_banned ($$) {
my $q=shift;
my $session=shift;
my $name=$session->param("name");
if (defined $name) {
- # XXX banned in userinfo is deprecated, should be removed
- # eventually, and only banned_users be checked.
- if (userinfo_get($session->param("name"), "banned") ||
- grep { $name eq $_ } @{$config{banned_users}}) {
- print $q->header(-status => "403 Forbidden");
+ if (grep { $name eq $_ } @{$config{banned_users}}) {
$session->delete();
- print gettext("You are banned.");
cgi_savesession($session);
- exit;
+ cgi_custom_failure(
+ $q->header(-status => "403 Forbidden"),
+ gettext("You are banned."));
}
}
}
-sub cgi_getsession ($) { #{{{
+sub cgi_getsession ($) {
my $q=shift;
- eval q{use CGI::Session};
+ eval q{use CGI::Session; use HTML::Entities};
error($@) if $@;
- CGI::Session->name("ikiwiki_session_".encode_utf8($config{wikiname}));
+ CGI::Session->name("ikiwiki_session_".encode_entities($config{wikiname}));
my $oldmask=umask(077);
my $session = eval {
@@ -294,18 +283,34 @@ sub cgi_getsession ($) { #{{{
umask($oldmask);
return $session;
-} #}}}
+}
+
+# To guard against CSRF, the user's session id (sid)
+# can be stored on a form. This function will check
+# (for logged in users) that the sid on the form matches
+# the session id in the cookie.
+sub checksessionexpiry ($$) {
+ my $q=shift;
+ my $session = shift;
-sub cgi_savesession ($) { #{{{
+ if (defined $session->param("name")) {
+ my $sid=$q->param('sid');
+ if (! defined $sid || $sid ne $session->id) {
+ error(gettext("Your login session has expired."));
+ }
+ }
+}
+
+sub cgi_savesession ($) {
my $session=shift;
# Force session flush with safe umask.
my $oldmask=umask(077);
$session->flush;
umask($oldmask);
-} #}}}
+}
-sub cgi (;$$) { #{{{
+sub cgi (;$$) {
my $q=shift;
my $session=shift;
@@ -331,7 +336,7 @@ sub cgi (;$$) { #{{{
error("\"do\" parameter missing");
}
}
-
+
# Need to lock the wiki before getting a session.
lockwiki();
loadindex();
@@ -375,16 +380,16 @@ sub cgi (;$$) { #{{{
else {
error("unknown do parameter");
}
-} #}}}
+}
# Does not need to be called directly; all errors will go through here.
-sub cgierror ($) { #{{{
+sub cgierror ($) {
my $message=shift;
print "Content-type: text/html\n\n";
print misctemplate(gettext("Error"),
"<p class=\"error\">".gettext("Error").": $message</p>");
die $@;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/404.pm b/IkiWiki/Plugin/404.pm
new file mode 100644
index 000000000..bae9e15d1
--- /dev/null
+++ b/IkiWiki/Plugin/404.pm
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+# Copyright © 2009 Simon McVittie <http://smcv.pseudorandom.co.uk/>
+# Licensed under the GNU GPL, version 2, or any later version published by the
+# Free Software Foundation
+package IkiWiki::Plugin::404;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "cgi", id => '404', call => \&cgi);
+ IkiWiki::loadplugin("goto");
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ # not really a matter of safety, but enabling/disabling
+ # through a web interface is useless - it needs web
+ # server admin action too
+ safe => 0,
+ rebuild => 0,
+ }
+}
+
+sub cgi_page_from_404 ($$$) {
+ my $path = shift;
+ my $baseurl = shift;
+ my $usedirs = shift;
+
+ # fail if missing from environment or whatever
+ return undef unless defined $path;
+ return undef unless defined $baseurl;
+
+ # with usedirs on, path is like /~fred/foo/bar/ or /~fred/foo/bar or
+ # /~fred/foo/bar/index.html
+ # with usedirs off, path is like /~fred/foo/bar.html
+ # baseurl is like 'http://people.example.com/~fred'
+
+ # convert baseurl to ~fred
+ unless ($baseurl =~ s{^https?://[^/]+/?}{}) {
+ return undef;
+ }
+
+ # convert path to /~fred/foo/bar
+ if ($usedirs) {
+ $path =~ s/\/*(?:index\.$config{htmlext})?$//;
+ }
+ else {
+ $path =~ s/\.$config{htmlext}$//;
+ }
+
+ # remove /~fred/
+ unless ($path =~ s{^/*\Q$baseurl\E/*}{}) {
+ return undef;
+ }
+
+ # special case for the index
+ unless ($path) {
+ return 'index';
+ }
+
+ return $path;
+}
+
+sub cgi ($) {
+ my $cgi=shift;
+
+ if (exists $ENV{REDIRECT_STATUS} &&
+ $ENV{REDIRECT_STATUS} eq '404') {
+ my $page = cgi_page_from_404($ENV{REDIRECT_URL},
+ $config{url}, $config{usedirs});
+ IkiWiki::Plugin::goto::cgi_goto($cgi, $page);
+ }
+}
+
+1;
diff --git a/IkiWiki/Plugin/aggregate.pm b/IkiWiki/Plugin/aggregate.pm
index 26c5cc9ae..e1baae666 100644
--- a/IkiWiki/Plugin/aggregate.pm
+++ b/IkiWiki/Plugin/aggregate.pm
@@ -4,7 +4,7 @@ package IkiWiki::Plugin::aggregate;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use HTML::Parser;
use HTML::Tagset;
use HTML::Entities;
@@ -14,7 +14,7 @@ use open qw{:utf8 :std};
my %feeds;
my %guids;
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "aggregate", call => \&getopt);
hook(type => "getsetup", id => "aggregate", call => \&getsetup);
hook(type => "checkconfig", id => "aggregate", call => \&checkconfig);
@@ -26,9 +26,9 @@ sub import { #{{{
if (exists $config{aggregate_webtrigger} && $config{aggregate_webtrigger}) {
hook(type => "cgi", id => "aggregate", call => \&cgi);
}
-} # }}}
+}
-sub getopt () { #{{{
+sub getopt () {
eval q{use Getopt::Long};
error($@) if $@;
Getopt::Long::Configure('pass_through');
@@ -36,9 +36,9 @@ sub getopt () { #{{{
"aggregate" => \$config{aggregate},
"aggregateinternal!" => \$config{aggregateinternal},
);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -46,7 +46,7 @@ sub getsetup () { #{{{
},
aggregateinternal => {
type => "boolean",
- example => 0,
+ example => 1,
description => "enable aggregation to internal pages?",
safe => 0, # enabling needs manual transition
rebuild => 0,
@@ -58,16 +58,20 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
+
+sub checkconfig () {
+ if (! defined $config{aggregateinternal}) {
+ $config{aggregateinternal}=1;
+ }
-sub checkconfig () { #{{{
if ($config{aggregate} && ! ($config{post_commit} &&
IkiWiki::commit_hook_enabled())) {
launchaggregation();
}
-} #}}}
+}
-sub cgi ($) { #{{{
+sub cgi ($) {
my $cgi=shift;
if (defined $cgi->param('do') &&
@@ -90,9 +94,9 @@ sub cgi ($) { #{{{
}
exit 0;
}
-} #}}}
+}
-sub launchaggregation () { #{{{
+sub launchaggregation () {
# See if any feeds need aggregation.
loadstate();
my @feeds=needsaggregate();
@@ -135,16 +139,16 @@ sub launchaggregation () { #{{{
unlockaggregate();
return 1;
-} #}}}
+}
# Pages with extension _aggregated have plain html markup, pass through.
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
return $params{content};
-} #}}}
+}
# Used by ikiwiki-transition aggregateinternal.
-sub migrate_to_internal { #{{{
+sub migrate_to_internal {
if (! lockaggregate()) {
error("an aggregation process is currently running");
}
@@ -190,9 +194,9 @@ sub migrate_to_internal { #{{{
IkiWiki::unlockwiki;
unlockaggregate();
-} #}}}
+}
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
loadstate();
@@ -206,9 +210,9 @@ sub needsbuild (@) { #{{{
markunseen($feed->{sourcepage});
}
}
-} # }}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
foreach my $required (qw{name url}) {
@@ -245,6 +249,7 @@ sub preprocess (@) { #{{{
$feed->{template}=$params{template} . ".tmpl";
delete $feed->{unseen};
$feed->{lastupdate}=0 unless defined $feed->{lastupdate};
+ $feed->{lasttry}=$feed->{lastupdate} unless defined $feed->{lasttry};
$feed->{numposts}=0 unless defined $feed->{numposts};
$feed->{newposts}=0 unless defined $feed->{newposts};
$feed->{message}=gettext("new feed") unless defined $feed->{message};
@@ -265,9 +270,9 @@ sub preprocess (@) { #{{{
($feed->{newposts} ? "; ".$feed->{newposts}.
" ".gettext("new") : "").
")";
-} # }}}
+}
-sub delete (@) { #{{{
+sub delete (@) {
my @files=@_;
# Remove feed data for removed pages.
@@ -275,9 +280,9 @@ sub delete (@) { #{{{
my $page=pagename($file);
markunseen($page);
}
-} #}}}
+}
-sub markunseen ($) { #{{{
+sub markunseen ($) {
my $page=shift;
foreach my $id (keys %feeds) {
@@ -285,11 +290,11 @@ sub markunseen ($) { #{{{
$feeds{$id}->{unseen}=1;
}
}
-} #}}}
+}
my $state_loaded=0;
-sub loadstate () { #{{{
+sub loadstate () {
return if $state_loaded;
$state_loaded=1;
if (-e "$config{wikistatedir}/aggregate") {
@@ -323,9 +328,9 @@ sub loadstate () { #{{{
close IN;
}
-} #}}}
+}
-sub savestate () { #{{{
+sub savestate () {
return unless $state_loaded;
garbage_collect();
my $newfile="$config{wikistatedir}/aggregate.new";
@@ -342,7 +347,8 @@ sub savestate () { #{{{
push @line, "tag=$_" foreach @{$data->{tags}};
}
else {
- push @line, "$field=".$data->{$field};
+ push @line, "$field=".$data->{$field}
+ if defined $data->{$field};
}
}
print OUT join(" ", @line)."\n" || error("write $newfile: $!", $cleanup);
@@ -350,9 +356,9 @@ sub savestate () { #{{{
close OUT || error("save $newfile: $!", $cleanup);
rename($newfile, "$config{wikistatedir}/aggregate") ||
error("rename $newfile: $!", $cleanup);
-} #}}}
+}
-sub garbage_collect () { #{{{
+sub garbage_collect () {
foreach my $name (keys %feeds) {
# remove any feeds that were not seen while building the pages
# that used to contain them
@@ -375,9 +381,9 @@ sub garbage_collect () { #{{{
delete $guid->{md5};
}
}
-} #}}}
+}
-sub mergestate () { #{{{
+sub mergestate () {
# Load the current state in from disk, and merge into it
# values from the state in memory that might have changed
# during aggregation.
@@ -390,8 +396,8 @@ sub mergestate () { #{{{
# fields.
foreach my $name (keys %myfeeds) {
if (exists $feeds{$name}) {
- foreach my $field (qw{message lastupdate numposts
- newposts error}) {
+ foreach my $field (qw{message lastupdate lasttry
+ numposts newposts error}) {
$feeds{$name}->{$field}=$myfeeds{$name}->{$field};
}
}
@@ -407,15 +413,15 @@ sub mergestate () { #{{{
$guids{$guid}=$myguids{$guid};
}
}
-} #}}}
+}
-sub clearstate () { #{{{
+sub clearstate () {
%feeds=();
%guids=();
$state_loaded=0;
-} #}}}
+}
-sub expire () { #{{{
+sub expire () {
foreach my $feed (values %feeds) {
next unless $feed->{expireage} || $feed->{expirecount};
my $count=0;
@@ -444,24 +450,24 @@ sub expire () { #{{{
}
}
}
-} #}}}
+}
-sub needsaggregate () { #{{{
+sub needsaggregate () {
return values %feeds if $config{rebuild};
return grep { time - $_->{lastupdate} >= $_->{updateinterval} } values %feeds;
-} #}}}
+}
-sub aggregate (@) { #{{{
+sub aggregate (@) {
eval q{use XML::Feed};
error($@) if $@;
eval q{use URI::Fetch};
error($@) if $@;
foreach my $feed (@_) {
- $feed->{lastupdate}=time;
+ $feed->{lasttry}=time;
$feed->{newposts}=0;
- $feed->{message}=sprintf(gettext("processed ok at %s"),
- displaytime($feed->{lastupdate}));
+ $feed->{message}=sprintf(gettext("last checked %s"),
+ displaytime($feed->{lasttry}));
$feed->{error}=0;
debug(sprintf(gettext("checking feed %s ..."), $feed->{name}));
@@ -483,6 +489,10 @@ sub aggregate (@) { #{{{
debug($feed->{message});
next;
}
+
+ # lastupdate is only set if we were able to contact the server
+ $feed->{lastupdate}=$feed->{lasttry};
+
if ($res->status == URI::Fetch::URI_GONE()) {
$feed->{message}=gettext("feed not found");
$feed->{error}=1;
@@ -496,15 +506,19 @@ sub aggregate (@) { #{{{
# that contains invalid UTF-8 sequences. Convert
# feed to ascii to try to work around.
$feed->{message}.=" ".sprintf(gettext("(invalid UTF-8 stripped from feed)"));
- $content=Encode::decode_utf8($content, 0);
- $f=eval{XML::Feed->parse(\$content)};
+ $f=eval {
+ $content=Encode::decode_utf8($content, 0);
+ XML::Feed->parse(\$content)
+ };
}
if ($@) {
# Another possibility is badly escaped entities.
$feed->{message}.=" ".sprintf(gettext("(feed entities escaped)"));
$content=~s/\&(?!amp)(\w+);/&amp;$1;/g;
- $content=Encode::decode_utf8($content, 0);
- $f=eval{XML::Feed->parse(\$content)};
+ $f=eval {
+ $content=Encode::decode_utf8($content, 0);
+ XML::Feed->parse(\$content)
+ };
}
if ($@) {
$feed->{message}=gettext("feed crashed XML::Feed!")." ($@)";
@@ -520,10 +534,15 @@ sub aggregate (@) { #{{{
}
foreach my $entry ($f->entries) {
- my $content=$content=$entry->content->body;
+ # XML::Feed doesn't work around XML::Atom's bizarre
+ # API, so we will. Real unicode strings? Yes please.
+ # See [[bugs/Aggregated_Atom_feeds_are_double-encoded]]
+ local $XML::Atom::ForceUnicode = 1;
+
+ my $c=$entry->content;
# atom feeds may have no content, only a summary
- if (! defined $content && ref $entry->summary) {
- $content=$entry->summary->body;
+ if (! defined $c && ref $entry->summary) {
+ $c=$entry->summary;
}
add_page(
@@ -531,15 +550,16 @@ sub aggregate (@) { #{{{
copyright => $f->copyright,
title => defined $entry->title ? decode_entities($entry->title) : "untitled",
link => $entry->link,
- content => defined $content ? $content : "",
+ content => (defined $c && defined $c->body) ? $c->body : "",
guid => defined $entry->id ? $entry->id : time."_".$feed->{name},
ctime => $entry->issued ? ($entry->issued->epoch || time) : time,
+ base => (defined $c && $c->can("base")) ? $c->base : undef,
);
}
}
-} #}}}
+}
-sub add_page (@) { #{{{
+sub add_page (@) {
my %params=@_;
my $feed=$params{feed};
@@ -605,7 +625,8 @@ sub add_page (@) { #{{{
my $template=template($feed->{template}, blind_cache => 1);
$template->param(title => $params{title})
if defined $params{title} && length($params{title});
- $template->param(content => htmlescape(htmlabs($params{content}, $feed->{feedurl})));
+ $template->param(content => wikiescape(htmlabs($params{content},
+ defined $params{base} ? $params{base} : $feed->{feedurl})));
$template->param(name => $feed->{name});
$template->param(url => $feed->{url});
$template->param(copyright => $params{copyright})
@@ -625,23 +646,25 @@ sub add_page (@) { #{{{
# Store it in pagectime for expiry code to use also.
$IkiWiki::pagectime{$guid->{page}}=$mtime;
}
-} #}}}
+ else {
+ # Dummy value for expiry code.
+ $IkiWiki::pagectime{$guid->{page}}=time;
+ }
+}
-sub htmlescape ($) { #{{{
+sub wikiescape ($) {
# escape accidental wikilinks and preprocessor stuff
- my $html=shift;
- $html=~s/(?<!\\)\[\[/\\\[\[/g;
- return $html;
-} #}}}
+ return encode_entities(shift, '\[\]');
+}
-sub urlabs ($$) { #{{{
+sub urlabs ($$) {
my $url=shift;
my $urlbase=shift;
URI->new_abs($url, $urlbase)->as_string;
-} #}}}
+}
-sub htmlabs ($$) { #{{{
+sub htmlabs ($$) {
# Convert links in html from relative to absolute.
# Note that this is a heuristic, which is not specified by the rss
# spec and may not be right for all feeds. Also, see Debian
@@ -677,15 +700,15 @@ sub htmlabs ($$) { #{{{
$p->eof;
return $ret;
-} #}}}
+}
-sub htmlfn ($) { #{{{
+sub htmlfn ($) {
return shift().".".($config{aggregateinternal} ? "_aggregated" : $config{htmlext});
-} #}}}
+}
my $aggregatelock;
-sub lockaggregate () { #{{{
+sub lockaggregate () {
# Take an exclusive lock to prevent multiple concurrent aggregators.
# Returns true if the lock was aquired.
if (! -d $config{wikistatedir}) {
@@ -698,11 +721,11 @@ sub lockaggregate () { #{{{
return 0;
}
return 1;
-} #}}}
+}
-sub unlockaggregate () { #{{{
+sub unlockaggregate () {
return close($aggregatelock) if $aggregatelock;
return;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/amazon_s3.pm b/IkiWiki/Plugin/amazon_s3.pm
index 597539c13..10bf358e4 100644
--- a/IkiWiki/Plugin/amazon_s3.pm
+++ b/IkiWiki/Plugin/amazon_s3.pm
@@ -4,7 +4,7 @@ package IkiWiki::Plugin::amazon_s3;
use warnings;
no warnings 'redefine';
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use IkiWiki::Render;
use Net::Amazon::S3;
@@ -16,13 +16,13 @@ BEGIN {
}
};
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "amazon_s3", call => \&getopt);
hook(type => "getsetup", id => "amazon_s3", call => \&getsetup);
hook(type => "checkconfig", id => "amazon_s3", call => \&checkconfig);
-} # }}}
+}
-sub getopt () { #{{{
+sub getopt () {
eval q{use Getopt::Long};
error($@) if $@;
Getopt::Long::Configure('pass_through');
@@ -38,9 +38,9 @@ sub getopt () { #{{{
debug(gettext("done"));
exit(0);
});
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0,
@@ -88,9 +88,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub checkconfig { #{{{
+sub checkconfig {
foreach my $field (qw{amazon_s3_key_id amazon_s3_key_file
amazon_s3_bucket}) {
if (! exists $config{$field} || ! defined $config{$field}) {
@@ -101,11 +101,11 @@ sub checkconfig { #{{{
! defined $config{amazon_s3_prefix}) {
$config{amazon_s3_prefix}="wiki/";
}
-} #}}}
+}
{
my $bucket;
-sub getbucket { #{{{
+sub getbucket {
return $bucket if defined $bucket;
open(IN, "<", $config{amazon_s3_key_file}) || error($config{amazon_s3_key_file}.": ".$!);
@@ -138,11 +138,11 @@ sub getbucket { #{{{
}
return $bucket;
-} #}}}
+}
}
# Given a file, return any S3 keys associated with it.
-sub file2keys ($) { #{{{
+sub file2keys ($) {
my $file=shift;
my @keys;
@@ -162,14 +162,14 @@ sub file2keys ($) { #{{{
}
}
return @keys;
-} #}}}
+}
package IkiWiki;
use File::MimeInfo;
use Encode;
# This is a wrapper around the real writefile.
-sub writefile ($$$;$$) { #{{{
+sub writefile ($$$;$$) {
my $file=shift;
my $destdir=shift;
my $content=shift;
@@ -225,10 +225,10 @@ sub writefile ($$$;$$) { #{{{
}
return $ret;
-} #}}}
+}
# This is a wrapper around the real prune.
-sub prune ($) { #{{{
+sub prune ($) {
my $file=shift;
my @keys=IkiWiki::Plugin::amazon_s3::file2keys($file);
@@ -247,6 +247,6 @@ sub prune ($) { #{{{
}
return $IkiWiki::Plugin::amazon_s3::subs{'IkiWiki::prune'}->($file);
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/anonok.pm b/IkiWiki/Plugin/anonok.pm
index 2be983693..243b98920 100644
--- a/IkiWiki/Plugin/anonok.pm
+++ b/IkiWiki/Plugin/anonok.pm
@@ -3,14 +3,14 @@ package IkiWiki::Plugin::anonok;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "anonok", call => \&getsetup);
hook(type => "canedit", id => "anonok", call => \&canedit);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -24,9 +24,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub canedit ($$$) { #{{{
+sub canedit ($$$) {
my $page=shift;
my $cgi=shift;
my $session=shift;
@@ -45,6 +45,6 @@ sub canedit ($$$) { #{{{
else {
return "";
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm
index dcac3e820..087c315a9 100644
--- a/IkiWiki/Plugin/attachment.pm
+++ b/IkiWiki/Plugin/attachment.pm
@@ -3,17 +3,18 @@ package IkiWiki::Plugin::attachment;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
+ add_underlay("javascript");
hook(type => "getsetup", id => "attachment", call => \&getsetup);
hook(type => "checkconfig", id => "attachment", call => \&checkconfig);
hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup);
hook(type => "formbuilder", id => "attachment", call => \&formbuilder);
IkiWiki::loadplugin("filecheck");
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -34,9 +35,9 @@ sub getsetup () { #{{{
safe => 0, # executed
rebuild => 0,
},
-} #}}}
+}
-sub check_canattach ($$;$) { #{{{
+sub check_canattach ($$;$) {
my $session=shift;
my $dest=shift; # where it's going to be put, under the srcdir
my $file=shift; # the path to the attachment currently
@@ -60,36 +61,19 @@ sub check_canattach ($$;$) { #{{{
);
}
- # XXX deprecated, should be removed eventually
- if ($allowed) {
- foreach my $admin (@{$config{adminuser}}) {
- my $allowed_attachments=IkiWiki::userinfo_get($admin, "allowed_attachments");
- if (defined $allowed_attachments &&
- length $allowed_attachments) {
- $allowed=pagespec_match($dest,
- $allowed_attachments,
- file => $file,
- user => $session->param("name"),
- ip => $ENV{REMOTE_ADDR},
- );
- last if $allowed;
- }
- }
- }
-
if (! $allowed) {
error(gettext("prohibited by allowed_attachments")." ($allowed)");
}
else {
return 1;
}
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
$config{cgi_disable_uploads}=0;
-} #}}}
+}
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
my $form=$params{form};
my $q=$params{cgi};
@@ -104,10 +88,10 @@ sub formbuilder_setup (@) { #{{{
$form->tmpl_param("field-upload" => '<input name="_submit" type="submit" value="Upload Attachment" />');
$form->tmpl_param("field-link" => '<input name="_submit" type="submit" value="Insert Links" />');
- # Add the javascript from the toggle plugin;
- # the attachments interface uses it to toggle visibility.
+ # Add the toggle javascript; the attachments interface uses
+ # it to toggle visibility.
require IkiWiki::Plugin::toggle;
- $form->tmpl_param("javascript" => $IkiWiki::Plugin::toggle::javascript);
+ $form->tmpl_param("javascript" => IkiWiki::Plugin::toggle::include_javascript($params{page}, 1));
# Start with the attachments interface toggled invisible,
# but if it was used, keep it open.
if ($form->submitted ne "Upload Attachment" &&
@@ -119,42 +103,9 @@ sub formbuilder_setup (@) { #{{{
$form->tmpl_param("attachments-class" => "toggleable-open");
}
}
- elsif ($form->title eq "preferences") {
- # XXX deprecated, should remove eventually
- my $session=$params{session};
- my $user_name=$session->param("name");
-
- $form->field(name => "allowed_attachments", size => 50,
- fieldset => "admin",
- comment => "deprecated; please move to allowed_attachments in setup file",
- );
- if (! IkiWiki::is_admin($user_name)) {
- $form->field(name => "allowed_attachments", type => "hidden");
- }
- if (! $form->submitted) {
- my $value=IkiWiki::userinfo_get($user_name, "allowed_attachments");
- if (length $value) {
- $form->field(name => "allowed_attachments", force => 1,
- value => IkiWiki::userinfo_get($user_name, "allowed_attachments"));
- }
- else {
- $form->field(name => "allowed_attachments", type => "hidden");
- }
- }
- if ($form->submitted && $form->submitted eq 'Save Preferences') {
- if (defined $form->field("allowed_attachments")) {
- IkiWiki::userinfo_set($user_name, "allowed_attachments",
- $form->field("allowed_attachments")) ||
- error("failed to set allowed_attachments");
- if (! length $form->field("allowed_attachments")) {
- $form->field(name => "allowed_attachments", type => "hidden");
- }
- }
- }
- }
-} #}}}
+}
-sub formbuilder (@) { #{{{
+sub formbuilder (@) {
my %params=@_;
my $form=$params{form};
my $q=$params{cgi};
@@ -252,9 +203,9 @@ sub formbuilder (@) { #{{{
# Generate the attachment list only after having added any new
# attachments.
$form->tmpl_param("attachment_list" => [attachment_list($form->field('page'))]);
-} # }}}
+}
-sub attachment_location ($) { #{{{
+sub attachment_location ($) {
my $page=shift;
# Put the attachment in a subdir of the page it's attached
@@ -263,9 +214,9 @@ sub attachment_location ($) { #{{{
$page.="/" if length $page;
return $page;
-} #}}}
+}
-sub attachment_list ($) { #{{{
+sub attachment_list ($) {
my $page=shift;
my $loc=attachment_location($page);
@@ -279,7 +230,6 @@ sub attachment_list ($) { #{{{
link => htmllink($page, $page, $f, noimageinline => 1),
size => IkiWiki::Plugin::filecheck::humansize((stat(_))[7]),
mtime => displaytime($IkiWiki::pagemtime{$f}),
- mtime_raw => $IkiWiki::pagemtime{$f},
};
}
}
@@ -287,6 +237,6 @@ sub attachment_list ($) { #{{{
# Sort newer attachments to the top of the list, so a newly-added
# attachment appears just before the form used to add it.
return sort { $b->{mtime_raw} <=> $a->{mtime_raw} || $a->{link} cmp $b->{link} } @ret;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/autoindex.pm b/IkiWiki/Plugin/autoindex.pm
index d1b3edb1f..555856b11 100644
--- a/IkiWiki/Plugin/autoindex.pm
+++ b/IkiWiki/Plugin/autoindex.pm
@@ -3,23 +3,23 @@ package IkiWiki::Plugin::autoindex;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use Encode;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "autoindex", call => \&getsetup);
hook(type => "refresh", id => "autoindex", call => \&refresh);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub genindex ($) { #{{{
+sub genindex ($) {
my $page=shift;
my $file=newpagefile($page, $config{default_pageext});
my $template=template("autoindex.tmpl");
@@ -28,9 +28,9 @@ sub genindex ($) { #{{{
if ($config{rcs}) {
IkiWiki::rcs_add($file);
}
-} #}}}
+}
-sub refresh () { #{{{
+sub refresh () {
eval q{use File::Find};
error($@) if $@;
@@ -107,6 +107,6 @@ sub refresh () { #{{{
IkiWiki::enable_commit_hook();
}
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/blogspam.pm b/IkiWiki/Plugin/blogspam.pm
new file mode 100644
index 000000000..cbd9859a5
--- /dev/null
+++ b/IkiWiki/Plugin/blogspam.pm
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::blogspam;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+my $defaulturl='http://test.blogspam.net:8888/';
+
+sub import {
+ hook(type => "getsetup", id => "blogspam", call => \&getsetup);
+ hook(type => "checkcontent", id => "blogspam", call => \&checkcontent);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => 0,
+ },
+ blogspam_pagespec => {
+ type => 'pagespec',
+ example => 'postcomment(*)',
+ description => 'PageSpec of pages to check for spam',
+ link => 'ikiwiki/PageSpec',
+ safe => 1,
+ rebuild => 0,
+ },
+ blogspam_options => {
+ type => "string",
+ example => "blacklist=1.2.3.4,blacklist=8.7.6.5,max-links=10",
+ description => "options to send to blogspam server",
+ link => "http://blogspam.net/api/testComment.html#options",
+ safe => 1,
+ rebuild => 0,
+ },
+ blogspam_server => {
+ type => "string",
+ default => $defaulturl,
+ description => "blogspam server XML-RPC url",
+ safe => 1,
+ rebuild => 0,
+ },
+}
+
+sub checkcontent (@) {
+ my %params=@_;
+
+ eval q{
+ use RPC::XML;
+ use RPC::XML::Client;
+ };
+ if ($@) {
+ warn($@);
+ return undef;
+ }
+
+ if (exists $config{blogspam_pagespec}) {
+ return undef
+ if ! pagespec_match($params{page}, $config{blogspam_pagespec},
+ location => $params{page});
+ }
+
+ my $url=$defaulturl;
+ $url = $config{blogspam_server} if exists $config{blogspam_server};
+ my $client = RPC::XML::Client->new($url);
+
+ my @options = split(",", $config{blogspam_options})
+ if exists $config{blogspam_options};
+
+ # Allow short comments and whitespace-only edits, unless the user
+ # has overridden min-words themselves.
+ push @options, "min-words=0"
+ unless grep /^min-words=/i, @options;
+ # Wiki pages can have a lot of urls, unless the user specifically
+ # wants to limit them.
+ push @options, "exclude=lotsaurls"
+ unless grep /^max-links/i, @options;
+ # Unless the user specified a size check, disable such checking.
+ push @options, "exclude=size"
+ unless grep /^(?:max|min)-size/i, @options;
+ # This test has absurd false positives on words like "alpha"
+ # and "buy".
+ push @options, "exclude=stopwords";
+
+ my %req=(
+ ip => $ENV{REMOTE_ADDR},
+ comment => $params{content},
+ subject => defined $params{subject} ? $params{subject} : "",
+ name => defined $params{author} ? $params{author} : "",
+ link => exists $params{url} ? $params{url} : "",
+ options => join(",", @options),
+ site => $config{url},
+ version => "ikiwiki ".$IkiWiki::version,
+ );
+ my $res = $client->send_request('testComment', \%req);
+
+ if (! ref $res || ! defined $res->value) {
+ debug("failed to get response from blogspam server ($url)");
+ return undef;
+ }
+ elsif ($res->value =~ /^SPAM:(.*)/) {
+ eval q{use Data::Dumper};
+ debug("blogspam server reports ".$res->value.": ".Dumper(\%req));
+ return gettext("Sorry, but that looks like spam to <a href=\"http://blogspam.net/\">blogspam</a>: ").$1;
+ }
+ elsif ($res->value ne 'OK') {
+ debug("blogspam server failure: ".$res->value);
+ return undef;
+ }
+ else {
+ return undef;
+ }
+}
+
+1
diff --git a/IkiWiki/Plugin/brokenlinks.pm b/IkiWiki/Plugin/brokenlinks.pm
index 37752dd3e..bf0d7560d 100644
--- a/IkiWiki/Plugin/brokenlinks.pm
+++ b/IkiWiki/Plugin/brokenlinks.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::brokenlinks;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "brokenlinks", call => \&getsetup);
hook(type => "preprocess", id => "brokenlinks", call => \&preprocess);
-} # }}}
+}
-sub getsetup { #{{{
+sub getsetup {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages}="*" unless defined $params{pages};
@@ -61,6 +61,6 @@ sub preprocess (@) { #{{{
}
sort @broken)
."</ul>\n";
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/bzr.pm b/IkiWiki/Plugin/bzr.pm
index 101e91b93..883007367 100644
--- a/IkiWiki/Plugin/bzr.pm
+++ b/IkiWiki/Plugin/bzr.pm
@@ -7,7 +7,7 @@ use IkiWiki;
use Encode;
use open qw{:utf8 :std};
-sub import { #{{{
+sub import {
hook(type => "checkconfig", id => "bzr", call => \&checkconfig);
hook(type => "getsetup", id => "bzr", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
@@ -20,18 +20,18 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (defined $config{bzr_wrapper} && length $config{bzr_wrapper}) {
push @{$config{wrappers}}, {
wrapper => $config{bzr_wrapper},
wrappermode => (defined $config{bzr_wrappermode} ? $config{bzr_wrappermode} : "06755"),
};
}
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
@@ -65,9 +65,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub bzr_log ($) { #{{{
+sub bzr_log ($) {
my $out = shift;
my @infos = ();
my $key = undef;
@@ -99,20 +99,20 @@ sub bzr_log ($) { #{{{
close $out;
return @infos;
-} #}}}
+}
-sub rcs_update () { #{{{
+sub rcs_update () {
my @cmdline = ("bzr", "update", "--quiet", $config{srcdir});
if (system(@cmdline) != 0) {
warn "'@cmdline' failed: $!";
}
-} #}}}
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
return "";
-} #}}}
+}
-sub bzr_author ($$) { #{{{
+sub bzr_author ($$) {
my ($user, $ipaddr) = @_;
if (defined $user) {
@@ -124,9 +124,9 @@ sub bzr_author ($$) { #{{{
else {
return "Anonymous";
}
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
my ($file, $message, $rcstoken, $user, $ipaddr) = @_;
$user = bzr_author($user, $ipaddr);
@@ -143,7 +143,7 @@ sub rcs_commit ($$$;$$) { #{{{
}
return undef; # success
-} #}}}
+}
sub rcs_commit_staged ($$$) {
# Commits all staged changes. Changes can be staged using rcs_add,
@@ -164,27 +164,27 @@ sub rcs_commit_staged ($$$) {
}
return undef; # success
-} #}}}
+}
-sub rcs_add ($) { # {{{
+sub rcs_add ($) {
my ($file) = @_;
my @cmdline = ("bzr", "add", "--quiet", "$config{srcdir}/$file");
if (system(@cmdline) != 0) {
warn "'@cmdline' failed: $!";
}
-} #}}}
+}
-sub rcs_remove ($) { # {{{
+sub rcs_remove ($) {
my ($file) = @_;
my @cmdline = ("bzr", "rm", "--force", "--quiet", "$config{srcdir}/$file");
if (system(@cmdline) != 0) {
warn "'@cmdline' failed: $!";
}
-} #}}}
+}
-sub rcs_rename ($$) { # {{{
+sub rcs_rename ($$) {
my ($src, $dest) = @_;
my $parent = IkiWiki::dirname($dest);
@@ -196,9 +196,9 @@ sub rcs_rename ($$) { # {{{
if (system(@cmdline) != 0) {
warn "'@cmdline' failed: $!";
}
-} #}}}
+}
-sub rcs_recentchanges ($) { #{{{
+sub rcs_recentchanges ($) {
my ($num) = @_;
my @cmdline = ("bzr", "log", "-v", "--show-ids", "--limit", $num,
@@ -246,16 +246,36 @@ sub rcs_recentchanges ($) { #{{{
rev => $info->{"revno"},
user => $user,
committype => "bzr",
- when => time - str2time($info->{"timestamp"}),
+ when => str2time($info->{"timestamp"}),
message => [@message],
pages => [@pages],
};
}
return @ret;
-} #}}}
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_diff ($) {
+ my $taintedrev=shift;
+ my ($rev) = $taintedrev =~ /^(\d+(\.\d+)*)$/; # untaint
+
+ my $prevspec = "before:" . $rev;
+ my $revspec = "revno:" . $rev;
+ my @cmdline = ("bzr", "diff", "--old", $config{srcdir},
+ "--new", $config{srcdir},
+ "-r", $prevspec . ".." . $revspec);
+ open (my $out, "@cmdline |");
+
+ my @lines = <$out>;
+ if (wantarray) {
+ return @lines;
+ }
+ else {
+ return join("", @lines);
+ }
+}
+
+sub rcs_getctime ($) {
my ($file) = @_;
# XXX filename passes through the shell here, should try to avoid
@@ -274,6 +294,6 @@ sub rcs_getctime ($) { #{{{
my $ctime = str2time($log[0]->{"timestamp"});
return $ctime;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/calendar.pm b/IkiWiki/Plugin/calendar.pm
index 6d536a91b..d473c8348 100644
--- a/IkiWiki/Plugin/calendar.pm
+++ b/IkiWiki/Plugin/calendar.pm
@@ -20,7 +20,7 @@ package IkiWiki::Plugin::calendar;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use Time::Local;
use POSIX;
@@ -29,13 +29,13 @@ my %linkcache;
my $time=time;
my @now=localtime($time);
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "calendar", call => \&getsetup);
hook(type => "needsbuild", id => "calendar", call => \&needsbuild);
hook(type => "preprocess", id => "calendar", call => \&preprocess);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -48,23 +48,23 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub is_leap_year (@) { #{{{
+sub is_leap_year (@) {
my %params=@_;
return ($params{year} % 4 == 0 && (($params{year} % 100 != 0) || $params{year} % 400 == 0));
-} #}}}
+}
-sub month_days { #{{{
+sub month_days {
my %params=@_;
my $days_in_month = (31,28,31,30,31,30,31,31,30,31,30,31)[$params{month}-1];
if ($params{month} == 2 && is_leap_year(%params)) {
$days_in_month++;
}
return $days_in_month;
-} #}}}
+}
-sub format_month (@) { #{{{
+sub format_month (@) {
my %params=@_;
my $pagespec = $params{pages};
@@ -215,9 +215,9 @@ EOF
add_depends($params{page}, join(" or ", @list));
return $calendar;
-} #}}}
+}
-sub format_year (@) { #{{{
+sub format_year (@) {
my %params=@_;
my $pagespec = $params{pages};
@@ -318,9 +318,9 @@ EOF
EOF
return $calendar;
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages} = "*" unless defined $params{pages};
$params{type} = "month" unless defined $params{type};
@@ -397,7 +397,7 @@ sub preprocess (@) { #{{{
return "\n<div><div class=\"calendar\">$calendar</div></div>\n";
} #}}
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
foreach my $page (keys %pagestate) {
if (exists $pagestate{$page}{calendar}{nextchange}) {
@@ -415,6 +415,6 @@ sub needsbuild (@) { #{{{
}
}
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/camelcase.pm b/IkiWiki/Plugin/camelcase.pm
index 7881becd5..74a8397d7 100644
--- a/IkiWiki/Plugin/camelcase.pm
+++ b/IkiWiki/Plugin/camelcase.pm
@@ -4,7 +4,7 @@ package IkiWiki::Plugin::camelcase;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
# This regexp is based on the one in Text::WikiFormat.
my $link_regexp=qr{
@@ -22,40 +22,52 @@ my $link_regexp=qr{
)
}x;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "camelcase", call => \&getsetup);
hook(type => "linkify", id => "camelcase", call => \&linkify);
hook(type => "scan", id => "camelcase", call => \&scan);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
- };
-} #}}}
+ },
+ camelcase_ignore => {
+ type => "string",
+ example => [],
+ description => "list of words to not turn into links",
+ safe => 1,
+ rebuild => undef, # might change links
+ },
+}
-sub linkify (@) { #{{{
+sub linkify (@) {
my %params=@_;
my $page=$params{page};
my $destpage=$params{destpage};
$params{content}=~s{$link_regexp}{
- htmllink($page, $destpage, linkpage($1))
+ ignored($1) ? $1 : htmllink($page, $destpage, linkpage($1))
}eg;
return $params{content};
-} #}}}
+}
-sub scan (@) { #{{{
+sub scan (@) {
my %params=@_;
my $page=$params{page};
my $content=$params{content};
while ($content =~ /$link_regexp/g) {
- push @{$links{$page}}, linkpage($1);
+ push @{$links{$page}}, linkpage($1) unless ignored($1)
}
}
+sub ignored ($) {
+ my $word=lc shift;
+ grep { $word eq lc $_ } @{$config{'camelcase_ignore'}}
+}
+
1
diff --git a/IkiWiki/Plugin/color.pm b/IkiWiki/Plugin/color.pm
index ac702ff02..20505893b 100644
--- a/IkiWiki/Plugin/color.pm
+++ b/IkiWiki/Plugin/color.pm
@@ -5,14 +5,14 @@ package IkiWiki::Plugin::color;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "preprocess", id => "color", call => \&preprocess);
hook(type => "format", id => "color", call => \&format);
-} #}}}
+}
-sub preserve_style ($$$) { #{{{
+sub preserve_style ($$$) {
my $foreground = shift;
my $background = shift;
my $text = shift;
@@ -37,18 +37,18 @@ sub preserve_style ($$$) { #{{{
return $preserved;
-} #}}}
+}
-sub replace_preserved_style ($) { #{{{
+sub replace_preserved_style ($) {
my $content = shift;
$content =~ s!<span class="color">((color: ([a-z]+|\#[0-9a-f]{3,6})?)?((; )?(background-color: ([a-z]+|\#[0-9a-f]{3,6})?)?)?)</span>!<span class="color" style="$1">!g;
$content =~ s!<span class="colorend">!!g;
return $content;
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params = @_;
# Preprocess the text to expand any preprocessor directives
@@ -57,13 +57,13 @@ sub preprocess (@) { #{{{
IkiWiki::filter($params{page}, $params{destpage}, $params{text}));
return preserve_style($params{foreground}, $params{background}, $params{text});
-} #}}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params = @_;
$params{content} = replace_preserved_style($params{content});
return $params{content};
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/comments.pm b/IkiWiki/Plugin/comments.pm
new file mode 100644
index 000000000..98f9f8b3d
--- /dev/null
+++ b/IkiWiki/Plugin/comments.pm
@@ -0,0 +1,840 @@
+#!/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
+package IkiWiki::Plugin::comments;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+use Encode;
+use POSIX qw(strftime);
+
+use constant PREVIEW => "Preview";
+use constant POST_COMMENT => "Post comment";
+use constant CANCEL => "Cancel";
+
+my $postcomment;
+my %commentstate;
+
+sub import {
+ hook(type => "checkconfig", id => 'comments', call => \&checkconfig);
+ hook(type => "getsetup", id => 'comments', call => \&getsetup);
+ hook(type => "preprocess", id => '_comment', call => \&preprocess);
+ hook(type => "sessioncgi", id => 'comment', call => \&sessioncgi);
+ hook(type => "htmlize", id => "_comment", call => \&htmlize);
+ hook(type => "pagetemplate", id => "comments", call => \&pagetemplate);
+ hook(type => "formbuilder_setup", id => "comments", call => \&formbuilder_setup);
+ # Load goto to fix up user page links for logged-in commenters
+ IkiWiki::loadplugin("goto");
+ IkiWiki::loadplugin("inline");
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => 1,
+ },
+ comments_pagespec => {
+ type => 'pagespec',
+ example => 'blog/* and !*/Discussion',
+ description => 'PageSpec of pages where comments are allowed',
+ link => 'ikiwiki/PageSpec',
+ safe => 1,
+ rebuild => 1,
+ },
+ comments_closed_pagespec => {
+ type => 'pagespec',
+ example => 'blog/controversial or blog/flamewar',
+ description => 'PageSpec of pages where posting new comments is not allowed',
+ 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,
+ },
+}
+
+sub checkconfig () {
+ $config{comments_commit} = 1
+ unless defined $config{comments_commit};
+ $config{comments_pagespec} = ''
+ unless defined $config{comments_pagespec};
+ $config{comments_closed_pagespec} = ''
+ unless defined $config{comments_closed_pagespec};
+ $config{comments_pagename} = 'comment_'
+ unless defined $config{comments_pagename};
+}
+
+sub htmlize {
+ my %params = @_;
+ return $params{content};
+}
+
+# FIXME: copied verbatim from meta
+sub safeurl ($) {
+ 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;
+ }
+}
+
+sub preprocess {
+ my %params = @_;
+ my $page = $params{page};
+
+ my $format = $params{format};
+ if (defined $format && ! exists $IkiWiki::hooks{htmlize}{$format}) {
+ error(sprintf(gettext("unsupported page format %s"), $format));
+ }
+
+ my $content = $params{content};
+ if (! defined $content) {
+ error(gettext("comment must have content"));
+ }
+ $content =~ s/\\"/"/g;
+
+ $content = IkiWiki::filter($page, $params{destpage}, $content);
+
+ if ($config{comments_allowdirectives}) {
+ $content = IkiWiki::preprocess($page, $params{destpage},
+ $content);
+ }
+
+ # 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,
+ );
+ });
+
+ # set metadata, possibly overriding [[!meta]] directives from the
+ # comment itself
+
+ my $commentuser;
+ my $commentip;
+ my $commentauthor;
+ my $commentauthorurl;
+ my $commentopenid;
+ if (defined $params{username}) {
+ $commentuser = $params{username};
+
+ my $oiduser = eval { IkiWiki::openiduser($commentuser) };
+
+ if (defined $oiduser) {
+ # looks like an OpenID
+ $commentauthorurl = $commentuser;
+ $commentauthor = $oiduser;
+ $commentopenid = $commentuser;
+ }
+ else {
+ $commentauthorurl = IkiWiki::cgiurl(
+ do => 'goto',
+ page => (length $config{userdir}
+ ? "$config{userdir}/$commentuser"
+ : "$commentuser"));
+
+ $commentauthor = $commentuser;
+ }
+ }
+ else {
+ if (defined $params{ip}) {
+ $commentip = $params{ip};
+ }
+ $commentauthor = gettext("Anonymous");
+ }
+
+ $commentstate{$page}{commentuser} = $commentuser;
+ $commentstate{$page}{commentopenid} = $commentopenid;
+ $commentstate{$page}{commentip} = $commentip;
+ $commentstate{$page}{commentauthor} = $commentauthor;
+ $commentstate{$page}{commentauthorurl} = $commentauthorurl;
+ if (! defined $pagestate{$page}{meta}{author}) {
+ $pagestate{$page}{meta}{author} = $commentauthor;
+ }
+ if (! defined $pagestate{$page}{meta}{authorurl}) {
+ $pagestate{$page}{meta}{authorurl} = $commentauthorurl;
+ }
+
+ if ($config{comments_allowauthor}) {
+ if (defined $params{claimedauthor}) {
+ $pagestate{$page}{meta}{author} = $params{claimedauthor};
+ }
+
+ if (defined $params{url}) {
+ my $url=$params{url};
+
+ eval q{use URI::Heuristic};
+ if (! $@) {
+ $url=URI::Heuristic::uf_uristr($url);
+ }
+
+ if (safeurl($url)) {
+ $pagestate{$page}{meta}{authorurl} = $url;
+ }
+ }
+ }
+ else {
+ $pagestate{$page}{meta}{author} = $commentauthor;
+ $pagestate{$page}{meta}{authorurl} = $commentauthorurl;
+ }
+
+ if (defined $params{subject}) {
+ $pagestate{$page}{meta}{title} = $params{subject};
+ }
+
+ if ($params{page} =~ m/\/(\Q$config{comments_pagename}\E\d+)$/) {
+ $pagestate{$page}{meta}{permalink} = urlto(IkiWiki::dirname($params{page}), undef, 1).
+ "#".page_to_id($params{page});
+ }
+
+ eval q{use Date::Parse};
+ if (! $@) {
+ my $time = str2time($params{date});
+ $IkiWiki::pagectime{$page} = $time if defined $time;
+ }
+
+ return $content;
+}
+
+sub sessioncgi ($$) {
+ my $cgi=shift;
+ my $session=shift;
+
+ my $do = $cgi->param('do');
+ 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);
+
+ eval q{use CGI::FormBuilder};
+ error($@) if $@;
+
+ my @buttons = (POST_COMMENT, PREVIEW, CANCEL);
+ my $form = CGI::FormBuilder->new(
+ fields => [qw{do sid page subject editcontent type author url}],
+ charset => 'utf-8',
+ method => 'POST',
+ required => [qw{editcontent}],
+ javascript => 0,
+ params => $cgi,
+ action => $config{cgiurl},
+ header => 0,
+ table => 0,
+ template => scalar IkiWiki::template_params('editcomment.tmpl'),
+ );
+
+ IkiWiki::decode_form_utf8($form);
+ IkiWiki::run_hooks(formbuilder_setup => sub {
+ shift->(title => "comment", form => $form, cgi => $cgi,
+ session => $session, buttons => \@buttons);
+ });
+ IkiWiki::decode_form_utf8($form);
+
+ my $type = $form->param('type');
+ if (defined $type && length $type && $IkiWiki::hooks{htmlize}{$type}) {
+ $type = IkiWiki::possibly_foolish_untaint($type);
+ }
+ else {
+ $type = $config{default_pageext};
+ }
+ my @page_types;
+ if (exists $IkiWiki::hooks{htmlize}) {
+ @page_types = grep { ! /^_/ } keys %{$IkiWiki::hooks{htmlize}};
+ }
+
+ $form->field(name => 'do', type => 'hidden');
+ $form->field(name => 'sid', type => 'hidden', value => $session->id,
+ force => 1);
+ $form->field(name => 'page', type => 'hidden');
+ $form->field(name => 'subject', type => 'text', size => 72);
+ $form->field(name => 'editcontent', type => 'textarea', rows => 10);
+ $form->field(name => "type", value => $type, force => 1,
+ type => 'select', options => \@page_types);
+
+ $form->tmpl_param(username => $session->param('name'));
+
+ if ($config{comments_allowauthor} and
+ ! defined $session->param('name')) {
+ $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);
+ }
+
+ # The untaint is OK (as in editpage) because we're about to pass
+ # it to file_pruned anyway
+ my $page = $form->field('page');
+ $page = IkiWiki::possibly_foolish_untaint($page);
+ if (! defined $page || ! length $page ||
+ IkiWiki::file_pruned($page, $config{srcdir})) {
+ error(gettext("bad page name"));
+ }
+
+ my $baseurl = urlto($page, undef, 1);
+
+ $form->title(sprintf(gettext("commenting on %s"),
+ IkiWiki::pagetitle($page)));
+
+ $form->tmpl_param('helponformattinglink',
+ htmllink($page, $page, 'ikiwiki/formatting',
+ noimageinline => 1,
+ linktext => 'FormattingHelp'),
+ allowdirectives => $config{allow_directives});
+
+ if ($form->submitted eq CANCEL) {
+ # bounce back to the page they wanted to comment on, and exit.
+ # CANCEL need not be considered in future
+ IkiWiki::redirect($cgi, urlto($page, undef, 1));
+ exit;
+ }
+
+ if (not exists $pagesources{$page}) {
+ error(sprintf(gettext(
+ "page '%s' doesn't exist, so you can't comment"),
+ $page));
+ }
+
+ if (pagespec_match($page, $config{comments_closed_pagespec},
+ location => $page)) {
+ error(sprintf(gettext(
+ "comments on page '%s' are closed"),
+ $page));
+ }
+
+ # 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;
+
+ my $location=unique_comment_location($page, $config{srcdir});
+
+ my $content = "[[!_comment format=$type\n";
+
+ # FIXME: handling of double quotes probably wrong?
+ if (defined $session->param('name')) {
+ my $username = $session->param('name');
+ $username =~ s/"/&quot;/g;
+ $content .= " username=\"$username\"\n";
+ }
+ elsif (defined $ENV{REMOTE_ADDR}) {
+ my $ip = $ENV{REMOTE_ADDR};
+ if ($ip =~ m/^([.0-9]+)$/) {
+ $content .= " ip=\"$1\"\n";
+ }
+ }
+
+ if ($config{comments_allowauthor}) {
+ my $author = $form->field('author');
+ if (defined $author && length $author) {
+ $author =~ s/"/&quot;/g;
+ $content .= " claimedauthor=\"$author\"\n";
+ }
+ my $url = $form->field('url');
+ if (defined $url && length $url) {
+ $url =~ s/"/&quot;/g;
+ $content .= " url=\"$url\"\n";
+ }
+ }
+
+ my $subject = $form->field('subject');
+ if (defined $subject && length $subject) {
+ $subject =~ s/"/&quot;/g;
+ $content .= " subject=\"$subject\"\n";
+ }
+
+ $content .= " date=\"" . decode_utf8(strftime('%Y-%m-%dT%H:%M:%SZ', gmtime)) . "\"\n";
+
+ my $editcontent = $form->field('editcontent') || '';
+ $editcontent =~ s/\r\n/\n/g;
+ $editcontent =~ s/\r/\n/g;
+ $editcontent =~ s/"/\\"/g;
+ $content .= " content=\"\"\"\n$editcontent\n\"\"\"]]\n";
+
+ # 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) {
+ my $preview=previewcomment($content, $location, $page, time);
+ IkiWiki::run_hooks(format => sub {
+ $preview = shift->(page => $page,
+ content => $preview);
+ });
+ $form->tmpl_param(page_preview => $preview);
+ }
+ else {
+ $form->tmpl_param(page_preview => "");
+ }
+
+ if ($form->submitted eq POST_COMMENT && $form->validate) {
+ IkiWiki::checksessionexpiry($cgi, $session);
+
+ $postcomment=1;
+ my $ok=IkiWiki::check_content(content => $form->field('editcontent'),
+ subject => $form->field('subject'),
+ $config{comments_allowauthor} ? (
+ author => $form->field('author'),
+ url => $form->field('url'),
+ ) : (),
+ page => $location,
+ cgi => $cgi,
+ session => $session,
+ nonfatal => 1,
+ );
+ $postcomment=0;
+
+ if (! $ok) {
+ 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").
+ "</p>");
+ exit;
+ }
+
+ # FIXME: could probably do some sort of graceful retry
+ # on error? Would require significant unwinding though
+ my $file = "$location._comment";
+ writefile($file, $config{srcdir}, $content);
+
+ my $conflict;
+
+ if ($config{rcs} and $config{comments_commit}) {
+ my $message = gettext("Added a comment");
+ if (defined $form->field('subject') &&
+ length $form->field('subject')) {
+ $message = sprintf(
+ gettext("Added a comment: %s"),
+ $form->field('subject'));
+ }
+
+ IkiWiki::rcs_add($file);
+ 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();
+
+ # this should never happen, unless a committer deliberately
+ # breaks it or something
+ error($conflict) if defined $conflict;
+
+ # Jump to the new comment on the page.
+ # The trailing question mark tries to avoid broken
+ # caches and get the most recent version of the page.
+ IkiWiki::redirect($cgi, urlto($page, undef, 1).
+ "?updated#".page_to_id($location));
+
+ }
+ else {
+ IkiWiki::showform ($form, \@buttons, $session, $cgi,
+ forcebaseurl => $baseurl);
+ }
+
+ 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 $rejectalldefer=$cgi->param('rejectalldefer');
+
+ 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' && ! $rejectalldefer;
+
+ # 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, $ctime)=@{$_};
+ my $file="$config{wikistatedir}/comments_pending/$id";
+ my $content=readfile($file);
+ my $preview=previewcomment($content, $id,
+ IkiWiki::dirname($_), $ctime);
+ {
+ id => $id,
+ view => $preview,
+ }
+ } sort { $b->[1] <=> $a->[1] } comments_pending();
+
+ my $template=template("commentmoderation.tmpl");
+ $template->param(
+ sid => $session->id,
+ comments => \@comments,
+ );
+ IkiWiki::printheader($session);
+ my $out=$template->output;
+ IkiWiki::run_hooks(format => sub {
+ $out = shift->(page => "", content => $out);
+ });
+ print IkiWiki::misctemplate(gettext("comment moderation"), $out);
+ exit;
+}
+
+sub formbuilder_setup (@) {
+ my %params=@_;
+
+ my $form=$params{form};
+ if ($form->title eq "preferences" &&
+ IkiWiki::is_admin($params{session}->param("name"))) {
+ push @{$params{buttons}}, "Comment Moderation";
+ if ($form->submitted && $form->submitted eq "Comment Moderation") {
+ commentmoderation($params{cgi}, $params{session});
+ }
+ }
+}
+
+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$/) {
+ my $ctime=(stat($f))[10];
+ $f=~s/^\Q$dir\E\/?//;
+ push @ret, [$f, $ctime];
+ }
+ }
+ }
+ }, $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)));
+
+ 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);
+ });
+
+ $template->param(have_actions => 0);
+
+ return $template->output;
+}
+
+sub commentsshown ($) {
+ my $page=shift;
+
+ return ! pagespec_match($page, "internal(*/$config{comments_pagename}*)",
+ location => $page) &&
+ pagespec_match($page, $config{comments_pagespec},
+ location => $page);
+}
+
+sub commentsopen ($) {
+ my $page = shift;
+
+ return length $config{cgiurl} > 0 &&
+ (! length $config{comments_closed_pagespec} ||
+ ! pagespec_match($page, $config{comments_closed_pagespec},
+ location => $page));
+}
+
+sub pagetemplate (@) {
+ my %params = @_;
+
+ my $page = $params{page};
+ my $template = $params{template};
+ my $shown = ($template->query(name => 'commentslink') ||
+ $template->query(name => 'commentsurl') ||
+ $template->query(name => 'atomcommentsurl') ||
+ $template->query(name => 'comments')) &&
+ commentsshown($page);
+
+ if ($template->query(name => 'comments')) {
+ my $comments = undef;
+ if ($shown) {
+ $comments = IkiWiki::preprocess_inline(
+ pages => "internal($page/$config{comments_pagename}*)",
+ template => 'comment',
+ show => 0,
+ reverse => 'yes',
+ page => $page,
+ destpage => $params{destpage},
+ feedfile => 'comments',
+ emptyfeeds => 'no',
+ );
+ }
+
+ if (defined $comments && length $comments) {
+ $template->param(comments => $comments);
+ }
+
+ if ($shown && commentsopen($page)) {
+ my $addcommenturl = IkiWiki::cgiurl(do => 'comment',
+ page => $page);
+ $template->param(addcommenturl => $addcommenturl);
+ }
+ }
+
+ if ($template->query(name => 'commentsurl')) {
+ if ($shown) {
+ $template->param(commentsurl =>
+ urlto($page, undef, 1).'#comments');
+ }
+ }
+
+ if ($template->query(name => 'atomcommentsurl') && $config{usedirs}) {
+ if ($shown) {
+ # This will 404 until there are some comments, but I
+ # think that's probably OK...
+ $template->param(atomcommentsurl =>
+ urlto($page, undef, 1).'comments.atom');
+ }
+ }
+
+ if ($template->query(name => 'commentslink')) {
+ # XXX Would be nice to say how many comments there are in
+ # the link. But, to update the number, blog pages
+ # would have to update whenever comments of any inlines
+ # page are added, which is not currently done.
+ if ($shown) {
+ $template->param(commentslink =>
+ htmllink($page, $params{destpage}, $page,
+ linktext => gettext("Comments"),
+ anchor => "comments",
+ noimageinline => 1));
+ }
+ }
+
+ # everything below this point is only relevant to the comments
+ # themselves
+ if (!exists $commentstate{$page}) {
+ return;
+ }
+
+ if ($template->query(name => 'commentid')) {
+ $template->param(commentid => page_to_id($page));
+ }
+
+ if ($template->query(name => 'commentuser')) {
+ $template->param(commentuser =>
+ $commentstate{$page}{commentuser});
+ }
+
+ if ($template->query(name => 'commentopenid')) {
+ $template->param(commentopenid =>
+ $commentstate{$page}{commentopenid});
+ }
+
+ if ($template->query(name => 'commentip')) {
+ $template->param(commentip =>
+ $commentstate{$page}{commentip});
+ }
+
+ if ($template->query(name => 'commentauthor')) {
+ $template->param(commentauthor =>
+ $commentstate{$page}{commentauthor});
+ }
+
+ if ($template->query(name => 'commentauthorurl')) {
+ $template->param(commentauthorurl =>
+ $commentstate{$page}{commentauthorurl});
+ }
+
+ 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);
+ }
+}
+
+sub unique_comment_location ($) {
+ my $page=shift;
+ my $dir=shift;
+
+ my $location;
+ my $i = 0;
+ do {
+ $i++;
+ $location = "$page/$config{comments_pagename}$i";
+ } while (-e "$dir/$location._comment");
+
+ return $location;
+}
+
+sub page_to_id ($) {
+ # Converts a comment page name into a unique, legal html id
+ # addtibute value, that can be used as an anchor to link to the
+ # comment.
+ my $page=shift;
+
+ eval q{use Digest::MD5 'md5_hex'};
+ error($@) if $@;
+
+ return "comment-".md5_hex($page);
+}
+
+package IkiWiki::PageSpec;
+
+sub match_postcomment ($$;@) {
+ my $page = shift;
+ my $glob = shift;
+
+ if (! $postcomment) {
+ return IkiWiki::FailReason->new("not posting a comment");
+ }
+ return match_glob($page, $glob);
+}
+
+1
diff --git a/IkiWiki/Plugin/conditional.pm b/IkiWiki/Plugin/conditional.pm
index e787424aa..7445dbdad 100644
--- a/IkiWiki/Plugin/conditional.pm
+++ b/IkiWiki/Plugin/conditional.pm
@@ -3,23 +3,23 @@ package IkiWiki::Plugin::conditional;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use UNIVERSAL;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "conditional", call => \&getsetup);
hook(type => "preprocess", id => "if", call => \&preprocess_if);
-} # }}}
+}
-sub getsetup { #{{{
+sub getsetup {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess_if (@) { #{{{
+sub preprocess_if (@) {
my %params=@_;
foreach my $param (qw{test then}) {
@@ -66,11 +66,11 @@ sub preprocess_if (@) { #{{{
}
return IkiWiki::preprocess($params{page}, $params{destpage},
IkiWiki::filter($params{page}, $params{destpage}, $ret));
-} # }}}
+}
package IkiWiki::PageSpec;
-sub match_enabled ($$;@) { #{{{
+sub match_enabled ($$;@) {
shift;
my $plugin=shift;
@@ -81,12 +81,14 @@ sub match_enabled ($$;@) { #{{{
else {
return IkiWiki::FailReason->new("$plugin is not enabled");
}
-} #}}}
+}
-sub match_sourcepage ($$;@) { #{{{
+sub match_sourcepage ($$;@) {
shift;
my $glob=shift;
my %params=@_;
+
+ $glob=derel($glob, $params{location});
return IkiWiki::FailReason->new("cannot match sourcepage") unless exists $params{sourcepage};
if (match_glob($params{sourcepage}, $glob, @_)) {
@@ -95,13 +97,15 @@ sub match_sourcepage ($$;@) { #{{{
else {
return IkiWiki::FailReason->new("sourcepage does not match $glob");
}
-} #}}}
+}
-sub match_destpage ($$;@) { #{{{
+sub match_destpage ($$;@) {
shift;
my $glob=shift;
my %params=@_;
+ $glob=derel($glob, $params{location});
+
return IkiWiki::FailReason->new("cannot match destpage") unless exists $params{destpage};
if (match_glob($params{destpage}, $glob, @_)) {
return IkiWiki::SuccessReason->new("destpage matches $glob");
@@ -109,9 +113,9 @@ sub match_destpage ($$;@) { #{{{
else {
return IkiWiki::FailReason->new("destpage does not match $glob");
}
-} #}}}
+}
-sub match_included ($$;@) { #{{{
+sub match_included ($$;@) {
shift;
shift;
my %params=@_;
@@ -123,6 +127,6 @@ sub match_included ($$;@) { #{{{
else {
return IkiWiki::FailReason->new("page $params{sourcepage} is not included");
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/creole.pm b/IkiWiki/Plugin/creole.pm
index 7c729300d..425e71043 100644
--- a/IkiWiki/Plugin/creole.pm
+++ b/IkiWiki/Plugin/creole.pm
@@ -5,22 +5,22 @@ package IkiWiki::Plugin::creole;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "creole", call => \&getsetup);
hook(type => "htmlize", id => "creole", call => \&htmlize);
-} # }}}
+}
-sub getsetup { #{{{
+sub getsetup {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
-} #}}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
my $content = $params{content};
@@ -32,6 +32,6 @@ sub htmlize (@) { #{{{
creole_custombarelinks();
return creole_parse($content);
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/cutpaste.pm b/IkiWiki/Plugin/cutpaste.pm
index 92667a1ef..417442f34 100644
--- a/IkiWiki/Plugin/cutpaste.pm
+++ b/IkiWiki/Plugin/cutpaste.pm
@@ -3,26 +3,26 @@ package IkiWiki::Plugin::cutpaste;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %savedtext;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "cutpaste", call => \&getsetup);
hook(type => "preprocess", id => "cut", call => \&preprocess_cut, scan => 1);
hook(type => "preprocess", id => "copy", call => \&preprocess_copy, scan => 1);
hook(type => "preprocess", id => "paste", call => \&preprocess_paste);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess_cut (@) { #{{{
+sub preprocess_cut (@) {
my %params=@_;
foreach my $param (qw{id text}) {
@@ -35,9 +35,9 @@ sub preprocess_cut (@) { #{{{
$savedtext{$params{page}}->{$params{id}} = $params{text};
return "" if defined wantarray;
-} # }}}
+}
-sub preprocess_copy (@) { #{{{
+sub preprocess_copy (@) {
my %params=@_;
foreach my $param (qw{id text}) {
@@ -51,9 +51,9 @@ sub preprocess_copy (@) { #{{{
return IkiWiki::preprocess($params{page}, $params{destpage},
IkiWiki::filter($params{page}, $params{destpage}, $params{text})) if defined wantarray;
-} # }}}
+}
-sub preprocess_paste (@) { #{{{
+sub preprocess_paste (@) {
my %params=@_;
foreach my $param (qw{id}) {
@@ -71,6 +71,6 @@ sub preprocess_paste (@) { #{{{
return IkiWiki::preprocess($params{page}, $params{destpage},
IkiWiki::filter($params{page}, $params{destpage}, $savedtext{$params{page}}->{$params{id}}));
-} # }}}
+}
1;
diff --git a/IkiWiki/Plugin/ddate.pm b/IkiWiki/Plugin/ddate.pm
index 6c36de0a6..bb77ce59f 100644
--- a/IkiWiki/Plugin/ddate.pm
+++ b/IkiWiki/Plugin/ddate.pm
@@ -2,22 +2,22 @@
# Discordian date support fnord ikiwiki.
package IkiWiki::Plugin::ddate;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
no warnings;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "ddate", call => \&getsetup);
-} # }}}
+}
-sub getsetup { #{{{
+sub getsetup {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub IkiWiki::displaytime ($;$) { #{{{
+sub IkiWiki::formattime ($;$) {
my $time=shift;
my $format=shift;
if (! defined $format) {
@@ -36,6 +36,6 @@ sub IkiWiki::displaytime ($;$) { #{{{
my $dt = DateTime->from_epoch(epoch => $time);
my $dd = DateTime::Calendar::Discordian->from_object(object => $dt);
return $dd->strftime($format);
-} #}}}
+}
5
diff --git a/IkiWiki/Plugin/editdiff.pm b/IkiWiki/Plugin/editdiff.pm
index f5d7837fc..7df6a9ffb 100644
--- a/IkiWiki/Plugin/editdiff.pm
+++ b/IkiWiki/Plugin/editdiff.pm
@@ -4,25 +4,25 @@ package IkiWiki::Plugin::editdiff;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use HTML::Entities;
use IPC::Open2;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "editdiff", call => \&getsetup);
hook(type => "formbuilder_setup", id => "editdiff",
call => \&formbuilder_setup);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub diff ($$) { #{{{
+sub diff ($$) {
my $orig=shift;
my $content=shift;
@@ -50,9 +50,9 @@ sub diff ($$) { #{{{
return "couldn't run diff\n" if $sigpipe;
return "<pre>".encode_entities($ret)."</pre>";
-} #}}}
+}
-sub formbuilder_setup { #{{{
+sub formbuilder_setup {
my %params=@_;
my $form=$params{form};
@@ -72,6 +72,6 @@ sub formbuilder_setup { #{{{
my $diff = diff(srcfile($pagesources{$page}), $content);
$form->tmpl_param("page_preview", $diff);
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/editpage.pm b/IkiWiki/Plugin/editpage.pm
index 30c93df20..0068a6b11 100644
--- a/IkiWiki/Plugin/editpage.pm
+++ b/IkiWiki/Plugin/editpage.pm
@@ -6,19 +6,19 @@ use strict;
use IkiWiki;
use open qw{:utf8 :std};
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "editpage", call => \&getsetup);
hook(type => "refresh", id => "editpage", call => \&refresh);
hook(type => "sessioncgi", id => "editpage", call => \&IkiWiki::cgi_editpage);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
sub refresh () {
if (exists $wikistate{editpage} && exists $wikistate{editpage}{previews}) {
@@ -51,37 +51,10 @@ sub refresh () {
# Back to ikiwiki namespace for the rest, this code is very much
# internal to ikiwiki even though it's separated into a plugin,
-# and other plugins use the functions below.
+# and other plugins use the function below.
package IkiWiki;
-sub check_canedit ($$$;$) { #{{{
- my $page=shift;
- my $q=shift;
- my $session=shift;
- my $nonfatal=shift;
-
- my $canedit;
- run_hooks(canedit => sub {
- return if defined $canedit;
- my $ret=shift->($page, $q, $session);
- if (defined $ret) {
- if ($ret eq "") {
- $canedit=1;
- }
- elsif (ref $ret eq 'CODE') {
- $ret->() unless $nonfatal;
- $canedit=0;
- }
- elsif (defined $ret) {
- error($ret) unless $nonfatal;
- $canedit=0;
- }
- }
- });
- return $canedit;
-} #}}}
-
-sub cgi_editpage ($$) { #{{{
+sub cgi_editpage ($$) {
my $q=shift;
my $session=shift;
@@ -105,7 +78,6 @@ sub cgi_editpage ($$) { #{{{
header => 0,
table => 0,
template => scalar template_params("editpage.tmpl"),
- wikiname => $config{wikiname},
);
decode_form_utf8($form);
@@ -122,7 +94,7 @@ sub cgi_editpage ($$) { #{{{
my $absolute=($page =~ s#^/+##);
if (! defined $page || ! length $page ||
file_pruned($page, $config{srcdir})) {
- error("bad page name");
+ error(gettext("bad page name"));
}
my $baseurl = urlto($page, undef, 1);
@@ -340,16 +312,7 @@ sub cgi_editpage ($$) { #{{{
else {
# save page
check_canedit($page, $q, $session);
-
- # The session id is stored on the form and checked to
- # guard against CSRF. But only if the user is logged in,
- # as anonok can allow anonymous edits.
- if (defined $session->param("name")) {
- my $sid=$q->param('sid');
- if (! defined $sid || $sid ne $session->id) {
- error(gettext("Your login session has expired."));
- }
- }
+ checksessionexpiry($q, $session, $q->param('sid'));
my $exists=-e "$config{srcdir}/$file";
@@ -378,8 +341,17 @@ sub cgi_editpage ($$) { #{{{
showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
exit;
}
+
+ my $message="";
+ if (defined $form->field('comments') &&
+ length $form->field('comments')) {
+ $message=$form->field('comments');
+ }
my $content=$form->field('editcontent');
+ check_content(content => $content, page => $page,
+ cgi => $q, session => $session,
+ subject => $message);
run_hooks(editcontent => sub {
$content=shift->(
content => $content,
@@ -413,12 +385,6 @@ sub cgi_editpage ($$) { #{{{
my $conflict;
if ($config{rcs}) {
- my $message="";
- if (defined $form->field('comments') &&
- length $form->field('comments')) {
- $message=$form->field('comments');
- }
-
if (! $exists) {
rcs_add($file);
}
@@ -462,6 +428,6 @@ sub cgi_editpage ($$) { #{{{
}
exit;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/edittemplate.pm b/IkiWiki/Plugin/edittemplate.pm
index 846b4e7c8..0bafc95d0 100644
--- a/IkiWiki/Plugin/edittemplate.pm
+++ b/IkiWiki/Plugin/edittemplate.pm
@@ -3,11 +3,11 @@ package IkiWiki::Plugin::edittemplate;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use HTML::Template;
use Encode;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "edittemplate",
call => \&getsetup);
hook(type => "needsbuild", id => "edittemplate",
@@ -16,17 +16,17 @@ sub import { #{{{
call => \&preprocess);
hook(type => "formbuilder", id => "edittemplate",
call => \&formbuilder);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
foreach my $page (keys %pagestate) {
@@ -40,9 +40,9 @@ sub needsbuild (@) { #{{{
}
}
}
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
return "" if $params{page} ne $params{destpage};
@@ -62,9 +62,9 @@ sub preprocess (@) { #{{{
return sprintf(gettext("edittemplate %s registered for %s"),
htmllink($params{page}, $params{destpage}, $link),
$params{match});
-} # }}}
+}
-sub formbuilder (@) { #{{{
+sub formbuilder (@) {
my %params=@_;
my $form=$params{form};
@@ -103,9 +103,9 @@ sub formbuilder (@) { #{{{
}
}
}
-} #}}}
+}
-sub filltemplate ($$) { #{{{
+sub filltemplate ($$) {
my $template_page=shift;
my $page=shift;
@@ -136,6 +136,6 @@ sub filltemplate ($$) { #{{{
$template->param(name => $page);
return $template->output;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/embed.pm b/IkiWiki/Plugin/embed.pm
index 2a1637392..a7d38358f 100644
--- a/IkiWiki/Plugin/embed.pm
+++ b/IkiWiki/Plugin/embed.pm
@@ -3,7 +3,7 @@ package IkiWiki::Plugin::embed;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my $attribr=qr/[^<>"]+/;
@@ -43,35 +43,35 @@ my $safehtml=qr{(
my @embedded;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "embed", call => \&getsetup);
hook(type => "filter", id => "embed", call => \&filter);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub embed ($) { #{{{
+sub embed ($) {
hook(type => "format", id => "embed", call => \&format) unless @embedded;
push @embedded, shift;
return "<div class=\"embed$#embedded\"></div>";
-} #}}}
+}
-sub filter (@) { #{{{
+sub filter (@) {
my %params=@_;
$params{content} =~ s/$safehtml/embed($1)/eg;
return $params{content};
-} # }}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params=@_;
$params{content} =~ s/<div class="embed(\d+)"><\/div>/$embedded[$1]/eg;
return $params{content};
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/external.pm b/IkiWiki/Plugin/external.pm
index ba6c7d8b9..066f15cf1 100644
--- a/IkiWiki/Plugin/external.pm
+++ b/IkiWiki/Plugin/external.pm
@@ -6,7 +6,7 @@ package IkiWiki::Plugin::external;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use RPC::XML;
use RPC::XML::Parser;
use IPC::Open2;
@@ -14,7 +14,7 @@ use IO::Handle;
my %plugins;
-sub import { #{{{
+sub import {
my $self=shift;
my $plugin=shift;
return unless defined $plugin;
@@ -32,17 +32,17 @@ sub import { #{{{
$RPC::XML::ENCODING="utf-8";
rpc_call($plugins{$plugin}, "import");
-} #}}}
+}
-sub rpc_write ($$) { #{{{
+sub rpc_write ($$) {
my $fh=shift;
my $string=shift;
$fh->print($string."\n");
$fh->flush;
-} #}}}
+}
-sub rpc_call ($$;@) { #{{{
+sub rpc_call ($$;@) {
my $plugin=shift;
my $command=shift;
@@ -131,12 +131,12 @@ sub rpc_call ($$;@) { #{{{
}
return undef;
-} #}}}
+}
package IkiWiki::RPC::XML;
use Memoize;
-sub getvar ($$$) { #{{{
+sub getvar ($$$) {
my $plugin=shift;
my $varname="IkiWiki::".shift;
my $key=shift;
@@ -145,9 +145,9 @@ sub getvar ($$$) { #{{{
my $ret=$varname->{$key};
use strict 'refs';
return $ret;
-} #}}}
+}
-sub setvar ($$$;@) { #{{{
+sub setvar ($$$;@) {
my $plugin=shift;
my $varname="IkiWiki::".shift;
my $key=shift;
@@ -157,18 +157,18 @@ sub setvar ($$$;@) { #{{{
my $ret=$varname->{$key}=$value;
use strict 'refs';
return $ret;
-} #}}}
+}
-sub getstate ($$$$) { #{{{
+sub getstate ($$$$) {
my $plugin=shift;
my $page=shift;
my $id=shift;
my $key=shift;
return $IkiWiki::pagestate{$page}{$id}{$key};
-} #}}}
+}
-sub setstate ($$$$;@) { #{{{
+sub setstate ($$$$;@) {
my $plugin=shift;
my $page=shift;
my $id=shift;
@@ -176,22 +176,22 @@ sub setstate ($$$$;@) { #{{{
my $value=shift;
return $IkiWiki::pagestate{$page}{$id}{$key}=$value;
-} #}}}
+}
-sub getargv ($) { #{{{
+sub getargv ($) {
my $plugin=shift;
return \@ARGV;
-} #}}}
+}
-sub setargv ($@) { #{{{
+sub setargv ($@) {
my $plugin=shift;
my $array=shift;
@ARGV=@$array;
-} #}}}
+}
-sub inject ($@) { #{{{
+sub inject ($@) {
# Bind a given perl function name to a particular RPC request.
my $plugin=shift;
my %params=@_;
@@ -202,12 +202,20 @@ sub inject ($@) { #{{{
my $sub = sub {
IkiWiki::Plugin::external::rpc_call($plugin, $params{call}, @_)
};
+ $sub=memoize($sub) if $params{memoize};
+
+ # This will add it to the symbol table even if not present.
+ no warnings;
eval qq{*$params{name}=\$sub};
- memoize($params{name}) if $params{memoize};
+ use warnings;
+
+ # This will ensure that everywhere it was exported to sees
+ # the injected version.
+ IkiWiki::inject(name => $params{name}, call => $sub);
return 1;
-} #}}}
+}
-sub hook ($@) { #{{{
+sub hook ($@) {
# the call parameter is a function name to call, since XML RPC
# cannot pass a function reference
my $plugin=shift;
@@ -219,13 +227,13 @@ sub hook ($@) { #{{{
IkiWiki::hook(%params, call => sub {
IkiWiki::Plugin::external::rpc_call($plugin, $callback, @_);
});
-} #}}}
+}
-sub pagespec_match ($@) { #{{{
+sub pagespec_match ($@) {
# convert pagespec_match's return object into a XML RPC boolean
my $plugin=shift;
return RPC::XML::boolean->new(0 + IkiWiki::pagespec_march(@_));
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/favicon.pm b/IkiWiki/Plugin/favicon.pm
index e9204dea9..6060914c5 100644
--- a/IkiWiki/Plugin/favicon.pm
+++ b/IkiWiki/Plugin/favicon.pm
@@ -5,22 +5,22 @@ package IkiWiki::Plugin::favicon;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "favicon", call => \&getsetup);
hook(type => "pagetemplate", id => "favicon", call => \&pagetemplate);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $template=$params{template};
@@ -28,6 +28,6 @@ sub pagetemplate (@) { #{{{
if ($template->query(name => "favicon")) {
$template->param(favicon => "favicon.ico");
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/filecheck.pm b/IkiWiki/Plugin/filecheck.pm
index 27f764e3b..8575ee108 100644
--- a/IkiWiki/Plugin/filecheck.pm
+++ b/IkiWiki/Plugin/filecheck.pm
@@ -3,7 +3,7 @@ package IkiWiki::Plugin::filecheck;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %units=( #{{{ # size in bytes
B => 1,
@@ -37,9 +37,9 @@ my %units=( #{{{ # size in bytes
# ikiwiki, if you find you need larger data quantities, either modify
# yourself to add them, or travel back in time to 2008 and kill me.
# -- Joey
-); #}}}
+);
-sub parsesize ($) { #{{{
+sub parsesize ($) {
my $size=shift;
no warnings;
@@ -51,10 +51,10 @@ sub parsesize ($) { #{{{
}
}
return $base;
-} #}}}
+}
# This is provided for other plugins that want to convert back the other way.
-sub humansize ($) { #{{{
+sub humansize ($) {
my $size=shift;
foreach my $unit (reverse sort { $units{$a} <=> $units{$b} || $b cmp $a } keys %units) {
@@ -63,11 +63,11 @@ sub humansize ($) { #{{{
}
}
return $size; # near zero, or negative
-} #}}}
+}
package IkiWiki::PageSpec;
-sub match_maxsize ($$;@) { #{{{
+sub match_maxsize ($$;@) {
my $page=shift;
my $maxsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
if ($@) {
@@ -86,9 +86,9 @@ sub match_maxsize ($$;@) { #{{{
else {
return IkiWiki::SuccessReason->new("file not too large");
}
-} #}}}
+}
-sub match_minsize ($$;@) { #{{{
+sub match_minsize ($$;@) {
my $page=shift;
my $minsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
if ($@) {
@@ -107,9 +107,9 @@ sub match_minsize ($$;@) { #{{{
else {
return IkiWiki::SuccessReason->new("file not too small");
}
-} #}}}
+}
-sub match_mimetype ($$;@) { #{{{
+sub match_mimetype ($$;@) {
my $page=shift;
my $wanted=shift;
@@ -140,9 +140,9 @@ sub match_mimetype ($$;@) { #{{{
else {
return IkiWiki::SuccessReason->new("file MIME type is $mimetype");
}
-} #}}}
+}
-sub match_virusfree ($$;@) { #{{{
+sub match_virusfree ($$;@) {
my $page=shift;
my $wanted=shift;
@@ -182,9 +182,9 @@ sub match_virusfree ($$;@) { #{{{
else {
return IkiWiki::SuccessReason->new("file seems virusfree ($reason)");
}
-} #}}}
+}
-sub match_ispage ($$;@) { #{{{
+sub match_ispage ($$;@) {
my $filename=shift;
if (defined IkiWiki::pagetype($filename)) {
@@ -193,4 +193,4 @@ sub match_ispage ($$;@) { #{{{
else {
return IkiWiki::FailReason->new("file is not a wiki page");
}
-} #}}}
+}
diff --git a/IkiWiki/Plugin/format.pm b/IkiWiki/Plugin/format.pm
new file mode 100644
index 000000000..bbe3aa9fe
--- /dev/null
+++ b/IkiWiki/Plugin/format.pm
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::format;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "preprocess", id => "format", call => \&preprocess);
+}
+
+sub preprocess (@) {
+ my $format=$_[0];
+ shift; shift;
+ my $text=$_[0];
+ shift; shift;
+ my %params=@_;
+
+ if (! defined $format || ! defined $text) {
+ error(gettext("must specify format and text"));
+ }
+ elsif (! exists $IkiWiki::hooks{htmlize}{$format}) {
+ error(sprintf(gettext("unsupported page format %s"), $format));
+ }
+
+ return IkiWiki::htmlize($params{page}, $params{destpage}, $format,
+ IkiWiki::preprocess($params{page}, $params{destpage}, $text));
+}
+
+1
diff --git a/IkiWiki/Plugin/fortune.pm b/IkiWiki/Plugin/fortune.pm
index 456b63e9f..17e57dea1 100644
--- a/IkiWiki/Plugin/fortune.pm
+++ b/IkiWiki/Plugin/fortune.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::fortune;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "fortune", call => \&getsetup);
hook(type => "preprocess", id => "fortune", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
$ENV{PATH}="$ENV{PATH}:/usr/games:/usr/local/games";
my $f = `fortune 2>/dev/null`;
@@ -29,6 +29,6 @@ sub preprocess (@) { #{{{
else {
return "<pre>$f</pre>\n";
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/git.pm b/IkiWiki/Plugin/git.pm
index 14b0ab285..68b114a73 100644
--- a/IkiWiki/Plugin/git.pm
+++ b/IkiWiki/Plugin/git.pm
@@ -9,8 +9,9 @@ use open qw{:utf8 :std};
my $sha1_pattern = qr/[0-9a-fA-F]{40}/; # pattern to validate Git sha1sums
my $dummy_commit_msg = 'dummy commit'; # message to skip in recent changes
+my $no_chdir=0;
-sub import { #{{{
+sub import {
hook(type => "checkconfig", id => "git", call => \&checkconfig);
hook(type => "getsetup", id => "git", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
@@ -23,24 +24,34 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+ hook(type => "rcs", id => "rcs_receive", call => \&rcs_receive);
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (! defined $config{gitorigin_branch}) {
$config{gitorigin_branch}="origin";
}
if (! defined $config{gitmaster_branch}) {
$config{gitmaster_branch}="master";
}
- if (defined $config{git_wrapper} && length $config{git_wrapper}) {
+ if (defined $config{git_wrapper} &&
+ length $config{git_wrapper}) {
push @{$config{wrappers}}, {
wrapper => $config{git_wrapper},
wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"),
};
}
-} #}}}
+ if (defined $config{git_test_receive_wrapper} &&
+ length $config{git_test_receive_wrapper}) {
+ push @{$config{wrappers}}, {
+ test_receive => 1,
+ wrapper => $config{git_test_receive_wrapper},
+ wrappermode => (defined $config{git_wrappermode} ? $config{git_wrappermode} : "06755"),
+ };
+ }
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
@@ -60,6 +71,20 @@ sub getsetup () { #{{{
safe => 0,
rebuild => 0,
},
+ git_test_receive_wrapper => {
+ type => "string",
+ example => "/git/wiki.git/hooks/pre-receive",
+ description => "git pre-receive hook to generate",
+ safe => 0, # file
+ rebuild => 0,
+ },
+ untrusted_committers => {
+ type => "string",
+ example => [],
+ description => "unix users whose commits should be checked by the pre-receive hook",
+ safe => 0,
+ rebuild => 0,
+ },
historyurl => {
type => "string",
example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=history;f=[[file]]",
@@ -69,8 +94,8 @@ sub getsetup () { #{{{
},
diffurl => {
type => "string",
- example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=blobdiff;h=[[sha1_to]];hp=[[sha1_from]];hb=[[sha1_parent]];f=[[file]]",
- description => "gitweb url to show a diff ([[sha1_to]], [[sha1_from]], [[sha1_parent]], and [[file]] substituted)",
+ example => "http://git.example.com/gitweb.cgi?p=wiki.git;a=blobdiff;f=[[file]];h=[[sha1_to]];hp=[[sha1_from]];hb=[[sha1_commit]];hpb=[[sha1_parent]]",
+ description => "gitweb url to show a diff ([[file]], [[sha1_to]], [[sha1_from]], [[sha1_commit]], and [[sha1_parent]] substituted)",
safe => 1,
rebuild => 1,
},
@@ -88,9 +113,9 @@ sub getsetup () { #{{{
safe => 0, # paranoia
rebuild => 0,
},
-} #}}}
+}
-sub safe_git (&@) { #{{{
+sub safe_git (&@) {
# Start a child process safely without resorting /bin/sh.
# Return command output or success state (in scalar context).
@@ -103,15 +128,25 @@ sub safe_git (&@) { #{{{
if (!$pid) {
# In child.
# Git commands want to be in wc.
- chdir $config{srcdir}
- or error("Cannot chdir to $config{srcdir}: $!");
+ if (! $no_chdir) {
+ chdir $config{srcdir}
+ or error("Cannot chdir to $config{srcdir}: $!");
+ }
exec @cmdline or error("Cannot exec '@cmdline': $!");
}
# In parent.
+ # git output is probably utf-8 encoded, but may contain
+ # other encodings or invalidly encoded stuff. So do not rely
+ # on the normal utf-8 IO layer, decode it by hand.
+ binmode($OUT);
+
my @lines;
while (<$OUT>) {
+ $_=decode_utf8($_, 0);
+
chomp;
+
push @lines, $_;
}
@@ -125,9 +160,9 @@ sub safe_git (&@) { #{{{
sub run_or_die ($@) { safe_git(\&error, @_) }
sub run_or_cry ($@) { safe_git(sub { warn @_ }, @_) }
sub run_or_non ($@) { safe_git(undef, @_) }
-#}}}
-sub merge_past ($$$) { #{{{
+
+sub merge_past ($$$) {
# Unlike with Subversion, Git cannot make a 'svn merge -rN:M file'.
# Git merge commands work with the committed changes, except in the
# implicit case of '-m' of git checkout(1). So we should invent a
@@ -219,9 +254,9 @@ sub merge_past ($$$) { #{{{
error("Git merge failed!\n$failure\n") if $failure;
return $conflict;
-} #}}}
+}
-sub parse_diff_tree ($@) { #{{{
+sub parse_diff_tree ($@) {
# Parse the raw diff tree chunk and return the info hash.
# See git-diff-tree(1) for the syntax.
@@ -320,6 +355,9 @@ sub parse_diff_tree ($@) { #{{{
'file' => decode("utf8", $file),
'sha1_from' => $sha1_from[0],
'sha1_to' => $sha1_to,
+ 'mode_from' => $mode_from[0],
+ 'mode_to' => $mode_to,
+ 'status' => $status,
};
}
next;
@@ -328,17 +366,17 @@ sub parse_diff_tree ($@) { #{{{
}
return \%ci;
-} #}}}
+}
-sub git_commit_info ($;$) { #{{{
- # Return an array of commit info hashes of num commits (default: 1)
+sub git_commit_info ($;$) {
+ # Return an array of commit info hashes of num commits
# starting from the given sha1sum.
-
my ($sha1, $num) = @_;
- $num ||= 1;
+ my @opts;
+ push @opts, "--max-count=$num" if defined $num;
- my @raw_lines = run_or_die('git', 'log', "--max-count=$num",
+ my @raw_lines = run_or_die('git', 'log', @opts,
'--pretty=raw', '--raw', '--abbrev=40', '--always', '-c',
'-r', $sha1, '--', '.');
my ($prefix) = run_or_die('git', 'rev-parse', '--show-prefix');
@@ -351,11 +389,10 @@ sub git_commit_info ($;$) { #{{{
warn "Cannot parse commit info for '$sha1' commit" if !@ci;
return wantarray ? @ci : $ci[0];
-} #}}}
+}
-sub git_sha1 (;$) { #{{{
+sub git_sha1 (;$) {
# Return head sha1sum (of given file).
-
my $file = shift || q{--};
# Ignore error since a non-existing file might be given.
@@ -365,26 +402,25 @@ sub git_sha1 (;$) { #{{{
($sha1) = $sha1 =~ m/($sha1_pattern)/; # sha1 is untainted now
} else { debug("Empty sha1sum for '$file'.") }
return defined $sha1 ? $sha1 : q{};
-} #}}}
+}
-sub rcs_update () { #{{{
+sub rcs_update () {
# Update working directory.
if (length $config{gitorigin_branch}) {
run_or_cry('git', 'pull', $config{gitorigin_branch});
}
-} #}}}
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
# Return the commit sha1sum of the file when editing begins.
# This will be later used in rcs_commit if a merge is required.
-
my ($file) = @_;
return git_sha1($file);
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
# Try to commit the page; returns undef on _success_ and
# a version of the page with the rcs's conflict markers on
# failure.
@@ -403,7 +439,7 @@ sub rcs_commit ($$$;$$) { #{{{
rcs_add($file);
return rcs_commit_staged($message, $user, $ipaddr);
-} #}}}
+}
sub rcs_commit_staged ($$$) {
# Commits all staged changes. Changes can be staged using rcs_add,
@@ -413,7 +449,7 @@ sub rcs_commit_staged ($$$) {
# Set the commit author and email to the web committer.
my %env=%ENV;
if (defined $user || defined $ipaddr) {
- my $u=defined $user ? $user : $ipaddr;
+ my $u=encode_utf8(defined $user ? $user : $ipaddr);
$ENV{GIT_AUTHOR_NAME}=$u;
$ENV{GIT_AUTHOR_EMAIL}="$u\@web";
}
@@ -444,29 +480,29 @@ sub rcs_commit_staged ($$$) {
return undef; # success
}
-sub rcs_add ($) { # {{{
+sub rcs_add ($) {
# Add file to archive.
my ($file) = @_;
run_or_cry('git', 'add', $file);
-} #}}}
+}
-sub rcs_remove ($) { # {{{
+sub rcs_remove ($) {
# Remove file from archive.
my ($file) = @_;
run_or_cry('git', 'rm', '-f', $file);
-} #}}}
+}
-sub rcs_rename ($$) { # {{{
+sub rcs_rename ($$) {
my ($src, $dest) = @_;
run_or_cry('git', 'mv', '-f', $src, $dest);
-} #}}}
+}
-sub rcs_recentchanges ($) { #{{{
+sub rcs_recentchanges ($) {
# List of recent changes.
my ($num) = @_;
@@ -475,7 +511,7 @@ sub rcs_recentchanges ($) { #{{{
error($@) if $@;
my @rets;
- foreach my $ci (git_commit_info('HEAD', $num)) {
+ foreach my $ci (git_commit_info('HEAD', $num || 1)) {
# Skip redundant commits.
next if ($ci->{'comment'} && @{$ci->{'comment'}}[0] eq $dummy_commit_msg);
@@ -493,6 +529,7 @@ sub rcs_recentchanges ($) { #{{{
$diffurl =~ s/\[\[sha1_parent\]\]/$ci->{'parent'}/go;
$diffurl =~ s/\[\[sha1_from\]\]/$detail->{'sha1_from'}/go;
$diffurl =~ s/\[\[sha1_to\]\]/$detail->{'sha1_to'}/go;
+ $diffurl =~ s/\[\[sha1_commit\]\]/$sha1/go;
push @pages, {
page => pagename($file),
@@ -533,9 +570,9 @@ sub rcs_recentchanges ($) { #{{{
}
return @rets;
-} #}}}
+}
-sub rcs_diff ($) { #{{{
+sub rcs_diff ($) {
my $rev=shift;
my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint
my @lines;
@@ -550,19 +587,112 @@ sub rcs_diff ($) { #{{{
else {
return join("", @lines);
}
-} #}}}
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_getctime ($) {
my $file=shift;
# Remove srcdir prefix
$file =~ s/^\Q$config{srcdir}\E\/?//;
- my $sha1 = git_sha1($file);
- my $ci = git_commit_info($sha1);
+ my @sha1s = run_or_non('git', 'rev-list', 'HEAD', '--', $file);
+ my $ci = git_commit_info($sha1s[$#sha1s], 1);
my $ctime = $ci->{'author_epoch'};
debug("ctime for '$file': ". localtime($ctime));
return $ctime;
-} #}}}
+}
+
+sub rcs_receive () {
+ # The wiki may not be the only thing in the git repo.
+ # Determine if it is in a subdirectory by examining the srcdir,
+ # and its parents, looking for the .git directory.
+ my $subdir="";
+ my $dir=$config{srcdir};
+ while (! -d "$dir/.git") {
+ $subdir=IkiWiki::basename($dir)."/".$subdir;
+ $dir=IkiWiki::dirname($dir);
+ if (! length $dir) {
+ error("cannot determine root of git repo");
+ }
+ }
+
+ my @rets;
+ while (<>) {
+ chomp;
+ my ($oldrev, $newrev, $refname) = split(' ', $_, 3);
+
+ # only allow changes to gitmaster_branch
+ if ($refname !~ /^refs\/heads\/\Q$config{gitmaster_branch}\E$/) {
+ error sprintf(gettext("you are not allowed to change %s"), $refname);
+ }
+
+ # Avoid chdir when running git here, because the changes
+ # are in the master git repo, not the srcdir repo.
+ # The pre-recieve hook already puts us in the right place.
+ $no_chdir=1;
+ my @changes=git_commit_info($oldrev."..".$newrev);
+ $no_chdir=0;
+
+ foreach my $ci (@changes) {
+ foreach my $detail (@{ $ci->{'details'} }) {
+ my $file = $detail->{'file'};
+
+ # check that all changed files are in the
+ # subdir
+ if (length $subdir &&
+ ! ($file =~ s/^\Q$subdir\E//)) {
+ error sprintf(gettext("you are not allowed to change %s"), $file);
+ }
+
+ my ($action, $mode, $path);
+ if ($detail->{'status'} =~ /^[M]+\d*$/) {
+ $action="change";
+ $mode=$detail->{'mode_to'};
+ }
+ elsif ($detail->{'status'} =~ /^[AM]+\d*$/) {
+ $action="add";
+ $mode=$detail->{'mode_to'};
+ }
+ elsif ($detail->{'status'} =~ /^[DAM]+\d*/) {
+ $action="remove";
+ $mode=$detail->{'mode_from'};
+ }
+ else {
+ error "unknown status ".$detail->{'status'};
+ }
+
+ # test that the file mode is ok
+ if ($mode !~ /^100[64][64][64]$/) {
+ error sprintf(gettext("you cannot act on a file with mode %s"), $mode);
+ }
+ if ($action eq "change") {
+ if ($detail->{'mode_from'} ne $detail->{'mode_to'}) {
+ error gettext("you are not allowed to change file modes");
+ }
+ }
+
+ # extract attachment to temp file
+ if (($action eq 'add' || $action eq 'change') &&
+ ! pagetype($file)) {
+ eval q{use File::Temp};
+ die $@ if $@;
+ my $fh;
+ ($fh, $path)=File::Temp::tempfile("XXXXXXXXXX", UNLINK => 1);
+ if (system("git show ".$detail->{sha1_to}." > '$path'") != 0) {
+ error("failed writing temp file");
+ }
+ }
+
+ push @rets, {
+ file => $file,
+ action => $action,
+ path => $path,
+ };
+ }
+ }
+ }
+
+ return reverse @rets;
+}
1
diff --git a/IkiWiki/Plugin/goodstuff.pm b/IkiWiki/Plugin/goodstuff.pm
index ed1f4ddfc..451cd6f84 100644
--- a/IkiWiki/Plugin/goodstuff.pm
+++ b/IkiWiki/Plugin/goodstuff.pm
@@ -4,13 +4,12 @@ package IkiWiki::Plugin::goodstuff;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my @bundle=qw{
brokenlinks
img
map
- meta
more
orphans
pagecount
@@ -23,21 +22,22 @@ my @bundle=qw{
template
toc
toggle
+ repolist
};
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "goodstuff", call => \&getsetup);
foreach my $plugin (@bundle) {
IkiWiki::loadplugin($plugin);
}
-} # }}}
+}
-sub getsetup { #{{{
+sub getsetup {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/google.pm b/IkiWiki/Plugin/google.pm
index 92b9b29eb..4bba5775c 100644
--- a/IkiWiki/Plugin/google.pm
+++ b/IkiWiki/Plugin/google.pm
@@ -3,26 +3,26 @@ package IkiWiki::Plugin::google;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use URI;
my $host;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "google", call => \&getsetup);
hook(type => "checkconfig", id => "google", call => \&checkconfig);
hook(type => "pagetemplate", id => "google", call => \&pagetemplate);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (! length $config{url}) {
error(sprintf(gettext("Must specify %s when using the google search plugin"), "url"));
}
@@ -31,10 +31,10 @@ sub checkconfig () { #{{{
error(gettext("Failed to parse url, cannot determine domain name"));
}
$host=$uri->host;
-} #}}}
+}
my $form;
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
my $template=$params{template};
@@ -49,6 +49,6 @@ sub pagetemplate (@) { #{{{
$template->param(searchform => $form);
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/googlecalendar.pm b/IkiWiki/Plugin/googlecalendar.pm
deleted file mode 100644
index 81a3ad677..000000000
--- a/IkiWiki/Plugin/googlecalendar.pm
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/perl
-package IkiWiki::Plugin::googlecalendar;
-
-use warnings;
-use strict;
-use IkiWiki 2.00;
-
-sub import { #{{{
- hook(type => "getsetup", id => "googlecalendar",
- call => \&getsetup);
- hook(type => "preprocess", id => "googlecalendar",
- call => \&preprocess);
- hook(type => "format", id => "googlecalendar",
- call => \&format);
-} # }}}
-
-sub getsetup () { #{{{
- return
- plugin => {
- safe => 1,
- rebuild => undef,
- },
-} #}}}
-
-sub preprocess (@) { #{{{
- my %params=@_;
-
- # Parse the html, looking for the url to embed for the calendar.
- # Avoid XSS attacks..
- my ($url)=$params{html}=~m#iframe\s+src="http://www\.google\.com/calendar/embed\?([^"<>]+)"#;
- if (! defined $url || ! length $url) {
- error gettext("failed to find url in html")
- }
- my ($height)=$params{html}=~m#height="(\d+)"#;
- my ($width)=$params{html}=~m#width="(\d+)"#;
-
- return "<div class=\"googlecalendar\" src=\"$url\" height=\"$height\" width=\"$width\"></div>";
-} # }}}
-
-sub format (@) { #{{{
- my %params=@_;
-
- $params{content}=~s/<div class=\"googlecalendar" src="([^"]+)" height="([^"]+)" width="([^"]+)"><\/div>/gencal($1,$2,$3)/eg;
-
- return $params{content};
-} # }}}
-
-sub gencal ($$$) { #{{{
- my $url=shift;
- my $height=shift;
- my $width=shift;
- return qq{<iframe src="http://www.google.com/calendar/embed?$url" style=" border-width:0 " width="$width" frameborder="0" height="$height"></iframe>};
-} #}}}
-
-1
diff --git a/IkiWiki/Plugin/goto.pm b/IkiWiki/Plugin/goto.pm
new file mode 100644
index 000000000..3f40c5859
--- /dev/null
+++ b/IkiWiki/Plugin/goto.pm
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::goto;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "cgi", id => 'goto', call => \&cgi);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => 0,
+ }
+}
+
+# cgi_goto(CGI, [page])
+# Redirect to a specified page, or display "not found". If not specified,
+# the page param from the CGI object is used.
+sub cgi_goto ($;$) {
+ my $q = shift;
+ my $page = shift;
+
+ if (!defined $page) {
+ $page = IkiWiki::decode_utf8($q->param("page"));
+
+ if (!defined $page) {
+ error("missing page parameter");
+ }
+ }
+
+ IkiWiki::loadindex();
+
+ # If the page is internal (like a comment), see if it has a
+ # permalink. Comments do.
+ if (IkiWiki::isinternal($page) &&
+ defined $pagestate{$page}{meta}{permalink}) {
+ IkiWiki::redirect($q, $pagestate{$page}{meta}{permalink});
+ }
+
+ my $link = bestlink("", $page);
+
+ if (! length $link) {
+ IkiWiki::cgi_custom_failure(
+ $q->header(-status => "404 Not Found"),
+ IkiWiki::misctemplate(gettext("missing page"),
+ "<p>".
+ sprintf(gettext("The page %s does not exist."),
+ htmllink("", "", $page)).
+ "</p>")
+ )
+ }
+ else {
+ IkiWiki::redirect($q, urlto($link, undef, 1));
+ }
+
+ exit;
+}
+
+sub cgi ($) {
+ my $cgi=shift;
+ my $do = $cgi->param('do');
+
+ if (defined $do && ($do eq 'goto' || $do eq 'commenter' ||
+ $do eq 'recentchanges_link')) {
+ # goto is the preferred name for this; recentchanges_link and
+ # commenter are for compatibility with any saved URLs
+ cgi_goto($cgi);
+ }
+}
+
+1;
diff --git a/IkiWiki/Plugin/graphviz.pm b/IkiWiki/Plugin/graphviz.pm
index 20b419413..32e994d6b 100644
--- a/IkiWiki/Plugin/graphviz.pm
+++ b/IkiWiki/Plugin/graphviz.pm
@@ -5,27 +5,27 @@ package IkiWiki::Plugin::graphviz;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use IPC::Open2;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "graphviz", call => \&getsetup);
hook(type => "preprocess", id => "graph", call => \&graph);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
my %graphviz_programs = (
"dot" => 1, "neato" => 1, "fdp" => 1, "twopi" => 1, "circo" => 1
);
-sub render_graph (\%) { #{{{
+sub render_graph (\%) {
my %params = %{(shift)};
my $src = "$params{type} g {\n";
@@ -45,7 +45,7 @@ sub render_graph (\%) { #{{{
if (! -e "$config{destdir}/$dest") {
my $pid;
- my $sigpipe=0;;
+ my $sigpipe=0;
$SIG{PIPE}=sub { $sigpipe=1 };
$pid=open2(*IN, *OUT, "$params{prog} -Tpng");
@@ -84,9 +84,9 @@ sub render_graph (\%) { #{{{
else {
return "<img src=\"".urlto($dest, $params{destpage})."\" />\n";
}
-} #}}}
+}
-sub graph (@) { #{{{
+sub graph (@) {
my %params=@_;
$params{src} = "" unless defined $params{src};
$params{type} = "digraph" unless defined $params{type};
@@ -94,6 +94,6 @@ sub graph (@) { #{{{
error gettext("prog not a valid graphviz program") unless $graphviz_programs{$params{prog}};
return render_graph(%params);
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/haiku.pm b/IkiWiki/Plugin/haiku.pm
index eb8b786e8..5a062a276 100644
--- a/IkiWiki/Plugin/haiku.pm
+++ b/IkiWiki/Plugin/haiku.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::haiku;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "haiku", call => \&getsetup);
hook(type => "preprocess", id => "haiku", call => \&preprocess);
-} # }}}
+}
-sub getsetup { #{{{
+sub getsetup {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
my $haiku;
@@ -54,6 +54,6 @@ sub preprocess (@) { #{{{
$haiku=~s/\n/<br \/>\n/mg;
return "\n\n<blockquote><p>$haiku</p></blockquote>\n\n";
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/hnb.pm b/IkiWiki/Plugin/hnb.pm
index 40e4f9452..bd2177a06 100644
--- a/IkiWiki/Plugin/hnb.pm
+++ b/IkiWiki/Plugin/hnb.pm
@@ -10,23 +10,23 @@ package IkiWiki::Plugin::hnb;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use File::Temp qw(:mktemp);
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "hnb", call => \&getsetup);
hook(type => "htmlize", id => "hnb", call => \&htmlize);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
-} #}}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params = @_;
# hnb outputs version number etc. every time to STDOUT, so
@@ -52,6 +52,6 @@ sub htmlize (@) { #{{{
$ret =~ s/<body>.*//si;
return $ret;
-} #}}}
+}
1;
diff --git a/IkiWiki/Plugin/html.pm b/IkiWiki/Plugin/html.pm
index b75207578..a7d5e8ce9 100644
--- a/IkiWiki/Plugin/html.pm
+++ b/IkiWiki/Plugin/html.pm
@@ -4,9 +4,9 @@ package IkiWiki::Plugin::html;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "html", call => \&getsetup);
hook(type => "htmlize", id => "html", call => \&htmlize);
hook(type => "htmlize", id => "htm", call => \&htmlize);
@@ -14,19 +14,19 @@ sub import { #{{{
# ikiwiki defaults to skipping .html files as a security measure;
# make it process them so this plugin can take effect
$config{wiki_file_prune_regexps} = [ grep { !m/\\\.x\?html\?\$/ } @{$config{wiki_file_prune_regexps}} ];
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
-} #}}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
return $params{content};
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/htmlbalance.pm b/IkiWiki/Plugin/htmlbalance.pm
new file mode 100644
index 000000000..26f8e494b
--- /dev/null
+++ b/IkiWiki/Plugin/htmlbalance.pm
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::htmlbalance;
+
+# htmlbalance: Parse and re-serialize HTML to ensure balanced tags
+#
+# 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
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+use HTML::Entities;
+
+sub import {
+ hook(type => "getsetup", id => "htmlbalance", call => \&getsetup);
+ hook(type => "sanitize", id => "htmlbalance", call => \&sanitize);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ },
+}
+
+sub sanitize (@) {
+ my %params=@_;
+ my $ret = '';
+
+ eval q{use HTML::TreeBuilder};
+ error $@ if $@;
+ my $tree = HTML::TreeBuilder->new();
+ $tree->ignore_unknown(0);
+ $tree->ignore_ignorable_whitespace(0);
+ $tree->no_space_compacting(1);
+ $tree->p_strict(1);
+ $tree->store_comments(0);
+ $tree->store_declarations(0);
+ $tree->store_pis(0);
+ $tree->parse_content($params{content});
+ my @nodes = $tree->disembowel();
+ foreach my $node (@nodes) {
+ if (ref $node) {
+ $ret .= $node->as_XML();
+ chomp $ret;
+ $node->delete();
+ }
+ else {
+ $ret .= encode_entities($node);
+ }
+ }
+ $tree->delete();
+ return $ret;
+}
+
+1
diff --git a/IkiWiki/Plugin/htmlscrubber.pm b/IkiWiki/Plugin/htmlscrubber.pm
index 7398c8478..a249cdf7a 100644
--- a/IkiWiki/Plugin/htmlscrubber.pm
+++ b/IkiWiki/Plugin/htmlscrubber.pm
@@ -3,13 +3,13 @@ package IkiWiki::Plugin::htmlscrubber;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
# This regexp matches urls that are in a known safe scheme.
# Feel free to use it from other plugins.
our $safe_url_regexp;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "htmlscrubber", call => \&getsetup);
hook(type => "sanitize", id => "htmlscrubber", call => \&sanitize);
@@ -33,9 +33,9 @@ sub import { #{{{
# data is a special case. Allow data:image/*, but
# disallow data:text/javascript and everything else.
$safe_url_regexp=qr/^(?:(?:$uri_schemes):|data:image\/|[^:]+(?:$|\/))/i;
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -49,9 +49,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub sanitize (@) { #{{{
+sub sanitize (@) {
my %params=@_;
if (exists $config{htmlscrubber_skip} &&
@@ -62,10 +62,10 @@ sub sanitize (@) { #{{{
}
return scrubber()->scrub($params{content});
-} # }}}
+}
my $_scrubber;
-sub scrubber { #{{{
+sub scrubber {
return $_scrubber if defined $_scrubber;
eval q{use HTML::Scrubber};
@@ -111,6 +111,6 @@ sub scrubber { #{{{
}],
);
return $_scrubber;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/htmltidy.pm b/IkiWiki/Plugin/htmltidy.pm
index 9ba5e9592..6f3379ef4 100644
--- a/IkiWiki/Plugin/htmltidy.pm
+++ b/IkiWiki/Plugin/htmltidy.pm
@@ -9,23 +9,23 @@ package IkiWiki::Plugin::htmltidy;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use IPC::Open2;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "tidy", call => \&getsetup);
hook(type => "sanitize", id => "tidy", call => \&sanitize);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub sanitize (@) { #{{{
+sub sanitize (@) {
my %params=@_;
my $pid;
@@ -49,6 +49,6 @@ sub sanitize (@) { #{{{
return "" if $sigpipe || ! defined $ret;
return $ret;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/httpauth.pm b/IkiWiki/Plugin/httpauth.pm
index fc0cffb1e..1816c9d74 100644
--- a/IkiWiki/Plugin/httpauth.pm
+++ b/IkiWiki/Plugin/httpauth.pm
@@ -4,28 +4,28 @@ package IkiWiki::Plugin::httpauth;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "httpauth", call => \&getsetup);
hook(type => "auth", id => "httpauth", call => \&auth);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub auth ($$) { #{{{
+sub auth ($$) {
my $cgi=shift;
my $session=shift;
if (defined $cgi->remote_user()) {
$session->param("name", $cgi->remote_user());
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/img.pm b/IkiWiki/Plugin/img.pm
index 7b89ab673..d295b833b 100644
--- a/IkiWiki/Plugin/img.pm
+++ b/IkiWiki/Plugin/img.pm
@@ -5,24 +5,24 @@ package IkiWiki::Plugin::img;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %imgdefaults;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "img", call => \&getsetup);
hook(type => "preprocess", id => "img", call => \&preprocess, scan => 1);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my ($image) = $_[0] =~ /$config{wiki_file_regexp}/; # untaint
my %params=@_;
@@ -119,9 +119,9 @@ sub preprocess (@) { #{{{
}
my $imgtag='<img src="'.$imgurl.
- '" alt="'.(exists $params{alt} ? $params{alt} : '').
'" width="'.$im->Get("width").
'" height="'.$im->Get("height").'"'.
+ (exists $params{alt} ? '" alt="'.$params{alt}.'"' : '').
(exists $params{title} ? ' title="'.$params{title}.'"' : '').
(exists $params{class} ? ' class="'.$params{class}.'"' : '').
(exists $params{id} ? ' id="'.$params{id}.'"' : '').
@@ -149,6 +149,6 @@ sub preprocess (@) { #{{{
else {
return $imgtag;
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/inline.pm b/IkiWiki/Plugin/inline.pm
index 6d88c2f15..9d7d4b0fd 100644
--- a/IkiWiki/Plugin/inline.pm
+++ b/IkiWiki/Plugin/inline.pm
@@ -5,7 +5,7 @@ package IkiWiki::Plugin::inline;
use warnings;
use strict;
use Encode;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use URI;
my %knownfeeds;
@@ -13,7 +13,7 @@ my %page_numfeeds;
my @inline;
my $nested=0;
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "inline", call => \&getopt);
hook(type => "getsetup", id => "inline", call => \&getsetup);
hook(type => "checkconfig", id => "inline", call => \&checkconfig);
@@ -22,15 +22,14 @@ sub import { #{{{
call => \&IkiWiki::preprocess_inline);
hook(type => "pagetemplate", id => "inline",
call => \&IkiWiki::pagetemplate_inline);
- hook(type => "format", id => "inline", call => \&format);
+ hook(type => "format", id => "inline", call => \&format, first => 1);
# Hook to change to do pinging since it's called late.
# This ensures each page only pings once and prevents slow
# pings interrupting page builds.
- hook(type => "change", id => "inline",
- call => \&IkiWiki::pingurl);
-} # }}}
+ hook(type => "change", id => "inline", call => \&IkiWiki::pingurl);
+}
-sub getopt () { #{{{
+sub getopt () {
eval q{use Getopt::Long};
error($@) if $@;
Getopt::Long::Configure('pass_through');
@@ -43,9 +42,9 @@ sub getopt () { #{{{
push @{$config{pingurl}}, $_[1];
},
);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -86,9 +85,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (($config{rss} || $config{atom}) && ! length $config{url}) {
error(gettext("Must specify url to wiki with --url when using --rss or --atom"));
}
@@ -101,9 +100,9 @@ sub checkconfig () { #{{{
if (! exists $config{pingurl}) {
$config{pingurl}=[];
}
-} #}}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params=@_;
# Fill in the inline content generated earlier. This is actually an
@@ -112,9 +111,9 @@ sub format (@) { #{{{
delete @inline[$1,]
}eg;
return $params{content};
-} #}}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $q=shift;
my $session=shift;
@@ -149,7 +148,7 @@ package IkiWiki;
my %toping;
my %feedlinks;
-sub preprocess_inline (@) { #{{{
+sub preprocess_inline (@) {
my %params=@_;
if (! exists $params{pages}) {
@@ -161,6 +160,7 @@ sub preprocess_inline (@) { #{{{
my $atom=(($config{atom} || $config{allowatom}) && exists $params{atom}) ? yesno($params{atom}) : $config{atom};
my $quick=exists $params{quick} ? yesno($params{quick}) : 0;
my $feeds=exists $params{feeds} ? yesno($params{feeds}) : !$quick;
+ my $emptyfeeds=exists $params{emptyfeeds} ? yesno($params{emptyfeeds}) : 1;
my $feedonly=yesno($params{feedonly});
if (! exists $params{show} && ! $archive) {
$params{show}=10;
@@ -194,6 +194,13 @@ sub preprocess_inline (@) { #{{{
if (exists $params{sort} && $params{sort} eq 'title') {
@list=sort { pagetitle(basename($a)) cmp pagetitle(basename($b)) } @list;
}
+ elsif (exists $params{sort} && $params{sort} eq 'title_natural') {
+ eval q{use Sort::Naturally};
+ if ($@) {
+ error(gettext("Sort::Naturally needed for title_natural sort"));
+ }
+ @list=sort { Sort::Naturally::ncmp(pagetitle(basename($a)), pagetitle(basename($b))) } @list;
+ }
elsif (exists $params{sort} && $params{sort} eq 'mtime') {
@list=sort { $pagemtime{$b} <=> $pagemtime{$a} } @list;
}
@@ -201,7 +208,7 @@ sub preprocess_inline (@) { #{{{
@list=sort { $pagectime{$b} <=> $pagectime{$a} } @list;
}
else {
- return sprintf(gettext("unknown sort type %s"), $params{sort});
+ error sprintf(gettext("unknown sort type %s"), $params{sort});
}
if (yesno($params{reverse})) {
@@ -232,29 +239,51 @@ sub preprocess_inline (@) { #{{{
# that if they are removed or otherwise changed, the inline will be
# sure to be updated.
add_depends($params{page}, join(" or ", $#list >= $#feedlist ? @list : @feedlist));
-
- my $feednum="";
-
- my $feedid=join("\0", map { $_."\0".$params{$_} } sort keys %params);
- if (exists $knownfeeds{$feedid}) {
- $feednum=$knownfeeds{$feedid};
+
+ if ($feeds && exists $params{feedpages}) {
+ @feedlist=grep { pagespec_match($_, $params{feedpages}, location => $params{page}) } @feedlist;
}
- else {
- if (exists $page_numfeeds{$params{destpage}}) {
- if ($feeds) {
- $feednum=$knownfeeds{$feedid}=++$page_numfeeds{$params{destpage}};
+
+ my ($feedbase, $feednum);
+ if ($feeds) {
+ # Ensure that multiple feeds on a page go to unique files.
+
+ # Feedfile can lead to conflicts if usedirs is not enabled,
+ # so avoid supporting it in that case.
+ delete $params{feedfile} if ! $config{usedirs};
+ # Tight limits on legal feedfiles, to avoid security issues
+ # and conflicts.
+ if (defined $params{feedfile}) {
+ if ($params{feedfile} =~ /\// ||
+ $params{feedfile} !~ /$config{wiki_file_regexp}/) {
+ error("illegal feedfile");
}
+ $params{feedfile}=possibly_foolish_untaint($params{feedfile});
+ }
+ $feedbase=targetpage($params{destpage}, "", $params{feedfile});
+
+ my $feedid=join("\0", $feedbase, map { $_."\0".$params{$_} } sort keys %params);
+ if (exists $knownfeeds{$feedid}) {
+ $feednum=$knownfeeds{$feedid};
}
else {
- $feednum=$knownfeeds{$feedid}="";
- if ($feeds) {
- $page_numfeeds{$params{destpage}}=1;
+ if (exists $page_numfeeds{$params{destpage}}{$feedbase}) {
+ if ($feeds) {
+ $feednum=$knownfeeds{$feedid}=++$page_numfeeds{$params{destpage}}{$feedbase};
+ }
+ }
+ else {
+ $feednum=$knownfeeds{$feedid}="";
+ if ($feeds) {
+ $page_numfeeds{$params{destpage}}{$feedbase}=1;
+ }
}
}
}
- my $rssurl=basename(rsspage($params{destpage}).$feednum) if $feeds && $rss;
- my $atomurl=basename(atompage($params{destpage}).$feednum) if $feeds && $atom;
+ my $rssurl=abs2rel($feedbase."rss".$feednum, dirname(htmlpage($params{destpage}))) if $feeds && $rss;
+ my $atomurl=abs2rel($feedbase."atom".$feednum, dirname(htmlpage($params{destpage}))) if $feeds && $atom;
+
my $ret="";
if (length $config{cgiurl} && ! $params{preview} && (exists $params{rootpage} ||
@@ -285,8 +314,12 @@ sub preprocess_inline (@) { #{{{
gettext("Add a new post titled:"));
}
$ret.=$formtemplate->output;
+
+ # The post form includes the feed buttons, so
+ # emptyfeeds cannot be hidden.
+ $emptyfeeds=1;
}
- elsif ($feeds && !$params{preview}) {
+ elsif ($feeds && !$params{preview} && ($emptyfeeds || @feedlist)) {
# Add feed buttons.
my $linktemplate=template("feedlink.tmpl", blind_cache => 1);
$linktemplate->param(rssurl => $rssurl) if $rss;
@@ -298,7 +331,7 @@ sub preprocess_inline (@) { #{{{
require HTML::Template;
my @params=IkiWiki::template_params($params{template}.".tmpl", blind_cache => 1);
if (! @params) {
- return sprintf(gettext("nonexistant template %s"), $params{template});
+ error sprintf(gettext("nonexistant template %s"), $params{template});
}
my $template=HTML::Template->new(@params) unless $raw;
@@ -314,6 +347,7 @@ sub preprocess_inline (@) { #{{{
$template->param(content => $content);
}
$template->param(pageurl => urlto(bestlink($params{page}, $page), $params{destpage}));
+ $template->param(inlinepage => $page);
$template->param(title => pagetitle(basename($page)));
$template->param(ctime => displaytime($pagectime{$page}, $params{timeformat}));
$template->param(mtime => displaytime($pagemtime{$page}, $params{timeformat}));
@@ -363,30 +397,26 @@ sub preprocess_inline (@) { #{{{
}
}
- if ($feeds) {
- if (exists $params{feedpages}) {
- @feedlist=grep { pagespec_match($_, $params{feedpages}, location => $params{page}) } @feedlist;
- }
-
+ if ($feeds && ($emptyfeeds || @feedlist)) {
if ($rss) {
- my $rssp=rsspage($params{destpage}).$feednum;
+ my $rssp=$feedbase."rss".$feednum;
will_render($params{destpage}, $rssp);
if (! $params{preview}) {
writefile($rssp, $config{destdir},
genfeed("rss",
$config{url}."/".$rssp, $desc, $params{guid}, $params{destpage}, @feedlist));
$toping{$params{destpage}}=1 unless $config{rebuild};
- $feedlinks{$params{destpage}}=qq{<link rel="alternate" type="application/rss+xml" title="$desc (RSS)" href="$rssurl" />};
+ $feedlinks{$params{destpage}}.=qq{<link rel="alternate" type="application/rss+xml" title="$desc (RSS)" href="$rssurl" />};
}
}
if ($atom) {
- my $atomp=atompage($params{destpage}).$feednum;
+ my $atomp=$feedbase."atom".$feednum;
will_render($params{destpage}, $atomp);
if (! $params{preview}) {
writefile($atomp, $config{destdir},
genfeed("atom", $config{url}."/".$atomp, $desc, $params{guid}, $params{destpage}, @feedlist));
$toping{$params{destpage}}=1 unless $config{rebuild};
- $feedlinks{$params{destpage}}=qq{<link rel="alternate" type="application/atom+xml" title="$desc (Atom)" href="$atomurl" />};
+ $feedlinks{$params{destpage}}.=qq{<link rel="alternate" type="application/atom+xml" title="$desc (Atom)" href="$atomurl" />};
}
}
}
@@ -394,18 +424,18 @@ sub preprocess_inline (@) { #{{{
return $ret if $raw || $nested;
push @inline, $ret;
return "<div class=\"inline\" id=\"$#inline\"></div>\n\n";
-} #}}}
+}
-sub pagetemplate_inline (@) { #{{{
+sub pagetemplate_inline (@) {
my %params=@_;
my $page=$params{page};
my $template=$params{template};
$template->param(feedlinks => $feedlinks{$page})
if exists $feedlinks{$page} && $template->query(name => "feedlinks");
-} #}}}
+}
-sub get_inline_content ($$) { #{{{
+sub get_inline_content ($$) {
my $page=shift;
my $destpage=shift;
@@ -424,9 +454,9 @@ sub get_inline_content ($$) { #{{{
else {
return "";
}
-} #}}}
+}
-sub date_822 ($) { #{{{
+sub date_822 ($) {
my $time=shift;
my $lc_time=POSIX::setlocale(&POSIX::LC_TIME);
@@ -434,9 +464,9 @@ sub date_822 ($) { #{{{
my $ret=POSIX::strftime("%a, %d %b %Y %H:%M:%S %z", localtime($time));
POSIX::setlocale(&POSIX::LC_TIME, $lc_time);
return $ret;
-} #}}}
+}
-sub date_3339 ($) { #{{{
+sub date_3339 ($) {
my $time=shift;
my $lc_time=POSIX::setlocale(&POSIX::LC_TIME);
@@ -444,9 +474,9 @@ sub date_3339 ($) { #{{{
my $ret=POSIX::strftime("%Y-%m-%dT%H:%M:%SZ", gmtime($time));
POSIX::setlocale(&POSIX::LC_TIME, $lc_time);
return $ret;
-} #}}}
+}
-sub absolute_urls ($$) { #{{{
+sub absolute_urls ($$) {
# sucky sub because rss sucks
my $content=shift;
my $baseurl=shift;
@@ -467,17 +497,9 @@ sub absolute_urls ($$) { #{{{
$content=~s/(<a(?:\s+(?:class|id)\s*="?\w+"?)?)\s+href=\s*"(?!\w+:)(\/[^"]*)"/$1 href="$urltop$2"/mig;
$content=~s/(<img(?:\s+(?:class|id|width|height)\s*="?\w+"?)*)\s+src=\s*"(?!\w+:)(\/[^"]*)"/$1 src="$urltop$2"/mig;
return $content;
-} #}}}
-
-sub rsspage ($) { #{{{
- return targetpage(shift, "rss");
-} #}}}
-
-sub atompage ($) { #{{{
- return targetpage(shift, "atom");
-} #}}}
+}
-sub genfeed ($$$$$@) { #{{{
+sub genfeed ($$$$$@) {
my $feedtype=shift;
my $feedurl=shift;
my $feeddesc=shift;
@@ -504,9 +526,15 @@ sub genfeed ($$$$$@) { #{{{
mdate_3339 => date_3339($pagemtime{$p}),
);
- if (exists $pagestate{$p} &&
- exists $pagestate{$p}{meta}{guid}) {
- $itemtemplate->param(guid => $pagestate{$p}{meta}{guid});
+ if (exists $pagestate{$p}) {
+ if (exists $pagestate{$p}{meta}{guid}) {
+ $itemtemplate->param(guid => $pagestate{$p}{meta}{guid});
+ }
+
+ if (exists $pagestate{$p}{meta}{updated}) {
+ $itemtemplate->param(mdate_822 => date_822($pagestate{$p}{meta}{updated}));
+ $itemtemplate->param(mdate_3339 => date_3339($pagestate{$p}{meta}{updated}));
+ }
}
if ($itemtemplate->query(name => "enclosure")) {
@@ -562,9 +590,9 @@ sub genfeed ($$$$$@) { #{{{
});
return $template->output;
-} #}}}
+}
-sub pingurl (@) { #{{{
+sub pingurl (@) {
return unless @{$config{pingurl}} && %toping;
eval q{require RPC::XML::Client};
@@ -610,6 +638,6 @@ sub pingurl (@) { #{{{
}
exit 0; # daemon done
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/link.pm b/IkiWiki/Plugin/link.pm
index 0638d4bdd..b79273f96 100644
--- a/IkiWiki/Plugin/link.pm
+++ b/IkiWiki/Plugin/link.pm
@@ -3,27 +3,27 @@ package IkiWiki::Plugin::link;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my $link_regexp;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "link", call => \&getsetup);
hook(type => "checkconfig", id => "link", call => \&checkconfig);
hook(type => "linkify", id => "link", call => \&linkify);
hook(type => "scan", id => "link", call => \&scan);
hook(type => "renamepage", id => "link", call => \&renamepage);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if ($config{prefix_directives}) {
$link_regexp = qr{
\[\[(?=[^!]) # beginning of link
@@ -58,9 +58,9 @@ sub checkconfig () { #{{{
\]\] # end of link
}x,
}
-} #}}}
+}
-sub linkify (@) { #{{{
+sub linkify (@) {
my %params=@_;
my $page=$params{page};
my $destpage=$params{destpage};
@@ -78,9 +78,9 @@ sub linkify (@) { #{{{
}eg;
return $params{content};
-} #}}}
+}
-sub scan (@) { #{{{
+sub scan (@) {
my %params=@_;
my $page=$params{page};
my $content=$params{content};
@@ -88,9 +88,9 @@ sub scan (@) { #{{{
while ($content =~ /(?<!\\)$link_regexp/g) {
push @{$links{$page}}, linkpage($2);
}
-} # }}}
+}
-sub renamepage (@) { #{{{
+sub renamepage (@) {
my %params=@_;
my $page=$params{page};
my $old=$params{oldpage};
@@ -118,6 +118,6 @@ sub renamepage (@) { #{{{
}eg;
return $params{content};
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/linkmap.pm b/IkiWiki/Plugin/linkmap.pm
index 81ee665c8..941ed5f36 100644
--- a/IkiWiki/Plugin/linkmap.pm
+++ b/IkiWiki/Plugin/linkmap.pm
@@ -3,27 +3,27 @@ package IkiWiki::Plugin::linkmap;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use IPC::Open2;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "linkmap", call => \&getsetup);
hook(type => "preprocess", id => "linkmap", call => \&preprocess);
hook(type => "format", id => "linkmap", call => \&format);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
my $mapnum=0;
my %maps;
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages}="*" unless defined $params{pages};
@@ -39,17 +39,17 @@ sub preprocess (@) { #{{{
$mapnum++;
$maps{$mapnum}=\%params;
return "<div class=\"linkmap$mapnum\"></div>";
-} # }}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params=@_;
$params{content}=~s/<div class=\"linkmap(\d+)"><\/div>/genmap($1)/eg;
return $params{content};
-} # }}}
+}
-sub genmap ($) { #{{{
+sub genmap ($) {
my $mapnum=shift;
return "" unless exists $maps{$mapnum};
my %params=%{$maps{$mapnum}};
@@ -106,6 +106,6 @@ sub genmap ($) { #{{{
error gettext("failed to run dot") if $sigpipe;
return $ret;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/listdirectives.pm b/IkiWiki/Plugin/listdirectives.pm
index fc8927ccb..d2cebca34 100644
--- a/IkiWiki/Plugin/listdirectives.pm
+++ b/IkiWiki/Plugin/listdirectives.pm
@@ -4,17 +4,17 @@ package IkiWiki::Plugin::listdirectives;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
add_underlay("directives");
hook(type => "getsetup", id => "listdirectives", call => \&getsetup);
hook(type => "checkconfig", id => "listdirectives", call => \&checkconfig);
hook(type => "needsbuild", id => "listdirectives", call => \&needsbuild);
hook(type => "preprocess", id => "listdirectives", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -27,28 +27,27 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
my @fulllist;
-my @earlylist;
+my @shortlist;
my $pluginstring;
-sub checkconfig () { #{{{
+sub checkconfig () {
if (! defined $config{directive_description_dir}) {
$config{directive_description_dir} = "ikiwiki/directive";
}
else {
$config{directive_description_dir} =~ s/\/+$//;
}
+}
- @earlylist = sort keys %{$IkiWiki::hooks{preprocess}};
-} #}}}
-
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
@fulllist = sort keys %{$IkiWiki::hooks{preprocess}};
- $pluginstring = join(' ', @earlylist) . " : " . join(' ', @fulllist);
+ @shortlist = grep { ! $IkiWiki::hooks{preprocess}{$_}{shortcut} } @fulllist;
+ $pluginstring = join(' ', @shortlist) . " : " . join(' ', @fulllist);
foreach my $page (keys %pagestate) {
if (exists $pagestate{$page}{listdirectives}{shown}) {
@@ -64,9 +63,9 @@ sub needsbuild (@) { #{{{
}
}
}
-} # }}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$pagestate{$params{destpage}}{listdirectives}{shown}=$pluginstring;
@@ -77,7 +76,7 @@ sub preprocess (@) { #{{{
@pluginlist = @fulllist;
}
else {
- @pluginlist = @earlylist;
+ @pluginlist = @shortlist;
}
my $result = '<ul class="listdirectives">';
@@ -93,6 +92,6 @@ sub preprocess (@) { #{{{
$result .= "</ul>";
return $result;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/lockedit.pm b/IkiWiki/Plugin/lockedit.pm
index f6cac6cdd..0fa329251 100644
--- a/IkiWiki/Plugin/lockedit.pm
+++ b/IkiWiki/Plugin/lockedit.pm
@@ -3,16 +3,14 @@ package IkiWiki::Plugin::lockedit;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "lockedit", call => \&getsetup);
hook(type => "canedit", id => "lockedit", call => \&canedit);
- hook(type => "formbuilder_setup", id => "lockedit",
- call => \&formbuilder_setup);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -26,9 +24,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub canedit ($$) { #{{{
+sub canedit ($$) {
my $page=shift;
my $cgi=shift;
my $session=shift;
@@ -52,63 +50,7 @@ sub canedit ($$) { #{{{
}
}
- # XXX deprecated, should be removed eventually
- foreach my $admin (@{$config{adminuser}}) {
- if (pagespec_match($page, IkiWiki::userinfo_get($admin, "locked_pages"),
- user => $session->param("name"),
- ip => $ENV{REMOTE_ADDR},
- )) {
- if (! defined $user ||
- ! IkiWiki::userinfo_get($session->param("name"), "regdate")) {
- return sub { IkiWiki::needsignin($cgi, $session) };
- }
- else {
- return sprintf(gettext("%s is locked and cannot be edited"),
- htmllink("", "", $page, noimageinline => 1));
- }
- }
- }
-
return undef;
-} #}}}
-
-sub formbuilder_setup (@) { #{{{
- my %params=@_;
-
- # XXX deprecated, should be removed eventually
- my $form=$params{form};
- if ($form->title eq "preferences") {
- my $session=$params{session};
- my $cgi=$params{cgi};
- my $user_name=$session->param("name");
-
- $form->field(name => "locked_pages", size => 50,
- fieldset => "admin",
- comment => "deprecated; please move to locked_pages in setup file"
- );
- if (! IkiWiki::is_admin($user_name)) {
- $form->field(name => "locked_pages", type => "hidden");
- }
- if (! $form->submitted) {
- my $value=IkiWiki::userinfo_get($user_name, "locked_pages");
- if (length $value) {
- $form->field(name => "locked_pages", force => 1, value => $value);
- }
- else {
- $form->field(name => "locked_pages", type => "hidden");
- }
- }
- if ($form->submitted && $form->submitted eq 'Save Preferences') {
- if (defined $form->field("locked_pages")) {
- IkiWiki::userinfo_set($user_name, "locked_pages",
- $form->field("locked_pages")) ||
- error("failed to set locked_pages");
- if (! length $form->field("locked_pages")) {
- $form->field(name => "locked_pages", type => "hidden");
- }
- }
- }
- }
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/map.pm b/IkiWiki/Plugin/map.pm
index 18c584a30..328493116 100644
--- a/IkiWiki/Plugin/map.pm
+++ b/IkiWiki/Plugin/map.pm
@@ -9,22 +9,22 @@ package IkiWiki::Plugin::map;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "map", call => \&getsetup);
hook(type => "preprocess", id => "map", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages}="*" unless defined $params{pages};
@@ -144,6 +144,6 @@ sub preprocess (@) { #{{{
}
$map .= "</div>\n";
return $map;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/mdwn.pm b/IkiWiki/Plugin/mdwn.pm
index 6c1d2ef3c..0e134c822 100644
--- a/IkiWiki/Plugin/mdwn.pm
+++ b/IkiWiki/Plugin/mdwn.pm
@@ -4,14 +4,14 @@ package IkiWiki::Plugin::mdwn;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "mdwn", call => \&getsetup);
hook(type => "htmlize", id => "mdwn", call => \&htmlize);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -24,10 +24,10 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
my $markdown_sub;
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
my $content = $params{content};
@@ -83,6 +83,6 @@ sub htmlize (@) { #{{{
$content=Encode::decode_utf8($content);
return $content;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/mercurial.pm b/IkiWiki/Plugin/mercurial.pm
index 7aceebcdb..11fdec529 100644
--- a/IkiWiki/Plugin/mercurial.pm
+++ b/IkiWiki/Plugin/mercurial.pm
@@ -7,7 +7,7 @@ use IkiWiki;
use Encode;
use open qw{:utf8 :std};
-sub import { #{{{
+sub import {
hook(type => "checkconfig", id => "mercurial", call => \&checkconfig);
hook(type => "getsetup", id => "mercurial", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
@@ -20,18 +20,18 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (exists $config{mercurial_wrapper} && length $config{mercurial_wrapper}) {
push @{$config{wrappers}}, {
wrapper => $config{mercurial_wrapper},
wrappermode => (defined $config{mercurial_wrappermode} ? $config{mercurial_wrappermode} : "06755"),
};
}
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
@@ -65,9 +65,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub mercurial_log ($) { #{{{
+sub mercurial_log ($) {
my $out = shift;
my @infos;
@@ -111,20 +111,20 @@ sub mercurial_log ($) { #{{{
close $out;
return @infos;
-} #}}}
+}
-sub rcs_update () { #{{{
+sub rcs_update () {
my @cmdline = ("hg", "-q", "-R", "$config{srcdir}", "update");
if (system(@cmdline) != 0) {
warn "'@cmdline' failed: $!";
}
-} #}}}
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
return "";
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
my ($file, $message, $rcstoken, $user, $ipaddr) = @_;
if (defined $user) {
@@ -149,7 +149,7 @@ sub rcs_commit ($$$;$$) { #{{{
}
return undef; # success
-} #}}}
+}
sub rcs_commit_staged ($$$) {
# Commits all staged changes. Changes can be staged using rcs_add,
@@ -159,28 +159,28 @@ sub rcs_commit_staged ($$$) {
error("rcs_commit_staged not implemented for mercurial"); # TODO
}
-sub rcs_add ($) { # {{{
+sub rcs_add ($) {
my ($file) = @_;
my @cmdline = ("hg", "-q", "-R", "$config{srcdir}", "add", "$config{srcdir}/$file");
if (system(@cmdline) != 0) {
warn "'@cmdline' failed: $!";
}
-} #}}}
+}
-sub rcs_remove ($) { # {{{
+sub rcs_remove ($) {
my ($file) = @_;
error("rcs_remove not implemented for mercurial"); # TODO
-} #}}}
+}
-sub rcs_rename ($$) { # {{{
+sub rcs_rename ($$) {
my ($src, $dest) = @_;
error("rcs_rename not implemented for mercurial"); # TODO
-} #}}}
+}
-sub rcs_recentchanges ($) { #{{{
+sub rcs_recentchanges ($) {
my ($num) = @_;
my @cmdline = ("hg", "-R", $config{srcdir}, "log", "-v", "-l", $num,
@@ -217,7 +217,7 @@ sub rcs_recentchanges ($) { #{{{
push @ret, {
rev => $info->{"changeset"},
user => $user,
- committype => "mercurial",
+ committype => "hg",
when => str2time($info->{"date"}),
message => [@message],
pages => [@pages],
@@ -225,18 +225,18 @@ sub rcs_recentchanges ($) { #{{{
}
return @ret;
-} #}}}
+}
-sub rcs_diff ($) { #{{{
+sub rcs_diff ($) {
# TODO
-} #}}}
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_getctime ($) {
my ($file) = @_;
# XXX filename passes through the shell here, should try to avoid
# that just in case
- my @cmdline = ("hg", "-R", $config{srcdir}, "log", "-v", "-l", '1',
+ my @cmdline = ("hg", "-R", $config{srcdir}, "log", "-v",
"--style", "default", "$config{srcdir}/$file");
open (my $out, "@cmdline |");
@@ -249,8 +249,8 @@ sub rcs_getctime ($) { #{{{
eval q{use Date::Parse};
error($@) if $@;
- my $ctime = str2time($log[0]->{"date"});
+ my $ctime = str2time($log[$#log]->{"date"});
return $ctime;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/meta.pm b/IkiWiki/Plugin/meta.pm
index 8d444109f..4a22fed30 100644
--- a/IkiWiki/Plugin/meta.pm
+++ b/IkiWiki/Plugin/meta.pm
@@ -4,26 +4,26 @@ package IkiWiki::Plugin::meta;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %metaheaders;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "meta", call => \&getsetup);
hook(type => "needsbuild", id => "meta", call => \&needsbuild);
hook(type => "preprocess", id => "meta", call => \&preprocess, scan => 1);
hook(type => "pagetemplate", id => "meta", call => \&pagetemplate);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
foreach my $page (keys %pagestate) {
if (exists $pagestate{$page}{meta}) {
@@ -38,16 +38,17 @@ sub needsbuild (@) { #{{{
}
}
-sub scrub ($) { #{{{
+sub scrub ($$) {
if (IkiWiki::Plugin::htmlscrubber->can("sanitize")) {
- return IkiWiki::Plugin::htmlscrubber::sanitize(content => shift);
+ return IkiWiki::Plugin::htmlscrubber::sanitize(
+ content => shift, destpage => shift);
}
else {
return shift;
}
-} #}}}
+}
-sub safeurl ($) { #{{{
+sub safeurl ($) {
my $url=shift;
if (exists $IkiWiki::Plugin::htmlscrubber::{safe_url_regexp} &&
defined $IkiWiki::Plugin::htmlscrubber::safe_url_regexp) {
@@ -56,9 +57,9 @@ sub safeurl ($) { #{{{
else {
return 1;
}
-} #}}}
+}
-sub htmlize ($$$) { #{{{
+sub htmlize ($$$) {
my $page = shift;
my $destpage = shift;
@@ -67,7 +68,7 @@ sub htmlize ($$$) { #{{{
IkiWiki::preprocess($page, $destpage, shift)));
}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
return "" unless @_;
my %params=@_;
my $key=shift;
@@ -120,6 +121,20 @@ sub preprocess (@) { #{{{
$pagestate{$page}{meta}{authorurl}=$value if safeurl($value);
# fallthrough
}
+ elsif ($key eq 'date') {
+ eval q{use Date::Parse};
+ if (! $@) {
+ my $time = str2time($value);
+ $IkiWiki::pagectime{$page}=$time if defined $time;
+ }
+ }
+ elsif ($key eq 'updated') {
+ eval q{use Date::Parse};
+ if (! $@) {
+ my $time = str2time($value);
+ $pagestate{$page}{meta}{updated}=$time if defined $time;
+ }
+ }
if (! defined wantarray) {
# avoid collecting duplicate data during scan pass
@@ -127,17 +142,10 @@ sub preprocess (@) { #{{{
}
# Metadata collection that happens only during preprocessing pass.
- if ($key eq 'date') {
- eval q{use Date::Parse};
- if (! $@) {
- my $time = str2time($value);
- $IkiWiki::pagectime{$page}=$time if defined $time;
- }
- }
- elsif ($key eq 'permalink') {
+ if ($key eq 'permalink') {
if (safeurl($value)) {
$pagestate{$page}{meta}{permalink}=$value;
- push @{$metaheaders{$page}}, scrub('<link rel="bookmark" href="'.encode_entities($value).'" />');
+ push @{$metaheaders{$page}}, scrub('<link rel="bookmark" href="'.encode_entities($value).'" />', $destpage);
}
}
elsif ($key eq 'stylesheet') {
@@ -206,7 +214,7 @@ sub preprocess (@) { #{{{
my $delay=int(exists $params{delay} ? $params{delay} : 0);
my $redir="<meta http-equiv=\"refresh\" content=\"$delay; URL=$value\" />";
if (! $safe) {
- $redir=scrub($redir);
+ $redir=scrub($redir, $destpage);
}
push @{$metaheaders{$page}}, $redir;
}
@@ -216,7 +224,7 @@ sub preprocess (@) { #{{{
join(" ", map {
encode_entities($_)."=\"".encode_entities(decode_entities($params{$_}))."\""
} keys %params).
- " />\n");
+ " />\n", $destpage);
}
}
elsif ($key eq 'robots') {
@@ -225,13 +233,13 @@ sub preprocess (@) { #{{{
}
else {
push @{$metaheaders{$page}}, scrub('<meta name="'.encode_entities($key).
- '" content="'.encode_entities($value).'" />');
+ '" content="'.encode_entities($value).'" />', $destpage);
}
return "";
-} # }}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
my $destpage=$params{destpage};
@@ -259,9 +267,9 @@ sub pagetemplate (@) { #{{{
$template->param($field => htmlize($page, $destpage, $pagestate{$page}{meta}{$field}));
}
}
-} # }}}
+}
-sub match { #{{{
+sub match {
my $field=shift;
my $page=shift;
@@ -287,28 +295,28 @@ sub match { #{{{
else {
return IkiWiki::FailReason->new("$page does not have a $field");
}
-} #}}}
+}
package IkiWiki::PageSpec;
-sub match_title ($$;@) { #{{{
+sub match_title ($$;@) {
IkiWiki::Plugin::meta::match("title", @_);
-} #}}}
+}
-sub match_author ($$;@) { #{{{
+sub match_author ($$;@) {
IkiWiki::Plugin::meta::match("author", @_);
-} #}}}
+}
-sub match_authorurl ($$;@) { #{{{
+sub match_authorurl ($$;@) {
IkiWiki::Plugin::meta::match("authorurl", @_);
-} #}}}
+}
-sub match_license ($$;@) { #{{{
+sub match_license ($$;@) {
IkiWiki::Plugin::meta::match("license", @_);
-} #}}}
+}
-sub match_copyright ($$;@) { #{{{
+sub match_copyright ($$;@) {
IkiWiki::Plugin::meta::match("copyright", @_);
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/mirrorlist.pm b/IkiWiki/Plugin/mirrorlist.pm
index aab60c435..737dcf767 100644
--- a/IkiWiki/Plugin/mirrorlist.pm
+++ b/IkiWiki/Plugin/mirrorlist.pm
@@ -3,14 +3,14 @@ package IkiWiki::Plugin::mirrorlist;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "mirrorlist", call => \&getsetup);
hook(type => "pagetemplate", id => "mirrorlist", call => \&pagetemplate);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -23,9 +23,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $template=$params{template};
@@ -34,9 +34,9 @@ sub pagetemplate (@) { #{{{
$value.=mirrorlist($params{page});
$template->param(extrafooter => $value);
}
-} # }}}
+}
-sub mirrorlist ($) { #{{{
+sub mirrorlist ($) {
my $page=shift;
return "<p>".
(keys %{$config{mirrorlist}} > 1 ? gettext("Mirrors") : gettext("Mirror")).
@@ -49,6 +49,6 @@ sub mirrorlist ($) { #{{{
} keys %{$config{mirrorlist}}
).
"</p>";
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/monotone.pm b/IkiWiki/Plugin/monotone.pm
index f31a8606b..bdb564a71 100644
--- a/IkiWiki/Plugin/monotone.pm
+++ b/IkiWiki/Plugin/monotone.pm
@@ -10,7 +10,7 @@ use Date::Format qw(time2str);
my $sha1_pattern = qr/[0-9a-fA-F]{40}/; # pattern to validate sha1sums
-sub import { #{{{
+sub import {
hook(type => "checkconfig", id => "monotone", call => \&checkconfig);
hook(type => "getsetup", id => "monotone", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
@@ -23,9 +23,9 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (!defined($config{mtnrootdir})) {
$config{mtnrootdir} = $config{srcdir};
}
@@ -61,9 +61,9 @@ sub checkconfig () { #{{{
wrappermode => (defined $config{mtn_wrappermode} ? $config{mtn_wrappermode} : "06755"),
};
}
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
@@ -117,9 +117,9 @@ sub getsetup () { #{{{
safe => 0, # path
rebuild => 0,
},
-} #}}}
+}
-sub get_rev () { #{{{
+sub get_rev () {
my $sha1 = `mtn --root=$config{mtnrootdir} automate get_base_revision_id`;
($sha1) = $sha1 =~ m/($sha1_pattern)/; # sha1 is untainted now
@@ -128,9 +128,9 @@ sub get_rev () { #{{{
}
return $sha1;
-} #}}}
+}
-sub get_rev_auto ($) { #{{{
+sub get_rev_auto ($) {
my $automator=shift;
my @results = $automator->call("get_base_revision_id");
@@ -142,9 +142,9 @@ sub get_rev_auto ($) { #{{{
}
return $sha1;
-} #}}}
+}
-sub mtn_merge ($$$$) { #{{{
+sub mtn_merge ($$$$) {
my $leftRev=shift;
my $rightRev=shift;
my $branch=shift;
@@ -172,9 +172,9 @@ sub mtn_merge ($$$$) { #{{{
debug("merged $leftRev, $rightRev to make $mergeRev");
return $mergeRev;
-} #}}}
+}
-sub commit_file_to_new_rev ($$$$$$$$) { #{{{
+sub commit_file_to_new_rev ($$$$$$$$) {
my $automator=shift;
my $wsfilename=shift;
my $oldFileID=shift;
@@ -219,9 +219,9 @@ sub commit_file_to_new_rev ($$$$$$$$) { #{{{
debug("Added certs for rev: $newRevID");
return $newRevID;
-} #}}}
+}
-sub read_certs ($$) { #{{{
+sub read_certs ($$) {
my $automator=shift;
my $rev=shift;
my @results = $automator->call("certs", $rev);
@@ -239,9 +239,9 @@ sub read_certs ($$) { #{{{
}
return @ret;
-} #}}}
+}
-sub get_changed_files ($$) { #{{{
+sub get_changed_files ($$) {
my $automator=shift;
my $rev=shift;
@@ -261,9 +261,9 @@ sub get_changed_files ($$) { #{{{
}
return @ret;
-} #}}}
+}
-sub rcs_update () { #{{{
+sub rcs_update () {
chdir $config{srcdir}
or error("Cannot chdir to $config{srcdir}: $!");
@@ -278,9 +278,9 @@ sub rcs_update () { #{{{
if (system("mtn", "--root=$config{mtnrootdir}", "update", "--quiet") != 0) {
debug("monotone update failed");
}
-} #}}}
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
my $file=shift;
chdir $config{srcdir}
@@ -289,9 +289,9 @@ sub rcs_prepedit ($) { #{{{
# For monotone, return the revision of the file when
# editing begins.
return get_rev();
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
# Tries to commit the page; returns undef on _success_ and
# a version of the page with the rcs's conflict markers on failure.
# The file is relative to the srcdir.
@@ -434,7 +434,7 @@ sub rcs_commit ($$$;$$) { #{{{
}
return undef # success
-} #}}}
+}
sub rcs_commit_staged ($$$) {
# Commits all staged changes. Changes can be staged using rcs_add,
@@ -466,7 +466,7 @@ sub rcs_commit_staged ($$$) {
}
}
-sub rcs_add ($) { #{{{
+sub rcs_add ($) {
my $file=shift;
chdir $config{srcdir}
@@ -476,9 +476,9 @@ sub rcs_add ($) { #{{{
$file) != 0) {
error("Monotone add failed");
}
-} #}}}
+}
-sub rcs_remove ($) { # {{{
+sub rcs_remove ($) {
my $file = shift;
chdir $config{srcdir}
@@ -495,9 +495,9 @@ sub rcs_remove ($) { # {{{
$file) != 0) {
error("Monotone remove failed");
}
-} #}}}
+}
-sub rcs_rename ($$) { # {{{
+sub rcs_rename ($$) {
my ($src, $dest) = @_;
chdir $config{srcdir}
@@ -507,9 +507,9 @@ sub rcs_rename ($$) { # {{{
$src, $dest) != 0) {
error("Monotone rename failed");
}
-} #}}}
+}
-sub rcs_recentchanges ($) { #{{{
+sub rcs_recentchanges ($) {
my $num=shift;
my @ret;
@@ -525,13 +525,12 @@ sub rcs_recentchanges ($) { #{{{
my $child = open(MTNLOG, "-|");
if (! $child) {
exec("mtn", "log", "--root=$config{mtnrootdir}", "--no-graph",
- "--brief") || error("mtn log failed to run");
+ "--brief", "--last=$num") || error("mtn log failed to run");
}
- while (($num >= 0) and (my $line = <MTNLOG>)) {
+ while (my $line = <MTNLOG>) {
if ($line =~ m/^($sha1_pattern)/) {
push @revs, $1;
- $num -= 1;
}
}
close MTNLOG || debug("mtn log exited $?");
@@ -560,7 +559,7 @@ sub rcs_recentchanges ($) { #{{{
if ($cert->{key} eq $config{mtnkey}) {
$committype = "web";
} else {
- $committype = "monotone";
+ $committype = "mtn";
}
} elsif ($cert->{name} eq "date") {
$when = str2time($cert->{value}, 'UTC');
@@ -615,9 +614,9 @@ sub rcs_recentchanges ($) { #{{{
$automator->close();
return @ret;
-} #}}}
+}
-sub rcs_diff ($) { #{{{
+sub rcs_diff ($) {
my $rev=shift;
my ($sha1) = $rev =~ /^($sha1_pattern)$/; # untaint
@@ -639,9 +638,9 @@ sub rcs_diff ($) { #{{{
else {
return join("", @lines);
}
-} #}}}
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_getctime ($) {
my $file=shift;
chdir $config{srcdir}
@@ -691,6 +690,6 @@ sub rcs_getctime ($) { #{{{
$date=str2time($date, 'UTC');
debug("found ctime ".localtime($date)." for $file");
return $date;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/more.pm b/IkiWiki/Plugin/more.pm
index 4484441c3..77d5fb077 100644
--- a/IkiWiki/Plugin/more.pm
+++ b/IkiWiki/Plugin/more.pm
@@ -3,24 +3,24 @@ package IkiWiki::Plugin::more;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my $linktext = gettext("more");
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "more", call => \&getsetup);
hook(type => "preprocess", id => "more", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{linktext} = $linktext unless defined $params{linktext};
diff --git a/IkiWiki/Plugin/norcs.pm b/IkiWiki/Plugin/norcs.pm
index 58c26b633..bfe84c0e1 100644
--- a/IkiWiki/Plugin/norcs.pm
+++ b/IkiWiki/Plugin/norcs.pm
@@ -6,7 +6,7 @@ use warnings;
use strict;
use IkiWiki;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "norcs", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit);
@@ -18,51 +18,51 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
rebuild => 0,
},
-} #}}}
+}
-sub rcs_update () { #{{{
-} #}}}
+sub rcs_update () {
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
return ""
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
my ($file, $message, $rcstoken, $user, $ipaddr) = @_;
return undef # success
-} #}}}
+}
-sub rcs_commit_staged ($$$) { #{{{
+sub rcs_commit_staged ($$$) {
my ($message, $user, $ipaddr)=@_;
return undef # success
-} #}}}
+}
-sub rcs_add ($) { #{{{
-} #}}}
+sub rcs_add ($) {
+}
-sub rcs_remove ($) { #{{{
-} #}}}
+sub rcs_remove ($) {
+}
-sub rcs_rename ($$) { #{{{
-} #}}}
+sub rcs_rename ($$) {
+}
-sub rcs_recentchanges ($) { #{{{
-} #}}}
+sub rcs_recentchanges ($) {
+}
-sub rcs_diff ($) { #{{{
-} #}}}
+sub rcs_diff ($) {
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_getctime ($) {
error gettext("getctime not implemented");
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/opendiscussion.pm b/IkiWiki/Plugin/opendiscussion.pm
index 96a74aee8..3da01efee 100644
--- a/IkiWiki/Plugin/opendiscussion.pm
+++ b/IkiWiki/Plugin/opendiscussion.pm
@@ -3,22 +3,22 @@ package IkiWiki::Plugin::opendiscussion;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "opendiscussion", call => \&getsetup);
hook(type => "canedit", id => "opendiscussion", call => \&canedit);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub canedit ($$) { #{{{
+sub canedit ($$) {
my $page=shift;
my $cgi=shift;
my $session=shift;
@@ -26,6 +26,6 @@ sub canedit ($$) { #{{{
my $discussion=gettext("discussion");
return "" if $page=~/(\/|^)\Q$discussion\E$/;
return undef;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm
index f12cbdaa3..5424c57e2 100644
--- a/IkiWiki/Plugin/openid.pm
+++ b/IkiWiki/Plugin/openid.pm
@@ -4,24 +4,24 @@ package IkiWiki::Plugin::openid;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "openid", call => \&getopt);
hook(type => "getsetup", id => "openid", call => \&getsetup);
hook(type => "auth", id => "openid", call => \&auth);
hook(type => "formbuilder_setup", id => "openid",
call => \&formbuilder_setup, last => 1);
-} # }}}
+}
-sub getopt () { #{{{
+sub getopt () {
eval q{use Getopt::Long};
error($@) if $@;
Getopt::Long::Configure('pass_through');
GetOptions("openidsignup=s" => \$config{openidsignup});
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -34,9 +34,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
my $form=$params{form};
@@ -92,7 +92,7 @@ sub formbuilder_setup (@) { #{{{
}
}
-sub validate ($$$;$) { #{{{
+sub validate ($$$;$) {
my $q=shift;
my $session=shift;
my $openid_url=shift;
@@ -121,9 +121,9 @@ sub validate ($$$;$) { #{{{
# eventually bounce them back to auth()
IkiWiki::redirect($q, $check_url);
exit 0;
-} #}}}
+}
-sub auth ($$) { #{{{
+sub auth ($$) {
my $q=shift;
my $session=shift;
@@ -147,9 +147,9 @@ sub auth ($$) { #{{{
# myopenid.com affiliate support
validate($q, $session, $q->param('openid_identifier'));
}
-} #}}}
+}
-sub getobj ($$) { #{{{
+sub getobj ($$) {
my $q=shift;
my $session=shift;
@@ -178,26 +178,28 @@ sub getobj ($$) { #{{{
consumer_secret => sub { return shift()+$secret },
required_root => $config{cgiurl},
);
-} #}}}
+}
package IkiWiki;
# This is not used by this plugin, but this seems the best place to put it.
# Used elsewhere to pretty-display the name of an openid user.
-sub openiduser ($) { #{{{
+sub openiduser ($) {
my $user=shift;
if ($user =~ m!^https?://! &&
eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) {
my $oid=Net::OpenID::VerifiedIdentity->new(identity => $user);
my $display=$oid->display;
- # Convert "user.somehost.com" to "user [somehost.com]".
+ # Convert "user.somehost.com" to "user [somehost.com]"
+ # (also "user.somehost.co.uk")
if ($display !~ /\[/) {
- $display=~s/^(.*?)\.([^.]+\.[a-z]+)$/$1 [$2]/;
+ $display=~s/^([-a-zA-Z0-9]+?)\.([-.a-zA-Z0-9]+\.[a-z]+)$/$1 [$2]/;
}
# Convert "http://somehost.com/user" to "user [somehost.com]".
+ # (also "https://somehost.com/user/")
if ($display !~ /\[/) {
- $display=~s/^https?:\/\/(.+)\/([^\/]+)$/$2 [$1]/;
+ $display=~s/^https?:\/\/(.+)\/([^\/]+)\/?$/$2 [$1]/;
}
$display=~s!^https?://!!; # make sure this is removed
eval q{use CGI 'escapeHTML'};
diff --git a/IkiWiki/Plugin/orphans.pm b/IkiWiki/Plugin/orphans.pm
index 32cbc5dd5..605e6e43a 100644
--- a/IkiWiki/Plugin/orphans.pm
+++ b/IkiWiki/Plugin/orphans.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::orphans;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "orphans", call => \&getsetup);
hook(type => "preprocess", id => "orphans", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages}="*" unless defined $params{pages};
@@ -58,6 +58,6 @@ sub preprocess (@) { #{{{
"</li>"
} sort @orphans).
"</ul>\n";
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/otl.pm b/IkiWiki/Plugin/otl.pm
index ef76d6215..c68fcbbe3 100644
--- a/IkiWiki/Plugin/otl.pm
+++ b/IkiWiki/Plugin/otl.pm
@@ -4,25 +4,25 @@ package IkiWiki::Plugin::otl;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use open qw{:utf8 :std};
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "otl", call => \&getsetup);
hook(type => "filter", id => "otl", call => \&filter);
hook(type => "htmlize", id => "otl", call => \&htmlize);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
-} #}}}
+}
-sub filter (@) { #{{{
+sub filter (@) {
my %params=@_;
# Munge up check boxes to look a little bit better. This is a hack.
@@ -34,9 +34,9 @@ sub filter (@) { #{{{
$params{content}=~s/^(\s*)\[_\]\s/${1}$unchecked /mg;
return $params{content};
-} # }}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
# Can't use open2 since otl2html doesn't play nice with buffering.
@@ -95,6 +95,6 @@ sub htmlize (@) { #{{{
$ret=~s/<body>.*//s;
$ret=~s/<div class="Footer">.*//s;
return $ret;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/pagecount.pm b/IkiWiki/Plugin/pagecount.pm
index e059fa618..a143f24d0 100644
--- a/IkiWiki/Plugin/pagecount.pm
+++ b/IkiWiki/Plugin/pagecount.pm
@@ -3,22 +3,22 @@ package IkiWiki::Plugin::pagecount;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "pagecount", call => \&getsetup);
hook(type => "preprocess", id => "pagecount", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages}="*" unless defined $params{pages};
@@ -33,6 +33,6 @@ sub preprocess (@) { #{{{
$count++ if pagespec_match($page, $params{pages}, location => $params{page});
}
return $count;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/pagestats.pm b/IkiWiki/Plugin/pagestats.pm
index de4b7d068..dbe69539d 100644
--- a/IkiWiki/Plugin/pagestats.pm
+++ b/IkiWiki/Plugin/pagestats.pm
@@ -12,25 +12,25 @@ package IkiWiki::Plugin::pagestats;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
# Names of the HTML classes to use for the tag cloud
our @classes = ('smallestPC', 'smallPC', 'normalPC', 'bigPC', 'biggestPC' );
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "pagestats", call => \&getsetup);
hook(type => "preprocess", id => "pagestats", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$params{pages}="*" unless defined $params{pages};
my $style = ($params{style} or 'cloud');
@@ -73,6 +73,6 @@ sub preprocess (@) { #{{{
return $res;
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/pagetemplate.pm b/IkiWiki/Plugin/pagetemplate.pm
index 99a66ee96..1d8a84ca7 100644
--- a/IkiWiki/Plugin/pagetemplate.pm
+++ b/IkiWiki/Plugin/pagetemplate.pm
@@ -3,25 +3,25 @@ package IkiWiki::Plugin::pagetemplate;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %templates;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "pagetemplate", call => \&getsetup);
hook(type => "preprocess", id => "pagetemplate", call => \&preprocess);
hook(type => "templatefile", id => "pagetemplate", call => \&templatefile);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
if (! exists $params{template} ||
@@ -35,9 +35,9 @@ sub preprocess (@) { #{{{
}
return "";
-} # }}}
+}
-sub templatefile (@) { #{{{
+sub templatefile (@) {
my %params=@_;
if (exists $templates{$params{page}}) {
@@ -45,6 +45,6 @@ sub templatefile (@) { #{{{
}
return undef;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/parentlinks.pm b/IkiWiki/Plugin/parentlinks.pm
index a8b3641e9..ebf1d449a 100644
--- a/IkiWiki/Plugin/parentlinks.pm
+++ b/IkiWiki/Plugin/parentlinks.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::parentlinks;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "parentlinks", id => "parentlinks", call => \&parentlinks);
hook(type => "pagetemplate", id => "parentlinks", call => \&pagetemplate);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub parentlinks ($) { #{{{
+sub parentlinks ($) {
my $page=shift;
my @ret;
@@ -48,9 +48,9 @@ sub parentlinks ($) { #{{{
$i++;
}
return @ret;
-} #}}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
my $template=$params{template};
@@ -58,6 +58,6 @@ sub pagetemplate (@) { #{{{
if ($template->query(name => "parentlinks")) {
$template->param(parentlinks => [parentlinks($page)]);
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/passwordauth.pm b/IkiWiki/Plugin/passwordauth.pm
index e0c0a3b0e..90e2ca564 100644
--- a/IkiWiki/Plugin/passwordauth.pm
+++ b/IkiWiki/Plugin/passwordauth.pm
@@ -4,16 +4,16 @@ package IkiWiki::Plugin::passwordauth;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "passwordauth", "call" => \&getsetup);
hook(type => "formbuilder_setup", id => "passwordauth", call => \&formbuilder_setup);
hook(type => "formbuilder", id => "passwordauth", call => \&formbuilder);
hook(type => "sessioncgi", id => "passwordauth", call => \&sessioncgi);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -33,10 +33,10 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
# Checks if a string matches a user's password, and returns true or false.
-sub checkpassword ($$;$) { #{{{
+sub checkpassword ($$;$) {
my $user=shift;
my $password=shift;
my $field=shift || "password";
@@ -74,9 +74,9 @@ sub checkpassword ($$;$) { #{{{
}
return $ret;
-} #}}}
+}
-sub setpassword ($$;$) { #{{{
+sub setpassword ($$;$) {
my $user=shift;
my $password=shift;
my $field=shift || "password";
@@ -94,9 +94,9 @@ sub setpassword ($$;$) { #{{{
else {
IkiWiki::userinfo_set($user, $field, $password);
}
-} #}}}
+}
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
my $form=$params{form};
@@ -222,7 +222,7 @@ sub formbuilder_setup (@) { #{{{
}
}
-sub formbuilder (@) { #{{{
+sub formbuilder (@) {
my %params=@_;
my $form=$params{form};
@@ -313,9 +313,9 @@ sub formbuilder (@) { #{{{
}
}
}
-} #}}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $q=shift;
my $session=shift;
@@ -335,6 +335,6 @@ sub sessioncgi ($$) { #{{{
IkiWiki::cgi_prefs($q, $session);
exit;
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/pingee.pm b/IkiWiki/Plugin/pingee.pm
index c2f21b0cf..f5386d0ca 100644
--- a/IkiWiki/Plugin/pingee.pm
+++ b/IkiWiki/Plugin/pingee.pm
@@ -3,22 +3,22 @@ package IkiWiki::Plugin::pingee;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "pingee", call => \&getsetup);
hook(type => "cgi", id => "pingee", call => \&cgi);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub cgi ($) { #{{{
+sub cgi ($) {
my $cgi=shift;
if (defined $cgi->param('do') && $cgi->param("do") eq "ping") {
@@ -37,6 +37,6 @@ sub cgi ($) { #{{{
IkiWiki::saveindex();
exit 0;
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/pinger.pm b/IkiWiki/Plugin/pinger.pm
index 043b074a7..c20ecb5d4 100644
--- a/IkiWiki/Plugin/pinger.pm
+++ b/IkiWiki/Plugin/pinger.pm
@@ -3,20 +3,20 @@ package IkiWiki::Plugin::pinger;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %pages;
my $pinged=0;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "pinger", call => \&getsetup);
hook(type => "needsbuild", id => "pinger", call => \&needsbuild);
hook(type => "preprocess", id => "ping", call => \&preprocess);
hook(type => "delete", id => "pinger", call => \&ping);
hook(type => "change", id => "pinger", call => \&ping);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -29,9 +29,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
foreach my $page (keys %pagestate) {
if (exists $pagestate{$page}{pinger}) {
@@ -45,9 +45,9 @@ sub needsbuild (@) { #{{{
}
}
}
-} # }}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
if (! exists $params{from} || ! exists $params{to}) {
error gettext("requires 'from' and 'to' parameters");
@@ -60,7 +60,7 @@ sub preprocess (@) { #{{{
else {
return sprintf(gettext("Ignoring ping directive for wiki %s (this wiki is %s)"), $params{from}, $config{url});
}
-} # }}}
+}
sub ping {
if (! $pinged && %pages) {
@@ -106,7 +106,7 @@ sub ping {
# will still be avoided.
next if $url=~/^\Q$config{cgiurl}\E/;
- $ua->head($url);
+ $ua->get($url);
}
exit 0;
diff --git a/IkiWiki/Plugin/poll.pm b/IkiWiki/Plugin/poll.pm
index fadc1773e..bc1e3501e 100644
--- a/IkiWiki/Plugin/poll.pm
+++ b/IkiWiki/Plugin/poll.pm
@@ -3,25 +3,25 @@ package IkiWiki::Plugin::poll;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use Encode;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "poll", call => \&getsetup);
hook(type => "preprocess", id => "poll", call => \&preprocess);
hook(type => "sessioncgi", id => "poll", call => \&sessioncgi);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
my %pagenum;
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=(open => "yes", total => "yes", percent => "yes", @_);
my $open=IkiWiki::yesno($params{open});
@@ -77,9 +77,9 @@ sub preprocess (@) { #{{{
$ret.="<span>".gettext("Total votes:")." $total</span>\n";
}
return "<div class=poll>$ret</div>";
-} # }}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $cgi=shift;
my $session=shift;
if (defined $cgi->param('do') && $cgi->param('do') eq "poll") {
@@ -152,6 +152,6 @@ sub sessioncgi ($$) { #{{{
-url => urlto($page, undef, 1));
exit;
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/polygen.pm b/IkiWiki/Plugin/polygen.pm
index fa564aa86..bc21d71c7 100644
--- a/IkiWiki/Plugin/polygen.pm
+++ b/IkiWiki/Plugin/polygen.pm
@@ -7,23 +7,23 @@ package IkiWiki::Plugin::polygen;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use File::Find;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "polygen", call => \&getsetup);
hook(type => "preprocess", id => "polygen", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
my $grammar = ($params{grammar} or 'polygen');
my $symbol = ($params{symbol} or undef);
@@ -64,6 +64,6 @@ sub preprocess (@) { #{{{
# markdown text
$res =~ s/\s*$//;
return $res;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/postsparkline.pm b/IkiWiki/Plugin/postsparkline.pm
index 1f4c065c1..ba43561fb 100644
--- a/IkiWiki/Plugin/postsparkline.pm
+++ b/IkiWiki/Plugin/postsparkline.pm
@@ -3,23 +3,23 @@ package IkiWiki::Plugin::postsparkline;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
IkiWiki::loadplugin('sparkline');
hook(type => "getsetup", id => "postsparkline", call => \&getsetup);
hook(type => "preprocess", id => "postsparkline", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
if (! exists $params{max}) {
@@ -78,7 +78,7 @@ sub preprocess (@) { #{{{
delete $params{color};
return IkiWiki::Plugin::sparkline::preprocess(%params,
map { $_.$color => "" } reverse @data);
-} # }}}
+}
sub perfoo ($@) {
my $sub=shift;
diff --git a/IkiWiki/Plugin/prettydate.pm b/IkiWiki/Plugin/prettydate.pm
index e997be3ce..e155dd39b 100644
--- a/IkiWiki/Plugin/prettydate.pm
+++ b/IkiWiki/Plugin/prettydate.pm
@@ -1,6 +1,6 @@
#!/usr/bin/perl
package IkiWiki::Plugin::prettydate;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use warnings;
no warnings 'redefine';
use strict;
@@ -39,12 +39,12 @@ sub default_timetable {
];
}
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "prettydate", call => \&getsetup);
hook(type => "checkconfig", id => "prettydate", call => \&checkconfig);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -64,9 +64,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (! defined $config{prettydateformat} ||
$config{prettydateformat} eq '%c') {
$config{prettydateformat}='%X, %B %o, %Y';
@@ -82,9 +82,9 @@ sub checkconfig () { #{{{
$config{timetable}[$h] = $config{timetable}[$h - 1];
}
}
-} #}}}
+}
-sub IkiWiki::displaytime ($;$) { #{{{
+sub IkiWiki::formattime ($;$) {
my $time=shift;
my $format=shift;
if (! defined $format) {
@@ -122,6 +122,6 @@ sub IkiWiki::displaytime ($;$) { #{{{
$format=~s/\%X/$t/g;
return strftime($format, \@t);
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/progress.pm b/IkiWiki/Plugin/progress.pm
index e536f4e23..76d994acc 100644
--- a/IkiWiki/Plugin/progress.pm
+++ b/IkiWiki/Plugin/progress.pm
@@ -3,25 +3,25 @@ package IkiWiki::Plugin::progress;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my $percentage_pattern = qr/[0-9]+\%?/; # pattern to validate percentages
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "progress", call => \&getsetup);
hook(type => "preprocess", id => "progress", call => \&preprocess);
hook(type => "format", id => "progress", call => \&format);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
my $fill;
@@ -64,9 +64,9 @@ sub preprocess (@) { #{{{
<div class="progress-done" style="width: $fill">$fill</div>
</div>
EODIV
-} # }}}
+}
-sub format(@) { #{{{
+sub format(@) {
my %params = @_;
# If HTMLScrubber has removed the style attribute, then bring it back
@@ -74,6 +74,6 @@ sub format(@) { #{{{
$params{content} =~ s!<div class="progress-done">($percentage_pattern)</div>!<div class="progress-done" style="width: $1">$1</div>!g;
return $params{content};
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/rawhtml.pm b/IkiWiki/Plugin/rawhtml.pm
index 74ca13f3b..ad8a610c1 100644
--- a/IkiWiki/Plugin/rawhtml.pm
+++ b/IkiWiki/Plugin/rawhtml.pm
@@ -4,19 +4,19 @@ package IkiWiki::Plugin::rawhtml;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "rawhtml", call => \&getsetup);
$config{wiki_file_prune_regexps} = [ grep { !m/\\\.x\?html\?\$/ } @{$config{wiki_file_prune_regexps}} ];
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # changes file types
},
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/recentchanges.pm b/IkiWiki/Plugin/recentchanges.pm
index eb23b184b..fa851e466 100644
--- a/IkiWiki/Plugin/recentchanges.pm
+++ b/IkiWiki/Plugin/recentchanges.pm
@@ -3,19 +3,21 @@ package IkiWiki::Plugin::recentchanges;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use Encode;
+use HTML::Entities;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "recentchanges", call => \&getsetup);
hook(type => "checkconfig", id => "recentchanges", call => \&checkconfig);
hook(type => "refresh", id => "recentchanges", call => \&refresh);
hook(type => "pagetemplate", id => "recentchanges", call => \&pagetemplate);
hook(type => "htmlize", id => "_change", call => \&htmlize);
- hook(type => "cgi", id => "recentchanges", call => \&cgi);
-} #}}}
+ # Load goto to fix up links from recentchanges
+ IkiWiki::loadplugin("goto");
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -35,14 +37,14 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
$config{recentchangespage}='recentchanges' unless defined $config{recentchangespage};
$config{recentchangesnum}=100 unless defined $config{recentchangesnum};
-} #}}}
+}
-sub refresh ($) { #{{{
+sub refresh ($) {
my %seen;
# add new changes
@@ -56,10 +58,10 @@ sub refresh ($) { #{{{
unlink($config{srcdir}.'/'.$pagesources{$page});
}
}
-} #}}}
+}
# Enable the recentchanges link on wiki pages.
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $template=$params{template};
my $page=$params{page};
@@ -70,48 +72,15 @@ sub pagetemplate (@) { #{{{
$template->param(recentchangesurl => urlto($config{recentchangespage}, $page));
$template->param(have_actions => 1);
}
-} #}}}
+}
# Pages with extension _change have plain html markup, pass through.
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
return $params{content};
-} #}}}
-
-sub cgi ($) { #{{{
- my $cgi=shift;
- if (defined $cgi->param('do') && $cgi->param('do') eq "recentchanges_link") {
- # This is a link from a change page to some
- # other page. Since the change pages are only generated
- # once, statically, links on them won't be updated if the
- # page they link to is deleted, or newly created, or
- # changes for whatever reason. So this CGI handles that
- # dynamic linking stuff.
- my $page=decode_utf8($cgi->param("page"));
- if (!defined $page) {
- error("missing page parameter");
- }
-
- IkiWiki::loadindex();
-
- my $link=bestlink("", $page);
- if (! length $link) {
- print "Content-type: text/html\n\n";
- print IkiWiki::misctemplate(gettext(gettext("missing page")),
- "<p>".
- sprintf(gettext("The page %s does not exist."),
- htmllink("", "", $page)).
- "</p>");
- }
- else {
- IkiWiki::redirect($cgi, urlto($link, undef, 1));
- }
-
- exit;
- }
}
-sub store ($$$) { #{{{
+sub store ($$$) {
my $change=shift;
my $page="$config{recentchangespage}/change_".titlepage($change->{rev});
@@ -128,10 +97,10 @@ sub store ($$$) { #{{{
if (length $config{cgiurl}) {
$_->{link} = "<a href=\"".
IkiWiki::cgiurl(
- do => "recentchanges_link",
+ do => "goto",
page => $_->{page}
).
- "\">".
+ "\" rel=\"nofollow\">".
pagetitle($_->{page}).
"</a>"
}
@@ -154,16 +123,18 @@ sub store ($$$) { #{{{
}
elsif (length $config{cgiurl}) {
$change->{authorurl} = IkiWiki::cgiurl(
- do => "recentchanges_link",
+ do => "goto",
page => (length $config{userdir} ? "$config{userdir}/" : "").$change->{author},
);
}
- # escape wikilinks and preprocessor stuff in commit messages
if (ref $change->{message}) {
foreach my $field (@{$change->{message}}) {
if (exists $field->{line}) {
- $field->{line} =~ s/(?<!\\)\[\[/\\\[\[/g;
+ # escape html
+ $field->{line} = encode_entities($field->{line});
+ # escape links and preprocessor stuff
+ $field->{line} = encode_entities($field->{line}, '\[\]');
}
}
}
@@ -175,6 +146,10 @@ sub store ($$$) { #{{{
commitdate => displaytime($change->{when}, "%X %x"),
wikiname => $config{wikiname},
);
+
+ $template->param(permalink => "$config{url}/$config{recentchangespage}/#change-".titlepage($change->{rev}))
+ if exists $config{url};
+
IkiWiki::run_hooks(pagetemplate => sub {
shift->(page => $page, destpage => $page,
template => $template, rev => $change->{rev});
@@ -185,6 +160,6 @@ sub store ($$$) { #{{{
utime $change->{when}, $change->{when}, "$config{srcdir}/$file";
return $page;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/recentchangesdiff.pm b/IkiWiki/Plugin/recentchangesdiff.pm
index 36acef72e..e3ba9b8d8 100644
--- a/IkiWiki/Plugin/recentchangesdiff.pm
+++ b/IkiWiki/Plugin/recentchangesdiff.pm
@@ -3,26 +3,27 @@ package IkiWiki::Plugin::recentchangesdiff;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
+use HTML::Entities;
my $maxlines=200;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "recentchangesdiff",
call => \&getsetup);
hook(type => "pagetemplate", id => "recentchangesdiff",
call => \&pagetemplate);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $template=$params{template};
if ($config{rcs} && exists $params{rev} && length $params{rev} &&
@@ -38,11 +39,13 @@ sub pagetemplate (@) { #{{{
else {
$diff=join("", @lines);
}
+ # escape html
+ $diff = encode_entities($diff);
# escape links and preprocessor stuff
- $diff =~ s/(?<!\\)\[\[/\\\[\[/g;
+ $diff = encode_entities($diff, '\[\]');
$template->param(diff => $diff);
}
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/relativedate.pm b/IkiWiki/Plugin/relativedate.pm
new file mode 100644
index 000000000..3e33cd5c3
--- /dev/null
+++ b/IkiWiki/Plugin/relativedate.pm
@@ -0,0 +1,60 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::relativedate;
+
+use warnings;
+no warnings 'redefine';
+use strict;
+use IkiWiki 3.00;
+use POSIX;
+use Encode;
+
+sub import {
+ add_underlay("javascript");
+ hook(type => "getsetup", id => "relativedate", call => \&getsetup);
+ hook(type => "format", id => "relativedate", call => \&format);
+ inject(name => "IkiWiki::displaytime", call => \&mydisplaytime);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => 1,
+ },
+}
+
+sub format (@) {
+ my %params=@_;
+
+ if (! ($params{content}=~s!^(<body>)!$1.include_javascript($params{page})!em)) {
+ # no </body> tag, probably in preview mode
+ $params{content}=include_javascript($params{page}, 1).$params{content};
+ }
+ return $params{content};
+}
+
+sub include_javascript ($;$) {
+ my $page=shift;
+ my $absolute=shift;
+
+ return '<script src="'.urlto("ikiwiki.js", $page, $absolute).
+ '" type="text/javascript" charset="utf-8"></script>'."\n".
+ '<script src="'.urlto("relativedate.js", $page, $absolute).
+ '" type="text/javascript" charset="utf-8"></script>';
+}
+
+sub mydisplaytime ($;$) {
+ my $time=shift;
+ my $format=shift;
+
+ # This needs to be in a form that can be parsed by javascript.
+ # Being fairly human readable is also nice, as it will be exposed
+ # as the title if javascript is not available.
+ my $gmtime=decode_utf8(POSIX::strftime("%a, %d %b %Y %H:%M:%S %z",
+ localtime($time)));
+
+ return '<span class="relativedate" title="'.$gmtime.'">'.
+ IkiWiki::formattime($time, $format).'</span>';
+}
+
+1
diff --git a/IkiWiki/Plugin/remove.pm b/IkiWiki/Plugin/remove.pm
index 68bf9d1ee..ee5784f20 100644
--- a/IkiWiki/Plugin/remove.pm
+++ b/IkiWiki/Plugin/remove.pm
@@ -3,25 +3,25 @@ package IkiWiki::Plugin::remove;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "remove", call => \&getsetup);
hook(type => "formbuilder_setup", id => "remove", call => \&formbuilder_setup);
hook(type => "formbuilder", id => "remove", call => \&formbuilder);
hook(type => "sessioncgi", id => "remove", call => \&sessioncgi);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub check_canremove ($$$) { #{{{
+sub check_canremove ($$$) {
my $page=shift;
my $q=shift;
my $session=shift;
@@ -41,7 +41,7 @@ sub check_canremove ($$$) { #{{{
error(sprintf(gettext("%s is not a file"), $file));
}
- # Must be editiable.
+ # Must be editable.
IkiWiki::check_canedit($page, $q, $session);
# If a user can't upload an attachment, don't let them delete it.
@@ -54,9 +54,9 @@ sub check_canremove ($$$) { #{{{
error("renaming of attachments is not allowed");
}
}
-} #}}}
+}
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
my $form=$params{form};
my $q=$params{cgi};
@@ -67,9 +67,9 @@ sub formbuilder_setup (@) { #{{{
push @{$params{buttons}}, "Remove" if $form->field("do") eq "edit";
$form->tmpl_param("field-remove" => '<input name="_submit" type="submit" value="Remove Attachments" />');
}
-} #}}}
+}
-sub confirmation_form ($$) { #{{{
+sub confirmation_form ($$) {
my $q=shift;
my $session=shift;
@@ -90,9 +90,9 @@ sub confirmation_form ($$) { #{{{
$f->field(name => "do", type => "hidden", value => "remove", force => 1);
return $f, ["Remove", "Cancel"];
-} #}}}
+}
-sub removal_confirm ($$@) { #{{{
+sub removal_confirm ($$@) {
my $q=shift;
my $session=shift;
my $attachment=shift;
@@ -122,9 +122,9 @@ sub removal_confirm ($$@) { #{{{
IkiWiki::showform($f, $buttons, $session, $q);
exit 0;
-} #}}}
+}
-sub postremove ($) { #{{{
+sub postremove ($) {
my $session=shift;
# Load saved form state and return to edit form.
@@ -132,9 +132,9 @@ sub postremove ($) { #{{{
$session->clear("postremove");
IkiWiki::cgi_savesession($session);
IkiWiki::cgi($postremove, $session);
-} #}}}
+}
-sub formbuilder (@) { #{{{
+sub formbuilder (@) {
my %params=@_;
my $form=$params{form};
@@ -154,9 +154,9 @@ sub formbuilder (@) { #{{{
removal_confirm($q, $session, 1, @selected);
}
}
-} #}}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $q=shift;
if ($q->param("do") eq 'remove') {
@@ -218,7 +218,7 @@ sub sessioncgi ($$) { #{{{
}
}
else {
- IkiWiki::showform($form, $buttons, $session, $q);
+ removal_confirm($q, $session, 0, $q->param("page"));
}
exit 0;
diff --git a/IkiWiki/Plugin/rename.pm b/IkiWiki/Plugin/rename.pm
index 7e55e271c..41af3ca4d 100644
--- a/IkiWiki/Plugin/rename.pm
+++ b/IkiWiki/Plugin/rename.pm
@@ -3,25 +3,25 @@ package IkiWiki::Plugin::rename;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "rename", call => \&getsetup);
hook(type => "formbuilder_setup", id => "rename", call => \&formbuilder_setup);
hook(type => "formbuilder", id => "rename", call => \&formbuilder);
hook(type => "sessioncgi", id => "rename", call => \&sessioncgi);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub check_canrename ($$$$$$) { #{{{
+sub check_canrename ($$$$$$) {
my $src=shift;
my $srcfile=shift;
my $dest=shift;
@@ -87,9 +87,9 @@ sub check_canrename ($$$$$$) { #{{{
IkiWiki::Plugin::attachment::check_canattach($session, $dest, $srcfile);
}
}
-} #}}}
+}
-sub rename_form ($$$) { #{{{
+sub rename_form ($$$) {
my $q=shift;
my $session=shift;
my $page=shift;
@@ -111,7 +111,7 @@ sub rename_form ($$$) { #{{{
$f->field(name => "do", type => "hidden", value => "rename", force => 1);
$f->field(name => "page", type => "hidden", value => $page, force => 1);
- $f->field(name => "new_name", value => pagetitle($page), size => 60);
+ $f->field(name => "new_name", value => pagetitle($page, 1), size => 60);
if (!$q->param("attachment")) {
# insert the standard extensions
my @page_types;
@@ -145,9 +145,9 @@ sub rename_form ($$$) { #{{{
$f->field(name => "attachment", type => "hidden");
return $f, ["Rename", "Cancel"];
-} #}}}
+}
-sub rename_start ($$$$) { #{{{
+sub rename_start ($$$$) {
my $q=shift;
my $session=shift;
my $attachment=shift;
@@ -171,9 +171,9 @@ sub rename_start ($$$$) { #{{{
my ($f, $buttons)=rename_form($q, $session, $page);
IkiWiki::showform($f, $buttons, $session, $q);
exit 0;
-} #}}}
+}
-sub postrename ($;$$$) { #{{{
+sub postrename ($;$$$) {
my $session=shift;
my $src=shift;
my $dest=shift;
@@ -204,9 +204,9 @@ sub postrename ($;$$$) { #{{{
}
IkiWiki::cgi_editpage($postrename, $session);
-} #}}}
+}
-sub formbuilder (@) { #{{{
+sub formbuilder (@) {
my %params=@_;
my $form=$params{form};
@@ -229,11 +229,11 @@ sub formbuilder (@) { #{{{
rename_start($q, $session, 1, $selected[0]);
}
}
-} #}}}
+}
my $renamesummary;
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
my $form=$params{form};
my $q=$params{cgi};
@@ -248,9 +248,9 @@ sub formbuilder_setup (@) { #{{{
$form->tmpl_param(message => $renamesummary);
}
}
-} #}}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $q=shift;
if ($q->param("do") eq 'rename') {
@@ -390,22 +390,8 @@ sub sessioncgi ($$) { #{{{
$template->param(error => $rename->{error});
if ($rename->{src} ne $rename->{dest}) {
$template->param(brokenlinks_checked => 1);
- $template->param(brokenlinks => [
- map {
- {
- page => htmllink($rename->{dest}, $rename->{dest}, $_,
- noimageinline => 1)
- }
- } @{$rename->{brokenlinks}}
- ]);
- $template->param(fixedlinks => [
- map {
- {
- page => htmllink($rename->{dest}, $rename->{dest}, $_,
- noimageinline => 1)
- }
- } @{$rename->{fixedlinks}}
- ]);
+ $template->param(brokenlinks => linklist($rename->{dest}, $rename->{brokenlinks}));
+ $template->param(fixedlinks => linklist($rename->{dest}, $rename->{fixedlinks}));
}
$renamesummary.=$template->output;
}
@@ -418,9 +404,26 @@ sub sessioncgi ($$) { #{{{
exit 0;
}
-} #}}}
+}
+
+sub linklist {
+ # generates a list of links in a form suitable for FormBuilder
+ my $dest=shift;
+ my $list=shift;
+ # converts a list of pages into a list of links
+ # in a form suitable for FormBuilder.
+
+ [map {
+ {
+ page => htmllink($dest, $dest, $_,
+ noimageinline => 1,
+ linktext => pagetitle($_),
+ )
+ }
+ } @{$list}]
+}
-sub renamepage_hook ($$$$) { #{{{
+sub renamepage_hook ($$$$) {
my ($page, $src, $dest, $content)=@_;
IkiWiki::run_hooks(renamepage => sub {
@@ -433,9 +436,9 @@ sub renamepage_hook ($$$$) { #{{{
});
return $content;
-}# }}}
+}
-sub do_rename ($$$) { #{{{
+sub do_rename ($$$) {
my $rename=shift;
my $q=shift;
my $session=shift;
@@ -460,9 +463,9 @@ sub do_rename ($$$) { #{{{
}
}
-} # }}}
+}
-sub fixlinks ($$$) { #{{{
+sub fixlinks ($$$) {
my $rename=shift;
my $session=shift;
@@ -498,6 +501,6 @@ sub fixlinks ($$$) { #{{{
}
return @fixedlinks;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/repolist.pm b/IkiWiki/Plugin/repolist.pm
new file mode 100644
index 000000000..f69ec3988
--- /dev/null
+++ b/IkiWiki/Plugin/repolist.pm
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::repolist;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "getsetup", id => "repolist", call => \&getsetup);
+ hook(type => "checkconfig", id => "repolist", call => \&checkconfig);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ },
+ repositories => {
+ type => "string",
+ example => ["svn://svn.example.org/wiki/trunk"],
+ description => "URIs of repositories containing the wiki's source",
+ safe => 1,
+ rebuild => undef,
+ },
+}
+
+my $relvcs;
+
+sub checkconfig () {
+ if (defined $config{rcs} && $config{repositories}) {
+ $relvcs=join("\n", map {
+ s/"//g; # avoid quotes just in case
+ qq{<link rel="vcs-$config{rcs}" href="$_" title="wiki $config{rcs} repository" />}
+ } @{$config{repositories}});
+
+ hook(type => "pagetemplate", id => "repolist", call => \&pagetemplate);
+ }
+}
+
+sub pagetemplate (@) {
+ my %params=@_;
+ my $page=$params{page};
+ my $template=$params{template};
+
+ if (defined $relvcs && $template->query(name => "relvcs")) {
+ $template->param(relvcs => $relvcs);
+ }
+}
+
+1
diff --git a/IkiWiki/Plugin/search.pm b/IkiWiki/Plugin/search.pm
index e40f4888c..d79e3170e 100644
--- a/IkiWiki/Plugin/search.pm
+++ b/IkiWiki/Plugin/search.pm
@@ -4,18 +4,18 @@ package IkiWiki::Plugin::search;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "search", call => \&getsetup);
hook(type => "checkconfig", id => "search", call => \&checkconfig);
hook(type => "pagetemplate", id => "search", call => \&pagetemplate);
hook(type => "postscan", id => "search", call => \&index);
hook(type => "delete", id => "search", call => \&delete);
hook(type => "cgi", id => "search", call => \&cgi);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -28,9 +28,9 @@ sub getsetup () { #{{{
safe => 0, # external program
rebuild => 0,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
foreach my $required (qw(url cgiurl)) {
if (! length $config{$required}) {
error(sprintf(gettext("Must specify %s when using the search plugin"), $required));
@@ -40,10 +40,10 @@ sub checkconfig () { #{{{
if (! defined $config{omega_cgi}) {
$config{omega_cgi}="/usr/lib/cgi-bin/omega/omega";
}
-} #}}}
+}
my $form;
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
my $template=$params{template};
@@ -58,11 +58,11 @@ sub pagetemplate (@) { #{{{
$template->param(searchform => $form);
}
-} #}}}
+}
my $scrubber;
my $stemmer;
-sub index (@) { #{{{
+sub index (@) {
my %params=@_;
setupfiles();
@@ -146,17 +146,17 @@ sub index (@) { #{{{
$doc->add_term($pageterm);
$db->replace_document_by_term($pageterm, $doc);
-} #}}}
+}
-sub delete (@) { #{{{
+sub delete (@) {
my $db=xapiandb();
foreach my $page (@_) {
my $pageterm=pageterm(pagename($page));
$db->delete_document_by_term($pageterm) if defined $pageterm;
}
-} #}}}
+}
-sub cgi ($) { #{{{
+sub cgi ($) {
my $cgi=shift;
if (defined $cgi->param('P')) {
@@ -169,9 +169,9 @@ sub cgi ($) { #{{{
noimageinline => 1, linktext => "Help");
exec($config{omega_cgi}) || error("$config{omega_cgi} failed: $!");
}
-} #}}}
+}
-sub pageterm ($) { #{{{
+sub pageterm ($) {
my $page=shift;
# 240 is the number used by omindex to decide when to hash an
@@ -190,10 +190,10 @@ sub pageterm ($) { #{{{
else {
return "U:".$page;
}
-} #}}}
+}
my $db;
-sub xapiandb () { #{{{
+sub xapiandb () {
if (! defined $db) {
eval q{
use Search::Xapian;
@@ -204,11 +204,11 @@ sub xapiandb () { #{{{
Search::Xapian::DB_CREATE_OR_OPEN());
}
return $db;
-} #}}}
+}
{
my $setup=0;
-sub setupfiles () { #{{{
+sub setupfiles () {
if (! $setup and (! -e $config{wikistatedir}."/xapian" || $config{rebuild})) {
writefile("omega.conf", $config{wikistatedir}."/xapian",
"database_dir .\n".
@@ -218,7 +218,7 @@ sub setupfiles () { #{{{
readfile(IkiWiki::template_file("searchquery.tmpl"))));
$setup=1;
}
-} #}}}
+}
}
1
diff --git a/IkiWiki/Plugin/shortcut.pm b/IkiWiki/Plugin/shortcut.pm
index 7bfce586f..1840a5722 100644
--- a/IkiWiki/Plugin/shortcut.pm
+++ b/IkiWiki/Plugin/shortcut.pm
@@ -3,33 +3,41 @@ package IkiWiki::Plugin::shortcut;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "shortcut", call => \&getsetup);
- hook(type => "refresh", id => "shortcut", call => \&refresh);
+ hook(type => "checkconfig", id => "shortcut", call => \&checkconfig);
hook(type => "preprocess", id => "shortcut", call => \&preprocess_shortcut);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub refresh () { #{{{
- # Preprocess the shortcuts page to get all the available shortcuts
- # defined before other pages are rendered.
- my $srcfile=srcfile("shortcuts.mdwn", 1);
- if (! defined $srcfile) {
- error(gettext("shortcut plugin will not work without a shortcuts.mdwn"));
+sub checkconfig () {
+ if (defined $config{srcdir} && length $config{srcdir}) {
+ # Preprocess the shortcuts page to get all the available shortcuts
+ # defined before other pages are rendered.
+ my $srcfile=srcfile("shortcuts.".$config{default_pageext}, 1);
+ if (! defined $srcfile) {
+ $srcfile=srcfile("shortcuts.mdwn", 1);
+ }
+ if (! defined $srcfile) {
+ print STDERR sprintf(gettext("shortcut plugin will not work without %s"),
+ "shortcuts.".$config{default_pageext})."\n";
+ }
+ else {
+ IkiWiki::preprocess("shortcuts", "shortcuts", readfile($srcfile));
+ }
}
- IkiWiki::preprocess("shortcuts", "shortcuts", readfile($srcfile));
-} # }}}
+}
-sub preprocess_shortcut (@) { #{{{
+sub preprocess_shortcut (@) {
my %params=@_;
if (! defined $params{name} || ! defined $params{url}) {
@@ -37,15 +45,16 @@ sub preprocess_shortcut (@) { #{{{
}
hook(type => "preprocess", no_override => 1, id => $params{name},
+ shortcut => 1,
call => sub { shortcut_expand($params{url}, $params{desc}, @_) });
#translators: This is used to display what shortcuts are defined.
#translators: First parameter is the name of the shortcut, the second
#translators: is an URL.
return sprintf(gettext("shortcut %s points to <i>%s</i>"), $params{name}, $params{url});
-} # }}}
+}
-sub shortcut_expand ($$@) { #{{{
+sub shortcut_expand ($$@) {
my $url=shift;
my $desc=shift;
my %params=@_;
@@ -82,6 +91,6 @@ sub shortcut_expand ($$@) { #{{{
}
return "<a href=\"$url\">$desc</a>";
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/sidebar.pm b/IkiWiki/Plugin/sidebar.pm
index 9697e1198..41812e1c1 100644
--- a/IkiWiki/Plugin/sidebar.pm
+++ b/IkiWiki/Plugin/sidebar.pm
@@ -6,22 +6,22 @@ package IkiWiki::Plugin::sidebar;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "sidebar", call => \&getsetup);
hook(type => "pagetemplate", id => "sidebar", call => \&pagetemplate);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub sidebar_content ($) { #{{{
+sub sidebar_content ($) {
my $page=shift;
my $sidebar_page=bestlink($page, "sidebar") || return;
@@ -42,9 +42,9 @@ sub sidebar_content ($) { #{{{
IkiWiki::filter($sidebar_page, $page, $content))));
}
-} # }}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
@@ -56,6 +56,6 @@ sub pagetemplate (@) { #{{{
$template->param(sidebar => $content);
}
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/signinedit.pm b/IkiWiki/Plugin/signinedit.pm
index ef7b9b428..032a0034c 100644
--- a/IkiWiki/Plugin/signinedit.pm
+++ b/IkiWiki/Plugin/signinedit.pm
@@ -3,23 +3,23 @@ package IkiWiki::Plugin::signinedit;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "signinedit", call => \&getsetup);
hook(type => "canedit", id => "signinedit", call => \&canedit,
last => 1);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 0,
},
-} #}}}
+}
-sub canedit ($$$) { #{{{
+sub canedit ($$$) {
my $page=shift;
my $cgi=shift;
my $session=shift;
@@ -34,6 +34,6 @@ sub canedit ($$$) { #{{{
else {
return "";
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/skeleton.pm.example b/IkiWiki/Plugin/skeleton.pm.example
index f844ddb91..ea7d6e47f 100644
--- a/IkiWiki/Plugin/skeleton.pm.example
+++ b/IkiWiki/Plugin/skeleton.pm.example
@@ -6,9 +6,9 @@ package IkiWiki::Plugin::skeleton;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "skeleton", call => \&getopt);
hook(type => "getsetup", id => "skeleton", call => \&getsetup);
hook(type => "checkconfig", id => "skeleton", call => \&checkconfig);
@@ -30,17 +30,18 @@ sub import { #{{{
hook(type => "auth", id => "skeleton", call => \&auth);
hook(type => "sessioncgi", id => "skeleton", call => \&sessioncgi);
hook(type => "canedit", id => "skeleton", call => \&canedit);
+ hook(type => "checkcontent", id => "skeleton", call => \&checkcontent);
hook(type => "editcontent", id => "skeleton", call => \&editcontent);
hook(type => "formbuilder_setup", id => "skeleton", call => \&formbuilder_setup);
hook(type => "formbuilder", id => "skeleton", call => \&formbuilder);
hook(type => "savestate", id => "skeleton", call => \&savestate);
-} # }}}
+}
-sub getopt () { #{{{
+sub getopt () {
debug("skeleton plugin getopt");
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -53,155 +54,161 @@ sub getsetup () { #{{{
safe => 0,
rebuild => 0,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
debug("skeleton plugin checkconfig");
-} #}}}
+}
-sub refresh () { #{{{
+sub refresh () {
debug("skeleton plugin refresh");
-} #}}}
+}
-sub needsbuild () { #{{{
+sub needsbuild () {
debug("skeleton plugin needsbuild");
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
return "skeleton plugin result";
-} # }}}
+}
-sub filter (@) { #{{{
+sub filter (@) {
my %params=@_;
debug("skeleton plugin running as filter");
return $params{content};
-} # }}}
+}
-sub linkify (@) { #{{{
+sub linkify (@) {
my %params=@_;
debug("skeleton plugin running as linkify");
return $params{content};
-} # }}}
+}
-sub scan (@) { #{{{a
+sub scan (@) {
my %params=@_;
debug("skeleton plugin running as scan");
-} # }}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
debug("skeleton plugin running as htmlize");
return $params{content};
-} # }}}
+}
-sub sanitize (@) { #{{{
+sub sanitize (@) {
my %params=@_;
debug("skeleton plugin running as a sanitizer");
return $params{content};
-} # }}}
+}
-sub postscan (@) { #{{{
+sub postscan (@) {
my %params=@_;
debug("skeleton plugin running as postscan");
-} # }}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params=@_;
debug("skeleton plugin running as a formatter");
return $params{content};
-} # }}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
my $template=$params{template};
debug("skeleton plugin running as a pagetemplate hook");
-} # }}}
+}
-sub templatefile (@) { #{{{
+sub templatefile (@) {
my %params=@_;
my $page=$params{page};
debug("skeleton plugin running as a templatefile hook");
-} # }}}
+}
-sub delete (@) { #{{{
+sub delete (@) {
my @files=@_;
debug("skeleton plugin told that files were deleted: @files");
-} #}}}
+}
-sub change (@) { #{{{
+sub change (@) {
my @files=@_;
debug("skeleton plugin told that changed files were rendered: @files");
-} #}}}
+}
-sub cgi ($) { #{{{
+sub cgi ($) {
my $cgi=shift;
debug("skeleton plugin running in cgi");
-} #}}}
+}
-sub auth ($$) { #{{{
+sub auth ($$) {
my $cgi=shift;
my $session=shift;
debug("skeleton plugin running in auth");
-} #}}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $cgi=shift;
my $session=shift;
debug("skeleton plugin running in sessioncgi");
-} #}}}
+}
-sub canedit ($$$) { #{{{
+sub canedit ($$$) {
my $page=shift;
my $cgi=shift;
my $session=shift;
debug("skeleton plugin running in canedit");
-} #}}}
+}
-sub editcontent ($$$) { #{{{
+sub checkcontent (@) {
+ my %params=@_;
+
+ debug("skeleton plugin running in checkcontent");
+}
+
+sub editcontent ($$$) {
my %params=@_;
debug("skeleton plugin running in editcontent");
return $params{content};
-} #}}}
+}
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
debug("skeleton plugin running in formbuilder_setup");
-} # }}}
+}
-sub formbuilder (@) { #{{{
+sub formbuilder (@) {
my %params=@_;
debug("skeleton plugin running in formbuilder");
-} # }}}
+}
-sub savestate () { #{{{
+sub savestate () {
debug("skeleton plugin running in savestate");
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/smiley.pm b/IkiWiki/Plugin/smiley.pm
index 2633b1ea1..0d77916d0 100644
--- a/IkiWiki/Plugin/smiley.pm
+++ b/IkiWiki/Plugin/smiley.pm
@@ -3,18 +3,18 @@ package IkiWiki::Plugin::smiley;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %smileys;
my $smiley_regexp;
-sub import { #{{{
+sub import {
add_underlay("smiley");
hook(type => "getsetup", id => "smiley", call => \&getsetup);
hook(type => "sanitize", id => "smiley", call => \&sanitize);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -22,9 +22,9 @@ sub getsetup () { #{{{
# removes the smileys, which would break links
rebuild => 1,
},
-} #}}}
+}
-sub build_regexp () { #{{{
+sub build_regexp () {
my $list=readfile(srcfile("smileys.mdwn"));
while ($list =~ m/^\s*\*\s+\\\\([^\s]+)\s+\[\[([^]]+)\]\]/mg) {
my $smiley=$1;
@@ -50,9 +50,9 @@ sub build_regexp () { #{{{
$smiley_regexp='('.join('|', map { quotemeta }
reverse sort keys %smileys).')';
#debug($smiley_regexp);
-} #}}}
+}
-sub sanitize (@) { #{{{
+sub sanitize (@) {
my %params=@_;
build_regexp() unless defined $smiley_regexp;
@@ -87,14 +87,14 @@ MATCH: while (m{(?:^|(?<=\s|>))(\\?)$smiley_regexp(?:(?=\s|<)|$)}g) {
}
else {
# Replace the smiley with its expanded value.
- substr($_, $spos, length($smiley))=
- htmllink($params{page}, $params{destpage},
+ my $link=htmllink($params{page}, $params{destpage},
$smileys{$smiley}, linktext => $smiley);
- pos=$epos+1;
+ substr($_, $spos, length($smiley))=$link;
+ pos=$epos+length($link);
}
}
return $_;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/sparkline.pm b/IkiWiki/Plugin/sparkline.pm
index 901c2f683..458192695 100644
--- a/IkiWiki/Plugin/sparkline.pm
+++ b/IkiWiki/Plugin/sparkline.pm
@@ -3,7 +3,7 @@ package IkiWiki::Plugin::sparkline;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use IPC::Open2;
my $match_num=qr/[-+]?[0-9]+(?:\.[0-9]+)?/;
@@ -14,20 +14,20 @@ my %locmap=(
left => 'TEXT_LEFT',
);
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "sparkline", call => \&getsetup);
hook(type => "preprocess", id => "sparkline", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
my $php;
@@ -130,7 +130,7 @@ sub preprocess (@) { #{{{
if (! -e "$config{destdir}/$fn") {
my $pid;
- my $sigpipe=0;;
+ my $sigpipe=0;
$SIG{PIPE}=sub { $sigpipe=1 };
$pid=open2(*IN, *OUT, "php");
@@ -166,6 +166,6 @@ sub preprocess (@) { #{{{
}
return '<img src="'.urlto($fn, $params{destpage}).'" alt="graph" />';
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/svn.pm b/IkiWiki/Plugin/svn.pm
index d738720be..fe55e7d08 100644
--- a/IkiWiki/Plugin/svn.pm
+++ b/IkiWiki/Plugin/svn.pm
@@ -6,7 +6,7 @@ use strict;
use IkiWiki;
use POSIX qw(setlocale LC_CTYPE);
-sub import { #{{{
+sub import {
hook(type => "checkconfig", id => "svn", call => \&checkconfig);
hook(type => "getsetup", id => "svn", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
@@ -19,9 +19,9 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (! defined $config{svnpath}) {
$config{svnpath}="trunk";
}
@@ -37,9 +37,9 @@ sub checkconfig () { #{{{
wrappermode => (defined $config{svn_wrappermode} ? $config{svn_wrappermode} : "04755"),
};
}
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
@@ -87,7 +87,7 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
# svn needs LC_CTYPE set to a UTF-8 locale, so try to find one. Any will do.
sub find_lc_ctype() {
@@ -107,27 +107,27 @@ sub find_lc_ctype() {
# fallback to the current locale
return $current;
-} # }}}
+}
$ENV{LC_CTYPE} = $ENV{LC_CTYPE} || find_lc_ctype();
-sub svn_info ($$) { #{{{
+sub svn_info ($$) {
my $field=shift;
my $file=shift;
my $info=`LANG=C svn info $file`;
my ($ret)=$info=~/^$field: (.*)$/m;
return $ret;
-} #}}}
+}
-sub rcs_update () { #{{{
+sub rcs_update () {
if (-d "$config{srcdir}/.svn") {
if (system("svn", "update", "--quiet", $config{srcdir}) != 0) {
warn("svn update failed\n");
}
}
-} #}}}
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
# Prepares to edit a file under revision control. Returns a token
# that must be passed into rcs_commit when the file is ready
# for committing.
@@ -140,9 +140,9 @@ sub rcs_prepedit ($) { #{{{
my $rev=svn_info("Revision", "$config{srcdir}/$file");
return defined $rev ? $rev : "";
}
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
# Tries to commit the page; returns undef on _success_ and
# a version of the page with the rcs's conflict markers on failure.
# The file is relative to the srcdir.
@@ -185,7 +185,7 @@ sub rcs_commit ($$$;$$) { #{{{
}
}
return undef # success
-} #}}}
+}
sub rcs_commit_staged ($$$) {
# Commits all staged changes. Changes can be staged using rcs_add,
@@ -209,7 +209,7 @@ sub rcs_commit_staged ($$$) {
return undef # success
}
-sub rcs_add ($) { #{{{
+sub rcs_add ($) {
# filename is relative to the root of the srcdir
my $file=shift;
@@ -224,9 +224,9 @@ sub rcs_add ($) { #{{{
warn("svn add failed\n");
}
}
-} #}}}
+}
-sub rcs_remove ($) { #{{{
+sub rcs_remove ($) {
# filename is relative to the root of the srcdir
my $file=shift;
@@ -235,9 +235,9 @@ sub rcs_remove ($) { #{{{
warn("svn rm failed\n");
}
}
-} #}}}
+}
-sub rcs_rename ($$) { #{{{
+sub rcs_rename ($$) {
# filenames relative to the root of the srcdir
my ($src, $dest)=@_;
@@ -258,9 +258,9 @@ sub rcs_rename ($$) { #{{{
warn("svn rename failed\n");
}
}
-} #}}}
+}
-sub rcs_recentchanges ($) { #{{{
+sub rcs_recentchanges ($) {
my $num=shift;
my @ret;
@@ -341,14 +341,14 @@ sub rcs_recentchanges ($) { #{{{
}
return @ret;
-} #}}}
+}
-sub rcs_diff ($) { #{{{
+sub rcs_diff ($) {
my $rev=IkiWiki::possibly_foolish_untaint(int(shift));
return `svnlook diff $config{svnrepo} -r$rev --no-diff-deleted`;
-} #}}}
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_getctime ($) {
my $file=shift;
my $svn_log_infoline=qr/^r\d+\s+\|\s+[^\s]+\s+\|\s+(\d+-\d+-\d+\s+\d+:\d+:\d+\s+[-+]?\d+).*/;
@@ -376,6 +376,6 @@ sub rcs_getctime ($) { #{{{
$date=str2time($date);
debug("found ctime ".localtime($date)." for $file");
return $date;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/table.pm b/IkiWiki/Plugin/table.pm
index e782fc238..96d63f455 100644
--- a/IkiWiki/Plugin/table.pm
+++ b/IkiWiki/Plugin/table.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::table;
use warnings;
use strict;
use Encode;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "table", call => \&getsetup);
- hook(type => "preprocess", id => "table", call => \&preprocess);
-} # }}}
+ hook(type => "preprocess", id => "table", call => \&preprocess, scan => 1);
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params =(
format => 'auto',
header => 'row',
@@ -27,13 +27,31 @@ sub preprocess (@) { #{{{
);
if (exists $params{file}) {
- if (! $pagesources{$params{file}}) {
+ if (! exists $pagesources{$params{file}}) {
error gettext("cannot find file");
}
$params{data} = readfile(srcfile($params{file}));
add_depends($params{page}, $params{file});
}
+ if (! defined wantarray) {
+ # scan mode -- if the table uses an external file, need to
+ # scan that file too.
+ return unless exists $params{file};
+
+ IkiWiki::run_hooks(scan => sub {
+ shift->(
+ page => $params{page},
+ content => $params{data},
+ );
+ });
+
+ # Preprocess in scan-only mode.
+ IkiWiki::preprocess($params{page}, $params{page}, $params{data}, 1);
+
+ return;
+ }
+
if (lc $params{format} eq 'auto') {
# first try the more simple format
if (is_dsv_data($params{data})) {
@@ -50,22 +68,18 @@ sub preprocess (@) { #{{{
defined $params{delimiter} ? $params{delimiter} : ",",);
# linkify after parsing since html link quoting can
# confuse CSV parsing
- if (! exists $params{file}) {
- @data=map {
- [ map {
- IkiWiki::linkify($params{page},
- $params{destpage}, $_);
- } @$_ ]
- } @data;
- }
+ @data=map {
+ [ map {
+ IkiWiki::linkify($params{page},
+ $params{destpage}, $_);
+ } @$_ ]
+ } @data;
}
elsif (lc $params{format} eq 'dsv') {
# linkify before parsing since wikilinks can contain the
# delimiter
- if (! exists $params{file}) {
- $params{data} = IkiWiki::linkify($params{page},
- $params{destpage}, $params{data});
- }
+ $params{data} = IkiWiki::linkify($params{page},
+ $params{destpage}, $params{data});
@data=split_dsv($params{data},
defined $params{delimiter} ? $params{delimiter} : "|",);
}
@@ -102,16 +116,16 @@ sub preprocess (@) { #{{{
else {
return $html;
}
-} #}}}
+}
-sub is_dsv_data ($) { #{{{
+sub is_dsv_data ($) {
my $text = shift;
my ($line) = split(/\n/, $text);
return $line =~ m{.+\|};
}
-sub split_csv ($$) { #{{{
+sub split_csv ($$) {
my @text_lines = split(/\n/, shift);
my $delimiter = shift;
@@ -137,9 +151,9 @@ sub split_csv ($$) { #{{{
}
return @data;
-} #}}}
+}
-sub split_dsv ($$) { #{{{
+sub split_dsv ($$) {
my @text_lines = split(/\n/, shift);
my $delimiter = shift;
$delimiter="|" unless defined $delimiter;
@@ -150,9 +164,9 @@ sub split_dsv ($$) { #{{{
}
return @data;
-} #}}}
+}
-sub genrow ($@) { #{{{
+sub genrow ($@) {
my %params=%{shift()};
my $elt = shift;
my @data = @_;
@@ -190,6 +204,6 @@ sub genrow ($@) { #{{{
push @ret, "\t\t</tr>";
return @ret;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/tag.pm b/IkiWiki/Plugin/tag.pm
index 158657507..8fe9c6828 100644
--- a/IkiWiki/Plugin/tag.pm
+++ b/IkiWiki/Plugin/tag.pm
@@ -4,26 +4,26 @@ package IkiWiki::Plugin::tag;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my %tags;
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "tag", call => \&getopt);
hook(type => "getsetup", id => "tag", call => \&getsetup);
hook(type => "preprocess", id => "tag", call => \&preprocess_tag, scan => 1);
hook(type => "preprocess", id => "taglink", call => \&preprocess_taglink, scan => 1);
hook(type => "pagetemplate", id => "tag", call => \&pagetemplate);
-} # }}}
+}
-sub getopt () { #{{{
+sub getopt () {
eval q{use Getopt::Long};
error($@) if $@;
Getopt::Long::Configure('pass_through');
GetOptions("tagbase=s" => \$config{tagbase});
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -36,35 +36,30 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub tagpage ($) { #{{{
+sub tagpage ($) {
my $tag=shift;
if ($tag !~ m{^\.?/} &&
defined $config{tagbase}) {
- $tag=$config{tagbase}."/".$tag;
+ $tag="/".$config{tagbase}."/".$tag;
+ $tag=~y#/#/#s; # squash dups
}
return $tag;
-} #}}}
+}
-sub taglink ($$$;@) { #{{{
+sub taglink ($$$;@) {
my $page=shift;
my $destpage=shift;
my $tag=shift;
my %opts=@_;
- my $link=tagpage($tag);
+ return htmllink($page, $destpage, tagpage($tag), %opts);
+}
- # Force tag creation links to create the tag under /tagbase,
- # if there is a tagbase and this tag used it.
- $link="/".$link if $tag ne $link;
-
- return htmllink($page, $destpage, $link, %opts);
-} #}}}
-
-sub preprocess_tag (@) { #{{{
+sub preprocess_tag (@) {
if (! @_) {
return "";
}
@@ -82,9 +77,9 @@ sub preprocess_tag (@) { #{{{
}
return "";
-} # }}}
+}
-sub preprocess_taglink (@) { #{{{
+sub preprocess_taglink (@) {
if (! @_) {
return "";
}
@@ -107,9 +102,9 @@ sub preprocess_taglink (@) { #{{{
grep {
$_ ne 'page' && $_ ne 'destpage' && $_ ne 'preview'
} keys %params);
-} # }}}
+}
-sub pagetemplate (@) { #{{{
+sub pagetemplate (@) {
my %params=@_;
my $page=$params{page};
my $destpage=$params{destpage};
@@ -128,6 +123,14 @@ sub pagetemplate (@) { #{{{
sort keys %{$tags{$page}}]);
}
}
-} # }}}
+}
+
+package IkiWiki::PageSpec;
+
+sub match_tagged ($$;@) {
+ my $page = shift;
+ my $glob = shift;
+ return match_link($page, IkiWiki::Plugin::tag::tagpage($glob));
+}
1
diff --git a/IkiWiki/Plugin/template.pm b/IkiWiki/Plugin/template.pm
index c980df48f..b6097bb49 100644
--- a/IkiWiki/Plugin/template.pm
+++ b/IkiWiki/Plugin/template.pm
@@ -4,24 +4,25 @@ package IkiWiki::Plugin::template;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use HTML::Template;
use Encode;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "template", call => \&getsetup);
- hook(type => "preprocess", id => "template", call => \&preprocess);
-} # }}}
+ hook(type => "preprocess", id => "template", call => \&preprocess,
+ scan => 1);
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
if (! exists $params{id}) {
@@ -68,9 +69,13 @@ sub preprocess (@) { #{{{
}
}
+ # This needs to run even in scan mode, in order to process
+ # links and other metadata includes via the template.
+ my $scan=! defined wantarray;
+
return IkiWiki::preprocess($params{page}, $params{destpage},
IkiWiki::filter($params{page}, $params{destpage},
- $template->output));
-} # }}}
+ $template->output), $scan);
+}
1
diff --git a/IkiWiki/Plugin/testpagespec.pm b/IkiWiki/Plugin/testpagespec.pm
index 9f9b50f01..440fca33b 100644
--- a/IkiWiki/Plugin/testpagespec.pm
+++ b/IkiWiki/Plugin/testpagespec.pm
@@ -3,22 +3,22 @@ package IkiWiki::Plugin::testpagespec;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "testpagespec", call => \&getsetup);
hook(type => "preprocess", id => "testpagespec", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
foreach my $param (qw{match pagespec}) {
@@ -37,6 +37,6 @@ sub preprocess (@) { #{{{
else {
return "no match: $ret";
}
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/teximg.pm b/IkiWiki/Plugin/teximg.pm
index 661d97b1f..dba5372b5 100644
--- a/IkiWiki/Plugin/teximg.pm
+++ b/IkiWiki/Plugin/teximg.pm
@@ -8,7 +8,7 @@ use strict;
use Digest::MD5 qw(md5_hex);
use File::Temp qw(tempdir);
use HTML::Entities;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
my $default_prefix = <<EOPREFIX;
\\documentclass{article}
@@ -21,12 +21,12 @@ EOPREFIX
my $default_postfix = '\\end{document}';
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "teximg", call => \&getsetup);
hook(type => "preprocess", id => "teximg", call => \&preprocess);
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -52,9 +52,9 @@ sub getsetup () { #{{{
safe => 0, # Not sure how secure LaTeX is...
rebuild => 1,
},
-} #}}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params = @_;
my $height = $params{height};
@@ -76,9 +76,9 @@ sub preprocess (@) { #{{{
else {
error gettext("code includes disallowed latex commands")
}
-} #}}}
+}
-sub check_height ($) { #{{{
+sub check_height ($) {
# Since latex doesn't support unlimited scaling this function
# returns the closest supported size.
my $height =shift;
@@ -95,9 +95,9 @@ sub check_height ($) { #{{{
}
}
return $ret;
-} #}}}
+}
-sub create ($$$) { #{{{
+sub create ($$$) {
# This function calls the image generating function and returns
# the <img .. /> for the generated image.
my $code = shift;
@@ -127,9 +127,9 @@ sub create ($$$) { #{{{
else {
error qq{<a href="$logurl">}.gettext("failed to generate image from code")."</a>";
}
-} #}}}
+}
-sub gen_image ($$$$) { #{{{
+sub gen_image ($$$$) {
# Actually creates the image.
my $code = shift;
my $height = shift;
@@ -180,18 +180,18 @@ sub gen_image ($$$$) { #{{{
return 0;
}
-} #}}}
+}
-sub create_tmp_dir ($) { #{{{
+sub create_tmp_dir ($) {
# Create a temp directory, it will be removed when ikiwiki exits.
my $base = shift;
my $template = $base.".XXXXXXXXXX";
my $tmpdir = tempdir($template, TMPDIR => 1, CLEANUP => 1);
return $tmpdir;
-} #}}}
+}
-sub check ($) { #{{{
+sub check ($) {
# Check if the code is ok
my $code = shift;
@@ -219,6 +219,6 @@ sub check ($) { #{{{
}
}
return 1;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/textile.pm b/IkiWiki/Plugin/textile.pm
index bbd282f0c..b604aa3c5 100644
--- a/IkiWiki/Plugin/textile.pm
+++ b/IkiWiki/Plugin/textile.pm
@@ -6,29 +6,29 @@ package IkiWiki::Plugin::textile;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use Encode;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "textile", call => \&getsetup);
hook(type => "htmlize", id => "txtl", call => \&htmlize);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
-} #}}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
my $content = decode_utf8(encode_utf8($params{content}));
eval q{use Text::Textile};
return $content if $@;
return Text::Textile::textile($content);
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/tla.pm b/IkiWiki/Plugin/tla.pm
index 0a5c161b2..f4b20a6ec 100644
--- a/IkiWiki/Plugin/tla.pm
+++ b/IkiWiki/Plugin/tla.pm
@@ -5,7 +5,7 @@ use warnings;
use strict;
use IkiWiki;
-sub import { #{{{
+sub import {
hook(type => "checkconfig", id => "tla", call => \&checkconfig);
hook(type => "getsetup", id => "tla", call => \&getsetup);
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
@@ -18,18 +18,18 @@ sub import { #{{{
hook(type => "rcs", id => "rcs_recentchanges", call => \&rcs_recentchanges);
hook(type => "rcs", id => "rcs_diff", call => \&rcs_diff);
hook(type => "rcs", id => "rcs_getctime", call => \&rcs_getctime);
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (defined $config{tla_wrapper} && length $config{tla_wrapper}) {
push @{$config{wrappers}}, {
wrapper => $config{tla_wrapper},
wrappermode => (defined $config{tla_wrappermode} ? $config{tla_wrappermode} : "06755"),
};
}
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # rcs plugin
@@ -63,9 +63,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub quiet_system (@) { #{{{
+sub quiet_system (@) {
# See Debian bug #385939.
open (SAVEOUT, ">&STDOUT");
close STDOUT;
@@ -75,17 +75,17 @@ sub quiet_system (@) { #{{{
open (STDOUT, ">&SAVEOUT");
close SAVEOUT;
return $ret;
-} #}}}
+}
-sub rcs_update () { #{{{
+sub rcs_update () {
if (-d "$config{srcdir}/{arch}") {
if (quiet_system("tla", "replay", "-d", $config{srcdir}) != 0) {
warn("tla replay failed\n");
}
}
-} #}}}
+}
-sub rcs_prepedit ($) { #{{{
+sub rcs_prepedit ($) {
my $file=shift;
if (-d "$config{srcdir}/{arch}") {
@@ -94,9 +94,9 @@ sub rcs_prepedit ($) { #{{{
my $rev=`tla tree-id $config{srcdir}`;
return defined $rev ? $rev : "";
}
-} #}}}
+}
-sub rcs_commit ($$$;$$) { #{{{
+sub rcs_commit ($$$;$$) {
my $file=shift;
my $message=shift;
my $rcstoken=shift;
@@ -135,7 +135,7 @@ sub rcs_commit ($$$;$$) { #{{{
}
}
return undef # success
-} #}}}
+}
sub rcs_commit_staged ($$$) {
# Commits all staged changes. Changes can be staged using rcs_add,
@@ -145,7 +145,7 @@ sub rcs_commit_staged ($$$) {
error("rcs_commit_staged not implemented for tla"); # TODO
}
-sub rcs_add ($) { #{{{
+sub rcs_add ($) {
my $file=shift;
if (-d "$config{srcdir}/{arch}") {
@@ -153,19 +153,19 @@ sub rcs_add ($) { #{{{
warn("tla add failed\n");
}
}
-} #}}}
+}
-sub rcs_remove ($) { # {{{
+sub rcs_remove ($) {
my $file = shift;
error("rcs_remove not implemented for tla"); # TODO
-} #}}}
+}
sub rcs_rename ($$) { # {{{a
my ($src, $dest) = @_;
error("rcs_rename not implemented for tla"); # TODO
-} #}}}
+}
sub rcs_recentchanges ($) {
my $num=shift;
@@ -239,7 +239,7 @@ sub rcs_recentchanges ($) {
return @ret;
}
-sub rcs_diff ($) { #{{{
+sub rcs_diff ($) {
my $rev=shift;
my $logs = `tla logs -d $config{srcdir}`;
my @changesets = reverse split(/\n/, $logs);
@@ -251,9 +251,9 @@ sub rcs_diff ($) { #{{{
my $revminusone = $changesets[$i+1];
return `tla diff -d $config{srcdir} $revminusone`;
-} #}}}
+}
-sub rcs_getctime ($) { #{{{
+sub rcs_getctime ($) {
my $file=shift;
eval q{use Date::Parse};
error($@) if $@;
@@ -281,6 +281,6 @@ sub rcs_getctime ($) { #{{{
my $date=str2time($sdate, 'UTC');
debug("found ctime ".localtime($date)." for $file");
return $date;
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/toc.pm b/IkiWiki/Plugin/toc.pm
index dff9d9aa5..a585564e7 100644
--- a/IkiWiki/Plugin/toc.pm
+++ b/IkiWiki/Plugin/toc.pm
@@ -4,26 +4,26 @@ package IkiWiki::Plugin::toc;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use HTML::Parser;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "toc", call => \&getsetup);
hook(type => "preprocess", id => "toc", call => \&preprocess);
hook(type => "format", id => "toc", call => \&format);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
my %tocpages;
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
if ($params{page} eq $params{destpage}) {
@@ -40,9 +40,9 @@ sub preprocess (@) { #{{{
# right.
return "";
}
-} # }}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params=@_;
my $content=$params{content};
diff --git a/IkiWiki/Plugin/toggle.pm b/IkiWiki/Plugin/toggle.pm
index 610d38e3a..aae8cdf84 100644
--- a/IkiWiki/Plugin/toggle.pm
+++ b/IkiWiki/Plugin/toggle.pm
@@ -3,80 +3,27 @@ package IkiWiki::Plugin::toggle;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-# Here's the javascript that makes this possible. A key feature is the use
-# of css to hide toggleables, to avoid any flashing on page load. The css
-# is only emitted after the javascript tests that it's going to be able to
-# show the toggleables.
-our $javascript=<<'EOF';
-<script type="text/javascript">
-<!--
-if (document.getElementById && document.getElementsByTagName && document.createTextNode) {
- document.write('<style type="text/css">div.toggleable { display: none; }</style>');
- window.onload = inittoggle;
-}
-
-function inittoggle() {
- var as = getElementsByClass('toggle');
- for (var i = 0; i < as.length; i++) {
- var id = as[i].href.match(/#(\w.+)/)[1];
- if (document.getElementById(id).className == "toggleable")
- document.getElementById(id).style.display="none";
- as[i].onclick = function() {
- toggle(this);
- return false;
- }
- }
-}
-
-function toggle(s) {
- var id = s.href.match(/#(\w.+)/)[1];
- style = document.getElementById(id).style;
- if (style.display == "none")
- style.display = "block";
- else
- style.display = "none";
-}
-
-function getElementsByClass(cls, node, tag) {
- if (document.getElementsByClass)
- return document.getElementsByClass(cls, node, tag);
- if (! node) node = document;
- if (! tag) tag = '*';
- var ret = new Array();
- var pattern = new RegExp("(^|\\s)"+cls+"(\\s|$)");
- var els = node.getElementsByTagName(tag);
- for (i = 0; i < els.length; i++) {
- if ( pattern.test(els[i].className) ) {
- ret.push(els[i]);
- }
- }
- return ret;
-}
-
-//-->
-</script>
-EOF
-
-sub import { #{{{
+sub import {
+ add_underlay("javascript");
hook(type => "getsetup", id => "toggle", call => \&getsetup);
hook(type => "preprocess", id => "toggle",
call => \&preprocess_toggle);
hook(type => "preprocess", id => "toggleable",
call => \&preprocess_toggleable);
hook(type => "format", id => "toggle", call => \&format);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub genid ($$) { #{{{
+sub genid ($$) {
my $page=shift;
my $id=shift;
@@ -88,16 +35,16 @@ sub genid ($$) { #{{{
$id="id$id";
}
return $id;
-} #}}}
+}
-sub preprocess_toggle (@) { #{{{
+sub preprocess_toggle (@) {
my %params=(id => "default", text => "more", @_);
my $id=genid($params{page}, $params{id});
return "<a class=\"toggle\" href=\"#$id\">$params{text}</a>";
-} # }}}
+}
-sub preprocess_toggleable (@) { #{{{
+sub preprocess_toggleable (@) {
my %params=(id => "default", text => "", open => "no", @_);
# Preprocess the text to expand any preprocessor directives
@@ -114,19 +61,29 @@ sub preprocess_toggleable (@) { #{{{
my ($indent)=$params{text}=~/( +)$/;
$indent="" unless defined $indent;
return "<div class=\"$class\" id=\"$id\"></div>\n\n$params{text}\n$indent<div class=\"toggleableend\"></div>";
-} # }}}
+}
-sub format (@) { #{{{
+sub format (@) {
my %params=@_;
if ($params{content}=~s!(<div class="toggleable(?:-open)?" id="[^"]+">\s*)</div>!$1!g) {
$params{content}=~s/<div class="toggleableend">//g;
- if (! ($params{content}=~s!^<body>!<body>$javascript!m)) {
+ if (! ($params{content}=~s!^(<body>)!$1.include_javascript($params{page})!em)) {
# no </body> tag, probably in preview mode
- $params{content}=$javascript.$params{content};
+ $params{content}=include_javascript($params{page}, 1).$params{content};
}
}
return $params{content};
-} # }}}
+}
+
+sub include_javascript ($;$) {
+ my $page=shift;
+ my $absolute=shift;
+
+ return '<script src="'.urlto("ikiwiki.js", $page, $absolute).
+ '" type="text/javascript" charset="utf-8"></script>'."\n".
+ '<script src="'.urlto("toggle.js", $page, $absolute).
+ '" type="text/javascript" charset="utf-8"></script>';
+}
1
diff --git a/IkiWiki/Plugin/txt.pm b/IkiWiki/Plugin/txt.pm
index e4c9e5d6a..8599bdc8e 100644
--- a/IkiWiki/Plugin/txt.pm
+++ b/IkiWiki/Plugin/txt.pm
@@ -8,7 +8,7 @@ package IkiWiki::Plugin::txt;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
use HTML::Entities;
my $findurl=0;
@@ -24,13 +24,13 @@ sub import {
}
}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => 1, # format plugin
},
-} #}}}
+}
# We use filter to convert raw text to HTML
# (htmlize is called after other plugins insert HTML)
@@ -39,7 +39,7 @@ sub filter (@) {
my $content = $params{content};
if (defined $pagesources{$params{page}} && $pagesources{$params{page}} =~ /\.txt$/) {
- encode_entities($content);
+ encode_entities($content, "<>&");
if ($findurl) {
my $finder = URI::Find->new(sub {
my ($uri, $orig_uri) = @_;
diff --git a/IkiWiki/Plugin/typography.pm b/IkiWiki/Plugin/typography.pm
index 27089b390..f62be82bb 100644
--- a/IkiWiki/Plugin/typography.pm
+++ b/IkiWiki/Plugin/typography.pm
@@ -4,22 +4,22 @@ package IkiWiki::Plugin::typography;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getopt", id => "typography", call => \&getopt);
hook(type => "getsetup", id => "typography", call => \&getsetup);
IkiWiki::hook(type => "sanitize", id => "typography", call => \&sanitize);
-} # }}}
+}
-sub getopt () { #{{{
+sub getopt () {
eval q{use Getopt::Long};
error($@) if $@;
Getopt::Long::Configure('pass_through');
GetOptions("typographyattributes=s" => \$config{typographyattributes});
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
eval q{use Text::Typography};
error($@) if $@;
@@ -36,9 +36,9 @@ sub getsetup () { #{{{
safe => 1,
rebuild => 1,
},
-} #}}}
+}
-sub sanitize (@) { #{{{
+sub sanitize (@) {
my %params=@_;
eval q{use Text::Typography};
@@ -46,6 +46,6 @@ sub sanitize (@) { #{{{
my $attributes=defined $config{typographyattributes} ? $config{typographyattributes} : '3';
return Text::Typography::typography($params{content}, $attributes);
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/underlay.pm b/IkiWiki/Plugin/underlay.pm
new file mode 100644
index 000000000..380d418fb
--- /dev/null
+++ b/IkiWiki/Plugin/underlay.pm
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::underlay;
+# 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
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "getsetup", id => "underlay", call => \&getsetup);
+ hook(type => "checkconfig", id => "underlay", call => \&checkconfig);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 0,
+ rebuild => undef,
+ },
+ add_underlays => {
+ type => "string",
+ default => [],
+ description => "extra underlay directories to add",
+ advanced => 1,
+ safe => 0,
+ rebuild => 1,
+ },
+}
+
+sub checkconfig () {
+ return unless exists $config{add_underlays};
+
+ foreach my $dir (@{$config{add_underlays}}) {
+ add_underlay($dir);
+ }
+}
+
+1;
diff --git a/IkiWiki/Plugin/version.pm b/IkiWiki/Plugin/version.pm
index 18e9613ae..587cd55fa 100644
--- a/IkiWiki/Plugin/version.pm
+++ b/IkiWiki/Plugin/version.pm
@@ -4,23 +4,23 @@ package IkiWiki::Plugin::version;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "version", call => \&getsetup);
hook(type => "needsbuild", id => "version", call => \&needsbuild);
hook(type => "preprocess", id => "version", call => \&preprocess);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
rebuild => undef,
},
-} #}}}
+}
-sub needsbuild (@) { #{{{
+sub needsbuild (@) {
my $needsbuild=shift;
foreach my $page (keys %pagestate) {
if (exists $pagestate{$page}{version}{shown}) {
@@ -36,11 +36,11 @@ sub needsbuild (@) { #{{{
}
}
}
-} # }}}
+}
-sub preprocess (@) { #{{{
+sub preprocess (@) {
my %params=@_;
$pagestate{$params{destpage}}{version}{shown}=$IkiWiki::version;
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/websetup.pm b/IkiWiki/Plugin/websetup.pm
index 827ee3099..95d044c08 100644
--- a/IkiWiki/Plugin/websetup.pm
+++ b/IkiWiki/Plugin/websetup.pm
@@ -3,17 +3,17 @@ package IkiWiki::Plugin::websetup;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "websetup", call => \&getsetup);
hook(type => "checkconfig", id => "websetup", call => \&checkconfig);
hook(type => "sessioncgi", id => "websetup", call => \&sessioncgi);
hook(type => "formbuilder_setup", id => "websetup",
call => \&formbuilder_setup);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 1,
@@ -33,15 +33,15 @@ sub getsetup () { #{{{
safe => 0,
rebuild => 0,
},
-} #}}}
+}
-sub checkconfig () { #{{{
+sub checkconfig () {
if (! exists $config{websetup_show_unsafe}) {
$config{websetup_show_unsafe}=1;
}
-} #}}}
+}
-sub formatexample ($$) { #{{{
+sub formatexample ($$) {
my $example=shift;
my $value=shift;
@@ -54,9 +54,9 @@ sub formatexample ($$) { #{{{
else {
return "";
}
-} #}}}
+}
-sub showfields ($$$@) { #{{{
+sub showfields ($$$@) {
my $form=shift;
my $plugin=shift;
my $enabled=shift;
@@ -139,7 +139,7 @@ sub showfields ($$$@) { #{{{
my $value=$config{$key};
if ($info{safe} && (ref $value eq 'ARRAY' || ref $info{example} eq 'ARRAY')) {
- $value=[@{$value}, "", ""]; # blank items for expansion
+ $value=[(ref $value eq 'ARRAY' ? @{$value} : ""), "", ""]; # blank items for expansion
}
if ($info{type} eq "string") {
@@ -207,16 +207,16 @@ sub showfields ($$$@) { #{{{
}
return %enabledfields;
-} #}}}
+}
-sub enable_plugin ($) { #{{{
+sub enable_plugin ($) {
my $plugin=shift;
$config{disable_plugins}=[grep { $_ ne $plugin } @{$config{disable_plugins}}];
push @{$config{add_plugins}}, $plugin;
}
-sub disable_plugin ($) { #{{{
+sub disable_plugin ($) {
my $plugin=shift;
if (grep { $_ eq $plugin } @{$config{add_plugins}}) {
@@ -227,7 +227,7 @@ sub disable_plugin ($) { #{{{
}
}
-sub showform ($$) { #{{{
+sub showform ($$) {
my $cgi=shift;
my $session=shift;
@@ -441,9 +441,9 @@ sub showform ($$) { #{{{
}
IkiWiki::showform($form, $buttons, $session, $cgi);
-} #}}}
+}
-sub sessioncgi ($$) { #{{{
+sub sessioncgi ($$) {
my $cgi=shift;
my $session=shift;
@@ -451,9 +451,9 @@ sub sessioncgi ($$) { #{{{
showform($cgi, $session);
exit;
}
-} #}}}
+}
-sub formbuilder_setup (@) { #{{{
+sub formbuilder_setup (@) {
my %params=@_;
my $form=$params{form};
@@ -464,6 +464,6 @@ sub formbuilder_setup (@) { #{{{
exit;
}
}
-} #}}}
+}
1
diff --git a/IkiWiki/Plugin/wikitext.pm b/IkiWiki/Plugin/wikitext.pm
index c47ccb7b1..accb03bbe 100644
--- a/IkiWiki/Plugin/wikitext.pm
+++ b/IkiWiki/Plugin/wikitext.pm
@@ -4,29 +4,29 @@ package IkiWiki::Plugin::wikitext;
use warnings;
use strict;
-use IkiWiki 2.00;
+use IkiWiki 3.00;
-sub import { #{{{
+sub import {
hook(type => "getsetup", id => "wiki", call => \&getsetup);
hook(type => "htmlize", id => "wiki", call => \&htmlize);
-} # }}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
return
plugin => {
safe => 0, # format plugin
rebuild => undef,
},
-} #}}}
+}
-sub htmlize (@) { #{{{
+sub htmlize (@) {
my %params=@_;
my $content = $params{content};
eval q{use Text::WikiFormat};
return $content if $@;
return Text::WikiFormat::format($content, undef, { implicit_links => 0 });
-} # }}}
+}
1
diff --git a/IkiWiki/Plugin/wmd.pm b/IkiWiki/Plugin/wmd.pm
new file mode 100644
index 000000000..9ddd237ab
--- /dev/null
+++ b/IkiWiki/Plugin/wmd.pm
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+package IkiWiki::Plugin::wmd;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+use POSIX;
+use Encode;
+
+sub import {
+ add_underlay("wmd");
+ hook(type => "getsetup", id => "wmd", call => \&getsetup);
+ hook(type => "formbuilder_setup", id => "wmd", call => \&formbuilder_setup);
+}
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ },
+}
+
+sub formbuilder_setup (@) {
+ my %params=@_;
+ my $form=$params{form};
+
+ return if ! defined $form->field("do");
+
+ return unless $form->field("do") eq "edit" ||
+ $form->field("do") eq "create" ||
+ $form->field("do") eq "comment";
+
+ $form->tmpl_param("wmd_preview", "<div class=\"wmd-preview\"></div>\n".
+ include_javascript(undef, 1));
+}
+
+sub include_javascript ($;$) {
+ my $page=shift;
+ my $absolute=shift;
+
+ my $wmdjs=urlto("wmd/wmd.js", $page, $absolute);
+ return <<"EOF"
+<script type="text/javascript">
+wmd_options = {
+ output: "Markdown"
+};
+</script>
+<script src="$wmdjs" type="text/javascript"></script>
+EOF
+}
+
+1
diff --git a/IkiWiki/Receive.pm b/IkiWiki/Receive.pm
new file mode 100644
index 000000000..37b6f2a62
--- /dev/null
+++ b/IkiWiki/Receive.pm
@@ -0,0 +1,135 @@
+#!/usr/bin/perl
+
+package IkiWiki::Receive;
+
+use warnings;
+use strict;
+use IkiWiki;
+
+sub getuser () {
+ my $user=(getpwuid(exists $ENV{CALLER_UID} ? $ENV{CALLER_UID} : $<))[0];
+ if (! defined $user) {
+ error("cannot determine username for $<");
+ }
+ return $user;
+}
+
+sub trusted () {
+ my $user=getuser();
+ return ! ref $config{untrusted_committers} ||
+ ! grep { $_ eq $user } @{$config{untrusted_committers}};
+}
+
+sub gen_wrapper () {
+ # Test for commits from untrusted committers in the wrapper, to
+ # avoid loading ikiwiki at all for trusted commits.
+
+ my $ret=<<"EOF";
+ {
+ int u=getuid();
+EOF
+ $ret.="\t\tif ( ".
+ join("&&", map {
+ my $uid=getpwnam($_);
+ if (! defined $uid) {
+ error(sprintf(gettext("cannot determine id of untrusted committer %s"), $_));
+ }
+ "u != $uid";
+ } @{$config{untrusted_committers}}).
+ ") exit(0);\n";
+ $ret.=<<"EOF";
+ asprintf(&s, "CALLER_UID=%i", u);
+ newenviron[i++]=s;
+ }
+EOF
+ return $ret;
+}
+
+sub test () {
+ exit 0 if trusted();
+
+ IkiWiki::lockwiki();
+ IkiWiki::loadindex();
+
+ # Dummy up a cgi environment to use when calling check_canedit
+ # and friends.
+ eval q{use CGI};
+ error($@) if $@;
+ my $cgi=CGI->new;
+ $ENV{REMOTE_ADDR}='unknown' unless exists $ENV{REMOTE_ADDR};
+
+ # And dummy up a session object.
+ require IkiWiki::CGI;
+ my $session=IkiWiki::cgi_getsession($cgi);
+ $session->param("name", getuser());
+ # Make sure whatever user was authed is in the
+ # userinfo db.
+ require IkiWiki::UserInfo;
+ if (! IkiWiki::userinfo_get($session->param("name"), "regdate")) {
+ IkiWiki::userinfo_setall($session->param("name"), {
+ email => "",
+ password => "",
+ regdate => time,
+ }) || error("failed adding user");
+ }
+
+ my %newfiles;
+
+ foreach my $change (IkiWiki::rcs_receive()) {
+ # This untaint is safe because we check file_pruned and
+ # wiki_file_regexp.
+ my ($file)=$change->{file}=~/$config{wiki_file_regexp}/;
+ $file=IkiWiki::possibly_foolish_untaint($file);
+ if (! defined $file || ! length $file ||
+ IkiWiki::file_pruned($file, $config{srcdir})) {
+ error(gettext("bad file name %s"), $file);
+ }
+
+ my $type=pagetype($file);
+ my $page=pagename($file) if defined $type;
+
+ if ($change->{action} eq 'add') {
+ $newfiles{$file}=1;
+ }
+
+ if ($change->{action} eq 'change' ||
+ $change->{action} eq 'add') {
+ if (defined $page) {
+ if (IkiWiki->can("check_canedit")) {
+ IkiWiki::check_canedit($page, $cgi, $session);
+ next;
+ }
+ }
+ else {
+ if (IkiWiki::Plugin::attachment->can("check_canattach")) {
+ IkiWiki::Plugin::attachment::check_canattach($session, $file, $change->{path});
+ next;
+ }
+ }
+ }
+ elsif ($change->{action} eq 'remove') {
+ # check_canremove tests to see if the file is present
+ # on disk. This will fail is a single commit adds a
+ # file and then removes it again. Avoid the problem
+ # by not testing the removal in such pairs of changes.
+ # (The add is still tested, just to make sure that
+ # no data is added to the repo that a web edit
+ # could add.)
+ next if $newfiles{$file};
+
+ if (IkiWiki::Plugin::remove->can("check_canremove")) {
+ IkiWiki::Plugin::remove::check_canremove(defined $page ? $page : $file, $cgi, $session);
+ next;
+ }
+ }
+ else {
+ error "unknown action ".$change->{action};
+ }
+
+ error sprintf(gettext("you are not allowed to change %s"), $file);
+ }
+
+ exit 0;
+}
+
+1
diff --git a/IkiWiki/Render.pm b/IkiWiki/Render.pm
index bc997ffb0..adae9f0d5 100644
--- a/IkiWiki/Render.pm
+++ b/IkiWiki/Render.pm
@@ -10,7 +10,7 @@ use Encode;
my %backlinks;
my $backlinks_calculated=0;
-sub calculate_backlinks () { #{{{
+sub calculate_backlinks () {
return if $backlinks_calculated;
%backlinks=();
foreach my $page (keys %links) {
@@ -22,9 +22,9 @@ sub calculate_backlinks () { #{{{
}
}
$backlinks_calculated=1;
-} #}}}
+}
-sub backlinks ($) { #{{{
+sub backlinks ($) {
my $page=shift;
calculate_backlinks();
@@ -45,9 +45,9 @@ sub backlinks ($) { #{{{
push @links, { url => $href, page => pagetitle($p_trimmed) };
}
return @links;
-} #}}}
+}
-sub genpage ($$) { #{{{
+sub genpage ($$) {
my $page=shift;
my $content=shift;
@@ -131,9 +131,9 @@ sub genpage ($$) { #{{{
});
return $content;
-} #}}}
+}
-sub scan ($) { #{{{
+sub scan ($) {
my $file=shift;
my $type=pagetype($file);
@@ -165,9 +165,9 @@ sub scan ($) { #{{{
else {
will_render($file, $file, 1);
}
-} #}}}
+}
-sub fast_file_copy (@) { #{{{
+sub fast_file_copy (@) {
my $srcfile=shift;
my $destfile=shift;
my $srcfd=shift;
@@ -191,7 +191,7 @@ sub fast_file_copy (@) { #{{{
}
}
-sub render ($) { #{{{
+sub render ($) {
my $file=shift;
my $type=pagetype($file);
@@ -233,9 +233,9 @@ sub render ($) { #{{{
fast_file_copy($srcfile, $file, $srcfd, @_);
});
}
-} #}}}
+}
-sub prune ($) { #{{{
+sub prune ($) {
my $file=shift;
unlink($file);
@@ -243,14 +243,14 @@ sub prune ($) { #{{{
while (rmdir($dir)) {
$dir=dirname($dir);
}
-} #}}}
+}
-sub refresh () { #{{{
+sub refresh () {
# security check, avoid following symlinks in the srcdir path by default
my $test=$config{srcdir};
while (length $test) {
if (-l $test && ! $config{allow_symlinks_before_srcdir}) {
- error(sprintf(gettext("symlink found in srcdir path (%s) -- set allow_symlinks_before_srcdir to allow this")), $test);
+ error(sprintf(gettext("symlink found in srcdir path (%s) -- set allow_symlinks_before_srcdir to allow this"), $test));
}
unless ($test=~s/\/+$//) {
$test=dirname($test);
@@ -507,9 +507,9 @@ sub refresh () { #{{{
if (%rendered) {
run_hooks(change => sub { shift->(keys %rendered) });
}
-} #}}}
+}
-sub commandline_render () { #{{{
+sub commandline_render () {
lockwiki();
loadindex();
unlockwiki();
@@ -528,9 +528,10 @@ sub commandline_render () { #{{{
$content=linkify($page, $page, $content);
$content=htmlize($page, $page, $type, $content);
$pagemtime{$page}=(stat($srcfile))[9];
+ $pagectime{$page}=$pagemtime{$page} if ! exists $pagectime{$page};
print genpage($page, $content);
exit 0;
-} #}}}
+}
1
diff --git a/IkiWiki/Setup.pm b/IkiWiki/Setup.pm
index 77afdd608..6ee112096 100644
--- a/IkiWiki/Setup.pm
+++ b/IkiWiki/Setup.pm
@@ -10,7 +10,7 @@ use IkiWiki;
use open qw{:utf8 :std};
use File::Spec;
-sub load ($) { # {{{
+sub load ($) {
my $setup=IkiWiki::possibly_foolish_untaint(shift);
$config{setupfile}=File::Spec->rel2abs($setup);
@@ -27,7 +27,7 @@ sub load ($) { # {{{
eval $code;
error("$setup: ".$@) if $@;
-} #}}}
+}
sub merge ($) {
# Merge setup into existing config and untaint.
@@ -71,9 +71,9 @@ sub merge ($) {
wrappermode => (defined $config{cgi_wrappermode} ? $config{cgi_wrappermode} : "06755"),
};
}
-} #}}}
+}
-sub getsetup () { #{{{
+sub getsetup () {
# Gets all available setup data from all plugins. Returns an
# ordered list of [plugin, setup] pairs.
my @ret;
@@ -105,9 +105,9 @@ sub getsetup () { #{{{
$config{syslog}=$syslog;
return @ret;
-} #}}}
+}
-sub dump ($) { #{{{
+sub dump ($) {
my $file=IkiWiki::possibly_foolish_untaint(shift);
require IkiWiki::Setup::Standard;
diff --git a/IkiWiki/Setup/Automator.pm b/IkiWiki/Setup/Automator.pm
index 88e9f3d24..7d9eca3af 100644
--- a/IkiWiki/Setup/Automator.pm
+++ b/IkiWiki/Setup/Automator.pm
@@ -9,21 +9,22 @@ use IkiWiki;
use IkiWiki::UserInfo;
use Term::ReadLine;
use File::Path;
+use Encode;
-sub ask ($$) { #{{{
+sub ask ($$) {
my ($question, $default)=@_;
my $r=Term::ReadLine->new("ikiwiki");
- $r->readline($question." ", $default);
-} #}}}
+ $r->readline(encode_utf8($question)." ", $default);
+}
-sub prettydir ($) { #{{{
+sub prettydir ($) {
my $dir=shift;
$dir=~s/^\Q$ENV{HOME}\E\//~\//;
return $dir;
-} #}}}
+}
-sub import (@) { #{{{
+sub import (@) {
my $this=shift;
IkiWiki::Setup::merge({@_});
@@ -73,8 +74,18 @@ sub import (@) { #{{{
print "\n\nSetting up $config{wikiname} ...\n";
- # Set up the repository.
+ # Set up the srcdir.
mkpath($config{srcdir}) || die "mkdir $config{srcdir}: $!";
+ # Copy in example wiki.
+ if (exists $config{example}) {
+ # cp -R is POSIX
+ # Another reason not to use -a is so that pages such as blog
+ # posts will not have old creation dates on this new wiki.
+ system("cp -R $IkiWiki::installdir/share/ikiwiki/examples/$config{example}/* $config{srcdir}");
+ delete $config{example};
+ }
+
+ # Set up the repository.
delete $config{repository} if ! $config{rcs} || $config{rcs}=~/bzr|mercurial/;
if ($config{rcs}) {
my @params=($config{rcs}, $config{srcdir});
@@ -99,11 +110,20 @@ sub import (@) { #{{{
next if $admin=~/^http\?:\/\//; # openid
# Prompt for password w/o echo.
+ my ($password, $password2);
system('stty -echo 2>/dev/null');
local $|=1;
print "\n\nCreating wiki admin $admin ...\n";
- print "Choose a password: ";
- chomp(my $password=<STDIN>);
+ for (;;) {
+ print "Choose a password: ";
+ chomp($password=<STDIN>);
+ print "Confirm password: ";
+ chomp($password2=<STDIN>);
+
+ last if $password2 eq $password;
+
+ print "Password mismatch.\n\n";
+ }
print "\n\n\n";
system('stty sane 2>/dev/null');
@@ -142,6 +162,6 @@ sub import (@) { #{{{
print "To modify settings, edit ".prettydir($config{dumpsetup})." and then run:\n";
print " ikiwiki -setup ".prettydir($config{dumpsetup})."\n";
exit 0;
-} #}}}
+}
1
diff --git a/IkiWiki/Setup/Standard.pm b/IkiWiki/Setup/Standard.pm
index 92f887f0c..951bcfc56 100644
--- a/IkiWiki/Setup/Standard.pm
+++ b/IkiWiki/Setup/Standard.pm
@@ -9,11 +9,11 @@ use warnings;
use strict;
use IkiWiki;
-sub import { #{{{
+sub import {
IkiWiki::Setup::merge($_[1]);
-} #}}}
+}
-sub dumpline ($$$$) { #{{{
+sub dumpline ($$$$) {
my $key=shift;
my $value=shift;
my $type=shift;
@@ -26,6 +26,8 @@ sub dumpline ($$$$) { #{{{
local $Data::Dumper::Pad="\t";
local $Data::Dumper::Sortkeys=1;
local $Data::Dumper::Quotekeys=0;
+ # only the perl version preserves utf-8 in output
+ local $Data::Dumper::Useperl=1;
my $dumpedvalue;
if (($type eq 'boolean' || $type eq 'integer') && $value=~/^[0-9]+$/) {
@@ -53,9 +55,9 @@ sub dumpline ($$$$) { #{{{
}
return "\t$prefix$key => $dumpedvalue,";
-} #}}}
+}
-sub dumpvalues ($@) { #{{{
+sub dumpvalues ($@) {
my $setup=shift;
my @ret;
while (@_) {
@@ -78,9 +80,9 @@ sub dumpvalues ($@) { #{{{
}
}
return @ret;
-} #}}}
+}
-sub gendump ($) { #{{{
+sub gendump ($) {
my $description=shift;
my %setup=(%config);
my @ret;
@@ -110,6 +112,6 @@ sub gendump ($) { #{{{
push @ret, "}";
return @ret;
-} #}}}
+}
1
diff --git a/IkiWiki/UserInfo.pm b/IkiWiki/UserInfo.pm
index dcf99da09..0bf100a95 100644
--- a/IkiWiki/UserInfo.pm
+++ b/IkiWiki/UserInfo.pm
@@ -7,12 +7,12 @@ use strict;
use Storable;
use IkiWiki;
-sub userinfo_retrieve () { #{{{
+sub userinfo_retrieve () {
my $userinfo=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
return $userinfo;
-} #}}}
+}
-sub userinfo_store ($) { #{{{
+sub userinfo_store ($) {
my $userinfo=shift;
my $newfile="$config{wikistatedir}/userdb.new";
@@ -26,9 +26,9 @@ sub userinfo_store ($) { #{{{
}
}
return $ret;
-} #}}}
+}
-sub userinfo_get ($$) { #{{{
+sub userinfo_get ($$) {
my $user=shift;
my $field=shift;
@@ -39,9 +39,9 @@ sub userinfo_get ($$) { #{{{
return "";
}
return $userinfo->{$user}->{$field};
-} #}}}
+}
-sub userinfo_set ($$$) { #{{{
+sub userinfo_set ($$$) {
my $user=shift;
my $field=shift;
my $value=shift;
@@ -54,9 +54,9 @@ sub userinfo_set ($$$) { #{{{
$userinfo->{$user}->{$field}=$value;
return userinfo_store($userinfo);
-} #}}}
+}
-sub userinfo_setall ($$) { #{{{
+sub userinfo_setall ($$) {
my $user=shift;
my $info=shift;
@@ -66,32 +66,12 @@ sub userinfo_setall ($$) { #{{{
}
$userinfo->{$user}=$info;
return userinfo_store($userinfo);
-} #}}}
+}
-sub is_admin ($) { #{{{
+sub is_admin ($) {
my $user_name=shift;
return grep { $_ eq $user_name } @{$config{adminuser}};
-} #}}}
-
-# XXX deprecated, should be removed eventually
-sub get_banned_users () { #{{{
- my @ret;
- my $userinfo=userinfo_retrieve();
- foreach my $user (keys %{$userinfo}) {
- push @ret, $user if $userinfo->{$user}->{banned};
- }
- return @ret;
-} #}}}
-
-# XXX deprecated, should be removed eventually
-sub set_banned_users (@) { #{{{
- my %banned=map { $_ => 1 } @_;
- my $userinfo=userinfo_retrieve();
- foreach my $user (keys %{$userinfo}) {
- $userinfo->{$user}->{banned} = $banned{$user};
- }
- return userinfo_store($userinfo);
-} #}}}
+}
1
diff --git a/IkiWiki/Wrapper.pm b/IkiWiki/Wrapper.pm
index 187314d16..6555fe625 100644
--- a/IkiWiki/Wrapper.pm
+++ b/IkiWiki/Wrapper.pm
@@ -8,7 +8,7 @@ use File::Spec;
use Data::Dumper;
use IkiWiki;
-sub gen_wrapper () { #{{{
+sub gen_wrapper () {
$config{srcdir}=File::Spec->rel2abs($config{srcdir});
$config{destdir}=File::Spec->rel2abs($config{destdir});
my $this=File::Spec->rel2abs($0);
@@ -28,34 +28,84 @@ sub gen_wrapper () { #{{{
my @envsave;
push @envsave, qw{REMOTE_ADDR QUERY_STRING REQUEST_METHOD REQUEST_URI
CONTENT_TYPE CONTENT_LENGTH GATEWAY_INTERFACE
- HTTP_COOKIE REMOTE_USER HTTPS} if $config{cgi};
+ HTTP_COOKIE REMOTE_USER HTTPS REDIRECT_STATUS
+ REDIRECT_URL} if $config{cgi};
my $envsave="";
foreach my $var (@envsave) {
- $envsave.=<<"EOF"
+ $envsave.=<<"EOF";
if ((s=getenv("$var")))
addenv("$var", s);
EOF
}
-
+
+ my $test_receive="";
+ if ($config{test_receive}) {
+ require IkiWiki::Receive;
+ $test_receive=IkiWiki::Receive::gen_wrapper();
+ }
+
+ my $check_commit_hook="";
+ my $pre_exec="";
+ if ($config{post_commit}) {
+ # Optimise checking !commit_hook_enabled() ,
+ # so that ikiwiki does not have to be started if the
+ # hook is disabled.
+ #
+ # Note that perl's flock may be implemented using fcntl
+ # or lockf on some systems. If so, and if there is no
+ # interop between the locking systems, the true C flock will
+ # always succeed, and this optimisation won't work.
+ # The perl code will later correctly check the lock,
+ # so the right thing will still happen, though without
+ # the benefit of this optimisation.
+ $check_commit_hook=<<"EOF";
+ {
+ int fd=open("$config{wikistatedir}/commitlock", O_CREAT | O_RDWR, 0666);
+ if (fd != -1) {
+ if (flock(fd, LOCK_SH | LOCK_NB) != 0)
+ exit(0);
+ close(fd);
+ }
+ }
+EOF
+ }
+ elsif ($config{cgi}) {
+ # Avoid more than one ikiwiki cgi running at a time by
+ # taking a cgi lock. Since ikiwiki uses several MB of
+ # memory, a pile up of processes could cause thrashing
+ # otherwise. The fd of the lock is stored in
+ # IKIWIKI_CGILOCK_FD so unlockwiki can close it.
+ $pre_exec=<<"EOF";
+ {
+ int fd=open("$config{wikistatedir}/cgilock", O_CREAT | O_RDWR, 0666);
+ if (fd != -1 && flock(fd, LOCK_EX) == 0) {
+ char *fd_s;
+ asprintf(&fd_s, "%i", fd);
+ setenv("IKIWIKI_CGILOCK_FD", fd_s, 1);
+ }
+ }
+EOF
+ }
+
$Data::Dumper::Indent=0; # no newlines
my $configstring=Data::Dumper->Dump([\%config], ['*config']);
$configstring=~s/\\/\\\\/g;
$configstring=~s/"/\\"/g;
$configstring=~s/\n/\\n/g;
- #translators: The first parameter is a filename, and the second is
- #translators: a (probably not translated) error message.
- open(OUT, ">$wrapper.c") || error(sprintf(gettext("failed to write %s: %s"), "$wrapper.c", $!));;
- print OUT <<"EOF";
+ writefile(basename("$wrapper.c"), dirname($wrapper), <<"EOF");
/* A wrapper for ikiwiki, can be safely made suid. */
#include <stdio.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/file.h>
extern char **environ;
-char *newenviron[$#envsave+5];
+char *newenviron[$#envsave+6];
int i=0;
addenv(char *var, char *val) {
@@ -67,8 +117,10 @@ addenv(char *var, char *val) {
}
int main (int argc, char **argv) {
- /* Sanitize environment. */
char *s;
+
+$check_commit_hook
+$test_receive
$envsave
newenviron[i++]="HOME=$ENV{HOME}";
newenviron[i++]="WRAPPED_OPTIONS=$configstring";
@@ -86,6 +138,7 @@ $envsave
exit(1);
}
+$pre_exec
execl("$this", "$this", NULL);
perror("exec $this");
exit(1);
@@ -118,6 +171,6 @@ EOF
#translators: The parameter is a filename.
printf(gettext("successfully generated %s"), $wrapper);
print "\n";
-} #}}}
+}
1