X-Git-Url: https://scripts.mit.edu/gitweb/www/ikiwiki.git/blobdiff_plain/1dac1cfc01d097711206320b7910cb296ae8c246..483f61d228a547627512ec14cc16345bdd4dc159:/ikiwiki diff --git a/ikiwiki b/ikiwiki index d1a31eb55..80ad72526 100755 --- 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; @@ -13,7 +16,7 @@ my (%links, %oldlinks, %oldpagemtime, %renderedfiles, %pagesources); # Holds global config settings, also used by some modules. our %config=( #{{{ wiki_file_prune_regexp => qr{((^|/).svn/|\.\.|^\.|\/\.|\.html?$)}, - wiki_link_regexp => qr/\[\[([^\s]+)\]\]/, + wiki_link_regexp => qr/\[\[([^\s\]]+)\]\]/, wiki_file_regexp => qr/(^[-A-Za-z0-9_.:\/+]+$)/, verbose => 0, wikiname => "wiki", @@ -23,6 +26,7 @@ our %config=( #{{{ url => '', cgiurl => '', historyurl => '', + diffurl => '', anonok => 0, rebuild => 0, wrapper => undef, @@ -31,6 +35,7 @@ our %config=( #{{{ destdir => undef, templatedir => undef, setup => undef, + adminuser => undef, ); #}}} GetOptions( #{{{ @@ -46,9 +51,11 @@ GetOptions( #{{{ "url=s" => \$config{url}, "cgiurl=s" => \$config{cgiurl}, "historyurl=s" => \$config{historyurl}, + "diffurl=s" => \$config{diffurl}, "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}) { @@ -324,9 +331,10 @@ sub indexlink () { #{{{ return "$config{wikiname}"; } #}}} -sub finalize ($$) { #{{{ +sub finalize ($$$) { #{{{ my $content=shift; my $page=shift; + my $mtime=shift; my $title=basename($page); $title=~s/_/ /g; @@ -336,6 +344,7 @@ sub finalize ($$) { #{{{ if (length $config{cgiurl}) { $template->param(editurl => "$config{cgiurl}?do=edit&page=$page"); + $template->param(prefsurl => "$config{cgiurl}?do=prefs"); if ($config{svn}) { $template->param(recentchangesurl => "$config{cgiurl}?do=recentchanges"); } @@ -343,7 +352,7 @@ sub finalize ($$) { #{{{ if (length $config{historyurl}) { my $u=$config{historyurl}; - $u=~s/\[\[\]\]/$pagesources{$page}/g; + $u=~s/\[\[file\]\]/$pagesources{$page}/g; $template->param(historyurl => $u); } @@ -354,6 +363,7 @@ sub finalize ($$) { #{{{ content => $content, backlinks => [backlinks($page)], discussionlink => htmllink($page, "Discussion", 1, 1), + mtime => scalar(gmtime($mtime)), ); return $template->output; @@ -385,7 +395,8 @@ sub render ($) { #{{{ $content=linkify($content, $page); $content=htmlize($type, $content); - $content=finalize($content, $page); + $content=finalize($content, $page, + mtime("$config{srcdir}/$file")); check_overwrite("$config{destdir}/".htmlpage($page), $page); writefile("$config{destdir}/".htmlpage($page), $content); @@ -568,8 +579,15 @@ sub rcs_recentchanges ($) { #{{{ $when=concise(ago(time - str2time($3))); } elsif ($state eq 'header' && /^\s+[A-Z]\s+\Q$svn_base\E\/([^ ]+)(?:$|\s)/) { - push @pages, { link => htmllink("", pagename($1), 1) } - if length $1; + my $file=$1; + my $diffurl=$config{diffurl}; + $diffurl=~s/\[\[file\]\]/$file/g; + $diffurl=~s/\[\[r1\]\]/$rev - 1/eg; + $diffurl=~s/\[\[r2\]\]/$rev/g; + push @pages, { + link => htmllink("", pagename($file), 1), + diffurl => $diffurl, + } if length $file; } elsif ($state eq 'header' && /^$/) { $state='body'; @@ -589,7 +607,8 @@ sub rcs_recentchanges ($) { #{{{ user => htmllink("", $user, 1), committype => $committype, when => $when, message => [@message], - pages => [@pages] } if @pages; + pages => [@pages], + } if @pages; return @ret if @ret >= $num; $state='header'; @@ -762,7 +781,9 @@ sub gen_wrapper (@) { #{{{ push @params, "--url=$config{url}" if length $config{url}; push @params, "--cgiurl=$config{cgiurl}" if length $config{cgiurl}; 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) { @@ -863,13 +884,33 @@ 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}; } #}}} -sub userinfo_set ($$) { #{{{ +sub userinfo_set ($$$) { #{{{ + my $user=shift; + my $field=shift; + my $value=shift; + + 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}) { + return ""; + } + + $userdata->{$user}->{$field}=$value; + my $oldmask=umask(077); + my $ret=Storable::lock_store($userdata, "$config{srcdir}/.ikiwiki/userdb"); + umask($oldmask); + return $ret; +} #}}} + +sub userinfo_setall ($$) { #{{{ my $user=shift; my $info=shift; @@ -891,7 +932,7 @@ sub cgi_signin ($$) { #{{{ eval q{use CGI::FormBuilder}; my $form = CGI::FormBuilder->new( - title => "$config{wikiname} signin", + title => "signin", fields => [qw(do page from name password confirm_password email)], header => 1, method => 'POST', @@ -918,7 +959,7 @@ sub cgi_signin ($$) { #{{{ $form->field(name => "confirm_password", type => "password", required => 0); $form->field(name => "email", required => 0); if ($q->param("do") ne "signin") { - $form->text("You need to log in before you can edit pages."); + $form->text("You need to log in first."); } if ($form->submitted) { @@ -996,7 +1037,7 @@ sub cgi_signin ($$) { #{{{ } elsif ($form->submitted eq 'Register') { my $user_name=$form->field('name'); - if (userinfo_set($user_name, { + if (userinfo_setall($user_name, { 'email' => $form->field('email'), 'password' => $form->field('password'), 'regdate' => time @@ -1045,6 +1086,128 @@ 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; + + eval q{use CGI::FormBuilder}; + my $form = CGI::FormBuilder->new( + title => "preferences", + fields => [qw(do name password confirm_password email locked_pages)], + header => 0, + method => 'POST', + validate => { + confirm_password => { + perl => q{eq $form->field("password")}, + }, + email => 'EMAIL', + }, + required => 'NONE', + javascript => 0, + params => $q, + action => $q->request_uri, + template => (-e "$config{templatedir}/prefs.tmpl" ? + "$config{templatedir}/prefs.tmpl" : "") + ); + my @buttons=("Save Preferences", "Logout", "Cancel"); + + my $user_name=$session->param("name"); + $form->field(name => "do", type => "hidden"); + $form->field(name => "name", disabled => 1, + 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", 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') { + $session->delete(); + print $q->redirect($config{url}); + return; + } + elsif ($form->submitted eq 'Cancel') { + print $q->redirect($config{url}); + return; + } + elsif ($form->submitted eq "Save Preferences" && $form->validate) { + 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"); + } + } + $form->text("Preferences saved."); + } + + print $session->header(); + print misctemplate($form->title, $form->render(submit => \@buttons)); +} #}}} + sub cgi_editpage ($$) { #{{{ my $q=shift; my $session=shift; @@ -1146,8 +1309,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); @@ -1156,6 +1321,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=""; @@ -1175,13 +1341,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 { @@ -1254,7 +1422,8 @@ sub cgi () { #{{{ # Everything below this point needs the user to be signed in. if ((! $config{anonok} && ! defined $session->param("name") || - ! userinfo_get($session->param("name"), "regdate")) || $do eq 'signin') { + ! defined $session->param("name") || + ! userinfo_get($session->param("name"), "regdate")) || $do eq 'signin') { cgi_signin($q, $session); # Force session flush with safe umask. @@ -1268,6 +1437,9 @@ sub cgi () { #{{{ if ($do eq 'create' || $do eq 'edit') { cgi_editpage($q, $session); } + elsif ($do eq 'prefs') { + cgi_prefs($q, $session); + } else { error("unknown do parameter"); }