]> sipb.mit.edu Git - ikiwiki.git/blobdiff - ikiwiki
fix oops in %config handling
[ikiwiki.git] / ikiwiki
diff --git a/ikiwiki b/ikiwiki
index 4b3bd488eac4715ead8a3e415da2fb1b81694df5..51f324d13abd56ed573ac3861683af89dbe9f19a 100755 (executable)
--- a/ikiwiki
+++ b/ikiwiki
@@ -1,4 +1,7 @@
 #!/usr/bin/perl -T
+
+eval 'exec /usr/bin/perl -T -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
 $ENV{PATH}="/usr/local/bin:/usr/bin:/bin";
 
 use warnings;
@@ -30,8 +33,9 @@ our %config=( #{{{
        wrappermode => undef,
        srcdir => undef,
        destdir => undef,
-       templatedir => undef,
+       templatedir => "/usr/share/ikiwiki/templates",
        setup => undef,
+       adminuser => undef,
 ); #}}}
 
 GetOptions( #{{{
@@ -51,21 +55,28 @@ GetOptions( #{{{
        "exclude=s@" => sub {
                $config{wiki_file_prune_regexp}=qr/$config{wiki_file_prune_regexp}|$_[1]/;
        },
+       "adminuser=s@" => sub { push @{$config{adminuser}}, $_[1] },
+       "templatedir=s" => sub { $config{templatedir}=possibly_foolish_untaint($_[1]) },
 ) || usage();
 
 if (! $config{setup}) {
-       usage() unless @ARGV == 3;
+       usage() unless @ARGV == 2;
        $config{srcdir} = possibly_foolish_untaint(shift);
-       $config{templatedir} = possibly_foolish_untaint(shift);
        $config{destdir} = possibly_foolish_untaint(shift);
+       checkoptions();
+}
+#}}}
+
+sub checkoptions { #{{{
        if ($config{cgi} && ! length $config{url}) {
                error("Must specify url to wiki with --url when using --cgi");
        }
-}
-#}}}
+       $config{wikistatedir}="$config{srcdir}/.ikiwiki"
+               unless exists $config{wikistatedir};
+} #}}}
 
 sub usage { #{{{
-       die "usage: ikiwiki [options] source templates dest\n";
+       die "usage: ikiwiki [options] source dest\n";
 } #}}}
 
 sub error { #{{{
@@ -410,10 +421,11 @@ sub render ($) { #{{{
 sub lockwiki () { #{{{
        # Take an exclusive lock on the wiki to prevent multiple concurrent
        # run issues. The lock will be dropped on program exit.
-       if (! -d "$config{srcdir}/.ikiwiki") {
-               mkdir("$config{srcdir}/.ikiwiki");
+       if (! -d $config{wikistatedir}) {
+               mkdir($config{wikistatedir});
        }
-       open(WIKILOCK, ">$config{srcdir}/.ikiwiki/lockfile") || error ("cannot write to lockfile: $!");
+       open(WIKILOCK, ">$config{wikistatedir}/lockfile") ||
+               error ("cannot write to $config{wikistatedir}/lockfile: $!");
        if (! flock(WIKILOCK, 2 | 4)) {
                debug("wiki seems to be locked, waiting for lock");
                my $wait=600; # arbitrary, but don't hang forever to 
@@ -431,7 +443,7 @@ sub unlockwiki () { #{{{
 } #}}}
 
 sub loadindex () { #{{{
-       open (IN, "$config{srcdir}/.ikiwiki/index") || return;
+       open (IN, "$config{wikistatedir}/index") || return;
        while (<IN>) {
                $_=possibly_foolish_untaint($_);
                chomp;
@@ -447,10 +459,11 @@ sub loadindex () { #{{{
 } #}}}
 
 sub saveindex () { #{{{
-       if (! -d "$config{srcdir}/.ikiwiki") {
-               mkdir("$config{srcdir}/.ikiwiki");
+       if (! -d $config{wikistatedir}) {
+               mkdir($config{wikistatedir});
        }
-       open (OUT, ">$config{srcdir}/.ikiwiki/index") || error("cannot write to index: $!");
+       open (OUT, ">$config{wikistatedir}/index") || 
+               error("cannot write to $config{wikistatedir}/index: $!");
        foreach my $page (keys %oldpagemtime) {
                print OUT "$oldpagemtime{$page} $pagesources{$page} $renderedfiles{$page} ".
                        join(" ", @{$links{$page}})."\n"
@@ -753,8 +766,7 @@ FILE:               foreach my $file (@files) {
        }
 } #}}}
 
-sub gen_wrapper (@) { #{{{
-       my %config=(@_);
+sub gen_wrapper () { #{{{
        eval q{use Cwd 'abs_path'};
        $config{srcdir}=abs_path($config{srcdir});
        $config{destdir}=abs_path($config{destdir});
@@ -767,8 +779,9 @@ sub gen_wrapper (@) { #{{{
                error("cannot create a wrapper that uses a setup file");
        }
        
-       my @params=($config{srcdir}, $config{templatedir}, $config{destdir},
-               "--wikiname=$config{wikiname}");
+       my @params=($config{srcdir}, $config{destdir},
+               "--wikiname=$config{wikiname}",
+               "--templatedir=$config{templatedir}");
        push @params, "--verbose" if $config{verbose};
        push @params, "--rebuild" if $config{rebuild};
        push @params, "--nosvn" if !$config{svn};
@@ -778,6 +791,7 @@ sub gen_wrapper (@) { #{{{
        push @params, "--historyurl=$config{historyurl}" if length $config{historyurl};
        push @params, "--diffurl=$config{diffurl}" if length $config{diffurl};
        push @params, "--anonok" if $config{anonok};
+       push @params, "--adminuser=$_" foreach @{$config{adminuser}};
        my $params=join(" ", @params);
        my $call='';
        foreach my $p ($this, $this, @params) {
@@ -876,9 +890,10 @@ sub userinfo_get ($$) { #{{{
        my $field=shift;
 
        eval q{use Storable};
-       my $userdata=eval{ Storable::lock_retrieve("$config{srcdir}/.ikiwiki/userdb") };
+       my $userdata=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
        if (! defined $userdata || ! ref $userdata || 
-           ! exists $userdata->{$user} || ! ref $userdata->{$user}) {
+           ! exists $userdata->{$user} || ! ref $userdata->{$user} ||
+            ! exists $userdata->{$user}->{$field}) {
                return "";
        }
        return $userdata->{$user}->{$field};
@@ -890,7 +905,7 @@ sub userinfo_set ($$$) { #{{{
        my $value=shift;
        
        eval q{use Storable};
-       my $userdata=eval{ Storable::lock_retrieve("$config{srcdir}/.ikiwiki/userdb") };
+       my $userdata=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
        if (! defined $userdata || ! ref $userdata || 
            ! exists $userdata->{$user} || ! ref $userdata->{$user}) {
                return "";
@@ -898,7 +913,7 @@ sub userinfo_set ($$$) { #{{{
        
        $userdata->{$user}->{$field}=$value;
        my $oldmask=umask(077);
-       my $ret=Storable::lock_store($userdata, "$config{srcdir}/.ikiwiki/userdb");
+       my $ret=Storable::lock_store($userdata, "$config{wikistatedir}/userdb");
        umask($oldmask);
        return $ret;
 } #}}}
@@ -908,13 +923,13 @@ sub userinfo_setall ($$) { #{{{
        my $info=shift;
        
        eval q{use Storable};
-       my $userdata=eval{ Storable::lock_retrieve("$config{srcdir}/.ikiwiki/userdb") };
+       my $userdata=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
        if (! defined $userdata || ! ref $userdata) {
                $userdata={};
        }
        $userdata->{$user}=$info;
        my $oldmask=umask(077);
-       my $ret=Storable::lock_store($userdata, "$config{srcdir}/.ikiwiki/userdb");
+       my $ret=Storable::lock_store($userdata, "$config{wikistatedir}/userdb");
        umask($oldmask);
        return $ret;
 } #}}}
@@ -1079,6 +1094,61 @@ sub cgi_signin ($$) { #{{{
        }
 } #}}}
 
+sub is_admin ($) { #{{{
+       my $user_name=shift;
+
+       return grep { $_ eq $user_name } @{$config{adminuser}};
+} #}}}
+
+sub glob_match ($$) { #{{{
+       my $page=shift;
+       my $glob=shift;
+
+       # turn glob into safe regexp
+       $glob=quotemeta($glob);
+       $glob=~s/\\\*/.*/g;
+       $glob=~s/\\\?/./g;
+       $glob=~s!\\/!/!g;
+       
+       $page=~/^$glob$/i;
+} #}}}
+
+sub globlist_match ($$) { #{{{
+       my $page=shift;
+       my @globlist=split(" ", shift);
+
+       # check any negated globs first
+       foreach my $glob (@globlist) {
+               return 0 if $glob=~/^!(.*)/ && glob_match($page, $1);
+       }
+
+       foreach my $glob (@globlist) {
+               return 1 if glob_match($page, $glob);
+       }
+       
+       return 0;
+} #}}}
+
+sub page_locked ($$;$) { #{{{
+       my $page=shift;
+       my $session=shift;
+       my $nonfatal=shift;
+       
+       my $user=$session->param("name");
+       return if length $user && is_admin($user);
+
+       foreach my $admin (@{$config{adminuser}}) {
+               my $locked_pages=userinfo_get($admin, "locked_pages");
+               if (globlist_match($page, userinfo_get($admin, "locked_pages"))) {
+                       return 1 if $nonfatal;
+                       error(htmllink("", $page, 1)." is locked by ".
+                             htmllink("", $admin, 1)." and cannot be edited.");
+               }
+       }
+
+       return 0;
+} #}}}
+
 sub cgi_prefs ($$) { #{{{
        my $q=shift;
        my $session=shift;
@@ -1086,7 +1156,7 @@ sub cgi_prefs ($$) { #{{{
        eval q{use CGI::FormBuilder};
        my $form = CGI::FormBuilder->new(
                title => "preferences",
-               fields => [qw(do name password confirm_password email)],
+               fields => [qw(do name password confirm_password email locked_pages)],
                header => 0,
                method => 'POST',
                validate => {
@@ -1110,9 +1180,18 @@ sub cgi_prefs ($$) { #{{{
                value => $user_name, force => 1);
        $form->field(name => "password", type => "password");
        $form->field(name => "confirm_password", type => "password");
+       $form->field(name => "locked_pages", size => 50,
+               comment => "(".htmllink("", "GlobList", 1).")");
+       
+       if (! is_admin($user_name)) {
+               $form->field(name => "locked_pages", type => "hidden");
+       }
        
        if (! $form->submitted) {
-               $form->field(name => "email", value => userinfo_get($user_name, "email"));
+               $form->field(name => "email", force => 1,
+                       value => userinfo_get($user_name, "email"));
+               $form->field(name => "locked_pages", force => 1,
+                       value => userinfo_get($user_name, "locked_pages"));
        }
        
        if ($form->submitted eq 'Logout') {
@@ -1125,7 +1204,7 @@ sub cgi_prefs ($$) { #{{{
                return;
        }
        elsif ($form->submitted eq "Save Preferences" && $form->validate) {
-               foreach my $field (qw(password email)) {
+               foreach my $field (qw(password email locked_pages)) {
                        if (length $form->field($field)) {
                                userinfo_set($user_name, $field, $form->field($field)) || error("failed to set $field");
                        }
@@ -1238,8 +1317,10 @@ sub cgi_editpage ($$) { #{{{
                                        push @page_locs, $dir.$page;
                                }
 
-                               @page_locs = grep { ! exists
-                                       $pagesources{lc($_)} } @page_locs;
+                               @page_locs = grep {
+                                       ! exists $pagesources{lc($_)} &&
+                                       ! page_locked($_, $session, 1)
+                               } @page_locs;
                        }
 
                        $form->tmpl_param("page_select", 1);
@@ -1248,6 +1329,7 @@ sub cgi_editpage ($$) { #{{{
                        $form->title("creating $page");
                }
                elsif ($form->field("do") eq "edit") {
+                       page_locked($page, $session);
                        if (! defined $form->field('content') || 
                            ! length $form->field('content')) {
                                my $content="";
@@ -1267,13 +1349,15 @@ sub cgi_editpage ($$) { #{{{
        }
        else {
                # save page
+               page_locked($page, $session);
+               
                my $content=$form->field('content');
                $content=~s/\r\n/\n/g;
                $content=~s/\r/\n/g;
                writefile("$config{srcdir}/$file", $content);
                
                my $message="web commit ";
-               if ($session->param("name")) {
+               if (length $session->param("name")) {
                        $message.="by ".$session->param("name");
                }
                else {
@@ -1341,7 +1425,7 @@ sub cgi () { #{{{
 
        my $oldmask=umask(077);
        my $session = CGI::Session->new("driver:db_file", $q,
-               { FileName => "$config{srcdir}/.ikiwiki/sessions.db" });
+               { FileName => "$config{wikistatedir}/sessions.db" });
        umask($oldmask);
        
        # Everything below this point needs the user to be signed in.
@@ -1387,7 +1471,7 @@ sub setup () { # {{{
 setup() if $config{setup};
 lockwiki();
 if ($config{wrapper}) {
-       gen_wrapper(%config);
+       gen_wrapper();
        exit;
 }
 memoize('pagename');