use Encode;
use HTML::Entities;
use URI::Escape q{uri_escape_utf8};
+use POSIX;
use open qw{:utf8 :std};
use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase
our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match
bestlink htmllink readfile writefile pagetype srcfile pagename
displaytime will_render gettext urlto targetpage
- %config %links %renderedfiles %pagesources);
-our $VERSION = 1.02; # plugin interface version, next is ikiwiki version
-our $version="1.45";my $installdir="/usr";
+ %config %links %renderedfiles %pagesources %destsources);
+our $VERSION = 2.00; # plugin interface version, next is ikiwiki version
+our $version='unknown'; # VERSION_AUTOREPLACE done by Makefile, DNE
+my $installdir=''; # INSTALLDIR_AUTOREPLACE done by Makefile, DNE
+
# Optimisation.
use Memoize;
memoize("abs2rel");
sub defaultconfig () { #{{{
wiki_file_prune_regexps => [qr/\.\./, qr/^\./, qr/\/\./,
qr/\.x?html?$/, qr/\.ikiwiki-new$/,
- qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//],
+ qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//,
+ qr/\.dpkg-tmp$/],
wiki_link_regexp => qr/\[\[(?:([^\]\|]+)\|)?([^\s\]#]+)(?:#([^\s\]]+))?\]\]/,
wiki_file_regexp => qr/(^[-[:alnum:]_.:\/+]+$)/,
web_commit_regexp => qr/^web commit (by (.*?(?=: |$))|from (\d+\.\d+\.\d+\.\d+)):?(.*)/,
setup => undef,
adminuser => undef,
adminemail => undef,
- plugin => [qw{mdwn inline htmlscrubber passwordauth signinedit
+ plugin => [qw{mdwn inline htmlscrubber passwordauth openid signinedit
lockedit conditional}],
timeformat => '%c',
locale => undef,
sslcookie => 0,
httpauth => 0,
userdir => "",
- usedirs => 0,
+ usedirs => 1,
numbacklinks => 10,
+ account_creation_password => "",
} #}}}
sub checkconfig () { #{{{
delete $ENV{LC_ALL};
}
if (defined $config{locale}) {
- eval q{use POSIX};
- error($@) if $@;
if (POSIX::setlocale(&POSIX::LC_ALL, $config{locale})) {
$ENV{LANG}=$config{locale};
$gettext_obj=undef;
$log_open=1;
}
eval {
- Sys::Syslog::syslog($type, "%s", join(" ", @_));
+ Sys::Syslog::syslog($type, "[$config{wikiname}] %s", join(" ", @_));
};
}
elsif (! $config{cgi}) {
return "$config{srcdir}/$file" if -e "$config{srcdir}/$file";
return "$config{underlaydir}/$file" if -e "$config{underlaydir}/$file";
- error("internal error: $file cannot be found");
+ error("internal error: $file cannot be found in $config{srcdir} or $config{underlaydir}");
} #}}}
sub readfile ($;$$) { #{{{
}
} while $cwd=~s!/?[^/]+$!!;
- if (length $config{userdir} && exists $links{"$config{userdir}/".lc($link)}) {
- return "$config{userdir}/".lc($link);
+ if (length $config{userdir}) {
+ my $l = "$config{userdir}/".lc($link);
+ if (exists $links{$l}) {
+ return $l;
+ }
+ elsif (exists $pagecase{lc $l}) {
+ return $pagecase{lc $l};
+ }
}
#print STDERR "warning: page $page, broken link: $link\n";
sub displaytime ($) { #{{{
my $time=shift;
- eval q{use POSIX};
- error($@) if $@;
# strftime doesn't know about encodings, so make sure
# its output is properly treated as utf8
return decode_utf8(POSIX::strftime(
return $content;
} #}}}
-sub filter ($$) { #{{{
+sub filter ($$$) { #{{{
my $page=shift;
+ my $destpage=shift;
my $content=shift;
run_hooks(filter => sub {
- $content=shift->(page => $page, content => $content);
+ $content=shift->(page => $page, destpage => $destpage,
+ content => $content);
});
return $content;
return "<a href=\"$config{url}\">$config{wikiname}</a>";
} #}}}
-sub lockwiki () { #{{{
+sub lockwiki (;$) { #{{{
+ my $wait=@_ ? shift : 1;
# Take an exclusive lock on the wiki to prevent multiple concurrent
# run issues. The lock will be dropped on program exit.
if (! -d $config{wikistatedir}) {
open(WIKILOCK, ">$config{wikistatedir}/lockfile") ||
error ("cannot write to $config{wikistatedir}/lockfile: $!");
if (! flock(WIKILOCK, 2 | 4)) { # LOCK_EX | LOCK_NB
- debug("wiki seems to be locked, waiting for lock");
- my $wait=600; # arbitrary, but don't hang forever to
- # prevent process pileup
- for (1..$wait) {
- return if flock(WIKILOCK, 2 | 4);
- sleep 1;
+ if ($wait) {
+ debug("wiki seems to be locked, waiting for lock");
+ my $wait=600; # arbitrary, but don't hang forever to
+ # prevent process pileup
+ for (1..$wait) {
+ return if flock(WIKILOCK, 2 | 4);
+ sleep 1;
+ }
+ error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)");
+ }
+ else {
+ return 0;
}
- error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)");
}
+ return 1;
} #}}}
sub unlockwiki () { #{{{
}
elsif ($word =~ /^(\w+)\((.*)\)$/) {
if (exists $IkiWiki::PageSpec::{"match_$1"}) {
- $code.="IkiWiki::PageSpec::match_$1(\$page, ".safequote($2).", \$from)";
+ $code.="IkiWiki::PageSpec::match_$1(\$page, ".safequote($2).", \@params)";
}
else {
$code.=" 0";
}
}
else {
- $code.=" IkiWiki::PageSpec::match_glob(\$page, ".safequote($word).", \$from)";
+ $code.=" IkiWiki::PageSpec::match_glob(\$page, ".safequote($word).", \@params)";
}
}
return $code;
} #}}}
-sub pagespec_match ($$;$) { #{{{
+sub pagespec_match ($$;@) { #{{{
my $page=shift;
my $spec=shift;
- my $from=shift;
+ my @params=@_;
+
+ # Backwards compatability with old calling convention.
+ if (@params == 1) {
+ unshift @params, "location";
+ }
- return eval pagespec_translate($spec);
+ my $ret=eval pagespec_translate($spec);
+ return IkiWiki::FailReason->new("syntax error") if $@;
+ return $ret;
+} #}}}
+
+package IkiWiki::FailReason;
+
+use overload ( #{{{
+ '""' => sub { ${$_[0]} },
+ '0+' => sub { 0 },
+ '!' => sub { bless $_[0], 'IkiWiki::SuccessReason'},
+ fallback => 1,
+); #}}}
+
+sub new { #{{{
+ bless \$_[1], $_[0];
} #}}}
+package IkiWiki::SuccessReason;
+
+use overload ( #{{{
+ '""' => sub { ${$_[0]} },
+ '0+' => sub { 1 },
+ '!' => sub { bless $_[0], 'IkiWiki::FailReason'},
+ fallback => 1,
+); #}}}
+
+sub new { #{{{
+ bless \$_[1], $_[0];
+}; #}}}
+
package IkiWiki::PageSpec;
-sub match_glob ($$$) { #{{{
+sub match_glob ($$;@) { #{{{
my $page=shift;
my $glob=shift;
- my $from=shift;
- if (! defined $from){
- $from = "";
- }
-
+ my %params=@_;
+
+ my $from=exists $params{location} ? $params{location} : "";
+
# relative matching
if ($glob =~ m!^\./!) {
$from=~s!/?[^/]+$!!;
$glob=~s/\\\*/.*/g;
$glob=~s/\\\?/./g;
- return $page=~/^$glob$/i;
+ if ($page=~/^$glob$/i) {
+ return IkiWiki::SuccessReason->new("$glob matches $page");
+ }
+ else {
+ return IkiWiki::FailReason->new("$glob does not match $page");
+ }
} #}}}
-sub match_link ($$$) { #{{{
+sub match_link ($$;@) { #{{{
my $page=shift;
my $link=lc(shift);
- my $from=shift;
- if (! defined $from){
- $from = "";
- }
+ my %params=@_;
+
+ my $from=exists $params{location} ? $params{location} : "";
# relative matching
if ($link =~ m!^\.! && defined $from) {
}
my $links = $IkiWiki::links{$page} or return undef;
- return 0 unless @$links;
+ return IkiWiki::FailReason->new("$page has no links") unless @$links;
my $bestlink = IkiWiki::bestlink($from, $link);
- return 0 unless length $bestlink;
+ return IkiWiki::FailReason->new("no such link") unless length $bestlink;
foreach my $p (@$links) {
- return 1 if $bestlink eq IkiWiki::bestlink($page, $p);
+ return IkiWiki::SuccessReason->new("$page links to $link")
+ if $bestlink eq IkiWiki::bestlink($page, $p);
}
- return 0;
+ return IkiWiki::FailReason->new("$page does not link to $link");
} #}}}
-sub match_backlink ($$$) { #{{{
- match_link($_[1], $_[0], $_[3]);
+sub match_backlink ($$;@) { #{{{
+ match_link($_[1], $_[0], @_);
} #}}}
-sub match_created_before ($$$) { #{{{
+sub match_created_before ($$;@) { #{{{
my $page=shift;
my $testpage=shift;
if (exists $IkiWiki::pagectime{$testpage}) {
- return $IkiWiki::pagectime{$page} < $IkiWiki::pagectime{$testpage};
+ if ($IkiWiki::pagectime{$page} < $IkiWiki::pagectime{$testpage}) {
+ IkiWiki::SuccessReason->new("$page created before $testpage");
+ }
+ else {
+ IkiWiki::FailReason->new("$page not created before $testpage");
+ }
}
else {
- return 0;
+ return IkiWiki::FailReason->new("$testpage has no ctime");
}
} #}}}
-sub match_created_after ($$$) { #{{{
+sub match_created_after ($$;@) { #{{{
my $page=shift;
my $testpage=shift;
if (exists $IkiWiki::pagectime{$testpage}) {
- return $IkiWiki::pagectime{$page} > $IkiWiki::pagectime{$testpage};
+ if ($IkiWiki::pagectime{$page} > $IkiWiki::pagectime{$testpage}) {
+ IkiWiki::SuccessReason->new("$page created after $testpage");
+ }
+ else {
+ IkiWiki::FailReason->new("$page not created after $testpage");
+ }
}
else {
- return 0;
+ return IkiWiki::FailReason->new("$testpage has no ctime");
+ }
+} #}}}
+
+sub match_creation_day ($$;@) { #{{{
+ if ((gmtime($IkiWiki::pagectime{shift()}))[3] == shift) {
+ return IkiWiki::SuccessReason->new("creation_day matched");
+ }
+ else {
+ return IkiWiki::FailReason->new("creation_day did not match");
}
} #}}}
-sub match_creation_day ($$$) { #{{{
- return ((gmtime($IkiWiki::pagectime{shift()}))[3] == shift);
+sub match_creation_month ($$;@) { #{{{
+ if ((gmtime($IkiWiki::pagectime{shift()}))[4] + 1 == shift) {
+ return IkiWiki::SuccessReason->new("creation_month matched");
+ }
+ else {
+ return IkiWiki::FailReason->new("creation_month did not match");
+ }
} #}}}
-sub match_creation_month ($$$) { #{{{
- return ((gmtime($IkiWiki::pagectime{shift()}))[4] + 1 == shift);
+sub match_creation_year ($$;@) { #{{{
+ if ((gmtime($IkiWiki::pagectime{shift()}))[5] + 1900 == shift) {
+ return IkiWiki::SuccessReason->new("creation_year matched");
+ }
+ else {
+ return IkiWiki::FailReason->new("creation_year did not match");
+ }
} #}}}
-sub match_creation_year ($$$) { #{{{
- return ((gmtime($IkiWiki::pagectime{shift()}))[5] + 1900 == shift);
+sub match_user ($$;@) { #{{{
+ shift;
+ my $user=shift;
+ my %params=@_;
+
+ return IkiWiki::FailReason->new("cannot match user") unless exists $params{user};
+ if ($user eq $params{user}) {
+ return IkiWiki::SuccessReason->new("user is $user")
+ }
+ else {
+ return IkiWiki::FailReason->new("user is not $user");
+ }
} #}}}
1