X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/200c599dcaeb4dde77564b84022b67bf71cd6bfa..997e0071cf0bacf03ffde2196b63ac7b2befa53e:/IkiWiki.pm diff --git a/IkiWiki.pm b/IkiWiki.pm index 989f3bee7..b7080bb0b 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -14,15 +14,15 @@ use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase %pagestate %wikistate %renderedfiles %oldrenderedfiles %pagesources %delpagesources %destsources %depends %depends_simple @mass_depends %hooks %forcerebuild %loaded_plugins %typedlinks - %oldtypedlinks %autofiles}; + %oldtypedlinks %autofiles @underlayfiles $lastrev}; use Exporter q{import}; our @EXPORT = qw(hook debug error htmlpage template template_depends deptype add_depends pagespec_match pagespec_match_list bestlink htmllink readfile writefile pagetype srcfile pagename - displaytime will_render gettext ngettext urlto targetpage + displaytime strftime_utf8 will_render gettext ngettext urlto targetpage add_underlay pagetitle titlepage linkpage newpagefile - inject add_link add_autofile + inject add_link add_autofile useragent %config %links %pagestate %wikistate %renderedfiles %pagesources %destsources %typedlinks); our $VERSION = 3.00; # plugin interface version, next is ikiwiki version @@ -118,6 +118,29 @@ sub getsetup () { safe => 0, rebuild => 0, }, + cgi_overload_delay => { + type => "string", + default => '', + example => "10", + description => "number of seconds to delay CGI requests when overloaded", + safe => 1, + rebuild => 0, + }, + cgi_overload_message => { + type => "string", + default => '', + example => "Please wait", + description => "message to display when overloaded (may contain html)", + safe => 1, + rebuild => 0, + }, + only_committed_changes => { + type => "boolean", + default => 0, + description => "enable optimization of only refreshing committed changes?", + safe => 1, + rebuild => 0, + }, rcs => { type => "string", default => '', @@ -237,8 +260,8 @@ sub getsetup () { html5 => { type => "boolean", default => 0, - description => "generate HTML5? (experimental)", - advanced => 1, + description => "generate HTML5?", + advanced => 0, safe => 1, rebuild => 1, }, @@ -305,9 +328,9 @@ sub getsetup () { rebuild => 0, }, umask => { - type => "integer", - example => "022", - description => "force ikiwiki to use a particular umask", + type => "string", + example => "public", + description => "force ikiwiki to use a particular umask (keywords public, group or private, or a number)", advanced => 1, safe => 0, # paranoia rebuild => 0, @@ -336,6 +359,14 @@ sub getsetup () { safe => 0, # paranoia rebuild => 0, }, + timezone => { + type => "string", + default => "", + example => "US/Eastern", + description => "time zone name", + safe => 1, + rebuild => 1, + }, include => { type => "string", default => undef, @@ -477,7 +508,7 @@ sub getsetup () { }, setuptype => { type => "internal", - default => "Standard", + default => "Yaml", description => "perl class to use to dump setup file", safe => 0, rebuild => 0, @@ -489,6 +520,13 @@ sub getsetup () { safe => 0, rebuild => 0, }, + cookiejar => { + type => "string", + default => { file => "$ENV{HOME}/.ikiwiki/cookies" }, + description => "cookie control", + safe => 0, # hooks into perl module internals + rebuild => 0, + }, } sub defaultconfig () { @@ -497,7 +535,6 @@ sub defaultconfig () { foreach my $key (keys %s) { push @ret, $key, $s{$key}->{default}; } - use Data::Dumper; return @ret; } @@ -529,6 +566,12 @@ sub checkconfig () { $ENV{$val}=$config{ENV}{$val}; } } + if (defined $config{timezone} && length $config{timezone}) { + $ENV{TZ}=$config{timezone}; + } + else { + $config{timezone}=$ENV{TZ}; + } if ($config{w3mmode}) { eval q{use Cwd q{abs_path}}; @@ -544,7 +587,7 @@ sub checkconfig () { error(gettext("Must specify url to wiki with --url when using --cgi")); } - if (length $config{url}) { + if (defined $config{url} && length $config{url}) { eval q{use URI}; my $baseurl = URI->new($config{url}); @@ -566,12 +609,31 @@ sub checkconfig () { $local_url =~ s{//$}{/}; } + else { + $local_cgiurl = $config{cgiurl}; + } $config{wikistatedir}="$config{srcdir}/.ikiwiki" unless exists $config{wikistatedir} && defined $config{wikistatedir}; if (defined $config{umask}) { - umask(possibly_foolish_untaint($config{umask})); + my $u = possibly_foolish_untaint($config{umask}); + + if ($u =~ m/^\d+$/) { + umask($u); + } + elsif ($u eq 'private') { + umask(077); + } + elsif ($u eq 'group') { + umask(027); + } + elsif ($u eq 'public') { + umask(022); + } + else { + error(sprintf(gettext("unsupported umask setting %s"), $u)); + } } run_hooks(checkconfig => sub { shift->() }); @@ -807,17 +869,23 @@ sub srcfile ($;$) { return (srcfile_stat(@_))[0]; } -sub add_underlay ($) { +sub add_literal_underlay ($) { my $dir=shift; - if ($dir !~ /^\//) { - $dir="$config{underlaydirbase}/$dir"; - } - if (! grep { $_ eq $dir } @{$config{underlaydirs}}) { unshift @{$config{underlaydirs}}, $dir; } +} + +sub add_underlay ($) { + my $dir = shift; + + if ($dir !~ /^\//) { + $dir="$config{underlaydirbase}/$dir"; + } + add_literal_underlay($dir); + # why does it return 1? we just don't know return 1; } @@ -1004,7 +1072,7 @@ sub bestlink ($$) { sub isinlinableimage ($) { my $file=shift; - return $file =~ /\.(png|gif|jpg|jpeg)$/i; + return $file =~ /\.(png|gif|jpg|jpeg|svg)$/i; } sub pagetitle ($;$) { @@ -1039,7 +1107,8 @@ sub linkpage ($) { sub cgiurl (@) { my %params=@_; - my $cgiurl=$config{cgiurl}; + my $cgiurl=$local_cgiurl; + if (exists $params{cgiurl}) { $cgiurl=$params{cgiurl}; delete $params{cgiurl}; @@ -1053,10 +1122,15 @@ sub cgiurl (@) { join("&", map $_."=".uri_escape_utf8($params{$_}), keys %params); } +sub cgiurl_abs (@) { + eval q{use URI}; + URI->new_abs(cgiurl(@_), $config{cgiurl}); +} + sub baseurl (;$) { my $page=shift; - return "$config{url}/" if ! defined $page; + return $local_url if ! defined $page; $page=htmlpage($page); $page=~s/[^\/]+$//; @@ -1064,6 +1138,16 @@ sub baseurl (;$) { return $page; } +sub urlabs ($$) { + my $url=shift; + my $urlbase=shift; + + return $url unless defined $urlbase && length $urlbase; + + eval q{use URI}; + URI->new_abs($url, $urlbase)->as_string; +} + sub abs2rel ($$) { # Work around very innefficient behavior in File::Spec if abs2rel # is passed two relative paths. It's much faster if paths are @@ -1099,9 +1183,19 @@ sub formattime ($;$) { $format=$config{timeformat}; } + return strftime_utf8($format, localtime($time)); +} + +my $strftime_encoding; +sub strftime_utf8 { # strftime doesn't know about encodings, so make sure - # its output is properly treated as utf8 - return decode_utf8(POSIX::strftime($format, localtime($time))); + # its output is properly treated as utf8. + # Note that this does not handle utf-8 in the format string. + ($strftime_encoding) = POSIX::setlocale(&POSIX::LC_TIME) =~ m#\.([^@]+)# + unless defined $strftime_encoding; + $strftime_encoding + ? Encode::decode($strftime_encoding, POSIX::strftime(@_)) + : POSIX::strftime(@_); } sub date_3339 ($) { @@ -1130,13 +1224,13 @@ sub beautify_urlpath ($) { return $url; } -sub urlto ($$;$) { +sub urlto ($;$$) { my $to=shift; my $from=shift; my $absolute=shift; if (! length $to) { - return beautify_urlpath(baseurl($from)."index.$config{htmlext}"); + $to = 'index'; } if (! $destsources{$to}) { @@ -1147,6 +1241,12 @@ sub urlto ($$;$) { return $config{url}.beautify_urlpath("/".$to); } + if (! defined $from) { + my $u = $local_url || ''; + $u =~ s{/$}{}; + return $u.beautify_urlpath("/".$to); + } + my $link = abs2rel($to, dirname(htmlpage($from))); return beautify_urlpath($link); @@ -1198,7 +1298,7 @@ sub htmllink ($$$;@) { $cgilink = " "create", - page => lc($link), + page => $link, from => $lpage )."\" rel=\"nofollow\">?"; } @@ -1235,7 +1335,7 @@ sub userpage ($) { sub openiduser ($) { my $user=shift; - if ($user =~ m!^https?://! && + if (defined $user && $user =~ m!^https?://! && eval q{use Net::OpenID::VerifiedIdentity; 1} && !$@) { my $display; @@ -1347,16 +1447,21 @@ sub preprocess ($$$;$$) { # consider it significant. my @params; while ($params =~ m{ - (?:([-\w]+)=)? # 1: named parameter key? + (?:([-.\w]+)=)? # 1: named parameter key? (?: """(.*?)""" # 2: triple-quoted value | "([^"]*?)" # 3: single-quoted value | - (\S+) # 4: unquoted value + '''(.*?)''' # 4: triple-single-quote + | + <<([a-zA-Z]+)\n # 5: heredoc start + (.*?)\n\5 # 6: heredoc value + | + (\S+) # 7: unquoted value ) (?:\s+|$) # delimiter to next param - }sgx) { + }msgx) { my $key=$1; my $val; if (defined $2) { @@ -1371,6 +1476,12 @@ sub preprocess ($$$;$$) { elsif (defined $4) { $val=$4; } + elsif (defined $7) { + $val=$7; + } + elsif (defined $6) { + $val=$6; + } if (defined $key) { push @params, $key, $val; @@ -1433,12 +1544,17 @@ sub preprocess ($$$;$$) { ( # 4: the parameters.. \s+ # Must have space if parameters present (?: - (?:[-\w]+=)? # named parameter key? + (?:[-.\w]+=)? # named parameter key? (?: """.*?""" # triple-quoted value | "[^"]*?" # single-quoted value | + '''.*?''' # triple-single-quote + | + <<([a-zA-Z]+)\n # 5: heredoc start + (?:.*?)\n\5 # heredoc value + | [^"\s\]]+ # unquoted value ) \s* # whitespace or end @@ -1456,12 +1572,17 @@ sub preprocess ($$$;$$) { \s+ ( # 4: the parameters.. (?: - (?:[-\w]+=)? # named parameter key? + (?:[-.\w]+=)? # named parameter key? (?: """.*?""" # triple-quoted value | "[^"]*?" # single-quoted value | + '''.*?''' # triple-single-quote + | + <<([a-zA-Z]+)\n # 5: heredoc start + (?:.*?)\n\5 # heredoc value + | [^"\s\]]+ # unquoted value ) \s* # whitespace or end @@ -1667,7 +1788,8 @@ sub enable_commit_hook () { sub loadindex () { %oldrenderedfiles=%pagectime=(); - if (! $config{rebuild}) { + my $rebuild=$config{rebuild}; + if (! $rebuild) { %pagesources=%pagemtime=%oldlinks=%links=%depends= %destsources=%renderedfiles=%pagecase=%pagestate= %depends_simple=%typedlinks=%oldtypedlinks=(); @@ -1707,10 +1829,16 @@ sub loadindex () { foreach my $src (keys %$pages) { my $d=$pages->{$src}; - my $page=pagename($src); + my $page; + if (exists $d->{page} && ! $rebuild) { + $page=$d->{page}; + } + else { + $page=pagename($src); + } $pagectime{$page}=$d->{ctime}; $pagesources{$page}=$src; - if (! $config{rebuild}) { + if (! $rebuild) { $pagemtime{$page}=$d->{mtime}; $renderedfiles{$page}=$d->{dest}; if (exists $d->{links} && ref $d->{links}) { @@ -1760,6 +1888,8 @@ sub loadindex () { foreach my $page (keys %renderedfiles) { $destsources{$_}=$page foreach @{$renderedfiles{$page}}; } + $lastrev=$index->{lastrev}; + @underlayfiles=@{$index->{underlayfiles}} if ref $index->{underlayfiles}; return close($in); } @@ -1781,6 +1911,7 @@ sub saveindex () { my $src=$pagesources{$page}; $index{page}{$src}={ + page => $page, ctime => $pagectime{$page}, mtime => $pagemtime{$page}, dest => $renderedfiles{$page}, @@ -1800,11 +1931,7 @@ sub saveindex () { } if (exists $pagestate{$page}) { - foreach my $id (@plugins) { - foreach my $key (keys %{$pagestate{$page}{$id}}) { - $index{page}{$src}{state}{$id}{$key}=$pagestate{$page}{$id}{$key}; - } - } + $index{page}{$src}{state}=$pagestate{$page}; } } @@ -1816,6 +1943,9 @@ sub saveindex () { } } + $index{lastrev}=$lastrev; + $index{underlayfiles}=\@underlayfiles; + $index{version}="3"; my $ret=Storable::nstore_fd(\%index, $out); return if ! defined $ret || ! $ret; @@ -1881,6 +2011,7 @@ sub template_depends ($$;@) { }, loop_context_vars => 1, die_on_bad_params => 0, + parent_global_vars => 1, filename => $filename, @_, ($untrusted ? (no_includes => 1) : ()), @@ -1895,39 +2026,6 @@ sub template ($;@) { template_depends(shift, undef, @_); } -sub misctemplate ($$;@) { - my $title=shift; - my $content=shift; - my %params=@_; - - my $template=template("page.tmpl"); - - my $page=""; - if (exists $params{page}) { - $page=delete $params{page}; - } - run_hooks(pagetemplate => sub { - shift->( - page => $page, - destpage => $page, - template => $template, - ); - }); - templateactions($template, ""); - - $template->param( - dynamic => 1, - title => $title, - wikiname => $config{wikiname}, - content => $content, - baseurl => baseurl(), - html5 => $config{html5}, - %params, - ); - - return $template->output; -} - sub templateactions ($$) { my $template=shift; my $page=shift; @@ -2022,7 +2120,7 @@ sub rcs_recentchanges ($) { $hooks{rcs}{rcs_recentchanges}{call}->(@_); } -sub rcs_diff ($) { +sub rcs_diff ($;$) { $hooks{rcs}{rcs_diff}{call}->(@_); } @@ -2199,6 +2297,13 @@ sub add_autofile ($$$) { $autofiles{$file}{generator}=$generator; } +sub useragent () { + return LWP::UserAgent->new( + cookie_jar => $config{cookiejar}, + env_proxy => 1, # respect proxy env vars + ); +} + sub sortspec_translate ($$) { my $spec = shift; my $reverse = shift; @@ -2603,8 +2708,14 @@ sub match_link ($$;@) { } sub match_backlink ($$;@) { - my $ret=match_link($_[1], $_[0], @_); - $ret->influences($_[1] => $IkiWiki::DEPEND_LINKS); + my $page=shift; + my $testpage=shift; + my %params=@_; + if ($testpage eq '.') { + $testpage = $params{'location'} + } + my $ret=match_link($testpage, $page, @_); + $ret->influences($testpage => $IkiWiki::DEPEND_LINKS); return $ret; } @@ -2695,12 +2806,12 @@ sub match_user ($$;@) { my $user=shift; my %params=@_; - my $regexp=IkiWiki::glob2re($user); - if (! exists $params{user}) { return IkiWiki::ErrorReason->new("no user specified"); } + my $regexp=IkiWiki::glob2re($user); + if (defined $params{user} && $params{user}=~$regexp) { return IkiWiki::SuccessReason->new("user is $user"); } @@ -2740,8 +2851,10 @@ sub match_ip ($$;@) { if (! exists $params{ip}) { return IkiWiki::ErrorReason->new("no IP specified"); } + + my $regexp=IkiWiki::glob2re(lc $ip); - if (defined $params{ip} && lc $params{ip} eq lc $ip) { + if (defined $params{ip} && lc $params{ip}=~$regexp) { return IkiWiki::SuccessReason->new("IP is $ip"); } else { @@ -2764,6 +2877,7 @@ sub cmp_title { IkiWiki::pagetitle(IkiWiki::basename($b)) } +sub cmp_path { IkiWiki::pagetitle($a) cmp IkiWiki::pagetitle($b) } sub cmp_mtime { $IkiWiki::pagemtime{$b} <=> $IkiWiki::pagemtime{$a} } sub cmp_age { $IkiWiki::pagectime{$b} <=> $IkiWiki::pagectime{$a} }