X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/blobdiff_plain/7bbfbf82fb17c05cc70ec042c1fdd4e0fb370b12..ae6655411567dfb620ec64e257be9a294fe9a478:/ikiwiki diff --git a/ikiwiki b/ikiwiki index bda2d0401..78c706c31 100755 --- a/ikiwiki +++ b/ikiwiki @@ -247,10 +247,10 @@ sub htmllink { #{{{ sub linkify ($$) { #{{{ my $content=shift; - my $file=shift; + my $page=shift; $content =~ s{(\\?)$config{wiki_link_regexp}}{ - $1 ? "[[$2]]" : htmllink(pagename($file), $2) + $1 ? "[[$2]]" : htmllink($page, $2) }eg; return $content; @@ -372,7 +372,7 @@ sub check_overwrite ($$) { #{{{ ", before, so not rendering from $src"); } } #}}} - + sub render ($) { #{{{ my $file=shift; @@ -383,7 +383,7 @@ sub render ($) { #{{{ $links{$page}=[findlinks($content, $page)]; - $content=linkify($content, $file); + $content=linkify($content, $page); $content=htmlize($type, $content); $content=finalize($content, $page); @@ -420,6 +420,10 @@ sub lockwiki () { #{{{ } } #}}} +sub unlockwiki () { #{{{ + close WIKILOCK; +} #}}} + sub loadindex () { #{{{ open (IN, "$config{srcdir}/.ikiwiki/index") || return; while () { @@ -457,19 +461,59 @@ sub rcs_update () { #{{{ } } #}}} -sub rcs_commit ($) { #{{{ +sub rcs_prepedit ($) { #{{{ + # Prepares to edit a file under revision control. Returns a token + # that must be passed into rcs_commit when the file is ready + # for committing. + # The file is relative to the srcdir. + my $file=shift; + + if (-d "$config{srcdir}/.svn") { + # For subversion, return the revision of the file when + # editing begins. + my $rev=svn_info("Revision", "$config{srcdir}/$file"); + return defined $rev ? $rev : ""; + } +} #}}} + +sub rcs_commit ($$$) { #{{{ + # Tries to commit the page; returns undef on _success_ and + # a version of the page with the rcs's conflict markers on failure. + # The file is relative to the srcdir. + my $file=shift; my $message=shift; + my $rcstoken=shift; if (-d "$config{srcdir}/.svn") { + # Check to see if the page has been changed by someone + # else since rcs_prepedit was called. + my $oldrev=int($rcstoken); + my $rev=svn_info("Revision", "$config{srcdir}/$file"); + if ($rev != $oldrev) { + # Merge their changes into the file that we've + # changed. + if (system("svn", "merge", "--quiet", "-r$oldrev:$rev", + "$config{srcdir}/$file") != 0) { + warn("svn merge -r$oldrev:$rev failed\n"); + } + } + if (system("svn", "commit", "--quiet", "-m", possibly_foolish_untaint($message), - $config{srcdir}) != 0) { + "$config{srcdir}/$file") != 0) { warn("svn commit failed\n"); + my $conflict=readfile("$config{srcdir}/$file"); + if (system("svn", "revert", "--quiet", "$config{srcdir}/$file") != 0) { + warn("svn revert failed\n"); + } + return $conflict; } } + return undef # success } #}}} sub rcs_add ($) { #{{{ + # filename is relative to the root of the srcdir my $file=shift; if (-d "$config{srcdir}/.svn") { @@ -485,16 +529,25 @@ sub rcs_add ($) { #{{{ } } #}}} +sub svn_info ($$) { #{{{ + my $file=shift; + my $field=shift; + + my $info=`LANG=C svn info $file`; + my ($ret)=$info=~/^$field: (.*)$/m; + return $ret; +} #}}} + sub rcs_recentchanges ($) { #{{{ my $num=shift; my @ret; + eval q{use CGI 'escapeHTML'}; eval q{use Date::Parse}; eval q{use Time::Duration}; if (-d "$config{srcdir}/.svn") { - my $info=`LANG=C svn info $config{srcdir}`; - my ($svn_url)=$info=~/^URL: (.*)$/m; + my $svn_url=svn_info("URL", $config{srcdir}); # FIXME: currently assumes that the wiki is somewhere # under trunk in svn, doesn't support other layouts. @@ -563,10 +616,9 @@ sub prune ($) { #{{{ } #}}} sub refresh () { #{{{ - # Find existing pages. + # find existing pages my %exists; my @files; - eval q{use File::Find}; find({ no_chdir => 1, @@ -607,7 +659,7 @@ sub refresh () { #{{{ foreach my $page (keys %oldpagemtime) { if (! $exists{$page}) { debug("removing old page $page"); - push @del, $renderedfiles{$page}; + push @del, $pagesources{$page}; prune($config{destdir}."/".$renderedfiles{$page}); delete $renderedfiles{$page}; $oldpagemtime{$page}=0; @@ -999,7 +1051,7 @@ sub cgi_editpage ($$) { #{{{ eval q{use CGI::FormBuilder}; my $form = CGI::FormBuilder->new( - fields => [qw(do from page content comments)], + fields => [qw(do rcsinfo from page content comments)], header => 1, method => 'POST', validate => { @@ -1012,6 +1064,7 @@ sub cgi_editpage ($$) { #{{{ table => 0, template => "$config{templatedir}/editpage.tmpl" ); + my @buttons=("Save Page", "Preview", "Cancel"); my ($page)=$form->param('page')=~/$config{wiki_file_regexp}/; if (! defined $page || ! length $page || $page ne $q->param('page') || @@ -1019,6 +1072,13 @@ sub cgi_editpage ($$) { #{{{ error("bad page name"); } $page=lc($page); + + my $file=$page.$config{default_pageext}; + my $newfile=1; + if (exists $pagesources{lc($page)}) { + $file=$pagesources{lc($page)}; + $newfile=0; + } $form->field(name => "do", type => 'hidden'); $form->field(name => "from", type => 'hidden'); @@ -1026,12 +1086,27 @@ sub cgi_editpage ($$) { #{{{ $form->field(name => "comments", type => "text", size => 80); $form->field(name => "content", type => "textarea", rows => 20, cols => 80); + if (! $form->submitted) { + $form->field(name => "rcsinfo", type => 'hidden', + value => rcs_prepedit($file), force => 1); + } if ($form->submitted eq "Cancel") { print $q->redirect("$config{url}/".htmlpage($page)); return; } - if (! $form->submitted || ! $form->validate) { + elsif ($form->submitted eq "Preview") { + $form->tmpl_param("page_preview", + htmlize($config{default_pageext}, + linkify($form->field('content'), $page))); + } + else { + $form->tmpl_param("page_preview", ""); + } + $form->tmpl_param("page_conflict", ""); + + if (! $form->submitted || $form->submitted eq "Preview" || + ! $form->validate) { if ($form->field("do") eq "create") { if (exists $pagesources{lc($page)}) { # hmm, someone else made the page in the @@ -1069,31 +1144,26 @@ sub cgi_editpage ($$) { #{{{ $form->title("creating $page"); } elsif ($form->field("do") eq "edit") { - my $content=""; - if (exists $pagesources{lc($page)}) { - $content=readfile("$config{srcdir}/$pagesources{lc($page)}"); - $content=~s/\n/\r\n/g; + if (! length $form->field('content')) { + my $content=""; + if (exists $pagesources{lc($page)}) { + $content=readfile("$config{srcdir}/$pagesources{lc($page)}"); + $content=~s/\n/\r\n/g; + } + $form->field(name => "content", value => $content, + force => 1); } $form->tmpl_param("page_select", 0); - $form->field(name => "content", value => $content, - force => 1); $form->field(name => "page", type => 'hidden'); $form->title("editing $page"); } $form->tmpl_param("can_commit", $config{svn}); $form->tmpl_param("indexlink", indexlink()); - print $form->render(submit => ["Save Page", "Cancel"]); + print $form->render(submit => \@buttons); } else { # save page - my $file=$page.$config{default_pageext}; - my $newfile=1; - if (exists $pagesources{lc($page)}) { - $file=$pagesources{lc($page)}; - $newfile=0; - } - my $content=$form->field('content'); $content=~s/\r\n/\n/g; $content=~s/\r/\n/g; @@ -1115,9 +1185,20 @@ sub cgi_editpage ($$) { #{{{ if ($newfile) { rcs_add($file); } + # prevent deadlock with post-commit hook + unlockwiki(); # presumably the commit will trigger an update # of the wiki - rcs_commit($message); + my $conflict=rcs_commit($file, $message, + $form->field("rcsinfo")); + + if (defined $conflict) { + $form->tmpl_param("page_conflict", 1); + $form->field("content", $conflict); + $form->field("do", "edit)"); + print $form->render(submit => \@buttons); + return; + } } else { loadindex();