use Encode;
use HTML::Entities;
use URI::Escape q{uri_escape_utf8};
-use POSIX;
+use POSIX ();
use Storable;
use open qw{:utf8 :std};
our @EXPORT = qw(hook debug error template htmlpage deptype
add_depends pagespec_match pagespec_match_list bestlink
htmllink readfile writefile pagetype srcfile pagename
- displaytime will_render gettext urlto targetpage
+ displaytime will_render gettext ngettext urlto targetpage
add_underlay pagetitle titlepage linkpage newpagefile
inject add_link
%config %links %pagestate %wikistate %renderedfiles
# Optimisation.
use Memoize;
memoize("abs2rel");
+memoize("cmpspec_translate");
memoize("pagespec_translate");
memoize("template_file");
safe => 0, # paranoia
rebuild => 0,
},
+ include => {
+ type => "string",
+ default => undef,
+ example => '^\.htaccess$',
+ description => "regexp of normally excluded files to include",
+ advanced => 1,
+ safe => 0, # regexp
+ rebuild => 1,
+ },
exclude => {
type => "string",
default => undef,
- example => '\.wav$',
- description => "regexp of source files to ignore",
+ example => '^(*\.private|Makefile)$',
+ description => "regexp of files that should be skipped",
advanced => 1,
safe => 0, # regexp
rebuild => 1,
safe => 0,
rebuild => 0,
},
+ clean => {
+ type => "internal",
+ default => 0,
+ description => "running in clean mode",
+ safe => 0,
+ rebuild => 0,
+ },
refresh => {
type => "internal",
default => 0,
safe => 0,
rebuild => 0,
},
+ setuptype => {
+ type => "internal",
+ default => "Standard",
+ description => "perl class to use to dump setup file",
+ safe => 0,
+ rebuild => 0,
+ },
allow_symlinks_before_srcdir => {
type => "boolean",
default => 0,
sub cgiurl (@) {
my %params=@_;
- return $config{cgiurl}."?".
+ my $cgiurl=$config{cgiurl};
+ if (exists $params{cgiurl}) {
+ $cgiurl=$params{cgiurl};
+ delete $params{cgiurl};
+ }
+ return $cgiurl."?".
join("&", map $_."=".uri_escape_utf8($params{$_}), keys %params);
}
return "<a href=\"$bestlink\"@attrs>$linktext</a>";
}
+sub userpage ($) {
+ my $user=shift;
+ return length $config{userdir} ? "$config{userdir}/$user" : $user;
+}
+
sub openiduser ($) {
my $user=shift;
my $display;
if (Net::OpenID::VerifiedIdentity->can("DisplayOfURL")) {
- # this works in at least 2.x
$display = Net::OpenID::VerifiedIdentity::DisplayOfURL($user);
}
else {
- # this only works in 1.x
+ # backcompat with old version
my $oid=Net::OpenID::VerifiedIdentity->new(identity => $user);
$display=$oid->display;
}
# 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'};
return;
}
-sub userlink ($) {
- my $user=shift;
-
- my $oiduser=eval { openiduser($user) };
- if (defined $oiduser) {
- return "<a href=\"$user\">$oiduser</a>";
- }
- else {
- eval q{use CGI 'escapeHTML'};
- error($@) if $@;
-
- return htmllink("", "", escapeHTML(
- length $config{userdir} ? $config{userdir}."/".$user : $user
- ), noimageinline => 1);
- }
-}
-
sub htmlize ($$$$) {
my $page=shift;
my $destpage=shift;
(?:
"""(.*?)""" # 2: triple-quoted value
|
- "([^"]+)" # 3: single-quoted value
+ "([^"]*?)" # 3: single-quoted value
|
(\S+) # 4: unquoted value
)
(?:
""".*?""" # triple-quoted value
|
- "[^"]+" # single-quoted value
+ "[^"]*?" # single-quoted value
|
[^"\s\]]+ # unquoted value
)
(?:
""".*?""" # triple-quoted value
|
- "[^"]+" # single-quoted value
+ "[^"]*?" # single-quoted value
|
[^"\s\]]+ # unquoted value
)
my %old=map { $_ => 1 }
split("\n", readfile(srcfile($pagesources{$params{page}})));
foreach my $line (split("\n", $params{content})) {
- push @diff, $line if ! exists $old{$_};
+ push @diff, $line if ! exists $old{$line};
}
$params{diff}=join("\n", @diff);
}
return $deptype;
}
+my $file_prune_regexp;
sub file_pruned ($;$) {
my $file=shift;
if (@_) {
$file =~ s#^\Q$base\E/+##;
}
- my $regexp='('.join('|', @{$config{wiki_file_prune_regexps}}).')';
- return $file =~ m/$regexp/;
+ if (defined $config{include} && length $config{include}) {
+ return 0 if $file =~ m/$config{include}/;
+ }
+
+ if (! defined $file_prune_regexp) {
+ $file_prune_regexp='('.join('|', @{$config{wiki_file_prune_regexps}}).')';
+ $file_prune_regexp=qr/$file_prune_regexp/;
+ }
+ return $file =~ m/$file_prune_regexp/;
}
sub define_gettext () {
# If translation is needed, redefine the gettext function to do it.
# Otherwise, it becomes a quick no-op.
- no warnings 'redefine';
+ my $gettext_obj;
+ my $getobj;
if ((exists $ENV{LANG} && length $ENV{LANG}) ||
(exists $ENV{LC_ALL} && length $ENV{LC_ALL}) ||
(exists $ENV{LC_MESSAGES} && length $ENV{LC_MESSAGES})) {
- *gettext=sub {
- my $gettext_obj=eval q{
+ $getobj=sub {
+ $gettext_obj=eval q{
use Locale::gettext q{textdomain};
Locale::gettext->domain('ikiwiki')
};
-
- if ($gettext_obj) {
- $gettext_obj->get(shift);
- }
- else {
- return shift;
- }
};
}
- else {
- *gettext=sub { return shift };
- }
+
+ no warnings 'redefine';
+ *gettext=sub {
+ $getobj->() if $getobj;
+ if ($gettext_obj) {
+ $gettext_obj->get(shift);
+ }
+ else {
+ return shift;
+ }
+ };
+ *ngettext=sub {
+ $getobj->() if $getobj;
+ if ($gettext_obj) {
+ $gettext_obj->nget(@_);
+ }
+ else {
+ return ($_[2] == 1 ? $_[0] : $_[1])
+ }
+ };
}
sub gettext {
gettext(@_);
}
+sub ngettext {
+ define_gettext();
+ ngettext(@_);
+}
+
sub yesno ($) {
my $val=shift;
unless grep { $_ eq $link } @{$links{$page}};
}
+sub cmpspec_translate ($) {
+ my $spec = shift;
+
+ my $code = "";
+ my @data;
+ while ($spec =~ m{
+ \s*
+ (-?) # group 1: perhaps negated
+ \s*
+ ( # group 2: a word
+ \w+\([^\)]*\) # command(params)
+ |
+ [^\s]+ # or anything else
+ )
+ \s*
+ }gx) {
+ my $negated = $1;
+ my $word = $2;
+ my $params = undef;
+
+ if ($word =~ m/^(\w+)\((.*)\)$/) {
+ # command with parameters
+ $params = $2;
+ $word = $1;
+ }
+ elsif ($word !~ m/^\w+$/) {
+ error(sprintf(gettext("invalid sort type %s"), $word));
+ }
+
+ if (length $code) {
+ $code .= " || ";
+ }
+
+ if ($negated) {
+ $code .= "-";
+ }
+
+ if (exists $IkiWiki::PageSpec::{"cmp_$word"}) {
+ if (defined $params) {
+ push @data, $params;
+ $code .= "IkiWiki::PageSpec::cmp_$word(\@_, \$data[$#data])";
+ }
+ else {
+ $code .= "IkiWiki::PageSpec::cmp_$word(\@_, undef)";
+ }
+ }
+ else {
+ error(sprintf(gettext("unknown sort type %s"), $word));
+ }
+ }
+
+ if (! length $code) {
+ # undefined sorting method... sort arbitrarily
+ return sub { 0 };
+ }
+
+ no warnings;
+ return eval 'sub { '.$code.' }';
+}
+
sub pagespec_translate ($) {
my $spec=shift;
}
if (defined $params{sort}) {
- my $f;
- if ($params{sort} eq 'title') {
- $f=sub { pagetitle(basename($a)) cmp pagetitle(basename($b)) };
- }
- elsif ($params{sort} eq 'title_natural') {
- eval q{use Sort::Naturally};
- if ($@) {
- error(gettext("Sort::Naturally needed for title_natural sort"));
- }
- $f=sub { Sort::Naturally::ncmp(pagetitle(basename($a)), pagetitle(basename($b))) };
- }
- elsif ($params{sort} eq 'mtime') {
- $f=sub { $pagemtime{$b} <=> $pagemtime{$a} };
- }
- elsif ($params{sort} eq 'age') {
- $f=sub { $pagectime{$b} <=> $pagectime{$a} };
- }
- else {
- error sprintf(gettext("unknown sort type %s"), $params{sort});
- }
- @candidates = sort { &$f } @candidates;
+ my $f = cmpspec_translate($params{sort});
+
+ @candidates = sort { $f->($a, $b) } @candidates;
}
@candidates=reverse(@candidates) if $params{reverse};
my $user=shift;
my %params=@_;
+ my $regexp=IkiWiki::glob2re($user);
+
if (! exists $params{user}) {
return IkiWiki::ErrorReason->new("no user specified");
}
- if (defined $params{user} && lc $params{user} eq lc $user) {
+ if (defined $params{user} && $params{user}=~/^$regexp$/i) {
return IkiWiki::SuccessReason->new("user is $user");
}
elsif (! defined $params{user}) {
}
}
+sub cmp_title {
+ IkiWiki::pagetitle(IkiWiki::basename($_[0]))
+ cmp
+ IkiWiki::pagetitle(IkiWiki::basename($_[1]))
+}
+
+sub cmp_mtime { $IkiWiki::pagemtime{$_[1]} <=> $IkiWiki::pagemtime{$_[0]} }
+sub cmp_age { $IkiWiki::pagectime{$_[1]} <=> $IkiWiki::pagectime{$_[0]} }
+
1