#!/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;
wrappermode => undef,
srcdir => undef,
destdir => undef,
- templatedir => undef,
+ templatedir => "/usr/share/ikiwiki/templates",
setup => undef,
+ adminuser => undef,
); #}}}
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 { #{{{
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
} #}}}
sub loadindex () { #{{{
- open (IN, "$config{srcdir}/.ikiwiki/index") || return;
+ open (IN, "$config{wikistatedir}/index") || return;
while (<IN>) {
$_=possibly_foolish_untaint($_);
chomp;
} #}}}
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"
}
} #}}}
-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});
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};
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) {
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};
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 "";
$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;
} #}}}
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;
} #}}}
}
} #}}}
+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;
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 => {
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') {
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");
}
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);
$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="";
}
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 {
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.
setup() if $config{setup};
lockwiki();
if ($config{wrapper}) {
- gen_wrapper(%config);
+ gen_wrapper();
exit;
}
memoize('pagename');