]> sipb.mit.edu Git - ikiwiki.git/blobdiff - ikiwiki
add --diffurl, if set RecentChanges has links to svn diffs
[ikiwiki.git] / ikiwiki
diff --git a/ikiwiki b/ikiwiki
index db0056aa4dcad553704468fe90aac778534a647a..54589ec2e05cc24252c59783cab9e7a3503e71b2 100755 (executable)
--- a/ikiwiki
+++ b/ikiwiki
@@ -13,7 +13,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 +23,7 @@ our %config=( #{{{
        url => '',
        cgiurl => '',
        historyurl => '',
+       diffurl => '',
        anonok => 0,
        rebuild => 0,
        wrapper => undef,
@@ -46,6 +47,7 @@ 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]/;
        },
@@ -324,9 +326,10 @@ sub indexlink () { #{{{
        return "<a href=\"$config{url}\">$config{wikiname}</a>";
 } #}}}
 
-sub finalize ($$) { #{{{
+sub finalize ($$$) { #{{{
        my $content=shift;
        my $page=shift;
+       my $mtime=shift;
 
        my $title=basename($page);
        $title=~s/_/ /g;
@@ -343,7 +346,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 +357,7 @@ sub finalize ($$) { #{{{
                content => $content,
                backlinks => [backlinks($page)],
                discussionlink => htmllink($page, "Discussion", 1, 1),
+               mtime => scalar(gmtime($mtime)),
        );
        
        return $template->output;
@@ -385,7 +389,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);
@@ -461,24 +466,49 @@ 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 $message=shift;
        my $file=shift;
+       my $message=shift;
+       my $rcstoken=shift;
 
        if (-d "$config{srcdir}/.svn") {
-               # svn up to let svn merge in other changes
-               if (system("svn", "update", "$config{srcdir}/$file") != 0) {
-                       warn("svn update failed\n");
+               # Check to see if the page has been changed by someone
+               # else since rcs_prepedit was called.
+               my ($oldrev)=$rcstoken=~/^([0-9]+)$/; # untaint
+               my $rev=svn_info("Revision", "$config{srcdir}/$file");
+               if (defined $rev && defined $oldrev && $rev != $oldrev) {
+                       # Merge their changes into the file that we've
+                       # changed.
+                       chdir($config{srcdir}); # svn merge wants to be here
+                       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}/$file") != 0) {
-                       warn("svn commit failed\n");
+                          "$config{srcdir}") != 0) {
                        my $conflict=readfile("$config{srcdir}/$file");
-                       if (system("svn", "revert", "$config{srcdir}/$file") != 0) {
+                       if (system("svn", "revert", "--quiet", "$config{srcdir}/$file") != 0) {
                                warn("svn revert failed\n");
                        }
                        return $conflict;
@@ -504,6 +534,15 @@ sub rcs_add ($) { #{{{
        }
 } #}}}
 
+sub svn_info ($$) { #{{{
+       my $field=shift;
+       my $file=shift;
+
+       my $info=`LANG=C svn info $file`;
+       my ($ret)=$info=~/^$field: (.*)$/m;
+       return $ret;
+} #}}}
+
 sub rcs_recentchanges ($) { #{{{
        my $num=shift;
        my @ret;
@@ -513,8 +552,7 @@ sub rcs_recentchanges ($) { #{{{
        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.
@@ -534,9 +572,16 @@ sub rcs_recentchanges ($) { #{{{
                                $user=$2;
                                $when=concise(ago(time - str2time($3)));
                        }
-                       elsif ($state eq 'header' && /^\s+[A-Z]\s+\Q$svn_base\E\/(.+)$/) {
-                               push @pages, { link => htmllink("", pagename($1), 1) }
-                                       if length $1;
+                       elsif ($state eq 'header' && /^\s+[A-Z]\s+\Q$svn_base\E\/([^ ]+)(?:$|\s)/) {
+                               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';
@@ -556,7 +601,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';
@@ -729,6 +775,7 @@ 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};
        my $params=join(" ", @params);
        my $call='';
@@ -1018,7 +1065,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 => {
@@ -1039,13 +1086,29 @@ 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');
+       $form->field(name => "rcsinfo", type => 'hidden');
        $form->field(name => "page", value => "$page", force => 1);
        $form->field(name => "comments", type => "text", size => 80);
        $form->field(name => "content", type => "textarea", rows => 20,
                cols => 80);
+       $form->tmpl_param("can_commit", $config{svn});
+       $form->tmpl_param("indexlink", indexlink());
+       $form->tmpl_param("helponformattinglink",
+               htmllink("", "HelpOnFormatting", 1));
+       if (! $form->submitted) {
+               $form->field(name => "rcsinfo", value => rcs_prepedit($file),
+                       force => 1);
+       }
        
        if ($form->submitted eq "Cancel") {
                print $q->redirect("$config{url}/".htmlpage($page));
@@ -1082,9 +1145,16 @@ sub cgi_editpage ($$) { #{{{
                        else {
                                my $dir=$from."/";
                                $dir=~s![^/]+/$!!;
+                               
+                               if ($page eq 'discussion') {
+                                       $best_loc="$from/$page";
+                               }
+                               else {
+                                       $best_loc=$dir.$page;
+                               }
+                               
                                push @page_locs, $dir.$page;
                                push @page_locs, "$from/$page";
-                               $best_loc="$from/$page";
                                while (length $dir) {
                                        $dir=~s![^/]+/$!!;
                                        push @page_locs, $dir.$page;
@@ -1100,7 +1170,8 @@ sub cgi_editpage ($$) { #{{{
                        $form->title("creating $page");
                }
                elsif ($form->field("do") eq "edit") {
-                       if (! length $form->field('content')) {
+                       if (! defined $form->field('content') || 
+                           ! length $form->field('content')) {
                                my $content="";
                                if (exists $pagesources{lc($page)}) {
                                                $content=readfile("$config{srcdir}/$pagesources{lc($page)}");
@@ -1114,19 +1185,10 @@ sub cgi_editpage ($$) { #{{{
                        $form->title("editing $page");
                }
                
-               $form->tmpl_param("can_commit", $config{svn});
-               $form->tmpl_param("indexlink", indexlink());
                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;
@@ -1152,12 +1214,18 @@ sub cgi_editpage ($$) { #{{{
                        unlockwiki();
                        # presumably the commit will trigger an update
                        # of the wiki
-                       my $conflict=rcs_commit($file, $message);
+                       my $conflict=rcs_commit($file, $message,
+                               $form->field("rcsinfo"));
                
                        if (defined $conflict) {
+                               $form->field(name => "rcsinfo", value => rcs_prepedit($file),
+                                       force => 1);
                                $form->tmpl_param("page_conflict", 1);
-                               $form->field("content", $conflict);
+                               $form->field("content", value => $conflict, force => 1);
                                $form->field("do", "edit)");
+                               $form->tmpl_param("page_select", 0);
+                               $form->field(name => "page", type => 'hidden');
+                               $form->title("editing $page");
                                print $form->render(submit => \@buttons);
                                return;
                        }
@@ -1234,8 +1302,8 @@ sub setup () { # {{{
 } #}}}
 
 # main {{{
-lockwiki();
 setup() if $config{setup};
+lockwiki();
 if ($config{wrapper}) {
        gen_wrapper(%config);
        exit;