]> sipb.mit.edu Git - ikiwiki.git/blobdiff - IkiWiki.pm
Tips on configuring lighttpd to enable ikiwiki.cgi
[ikiwiki.git] / IkiWiki.pm
index edbec77d62ed55f977f97c6f8b476d06c65c6676..4e4da11c5a016daa9d04c0b6ee04df7f786a14a7 100644 (file)
@@ -13,7 +13,8 @@ use open qw{:utf8 :std};
 
 use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase
            %pagestate %renderedfiles %oldrenderedfiles %pagesources
-           %destsources %depends %hooks %forcerebuild $gettext_obj};
+           %destsources %depends %hooks %forcerebuild $gettext_obj
+           %loaded_plugins};
 
 use Exporter q{import};
 our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match
@@ -40,6 +41,28 @@ sub getsetup () { #{{{
                safe => 1,
                rebuild => 1,
        },
+       adminemail => {
+               type => "string",
+               default => undef,
+               example => 'me@example.com',
+               description => "contact email for wiki",
+               safe => 1,
+               rebuild => 0,
+       },
+       adminuser => {
+               type => "string",
+               default => [],
+               description => "users who are wiki admins",
+               safe => 1,
+               rebuild => 0,
+       },
+       banned_users => {
+               type => "string",
+               default => [],
+               description => "users who are banned from the wiki",
+               safe => 1,
+               rebuild => 0,
+       },
        srcdir => {
                type => "string",
                default => undef,
@@ -56,21 +79,6 @@ sub getsetup () { #{{{
                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 => '',
@@ -82,11 +90,26 @@ sub getsetup () { #{{{
        cgiurl => {
                type => "string",
                default => '',
-               examples => "http://example.com/wiki/ikiwiki.cgi",
+               example => "http://example.com/wiki/ikiwiki.cgi",
                description => "url to the ikiwiki.cgi",
                safe => 1,
                rebuild => 1,
        },
+       cgi_wrapper => {
+               type => "string",
+               default => '',
+               example => "/var/www/wiki/ikiwiki.cgi",
+               description => "cgi wrapper to generate",
+               safe => 0, # file
+               rebuild => 0,
+       },
+       cgi_wrappermode => {
+               type => "string",
+               default => '06755',
+               description => "mode for cgi_wrapper (can safely be made suid)",
+               safe => 0,
+               rebuild => 0,
+       },
        rcs => {
                type => "string",
                default => '',
@@ -94,79 +117,34 @@ sub getsetup () { #{{{
                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,
+       default_plugins => {
+               type => "internal",
+               default => [qw{mdwn link inline htmlscrubber passwordauth
+                               openid signinedit lockedit conditional
+                               recentchanges parentlinks editpage}],
+               description => "plugins to enable by default",
+               safe => 0,
                rebuild => 1,
        },
-       diffurl => {
+       add_plugins => {
                type => "string",
-               # TODO ditto above
-               default => '',
-               example => "XXX",
-               description => "XXX",
+               default => [],
+               description => "plugins to add to the default configuration",
                safe => 1,
                rebuild => 1,
        },
-       discussion => {
-               type => "boolean",
-               default => 1,
-               description => "enable Discussion pages?",
+       disable_plugins => {
+               type => "string",
+               default => [],
+               description => "plugins to disable",
                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",
+               advanced => 1,
                safe => 0, # path
                rebuild => 1,
        },
@@ -174,9 +152,17 @@ sub getsetup () { #{{{
                type => "string",
                default => "$installdir/share/ikiwiki/basewiki",
                description => "base wiki source location",
+               advanced => 1,
                safe => 0, # path
                rebuild => 0,
        },
+       wrappers => {
+               type => "internal",
+               default => [],
+               description => "wrappers to generate",
+               safe => 0,
+               rebuild => 0,
+       },
        underlaydirs => {
                type => "internal",
                default => [],
@@ -186,14 +172,14 @@ sub getsetup () { #{{{
        },
        verbose => {
                type => "boolean",
-               default => 0,
+               example => 1,
                description => "display verbose messages when building?",
                safe => 1,
                rebuild => 0,
        },
        syslog => {
                type => "boolean",
-               default => 0,
+               example => 1,
                description => "log to syslog?",
                safe => 1,
                rebuild => 0,
@@ -212,6 +198,21 @@ sub getsetup () { #{{{
                safe => 0, # changing requires manual transition
                rebuild => 1,
        },
+       discussion => {
+               type => "boolean",
+               default => 1,
+               description => "enable Discussion pages?",
+               safe => 1,
+               rebuild => 1,
+       },
+       sslcookie => {
+               type => "boolean",
+               default => 0,
+               description => "only send cookies over SSL connections?",
+               advanced => 1,
+               safe => 1,
+               rebuild => 0,
+       },
        default_pageext => {
                type => "string",
                default => "mdwn",
@@ -230,6 +231,7 @@ sub getsetup () { #{{{
                type => "string",
                default => '%c',
                description => "strftime format string to display date",
+               advanced => 1,
                safe => 1,
                rebuild => 1,
        },
@@ -238,16 +240,10 @@ sub getsetup () { #{{{
                default => undef,
                example => "en_US.UTF-8",
                description => "UTF-8 locale to use",
+               advanced => 1,
                safe => 0,
                rebuild => 1,
        },
-       sslcookie => {
-               type => "boolean",
-               default => 0,
-               description => "only send cookies over SSL connections?",
-               safe => 1,
-               rebuild => 0,
-       },
        userdir => {
                type => "string",
                default => "",
@@ -267,15 +263,41 @@ sub getsetup () { #{{{
                type => "boolean",
                default => 0,
                description => "attempt to hardlink source files? (optimisation for large files)",
+               advanced => 1,
+               safe => 0, # paranoia
+               rebuild => 0,
+       },
+       umask => {
+               type => "integer",
+               description => "",
+               example => "022",
+               description => "force ikiwiki to use a particular umask",
+               advanced => 1,
+               safe => 0, # paranoia
+               rebuild => 0,
+       },
+       libdir => {
+               type => "string",
+               default => "",
+               example => "$ENV{HOME}/.ikiwiki/",
+               description => "extra library and plugin directory",
+               advanced => 1,
+               safe => 0, # directory
+               rebuild => 0,
+       },
+       ENV => {
+               type => "string", 
+               default => {},
+               description => "environment variables",
                safe => 0, # paranoia
                rebuild => 0,
        },
-
        exclude => {
                type => "string",
                default => undef,
                example => '\.wav$',
                description => "regexp of source files to ignore",
+               advanced => 1,
                safe => 0, # regexp
                rebuild => 1,
        },
@@ -290,9 +312,15 @@ sub getsetup () { #{{{
                safe => 0,
                rebuild => 1,
        },
+       wiki_file_chars => {
+               type => "string",
+               description => "specifies the characters that are allowed in source filenames",
+               default => "-[:alnum:]+/.:_",
+               safe => 0,
+               rebuild => 1,
+       },
        wiki_file_regexp => {
                type => "internal",
-               default => qr/(^[-[:alnum:]_.:\/+]+$)/,
                description => "regexp of legal source files",
                safe => 0,
                rebuild => 1,
@@ -332,6 +360,13 @@ sub getsetup () { #{{{
                safe => 0,
                rebuild => 0,
        },
+       setup => {
+               type => "internal",
+               default => undef,
+               description => "running in setup mode",
+               safe => 0,
+               rebuild => 0,
+       },
        refresh => {
                type => "internal",
                default => 0,
@@ -353,41 +388,17 @@ sub getsetup () { #{{{
                safe => 0,
                rebuild => 0,
        },
-       setup => {
+       setupfile => {
                type => "internal",
                default => undef,
-               description => "setup file to read",
+               description => "path to setup file",
                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 => {
+       allow_symlinks_before_srcdir => {
                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",
+               default => 0,
+               description => "allow symlinks in the path leading to the srcdir (potentially insecure)",
                safe => 0,
                rebuild => 0,
        },
@@ -415,6 +426,10 @@ sub checkconfig () { #{{{
                        $gettext_obj=undef;
                }
        }
+               
+       if (! defined $config{wiki_file_regexp}) {
+               $config{wiki_file_regexp}=qr/(^[$config{wiki_file_chars}]+$)/;
+       }
 
        if (ref $config{ENV} eq 'HASH') {
                foreach my $val (keys %{$config{ENV}}) {
@@ -438,18 +453,8 @@ sub checkconfig () { #{{{
        
        $config{wikistatedir}="$config{srcdir}/.ikiwiki"
                unless exists $config{wikistatedir};
-       
-       if ($config{rcs}) {
-               eval qq{use IkiWiki::Rcs::$config{rcs}};
-               if ($@) {
-                       error("Failed to load RCS module IkiWiki::Rcs::$config{rcs}: $@");
-               }
-       }
-       else {
-               require IkiWiki::Rcs::Stub;
-       }
 
-       if (exists $config{umask}) {
+       if (defined $config{umask}) {
                umask(possibly_foolish_untaint($config{umask}));
        }
 
@@ -458,12 +463,44 @@ sub checkconfig () { #{{{
        return 1;
 } #}}}
 
+sub listplugins () { #{{{
+       my %ret;
+
+       foreach my $dir (@INC, $config{libdir}) {
+               next unless defined $dir && length $dir;
+               foreach my $file (glob("$dir/IkiWiki/Plugin/*.pm")) {
+                       my ($plugin)=$file=~/.*\/(.*)\.pm$/;
+                       $ret{$plugin}=1;
+               }
+       }
+       foreach my $dir ($config{libdir}, "$installdir/lib/ikiwiki") {
+               next unless defined $dir && length $dir;
+               foreach my $file (glob("$dir/plugins/*")) {
+                       $ret{basename($file)}=1 if -x $file;
+               }
+       }
+
+       return keys %ret;
+} #}}}
+
 sub loadplugins () { #{{{
-       if (defined $config{libdir}) {
+       if (defined $config{libdir} && length $config{libdir}) {
                unshift @INC, possibly_foolish_untaint($config{libdir});
        }
 
-       loadplugin($_) foreach @{$config{default_plugins}}, @{$config{add_plugins}};
+       foreach my $plugin (@{$config{default_plugins}}, @{$config{add_plugins}}) {
+               loadplugin($plugin);
+       }
+       
+       if ($config{rcs}) {
+               if (exists $IkiWiki::hooks{rcs}) {
+                       error(gettext("cannot use multiple rcs plugins"));
+               }
+               loadplugin($config{rcs});
+       }
+       if (! exists $IkiWiki::hooks{rcs}) {
+               loadplugin("norcs");
+       }
 
        run_hooks(getopt => sub { shift->() });
        if (grep /^-/, @ARGV) {
@@ -483,8 +520,13 @@ sub loadplugin ($) { #{{{
        foreach my $dir (defined $config{libdir} ? possibly_foolish_untaint($config{libdir}) : undef,
                         "$installdir/lib/ikiwiki") {
                if (defined $dir && -x "$dir/plugins/$plugin") {
-                       require IkiWiki::Plugin::external;
+                       eval { require IkiWiki::Plugin::external };
+                       if ($@) {
+                               my $reason=$@;
+                               error(sprintf(gettext("failed to load external plugin needed for %s plugin: %s"), $plugin, $reason));
+                       }
                        import IkiWiki::Plugin::external "$dir/plugins/$plugin";
+                       $loaded_plugins{$plugin}=1;
                        return 1;
                }
        }
@@ -494,6 +536,7 @@ sub loadplugin ($) { #{{{
        if ($@) {
                error("Failed to load plugin $mod: $@");
        }
+       $loaded_plugins{$plugin}=1;
        return 1;
 } #}}}
 
@@ -748,7 +791,7 @@ sub bestlink ($$) { #{{{
                elsif (exists $pagecase{lc $l}) {
                        return $pagecase{lc $l};
                }
-       } while $cwd=~s!/?[^/]+$!!;
+       } while $cwd=~s{/?[^/]+$}{};
 
        if (length $config{userdir}) {
                my $l = "$config{userdir}/".lc($link);
@@ -786,13 +829,16 @@ sub pagetitle ($;$) { #{{{
 
 sub titlepage ($) { #{{{
        my $title=shift;
-       $title=~s/([^-[:alnum:]:+\/.])/$1 eq ' ' ? '_' : "__".ord($1)."__"/eg;
+       # support use w/o %config set
+       my $chars = defined $config{wiki_file_chars} ? $config{wiki_file_chars} : "-[:alnum:]+/.:_";
+       $title=~s/([^$chars]|_)/$1 eq ' ' ? '_' : "__".ord($1)."__"/eg;
        return $title;
 } #}}}
 
 sub linkpage ($) { #{{{
        my $link=shift;
-       $link=~s/([^-[:alnum:]:+\/._])/$1 eq ' ' ? '_' : "__".ord($1)."__"/eg;
+       my $chars = defined $config{wiki_file_chars} ? $config{wiki_file_chars} : "-[:alnum:]+/.:_";
+       $link=~s/([^$chars])/$1 eq ' ' ? '_' : "__".ord($1)."__"/eg;
        return $link;
 } #}}}
 
@@ -1030,6 +1076,8 @@ sub preprocess ($$$;$$) { #{{{
                my $prefix=shift;
                my $command=shift;
                my $params=shift;
+               $params="" if ! defined $params;
+
                if (length $escape) {
                        return "[[$prefix$command $params]]";
                }
@@ -1074,13 +1122,10 @@ sub preprocess ($$$;$$) { #{{{
                        if ($preprocessing{$page}++ > 3) {
                                # Avoid loops of preprocessed pages preprocessing
                                # other pages that preprocess them, etc.
-                               #translators: The first parameter is a
-                               #translators: preprocessor directive name,
-                               #translators: the second a page name, the
-                               #translators: third a number.
-                               return "[[".sprintf(gettext("%s preprocessing loop detected on %s at depth %i"),
-                                       $command, $page, $preprocessing{$page}).
-                               "]]";
+                               return "[[!$command <span class=\"error\">".
+                                       sprintf(gettext("preprocessing loop detected on %s at depth %i"),
+                                               $page, $preprocessing{$page}).
+                                       "</span>]]";
                        }
                        my $ret;
                        if (! $scan) {
@@ -1437,6 +1482,46 @@ sub run_hooks ($$) { # {{{
        return 1;
 } #}}}
 
+sub rcs_update () { #{{{
+       $hooks{rcs}{rcs_update}{call}->(@_);
+} #}}}
+
+sub rcs_prepedit ($) { #{{{
+       $hooks{rcs}{rcs_prepedit}{call}->(@_);
+} #}}}
+
+sub rcs_commit ($$$;$$) { #{{{
+       $hooks{rcs}{rcs_commit}{call}->(@_);
+} #}}}
+
+sub rcs_commit_staged ($$$) { #{{{
+       $hooks{rcs}{rcs_commit_staged}{call}->(@_);
+} #}}}
+
+sub rcs_add ($) { #{{{
+       $hooks{rcs}{rcs_add}{call}->(@_);
+} #}}}
+
+sub rcs_remove ($) { #{{{
+       $hooks{rcs}{rcs_remove}{call}->(@_);
+} #}}}
+
+sub rcs_rename ($$) { #{{{
+       $hooks{rcs}{rcs_rename}{call}->(@_);
+} #}}}
+
+sub rcs_recentchanges ($) { #{{{
+       $hooks{rcs}{rcs_recentchanges}{call}->(@_);
+} #}}}
+
+sub rcs_diff ($) { #{{{
+       $hooks{rcs}{rcs_diff}{call}->(@_);
+} #}}}
+
+sub rcs_getctime ($) { #{{{
+       $hooks{rcs}{rcs_getctime}{call}->(@_);
+} #}}}
+
 sub globlist_to_pagespec ($) { #{{{
        my @globlist=split(' ', shift);