$config{wikistatedir}="$config{srcdir}/.ikiwiki"
unless exists $config{wikistatedir};
- if ($config{svn}) {
- require IkiWiki::Rcs::SVN;
- $config{rcs}=1;
+ if ($config{rcs}) {
+ eval qq{require IkiWiki::Rcs::$config{rcs}};
+ if ($@) {
+ error("Failed to load RCS module IkiWiki::Rcs::$config{rcs}: $@");
+ }
}
else {
require IkiWiki::Rcs::Stub;
- $config{rcs}=0;
}
foreach my $plugin (@{$config{plugin}}) {
+++ /dev/null
-#!/usr/bin/perl
-# For subversion support.
-
-use warnings;
-use strict;
-use IkiWiki;
-
-package IkiWiki;
-
-my $svn_log_infoline=qr/^r(\d+)\s+\|\s+([^\s]+)\s+\|\s+(\d+-\d+-\d+\s+\d+:\d+:\d+\s+[-+]?\d+).*/;
-my $svn_webcommit=qr/^web commit by (\w+):?(.*)/;
-
-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_update () { #{{{
- if (-d "$config{srcdir}/.svn") {
- if (system("svn", "update", "--quiet", $config{srcdir}) != 0) {
- warn("svn update failed\n");
- }
- }
-} #}}}
-
-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)=$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}") != 0) {
- 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") {
- my $parent=dirname($file);
- while (! -d "$config{srcdir}/$parent/.svn") {
- $file=$parent;
- $parent=dirname($file);
- }
-
- if (system("svn", "add", "--quiet", "$config{srcdir}/$file") != 0) {
- warn("svn add failed\n");
- }
- }
-} #}}}
-
-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 $svn_url=svn_info("URL", $config{srcdir});
-
- my $div=qr/^--------------------+$/;
- my $state='start';
- my ($rev, $user, $when, @pages, @message);
- foreach (`LANG=C svn log -v '$svn_url'`) {
- chomp;
- if ($state eq 'start' && /$div/) {
- $state='header';
- }
- elsif ($state eq 'header' && /$svn_log_infoline/) {
- $rev=$1;
- $user=$2;
- $when=concise(ago(time - str2time($3)));
- }
- elsif ($state eq 'header' && /^\s+[A-Z]+\s+\/\Q$config{svnpath}\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';
- }
- elsif ($state eq 'body' && /$div/) {
- my $committype="web";
- if (defined $message[0] &&
- $message[0]->{line}=~/$svn_webcommit/) {
- $user="$1";
- $message[0]->{line}=$2;
- }
- else {
- $committype="svn";
- }
-
- push @ret, { rev => $rev,
- user => htmllink("", $user, 1),
- committype => $committype,
- when => $when, message => [@message],
- pages => [@pages],
- } if @pages;
- return @ret if @ret >= $num;
-
- $state='header';
- $rev=$user=$when=undef;
- @pages=@message=();
- }
- elsif ($state eq 'body') {
- push @message, {line => escapeHTML($_)},
- }
- }
- }
-
- return @ret;
-} #}}}
-
-sub rcs_notify () { #{{{
- if (! exists $ENV{REV}) {
- error("REV is not set, not running from svn post-commit hook, cannot send notifications");
- }
- my $rev=int(possibly_foolish_untaint($ENV{REV}));
-
- my $user=`svnlook author $config{svnrepo} -r $rev`;
- chomp $user;
- my $message=`svnlook log $config{svnrepo} -r $rev`;
- if ($message=~/$svn_webcommit/) {
- $user="$1";
- $message=$2;
- }
-
- my @changed_pages;
- foreach my $change (`svnlook changed $config{svnrepo} -r $rev`) {
- chomp $change;
- if ($change =~ /^[A-Z]+\s+\Q$config{svnpath}\E\/(.*)/) {
- push @changed_pages, $1;
- }
- }
-
- require IkiWiki::UserInfo;
- my @email_recipients=commit_notify_list($user, @changed_pages);
- if (@email_recipients) {
- # TODO: if a commit spans multiple pages, this will send
- # subscribers a diff that might contain pages they did not
- # sign up for. Should separate the diff per page and
- # reassemble into one mail with just the pages subscribed to.
- my $diff=`svnlook diff $config{svnrepo} -r $rev --no-diff-deleted`;
-
- my $subject="$config{wikiname} update of ";
- if (@changed_pages > 2) {
- $subject.="$changed_pages[0] $changed_pages[1] etc";
- }
- else {
- $subject.=join(" ", @changed_pages);
- }
- $subject.=" by $user";
-
- my $template=HTML::Template->new(
- filename => "$config{templatedir}/notifymail.tmpl"
- );
- $template->param(
- wikiname => $config{wikiname},
- diff => $diff,
- user => $user,
- message => $message,
- );
-
- eval q{use Mail::Sendmail};
- foreach my $email (@email_recipients) {
- sendmail(
- To => $email,
- From => "$config{wikiname} <$config{adminemail}>",
- Subject => $subject,
- Message => $template->output,
- ) or error("Failed to send update notification mail");
- }
- }
-} #}}}
-
-sub rcs_getctime () { #{{{
- eval q{use Date::Parse};
- foreach my $page (keys %pagectime) {
- my $file="$config{srcdir}/$pagesources{$page}";
- next unless -e $file;
- my $child = open(SVNLOG, "-|");
- if (! $child) {
- exec("svn", "log", $file) || error("svn log $file failed to run");
- }
-
- my $date;
- while (<SVNLOG>) {
- if (/$svn_log_infoline/) {
- $date=$3;
- }
- }
- close SVNLOG || warn "svn log $file exited $?";
-
- if (! defined $date) {
- warn "failed to parse svn log for $file\n";
- next;
- }
-
- $pagectime{$page}=$date=str2time($date);
- debug("found ctime ".localtime($date)." for $page");
- }
-} #}}}
-
-1
--- /dev/null
+#!/usr/bin/perl
+# For subversion support.
+
+use warnings;
+use strict;
+use IkiWiki;
+
+package IkiWiki;
+
+my $svn_log_infoline=qr/^r(\d+)\s+\|\s+([^\s]+)\s+\|\s+(\d+-\d+-\d+\s+\d+:\d+:\d+\s+[-+]?\d+).*/;
+my $svn_webcommit=qr/^web commit by (\w+):?(.*)/;
+
+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_update () { #{{{
+ if (-d "$config{srcdir}/.svn") {
+ if (system("svn", "update", "--quiet", $config{srcdir}) != 0) {
+ warn("svn update failed\n");
+ }
+ }
+} #}}}
+
+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)=$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}") != 0) {
+ 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") {
+ my $parent=dirname($file);
+ while (! -d "$config{srcdir}/$parent/.svn") {
+ $file=$parent;
+ $parent=dirname($file);
+ }
+
+ if (system("svn", "add", "--quiet", "$config{srcdir}/$file") != 0) {
+ warn("svn add failed\n");
+ }
+ }
+} #}}}
+
+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 $svn_url=svn_info("URL", $config{srcdir});
+
+ my $div=qr/^--------------------+$/;
+ my $state='start';
+ my ($rev, $user, $when, @pages, @message);
+ foreach (`LANG=C svn log -v '$svn_url'`) {
+ chomp;
+ if ($state eq 'start' && /$div/) {
+ $state='header';
+ }
+ elsif ($state eq 'header' && /$svn_log_infoline/) {
+ $rev=$1;
+ $user=$2;
+ $when=concise(ago(time - str2time($3)));
+ }
+ elsif ($state eq 'header' && /^\s+[A-Z]+\s+\/\Q$config{svnpath}\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';
+ }
+ elsif ($state eq 'body' && /$div/) {
+ my $committype="web";
+ if (defined $message[0] &&
+ $message[0]->{line}=~/$svn_webcommit/) {
+ $user="$1";
+ $message[0]->{line}=$2;
+ }
+ else {
+ $committype="svn";
+ }
+
+ push @ret, { rev => $rev,
+ user => htmllink("", $user, 1),
+ committype => $committype,
+ when => $when, message => [@message],
+ pages => [@pages],
+ } if @pages;
+ return @ret if @ret >= $num;
+
+ $state='header';
+ $rev=$user=$when=undef;
+ @pages=@message=();
+ }
+ elsif ($state eq 'body') {
+ push @message, {line => escapeHTML($_)},
+ }
+ }
+ }
+
+ return @ret;
+} #}}}
+
+sub rcs_notify () { #{{{
+ if (! exists $ENV{REV}) {
+ error("REV is not set, not running from svn post-commit hook, cannot send notifications");
+ }
+ my $rev=int(possibly_foolish_untaint($ENV{REV}));
+
+ my $user=`svnlook author $config{svnrepo} -r $rev`;
+ chomp $user;
+ my $message=`svnlook log $config{svnrepo} -r $rev`;
+ if ($message=~/$svn_webcommit/) {
+ $user="$1";
+ $message=$2;
+ }
+
+ my @changed_pages;
+ foreach my $change (`svnlook changed $config{svnrepo} -r $rev`) {
+ chomp $change;
+ if ($change =~ /^[A-Z]+\s+\Q$config{svnpath}\E\/(.*)/) {
+ push @changed_pages, $1;
+ }
+ }
+
+ require IkiWiki::UserInfo;
+ my @email_recipients=commit_notify_list($user, @changed_pages);
+ if (@email_recipients) {
+ # TODO: if a commit spans multiple pages, this will send
+ # subscribers a diff that might contain pages they did not
+ # sign up for. Should separate the diff per page and
+ # reassemble into one mail with just the pages subscribed to.
+ my $diff=`svnlook diff $config{svnrepo} -r $rev --no-diff-deleted`;
+
+ my $subject="$config{wikiname} update of ";
+ if (@changed_pages > 2) {
+ $subject.="$changed_pages[0] $changed_pages[1] etc";
+ }
+ else {
+ $subject.=join(" ", @changed_pages);
+ }
+ $subject.=" by $user";
+
+ my $template=HTML::Template->new(
+ filename => "$config{templatedir}/notifymail.tmpl"
+ );
+ $template->param(
+ wikiname => $config{wikiname},
+ diff => $diff,
+ user => $user,
+ message => $message,
+ );
+
+ eval q{use Mail::Sendmail};
+ foreach my $email (@email_recipients) {
+ sendmail(
+ To => $email,
+ From => "$config{wikiname} <$config{adminemail}>",
+ Subject => $subject,
+ Message => $template->output,
+ ) or error("Failed to send update notification mail");
+ }
+ }
+} #}}}
+
+sub rcs_getctime () { #{{{
+ eval q{use Date::Parse};
+ foreach my $page (keys %pagectime) {
+ my $file="$config{srcdir}/$pagesources{$page}";
+ next unless -e $file;
+ my $child = open(SVNLOG, "-|");
+ if (! $child) {
+ exec("svn", "log", $file) || error("svn log $file failed to run");
+ }
+
+ my $date;
+ while (<SVNLOG>) {
+ if (/$svn_log_infoline/) {
+ $date=$3;
+ }
+ }
+ close SVNLOG || warn "svn log $file exited $?";
+
+ if (! defined $date) {
+ warn "failed to parse svn log for $file\n";
+ next;
+ }
+
+ $pagectime{$page}=$date=str2time($date);
+ debug("found ctime ".localtime($date)." for $page");
+ }
+} #}}}
+
+1
asprintf(&newenviron[i++], "%s=%s", "$var", s);
EOF
}
- if ($config{svn} && $config{notify}) {
+ if ($config{rcs} eq "svn" && $config{notify}) {
# Support running directly as hooks/post-commit by passing
# $2 in REV in the environment.
$envsave.=<<"EOF"
extra_build:
./ikiwiki doc html --templatedir=templates --underlaydir=basewiki \
- --wikiname="ikiwiki" --verbose --nosvn \
+ --wikiname="ikiwiki" --verbose --no-rcs \
--exclude=/discussion --plugin=brokenlinks \
--plugin=pagecount --plugin=orphans
./mdwn2man doc/usage.mdwn > ikiwiki.man
--- /dev/null
+ikiwiki (1.1) unstable; urgency=low
+
+ The --svn and --no-svn switches are removed, instead you should use
+ --rcs=svn or --no-rcs. ikiwiki setup files that set svn => 1 should
+ be changed to set rcs => "svn"; if your setup file sets svn => 0
+ then change it to use rcs => "" to disable svn.
+
+ You will need to rebuild your wiki when upgrading to this version.
+ If you listed your wiki in /etc/ikiwiki/wikilist this will be done
+ automatically.
+
+ -- Joey Hess <joeyh@debian.org> Tue, 2 May 2006 14:13:59 -0400
it, this will allow for adding a unit test suite.
* Add a unit test for globlist_match().
* Smart globlist merging.
+ * Patch from Thomas Schwinge to switch from --svn to --rcs=svn, etc,
+ to pave the way for adding other RCS support. This also changes the
+ setup files, where before they had svn => 1 or svn => 0, now they have
+ rcs => "svn" or rcs => "".
+ * Add a debian/NEWS file with upgrade notes.
+ * Load whatever rcs module is specified, so new ones can be just dropped in
+ as plugins and work.
+ * Add some basic docs about writing RCS modules.
- -- Joey Hess <joeyh@debian.org> Tue, 2 May 2006 10:56:33 -0400
+ -- Joey Hess <joeyh@debian.org> Tue, 2 May 2006 14:13:59 -0400
ikiwiki (1.0) unstable; urgency=low
#diffurl => "http://svn.someurl/trunk/[[file]]?root=wiki&r1=[[r1]]&r2=[[r2]]",
#templatedir => "/usr/share/ikiwiki/templates",
- # Whether to integrate with svn.
- svn => 1,
+ #rcs => "", # don't use svn
+ rcs => "svn", # use svn (the default)
svnrepo => "/svn/wiki",
svnpath => "trunk",
-Plugins can be used to add additional features to ikiwiki. It's possible to
-[[write]] your own plugins, or you can install and use plugins contributed by
-others.
+Plugins can be used to add additional features to ikiwiki. Ikiwiki's
+backend RCS support is also pluggable, so support for new revision control
+systems can be added to ikiwiki. It's possible to [[write]] your own plugins,
+or you can install and use plugins contributed by others.
The ikiiki package includes some standard plugins that are installed and
by default. These include [[inline]], [[pagecount]], and [[brokenlinks]].
Of these, [[inline]] is enabled by default.
-To enable other plugins, use the `--plugin` switch described in [[usage]], or the equivilant line in
-[[ikiwiki.setup]].
+To enable other plugins, use the `--plugin` switch described in [[usage]],
+or the equivalent line in [[ikiwiki.setup]].
## Plugin directory
IkiWiki::Plugin namespace, so they go in a IkiWiki/Plugin subdirectory
inside the perl search path. For example, if your perl looks in
`/usr/local/lib/site_perl` for modules, you can locally install ikiwiki
-plugins to `/usr/local/lib/site_perl/IkiWiki/Plugin`
\ No newline at end of file
+plugins to `/usr/local/lib/site_perl/IkiWiki/Plugin`
Many plugins will need to add dependencies to this hash; the best way to do
it is by using the IkiWiki::add_depends function, which takes as its
parameters the page name and a [[GlobList]] of dependencies to add.
+
+## RCS plugins
+
+ikiwiki's support for revision control systems also uses pluggable perl
+modules. These are in the `IkiWiki::RCS` namespace, for example
+`IkiWiki::RCS::svn`.
+
+Each RCS plugin must support all the IkiWiki::rcs_* functions.
+See IkiWiki::RCS::Stub for the full list of functions. It's ok if
+rcs_getctime does nothing except for throwing an error.
Enable email notification of commits. This should be used when running
ikiwiki as a [[Subversion]] [[post-commit]] hook.
-* --svn, --nosvn
+* --rcs=svn, --no-rcs
- Enable or disable use of [[subversion]]. If subversion is enabled, the
- `source` directory is assumed to be a working copy, and is automatically
- updated before building the wiki.
+ Enable or disable use of a revision control system.
- In [[CGI]] mode, with subversion enabled pages edited via the web will be
- committed to subversion. Also, the [[RecentChanges]] link will be placed
+ If you use svn ([[Subversion]]), the `source` directory is assumed to be
+ a working copy, and is automatically updated before building the wiki.
+
+ In [[CGI]] mode, with a revision control system enabled pages edited via
+ the web will be committed. Also, the [[RecentChanges]] link will be placed
on pages.
- Subversion is enabled by default.
+ svn is enabled by default.
* --svnrepo /svn/wiki
wikiname => "wiki",
default_pageext => ".mdwn",
cgi => 0,
- svn => 1,
+ rcs => 'svn',
notify => 0,
url => '',
cgiurl => '',
"refresh!" => \$config{refresh},
"getctime" => \$config{getctime},
"wrappermode=i" => \$config{wrappermode},
- "svn!" => \$config{svn},
+ "rcs=s" => \$config{rcs},
+ "no-rcs" => sub { $config{rcs}="" },
"anonok!" => \$config{anonok},
"hyperestraier" => \$config{hyperestraier},
"rss!" => \$config{rss},
"diffurl=s" => \$config{diffurl},
"svnrepo" => \$config{svnrepo},
"svnpath" => \$config{svnpath},
+ "rcs=s" => \$config{rcs},
+ "no-rcs" => sub { $config{rcs}="" },
"adminemail=s" => \$config{adminemail},
"exclude=s@" => sub {
$config{wiki_file_prune_regexp}=qr/$config{wiki_file_prune_regexp}|$_[1]/;