]> sipb.mit.edu Git - ikiwiki.git/commitdiff
Merge branch 'master' into autoconfig
authorJoey Hess <joey@kodama.kitenet.net>
Sat, 26 Jul 2008 19:49:08 +0000 (15:49 -0400)
committerJoey Hess <joey@kodama.kitenet.net>
Sat, 26 Jul 2008 19:49:08 +0000 (15:49 -0400)
24 files changed:
IkiWiki.pm
IkiWiki/Plugin/aggregate.pm
IkiWiki/Plugin/amazon_s3.pm
IkiWiki/Plugin/anonok.pm
IkiWiki/Plugin/attachment.pm
IkiWiki/Plugin/calendar.pm
IkiWiki/Plugin/graphviz.pm
IkiWiki/Plugin/inline.pm
IkiWiki/Plugin/mdwn.pm
IkiWiki/Plugin/mirrorlist.pm
IkiWiki/Plugin/openid.pm
IkiWiki/Plugin/passwordauth.pm
IkiWiki/Plugin/pinger.pm
IkiWiki/Plugin/prettydate.pm
IkiWiki/Plugin/recentchanges.pm
IkiWiki/Plugin/search.pm
IkiWiki/Plugin/skeleton.pm.example
IkiWiki/Plugin/tag.pm
IkiWiki/Plugin/typography.pm
IkiWiki/Setup.pm
IkiWiki/Setup/Standard.pm
doc/plugins/write.mdwn
doc/usage.mdwn
ikiwiki.in

index 01e7cc1e4c1febee4d84209c281e84a78deb7198..edbec77d62ed55f977f97c6f8b476d06c65c6676 100644 (file)
@@ -32,65 +32,375 @@ memoize("abs2rel");
 memoize("pagespec_translate");
 memoize("file_pruned");
 
+sub getsetup () { #{{{
+       wikiname => {
+               type => "string",
+               default => "wiki",
+               description => "name of the wiki",
+               safe => 1,
+               rebuild => 1,
+       },
+       srcdir => {
+               type => "string",
+               default => undef,
+               example => "$ENV{HOME}/wiki",
+               description => "where the source of the wiki is located",
+               safe => 0, # path
+               rebuild => 1,
+       },
+       destdir => {
+               type => "string",
+               default => undef,
+               example => "/var/www/wiki",
+               description => "where to build the wiki",
+               safe => 0, # path
+               rebuild => 1,
+       },
+       adminuser => {
+               type => "string",
+               default => [],
+               description => "user names of wiki admins",
+               safe => 1,
+               rebuild => 0,
+       },
+       adminemail => {
+               type => "string",
+               default => undef,
+               example => 'me@example.com',
+               description => "contact email for wiki",
+               safe => 1,
+               rebuild => 0,
+       },
+       url => {
+               type => "string",
+               default => '',
+               example => "http://example.com/wiki",
+               description => "base url to the wiki",
+               safe => 1,
+               rebuild => 1,
+       },
+       cgiurl => {
+               type => "string",
+               default => '',
+               examples => "http://example.com/wiki/ikiwiki.cgi",
+               description => "url to the ikiwiki.cgi",
+               safe => 1,
+               rebuild => 1,
+       },
+       rcs => {
+               type => "string",
+               default => '',
+               description => "rcs backend to use",
+               safe => 0, # don't allow overriding
+               rebuild => 0,
+       },
+       historyurl => {
+               type => "string",
+               # TODO should be set per-rcs to allow different
+               # examples and descriptions
+               default => '',
+               example => "XXX",
+               description => "XXX",
+               safe => 1,
+               rebuild => 1,
+       },
+       diffurl => {
+               type => "string",
+               # TODO ditto above
+               default => '',
+               example => "XXX",
+               description => "XXX",
+               safe => 1,
+               rebuild => 1,
+       },
+       discussion => {
+               type => "boolean",
+               default => 1,
+               description => "enable Discussion pages?",
+               safe => 1,
+               rebuild => 1,
+       },
+       svnpath => {
+               # TODO move
+               type => "string",
+               default => "trunk",
+               description => "path inside svn repo where wiki is located",
+               safe => 0, # could expose/overwrite data
+               rebuild => 0,
+       },
+       gitorigin_branch => {
+               type => "string",
+               default => "origin",
+               description => "the git origin to pull from",
+               safe => 0, # paranoia
+               rebuild => 0,
+       },
+       gitmaster_branch => {
+               type => "string",
+               default => "master",
+               description => "the git master branch",
+               safe => 0, # paranoia
+               rebuild => 0,
+       },
+       wrappers => {
+               type => "string",
+               default => undef,
+               description => "definitions of wrappers to generate",
+               safe => 0,
+               rebuild => 0,
+       },
+       wrapper => {
+               type => "internal",
+               default => undef,
+               description => "wrapper filename",
+               safe => 0,
+               rebuild => 0,
+       },
+       wrappermode => {
+               type => "internal",
+               default => undef,
+               description => "mode of wrapper file",
+               safe => 0,
+               rebuild => 0,
+       },
+       templatedir => {
+               type => "string",
+               default => "$installdir/share/ikiwiki/templates",
+               description => "location of template files",
+               safe => 0, # path
+               rebuild => 1,
+       },
+       underlaydir => {
+               type => "string",
+               default => "$installdir/share/ikiwiki/basewiki",
+               description => "base wiki source location",
+               safe => 0, # path
+               rebuild => 0,
+       },
+       underlaydirs => {
+               type => "internal",
+               default => [],
+               description => "additional underlays to use",
+               safe => 0,
+               rebuild => 0,
+       },
+       verbose => {
+               type => "boolean",
+               default => 0,
+               description => "display verbose messages when building?",
+               safe => 1,
+               rebuild => 0,
+       },
+       syslog => {
+               type => "boolean",
+               default => 0,
+               description => "log to syslog?",
+               safe => 1,
+               rebuild => 0,
+       },
+       usedirs => {
+               type => "boolean",
+               default => 1,
+               description => "create output files named page/index.html?",
+               safe => 0, # changing requires manual transition
+               rebuild => 1,
+       },
+       prefix_directives => {
+               type => "boolean",
+               default => 0,
+               description => "use '!'-prefixed preprocessor directives?",
+               safe => 0, # changing requires manual transition
+               rebuild => 1,
+       },
+       default_pageext => {
+               type => "string",
+               default => "mdwn",
+               description => "extension to use for new pages",
+               safe => 0, # not sanitized
+               rebuild => 0,
+       },
+       htmlext => {
+               type => "string",
+               default => "html",
+               description => "extension to use for html files",
+               safe => 0, # not sanitized
+               rebuild => 1,
+       },
+       timeformat => {
+               type => "string",
+               default => '%c',
+               description => "strftime format string to display date",
+               safe => 1,
+               rebuild => 1,
+       },
+       locale => {
+               type => "string",
+               default => undef,
+               example => "en_US.UTF-8",
+               description => "UTF-8 locale to use",
+               safe => 0,
+               rebuild => 1,
+       },
+       sslcookie => {
+               type => "boolean",
+               default => 0,
+               description => "only send cookies over SSL connections?",
+               safe => 1,
+               rebuild => 0,
+       },
+       userdir => {
+               type => "string",
+               default => "",
+               example => "users",
+               description => "put user pages below specified page",
+               safe => 1,
+               rebuild => 1,
+       },
+       numbacklinks => {
+               type => "integer",
+               default => 10,
+               description => "how many backlinks to show before hiding excess (0 to show all)",
+               safe => 1,
+               rebuild => 1,
+       },
+       hardlink => {
+               type => "boolean",
+               default => 0,
+               description => "attempt to hardlink source files? (optimisation for large files)",
+               safe => 0, # paranoia
+               rebuild => 0,
+       },
+
+       exclude => {
+               type => "string",
+               default => undef,
+               example => '\.wav$',
+               description => "regexp of source files to ignore",
+               safe => 0, # regexp
+               rebuild => 1,
+       },
+       wiki_file_prune_regexps => {
+               type => "internal",
+               default => [qr/(^|\/)\.\.(\/|$)/, qr/^\./, qr/\/\./,
+                       qr/\.x?html?$/, qr/\.ikiwiki-new$/,
+                       qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//,
+                       qr/(^|\/)_MTN\//,
+                       qr/\.dpkg-tmp$/],
+               description => "regexps of source files to ignore",
+               safe => 0,
+               rebuild => 1,
+       },
+       wiki_file_regexp => {
+               type => "internal",
+               default => qr/(^[-[:alnum:]_.:\/+]+$)/,
+               description => "regexp of legal source files",
+               safe => 0,
+               rebuild => 1,
+       },
+       web_commit_regexp => {
+               type => "internal",
+               default => qr/^web commit (by (.*?(?=: |$))|from (\d+\.\d+\.\d+\.\d+)):?(.*)/,
+               description => "regexp to parse web commits from logs",
+               safe => 0,
+               rebuild => 0,
+       },
+       cgi => {
+               type => "internal",
+               default => 0,
+               description => "run as a cgi",
+               safe => 0,
+               rebuild => 0,
+       },
+       cgi_disable_uploads => {
+               type => "internal",
+               default => 1,
+               description => "whether CGI should accept file uploads",
+               safe => 0,
+               rebuild => 0,
+       },
+       post_commit => {
+               type => "internal",
+               default => 0,
+               description => "run as a post-commit hook",
+               safe => 0,
+               rebuild => 0,
+       },
+       rebuild => {
+               type => "internal",
+               default => 0,
+               description => "running in rebuild mode",
+               safe => 0,
+               rebuild => 0,
+       },
+       refresh => {
+               type => "internal",
+               default => 0,
+               description => "running in refresh mode",
+               safe => 0,
+               rebuild => 0,
+       },
+       getctime => {
+               type => "internal",
+               default => 0,
+               description => "running in getctime mode",
+               safe => 0,
+               rebuild => 0,
+       },
+       w3mmode => {
+               type => "internal",
+               default => 0,
+               description => "running in w3mmode",
+               safe => 0,
+               rebuild => 0,
+       },
+       setup => {
+               type => "internal",
+               default => undef,
+               description => "setup file to read",
+               safe => 0,
+               rebuild => 0,
+       },
+       default_plugins => {
+               type => "internal",
+               default => [qw{mdwn link inline htmlscrubber passwordauth
+                               openid signinedit lockedit conditional
+                               recentchanges parentlinks}],
+               description => "plugins to enable by default",
+               safe => 1,
+               rebuild => 1,
+       },
+       add_plugins => {
+               type => "string",
+               default => [],
+               description => "plugins to add to the default configuration",
+               safe => 1,
+               rebuild => 1,
+       },
+       disable_plugins => {
+               type => "string",
+               default => [],
+               description => "plugins to disable",
+               safe => 1,
+               rebuild => 1,
+       },
+       libdir => {
+               type => "internal",
+               default => undef,
+               example => "$ENV{HOME}/.ikiwiki/",
+               description => "extra library and plugin directory",
+               safe => 0,
+               rebuild => 0,
+       },
+} #}}}
+
 sub defaultconfig () { #{{{
-       return
-       wiki_file_prune_regexps => [qr/(^|\/)\.\.(\/|$)/, qr/^\./, qr/\/\./,
-               qr/\.x?html?$/, qr/\.ikiwiki-new$/,
-               qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//,
-               qr/(^|\/)_MTN\//,
-               qr/\.dpkg-tmp$/],
-       wiki_file_regexp => qr/(^[-[:alnum:]_.:\/+]+$)/,
-       web_commit_regexp => qr/^web commit (by (.*?(?=: |$))|from (\d+\.\d+\.\d+\.\d+)):?(.*)/,
-       verbose => 0,
-       syslog => 0,
-       wikiname => "wiki",
-       default_pageext => "mdwn",
-       htmlext => "html",
-       cgi => 0,
-       post_commit => 0,
-       rcs => '',
-       url => '',
-       cgiurl => '',
-       historyurl => '',
-       diffurl => '',
-       rss => 0,
-       atom => 0,
-       allowrss => 0,
-       allowatom => 0,
-       discussion => 1,
-       rebuild => 0,
-       refresh => 0,
-       getctime => 0,
-       w3mmode => 0,
-       wrapper => undef,
-       wrappermode => undef,
-       svnpath => "trunk",
-       gitorigin_branch => "origin",
-       gitmaster_branch => "master",
-       srcdir => undef,
-       destdir => undef,
-       pingurl => [],
-       templatedir => "$installdir/share/ikiwiki/templates",
-       underlaydir => "$installdir/share/ikiwiki/basewiki",
-       underlaydirs => [],
-       setup => undef,
-       adminuser => undef,
-       adminemail => undef,
-       plugin => [qw{mdwn link inline htmlscrubber passwordauth openid
-                       signinedit lockedit conditional recentchanges
-                       parentlinks}],
-       libdir => undef,
-       timeformat => '%c',
-       locale => undef,
-       sslcookie => 0,
-       httpauth => 0,
-       userdir => "",
-       usedirs => 1,
-       numbacklinks => 10,
-       account_creation_password => "",
-       prefix_directives => 0,
-       hardlink => 0,
-       cgi_disable_uploads => 1,
+       my %s=getsetup();
+       my @ret;
+       foreach my $key (keys %s) {
+               push @ret, $key, $s{$key}->{default};
+       }
+       use Data::Dumper;
+       return @ret;
 } #}}}
 
 sub checkconfig () { #{{{
@@ -153,7 +463,7 @@ sub loadplugins () { #{{{
                unshift @INC, possibly_foolish_untaint($config{libdir});
        }
 
-       loadplugin($_) foreach @{$config{plugin}};
+       loadplugin($_) foreach @{$config{default_plugins}}, @{$config{add_plugins}};
 
        run_hooks(getopt => sub { shift->() });
        if (grep /^-/, @ARGV) {
index e000bc8648a39f33cea5aef932286255f343f4a2..0886fd753f3f09bfe35e4cc5d0c74647e97bc309 100644 (file)
@@ -16,6 +16,7 @@ my %guids;
 
 sub import { #{{{
        hook(type => "getopt", id => "aggregate", call => \&getopt);
+       hook(type => "getsetup", id => "aggregate", call => \&getsetup);
        hook(type => "checkconfig", id => "aggregate", call => \&checkconfig);
        hook(type => "needsbuild", id => "aggregate", call => \&needsbuild);
        hook(type => "preprocess", id => "aggregate", call => \&preprocess);
@@ -37,6 +38,24 @@ sub getopt () { #{{{
        );
 } #}}}
 
+sub getsetup () { #{{{
+       return
+               aggregateinternal => {
+                       type => "boolean",
+                       default => 0,
+                       description => "enable aggregation to internal pages?",
+                       safe => 0, # enabling needs manual transition
+                       rebuild => 0,
+               },
+               aggregate_webtrigger => {
+                       type => "boolean",
+                       default => 0,
+                       description => "allow aggregation to be triggered via the web?",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
+
 sub checkconfig () { #{{{
        if ($config{aggregate} && ! ($config{post_commit} && 
                                     IkiWiki::commit_hook_enabled())) {
index 187700f30d911dee95009a7d2515ef3aca61e176..9cb74fb1e805e14dfc5fae71cfd1dd694655aea5 100644 (file)
@@ -18,6 +18,7 @@ BEGIN {
 
 sub import { #{{{
        hook(type => "getopt", id => "amazon_s3", call => \&getopt);
+       hook(type => "getsetup", id => "amazon_s3", call => \&getsetup);
        hook(type => "checkconfig", id => "amazon_s3", call => \&checkconfig);
 } # }}}
 
@@ -39,6 +40,54 @@ sub getopt () { #{{{
        });
 } #}}}
 
+sub getsetup () { #{{{
+       return
+                amazon_s3_key_id => {
+                       type => "string",
+                       default => "",
+                       description => "public access key id",
+                       safe => 1,
+                       rebuild => 0,
+               },
+               amazon_s3_key_id => {
+                       type => "string",
+                       default => "",
+                       description => "file holding secret key",
+                       safe => 0, # ikiwiki reads this file
+                       rebuild => 0,
+               },
+               amazon_s3_bucket => {
+                       type => "string",
+                       default => "",
+                       example => "mywiki",
+                       description => "globally unique name of bucket to store wiki in",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               amazon_s3_prefix => {
+                       type => "string",
+                       default => "wiki/",
+                       description => "a prefix to prepend to each page name",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               amazon_s3_location => {
+                       type => "string",
+                       default => "",
+                       example => "EU",
+                       description => "which S3 datacenter to use (leave blank for default)",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               amazon_s3_dupindex => {
+                       type => "boolean",
+                       default => 0,
+                       description => "store each index file twice? (to allow urls ending in \"/index.html\" and \"/\")",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 sub checkconfig { #{{{
        foreach my $field (qw{amazon_s3_key_id amazon_s3_key_file
                              amazon_s3_bucket}) {
index 1880516d568b35e2b96463676d23c04c62f103f2..e615499863a7c119143f3953775d7103c0dad203 100644 (file)
@@ -6,9 +6,22 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
-       hook(type => "canedit", id => "anonok", call => \&canedit,);
+       hook(type => "getsetup", id => "anonok", call => \&getsetup);
+       hook(type => "canedit", id => "anonok", call => \&canedit);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               anonok_pagespec => {
+                       type => "string",
+                       default => "",
+                       example => "*/discussion",
+                       description => "PageSpec to limit which pages anonymouse users can edit",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
+
 sub canedit ($$$) { #{{{
        my $page=shift;
        my $cgi=shift;
index 720078be15cfcd51399f375815b4d22e1a2e8d7b..3fe33c858ae849f7314a2e33ab9a40d946f259f6 100644 (file)
@@ -6,11 +6,24 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
+       hook(type => "getsetup", id => "attachment", call => \&getsetup);
        hook(type => "checkconfig", id => "attachment", call => \&checkconfig);
        hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup);
        hook(type => "formbuilder", id => "attachment", call => \&formbuilder);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+                => {
+                       type => "string",
+                       default => "",
+                       example => "clamdscan -",
+                       description => "virus checker program (reads STDIN, returns nonzero if virus found)",
+                       safe => 0, # executed
+                       rebuild => 0,
+               },
+} #}}}
+
 sub check_canattach ($$;$) { #{{{
        my $session=shift;
        my $dest=shift; # where it's going to be put, under the srcdir
index aed087eed58248d5da153543dba03abba17b5ef0..b808c9d1d64bbc5adf226335e14b31d7b5c67085 100644 (file)
@@ -30,10 +30,22 @@ my $time=time;
 my @now=localtime($time);
 
 sub import { #{{{
+       hook(type => "getsetup", id => "version", call => \&getsetup);
        hook(type => "needsbuild", id => "version", call => \&needsbuild);
        hook(type => "preprocess", id => "calendar", call => \&preprocess);
 } #}}}
 
+sub getsetup () { #{{{
+       return
+               archivebase => {
+                       type => "string",
+                       default => "archives",
+                       description => "base of the archives hierarchy",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 sub is_leap_year (@) { #{{{
        my %params=@_;
        return ($params{year} % 4 == 0 && (($params{year} % 100 != 0) || $params{year} % 400 == 0));
index b13d15fa6350aadd237494350dd1c8949bf72700..021aa6b230c2a61b0a2289d2bbccc4e83abe4ae6 100644 (file)
@@ -9,7 +9,7 @@ use IkiWiki 2.00;
 use IPC::Open2;
 
 sub import { #{{{
-       hook(type => "preprocess", id => "graph", call => \&graph);
+       hook(type => "preprocess", id => "graphviz", call => \&graph);
 } # }}}
 
 my %graphviz_programs = (
index 2f09019431b44fd4c5ab458f44fe90026367d996..be9526df8de99de9643dfd82e0a40dda352d20e4 100644 (file)
@@ -15,6 +15,7 @@ my $nested=0;
 
 sub import { #{{{
        hook(type => "getopt", id => "inline", call => \&getopt);
+       hook(type => "getsetup", id => "inline", call => \&getsetup);
        hook(type => "checkconfig", id => "inline", call => \&checkconfig);
        hook(type => "sessioncgi", id => "inline", call => \&sessioncgi);
        hook(type => "preprocess", id => "inline", 
@@ -27,7 +28,6 @@ sub import { #{{{
        # pings interrupting page builds.
        hook(type => "change", id => "inline", 
                call => \&IkiWiki::pingurl);
-
 } # }}}
 
 sub getopt () { #{{{
@@ -39,8 +39,51 @@ sub getopt () { #{{{
                "atom!" => \$config{atom},
                "allowrss!" => \$config{allowrss},
                "allowatom!" => \$config{allowatom},
+               "pingurl=s" => sub {
+                       push @{$config{pingurl}}, $_[1];
+               },      
        );
-}
+} #}}}
+
+sub getsetup () { #{{{
+       return
+               rss => {
+                       type => "boolean",
+                       default => 0,
+                       description => "enable rss feeds by default?",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               atom => {
+                       type => "boolean",
+                       default => 0,
+                       description => "enable atom feeds by default?",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               allowrss => {
+                       type => "boolean",
+                       default => 0,
+                       description => "allow rss feeds to be used?",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               allowatom => {
+                       type => "boolean",
+                       default => 0,
+                       description => "allow atom feeds to be used?",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               pingurl => {
+                       type => "string",
+                       default => "",
+                       example => "http://rpc.technorati.com/rpc/ping",
+                       description => "urls to ping (using XML-RPC) on feed update",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
 
 sub checkconfig () { #{{{
        if (($config{rss} || $config{atom}) && ! length $config{url}) {
@@ -52,6 +95,9 @@ sub checkconfig () { #{{{
        if ($config{atom}) {
                push @{$config{wiki_file_prune_regexps}}, qr/\.atom$/;
        }
+       if (! exists $config{pingurl}) {
+               $config{pingurl}=[];
+       }
 } #}}}
 
 sub format (@) { #{{{
index 11f3f0137627643fe285e0464b59b2e0086b3f88..59cb93bc41e3103ab8c5dd61658a1bac542d0b25 100644 (file)
@@ -7,9 +7,21 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
+       hook(type => "getsetup", id => "mdwn", call => \&getsetup);
        hook(type => "htmlize", id => "mdwn", call => \&htmlize);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               multimarkdown => {
+                       type => "boolean",
+                       default => 0,
+                       description => "enable multimarkdown features?",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 my $markdown_sub;
 sub htmlize (@) { #{{{
        my %params=@_;
@@ -25,13 +37,13 @@ sub htmlize (@) { #{{{
                if (exists $config{multimarkdown} && $config{multimarkdown}) {
                        eval q{use Text::MultiMarkdown};
                        if ($@) {
-                               error(gettext("multimarkdown is enabled, but Text::MultiMarkdown is not installed"));
+                               debug(gettext("multimarkdown is enabled, but Text::MultiMarkdown is not installed"));
                        }
                        $markdown_sub=sub {
                                Text::MultiMarkdown::markdown(shift, {use_metadata => 0});
                        }
                }
-               else {
+               if (! defined $markdown_sub) {
                        eval q{use Text::Markdown};
                        if (! $@) {
                                if (Text::Markdown->can('markdown')) {
index 3997e6fefcd31c15a8fa6df7e559a6ffdf84e863..c7630d81f0b60911fed26b32baba015e3ea9757c 100644 (file)
@@ -6,9 +6,21 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
+       hook(type => "getsetup", id => "mirrorlist", call => \&getsetup);
        hook(type => "pagetemplate", id => "mirrorlist", call => \&pagetemplate);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               mirrorlist => {
+                       type => "string",
+                       default => "",
+                       description => "list of mirrors",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 sub pagetemplate (@) { #{{{
        my %params=@_;
         my $template=$params{template};
index 10a8fa22f46aa8116f10e13a48ac01678b8ba997..bc9311d9c04aec49b1db6f269663bc52b379391b 100644 (file)
@@ -8,6 +8,7 @@ use IkiWiki 2.00;
 
 sub import { #{{{
        hook(type => "getopt", id => "openid", call => \&getopt);
+       hook(type => "getsetup", id => "openid", call => \&getsetup);
        hook(type => "auth", id => "openid", call => \&auth);
        hook(type => "formbuilder_setup", id => "openid",
                call => \&formbuilder_setup, last => 1);
@@ -20,6 +21,18 @@ sub getopt () { #{{{
        GetOptions("openidsignup=s" => \$config{openidsignup});
 } #}}}
 
+sub getsetup () { #{{{
+       return
+               openidsignup => {
+                       type => "string",
+                       default => "",
+                       example => "http://myopenid.com/",
+                       description => "an url where users can signup for an OpenID",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
+
 sub formbuilder_setup (@) { #{{{
        my %params=@_;
 
index f3f1aa4bf2b7b5f761859d7a75a2c0d68f8b4eb7..7319614f7a7757e0a361b7703d7a06462ec398e2 100644 (file)
@@ -7,13 +7,30 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
-        hook(type => "formbuilder_setup", id => "passwordauth",
-               call => \&formbuilder_setup);
-        hook(type => "formbuilder", id => "passwordauth",
-               call => \&formbuilder);
+       hook(type => "getsetup", id => "passwordauth", "call" => \&getsetup);
+        hook(type => "formbuilder_setup", id => "passwordauth", call => \&formbuilder_setup);
+        hook(type => "formbuilder", id => "passwordauth", call => \&formbuilder);
        hook(type => "sessioncgi", id => "passwordauth", call => \&sessioncgi);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               account_creation_password => {
+                       type => "string",
+                       default => "",
+                       description => "a password that must be entered when signing up for an account",
+                       safe => 1,
+                       rebuild => 0,
+               },
+               password_cost => {
+                       type => "integer",
+                       default => 8,
+                       description => "cost of generating a password using Authen::Passphrase::BlowfishCrypt",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
+
 # Checks if a string matches a user's password, and returns true or false.
 sub checkpassword ($$;$) { #{{{
        my $user=shift;
@@ -88,7 +105,9 @@ sub formbuilder_setup (@) { #{{{
                
                if ($form->submitted eq "Register" || $form->submitted eq "Create Account") {
                        $form->field(name => "confirm_password", type => "password");
-                       $form->field(name => "account_creation_password", type => "password") if (length $config{account_creation_password});
+                       $form->field(name => "account_creation_password", type => "password")
+                                if (defined $config{account_creation_password} &&
+                                    length $config{account_creation_password});
                        $form->field(name => "email", size => 50);
                        $form->title("register");
                        $form->text("");
@@ -125,7 +144,8 @@ sub formbuilder_setup (@) { #{{{
                                                shift eq $config{account_creation_password};
                                        },
                                        required => 1,
-                               ) if (length $config{account_creation_password});
+                               ) if (defined $config{account_creation_password} &&
+                                     length $config{account_creation_password});
                                $form->field(
                                        name => "email",
                                        validate => "EMAIL",
@@ -259,7 +279,9 @@ sub formbuilder (@) { #{{{
                                error($@) if $@;
                                sendmail(
                                        To => IkiWiki::userinfo_get($user_name, "email"),
-                                       From => "$config{wikiname} admin <$config{adminemail}>",
+                                       From => "$config{wikiname} admin <".
+                                               (defined $config{adminemail} ? $config{adminemail} : "")
+                                               .">",
                                        Subject => "$config{wikiname} information",
                                        Message => $template->output,
                                ) or error(gettext("Failed to send mail"));
index 614d428853c7d0d2efbaa74987edc97e6643a452..0aee17f8a582235af049ae5b5a53cc6697e9f0fe 100644 (file)
@@ -9,12 +9,24 @@ my %pages;
 my $pinged=0;
 
 sub import { #{{{
+       hook(type => "getsetup", id => "pinger", call => \&getsetup);
        hook(type => "needsbuild", id => "pinger", call => \&needsbuild);
        hook(type => "preprocess", id => "ping", call => \&preprocess);
        hook(type => "delete", id => "pinger", call => \&ping);
        hook(type => "change", id => "pinger", call => \&ping);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               pinger_timeout => {
+                       type => "integer",
+                       default => 15,
+                       description => "how many seconds to try pinging before timing out",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
+
 sub needsbuild (@) { #{{{
        my $needsbuild=shift;
        foreach my $page (keys %pagestate) {
index 745e6a1de436e2db15b97ebe897858f891de2a51..29330f29fd5666c75a7bead7e4fcbbcf394ea3b0 100644 (file)
@@ -40,9 +40,28 @@ sub default_timetable {
 }
 
 sub import { #{{{
+       hook(type => "getsetup", id => "prettydate", call => \&getsetup);
        hook(type => "checkconfig", id => "prettydate", call => \&checkconfig);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               prettydateformat => {
+                       type => "string",
+                       default => '%X, %B %o, %Y',
+                       description => "format to use to display date",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               timetable => {
+                       type => undef, # don't try to show in interface
+                       default => '%X, %B %o, %Y',
+                       description => "array of time descriptions",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 sub checkconfig () { #{{{
        if (! defined $config{prettydateformat} ||
            $config{prettydateformat} eq '%c') {
index 8383fb72a12e19c8d92627956efc154855ff57e5..6ab4f9d033a66e2ce57ab83b5a26b9a429811b91 100644 (file)
@@ -6,6 +6,7 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
+       hook(type => "getsetup", id => "recentchanges", call => \&getsetup);
        hook(type => "checkconfig", id => "recentchanges", call => \&checkconfig);
        hook(type => "refresh", id => "recentchanges", call => \&refresh);
        hook(type => "pagetemplate", id => "recentchanges", call => \&pagetemplate);
@@ -13,6 +14,24 @@ sub import { #{{{
        hook(type => "cgi", id => "recentchanges", call => \&cgi);
 } #}}}
 
+sub getsetup () { #{{{
+       return
+               recentchangespage => {
+                       type => "string",
+                       default => "recentchanges",
+                       description => "name of the recentchanges page",
+                       safe => 1,
+                       rebuild => 1,
+               },
+               recentchangesnum => {
+                       type => "integer",
+                       default => 100,
+                       description => "number of changes to track",
+                       safe => 1,
+                       rebuild => 0,
+               },
+} #}}}
+
 sub checkconfig () { #{{{
        $config{recentchangespage}='recentchanges' unless defined $config{recentchangespage};
        $config{recentchangesnum}=100 unless defined $config{recentchangesnum};
index eedfa69244a166092b543b51e61a360b486a00f5..ff18e1faf4eccd4119dcb1e6a4128c62cec41396 100644 (file)
@@ -7,6 +7,7 @@ use strict;
 use IkiWiki 2.00;
 
 sub import { #{{{
+       hook(type => "getsetup", id => "search", call => \&getsetup);
        hook(type => "checkconfig", id => "search", call => \&checkconfig);
        hook(type => "pagetemplate", id => "search", call => \&pagetemplate);
        hook(type => "postscan", id => "search", call => \&index);
@@ -14,6 +15,17 @@ sub import { #{{{
        hook(type => "cgi", id => "search", call => \&cgi);
 } # }}}
 
+sub getsetup () { #{{{
+       return
+               omega_cgi => {
+                       type => "string",
+                       default => "/usr/lib/cgi-bin/omega/omega",
+                       description => "path to the omega cgi program",
+                       safe => 0, # external program
+                       rebuild => 0,
+               },
+} #}}}
+
 sub checkconfig () { #{{{
        foreach my $required (qw(url cgiurl)) {
                if (! length $config{$required}) {
index 1af8e4e9df3b0903cd1341327f606fd4599afcde..716ba63ddd480f1a8b69cd530cff40a739e30c8c 100644 (file)
@@ -10,6 +10,7 @@ use IkiWiki 2.00;
 
 sub import { #{{{
        hook(type => "getopt", id => "skeleton",  call => \&getopt);
+       hook(type => "getsetup", id => "skeleton",  call => \&getsetup);
        hook(type => "checkconfig", id => "skeleton", call => \&checkconfig);
        hook(type => "needsbuild", id => "skeleton", call => \&needsbuild);
        hook(type => "preprocess", id => "skeleton", call => \&preprocess);
@@ -38,6 +39,17 @@ sub getopt () { #{{{
        debug("skeleton plugin getopt");
 } #}}}
 
+sub getsetup () { #{{{
+       return
+               skeleton => {
+                       type => "boolean",
+                       default => 0,
+                       description => "example option",
+                       safe => 0,
+                       rebuild => 0,
+               },
+} #}}}
+
 sub checkconfig () { #{{{
        debug("skeleton plugin checkconfig");
 } #}}}
index b0a0e53be7cec3c73147ce61fe353aaa9b8a3d69..d4cbb67059a2ed0cd38db658cad288341a979a4a 100644 (file)
@@ -10,6 +10,7 @@ my %tags;
 
 sub import { #{{{
        hook(type => "getopt", id => "tag", call => \&getopt);
+       hook(type => "getsetup", id => "tag", call => \&getsetup);
        hook(type => "preprocess", id => "tag", call => \&preprocess_tag, scan => 1);
        hook(type => "preprocess", id => "taglink", call => \&preprocess_taglink, scan => 1);
        hook(type => "pagetemplate", id => "tag", call => \&pagetemplate);
@@ -22,6 +23,18 @@ sub getopt () { #{{{
        GetOptions("tagbase=s" => \$config{tagbase});
 } #}}}
 
+sub getsetup () { #{{{
+       return
+               tagbase => {
+                       type => "string",
+                       default => "",
+                       example => "tag",
+                       description => "parent page tags are located under",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 sub tagpage ($) { #{{{
        my $tag=shift;
                        
index fe69968981029f07e0a60c2903bfb9e02fe6a4a6..4c486d4b4ad1917c965380fde9ba6cbed66d715e 100644 (file)
@@ -8,6 +8,7 @@ use IkiWiki 2.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);
 } # }}}
 
@@ -18,11 +19,26 @@ sub getopt () { #{{{
        GetOptions("typographyattributes=s" => \$config{typographyattributes});
 } #}}}
 
+sub getsetup () { #{{{
+       eval q{use Text::Typography};
+       error($@) if $@;
+
+       return
+               typographyattributes => {
+                       type => "string",
+                       default => "3",
+                       example => "tag",
+                       description => "Text::Typography attributes value",
+                       safe => 1,
+                       rebuild => 1,
+               },
+} #}}}
+
 sub sanitize (@) { #{{{
        my %params=@_;
 
        eval q{use Text::Typography};
-       error($@) if $@;
+       return $params{content} if $@;
 
        my $attributes=defined $config{typographyattributes} ? $config{typographyattributes} : '3';
        return Text::Typography::typography($params{content}, $attributes);
index 3b7a112539acf794e855a6daffb192f657c49f11..262d494797639a43f62b9a5c89c34f34fec03b04 100644 (file)
@@ -1,20 +1,18 @@
 #!/usr/bin/perl
 # Ikiwiki setup files are perl files that 'use IkiWiki::Setup::foo',
 # passing it some sort of configuration data.
-#
-# There can be multiple modules, with different configuration styles.
-# The setup modules each convert the data into the hashes used by ikiwiki
-# internally (if it's not already in that format), and store it in
-# IkiWiki::Setup::$raw_setup, to pass it back to this module.
 
 package IkiWiki::Setup;
 
 use warnings;
 use strict;
 use IkiWiki;
-use IkiWiki::Wrapper;
 use open qw{:utf8 :std};
 
+# There can be multiple modules, with different configuration styles.
+# The setup modules each convert the data into the hashes used by ikiwiki
+# internally (if it's not already in that format), and store it in
+# IkiWiki::Setup::$raw_setup, to pass it back to this module.
 our $raw_setup;
 
 sub load ($) { # {{{
@@ -34,54 +32,27 @@ sub load ($) { # {{{
        eval $code;
        error("$setup: ".$@) if $@;
 
-       my $ret=$raw_setup;
+       my %setup=%{$raw_setup};
        $raw_setup=undef;
 
-       return %$ret;
-} #}}}
-
-package IkiWiki;
-
-sub setup () { #{{{
-       my %setup=IkiWiki::Setup::load($config{setup});
-
-       $setup{plugin}=$config{plugin};
+       # Merge setup into existing config and untaint.
        if (exists $setup{add_plugins}) {
-               push @{$setup{plugin}}, @{$setup{add_plugins}};
-               delete $setup{add_plugins};
+               push @{$setup{add_plugins}}, @{$config{add_plugins}};
        }
        if (exists $setup{exclude}) {
                push @{$config{wiki_file_prune_regexps}}, $setup{exclude};
        }
-
-       if (! $config{render} && (! $config{refresh} || $config{wrappers})) {
-               debug(gettext("generating wrappers.."));
-               my @wrappers=@{$setup{wrappers}};
-               delete $setup{wrappers};
-               my %startconfig=(%config);
-               foreach my $wrapper (@wrappers) {
-                       %config=(%startconfig, rebuild => 0, verbose => 0, %setup, %{$wrapper});
-                       checkconfig();
-                       if (! $config{cgi} && ! $config{post_commit}) {
-                               $config{post_commit}=1;
-                       }
-                       gen_wrapper();
-               }
-               %config=(%startconfig);
-       }
-       
        foreach my $c (keys %setup) {
-               next if $c eq 'syslog';
                if (defined $setup{$c}) {
-                       if (! ref $setup{$c}) {
-                               $config{$c}=possibly_foolish_untaint($setup{$c});
+                       if (! ref $setup{$c} || ref $setup{$c} eq 'Regexp') {
+                               $config{$c}=IkiWiki::possibly_foolish_untaint($setup{$c});
                        }
                        elsif (ref $setup{$c} eq 'ARRAY') {
-                               $config{$c}=[map { possibly_foolish_untaint($_) } @{$setup{$c}}]
+                               $config{$c}=[map { IkiWiki::possibly_foolish_untaint($_) } @{$setup{$c}}]
                        }
                        elsif (ref $setup{$c} eq 'HASH') {
                                foreach my $key (keys %{$setup{$c}}) {
-                                       $config{$c}{$key}=possibly_foolish_untaint($setup{$c}{$key});
+                                       $config{$c}{$key}=IkiWiki::possibly_foolish_untaint($setup{$c}{$key});
                                }
                        }
                }
@@ -89,33 +60,17 @@ sub setup () { #{{{
                        $config{$c}=undef;
                }
        }
-       
-       if (! $config{refresh}) {
-               $config{rebuild}=1;
-       }
-       
-       loadplugins();
-       checkconfig();
-
-       require IkiWiki::Render;
-
-       if ($config{render}) {
-               commandline_render();
-       }
-
-       if (! $config{refresh}) {
-               debug(gettext("rebuilding wiki.."));
-       }
-       else {
-               debug(gettext("refreshing wiki.."));
-       }
+} #}}}
 
-       lockwiki();
-       loadindex();
-       refresh();
+sub dump ($) { #{{{
+       my $file=IkiWiki::possibly_foolish_untaint(shift);
+       
+       require IkiWiki::Setup::Standard;
+       my @dump=IkiWiki::Setup::Standard::gendump("Setup file for ikiwiki.");
 
-       debug(gettext("done"));
-       saveindex();
-} #}}}
+       open (OUT, ">", $file) || die "$file: $!";
+       print OUT "$_\n" foreach @dump;
+       close OUT;
+}
 
 1
index f67c3829b71d437dc73ed7477196d9323247ece4..54819ae75d822b85f86d7ecdb0b0c30a28094855 100644 (file)
@@ -7,9 +7,100 @@ package IkiWiki::Setup::Standard;
 
 use warnings;
 use strict;
+use IkiWiki;
 
-sub import {
+sub import { #{{{
        $IkiWiki::Setup::raw_setup=$_[1];
-}
+} #}}}
+
+sub dumpline ($$$$) { #{{{
+       my $key=shift;
+       my $value=shift;
+       my $type=shift;
+       my $prefix=shift;
+       
+       eval q{use Data::Dumper};
+       error($@) if $@;
+       local $Data::Dumper::Terse=1;
+       local $Data::Dumper::Indent=1;
+       local $Data::Dumper::Pad="\t";
+       local $Data::Dumper::Sortkeys=1;
+       local $Data::Dumper::Quotekeys=0;
+       
+       my $dumpedvalue;
+       if ($type eq 'boolean' || $type eq 'integer') {
+               # avoid quotes
+               $dumpedvalue=$value;
+       }
+       elsif ($type eq 'string' && ref $value eq 'ARRAY' && @$value &&
+           ! grep { /[^-A-Za-z0-9_]/ } @$value) {
+               # dump simple array as qw{}
+               $dumpedvalue="[qw{ ".join(" ", @$value)." }]";
+       }
+       else {
+               $dumpedvalue=Dumper($value);
+               chomp $dumpedvalue;
+               $dumpedvalue=~s/^\t//;
+       }
+       
+       return "\t$prefix$key => $dumpedvalue,";
+} #}}}
+
+sub dumpvalues ($@) { #{{{
+       my $setup=shift;
+       my @ret;
+       while (@_) {
+               my $key=shift;
+               my %info=%{shift()};
+
+               next if $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{default} && defined $info{default}) {
+                       push @ret, dumpline($key, $info{default}, $info{type}, "#");
+               }
+               elsif (exists $info{example}) {
+                       push @ret, dumpline($key, $info{example}, $info{type}, "#");
+               }
+       }
+       return @ret;
+} #}}}
+
+sub gendump ($) { #{{{
+       my $description=shift;
+       my %setup=(%config);
+       my @ret;
+       
+       push @ret, "\t# basic setup";
+       push @ret, dumpvalues(\%setup, IkiWiki::getsetup());
+       push @ret, "";
+
+       foreach my $id (sort keys %{$IkiWiki::hooks{getsetup}}) {
+               # use an array rather than a hash, to preserve order
+               my @s=$IkiWiki::hooks{getsetup}{$id}{call}->();
+               return unless @s;
+               push @ret, "\t# $id plugin";
+               push @ret, dumpvalues(\%setup, @s);
+               push @ret, "";
+       }
+       
+       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
index 7c28088ded55f9fce9b9012e9c31f9e72814145e..faf1643587078e1ae69ff421cb5510c2421073de 100644 (file)
@@ -357,6 +357,50 @@ something. The hook is passed named parameters: `page`, `oldpage`,
 `newpage`, and `content`, and should try to modify the content to reflect
 the name change. For example, by converting links to point to the new page.
 
+### getsetup
+
+       hook(type => "getsetup", id => "foo", call => \&getsetup);
+
+This hooks is not called during normal operation, but only when setting up 
+the wiki, or generating a setup file. Plugins can use this hook to add
+configuration options.
+
+The hook is passed no parameters. It returns data about the configuration
+options added by the plugin. It can also check if the plugin is usable, and
+die if the plugin is not available, which will cause the plugin to not be
+offered in the configuration interface.
+
+The data returned is a list of `%config` options, followed by a hash
+describing the option. For example:
+
+                return
+                       option_foo => {
+                               type => "boolean",
+                               default => 0,
+                               description => "enable foo",
+                               safe => 1,
+                               rebuild => 1,
+                       },
+                       option_bar => {
+                               type => "string",
+                               example => "hello",
+                               description => "what to say",
+                               safe => 1,
+                               rebuild => 0,
+                       },
+
+* `type` can be "boolean", "string", "integer", "internal" (used for values
+  that are not user-visible) or `undef` (use for complex types). Note that
+  the type is the type of the leaf values; the `%config` option may be an
+  array or hash of these.
+* `default` should be set to the default value of the option, if any.
+* `example` can be set to an example value, which will not be used by default.
+* `description` is a short description of the option.
+* `safe` should be false if the option should not be displayed in unsafe
+  configuration methods, such as the web interface. Anything that specifies
+  a command to run, a path on disk, or a regexp should be marked as unsafe.
+* `rebuild` should be true if changing the option will require a wiki rebuild.
+
 ## Plugin interface
 
 To import the ikiwiki plugin interface:
index 2b104bcdb5597659f08c734046ba89ddd711741c..473d1c9b10e37f467c7f66403948abeef0de9ce6 100644 (file)
@@ -62,6 +62,11 @@ These options control the mode that ikiwiki operates in.
   If you only want to build any changed pages, you can use --refresh with
   --setup.
 
+* --dumpsetup configfile
+
+  Causes ikiwiki to write to the specified config file, dumping out
+  its current configuration.
+
 * --wrappers
 
   If used with --setup --refresh, this makes it also update any configured
index 3bb881c4320f246b1edd4f9af80014149036d9e4..febc8ff56ed42ee876b85db2b235270961737963 100755 (executable)
@@ -20,6 +20,7 @@ sub getconfig () { #{{{
                Getopt::Long::Configure('pass_through');
                GetOptions(
                        "setup|s=s" => \$config{setup},
+                       "dumpsetup|s=s" => \$config{dumpsetup},
                        "wikiname=s" => \$config{wikiname},
                        "verbose|v!" => \$config{verbose},
                        "syslog!" => \$config{syslog},
@@ -27,7 +28,7 @@ sub getconfig () { #{{{
                        "refresh!" => \$config{refresh},
                        "post-commit" => \$config{post_commit},
                        "render=s" => \$config{render},
-                       "wrappers!" => \$config{wrappers},
+                       "wrappers!" => \$config{genwrappers},
                        "usedirs!" => \$config{usedirs},
                        "prefix-directives!" => \$config{prefix_directives},
                        "getctime" => \$config{getctime},
@@ -45,7 +46,6 @@ sub getconfig () { #{{{
                        "adminemail=s" => \$config{adminemail},
                        "timeformat=s" => \$config{timeformat},
                        "sslcookie!" => \$config{sslcookie},
-                       "httpauth!" => \$config{httpauth},
                        "userdir=s" => \$config{userdir},
                        "htmlext=s" => \$config{htmlext},
                        "libdir=s" => \$config{libdir},
@@ -68,14 +68,11 @@ sub getconfig () { #{{{
                                $config{wrappermode}=possibly_foolish_untaint($_[1])
                        },
                        "plugin=s@" => sub {
-                               push @{$config{plugin}}, $_[1];
+                               push @{$config{add_plugins}}, $_[1];
                        },
                        "disable-plugin=s@" => sub {
                                push @{$config{disable_plugins}}, $_[1];
                        },
-                       "pingurl=s" => sub {
-                               push @{$config{pingurl}}, $_[1];
-                       },
                        "set=s" => sub {
                                my ($var, $val)=split('=', $_[1], 2);
                                if (! defined $var || ! defined $val) {
@@ -114,7 +111,47 @@ sub main () { #{{{
        
        if ($config{setup}) {
                require IkiWiki::Setup;
-               setup();
+               IkiWiki::Setup::load($config{setup});
+               if (@{$config{wrappers}} && 
+                   ! $config{render} && ! $config{dumpsetup} &&
+                   (! $config{refresh} || $config{genwrappers})) {
+                       debug(gettext("generating wrappers.."));
+                       require IkiWiki::Wrapper;
+                       my %origconfig=(%config);
+                       my @wrappers=@{$config{wrappers}};
+                       delete $config{wrappers};
+                       delete $config{genwrappers};
+                       foreach my $wrapper (@wrappers) {
+                               %config=(%origconfig,
+                                       rebuild => 0,
+                                       verbose => 0,
+                                       %{$wrapper},
+                               );
+                               checkconfig();
+                               if (! $config{cgi} && ! $config{post_commit}) {
+                                       $config{post_commit}=1;
+                               }
+                               gen_wrapper();
+                       }
+                       %config=(%origconfig);
+               }
+               
+               # setup implies a wiki rebuild by default
+               if (! $config{refresh}) {
+                       $config{rebuild}=1;
+               }
+               
+               # ignore syslog setting from setup file
+               # while doing initial setup
+               $config{syslog}=0 unless $config{dumpsetup};
+               
+               loadplugins();
+               checkconfig();
+       }
+
+       if ($config{dumpsetup}) {
+               require IkiWiki::Setup;
+               IkiWiki::Setup::dump($config{dumpsetup});
        }
        elsif ($config{wrapper}) {
                lockwiki();
@@ -136,12 +173,19 @@ sub main () { #{{{
                # do nothing
        }
        else {
+               if (! $config{refresh}) {
+                       debug(gettext("rebuilding wiki.."));
+               }
+               else {
+                       debug(gettext("refreshing wiki.."));
+               }
                lockwiki();
                loadindex();
                require IkiWiki::Render;
                rcs_update();
                refresh();
                saveindex();
+               debug(gettext("done"));
        }
 } #}}}