diff options
Diffstat (limited to 'IkiWiki')
94 files changed, 1106 insertions, 306 deletions
diff --git a/IkiWiki/Plugin/404.pm b/IkiWiki/Plugin/404.pm index 85486e559..8adfd5dd9 100644 --- a/IkiWiki/Plugin/404.pm +++ b/IkiWiki/Plugin/404.pm @@ -21,6 +21,7 @@ sub getsetup () { # server admin action too safe => 0, rebuild => 0, + section => "web", } } diff --git a/IkiWiki/Plugin/amazon_s3.pm b/IkiWiki/Plugin/amazon_s3.pm index 3571c4189..cfd8cd347 100644 --- a/IkiWiki/Plugin/amazon_s3.pm +++ b/IkiWiki/Plugin/amazon_s3.pm @@ -133,6 +133,10 @@ sub getbucket { } if (! $bucket) { + # Try to use existing bucket. + $bucket=$s3->bucket($config{amazon_s3_bucket}); + } + if (! $bucket) { error(gettext("Failed to create S3 bucket: "). $s3->err.": ".$s3->errstr."\n"); } @@ -178,7 +182,7 @@ sub writefile ($$$;$$) { # First, write the file to disk. my $ret=$IkiWiki::Plugin::amazon_s3::subs{'IkiWiki::writefile'}->($file, $destdir, $content, $binary, $writer); - + my @keys=IkiWiki::Plugin::amazon_s3::file2keys("$destdir/$file"); # Store the data in S3. diff --git a/IkiWiki/Plugin/anonok.pm b/IkiWiki/Plugin/anonok.pm index 243b98920..0e74cbfad 100644 --- a/IkiWiki/Plugin/anonok.pm +++ b/IkiWiki/Plugin/anonok.pm @@ -15,6 +15,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, anonok_pagespec => { type => "pagespec", diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index cbe6efc21..ad1dd9bca 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "web", }, allowed_attachments => { type => "pagespec", diff --git a/IkiWiki/Plugin/autoindex.pm b/IkiWiki/Plugin/autoindex.pm index 555856b11..c71d73349 100644 --- a/IkiWiki/Plugin/autoindex.pm +++ b/IkiWiki/Plugin/autoindex.pm @@ -61,8 +61,16 @@ sub refresh () { } my %deleted; - if (ref $pagestate{index}{autoindex}{deleted}) { - %deleted=%{$pagestate{index}{autoindex}{deleted}}; + if (ref $wikistate{autoindex}{deleted}) { + %deleted=%{$wikistate{autoindex}{deleted}}; + } + elsif (ref $pagestate{index}{autoindex}{deleted}) { + # compatability code + %deleted=%{$pagestate{index}{autoindex}{deleted}}; + delete $pagestate{index}{autoindex}; + } + + if (keys %deleted) { foreach my $dir (keys %deleted) { # remove deleted page state if the deleted page is re-added, # or if all its subpages are deleted @@ -71,7 +79,7 @@ sub refresh () { delete $deleted{$dir}; } } - $pagestate{index}{autoindex}{deleted}=\%deleted; + $wikistate{autoindex}{deleted}=\%deleted; } my @needed; @@ -82,10 +90,10 @@ sub refresh () { # This page must have just been deleted, so # don't re-add it. And remember it was # deleted. - if (! ref $pagestate{index}{autoindex}{deleted}) { - $pagestate{index}{autoindex}{deleted}={}; + if (! ref $wikistate{autoindex}{deleted}) { + $wikistate{autoindex}{deleted}={}; } - ${$pagestate{index}{autoindex}{deleted}}{$dir}=1; + ${$wikistate{autoindex}{deleted}}{$dir}=1; } else { push @needed, $dir; diff --git a/IkiWiki/Plugin/blogspam.pm b/IkiWiki/Plugin/blogspam.pm index 626c8ec42..c4e5cf390 100644 --- a/IkiWiki/Plugin/blogspam.pm +++ b/IkiWiki/Plugin/blogspam.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, blogspam_pagespec => { type => 'pagespec', diff --git a/IkiWiki/Plugin/bzr.pm b/IkiWiki/Plugin/bzr.pm index 883007367..f79ca7c8f 100644 --- a/IkiWiki/Plugin/bzr.pm +++ b/IkiWiki/Plugin/bzr.pm @@ -20,6 +20,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub checkconfig () { @@ -36,6 +37,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, bzr_wrapper => { type => "string", @@ -72,31 +74,40 @@ sub bzr_log ($) { my @infos = (); my $key = undef; + my %info; while (<$out>) { my $line = $_; my ($value); if ($line =~ /^message:/) { $key = "message"; - $infos[$#infos]{$key} = ""; + $info{$key} = ""; } elsif ($line =~ /^(modified|added|renamed|renamed and modified|removed):/) { $key = "files"; - unless (defined($infos[$#infos]{$key})) { $infos[$#infos]{$key} = ""; } + $info{$key} = "" unless defined $info{$key}; } elsif (defined($key) and $line =~ /^ (.*)/) { - $infos[$#infos]{$key} .= "$1\n"; + $info{$key} .= "$1\n"; } elsif ($line eq "------------------------------------------------------------\n") { + push @infos, {%info} if keys %info; + %info = (); $key = undef; - push (@infos, {}); } - else { + elsif ($line =~ /: /) { chomp $line; + if ($line =~ /^revno: (\d+)/) { + $key = "revno"; + $value = $1; + } + else { ($key, $value) = split /: +/, $line, 2; - $infos[$#infos]{$key} = $value; - } + } + $info{$key} = $value; + } } close $out; + push @infos, {%info} if keys %info; return @infos; } @@ -212,7 +223,7 @@ sub rcs_recentchanges ($) { foreach my $info (bzr_log($out)) { my @pages = (); my @message = (); - + foreach my $msgline (split(/\n/, $info->{message})) { push @message, { line => $msgline }; } @@ -296,4 +307,8 @@ sub rcs_getctime ($) { return $ctime; } +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for bzr\n"; # TODO +} + 1 diff --git a/IkiWiki/Plugin/calendar.pm b/IkiWiki/Plugin/calendar.pm index 2b87451ce..aeb5f3d29 100644 --- a/IkiWiki/Plugin/calendar.pm +++ b/IkiWiki/Plugin/calendar.pm @@ -22,7 +22,7 @@ use warnings; use strict; use IkiWiki 3.00; use Time::Local; -use POSIX; +use POSIX (); my $time=time; my @now=localtime($time); @@ -38,6 +38,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, archivebase => { type => "string", @@ -46,6 +47,14 @@ sub getsetup () { safe => 1, rebuild => 1, }, + archive_pagespec => { + type => "pagespec", + example => "posts/* and !*/Discussion", + description => "PageSpec of pages to include in the archives; used by ikiwiki-calendar command", + link => 'ikiwiki/PageSpec', + safe => 1, + rebuild => 0, + }, } sub is_leap_year (@) { @@ -155,11 +164,11 @@ sub format_month (@) { # Start producing the month calendar $calendar=<<EOF; <table class="month-calendar"> - <caption class="month-calendar-head"> - $purl - $url - $nurl - </caption> + <tr> + <th class="month-calendar-arrow">$purl</th> + <th class="month-calendar-head" colspan="5">$url</th> + <th class="month-calendar-arrow">$nurl</th> + </tr> <tr> EOF @@ -303,13 +312,14 @@ sub format_year (@) { add_depends($params{page}, "$archivebase/$nyear", deptype("presence")); # Start producing the year calendar + my $m=$params{months_per_row}-2; $calendar=<<EOF; <table class="year-calendar"> - <caption class="year-calendar-head"> - $purl - $url - $nurl - </caption> + <tr> + <th class="year-calendar-arrow">$purl</th> + <th class="year-calendar-head" colspan="$m">$url</th> + <th class="year-calendar-arrow">$nurl</th> + </tr> <tr> <th class="year-calendar-subhead" colspan="$params{months_per_row}">Months</th> </tr> diff --git a/IkiWiki/Plugin/color.pm b/IkiWiki/Plugin/color.pm index 20505893b..d550dd9f4 100644 --- a/IkiWiki/Plugin/color.pm +++ b/IkiWiki/Plugin/color.pm @@ -10,6 +10,16 @@ use IkiWiki 3.00; sub import { hook(type => "preprocess", id => "color", call => \&preprocess); hook(type => "format", id => "color", call => \&format); + hook(type => "getsetup", id => "color", call => \&getsetup); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, } sub preserve_style ($$$) { diff --git a/IkiWiki/Plugin/comments.pm b/IkiWiki/Plugin/comments.pm index 5586cca52..0aa043215 100644 --- a/IkiWiki/Plugin/comments.pm +++ b/IkiWiki/Plugin/comments.pm @@ -38,6 +38,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "web", }, comments_pagespec => { type => 'pagespec', @@ -171,9 +172,8 @@ sub preprocess { else { $commentauthorurl = IkiWiki::cgiurl( do => 'goto', - page => (length $config{userdir} - ? "$config{userdir}/$commentuser" - : "$commentuser")); + page => IkiWiki::userpage($commentuser) + ); $commentauthor = $commentuser; } @@ -221,7 +221,9 @@ sub preprocess { } if (defined $params{subject}) { - $pagestate{$page}{meta}{title} = $params{subject}; + # decode title the same way meta does + eval q{use HTML::Entities}; + $pagestate{$page}{meta}{title} = decode_entities($params{subject}); } if ($params{page} =~ m/\/\Q$config{comments_pagename}\E\d+_/) { @@ -736,39 +738,43 @@ sub pagetemplate (@) { } if ($shown && commentsopen($page)) { - my $addcommenturl = IkiWiki::cgiurl(do => 'comment', - page => $page); - $template->param(addcommenturl => $addcommenturl); + $template->param(addcommenturl => addcommenturl($page)); } } - if ($template->query(name => 'commentsurl')) { - if ($shown) { + if ($shown) { + if ($template->query(name => 'commentsurl')) { $template->param(commentsurl => urlto($page, undef, 1).'#comments'); } - } - if ($template->query(name => 'atomcommentsurl') && $config{usedirs}) { - if ($shown) { + if ($template->query(name => 'atomcommentsurl') && $config{usedirs}) { # 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"), + if ($template->query(name => 'commentslink')) { + my $num=num_comments($page, $config{srcdir}); + my $link; + if ($num > 0) { + $link = htmllink($page, $params{destpage}, $page, + linktext => sprintf(ngettext("%i comment", "%i comments", $num), $num), anchor => "comments", - noimageinline => 1)); + noimageinline => 1 + ); + } + elsif (commentsopen($page)) { + $link = "<a href=\"".addcommenturl($page)."\">". + #translators: Here "Comment" is a verb; + #translators: the user clicks on it to + #translators: post a comment. + gettext("Comment"). + "</a>"; + } + $template->param(commentslink => $link) + if defined $link; } } @@ -816,6 +822,12 @@ sub pagetemplate (@) { } } +sub addcommenturl ($) { + my $page=shift; + + return IkiWiki::cgiurl(do => 'comment', page => $page); +} + sub num_comments ($$) { my $page=shift; my $dir=shift; @@ -829,7 +841,7 @@ sub unique_comment_location ($$$) { eval q{use Digest::MD5 'md5_hex'}; error($@) if $@; - my $content_md5=md5_hex(shift); + my $content_md5=md5_hex(Encode::encode_utf8(shift)); my $dir=shift; @@ -845,14 +857,14 @@ sub unique_comment_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 + # attribute 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); + return "comment-".md5_hex(Encode::encode_utf8(($page))); } package IkiWiki::PageSpec; diff --git a/IkiWiki/Plugin/conditional.pm b/IkiWiki/Plugin/conditional.pm index aad617812..8a5796149 100644 --- a/IkiWiki/Plugin/conditional.pm +++ b/IkiWiki/Plugin/conditional.pm @@ -16,6 +16,7 @@ sub getsetup { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } @@ -29,7 +30,7 @@ sub preprocess_if (@) { } my $result=0; - if (! IkiWiki::yesno($params{all}) || + if ((exists $params{all} && ! IkiWiki::yesno($params{all})) || # An optimisation to avoid needless looping over every page # for simple uses of some of the tests. $params{test} =~ /^([\s\!()]*((enabled|sourcepage|destpage|included)\([^)]*\)|(and|or))[\s\!()]*)+$/) { diff --git a/IkiWiki/Plugin/creole.pm b/IkiWiki/Plugin/creole.pm index 425e71043..a1e4b31d3 100644 --- a/IkiWiki/Plugin/creole.pm +++ b/IkiWiki/Plugin/creole.pm @@ -17,6 +17,7 @@ sub getsetup { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, } diff --git a/IkiWiki/Plugin/cutpaste.pm b/IkiWiki/Plugin/cutpaste.pm index 417442f34..01e9ce043 100644 --- a/IkiWiki/Plugin/cutpaste.pm +++ b/IkiWiki/Plugin/cutpaste.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/cvs.pm b/IkiWiki/Plugin/cvs.pm index f6db8bc98..360d97249 100644 --- a/IkiWiki/Plugin/cvs.pm +++ b/IkiWiki/Plugin/cvs.pm @@ -49,6 +49,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub genwrapper () { @@ -85,6 +86,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, cvsrepo => { type => "string", @@ -484,4 +486,8 @@ sub rcs_getctime ($) { return $date; } +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for cvs\n"; # TODO +} + 1 diff --git a/IkiWiki/Plugin/darcs.pm b/IkiWiki/Plugin/darcs.pm index 0d68f27e5..c1d6661d3 100644 --- a/IkiWiki/Plugin/darcs.pm +++ b/IkiWiki/Plugin/darcs.pm @@ -18,6 +18,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub silentsystem (@) { @@ -51,7 +52,7 @@ sub darcs_info ($$$) { return $_; } -sub file_in_vc($$) { +sub file_in_vc ($$) { my $repodir = shift; my $file = shift; @@ -69,7 +70,7 @@ sub file_in_vc($$) { return $found; } -sub darcs_rev($) { +sub darcs_rev ($) { my $file = shift; # Relative to the repodir. my $repodir = $config{srcdir}; @@ -78,7 +79,7 @@ sub darcs_rev($) { return defined $hash ? $hash : ""; } -sub checkconfig() { +sub checkconfig () { if (defined $config{darcs_wrapper} && length $config{darcs_wrapper}) { push @{$config{wrappers}}, { wrapper => $config{darcs_wrapper}, @@ -87,11 +88,12 @@ sub checkconfig() { } } -sub getsetup() { +sub getsetup () { return plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, darcs_wrapper => { type => "string", @@ -237,7 +239,7 @@ sub rcs_commit ($$$;$$) { } } -sub rcs_commit_staged($$$) { +sub rcs_commit_staged ($$$) { my ($message, $user, $ipaddr) = @_; my $author; @@ -426,4 +428,8 @@ sub rcs_getctime ($) { return $date; } +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for darcs\n"; # TODO +} + 1 diff --git a/IkiWiki/Plugin/date.pm b/IkiWiki/Plugin/date.pm index 652e6df0a..ea5c9a9c5 100644 --- a/IkiWiki/Plugin/date.pm +++ b/IkiWiki/Plugin/date.pm @@ -15,6 +15,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/editdiff.pm b/IkiWiki/Plugin/editdiff.pm index 7df6a9ffb..d8f53a42e 100644 --- a/IkiWiki/Plugin/editdiff.pm +++ b/IkiWiki/Plugin/editdiff.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "web", }, } diff --git a/IkiWiki/Plugin/editpage.pm b/IkiWiki/Plugin/editpage.pm index fca970c60..44fe5514a 100644 --- a/IkiWiki/Plugin/editpage.pm +++ b/IkiWiki/Plugin/editpage.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "core", }, } @@ -245,8 +246,9 @@ sub cgi_editpage ($$) { push @page_locs, $dir.$page; } - push @page_locs, "$config{userdir}/$page" - if length $config{userdir}; + my $userpage=IkiWiki::userpage($page); + push @page_locs, $userpage + if ! grep { $_ eq $userpage } @page_locs; } @page_locs = grep { @@ -271,8 +273,10 @@ sub cgi_editpage ($$) { check_canedit($_, $q, $session, 1) } @page_locs; if (! @editable_locs) { - # let it throw an error this time - map { check_canedit($_, $q, $session) } @page_locs; + # now let it throw an error, or prompt for + # login + map { check_canedit($_, $q, $session) } + ($best_loc, @page_locs); } my @page_types; diff --git a/IkiWiki/Plugin/edittemplate.pm b/IkiWiki/Plugin/edittemplate.pm index a163b0d84..5f0551d92 100644 --- a/IkiWiki/Plugin/edittemplate.pm +++ b/IkiWiki/Plugin/edittemplate.pm @@ -23,6 +23,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "web", }, } diff --git a/IkiWiki/Plugin/filecheck.pm b/IkiWiki/Plugin/filecheck.pm index 01d490961..1549b82db 100644 --- a/IkiWiki/Plugin/filecheck.pm +++ b/IkiWiki/Plugin/filecheck.pm @@ -5,7 +5,7 @@ use warnings; use strict; use IkiWiki 3.00; -my %units=( #{{{ # size in bytes +my %units=( # size in bytes B => 1, byte => 1, KB => 2 ** 10, @@ -75,9 +75,9 @@ sub match_maxsize ($$;@) { } my %params=@_; - my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page}; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); if (! defined $file) { - return IkiWiki::ErrorReason->new("no file specified"); + return IkiWiki::ErrorReason->new("file does not exist"); } if (-s $file > $maxsize) { @@ -96,9 +96,9 @@ sub match_minsize ($$;@) { } my %params=@_; - my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page}; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); if (! defined $file) { - return IkiWiki::ErrorReason->new("no file specified"); + return IkiWiki::ErrorReason->new("file does not exist"); } if (-s $file < $minsize) { @@ -114,9 +114,9 @@ sub match_mimetype ($$;@) { my $wanted=shift; my %params=@_; - my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page}; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); if (! defined $file) { - return IkiWiki::ErrorReason->new("no file specified"); + return IkiWiki::ErrorReason->new("file does not exist"); } # Use ::magic to get the mime type, the idea is to only trust @@ -147,9 +147,9 @@ sub match_virusfree ($$;@) { my $wanted=shift; my %params=@_; - my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page}; + my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page}); if (! defined $file) { - return IkiWiki::ErrorReason->new("no file specified"); + return IkiWiki::ErrorReason->new("file does not exist"); } if (! exists $IkiWiki::config{virus_checker} || diff --git a/IkiWiki/Plugin/format.pm b/IkiWiki/Plugin/format.pm index 1513cbed7..d54e71131 100644 --- a/IkiWiki/Plugin/format.pm +++ b/IkiWiki/Plugin/format.pm @@ -7,6 +7,16 @@ use IkiWiki 3.00; sub import { hook(type => "preprocess", id => "format", call => \&preprocess); + hook(type => "getsetup", id => "format", call => \&getsetup); +} + +sub getsetup () { + return + plugin => { + safe => 1, + rebuild => undef, + section => "widget", + }, } sub preprocess (@) { diff --git a/IkiWiki/Plugin/fortune.pm b/IkiWiki/Plugin/fortune.pm index 17e57dea1..f481c7eac 100644 --- a/IkiWiki/Plugin/fortune.pm +++ b/IkiWiki/Plugin/fortune.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/getsource.pm b/IkiWiki/Plugin/getsource.pm index d1555430e..b362de726 100644 --- a/IkiWiki/Plugin/getsource.pm +++ b/IkiWiki/Plugin/getsource.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "web", }, getsource_mimetype => { type => "string", diff --git a/IkiWiki/Plugin/git.pm b/IkiWiki/Plugin/git.pm index 1eec6aee6..aa402c04f 100644 --- a/IkiWiki/Plugin/git.pm +++ b/IkiWiki/Plugin/git.pm @@ -25,6 +25,7 @@ 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_getmtime", call => \&rcs_getmtime); hook(type => "rcs", id => "rcs_receive", call => \&rcs_receive); } @@ -68,6 +69,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, git_wrapper => { type => "string", @@ -614,23 +616,51 @@ sub rcs_diff ($) { } } -sub rcs_getctime ($) { +{ +my %time_cache; + +sub findtimes ($$) { my $file=shift; + my $id=shift; # 0 = mtime ; 1 = ctime + # Remove srcdir prefix $file =~ s/^\Q$config{srcdir}\E\/?//; - my @raw_lines = run_or_die('git', 'log', - '--follow', '--no-merges', - '--pretty=raw', '--raw', '--abbrev=40', '--always', '-c', - '-r', '--', $file); - my @ci; - while (my $parsed = parse_diff_tree("", \@raw_lines)) { - push @ci, $parsed; + if (! keys %time_cache) { + my $date; + foreach my $line (run_or_die('git', 'log', + '--pretty=format:%ct', + '--name-only', '--relative')) { + if (! defined $date && $line =~ /^(\d+)$/) { + $date=$line; + } + elsif (! length $line) { + $date=undef; + } + else { + if (! $time_cache{$line}) { + $time_cache{$line}[0]=$date; # mtime + } + $time_cache{$line}[1]=$date; # ctime + } + } } - my $ctime = $ci[$#ci]->{'author_epoch'}; - debug("ctime for '$file': ". localtime($ctime)); - return $ctime; + return exists $time_cache{$file} ? $time_cache{$file}[$id] : 0; +} + +} + +sub rcs_getctime ($) { + my $file=shift; + + return findtimes($file, 1); +} + +sub rcs_getmtime ($) { + my $file=shift; + + return findtimes($file, 0); } sub rcs_receive () { diff --git a/IkiWiki/Plugin/google.pm b/IkiWiki/Plugin/google.pm index 483fa1707..48ad4c8ce 100644 --- a/IkiWiki/Plugin/google.pm +++ b/IkiWiki/Plugin/google.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "web", }, } diff --git a/IkiWiki/Plugin/goto.pm b/IkiWiki/Plugin/goto.pm index 439552f62..03bd682b3 100644 --- a/IkiWiki/Plugin/goto.pm +++ b/IkiWiki/Plugin/goto.pm @@ -14,6 +14,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "web", } } diff --git a/IkiWiki/Plugin/graphviz.pm b/IkiWiki/Plugin/graphviz.pm index 32e994d6b..bec122076 100644 --- a/IkiWiki/Plugin/graphviz.pm +++ b/IkiWiki/Plugin/graphviz.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/haiku.pm b/IkiWiki/Plugin/haiku.pm index 5a062a276..bf23dce67 100644 --- a/IkiWiki/Plugin/haiku.pm +++ b/IkiWiki/Plugin/haiku.pm @@ -16,6 +16,7 @@ sub getsetup { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/highlight.pm b/IkiWiki/Plugin/highlight.pm index 9bdde85ae..e517ac5c0 100644 --- a/IkiWiki/Plugin/highlight.pm +++ b/IkiWiki/Plugin/highlight.pm @@ -23,6 +23,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, tohighlight => { type => "string", @@ -79,7 +80,7 @@ my %highlighters; # Parse highlight's config file to get extension => language mappings. sub read_filetypes () { - open (IN, $filetypes); + open (IN, $filetypes) || error("$filetypes: $!"); while (<IN>) { chomp; if (/^\$ext\((.*)\)=(.*)$/) { diff --git a/IkiWiki/Plugin/hnb.pm b/IkiWiki/Plugin/hnb.pm index bd2177a06..32c9cf3ad 100644 --- a/IkiWiki/Plugin/hnb.pm +++ b/IkiWiki/Plugin/hnb.pm @@ -23,6 +23,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, } diff --git a/IkiWiki/Plugin/html.pm b/IkiWiki/Plugin/html.pm index a7d5e8ce9..4dbae081b 100644 --- a/IkiWiki/Plugin/html.pm +++ b/IkiWiki/Plugin/html.pm @@ -21,6 +21,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, } diff --git a/IkiWiki/Plugin/htmlscrubber.pm b/IkiWiki/Plugin/htmlscrubber.pm index a249cdf7a..9f40c752f 100644 --- a/IkiWiki/Plugin/htmlscrubber.pm +++ b/IkiWiki/Plugin/htmlscrubber.pm @@ -30,9 +30,9 @@ sub import { "msnim", "notes", "rsync", "secondlife", "skype", "ssh", "sftp", "smb", "sms", "snews", "webcal", "ymsgr", ); - # 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; + # data is a special case. Allow a few data:image/ types, + # but disallow data:text/javascript and everything else. + $safe_url_regexp=qr/^(?:(?:$uri_schemes):|data:image\/(?:png|jpeg|gif)|[^:]+(?:$|[\/\?]))/i; } sub getsetup () { @@ -40,6 +40,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "core", }, htmlscrubber_skip => { type => "pagespec", diff --git a/IkiWiki/Plugin/httpauth.pm b/IkiWiki/Plugin/httpauth.pm index 127c321f0..478f67446 100644 --- a/IkiWiki/Plugin/httpauth.pm +++ b/IkiWiki/Plugin/httpauth.pm @@ -9,6 +9,10 @@ use IkiWiki 3.00; sub import { hook(type => "getsetup", id => "httpauth", call => \&getsetup); hook(type => "auth", id => "httpauth", call => \&auth); + hook(type => "formbuilder_setup", id => "httpauth", + call => \&formbuilder_setup); + hook(type => "canedit", id => "httpauth", call => \&canedit, + first => 1); } sub getsetup () { @@ -16,6 +20,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, cgiauthurl => { type => "string", @@ -24,6 +29,23 @@ sub getsetup () { safe => 1, rebuild => 0, }, + httpauth_pagespec => { + type => "pagespec", + example => "!*/Discussion", + description => "PageSpec of pages where only httpauth will be used for authentication", + safe => 0, + rebuild => 0, + }, +} + +sub redir_cgiauthurl ($;@) { + my $cgi=shift; + + IkiWiki::redirect($cgi, + @_ > 1 ? IkiWiki::cgiurl(cgiurl => $config{cgiauthurl}, @_) + : $config{cgiauthurl}."?@_" + ); + exit; } sub auth ($$) { @@ -33,9 +55,53 @@ sub auth ($$) { if (defined $cgi->remote_user()) { $session->param("name", $cgi->remote_user()); } - elsif (defined $config{cgiauthurl}) { - IkiWiki::redirect($cgi, $config{cgiauthurl}.'?'.$cgi->query_string()); - exit; +} + +sub formbuilder_setup (@) { + my %params=@_; + + my $form=$params{form}; + my $session=$params{session}; + my $cgi=$params{cgi}; + my $buttons=$params{buttons}; + + if ($form->title eq "signin" && + ! defined $cgi->remote_user() && defined $config{cgiauthurl}) { + my $button_text="Login with HTTP auth"; + push @$buttons, $button_text; + + if ($form->submitted && $form->submitted eq $button_text) { + # bounce thru cgiauthurl and then back to + # the stored postsignin action + redir_cgiauthurl($cgi, do => "postsignin"); + } + } +} + +sub test_httpauth_pagespec ($) { + my $page=shift; + + return ( + ); +} + +sub canedit ($$$) { + my $page=shift; + my $cgi=shift; + my $session=shift; + + if (! defined $cgi->remote_user() && + defined $config{httpauth_pagespec} && + length $config{httpauth_pagespec} && + defined $config{cgiauthurl} && + pagespec_match($page, $config{httpauth_pagespec})) { + return sub { + # bounce thru cgiauthurl and back to edit action + redir_cgiauthurl($cgi, $cgi->query_string()); + }; + } + else { + return undef; } } diff --git a/IkiWiki/Plugin/img.pm b/IkiWiki/Plugin/img.pm index 82db15a7e..f06121578 100644 --- a/IkiWiki/Plugin/img.pm +++ b/IkiWiki/Plugin/img.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/inline.pm b/IkiWiki/Plugin/inline.pm index 401852513..3359af314 100644 --- a/IkiWiki/Plugin/inline.pm +++ b/IkiWiki/Plugin/inline.pm @@ -49,6 +49,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "core", }, rss => { type => "boolean", @@ -159,7 +160,7 @@ sub preprocess_inline (@) { my $rss=(($config{rss} || $config{allowrss}) && exists $params{rss}) ? yesno($params{rss}) : $config{rss}; 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=! $nested && (exists $params{feeds} ? yesno($params{feeds}) : !$quick); + my $feeds=! $nested && (exists $params{feeds} ? yesno($params{feeds}) : !$quick && ! $raw); my $emptyfeeds=exists $params{emptyfeeds} ? yesno($params{emptyfeeds}) : 1; my $feedonly=yesno($params{feedonly}); if (! exists $params{show} && ! $archive) { @@ -552,7 +553,8 @@ sub genfeed ($$$$$@) { if (exists $pagestate{$p}) { if (exists $pagestate{$p}{meta}{guid}) { - $itemtemplate->param(guid => $pagestate{$p}{meta}{guid}); + eval q{use HTML::Entities}; + $itemtemplate->param(guid => HTML::Entities::encode_numeric($pagestate{$p}{meta}{guid})); } if (exists $pagestate{$p}{meta}{updated}) { diff --git a/IkiWiki/Plugin/link.pm b/IkiWiki/Plugin/link.pm index 4c1add985..3838aec09 100644 --- a/IkiWiki/Plugin/link.pm +++ b/IkiWiki/Plugin/link.pm @@ -20,6 +20,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "core", }, } diff --git a/IkiWiki/Plugin/linkmap.pm b/IkiWiki/Plugin/linkmap.pm index 68eb6c8c6..ac26e072e 100644 --- a/IkiWiki/Plugin/linkmap.pm +++ b/IkiWiki/Plugin/linkmap.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/listdirectives.pm b/IkiWiki/Plugin/listdirectives.pm index 09f08c567..8a67f7160 100644 --- a/IkiWiki/Plugin/listdirectives.pm +++ b/IkiWiki/Plugin/listdirectives.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, directive_description_dir => { type => "string", diff --git a/IkiWiki/Plugin/lockedit.pm b/IkiWiki/Plugin/lockedit.pm index 74ddbb153..1466e8337 100644 --- a/IkiWiki/Plugin/lockedit.pm +++ b/IkiWiki/Plugin/lockedit.pm @@ -15,6 +15,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, locked_pages => { type => "pagespec", diff --git a/IkiWiki/Plugin/map.pm b/IkiWiki/Plugin/map.pm index 788b96827..ce3ac1d24 100644 --- a/IkiWiki/Plugin/map.pm +++ b/IkiWiki/Plugin/map.pm @@ -21,6 +21,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/mdwn.pm b/IkiWiki/Plugin/mdwn.pm index 3de59ef3d..b892eabee 100644 --- a/IkiWiki/Plugin/mdwn.pm +++ b/IkiWiki/Plugin/mdwn.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, multimarkdown => { type => "boolean", diff --git a/IkiWiki/Plugin/mercurial.pm b/IkiWiki/Plugin/mercurial.pm index 11fdec529..34e009c7a 100644 --- a/IkiWiki/Plugin/mercurial.pm +++ b/IkiWiki/Plugin/mercurial.pm @@ -20,6 +20,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub checkconfig () { @@ -36,6 +37,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, mercurial_wrapper => { type => "string", @@ -253,4 +255,8 @@ sub rcs_getctime ($) { return $ctime; } +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for mercurial\n"; # TODO +} + 1 diff --git a/IkiWiki/Plugin/meta.pm b/IkiWiki/Plugin/meta.pm index 55c9ddbd1..128a6342c 100644 --- a/IkiWiki/Plugin/meta.pm +++ b/IkiWiki/Plugin/meta.pm @@ -20,6 +20,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "core", }, } @@ -87,15 +88,21 @@ sub preprocess (@) { # Metadata collection that needs to happen during the scan pass. if ($key eq 'title') { - $pagestate{$page}{meta}{title}=HTML::Entities::encode_numeric($value); + $pagestate{$page}{meta}{title}=$value; + if (exists $params{sortas}) { + $pagestate{$page}{meta}{titlesort}=$params{sortas}; + } + else { + delete $pagestate{$page}{meta}{titlesort}; + } return ""; } elsif ($key eq 'description') { - $pagestate{$page}{meta}{description}=HTML::Entities::encode_numeric($value); + $pagestate{$page}{meta}{description}=$value; # fallthrough } elsif ($key eq 'guid') { - $pagestate{$page}{meta}{guid}=HTML::Entities::encode_numeric($value); + $pagestate{$page}{meta}{guid}=$value; # fallthrough } elsif ($key eq 'license') { @@ -115,6 +122,12 @@ sub preprocess (@) { } elsif ($key eq 'author') { $pagestate{$page}{meta}{author}=$value; + if (exists $params{sortas}) { + $pagestate{$page}{meta}{authorsort}=$params{sortas}; + } + else { + delete $pagestate{$page}{meta}{authorsort}; + } # fallthorough } elsif ($key eq 'authorurl') { @@ -263,15 +276,20 @@ sub pagetemplate (@) { $template->param(meta => join("\n", grep { (! $seen{$_}) && ($seen{$_}=1) } @{$metaheaders{$page}})); } if (exists $pagestate{$page}{meta}{title} && $template->query(name => "title")) { - $template->param(title => $pagestate{$page}{meta}{title}); + $template->param(title => HTML::Entities::encode_numeric($pagestate{$page}{meta}{title})); $template->param(title_overridden => 1); } - foreach my $field (qw{author authorurl description permalink}) { + foreach my $field (qw{author authorurl permalink}) { $template->param($field => $pagestate{$page}{meta}{$field}) if exists $pagestate{$page}{meta}{$field} && $template->query(name => $field); } + foreach my $field (qw{description}) { + $template->param($field => HTML::Entities::encode_numeric($pagestate{$page}{meta}{$field})) + if exists $pagestate{$page}{meta}{$field} && $template->query(name => $field); + } + foreach my $field (qw{license copyright}) { if (exists $pagestate{$page}{meta}{$field} && $template->query(name => $field) && ($page eq $destpage || ! exists $pagestate{$destpage}{meta}{$field} || @@ -281,6 +299,33 @@ sub pagetemplate (@) { } } +sub get_sort_key { + my $page = shift; + my $meta = shift; + + # e.g. titlesort (also makes sense for author) + my $key = $pagestate{$page}{meta}{$meta . "sort"}; + return $key if defined $key; + + # e.g. title + $key = $pagestate{$page}{meta}{$meta}; + return $key if defined $key; + + # fall back to closer-to-core things + if ($meta eq 'title') { + return pagetitle(IkiWiki::basename($page)); + } + elsif ($meta eq 'date') { + return $IkiWiki::pagectime{$page}; + } + elsif ($meta eq 'updated') { + return $IkiWiki::pagemtime{$page}; + } + else { + return ''; + } +} + sub match { my $field=shift; my $page=shift; @@ -331,4 +376,27 @@ sub match_copyright ($$;@) { IkiWiki::Plugin::meta::match("copyright", @_); } +package IkiWiki::SortSpec; + +sub cmp_meta { + my $meta = shift; + error(gettext("sort=meta requires a parameter")) unless defined $meta; + + if ($meta eq 'updated' || $meta eq 'date') { + return IkiWiki::Plugin::meta::get_sort_key($a, $meta) + <=> + IkiWiki::Plugin::meta::get_sort_key($b, $meta); + } + + return IkiWiki::Plugin::meta::get_sort_key($a, $meta) + cmp + IkiWiki::Plugin::meta::get_sort_key($b, $meta); +} + +# A prototype of how sort=title could behave in 4.0 or something +sub cmp_meta_title { + $_[0] = 'title'; + return cmp_meta(@_); +} + 1 diff --git a/IkiWiki/Plugin/mirrorlist.pm b/IkiWiki/Plugin/mirrorlist.pm index d0a6107ef..92be7913e 100644 --- a/IkiWiki/Plugin/mirrorlist.pm +++ b/IkiWiki/Plugin/mirrorlist.pm @@ -15,6 +15,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "web", }, mirrorlist => { type => "string", diff --git a/IkiWiki/Plugin/moderatedcomments.pm b/IkiWiki/Plugin/moderatedcomments.pm index 2555927b7..b0a328a06 100644 --- a/IkiWiki/Plugin/moderatedcomments.pm +++ b/IkiWiki/Plugin/moderatedcomments.pm @@ -15,11 +15,13 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, - moderate_users => { - type => 'boolean', - example => 1, - description => 'Moderate comments of logged-in users?', + moderate_pagespec => { + type => 'pagespec', + example => 'user(http://*)', + description => 'PageSpec matching users or comment locations to moderate', + link => 'ikiwiki/PageSpec', safe => 1, rebuild => 0, }, @@ -31,14 +33,32 @@ sub checkcontent (@) { # only handle comments return undef unless pagespec_match($params{page}, "postcomment(*)", location => $params{page}); + + # backwards compatability + if (exists $config{moderate_users} && + ! exists $config{moderate_pagespec}) { + $config{moderate_pagespec} = $config{moderate_users} + ? "!admin()" + : "!user(*)"; + } + + # default is to moderate all except admins + if (! exists $config{moderate_pagespec}) { + $config{moderate_pagespec}="!admin()"; + } - # admins and maybe users can comment w/o moderation my $session=$params{session}; my $user=$session->param("name") if $session; - return undef if defined $user && (IkiWiki::is_admin($user) || - (exists $config{moderate_users} && ! $config{moderate_users})); - - return gettext("comment needs moderation"); + if (pagespec_match($params{page}, $config{moderate_pagespec}, + location => $params{page}, + (defined $user ? (user => $user) : ()), + (defined $ENV{REMOTE_ADDR} ? (ip => $ENV{REMOTE_ADDR}) : ()), + )) { + return gettext("comment needs moderation"); + } + else { + return undef; + } } 1 diff --git a/IkiWiki/Plugin/monotone.pm b/IkiWiki/Plugin/monotone.pm index 9502804f1..67d4abbaa 100644 --- a/IkiWiki/Plugin/monotone.pm +++ b/IkiWiki/Plugin/monotone.pm @@ -23,6 +23,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub checkconfig () { @@ -68,6 +69,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, mtn_wrapper => { type => "string", @@ -692,4 +694,8 @@ sub rcs_getctime ($) { return $date; } +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for monotone\n"; # TODO +} + 1 diff --git a/IkiWiki/Plugin/more.pm b/IkiWiki/Plugin/more.pm index 77d5fb077..266c8e1d0 100644 --- a/IkiWiki/Plugin/more.pm +++ b/IkiWiki/Plugin/more.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/norcs.pm b/IkiWiki/Plugin/norcs.pm index bfe84c0e1..053652a5f 100644 --- a/IkiWiki/Plugin/norcs.pm +++ b/IkiWiki/Plugin/norcs.pm @@ -18,6 +18,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub getsetup () { @@ -25,6 +26,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => 0, + section => "rcs", }, } @@ -62,7 +64,11 @@ sub rcs_diff ($) { } sub rcs_getctime ($) { - error gettext("getctime not implemented"); + return 0; +} + +sub rcs_getmtime ($) { + return 0; } 1 diff --git a/IkiWiki/Plugin/opendiscussion.pm b/IkiWiki/Plugin/opendiscussion.pm index 4517ff88b..2805f60ef 100644 --- a/IkiWiki/Plugin/opendiscussion.pm +++ b/IkiWiki/Plugin/opendiscussion.pm @@ -7,7 +7,8 @@ use IkiWiki 3.00; sub import { hook(type => "getsetup", id => "opendiscussion", call => \&getsetup); - hook(type => "canedit", id => "opendiscussion", call => \&canedit); + hook(type => "canedit", id => "opendiscussion", call => \&canedit, + first => 1); } sub getsetup () { @@ -15,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, } @@ -24,6 +26,7 @@ sub canedit ($$) { my $session=shift; return "" if $page=~/(\/|^)\Q$config{discussionpage}\E$/i; + return "" if pagespec_match($page, "postcomment(*)"); return undef; } diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm index dc0e0f48e..7b1a17831 100644 --- a/IkiWiki/Plugin/openid.pm +++ b/IkiWiki/Plugin/openid.pm @@ -26,6 +26,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, openidsignup => { type => "string", @@ -56,7 +57,7 @@ sub formbuilder_setup (@) { # OpenID fieldset. $form->fieldsets("OpenID"); - $form->field( + $form->field( name => "openid_url", label => gettext("Log in with")." ".htmllink("", "", "ikiwiki/OpenID", noimageinline => 1), fieldset => "OpenID", @@ -82,13 +83,14 @@ sub formbuilder_setup (@) { } } } - elsif ($form->title eq "preferences") { - if (! defined $form->field(name => "name")) { - $form->field(name => "OpenID", disabled => 1, - value => $session->param("name"), - size => 50, force => 1, - fieldset => "login"); - } + elsif ($form->title eq "preferences" && + IkiWiki::openiduser($session->param("name"))) { + $form->field(name => "openid_url", disabled => 1, + label => htmllink("", "", "ikiwiki/OpenID", noimageinline => 1), + value => $session->param("name"), + size => 50, force => 1, + fieldset => "login"); + $form->field(name => "email", type => "hidden"); } } @@ -112,6 +114,28 @@ sub validate ($$$;$) { } } + # Ask for client to provide a name and email, if possible. + # Try sreg and ax + if ($claimed_identity->can("set_extension_args")) { + $claimed_identity->set_extension_args( + 'http://openid.net/extensions/sreg/1.1', + { + optional => 'email,fullname,nickname', + }, + ); + $claimed_identity->set_extension_args( + 'http://openid.net/srv/ax/1.0', + { + mode => 'fetch_request', + 'required' => 'email,fullname,nickname,firstname', + 'type.email' => "http://schema.openid.net/contact/email", + 'type.fullname' => "http://axschema.org/namePerson", + 'type.nickname' => "http://axschema.org/namePerson/friendly", + 'type.firstname' => "http://axschema.org/namePerson/first", + }, + ); + } + my $check_url = $claimed_identity->check_url( return_to => IkiWiki::cgiurl(do => "postsignin"), trust_root => $config{cgiurl}, @@ -138,6 +162,32 @@ sub auth ($$) { } elsif (my $vident = $csr->verified_identity) { $session->param(name => $vident->url); + + my @extensions; + if ($vident->can("signed_extension_fields")) { + @extensions=grep { defined } ( + $vident->signed_extension_fields('http://openid.net/extensions/sreg/1.1'), + $vident->signed_extension_fields('http://openid.net/srv/ax/1.0'), + ); + } + foreach my $ext (@extensions) { + foreach my $field (qw{value.email email}) { + if (exists $ext->{$field} && + defined $ext->{$field} && + length $ext->{$field}) { + $session->param(email => $ext->{$field}); + last; + } + } + foreach my $field (qw{value.nickname nickname value.fullname fullname value.firstname}) { + if (exists $ext->{$field} && + defined $ext->{$field} && + length $ext->{$field}) { + $session->param(username => $ext->{$field}); + last; + } + } + } } else { error("OpenID failure: ".$csr->err); diff --git a/IkiWiki/Plugin/orphans.pm b/IkiWiki/Plugin/orphans.pm index 702943f87..e3cc3c940 100644 --- a/IkiWiki/Plugin/orphans.pm +++ b/IkiWiki/Plugin/orphans.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/otl.pm b/IkiWiki/Plugin/otl.pm index 3ab2441bf..3801a6ec2 100644 --- a/IkiWiki/Plugin/otl.pm +++ b/IkiWiki/Plugin/otl.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, } diff --git a/IkiWiki/Plugin/pagecount.pm b/IkiWiki/Plugin/pagecount.pm index 8d36f057e..dd5de3c83 100644 --- a/IkiWiki/Plugin/pagecount.pm +++ b/IkiWiki/Plugin/pagecount.pm @@ -15,6 +15,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/pagestats.pm b/IkiWiki/Plugin/pagestats.pm index 4313aa271..17b26f7ba 100644 --- a/IkiWiki/Plugin/pagestats.pm +++ b/IkiWiki/Plugin/pagestats.pm @@ -27,6 +27,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } @@ -74,7 +75,7 @@ sub preprocess (@) { } if ($style eq 'table') { - return "<table class='pageStats'>\n". + return "<table class='".(exists $params{class} ? $params{class} : "pageStats")."'>\n". join("\n", map { "<tr><td>". htmllink($params{page}, $params{destpage}, $_, noimageinline => 1). @@ -86,16 +87,31 @@ sub preprocess (@) { else { # In case of misspelling, default to a page cloud - my $res = "<div class='pagecloud'>\n"; + my $res; + if ($style eq 'list') { + $res = "<ul class='".(exists $params{class} ? $params{class} : "list")."'>\n"; + } + else { + $res = "<div class='".(exists $params{class} ? $params{class} : "pagecloud")."'>\n"; + } foreach my $page (sort keys %counts) { next unless $counts{$page} > 0; my $class = $classes[$counts{$page} * scalar(@classes) / ($max + 1)]; + + $res.="<li>" if $style eq 'list'; $res .= "<span class=\"$class\">". htmllink($params{page}, $params{destpage}, $page). "</span>\n"; + $res.="</li>" if $style eq 'list'; + + } + if ($style eq 'list') { + $res .= "</ul>\n"; + } + else { + $res .= "</div>\n"; } - $res .= "</div>\n"; return $res; } diff --git a/IkiWiki/Plugin/parentlinks.pm b/IkiWiki/Plugin/parentlinks.pm index e678a057d..728bbc399 100644 --- a/IkiWiki/Plugin/parentlinks.pm +++ b/IkiWiki/Plugin/parentlinks.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "core", }, } diff --git a/IkiWiki/Plugin/passwordauth.pm b/IkiWiki/Plugin/passwordauth.pm index 8cf5af51e..4848b47bb 100644 --- a/IkiWiki/Plugin/passwordauth.pm +++ b/IkiWiki/Plugin/passwordauth.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, account_creation_password => { type => "string", @@ -104,11 +105,11 @@ sub formbuilder_setup (@) { my $session=$params{session}; my $cgi=$params{cgi}; - if ($form->title eq "signin" || $form->title eq "register") { + if ($form->title eq "signin" || $form->title eq "register" || $cgi->param("do") eq "register") { $form->field(name => "name", required => 0); $form->field(name => "password", type => "password", required => 0); - if ($form->submitted eq "Register" || $form->submitted eq "Create Account") { + if ($form->submitted eq "Register" || $form->submitted eq "Create Account" || $cgi->param("do") eq "register") { $form->field(name => "confirm_password", type => "password"); $form->field(name => "account_creation_password", type => "password") if (defined $config{account_creation_password} && @@ -207,19 +208,34 @@ sub formbuilder_setup (@) { } } elsif ($form->title eq "preferences") { - $form->field(name => "name", disabled => 1, - value => $session->param("name"), force => 1, - fieldset => "login"); - $form->field(name => "password", type => "password", - fieldset => "login", - validate => sub { - shift eq $form->field("confirm_password"); - }), - $form->field(name => "confirm_password", type => "password", - fieldset => "login", - validate => sub { - shift eq $form->field("password"); - }), + my $user=$session->param("name"); + if (! IkiWiki::openiduser($user)) { + $form->field(name => "name", disabled => 1, + value => $user, force => 1, + fieldset => "login"); + $form->field(name => "password", type => "password", + fieldset => "login", + validate => sub { + shift eq $form->field("confirm_password"); + }); + $form->field(name => "confirm_password", type => "password", + fieldset => "login", + validate => sub { + shift eq $form->field("password"); + }); + + my $userpage=IkiWiki::userpage($user); + if (exists $pagesources{$userpage}) { + $form->text(gettext("Your user page: "). + htmllink("", "", $userpage, + noimageinline => 1)); + } + else { + $form->text("<a href=\"". + IkiWiki::cgiurl(do => "edit", page => $userpage). + "\">".gettext("Create your user page")."</a>"); + } + } } } @@ -232,7 +248,7 @@ sub formbuilder (@) { my $buttons=$params{buttons}; if ($form->title eq "signin" || $form->title eq "register") { - if ($form->submitted && $form->validate) { + if (($form->submitted && $form->validate) || $cgi->param("do") eq "register") { if ($form->submitted eq 'Login') { $session->param("name", $form->field("name")); IkiWiki::cgi_postsignin($cgi, $session); @@ -295,7 +311,7 @@ sub formbuilder (@) { $form->field(name => "name", required => 0); push @$buttons, "Reset Password"; } - elsif ($form->submitted eq "Register") { + elsif ($form->submitted eq "Register" || $cgi->param("do") eq "register") { @$buttons="Create Account"; } } @@ -336,6 +352,14 @@ sub sessioncgi ($$) { IkiWiki::cgi_prefs($q, $session); exit; } + elsif ($q->param("do") eq "register") { + # After registration, need to go somewhere, so show prefs page. + $session->param(postsignin => "do=prefs"); + # Due to do=register, this will run in registration-only + # mode. + IkiWiki::cgi_signin($q, $session); + exit; + } } sub auth ($$) { diff --git a/IkiWiki/Plugin/po.pm b/IkiWiki/Plugin/po.pm index f8801185e..759e812e6 100644 --- a/IkiWiki/Plugin/po.pm +++ b/IkiWiki/Plugin/po.pm @@ -85,7 +85,8 @@ sub getsetup () { return plugin => { safe => 0, - rebuild => 1, + rebuild => 1, # format plugin + section => "format", }, po_master_language => { type => "string", @@ -132,6 +133,7 @@ sub checkconfig () { $field, 'po')); } } + delete $config{po_slave_languages}{$config{po_master_language}{code}};; map { islanguagecode($_) @@ -173,7 +175,8 @@ sub checkconfig () { if ($config{po_master_language}{code} ne 'en') { # Add underlay containing translated source files # for the master language. - add_underlay("locale/$config{po_master_language}{code}/$underlay"); + add_underlay("locale/$config{po_master_language}{code}/$underlay") + if -d "$config{underlaydirbase}/locale/$config{po_master_language}{code}/$underlay"; } } } @@ -307,7 +310,7 @@ sub pagetemplate (@) { if (ishomepage($page) && $template->query(name => "title")) { $template->param(title => $config{wikiname}); } -} # }}} +} # Add the renamed page translations to the list of to-be-renamed pages. sub renamepages (@) { diff --git a/IkiWiki/Plugin/poll.pm b/IkiWiki/Plugin/poll.pm index bc1e3501e..6bc4579c2 100644 --- a/IkiWiki/Plugin/poll.pm +++ b/IkiWiki/Plugin/poll.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/polygen.pm b/IkiWiki/Plugin/polygen.pm index bc21d71c7..78e3611e1 100644 --- a/IkiWiki/Plugin/polygen.pm +++ b/IkiWiki/Plugin/polygen.pm @@ -20,6 +20,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/postsparkline.pm b/IkiWiki/Plugin/postsparkline.pm index 0d5a12e33..2fae9c5fe 100644 --- a/IkiWiki/Plugin/postsparkline.pm +++ b/IkiWiki/Plugin/postsparkline.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/progress.pm b/IkiWiki/Plugin/progress.pm index fe64b40b1..d27df5ca8 100644 --- a/IkiWiki/Plugin/progress.pm +++ b/IkiWiki/Plugin/progress.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/rawhtml.pm b/IkiWiki/Plugin/rawhtml.pm index ad8a610c1..0838bcb22 100644 --- a/IkiWiki/Plugin/rawhtml.pm +++ b/IkiWiki/Plugin/rawhtml.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # changes file types + section => "format", }, } diff --git a/IkiWiki/Plugin/recentchanges.pm b/IkiWiki/Plugin/recentchanges.pm index fa851e466..5c7b71aaa 100644 --- a/IkiWiki/Plugin/recentchanges.pm +++ b/IkiWiki/Plugin/recentchanges.pm @@ -124,7 +124,7 @@ sub store ($$$) { elsif (length $config{cgiurl}) { $change->{authorurl} = IkiWiki::cgiurl( do => "goto", - page => (length $config{userdir} ? "$config{userdir}/" : "").$change->{author}, + page => IkiWiki::userpage($change->{author}), ); } diff --git a/IkiWiki/Plugin/relativedate.pm b/IkiWiki/Plugin/relativedate.pm index 06df2efd5..7f006af83 100644 --- a/IkiWiki/Plugin/relativedate.pm +++ b/IkiWiki/Plugin/relativedate.pm @@ -5,7 +5,7 @@ use warnings; no warnings 'redefine'; use strict; use IkiWiki 3.00; -use POSIX; +use POSIX (); use Encode; sub import { diff --git a/IkiWiki/Plugin/remove.pm b/IkiWiki/Plugin/remove.pm index 3c1e0c713..0fc180f69 100644 --- a/IkiWiki/Plugin/remove.pm +++ b/IkiWiki/Plugin/remove.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "web", }, } @@ -48,7 +49,7 @@ sub check_canremove ($$$) { # This is sorta overkill, but better safe than sorry. if (! defined pagetype($pagesources{$page})) { if (IkiWiki::Plugin::attachment->can("check_canattach")) { - IkiWiki::Plugin::attachment::check_canattach($session, $page, $file); + IkiWiki::Plugin::attachment::check_canattach($session, $page, "$config{srcdir}/$file"); } else { error("removal of attachments is not allowed"); diff --git a/IkiWiki/Plugin/rename.pm b/IkiWiki/Plugin/rename.pm index 8213d21f6..1a9da6363 100644 --- a/IkiWiki/Plugin/rename.pm +++ b/IkiWiki/Plugin/rename.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "web", }, } @@ -49,7 +50,7 @@ sub check_canrename ($$$$$$) { IkiWiki::check_canedit($src, $q, $session); if ($attachment) { if (IkiWiki::Plugin::attachment->can("check_canattach")) { - IkiWiki::Plugin::attachment::check_canattach($session, $src, $srcfile); + IkiWiki::Plugin::attachment::check_canattach($session, $src, "$config{srcdir}/$srcfile"); } else { error("renaming of attachments is not allowed"); @@ -84,7 +85,7 @@ sub check_canrename ($$$$$$) { if ($attachment) { # Note that $srcfile is used here, not $destfile, # because it wants the current file, to check it. - IkiWiki::Plugin::attachment::check_canattach($session, $dest, $srcfile); + IkiWiki::Plugin::attachment::check_canattach($session, $dest, "$config{srcdir}/$srcfile"); } } diff --git a/IkiWiki/Plugin/repolist.pm b/IkiWiki/Plugin/repolist.pm index f69ec3988..ba7c5f0aa 100644 --- a/IkiWiki/Plugin/repolist.pm +++ b/IkiWiki/Plugin/repolist.pm @@ -15,6 +15,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "web", }, repositories => { type => "string", diff --git a/IkiWiki/Plugin/search.pm b/IkiWiki/Plugin/search.pm index 393c17e0f..c0e8703d8 100644 --- a/IkiWiki/Plugin/search.pm +++ b/IkiWiki/Plugin/search.pm @@ -20,6 +20,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, + section => "web", }, omega_cgi => { type => "string", @@ -109,6 +110,7 @@ sub index (@) { # data used by omega # Decode html entities in it, since omega re-encodes them. eval q{use HTML::Entities}; + error $@ if $@; $doc->set_data( "url=".urlto($params{page}, "")."\n". "sample=".decode_entities($sample)."\n". @@ -213,9 +215,19 @@ sub setupfiles () { writefile("omega.conf", $config{wikistatedir}."/xapian", "database_dir .\n". "template_dir ./templates\n"); + + # Avoid omega interpreting anything in the misctemplate + # as an omegascript command. + my $misctemplate=IkiWiki::misctemplate(gettext("search"), "\0"); + eval q{use HTML::Entities}; + error $@ if $@; + $misctemplate=encode_entities($misctemplate, '\$'); + + my $querytemplate=readfile(IkiWiki::template_file("searchquery.tmpl")); + $misctemplate=~s/\0/$querytemplate/; + writefile("query", $config{wikistatedir}."/xapian/templates", - IkiWiki::misctemplate(gettext("search"), - readfile(IkiWiki::template_file("searchquery.tmpl")))); + $misctemplate); $setup=1; } } diff --git a/IkiWiki/Plugin/shortcut.pm b/IkiWiki/Plugin/shortcut.pm index 1840a5722..0cedbe447 100644 --- a/IkiWiki/Plugin/shortcut.pm +++ b/IkiWiki/Plugin/shortcut.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/sidebar.pm b/IkiWiki/Plugin/sidebar.pm index 41812e1c1..0350f2b71 100644 --- a/IkiWiki/Plugin/sidebar.pm +++ b/IkiWiki/Plugin/sidebar.pm @@ -10,6 +10,7 @@ use IkiWiki 3.00; sub import { hook(type => "getsetup", id => "sidebar", call => \&getsetup); + hook(type => "preprocess", id => "sidebar", call => \&preprocess); hook(type => "pagetemplate", id => "sidebar", call => \&pagetemplate); } @@ -19,11 +20,51 @@ sub getsetup () { safe => 1, rebuild => 1, }, + global_sidebars => { + type => "boolean", + examples => 1, + description => "show sidebar page on all pages?", + safe => 1, + rebuild => 1, + }, +} + +my %pagesidebar; + +sub preprocess (@) { + my %params=@_; + + my $page=$params{page}; + return "" unless $page eq $params{destpage}; + + if (! defined $params{content}) { + $pagesidebar{$page}=undef; + } + else { + my $file = $pagesources{$page}; + my $type = pagetype($file); + + $pagesidebar{$page}= + IkiWiki::htmlize($page, $page, $type, + IkiWiki::linkify($page, $page, + IkiWiki::preprocess($page, $page, + IkiWiki::filter($page, $page, $params{content})))); + } + + return ""; } +my $oldfile; +my $oldcontent; + sub sidebar_content ($) { my $page=shift; + return delete $pagesidebar{$page} if defined $pagesidebar{$page}; + + return if ! exists $pagesidebar{$page} && + defined $config{global_sidebars} && ! $config{global_sidebars}; + my $sidebar_page=bestlink($page, "sidebar") || return; my $sidebar_file=$pagesources{$sidebar_page} || return; my $sidebar_type=pagetype($sidebar_file); @@ -34,7 +75,16 @@ sub sidebar_content ($) { # currently requires a wiki rebuild. add_depends($page, $sidebar_page); - my $content=readfile(srcfile($sidebar_file)); + my $content; + if (defined $oldfile && $sidebar_file eq $oldfile) { + $content=$oldcontent; + } + else { + $content=readfile(srcfile($sidebar_file)); + $oldcontent=$content; + $oldfile=$sidebar_file; + } + return unless length $content; return IkiWiki::htmlize($sidebar_page, $page, $sidebar_type, IkiWiki::linkify($sidebar_page, $page, @@ -47,11 +97,10 @@ sub sidebar_content ($) { sub pagetemplate (@) { my %params=@_; - my $page=$params{page}; my $template=$params{template}; - - if ($template->query(name => "sidebar")) { - my $content=sidebar_content($page); + if ($params{destpage} eq $params{page} && + $template->query(name => "sidebar")) { + my $content=sidebar_content($params{destpage}); if (defined $content && length $content) { $template->param(sidebar => $content); } diff --git a/IkiWiki/Plugin/signinedit.pm b/IkiWiki/Plugin/signinedit.pm index 8b44a68f7..31160c02f 100644 --- a/IkiWiki/Plugin/signinedit.pm +++ b/IkiWiki/Plugin/signinedit.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "auth", }, } diff --git a/IkiWiki/Plugin/sortnaturally.pm b/IkiWiki/Plugin/sortnaturally.pm new file mode 100644 index 000000000..62e42767c --- /dev/null +++ b/IkiWiki/Plugin/sortnaturally.pm @@ -0,0 +1,32 @@ +#!/usr/bin/perl +# Sort::Naturally-powered title_natural sort order for IkiWiki +package IkiWiki::Plugin::sortnaturally; + +use IkiWiki 3.00; +no warnings; + +sub import { + hook(type => "getsetup", id => "sortnaturally", call => \&getsetup); +} + +sub getsetup { + return + plugin => { + safe => 1, + rebuild => undef, + }, +} + +sub checkconfig () { + eval q{use Sort::Naturally}; + error $@ if $@; +} + +package IkiWiki::SortSpec; + +sub cmp_title_natural { + Sort::Naturally::ncmp(IkiWiki::pagetitle(IkiWiki::basename($a)), + IkiWiki::pagetitle(IkiWiki::basename($b))) +} + +1; diff --git a/IkiWiki/Plugin/sparkline.pm b/IkiWiki/Plugin/sparkline.pm index fb4849492..42665ac63 100644 --- a/IkiWiki/Plugin/sparkline.pm +++ b/IkiWiki/Plugin/sparkline.pm @@ -24,6 +24,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/svn.pm b/IkiWiki/Plugin/svn.pm index 06b987f51..d10b4888d 100644 --- a/IkiWiki/Plugin/svn.pm +++ b/IkiWiki/Plugin/svn.pm @@ -19,6 +19,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub checkconfig () { @@ -44,6 +45,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, svnrepo => { type => "string", @@ -348,9 +350,18 @@ sub rcs_diff ($) { return `svnlook diff $config{svnrepo} -r$rev --no-diff-deleted`; } -sub rcs_getctime ($) { +{ + +my ($lastfile, $lastmtime, $lastctime); + +sub findtimes ($) { my $file=shift; + if (defined $lastfile && $lastfile eq $file) { + return $lastmtime, $lastctime; + } + $lastfile=$file; + my $svn_log_infoline=qr/^r\d+\s+\|\s+[^\s]+\s+\|\s+(\d+-\d+-\d+\s+\d+:\d+:\d+\s+[-+]?\d+).*/; my $child = open(SVNLOG, "-|"); @@ -358,24 +369,39 @@ sub rcs_getctime ($) { exec("svn", "log", $file) || error("svn log $file failed to run"); } - my $date; + my ($cdate, $mdate); while (<SVNLOG>) { if (/$svn_log_infoline/) { - $date=$1; + $cdate=$1; + $mdate=$1 unless defined $mdate; } } - close SVNLOG || warn "svn log $file exited $?"; + close SVNLOG || error "svn log $file exited $?"; - if (! defined $date) { - warn "failed to parse svn log for $file\n"; - return 0; + if (! defined $cdate) { + error "failed to parse svn log for $file\n"; } eval q{use Date::Parse}; error($@) if $@; - $date=str2time($date); - debug("found ctime ".localtime($date)." for $file"); - return $date; + + $lastctime=str2time($cdate); + $lastmtime=str2time($mdate); + return $lastmtime, $lastctime; +} + +} + +sub rcs_getctime ($) { + my $file=shift; + + return (findtimes($file))[1]; +} + +sub rcs_getmtime ($) { + my $file=shift; + + return (findtimes($file))[0]; } 1 diff --git a/IkiWiki/Plugin/table.pm b/IkiWiki/Plugin/table.pm index 96d63f455..2edd1eacd 100644 --- a/IkiWiki/Plugin/table.pm +++ b/IkiWiki/Plugin/table.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/tag.pm b/IkiWiki/Plugin/tag.pm index fdd63d637..9e6f417bf 100644 --- a/IkiWiki/Plugin/tag.pm +++ b/IkiWiki/Plugin/tag.pm @@ -6,8 +6,6 @@ use warnings; use strict; use IkiWiki 3.00; -my %tags; - sub import { hook(type => "getopt", id => "tag", call => \&getopt); hook(type => "getsetup", id => "tag", call => \&getsetup); @@ -94,13 +92,12 @@ sub preprocess_tag (@) { foreach my $tag (keys %params) { $tag=linkpage($tag); - $tags{$page}{$tag}=1; - + + # hidden WikiLink + add_link($page, tagpage($tag), 'tag'); + # add tagpage if necessary gentag($tag); - - # hidden WikiLink - add_link($page, tagpage($tag)); } return ""; @@ -114,15 +111,13 @@ sub preprocess_taglink (@) { return join(" ", map { if (/(.*)\|(.*)/) { my $tag=linkpage($2); - $tags{$params{page}}{$tag}=1; - add_link($params{page}, tagpage($tag)); + add_link($params{page}, tagpage($tag), 'tag'); return taglink($params{page}, $params{destpage}, $tag, linktext => pagetitle($1)); } else { my $tag=linkpage($_); - $tags{$params{page}}{$tag}=1; - add_link($params{page}, tagpage($tag)); + add_link($params{page}, tagpage($tag), 'tag'); return taglink($params{page}, $params{destpage}, $tag); } } @@ -137,17 +132,19 @@ sub pagetemplate (@) { my $destpage=$params{destpage}; my $template=$params{template}; + my $tags = $typedlinks{$page}{tag}; + $template->param(tags => [ map { link => taglink($page, $destpage, $_, rel => "tag") - }, sort keys %{$tags{$page}} - ]) if exists $tags{$page} && %{$tags{$page}} && $template->query(name => "tags"); + }, sort keys %$tags + ]) if defined $tags && %$tags && $template->query(name => "tags"); if ($template->query(name => "categories")) { # It's an rss/atom template. Add any categories. - if (exists $tags{$page} && %{$tags{$page}}) { + if (defined $tags && %$tags) { $template->param(categories => [map { category => $_ }, - sort keys %{$tags{$page}}]); + sort keys %$tags]); } } } @@ -155,9 +152,7 @@ sub pagetemplate (@) { package IkiWiki::PageSpec; sub match_tagged ($$;@) { - my $page = shift; - my $glob = shift; - return match_link($page, IkiWiki::Plugin::tag::tagpage($glob)); + return match_link($_[0], IkiWiki::Plugin::tag::tagpage($_[1]), linktype => 'tag'); } 1 diff --git a/IkiWiki/Plugin/template.pm b/IkiWiki/Plugin/template.pm index 39d9667f9..98a13b5fa 100644 --- a/IkiWiki/Plugin/template.pm +++ b/IkiWiki/Plugin/template.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } @@ -36,7 +37,13 @@ sub preprocess (@) { my $template_page="templates/$params{id}"; add_depends($params{page}, $template_page); - my $template_file=$pagesources{$template_page}; + my $template_file; + if (exists $pagesources{$template_page}) { + $template_file=srcfile($pagesources{$template_page}); + } + else { + $template_file=IkiWiki::template_file("$params{id}.tmpl") + } return sprintf(gettext("template %s not found"), htmllink($params{page}, $params{destpage}, "/".$template_page)) unless defined $template_file; @@ -49,7 +56,7 @@ sub preprocess (@) { $$text_ref=&Encode::decode_utf8($$text_ref); chomp $$text_ref; }, - filename => srcfile($template_file), + filename => $template_file, die_on_bad_params => 0, no_includes => 1, blind_cache => 1, diff --git a/IkiWiki/Plugin/teximg.pm b/IkiWiki/Plugin/teximg.pm index f92ed0132..521af499f 100644 --- a/IkiWiki/Plugin/teximg.pm +++ b/IkiWiki/Plugin/teximg.pm @@ -8,6 +8,7 @@ use strict; use Digest::MD5 qw(md5_hex); use File::Temp qw(tempdir); use HTML::Entities; +use Encode; use IkiWiki 3.00; my $default_prefix = <<EOPREFIX; @@ -31,6 +32,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, teximg_dvipng => { type => "boolean", @@ -102,7 +104,7 @@ sub create ($$$) { $height = 12; } - my $digest = md5_hex($code, $height); + my $digest = md5_hex(Encode::encode_utf8($code), $height); my $imglink= $params->{page} . "/$digest.png"; my $imglog = $params->{page} . "/$digest.log"; diff --git a/IkiWiki/Plugin/textile.pm b/IkiWiki/Plugin/textile.pm index 8cc5a7951..56bb4bffc 100644 --- a/IkiWiki/Plugin/textile.pm +++ b/IkiWiki/Plugin/textile.pm @@ -19,6 +19,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, } diff --git a/IkiWiki/Plugin/tla.pm b/IkiWiki/Plugin/tla.pm index f4b20a6ec..f5ad0cc96 100644 --- a/IkiWiki/Plugin/tla.pm +++ b/IkiWiki/Plugin/tla.pm @@ -18,6 +18,7 @@ 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_getmtime", call => \&rcs_getmtime); } sub checkconfig () { @@ -34,6 +35,7 @@ sub getsetup () { plugin => { safe => 0, # rcs plugin rebuild => undef, + section => "rcs", }, tla_wrapper => { type => "string", @@ -161,7 +163,7 @@ sub rcs_remove ($) { error("rcs_remove not implemented for tla"); # TODO } -sub rcs_rename ($$) { # {{{a +sub rcs_rename ($$) { my ($src, $dest) = @_; error("rcs_rename not implemented for tla"); # TODO @@ -283,4 +285,8 @@ sub rcs_getctime ($) { return $date; } +sub rcs_getmtime ($) { + error "rcs_getmtime is not implemented for tla\n"; # TODO +} + 1 diff --git a/IkiWiki/Plugin/toc.pm b/IkiWiki/Plugin/toc.pm index b8537d3eb..ac07b9af6 100644 --- a/IkiWiki/Plugin/toc.pm +++ b/IkiWiki/Plugin/toc.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/toggle.pm b/IkiWiki/Plugin/toggle.pm index ef066a42f..f9c899540 100644 --- a/IkiWiki/Plugin/toggle.pm +++ b/IkiWiki/Plugin/toggle.pm @@ -20,6 +20,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/txt.pm b/IkiWiki/Plugin/txt.pm index 8599bdc8e..0d9a0b35b 100644 --- a/IkiWiki/Plugin/txt.pm +++ b/IkiWiki/Plugin/txt.pm @@ -29,6 +29,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 1, # format plugin + section => "format", }, } @@ -38,7 +39,14 @@ sub filter (@) { my %params = @_; my $content = $params{content}; - if (defined $pagesources{$params{page}} && $pagesources{$params{page}} =~ /\.txt$/) { + if (defined $pagesources{$params{page}} && + $pagesources{$params{page}} =~ /\.txt$/) { + if ($pagesources{$params{page}} eq 'robots.txt' && + $params{page} eq $params{destpage}) { + will_render($params{page}, 'robots.txt'); + writefile('robots.txt', $config{destdir}, $content); + } + encode_entities($content, "<>&"); if ($findurl) { my $finder = URI::Find->new(sub { diff --git a/IkiWiki/Plugin/typography.pm b/IkiWiki/Plugin/typography.pm index f62be82bb..9389b24d4 100644 --- a/IkiWiki/Plugin/typography.pm +++ b/IkiWiki/Plugin/typography.pm @@ -9,7 +9,7 @@ use IkiWiki 3.00; sub import { hook(type => "getopt", id => "typography", call => \&getopt); hook(type => "getsetup", id => "typography", call => \&getsetup); - IkiWiki::hook(type => "sanitize", id => "typography", call => \&sanitize); + hook(type => "sanitize", id => "typography", call => \&sanitize); } sub getopt () { diff --git a/IkiWiki/Plugin/version.pm b/IkiWiki/Plugin/version.pm index 587cd55fa..c13643478 100644 --- a/IkiWiki/Plugin/version.pm +++ b/IkiWiki/Plugin/version.pm @@ -17,6 +17,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => undef, + section => "widget", }, } diff --git a/IkiWiki/Plugin/websetup.pm b/IkiWiki/Plugin/websetup.pm index e477bcc20..d444c0a3d 100644 --- a/IkiWiki/Plugin/websetup.pm +++ b/IkiWiki/Plugin/websetup.pm @@ -18,6 +18,7 @@ sub getsetup () { plugin => { safe => 1, rebuild => 0, + section => "web", }, websetup_force_plugins => { type => "string", @@ -26,6 +27,13 @@ sub getsetup () { safe => 0, rebuild => 0, }, + websetup_unsafe => { + type => "string", + example => [], + description => "list of additional setup field keys to treat as unsafe", + safe => 0, + rebuild => 0, + }, websetup_show_unsafe => { type => "boolean", example => 1, @@ -56,6 +64,12 @@ sub formatexample ($$) { } } +sub issafe ($) { + my $key=shift; + + return ! grep { $_ eq $key } @{$config{websetup_unsafe}}; +} + sub showfields ($$$@) { my $form=shift; my $plugin=shift; @@ -66,27 +80,30 @@ sub showfields ($$$@) { while (@_) { my $key=shift; my %info=%{shift()}; + + if ($key eq 'plugin') { + %plugininfo=%info; + next; + } # skip internal settings next if defined $info{type} && $info{type} eq "internal"; # XXX hashes not handled yet next if ref $config{$key} && ref $config{$key} eq 'HASH' || ref $info{example} eq 'HASH'; # maybe skip unsafe settings - next if ! $info{safe} && ! ($config{websetup_show_unsafe} && $config{websetup_advanced}); + next if ! ($config{websetup_show_unsafe} && $config{websetup_advanced}) && + (! $info{safe} || ! issafe($key)); # maybe skip advanced settings next if $info{advanced} && ! $config{websetup_advanced}; # these are handled specially, so don't show next if $key eq 'add_plugins' || $key eq 'disable_plugins'; - if ($key eq 'plugin') { - %plugininfo=%info; - next; - } - push @show, $key, \%info; } - my $section=defined $plugin ? $plugin." ".gettext("plugin") : "main"; + my $section=defined $plugin + ? sprintf(gettext("%s plugin:"), $plugininfo{section}).$plugin + : "main"; my %enabledfields; my $shownfields=0; @@ -97,6 +114,16 @@ sub showfields ($$$@) { @show=(); } + my $section_fieldset; + if (defined $plugin) { + # Define the combined fieldset for the plugin's section. + # This ensures that this fieldset comes first. + $section_fieldset=sprintf(gettext("%s plugins"), $plugininfo{section}); + $form->field(name => "placeholder.$plugininfo{section}", + type => "hidden", + fieldset => $section_fieldset); + } + # show plugin toggle if (defined $plugin && (! $plugin_forced || $config{websetup_advanced})) { my $name="enable.$plugin"; @@ -137,10 +164,13 @@ sub showfields ($$$@) { my $name=defined $plugin ? $plugin.".".$key : $section.".".$key; my $value=$config{$key}; + if (! defined $value) { + $value=""; + } - if ($info{safe} && (ref $value eq 'ARRAY' || ref $info{example} eq 'ARRAY')) { - $value=[(ref $value eq 'ARRAY' ? map { Encode::encode_utf8($_) } @{$value} : ""), - "", ""]; # blank items for expansion + if (ref $value eq 'ARRAY' || ref $info{example} eq 'ARRAY') { + $value=[(ref $value eq 'ARRAY' ? map { Encode::encode_utf8($_) } @{$value} : "")]; + push @$value, "", "" if $info{safe} && issafe($key); # blank items for expansion } else { $value=Encode::encode_utf8($value); @@ -194,7 +224,7 @@ sub showfields ($$$@) { } } - if (! $info{safe}) { + if (! $info{safe} || ! issafe($key)) { $form->field(name => $name, disabled => 1); } else { @@ -203,11 +233,11 @@ sub showfields ($$$@) { $shownfields++; } - # if no fields were shown for the plugin, drop it into the - # plugins fieldset + # if no fields were shown for the plugin, drop it into a combined + # fieldset for its section if (defined $plugin && (! $plugin_forced || $config{websetup_advanced}) && ! $shownfields) { - $form->field(name => "enable.$plugin", fieldset => "plugins"); + $form->field(name => "enable.$plugin", fieldset => $section_fieldset); } return %enabledfields; @@ -258,7 +288,6 @@ sub showform ($$) { params => $cgi, fieldsets => [ [main => gettext("main")], - [plugins => gettext("plugins")] ], action => $config{cgiurl}, template => {type => 'div'}, @@ -331,7 +360,7 @@ sub showform ($$) { @value=0; } - if (! $info{safe}) { + if (! $info{safe} || ! issafe($key)) { error("unsafe field $key"); # should never happen } diff --git a/IkiWiki/Plugin/wikitext.pm b/IkiWiki/Plugin/wikitext.pm index accb03bbe..b24630b15 100644 --- a/IkiWiki/Plugin/wikitext.pm +++ b/IkiWiki/Plugin/wikitext.pm @@ -16,6 +16,7 @@ sub getsetup () { plugin => { safe => 0, # format plugin rebuild => undef, + section => "format", }, } diff --git a/IkiWiki/Plugin/wmd.pm b/IkiWiki/Plugin/wmd.pm index 9ddd237ab..99b136281 100644 --- a/IkiWiki/Plugin/wmd.pm +++ b/IkiWiki/Plugin/wmd.pm @@ -4,7 +4,6 @@ package IkiWiki::Plugin::wmd; use warnings; use strict; use IkiWiki 3.00; -use POSIX; use Encode; sub import { @@ -17,6 +16,8 @@ sub getsetup () { return plugin => { safe => 1, + rebuild => 0, + section => "web", }, } diff --git a/IkiWiki/Render.pm b/IkiWiki/Render.pm index 0c21455fb..796af6af2 100644 --- a/IkiWiki/Render.pm +++ b/IkiWiki/Render.pm @@ -167,6 +167,7 @@ sub scan ($) { else { $links{$page}=[]; } + delete $typedlinks{$page}; run_hooks(scan => sub { shift->( @@ -346,6 +347,8 @@ sub find_new_files ($) { my @new; my @internal_new; + my $times_noted; + foreach my $file (@$files) { my $page=pagename($file); if (exists $pagesources{$page} && $pagesources{$page} ne $file) { @@ -357,16 +360,33 @@ sub find_new_files ($) { if (isinternal($page)) { push @internal_new, $file; } - else { + elsif ($config{rcs}) { + if (! $times_noted) { + debug(sprintf(gettext("querying %s for file creation and modification times.."), $config{rcs})); + $times_noted=1; + } + push @new, $file; - if ($config{getctime} && -e "$config{srcdir}/$file") { + if ($config{gettime} && -e "$config{srcdir}/$file") { eval { - my $time=rcs_getctime("$config{srcdir}/$file"); - $pagectime{$page}=$time; + my $ctime=rcs_getctime("$config{srcdir}/$file"); + if ($ctime > 0) { + $pagectime{$page}=$ctime; + } }; if ($@) { print STDERR $@; } + my $mtime; + eval { + $mtime=rcs_getmtime("$config{srcdir}/$file"); + }; + if ($@) { + print STDERR $@; + } + elsif ($mtime > 0) { + utime($mtime, $mtime, "$config{srcdir}/$file"); + } } } $pagecase{lc $page}=$page; @@ -393,6 +413,7 @@ sub find_del_files ($) { push @del, $pagesources{$page}; } $links{$page}=[]; + delete $typedlinks{$page}; $renderedfiles{$page}=[]; $pagemtime{$page}=0; } @@ -494,6 +515,29 @@ sub remove_unrendered () { } } +sub link_types_changed ($$) { + # each is of the form { type => { link => 1 } } + my $new = shift; + my $old = shift; + + return 0 if !defined $new && !defined $old; + return 1 if !defined $new || !defined $old; + + while (my ($type, $links) = each %$new) { + foreach my $link (keys %$links) { + return 1 unless exists $old->{$type}{$link}; + } + } + + while (my ($type, $links) = each %$old) { + foreach my $link (keys %$links) { + return 1 unless exists $new->{$type}{$link}; + } + } + + return 0; +} + sub calculate_changed_links ($$$) { my ($changed, $del, $oldlink_targets)=@_; @@ -520,6 +564,14 @@ sub calculate_changed_links ($$$) { } $linkchangers{lc($page)}=1; } + + # we currently assume that changing the type of a link doesn't + # change backlinks + if (!exists $linkchangers{lc($page)}) { + if (link_types_changed($typedlinks{$page}, $oldtypedlinks{$page})) { + $linkchangers{lc($page)}=1; + } + } } return \%backlinkchanged, \%linkchangers; @@ -560,7 +612,7 @@ sub render_dependent ($$$$$$$) { if (exists $depends{$p} && ! defined $reason) { foreach my $dep (keys %{$depends{$p}}) { my $sub=pagespec_translate($dep); - next if $@ || ! defined $sub; + next unless defined $sub; # only consider internal files # if the page explicitly depends @@ -695,6 +747,17 @@ sub refresh () { } } +sub clean_rendered { + lockwiki(); + loadindex(); + remove_unrendered(); + foreach my $page (keys %oldrenderedfiles) { + foreach my $file (@{$oldrenderedfiles{$page}}) { + prune($config{destdir}."/".$file); + } + } +} + sub commandline_render () { lockwiki(); loadindex(); diff --git a/IkiWiki/Setup.pm b/IkiWiki/Setup.pm index 8a25ecc57..06102058b 100644 --- a/IkiWiki/Setup.pm +++ b/IkiWiki/Setup.pm @@ -1,6 +1,8 @@ #!/usr/bin/perl -# Ikiwiki setup files are perl files that 'use IkiWiki::Setup::foo', -# passing it some sort of configuration data. +# Ikiwiki setup files can be perl files that 'use IkiWiki::Setup::foo', +# passing it some sort of configuration data. Or, they can contain +# the module name at the top, without the 'use', and the whole file is +# then fed into that module. package IkiWiki::Setup; @@ -10,24 +12,58 @@ use IkiWiki; use open qw{:utf8 :std}; use File::Spec; -sub load ($) { - my $setup=IkiWiki::possibly_foolish_untaint(shift); - $config{setupfile}=File::Spec->rel2abs($setup); +sub load ($;$) { + my $file=IkiWiki::possibly_foolish_untaint(shift); + my $safemode=shift; + + $config{setupfile}=File::Spec->rel2abs($file); #translators: The first parameter is a filename, and the second #translators: is a (probably not translated) error message. - open (IN, $setup) || error(sprintf(gettext("cannot read %s: %s"), $setup, $!)); - my $code; + open (IN, $file) || error(sprintf(gettext("cannot read %s: %s"), $file, $!)); + my $content; { local $/=undef; - $code=<IN> || error("$setup: $!"); + $content=<IN> || error("$file: $!"); } - - ($code)=$code=~/(.*)/s; close IN; - eval $code; - error("$setup: ".$@) if $@; + if ($content=~/((?:use|require)\s+)?IkiWiki::Setup::(\w+)/) { + $config{setuptype}=$2; + if ($1) { + error sprintf(gettext("cannot load %s in safe mode"), $file) + if $safemode; + eval IkiWiki::possibly_foolish_untaint($content); + error("$file: ".$@) if $@; + } + else { + eval qq{require IkiWiki::Setup::$config{setuptype}}; + error $@ if $@; + "IkiWiki::Setup::$config{setuptype}"->loaddump(IkiWiki::possibly_foolish_untaint($content)); + } + } + else { + error sprintf(gettext("failed to parse %s"), $file); + } +} + +sub dump ($) { + my $file=IkiWiki::possibly_foolish_untaint(shift); + + eval qq{require IkiWiki::Setup::$config{setuptype}}; + error $@ if $@; + my @dump="IkiWiki::Setup::$config{setuptype}"->gendump( + "Setup file for ikiwiki.", + "", + "Passing this to ikiwiki --setup will make ikiwiki generate", + "wrappers and build the wiki.", + "", + "Remember to re-run ikiwiki --setup any time you edit this file.", + ); + + open (OUT, ">", $file) || die "$file: $!"; + print OUT "$_\n" foreach @dump; + close OUT; } sub merge ($) { @@ -77,7 +113,6 @@ sub merge ($) { sub getsetup () { # Gets all available setup data from all plugins. Returns an # ordered list of [plugin, setup] pairs. - my @ret; # disable logging to syslog while dumping, broken plugins may # whine when loaded @@ -85,38 +120,116 @@ sub getsetup () { $config{syslog}=undef; # Load all plugins, so that all setup options are available. - my @plugins=grep { $_ ne $config{rcs} } sort(IkiWiki::listplugins()); - unshift @plugins, $config{rcs} if $config{rcs}; # rcs plugin 1st + my @plugins=IkiWiki::listplugins(); foreach my $plugin (@plugins) { eval { IkiWiki::loadplugin($plugin) }; if (exists $IkiWiki::hooks{checkconfig}{$plugin}{call}) { my @s=eval { $IkiWiki::hooks{checkconfig}{$plugin}{call}->() }; } } - + + my %sections; foreach my $plugin (@plugins) { if (exists $IkiWiki::hooks{getsetup}{$plugin}{call}) { # use an array rather than a hash, to preserve order my @s=eval { $IkiWiki::hooks{getsetup}{$plugin}{call}->() }; next unless @s; - push @ret, [ $plugin, \@s ], + + # set default section value (note use of shared + # hashref between array and hash) + my %s=@s; + if (! exists $s{plugin} || ! $s{plugin}->{section}) { + $s{plugin}->{section}="other"; + } + + # only the selected rcs plugin is included + if ($config{rcs} && $plugin eq $config{rcs}) { + $s{plugin}->{section}="core"; + } + elsif ($s{plugin}->{section} eq "rcs") { + next; + } + + push @{$sections{$s{plugin}->{section}}}, [ $plugin, \@s ]; } } $config{syslog}=$syslog; - return @ret; + return map { sort { $a->[0] cmp $b->[0] } @{$sections{$_}} } + sort { # core first, other last, otherwise alphabetical + ($b eq "core") <=> ($a eq "core") + || + ($a eq "other") <=> ($b eq "other") + || + $a cmp $b + } keys %sections; } -sub dump ($) { - my $file=IkiWiki::possibly_foolish_untaint(shift); +sub commented_dump ($$) { + my $dumpline=shift; + my $indent=shift; + + my %setup=(%config); + my @ret; - require IkiWiki::Setup::Standard; - my @dump=IkiWiki::Setup::Standard::gendump("Setup file for ikiwiki."); + # disable logging to syslog while dumping + $config{syslog}=undef; - open (OUT, ">", $file) || die "$file: $!"; - print OUT "$_\n" foreach @dump; - close OUT; + eval q{use Text::Wrap}; + die $@ if $@; + + my %section_plugins; + push @ret, commented_dumpvalues($dumpline, $indent, \%setup, IkiWiki::getsetup()); + foreach my $pair (IkiWiki::Setup::getsetup()) { + my $plugin=$pair->[0]; + my $setup=$pair->[1]; + my %s=@{$setup}; + my $section=$s{plugin}->{section}; + push @{$section_plugins{$section}}, $plugin; + if (@{$section_plugins{$section}} == 1) { + push @ret, "", $indent.("#" x 70), "$indent# $section plugins", + sub { + wrap("$indent# (", "$indent# ", + join(", ", @{$section_plugins{$section}})).")" + }, + $indent.("#" x 70); + } + + my @values=commented_dumpvalues($dumpline, $indent, \%setup, @{$setup}); + if (@values) { + push @ret, "", "$indent# $plugin plugin", @values; + } + } + + return map { ref $_ ? $_->() : $_ } @ret; +} + +sub commented_dumpvalues ($$$@) { + my $dumpline=shift; + my $indent=shift; + my $setup=shift; + my @ret; + while (@_) { + my $key=shift; + my %info=%{shift()}; + + next if $key eq "plugin" || $info{type} eq "internal"; + + push @ret, "$indent# ".$info{description} if exists $info{description}; + + if (exists $setup->{$key} && defined $setup->{$key}) { + push @ret, $dumpline->($key, $setup->{$key}, $info{type}, ""); + delete $setup->{$key}; + } + elsif (exists $info{example}) { + push @ret, $dumpline->($key, $info{example}, $info{type}, "#"); + } + else { + push @ret, $dumpline->($key, "", $info{type}, "#"); + } + } + return @ret; } 1 diff --git a/IkiWiki/Setup/Automator.pm b/IkiWiki/Setup/Automator.pm index 9fee4dcb7..2dcb424e5 100644 --- a/IkiWiki/Setup/Automator.pm +++ b/IkiWiki/Setup/Automator.pm @@ -38,19 +38,22 @@ sub sanitize_wikiname ($) { sub import (@) { my $this=shift; + $config{setuptype}='Standard'; IkiWiki::Setup::merge({@_}); - # Avoid overwriting any existing files. - foreach my $key (qw{srcdir destdir repository dumpsetup}) { - next unless exists $config{$key}; - my $add=""; - my $dir=IkiWiki::dirname($config{$key})."/"; - my $base=IkiWiki::basename($config{$key}); - while (-e $dir.$add.$base) { - $add=1 if ! $add; - $add++; + if (! $config{force_overwrite}) { + # Avoid overwriting any existing files. + foreach my $key (qw{srcdir destdir repository dumpsetup}) { + next unless exists $config{$key}; + my $add=""; + my $dir=IkiWiki::dirname($config{$key})."/"; + my $base=IkiWiki::basename($config{$key}); + while (-e $dir.$add.$base) { + $add=1 if ! $add; + $add++; + } + $config{$key}=$dir.$add.$base; } - $config{$key}=$dir.$add.$base; } # Set up wrapper @@ -69,9 +72,15 @@ sub import (@) { } elsif ($config{rcs} eq 'bzr') { # TODO + print STDERR "warning: do not know how to set up the bzr_wrapper hook!\n"; } elsif ($config{rcs} eq 'mercurial') { # TODO + print STDERR "warning: do not know how to set up the mercurial_wrapper hook!\n"; + } + elsif ($config{rcs} eq 'tla') { + # TODO + print STDERR "warning: do not know how to set up the tla_wrapper hook!\n"; } elsif ($config{rcs} eq 'cvs') { $config{cvs_wrapper}=$config{repository}."/CVSROOT/post-commit"; @@ -121,9 +130,10 @@ sub import (@) { IkiWiki::run_hooks(checkconfig => sub { shift->() }); }; if ($@) { + my $err=$@; print STDERR sprintf(gettext("** Disabling plugin %s, since it is failing with this message:"), $plugin)."\n"; - print STDERR "$@\n"; + print STDERR "$err\n"; push @{$bakconfig{disable_plugins}}, $plugin; } } @@ -142,7 +152,7 @@ sub import (@) { # Create admin user(s). foreach my $admin (@{$config{adminuser}}) { - next if $admin=~/^http\?:\/\//; # openid + next if defined IkiWiki::openiduser($admin); # Prompt for password w/o echo. my ($password, $password2); diff --git a/IkiWiki/Setup/Standard.pm b/IkiWiki/Setup/Standard.pm index 951bcfc56..c85069304 100644 --- a/IkiWiki/Setup/Standard.pm +++ b/IkiWiki/Setup/Standard.pm @@ -1,7 +1,4 @@ #!/usr/bin/perl -# Standard ikiwiki setup module. -# Parameters to import should be all the standard ikiwiki config stuff, -# plus an array of wrappers to set up. package IkiWiki::Setup::Standard; @@ -9,10 +6,22 @@ use warnings; use strict; use IkiWiki; +# Parameters to import should be all the standard ikiwiki config, in a hash. sub import { IkiWiki::Setup::merge($_[1]); } +sub gendump ($@) { + my $class=shift; + + "#!/usr/bin/perl", + "#", + (map { "# $_" } @_), + "use IkiWiki::Setup::Standard {", + IkiWiki::Setup::commented_dump(\&dumpline, "\t"), + "}" +} + sub dumpline ($$$$) { my $key=shift; my $value=shift; @@ -57,61 +66,4 @@ sub dumpline ($$$$) { return "\t$prefix$key => $dumpedvalue,"; } -sub dumpvalues ($@) { - my $setup=shift; - my @ret; - while (@_) { - my $key=shift; - my %info=%{shift()}; - - next if $key eq "plugin" || $info{type} eq "internal"; - - push @ret, "\t# ".$info{description} if exists $info{description}; - - if (exists $setup->{$key} && defined $setup->{$key}) { - push @ret, dumpline($key, $setup->{$key}, $info{type}, ""); - delete $setup->{$key}; - } - elsif (exists $info{example}) { - push @ret, dumpline($key, $info{example}, $info{type}, "#"); - } - else { - push @ret, dumpline($key, "", $info{type}, "#"); - } - } - return @ret; -} - -sub gendump ($) { - my $description=shift; - my %setup=(%config); - my @ret; - - # disable logging to syslog while dumping - $config{syslog}=undef; - - push @ret, dumpvalues(\%setup, IkiWiki::getsetup()); - foreach my $pair (IkiWiki::Setup::getsetup()) { - my $plugin=$pair->[0]; - my $setup=$pair->[1]; - my @values=dumpvalues(\%setup, @{$setup}); - if (@values) { - push @ret, "", "\t# $plugin plugin", @values; - } - } - - unshift @ret, - "#!/usr/bin/perl", - "# $description", - "#", - "# Passing this to ikiwiki --setup will make ikiwiki generate", - "# wrappers and build the wiki.", - "#", - "# Remember to re-run ikiwiki --setup any time you edit this file.", - "use IkiWiki::Setup::Standard {"; - push @ret, "}"; - - return @ret; -} - 1 diff --git a/IkiWiki/Setup/Yaml.pm b/IkiWiki/Setup/Yaml.pm new file mode 100644 index 000000000..904784728 --- /dev/null +++ b/IkiWiki/Setup/Yaml.pm @@ -0,0 +1,50 @@ +#!/usr/bin/perl + +package IkiWiki::Setup::Yaml; + +use warnings; +use strict; +use IkiWiki; + +sub loaddump ($$) { + my $class=shift; + my $content=shift; + + eval q{use YAML::Any}; + eval q{use YAML} if $@; + die $@ if $@; + $YAML::Syck::ImplicitUnicode=1; + IkiWiki::Setup::merge(Load($content)); +} + +sub gendump ($@) { + my $class=shift; + + "# IkiWiki::Setup::Yaml - YAML formatted setup file", + "#", + (map { "# $_" } @_), + "#", + IkiWiki::Setup::commented_dump(\&dumpline, "") +} + + +sub dumpline ($$$$) { + my $key=shift; + my $value=shift; + my $type=shift; + my $prefix=shift; + + eval q{use YAML::Old}; + eval q{use YAML} if $@; + die $@ if $@; + $YAML::UseHeader=0; + + my $dump=Dump({$key => $value}); + chomp $dump; + if (length $prefix) { + $dump=join("\n", map { $prefix.$_ } split(/\n/, $dump)); + } + return $dump; +} + +1 diff --git a/IkiWiki/Wrapper.pm b/IkiWiki/Wrapper.pm index 5427a5c80..3f3e63ca6 100644 --- a/IkiWiki/Wrapper.pm +++ b/IkiWiki/Wrapper.pm @@ -76,8 +76,8 @@ 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); + char *fd_s=malloc(8); + sprintf(fd_s, "%i", fd); setenv("IKIWIKI_CGILOCK_FD", fd_s, 1); } } @@ -105,7 +105,7 @@ extern char **environ; char *newenviron[$#envsave+6]; int i=0; -addenv(char *var, char *val) { +void addenv(char *var, char *val) { char *s=malloc(strlen(var)+1+strlen(val)+1); if (!s) perror("malloc"); @@ -121,8 +121,18 @@ $check_commit_hook $envsave newenviron[i++]="HOME=$ENV{HOME}"; newenviron[i++]="WRAPPED_OPTIONS=$configstring"; + +#ifdef __TINYC__ + if (clearenv() != 0) { + perror("clearenv"); + exit(1); + } + for (; i>0; i--) + putenv(newenviron[i-1]); +#else newenviron[i]=NULL; environ=newenviron; +#endif if (setregid(getegid(), -1) != 0 && setregid(getegid(), -1) != 0) { |