added adminuser settings, globlist support, and used this to implement page

locking
master
joey 2006-03-23 01:40:46 +00:00
parent 33b7f3444c
commit 325d5c791f
8 changed files with 149 additions and 15 deletions

View File

@ -0,0 +1,16 @@
When the wiki stores lists of pages, such as pages that are locked or pages
that you want to be emailed if changed, it uses a GlobList.
This is a list of page names, separated by white space. The "glob" bit is
that as well as full page names, it can contain glob patterns. "`*`" stands
in for any part of the page name, and "`?`" for any single letter of its
name. So if you wanted to list all the pages about tea, and any
[[SubPage]]s of the SandBox, but not including the SandBox itself:
*tea* SandBox/*
You can also prefix an item in the list with "!" to skip matching any
pages that match it. So if you want to specify all pages except for
Discussion pages:
!*/Discussion

View File

@ -75,10 +75,21 @@ Currently implemented:
* Smart merging and conflict resolution in your web browser
Since it uses subversion, ikiwiki takes advantage of its smart merging to avoid any conflicts when two people edit different parts of the same page at the same time. No annoying warnings about other editors, or locking, etc, instead the other person's changes will be automaticaly merged with yours when you commit.
Since it uses subversion, ikiwiki takes advantage of its smart merging to
avoid any conflicts when two people edit different parts of the same page
at the same time. No annoying warnings about other editors, or locking,
etc, instead the other person's changes will be automaticaly merged with
yours when you commit.
In the rare cases where automatic merging fails due to the same part of a page being concurrently edited, regular subversion commit markers are shown in the file to resolve the conflict, so if you're already familiar with that there's no new commit marker syntax to learn.
In the rare cases where automatic merging fails due to the same part of a
page being concurrently edited, regular subversion commit markers are
shown in the file to resolve the conflict, so if you're already familiar
with that there's no new commit marker syntax to learn.
* page locking
Wiki admin can [[lock]] pages so that only other admins can edit them.
----
It also has lots of [[TODO]] items and [[Bugs]]. This wiki is not ready for production!
It also has some [[TODO]] items and [[Bugs]].

16
doc/globlist.mdwn 100644
View File

@ -0,0 +1,16 @@
When the wiki stores lists of pages, such as pages that are locked or pages
that you want to be emailed if changed, it uses a GlobList.
This is a list of page names, separated by white space. The "glob" bit is
that as well as full page names, it can contain glob patterns. "`*`" stands
in for any part of the page name, and "`?`" for any single letter of its
name. So if you wanted to list all the pages about tea, and any
[[SubPage]]s of the SandBox, but not including the SandBox itself:
*tea* SandBox/*
You can also prefix an item in the list with "!" to skip matching any
pages that match it. So if you want to specify all pages except for
Discussion pages:
!*/Discussion

View File

@ -7,7 +7,8 @@
use IkiWiki::Setup::Standard {
wikiname => "MyWiki",
#adminuser => ["yourname", ],
# Be sure to customise these..
srcdir => "/path/to/source",
destdir => "/var/www/wiki",

View File

@ -50,6 +50,13 @@ It's actually possible to force a whole series of svn commits to appear to have
ikiwiki escapes any html in svn commit logs to prevent other mischief.
## page locking can be bypassed via direct svn commits
A [[lock]]ed page can only be edited on the web by an admin, but
anyone who is allowed to commit direct to svn can bypass this. This is by
design, although a subversion pre-commit hook could be used to prevent
editing of locked pages when using subversion, if you really need to.
----
# Hopefully non-holes
@ -136,4 +143,4 @@ directory with a symlink and trick it into following the link.
Also, if someone checks in a symlink to /etc/passwd, ikiwiki would read and publish that, which could be used to expose files a committer otherwise wouldn't see.
To avoid this, ikiwiki will avoid reading files that are symlinks, and uses locking to prevent more than one instance running at a time. The lock prevents one ikiwiki from running a svn up at the wrong time to race another ikiwiki. So only attackers who can write to the working copy on their own can race it.
To avoid this, ikiwiki will avoid reading files that are symlinks, and uses locking to prevent more than one instance running at a time. The lock prevents one ikiwiki from running a svn up at the wrong time to race another ikiwiki. So only attackers who can write to the working copy on their own can race it.

View File

@ -104,11 +104,14 @@ you need that data..
Might be nice to support automatically generating an index based on headers in a page, for long pages. The question is, how to turn on such an index?
## page locking
## basewiki underlay
Some wikis will need the abiity to lock a page, or the whole wiki, so that only admins can edit them. Probably using the same globbing as for recentchanges mails to determine what to lock.
Rather than copy the basewii around everywhere, it should be configured to
underlay the main srcdir, and pages be rendered from there if not in the
srcdir. This would allow upgrades to add/edit pages in the basewiki.
Probably it's ok if locking is only supported for web commits.
Impementaion will be slightly tricky since currently ikiwiki is hardcoded
in many places to look in srcdir for pages.
## Logo

View File

@ -94,6 +94,12 @@ flags such as --verbose can be negated with --no-verbose.
Specifies a rexexp of source files to exclude from processing.
May be specified multiple times to add to exclude list.
* --adminuser name
Specifies a username of a user who has the powers of a wiki admin.
Currently allows locking of any page, other powers may be added later.
May be specified multiple times for multiple admins.
* --setup configfile
In setup mode, ikiwiki reads the config file, which is really a perl

88
ikiwiki
View File

@ -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;
@ -32,6 +35,7 @@ our %config=( #{{{
destdir => undef,
templatedir => undef,
setup => undef,
adminuser => undef,
); #}}}
GetOptions( #{{{
@ -51,6 +55,7 @@ GetOptions( #{{{
"exclude=s@" => sub {
$config{wiki_file_prune_regexp}=qr/$config{wiki_file_prune_regexp}|$_[1]/;
},
"adminuser=s@" => sub { push @{$config{adminuser}}, $_[1] },
) || usage();
if (! $config{setup}) {
@ -778,6 +783,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) {
@ -878,7 +884,8 @@ sub userinfo_get ($$) { #{{{
eval q{use Storable};
my $userdata=eval{ Storable::lock_retrieve("$config{srcdir}/.ikiwiki/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};
@ -1079,6 +1086,59 @@ 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.");
}
}
} #}}}
sub cgi_prefs ($$) { #{{{
my $q=shift;
my $session=shift;
@ -1086,7 +1146,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 +1170,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 +1194,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 +1307,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 +1319,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 +1339,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 {