]> sipb.mit.edu Git - ikiwiki.git/blobdiff - ikiwiki
web commit by joey: conflict test
[ikiwiki.git] / ikiwiki
diff --git a/ikiwiki b/ikiwiki
index de7c60cc9d20c45fe3fe1a6b09c410f050beea79..78c706c31bea31afabd8479039d76122b0beeead 100755 (executable)
--- a/ikiwiki
+++ b/ikiwiki
@@ -420,6 +420,10 @@ sub lockwiki () { #{{{
        }
 } #}}}
 
+sub unlockwiki () { #{{{
+       close WIKILOCK;
+} #}}}
+
 sub loadindex () { #{{{
        open (IN, "$config{srcdir}/.ikiwiki/index") || return;
        while (<IN>) {
@@ -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,6 +1086,10 @@ 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));
@@ -1039,6 +1103,7 @@ sub cgi_editpage ($$) { #{{{
        else {
                $form->tmpl_param("page_preview", "");
        }
+       $form->tmpl_param("page_conflict", "");
        
        if (! $form->submitted || $form->submitted eq "Preview" || 
            ! $form->validate) {
@@ -1095,17 +1160,10 @@ sub cgi_editpage ($$) { #{{{
                
                $form->tmpl_param("can_commit", $config{svn});
                $form->tmpl_param("indexlink", indexlink());
-               print $form->render(submit => ["Save Page", "Preview", "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;
@@ -1127,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();