Mail::Sendmail
CGI
Data::Dumper
-YAML
+YAML::XS
JSON
RPC::XML
our @EXPORT = qw(hook debug error htmlpage template template_depends
deptype add_depends pagespec_match pagespec_match_list bestlink
htmllink readfile writefile pagetype srcfile pagename
- displaytime will_render gettext ngettext urlto targetpage
+ displaytime strftime_utf8 will_render gettext ngettext urlto targetpage
add_underlay pagetitle titlepage linkpage newpagefile
inject add_link add_autofile
%config %links %pagestate %wikistate %renderedfiles
$format=$config{timeformat};
}
+ return strftime_utf8($format, localtime($time));
+}
+
+my $strftime_encoding;
+sub strftime_utf8 {
# strftime doesn't know about encodings, so make sure
- # its output is properly treated as utf8
- return decode_utf8(POSIX::strftime($format, localtime($time)));
+ # its output is properly treated as utf8.
+ # Note that this does not handle utf-8 in the format string.
+ $strftime_encoding = POSIX::setlocale(&POSIX::LC_TIME) =~ m#\.([^@]+)#
+ unless defined $strftime_encoding;
+ $strftime_encoding
+ ? Encode::decode($strftime_encoding, POSIX::strftime(@_))
+ : POSIX::strftime(@_);
}
sub date_3339 ($) {
}
sub match_backlink ($$;@) {
- my $ret=match_link($_[1], $_[0], @_);
- $ret->influences($_[1] => $IkiWiki::DEPEND_LINKS);
+ my $page=shift;
+ my $testpage=shift;
+ my %params=@_;
+ if ($testpage eq '.') {
+ $testpage = $params{'location'}
+ }
+ my $ret=match_link($testpage, $page, @_);
+ $ret->influences($testpage => $IkiWiki::DEPEND_LINKS);
return $ret;
}
my @attachments;
my $dir=attachment_holding_location($form->field('page'));
foreach my $filename (glob("$dir/*")) {
+ $filename=Encode::decode_utf8($filename);
next unless -f $filename;
my $destdir=$config{srcdir}."/".
linkpage(IkiWiki::possibly_foolish_untaint(
my $dir=attachment_holding_location($page);
my $heldmsg=gettext("this attachment is not yet saved");
foreach my $file (glob("$dir/*")) {
+ $file=Encode::decode_utf8($file);
next unless -f $file;
my $base=IkiWiki::basename($file);
my $f=$loc.$base;
use strict;
use IkiWiki 3.00;
use Time::Local;
-use POSIX ();
my $time=time;
my @now=localtime($time);
}
# Find out month names for this, next, and previous months
- my $monthabbrev=POSIX::strftime("%b", @monthstart);
- my $monthname=POSIX::strftime("%B", @monthstart);
- my $pmonthname=POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900)));
- my $nmonthname=POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900)));
+ my $monthabbrev=strftime_utf8("%b", @monthstart);
+ my $monthname=strftime_utf8("%B", @monthstart);
+ my $pmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900)));
+ my $nmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900)));
my $archivebase = 'archives';
$archivebase = $config{archivebase} if defined $config{archivebase};
my %dowabbr;
for my $dow ($week_start_day..$week_start_day+6) {
my @day=localtime(timelocal(0,0,0,$start_day++,$params{month}-1,$params{year}-1900));
- my $downame = POSIX::strftime("%A", @day);
+ my $downame = strftime_utf8("%A", @day);
my $dowabbr = substr($downame, 0, 1);
$downame{$dow % 7}=$downame;
$dowabbr{$dow % 7}=$dowabbr;
for (my $month = 1; $month <= 12; $month++) {
my @day=localtime(timelocal(0,0,0,15,$month-1,$params{year}-1900));
my $murl;
- my $monthname = POSIX::strftime("%B", @day);
- my $monthabbr = POSIX::strftime("%b", @day);
+ my $monthname = strftime_utf8("%B", @day);
+ my $monthabbr = strftime_utf8("%b", @day);
$calendar.=qq{\t<tr>\n} if ($month % $params{months_per_row} == 1);
my $tag;
my $mtag=sprintf("%02d", $month);
use strict;
use IkiWiki 3.00;
use Encode;
-use POSIX qw(strftime);
use constant PREVIEW => "Preview";
use constant POST_COMMENT => "Post comment";
}
$content .= " subject=\"$subject\"\n";
- $content .= " date=\"" . decode_utf8(strftime('%Y-%m-%dT%H:%M:%SZ', gmtime)) . "\"\n";
+ $content .= " date=\"" . strftime_utf8('%Y-%m-%dT%H:%M:%SZ', gmtime) . "\"\n";
my $editcontent = $form->field('editcontent');
$editcontent="" if ! defined $editcontent;
use File::chdir;
+
+# GENERAL PLUGIN API CALLS
+
sub import {
- hook(type => "genwrapper", id => "cvs", call => \&genwrapper);
hook(type => "checkconfig", id => "cvs", call => \&checkconfig);
hook(type => "getsetup", id => "cvs", call => \&getsetup);
+ hook(type => "genwrapper", id => "cvs", call => \&genwrapper);
+
hook(type => "rcs", id => "rcs_update", call => \&rcs_update);
hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit);
hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit);
hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime);
}
-sub genwrapper () {
- return <<EOF;
- {
- int j;
- for (j = 1; j < argc; j++)
- if (strstr(argv[j], "New directory") != NULL)
- exit(0);
- }
-EOF
-}
-
sub checkconfig () {
if (! defined $config{cvspath}) {
$config{cvspath}="ikiwiki";
},
}
-sub cvs_info ($$) {
- my $field=shift;
- my $file=shift;
-
- local $CWD = $config{srcdir};
-
- my $info=`cvs status $file`;
- my ($ret)=$info=~/^\s*$field:\s*(\S+)/m;
- return $ret;
+sub genwrapper () {
+ return <<EOF;
+ {
+ int j;
+ for (j = 1; j < argc; j++)
+ if (strstr(argv[j], "New directory") != NULL)
+ exit(0);
+ }
+EOF
}
-sub cvs_runcvs(@) {
- my @cmd = @_;
- unshift @cmd, 'cvs', '-Q';
-
- local $CWD = $config{srcdir};
-
- open(my $savedout, ">&STDOUT");
- open(STDOUT, ">", "/dev/null");
- my $ret = system(@cmd);
- open(STDOUT, ">&", $savedout);
- return ($ret == 0) ? 1 : 0;
-}
-
-sub cvs_is_controlling {
- my $dir=shift;
- $dir=$config{srcdir} unless defined($dir);
- return (-d "$dir/CVS") ? 1 : 0;
-}
+# VCS PLUGIN API CALLS
sub rcs_update () {
- return unless cvs_is_controlling;
+ return unless cvs_is_controlling();
cvs_runcvs('update', '-dP');
}
# The file is relative to the srcdir.
my $file=shift;
- return unless cvs_is_controlling;
+ return unless cvs_is_controlling();
# For cvs, return the revision of the file when
# editing begins.
return defined $rev ? $rev : "";
}
-sub commitmessage (@) {
- my %params=@_;
-
- if (defined $params{session}) {
- if (defined $params{session}->param("name")) {
- return "web commit by ".
- $params{session}->param("name").
- (length $params{message} ? ": $params{message}" : "");
- }
- elsif (defined $params{session}->remote_addr()) {
- return "web commit from ".
- $params{session}->remote_addr().
- (length $params{message} ? ": $params{message}" : "");
- }
- }
- return $params{message};
-}
-
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 %params=@_;
- return unless cvs_is_controlling;
+ return unless cvs_is_controlling();
# Check to see if the page has been changed by someone
# else since rcs_prepedit was called.
my $parent=IkiWiki::dirname($file);
my @files_to_add = ($file);
- eval q{use File::MimeInfo};
- error($@) if $@;
-
until ((length($parent) == 0) || cvs_is_controlling("$config{srcdir}/$parent")){
push @files_to_add, $parent;
$parent = IkiWiki::dirname($parent);
while ($file = pop @files_to_add) {
if (@files_to_add == 0) {
# file
- my $filemime = File::MimeInfo::default($file);
- if (defined($filemime) && $filemime eq 'text/plain') {
- cvs_runcvs('add', $file) ||
- warn("cvs add $file failed\n");
- }
- else {
- cvs_runcvs('add', '-kb', $file) ||
- warn("cvs add binary $file failed\n");
- }
+ cvs_runcvs('add', cvs_keyword_subst_args($file)) ||
+ warn("cvs add $file failed\n");
}
else {
# directory
# filename is relative to the root of the srcdir
my $file=shift;
- return unless cvs_is_controlling;
+ return unless cvs_is_controlling();
cvs_runcvs('rm', '-f', $file) ||
warn("cvs rm $file failed\n");
# filenames relative to the root of the srcdir
my ($src, $dest)=@_;
- return unless cvs_is_controlling;
+ return unless cvs_is_controlling();
local $CWD = $config{srcdir};
my $num = shift;
my @ret;
- return unless cvs_is_controlling;
+ return unless cvs_is_controlling();
eval q{use Date::Parse};
error($@) if $@;
error "rcs_getmtime is not implemented for cvs\n"; # TODO
}
+
+# INTERNAL SUPPORT ROUTINES
+
+sub commitmessage (@) {
+ my %params=@_;
+
+ if (defined $params{session}) {
+ if (defined $params{session}->param("name")) {
+ return "web commit by ".
+ $params{session}->param("name").
+ (length $params{message} ? ": $params{message}" : "");
+ }
+ elsif (defined $params{session}->remote_addr()) {
+ return "web commit from ".
+ $params{session}->remote_addr().
+ (length $params{message} ? ": $params{message}" : "");
+ }
+ }
+ return $params{message};
+}
+
+sub cvs_info ($$) {
+ my $field=shift;
+ my $file=shift;
+
+ local $CWD = $config{srcdir};
+
+ my $info=`cvs status $file`;
+ my ($ret)=$info=~/^\s*$field:\s*(\S+)/m;
+ return $ret;
+}
+
+sub cvs_is_controlling {
+ my $dir=shift;
+ $dir=$config{srcdir} unless defined($dir);
+ return (-d "$dir/CVS") ? 1 : 0;
+}
+
+sub cvs_keyword_subst_args ($) {
+ my $file = shift;
+
+ local $CWD = $config{srcdir};
+
+ eval q{use File::MimeInfo};
+ error($@) if $@;
+ my $filemime = File::MimeInfo::default($file);
+ # if (-T $file) {
+
+ if (defined($filemime) && $filemime eq 'text/plain') {
+ return ($file);
+ }
+ else {
+ return ('-kb', $file);
+ }
+}
+
+sub cvs_runcvs(@) {
+ my @cmd = @_;
+ unshift @cmd, 'cvs', '-Q';
+
+ local $CWD = $config{srcdir};
+
+ open(my $savedout, ">&STDOUT");
+ open(STDOUT, ">", "/dev/null");
+ my $ret = system(@cmd);
+ open(STDOUT, ">&", $savedout);
+
+ return ($ret == 0) ? 1 : 0;
+}
+
1
safe => 1,
rebuild => 1,
},
+ nodiscount => {
+ type => "boolean",
+ example => 0,
+ description => "disable use of markdown discount?",
+ safe => 1,
+ rebuild => 1,
+ },
}
my $markdown_sub;
}
}
}
- if (! defined $markdown_sub) {
+ if (! defined $markdown_sub &&
+ (! exists $config{nodiscount} || ! $config{nodiscount})) {
eval q{use Text::Markdown::Discount};
if (! $@) {
$markdown_sub=sub {
+ my $t=shift;
# Workaround for discount binding bug
# https://rt.cpan.org/Ticket/Display.html?id=73657
- return "" if $_[0]=~/^\s*$/;
- Text::Markdown::Discount::markdown(@_);
+ return "" if $t=~/^\s*$/;
+ # Workaround for discount's eliding
+ # of <style> blocks.
+ # https://rt.cpan.org/Ticket/Display.html?id=74016
+ $t=~s/<style/<elyts/ig;
+ my $r=Text::Markdown::Discount::markdown($t);
+ $r=~s/<elyts/<style/ig;
+ return $r;
}
}
}
}
}
- $t=~s{\%A-}{my @yest=@t; $yest[6]--; strftime("%A", \@yest)}eg;
+ $t=~s{\%A-}{my @yest=@t; $yest[6]--; strftime_utf8("%A", \@yest)}eg;
$format=~s/\%X/$t/g;
- return strftime($format, \@t);
+ return strftime_utf8($format, \@t);
}
1
my @lines=IkiWiki::rcs_diff($params{rev}, $maxlines+1);
if (@lines) {
my $diff;
+ my $trunc=0;
if (@lines > $maxlines) {
- $diff=join("", @lines[0..($maxlines-1)])."\n".
- gettext("(Diff truncated)");
+ $diff=join("", @lines[0..($maxlines-1)]);
+ $trunc=1;
}
else {
$diff=join("", @lines);
}
+ if (length $diff > 102400) {
+ $diff=substr($diff, 0, 10240);
+ $trunc=1;
+ }
+ if ($trunc) {
+ $diff.="\n".gettext("(Diff truncated)");
+ }
# escape html
$diff = encode_entities($diff);
# escape links and preprocessor stuff
my $class=shift;
my $content=shift;
- eval q{use YAML::Any};
- eval q{use YAML} if $@;
+ eval q{use YAML::XS};
die $@ if $@;
- $YAML::Syck::ImplicitUnicode=1;
IkiWiki::Setup::merge(Load(encode_utf8($content)));
}
my $type=shift;
my $prefix=shift;
- eval q{use YAML::Old};
- eval q{use YAML} if $@;
+ eval q{use YAML::XS};
die $@ if $@;
- $YAML::UseHeader=0;
+ $YAML::XS::QuoteNumericStrings=0;
- my $dump=Dump({$key => $value});
+ my $dump=decode_utf8(Dump({$key => $value}));
+ $dump=~s/^---\n//; # yaml header, we don't want
chomp $dump;
if (length $prefix) {
$dump=join("\n", map { $prefix.$_ } split(/\n/, $dump));
-ikiwiki (3.20111230) UNRELEASED; urgency=low
+ikiwiki (3.20120116) UNRELEASED; urgency=low
+
+ * mdwn: Added nodiscount setting, which can be used to avoid using the
+ markdown discount engine, when maximum compatability is needed.
+ * Switch to YAML::XS to work around insanity in YAML::Mo. Closes: #657533
+ * cvs: Ensure text files are added in non-binary mode. (Amitai Schlair)
+ * cvs: Various cleanups and testing. (Amitai Schlair)
+ * calendar, prettydate: Fix strftime encoding bug.
+ * shortcuts: Fixed a broken shortcut to wikipedia (accidentially
+ made into a shortcut to wikiMedia).
+
+ -- Joey Hess <joeyh@debian.org> Mon, 16 Jan 2012 13:41:14 -0400
+
+ikiwiki (3.20120115) unstable; urgency=low
+
+ * Make backlink(.) work. Thanks, Giuseppe Bilotta.
+ * mdwn: Workaround discount's eliding of <style> blocks.
+ * attachment: Fix utf-8 display bug.
+
+ -- Joey Hess <joeyh@debian.org> Sun, 15 Jan 2012 16:19:25 -0400
+
+ikiwiki (3.20120109) unstable; urgency=low
* mdwn: Can use the discount markdown library, via the
Text::Markdown::Discount perl module. This is preferred if available
since it's the fastest currently supported markdown library, speeding up
- ikiwiki's rendering by a factor of 40.
+ ikiwiki's markdown rendering by a factor of 40.
(However, when multimarkdown is enabled, Text::Markdown::Multimarkdown
is still used.)
* On Debian, depend on libtext-markdown-discount.
- -- Joey Hess <joeyh@debian.org> Sun, 01 Jan 2012 16:22:24 -0400
+ -- Joey Hess <joeyh@debian.org> Mon, 09 Jan 2012 11:49:14 -0400
ikiwiki (3.20111229) unstable; urgency=low
libtimedate-perl, libhtml-template-perl,
libhtml-scrubber-perl, wdg-html-validator,
libhtml-parser-perl, liburi-perl (>= 1.36), perlmagick, po4a (>= 0.34),
- libfile-chdir-perl, libyaml-perl, python-support
+ libfile-chdir-perl, libyaml-libyaml-perl, python-support
Maintainer: Joey Hess <joeyh@debian.org>
Uploaders: Josh Triplett <josh@freedesktop.org>
Standards-Version: 3.9.2
Depends: ${misc:Depends}, ${perl:Depends}, ${python:Depends},
libtext-markdown-discount-perl,
libhtml-scrubber-perl, libhtml-template-perl,
- libhtml-parser-perl, liburi-perl (>= 1.36), libyaml-perl, libjson-perl
+ libhtml-parser-perl, liburi-perl (>= 1.36), libyaml-libyaml-perl, libjson-perl
Recommends: gcc | c-compiler,
libc6-dev | libc-dev,
git (>= 1:1.7) | git-core (>= 1:1.5.0) | subversion | tla | bzr (>= 0.91) | mercurial | monotone (>= 0.38) | darcs,
--- /dev/null
+Hello,
+
+I studied this [[guy's problem|forum/Encoding_problem_in_french_with_ikiwiki-calendar]] and I propose here a (dirty) hack to correct it.
+
+Bug summary: when using the [[calendar plugin|plugins/calendar]] in French (`LANG=fr_FR.UTF-8`), "Décembre" (French for "December") is rendered as "Décembre".
+
+I managed to track this problem down to an encoding problem of `POSIX::strftime` in `Ikiwiki/Plugin/calendar.pm`. I used [[this guy's solution|http://www.perlmonks.org/?node_id=857018]] to solve the problem (the diff is printed below).
+
+The problem is that I do not know Perl, encoding is one of the thing I would be happy not to dive into, and it is the first time I contribute to Ikiwiki: I copied and made a few changes to the code I found without understanding it. So I am not sure that my code is neat, or works in every situation. Feel free to (help me to) improve it!
+
+Cheers,
+Louis
+
+> Yes, this seems basically right. I've applied a modified version of this.
+> [[done]]
+> --[[Joey]]
+
+
+ diff --git a/IkiWiki/Plugin/calendar.pm b/IkiWiki/Plugin/calendar.pm
+ index c7d2b7c..1345939 100644
+ --- a/IkiWiki/Plugin/calendar.pm
+ +++ b/IkiWiki/Plugin/calendar.pm
+ @@ -22,7 +22,14 @@ use warnings;
+ use strict;
+ use IkiWiki 3.00;
+ use Time::Local;
+ -use POSIX ();
+ +
+ +use POSIX qw/setlocale LC_TIME strftime/;
+ +use Encode;
+ +my ($strftime_encoding)= setlocale(LC_TIME)=~m#\.([^@]+)#;
+ +sub strftime_utf8 {
+ +# try to return an utf8 value from strftime
+ + $strftime_encoding ? Encode::decode($strftime_encoding, &strftime) : &strftime;
+ +}
+
+ my $time=time;
+ my @now=localtime($time);
+ @@ -123,10 +130,10 @@ sub format_month (@) {
+ }
+
+ # Find out month names for this, next, and previous months
+ - my $monthabbrev=POSIX::strftime("%b", @monthstart);
+ - my $monthname=POSIX::strftime("%B", @monthstart);
+ - my $pmonthname=POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900)));
+ - my $nmonthname=POSIX::strftime("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900)));
+ + my $monthabbrev=strftime_utf8("%b", @monthstart);
+ + my $monthname=strftime_utf8("%B", @monthstart);
+ + my $pmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$pmonth-1,$pyear-1900)));
+ + my $nmonthname=strftime_utf8("%B", localtime(timelocal(0,0,0,1,$nmonth-1,$nyear-1900)));
+
+ my $archivebase = 'archives';
+ $archivebase = $config{archivebase} if defined $config{archivebase};
+ @@ -182,7 +189,7 @@ EOF
+ my %dowabbr;
+ for my $dow ($week_start_day..$week_start_day+6) {
+ my @day=localtime(timelocal(0,0,0,$start_day++,$params{month}-1,$params{year}-1900));
+ - my $downame = POSIX::strftime("%A", @day);
+ + my $downame = strftime_utf8("%A", @day);
+ my $dowabbr = substr($downame, 0, 1);
+ $downame{$dow % 7}=$downame;
+ $dowabbr{$dow % 7}=$dowabbr;
+ @@ -329,8 +336,8 @@ EOF
+ for (my $month = 1; $month <= 12; $month++) {
+ my @day=localtime(timelocal(0,0,0,15,$month-1,$params{year}-1900));
+ my $murl;
+ - my $monthname = POSIX::strftime("%B", @day);
+ - my $monthabbr = POSIX::strftime("%b", @day);
+ + my $monthname = strftime_utf8("%B", @day);
+ + my $monthabbr = strftime_utf8("%b", @day);
+ $calendar.=qq{\t<tr>\n} if ($month % $params{months_per_row} == 1);
+ my $tag;
+ my $mtag=sprintf("%02d", $month);
--- /dev/null
+I have ikiwiki_3.20111229 installed on Debian Squeeze (Perl 5.10.1, UTF-8
+locale). The attachment plugin mangles UTF8-encoded attachment filenames if
+the name contains multibyte characters, e.g. "lää.png" becomes "lää.png".
+Apparently glob returns byte strings which are subject to implicit
+upgrading when concatenated with Perl strings. The following patch fixes
+the problem for me:
+
+----
+
+ diff -r -U 1 a/attachment.pm b/attachment.pm
+ --- a/attachment.pm 2012-01-13 23:07:29.000000000 +0200
+ +++ b/attachment.pm 2012-01-13 23:33:07.000000000 +0200
+ @@ -274,2 +274,3 @@
+ foreach my $filename (glob("$dir/*")) {
+ + $filename=Encode::decode_utf8($filename);
+ next unless -f $filename;
+ @@ -347,2 +348,3 @@
+ foreach my $file (glob("$dir/*")) {
+ + $file = Encode::decode_utf8($file);
+ next unless -f $file;
+
+> Seems it only mangled display of the just-uploaded attachment's filename,
+> the attachment was otherwise saved to disk with a valid UTF-8 name, and
+> doing other stuff with it also was ok. In any case, I applied your patch,
+> thanks. [[done]] --[[Joey]]
--- /dev/null
+It seems `backlink(.)` doesn't work, that is, it doesn't match pages linked
+to from the current page.
+
+If I have two test pages, `foo`, which links to `bar`, then (on the `foo`
+page):
+
+ * backlink(foo) lists 'bar'
+ * backlink(.) lists nothing
+
+tested with 3.20120109.
+
+— [[Jon]]
+
+> The attached patch should fix it:
+
+>> [[applied|done]] thanks --[[Joey]]
+
+ From 30512ac5f6a724bafb1095ab246e0648999f7b6c Mon Sep 17 00:00:00 2001
+ From: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
+ Date: Fri, 13 Jan 2012 11:02:11 +0100
+ Subject: [PATCH] backlink(.) should behave like backlink(<current page>)
+
+ Since commit c4d4cad3befbbd444d094cbeb0b6ebba3910a025, the single dot in
+ a pagespec can be used to mean the current page. While this worked
+ correctly in link() it didn't work in backlink(). Fix this by explicitly
+ checking the testpage in backlink against . and replacing it with the
+ current location if necessary.
+ ---
+ IkiWiki.pm | 10 ++++++++--
+ 1 files changed, 8 insertions(+), 2 deletions(-)
+
+ diff --git a/IkiWiki.pm b/IkiWiki.pm
+ index 08e242a..bc56501 100644
+ --- a/IkiWiki.pm
+ +++ b/IkiWiki.pm
+ @@ -2647,8 +2647,14 @@ sub match_link ($$;@) {
+ }
+
+ sub match_backlink ($$;@) {
+ - my $ret=match_link($_[1], $_[0], @_);
+ - $ret->influences($_[1] => $IkiWiki::DEPEND_LINKS);
+ + my $page=shift;
+ + my $testpage=shift;
+ + my %params=@_;
+ + if ($testpage eq '.') {
+ + $testpage = $params{'location'}
+ + }
+ + my $ret=match_link($testpage, $page, @_);
+ + $ret->influences($testpage => $IkiWiki::DEPEND_LINKS);
+ return $ret;
+ }
+
+ --
+ 1.7.8.rc2.253.gdbf3
+
+
+> (you need to re-make IkiWiki for it to work)
+++ /dev/null
-phil hands is kindly hosting rhombus-tech.net on an h-branchable ikiwiki. openid has been enabled, for convenience. unfortunately... :/etc/ikiwiki-hosting/ikiwiki-hosting.conf has an openid realm of "http://*.hands.com" which is kinda important (i assume) for security reasons. however this conflicts with what openid requires. openid logins are now specifying the realm of http://*.hands.com which of course doesn't match with http://rhombus-tech.net - it all goes pear-shaped from there.
-
-any ideas? thanks folks.
>>>>> explicitly removed", so if ikiwiki can preferentially find that
>>>>> installed, even with the above commit, `openid` won't be able to
>>>>> traverse a proxy. --[[schmonz]]
+
+[[!template id=gitbranch branch=schmonz/proxies author="[[schmonz]]"]]
+
+>>>>> I bollixed up my git, recloned, and reapplied the diffs, so
+>>>>> that commit won't exist anymore. My proxy-related changes are
+>>>>> now on a branch. --[[schmonz]]
--- /dev/null
+When an `ikiwiki` instance is holding a lock, a web user clicking on "add comment" (for example) will have to wait for the lock to be released. However, all they are then presented with is a web form. Perhaps CGI requests that are read-only (such as generating a comment form, or perhaps certain types of edits) should ignore locks? Of course, I'd understand that the submission would need to wait for a lock. — [[Jon]]
+
+> Ikiwiki has what I think of as the Big Wiki Lock (remembering the "Big
+> Kernel Lock"). It takes the exclusive lock before loading any state,
+> to ensure that any changes to that state are made safely.
+>
+> A few CGI actions that don't need that info loaded do avoid taking the
+> lock.
+>
+> In the case of showing the comment form, the comments
+> plugin needs CGI session information to be loaded, so it can check if
+> the user is logged in, and so it can add XSRF prevention tokens based on
+> the session ID. (Actually, it might be possible to rely on
+> `CGI::Session`'s own locking of the sessions file, and have a hook that
+> runs with a session but before the indexdb is loaded.)
+>
+> But, the comment form also needs to load the indexdb, in order to call
+> `check_canedit`, which matches a pagespec, which can need to look things
+> up in the indexdb. (Though the pagespecs that can do that are unlikely
+> to be relevant when posting a comment.)
+>
+> I've thought about trying to get rid of the Big Wiki Lock from time to
+> time. It's difficult though; if two ikiwikis are both making changes
+> to the stored state, it's hard to see a way to reconcile them. (There
+> could be a daemon that all changes are fed thru using a protocol, but
+> that's really complicated, and it'd almost be better to have a single
+> daemon that just runs ikiwiki; a major architectural change.)
+>
+> One way that *almost* seems it could work is to have a entry path
+> that loads everything read-only, without a lock. And then in read-only
+> mode, `saveindex` would be an error to run. However, both the commenting
+> code and the page edit code currently have the same entry path for
+> drawing the form as is used for handling the posted form, so they would
+> need to be adapted to separate that into two code paths. --[[Joey]]
Note that for more formal bug reports or todo items, you can also edit the
[[bugs]] and [[todo]] pages.
+
## Current topics ##
[[!inline pages="forum/* and !forum/discussion and !forum/*/*"
--- /dev/null
+Unfortunately debian stable / squeeze repos are still on version 3.20100815.7
+
+Do you think squeeze will update to a newer version soon? I am lacking support of the gallery plugin and 1 more that I forgot right now ;)
+
+Is everyone else upgrading by directly installing via dpkg (no updates, but yeah)?
+
+Regards
--- /dev/null
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 1"
+ date="2012-01-16T14:52:22Z"
+ content="""
+Nothing wrong with 3.20100815.7 unless you need newer features.
+
+At Branchable we run Debian stable and backport the current ikiwiki to run on it. This is quite easy to do, it just builds and works. (Edit debian/control and s/markdown-discount/markdown/)
+
+It's probably time someone released a backport. I have historically not done that myself though.
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawndsaC4GaIBw49WNdbk2Faqfm_mrtQgul8"
+ nickname="Christian"
+ subject="thats cool"
+ date="2012-01-16T15:31:22Z"
+ content="""
+thanks
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawndsaC4GaIBw49WNdbk2Faqfm_mrtQgul8"
+ nickname="Christian"
+ subject="Great! It worked!"
+ date="2012-01-17T01:18:06Z"
+ content="""
+Thanks Joey, also for the replacement hint, had to install one or two dependencies and it worked like a charm. Version from the day before yesterday now. (Awesome!)
+
+I have not upgraded the mypage.setup file, yet. I included \"headinganchors\" which does not seem to work right now. The page compiles without errors but contains no anchors when viewed in browser. Hm..
+
+Great job thanks again!
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawndsaC4GaIBw49WNdbk2Faqfm_mrtQgul8"
+ nickname="Christian"
+ subject="updating setup file"
+ date="2012-01-17T01:30:32Z"
+ content="""
+just for the record - I created a new wiki and the setup file is then automatically in YAML format. I think I am going to just transfer the settings from the \"old\" setup file to the new one.
+
+If anyone has an idea why the headinganchors don't work, pls let me know.
+
+As for the rest - insanely cool piece of software - great
+"""]]
--- /dev/null
+Hi!
+
+I'm using the ikiwiki calendar plugin.
+
+My website is in french (locale fr_FR.UTF-8), and calendars that are generated by the plugin makes some encodi$
+
+I don't know how the plugin generate translation for dates, but I've seen that there is no ikiwiki translation$
+
+That's why I suppose (but I'm not sure) that it use date unix command to insert date into the html page, witho$
+
+Could I have forgotten some options to make it nice or not?
+
+Is someone could test it and verify if it works or not?
+
+Thanks.
+
+Zut
+
+> This was discussed in [[bugs/Encoding_problem_in_calendar_plugin]]
+> and is now fixed. --[[Joey]]
--- /dev/null
+How to allow .markdown and .md (at the same time) as valid extensions for source files? The default is .mdwn.
--- /dev/null
+Well, I simply don't see it.
+I would like to change the "account registration" page, where it says user, password, repeat password, Account Creation Password, E-Mail.
+
+I simply want it to ask a question like "Who's your daddy" or "What are we all working on" instead of "Account creation password".
+
+I already grepped through the files of the source which I compiled ikiwiki from - I just can't find it. I'm a noob in cgi, it seems to be somewhat in there, but that could also be totally wrong.
+
+Can you tell me where to look?
+
--- /dev/null
+I followed instructions at
+
+ http://ikiwiki.info/plugins/po/
+
+and added to `configfile`
+
+ po_master_language => 'en|English',
+ po_slave_languages => [ 'zh|Chinese' ],
+ po_translatable_pages => '(* and !*/Discussion and !blog/*/comment_*)',
+ po_link_to => 'current'
+
+and did
+
+ ikiwiki --setup configfile
+
+But I don't seem to see any change in the newly built site.
+
+How do I actually use po to create translation pages?
+
+1) I have existing pages that's in English. How do I add translated versions of some of those pages in the slave language?
+
+2) How do I add new pages with the primary language version and alternative versions in slave languages?
+
+The documentation of po is not explicit with what are the concrete steps.
--- /dev/null
+Since ikiwiki doesn't have much of a chance of working in windows, how about we compromise by making an offline ikiwiki editor for Windows? In fact, it might be advantageous to use it in Linux, too...
+
+It should be very simple: It would enter the source wiki and show the Markdown code by default, but would have an option to preview your page in another tab.
+
+Basic features:
+
+* wikilinks, maps, images, inlinepages, and other basic functions should all work in the preview
+* perhaps use local.css to format preview
+* See the DVCS history with diffs and all
+* have a discussion tab to easily see what other people have said about the page
+
+If we want to add some more bells and whistles, maybe we could throw in some buttons to insert markdown formatting (like in forums, mediawiki, or RES).
+
+Any thoughts on this?
--- /dev/null
+[[!comment format=mdwn
+ username="http://kerravonsen.dreamwidth.org/"
+ ip="202.173.183.92"
+ subject="comment 1"
+ date="2012-01-13T22:32:47Z"
+ content="""
+It would probably be quite complex to write, and difficult to maintain. I don't think much of your chances of getting someone to write it. If you want to write it yourself, have fun doing so!
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawkr8GVPw30JBR34Btg-SKcS8gxEf7zpSJQ"
+ nickname="Lawrence"
+ subject="comment 2"
+ date="2012-01-14T03:14:38Z"
+ content="""
+Eh, ok, lol. I know that implementing most of the wiki features over again could be difficult, and so would a Git diff reader, but it shouldn't be that hard to get Wikilinking or a markdown previewer working.
+
+Could you point out some specific problems of this approach, so that it would help me out to do so?
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawkr8GVPw30JBR34Btg-SKcS8gxEf7zpSJQ"
+ nickname="Lawrence"
+ subject="comment 3"
+ date="2012-01-14T17:41:52Z"
+ content="""
+Like, there's already a whole host of Markdown previewer apps that are pretty good. [Here's a](http://www.macworld.com/article/164744/2012/01/marked_excels_at_previewing_markdown_and_html_documents.html) popular one on Mac, and there are many more...
+
+There's also a plugin for Emacs that does so, and even resolves wikilinks (in some way..).
+
+But I'd have to say that I probably made a misleading title, WYSIWYG would probably be low on the list of needed features. And I'm just dumping an idea I have here in case anyone has any suggestions or comments, I'll probably do it myself in my free time.
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawndsaC4GaIBw49WNdbk2Faqfm_mrtQgul8"
+ nickname="Christian"
+ subject="just my 2 cents"
+ date="2012-01-17T11:10:09Z"
+ content="""
+why?
+"""]]
--- /dev/null
+Hi,
+
+unfortunately, openID is not working at my wiki. I get the error
+
+no_identity_server: The provided URL doesn't declare its OpenID identity server.
+
+I think this is related to the ID of my wiki not being defined right. Where and how do I have to define it? I have used the !meta openid as described on ikiwiki.info (are there two quotes at the end where only one should be (joeyh example) on index.html.
+
+Somehow I think its not transferred right to the openID provider of the user upon login.
+
+thanks in advance
+chris
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawndsaC4GaIBw49WNdbk2Faqfm_mrtQgul8"
+ nickname="Christian"
+ subject="apache module?"
+ date="2012-01-18T15:40:57Z"
+ content="""
+Do I have to install the openid apache module, load it, and configure apache to use my openid? Except that in my case I can get it from the package system, there is a description <a href=\"http://findingscience.com/mod_auth_openid/\">here</a> what I mean. I got a feeling that's it.
+"""]]
--- /dev/null
+How can I add a button to each wiki page which launches an external application or script with the markdown code of the current page as input?
--- /dev/null
+Why doesn't the [[plugins/search]] plugin index attachments? are there any
+technical reasons for not including this feature/option? (besides increased
+processing time, and depending from external programs.)
+
+One could check for all non-mdwn files, convert them to text, if such thing is
+possible, and add them as documents; I guess `needsbuild` would be a good site
+for that.
+
+--[[jerojasro]]
--- /dev/null
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 1"
+ date="2012-01-13T17:46:49Z"
+ content="""
+I don't think there are really any reasons, other than noone having done it.
+
+Although it is worth noting that using additional libraries/programs to eg, pull exif data and comments out of image files and make it searchable, does potentially increase ikiwiki's attack surface.
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="jerojasro"
+ nickname="jerojasro"
+ subject="RE: comment 1"
+ date="2012-01-15T23:49:49Z"
+ content="""
+I've modified the plugin adding the possibility of indexing attachments. Only
+PDF attachments for now, but support for other filetypes should be real easy to add.
+
+The changes to `IkiWiki/Plugin/search.pm` are available at
+<http://git.devnull.li/ikiwiki.git>, in the `srchatt` branch.
+
+I have a small question about filenames and security: I'm using `qx` to execute
+the program that extracts the text from the PDF files, but `qx` executes a
+whole string, and passes it not to the program I want to run, but to a shell,
+so it is possible (I think) to craft a filename that, in a shell, expands to
+something nasty.
+
+How do the Perl/IkiWiki experts suggest to handle these potentially unsafe
+filenames? I've thought of the following options:
+
+ * Running the text extractor program using `Proc::Safe`. I could not find a
+ Debian package for it, and I'd rather avoid adding another dependency to
+ IkiWiki.
+ * Running the text extractor program as suggested in the `perlipc` document,
+ using `fork` + `exec`.
+
+I haven't done any of those because I'd like to check if there are any helpers
+in IkiWiki to do this. Perhaps the `IkiWiki::possibly_foolish_untaint` function
+does it? (I didn't really understand what it does...)
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawljSQThLsc4vHz0jw1aSR74Dj9K5J_NKqk"
+ nickname="Michal"
+ subject="comment 3"
+ date="2012-01-17T16:45:37Z"
+ content="""
+Maybe it could be sufficient to run a command similar to
+
+ omindex --db /path/to/.ikiwiki/xapian/default --url http://webserver/ikiwiki /path/to/public_html
+"""]]
--- /dev/null
+[[!comment format=mdwn
+ username="jerojasro"
+ nickname="jerojasro"
+ subject="RE: comment 1"
+ date="2012-01-21T21:44:00"
+ content="""
+[[Michal]], that's not a bad idea IMO, but we would lose some [[searching
+keywords|ikiwiki/searching]] and would also index structural elements
+(navigation text, and so on)
+"""]]
--- /dev/null
+Just thought people might like to know I've added a couple more plugins to contrib.
+
+[[plugins/contrib/newpage]]: This plugin adds a new action to the "ACTIONS" section of a page; a button labelled "create" and an input field next to it.
+
+The common way of creating a new page is to edit a different page and add a link to the new page. However, there are some situations where that is a nuisance; for example, where pages are listed using a map directive. The newpage plugin enables one to simply type the name of the new page, click the "create" button, and one is then taken to the standard IkiWiki create-page form.
+
+[[plugins/contrib/jssearchfield]]: This plugin provides the [[plugins/contrib/ikiwiki/directive/jssearchfield]] directive. This
+enables one to search the structured data ("field" values) of multiple pages.
+This uses Javascript for the searching, which means that the entire thing
+is self-contained and does not require a server or CGI access, unlike
+the default IkiWiki search. This means that it can be used in places such
+as ebook readers. The disadvantage is that because Javascript runs
+in the browser, the searching is only as fast as the machine your browser
+is running on.
+
+Because this uses Javascript, the htmlscrubber must be turned off for any page where the directive is used.
+
+This plugin depends on the [[!iki plugins/contrib/field]] plugin.
+
+--[[KathrynAndersen]]
* [The Amnesic Incognito Live System](https://tails.boum.org/index.en.html)
* [The Progress Linux OS wiki](http://wiki.progress-linux.org/)
* [Oxford Computer Society](http://www.ox.compsoc.net/)
+* [Russian OpenBSD Community wiki](http://wiki.openbsd.ru/)
+* [Arcada Project](http://arcadaproject.org/)
Personal sites and blogs
========================
* [Richard "RichiH" Hartmann](http://richardhartmann.de/blog) - I thought I had added myself a year ago. Oups :)
* [Jonas Smedegaard](http://dr.jones.dk/) multilingual "classic" website w/ blog
* [Siri Reiter](http://sirireiter.dk/) portfolio website with a blog (in danish)
-* [L'Altro Wiki](http://laltromondo.dynalias.net/~iki/) Tutorials, reviews, miscellaneus articles in English and Italian, from the IRC network syrolnet.org
+* [L'Altro Wiki](http://laltromondo.dynalias.net/~iki/) Tutorials, reviews, miscellaneus articles in English and Italian.
* [STUPiD](http://lhzhang.com/)
* gregoa's [p.r. - political rants](http://info.comodo.priv.at/pr/)
* [Michael Hammer](http://www.michael-hammer.at/)
5c177c96ac98b24aaa0613ca241fb113f1b32c55.
--[[schmonz]]
+
+-----
+
+[[!template id=gitbranch branch=schmonz/portability author="[[schmonz]]"]]
+
+My git was in a screwy state so I started over. These changes are
+now on a branch. --[[schmonz]]
+++ /dev/null
-ikiwiki 3.20110715 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
- * rename: Fix logic error that broke renaming pages when the attachment
- plugin was disabled.
- * rename: Fix logic error that bypassed the usual pagespec checks."""]]
\ No newline at end of file
+++ /dev/null
-ikiwiki 3.20110905 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
- * mercurial: Openid nicknames are now used when committing. (Daniel Andersson)
- * mercurial: Implement rcs\_commit\_staged so comments, attachments, etc
- can be used. (Daniel Andersson)
- * mercurial: Implement rcs\_rename, rcs\_remove. (Daniel Andersson)
- * mercurial: Fix viewing of a diff containing non-utf8 changes.
- (Daniel Andersson)
- * mercurial: Make both rcs\_getctime and rcs\_getmtime fast. (Daniel Andersson)
- * mercurial: Implement rcs\_diff. (Daniel Andersson)
- * po: Add `LANG\_CODE` and `LANG\_NAME` template variables. (intrigeri)
- * Fix typo in Danish translation of shortcuts page that caused exponential
- regexp blowup.
- * Fix escaping of html entities in permalinks.
- * Fix escaping of html entities in tag names.
- * Avoid using named capture groups in heredoc code for oldperl compatibility.
- * Put in a workaround for #622591, by ensuring Search::Xapian gets loaded
- before Image::Magick.
- * Add unminified jquery js and css files to source.
- * Update to jquery 1.6.2, and jquery-ui 1.8.14.
- * Use lockf rather than flock when taking the cgilock, for better
- portability.
- * search: Fix encoding bug in calculation of maximum term size.
- * inline: When indexing internal pages for searching, use the url of
- the inlining page.
- * Fix comments testsuite to not rely on Date::Parse's ability to
- parse the date Columbus discovered America. Closes: #[640350](http://bugs.debian.org/640350)
- * Avoid warning message when generating setup file if highlight
- is not installed. Closes: #[637606](http://bugs.debian.org/637606)
- * Promote RPC::XML to a Recommends, since it's used by auto-blog.setup.
- Closes: #[637603](http://bugs.debian.org/637603)
- * Fix web revert of a file deletion."""]]
\ No newline at end of file
--- /dev/null
+ikiwiki 3.20120109 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+ * mdwn: Can use the discount markdown library, via the
+ Text::Markdown::Discount perl module. This is preferred if available
+ since it's the fastest currently supported markdown library, speeding up
+ ikiwiki's markdown rendering by a factor of 40.
+ (However, when multimarkdown is enabled, Text::Markdown::Multimarkdown
+ is still used.)
+ * On Debian, depend on libtext-markdown-discount."""]]
\ No newline at end of file
--- /dev/null
+ikiwiki 3.20120115 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+ * Make backlink(.) work. Thanks, Giuseppe Bilotta.
+ * mdwn: Workaround discount's eliding of <style> blocks.
+ * attachment: Fix utf-8 display bug."""]]
\ No newline at end of file
--- /dev/null
+The `jssearchfield` directive is supplied by the [[!iki plugins/contrib/jssearchfield desc=jssearchfield]] plugin.
+
+This enables one to search the structured data ("field" values) of
+multiple pages. A search form is constructed, and the searching is
+done with Javascript, which means that the entire thing is self-contained.
+This depends on the [[!iki plugins/contrib/field]] plugin.
+
+The pages to search are selected by a PageSpec given by the "pages"
+parameter.
+The fields to search are given by the "fields" parameter. By default,
+the field name is given, and the user can type the search parameter for
+that field into a text input field.
+
+## OPTIONS
+
+**pages**: A PageSpec to determine the pages to search through.
+
+**fields**: The fields to put into the search form, and to display
+in the results.
+
+**tagfields**: Display the given fields as a list of tags that can
+be selected from, rather than having a text input field. Every distinct
+value of that field will be listed, so it is best used for things with
+short values, like "Author" rather than long ones like "Description".
+Note that "tagfields" must be a subset of "fields".
+
+**sort**: A SortSpec to determine how the matching pages should be sorted; this is the "default" sort order that the results will be displayed in.
+The search form also gives the option of "random" sort, which will
+display the search results in random order.
+
+## SEARCHING
+
+The search form that is created by this directive contains the following:
+
+* for each search field, a label, plus either a text input field, or a list of checkboxes with values next to them if the field is also a tagfield. Note that the lists of checkboxes are initially hidden; one must click on the triangle next to the label to display them.
+* a "sort" toggle. One can select either "default" or "random".
+* A "Search!" button, to trigger the search if needed (see below)
+* A "Reset" button, which will clear all the values.
+
+The searching is dynamic. As soon as a value is changed, either by tabbing out of the text field, or by selecting or de-selecting a checkbox, the search
+results are updated. Furthermore, for tagfields, the tagfield lists
+themselves are updated to reflect the current search results.
--- /dev/null
+[[!template id=plugin name=jssearchfield author="[[rubykat]]"]]
+[[!tag type/search]]
+IkiWiki::Plugin::jssearchfield - Create a search form to search page field data.
+
+This plugin provides the [[ikiwiki/directive/jssearchfield]] directive. This
+enables one to search the structured data ("field" values) of multiple pages.
+This uses Javascript for the searching, which means that the entire thing
+is self-contained and does not require a server or CGI access, unlike
+the default IkiWiki search. This means that it can be used in places such
+as ebook readers. The disadvantage is that because Javascript runs
+in the browser, the searching is only as fast as the machine your browser
+is running on.
+
+Because this uses Javascript, the htmlscrubber must be turned off for any page where the directive is used.
+
+This plugin depends on the [[!iki plugins/contrib/field]] plugin.
+
+## Activate the plugin
+
+ # activate the plugin
+ add_plugins => [qw{goodstuff field jssearchfield ....}],
+
+ # disable scrubbing for search page
+ htmlscrubber_skip => 'mysearchpage',
+
+## PREREQUISITES
+
+ IkiWiki
+ IkiWiki::Plugin::field
+ HTML::Template
+
+## DOWNLOAD
+
+* browse at GitHub: <http://github.com/rubykat/ikiplugins/blob/master/IkiWiki/Plugin/jssearchfield.pm>
+* git repo at git://github.com/rubykat/ikiplugins.git
[[!template id=plugin name=mandoc author="[[schmonz]]"]]
-[[!template id=gitbranch branch=schmonz/master author="[[schmonz]]"]]
+[[!template id=gitbranch branch=schmonz/mandoc author="[[schmonz]]"]]
[[!tag type/format]]
This plugin lets ikiwiki convert Unix man pages to HTML. It uses
--- /dev/null
+[[!template id=plugin name=newpage author="[[rubykat]]"]]
+[[!tag type/web]]
+[[!toc]]
+## NAME
+
+IkiWiki::Plugin::newpage - add a "create new page" form to actions
+
+## SYNOPSIS
+
+ # activate the plugin
+ add_plugins => [qw{goodstuff newpage ....}],
+
+## DESCRIPTION
+
+This plugin adds a new action to the "ACTIONS" section of a page;
+a button labelled "create" and an input field next to it.
+
+The common way of creating a new page is to edit a different page
+and add a link to the new page. However, there are some situations
+where that is a nuisance; for example, where pages are listed using
+a [[plugins/map]] directive. The newpage plugin enables
+one to simply type the name of the new page, click the "create" button,
+and one is then taken to the standard IkiWiki create-page form.
+
+## DOWNLOAD
+
+* browse at GitHub: <http://github.com/rubykat/ikiplugins/blob/master/IkiWiki/Plugin/newpage.pm>
+* git repo at git://github.com/rubykat/ikiplugins.git
+
--- /dev/null
+How is this better than creating an inline with `rootpage` set,
+which creates a similar new page form? I sometimes make the inline match
+nothing, while still creating pages, in the odd cases where I have a map
+or such displaying the pages. --[[Joey]]
> It does not, however, have a markdown to html converter -- for
> previewing it has to talk to the server with AJAX.
> --[[Joey]]
+
+>> I've got pagedown working on my personal site (simon.kisikew.org) but I'm not sure how
+>> I can inject the relevant <div>'s in the right place. They need to go **above**
+>> the editing <textarea> . (Too bad about the licensing, it's rather nice.)
+>> I had to do one minor change to it to have it inject itself into the page properly,
+>> and that was to make this change in `Markdown.Editor.js`:
+>>
+>> `this.input = doc.getElementById("editcontent" + postfix);`
+>>
+>> on line 247. --[[simonraven]]
+
+>>> Well, I re-figured out that I needed a TMPL_VAR FOO in the template(s). --[[simonraven]]
Remove a file. The filename is relative to the root of the srcdir.
Note that this should not commit the removal, it should only prepare for it
-to be committed when `rcs_commit` (or `rcs_commit_staged`) is called. Note
-that the new file may be in a new subdir that is not yet in version
-control; the subdir can be added if so.
+to be committed when `rcs_commit` (or `rcs_commit_staged`) is called.
#### `rcs_rename($$)`
-If you really need to, you can use [[!wikipedia desc="CVS" Concurrent Versions System]]
-with ikiwiki.
+[[!template id=gitbranch branch=schmonz/cvs author="[[schmonz]]"]]
+
+If you really need to, you can use [[!wikipedia desc="CVS" Concurrent
+Versions System]] with ikiwiki.
### Usage
7. Install [[!cpan File::chdir]], [[!cpan File::ReadBackwards]],
-[cvsps](http://www.cobite.com/cvsps/), and
-[cvsweb](http://www.freebsd.org/projects/cvsweb.html) or the like.
+ [cvsps](http://www.cobite.com/cvsps/), and
+ [cvsweb](http://www.freebsd.org/projects/cvsweb.html) or the like.
7. Adjust CVS-related parameters in your setup file.
-Consider creating `$HOME/.cvsrc` if you don't have one already; the plugin doesn't need it, but you yourself might. Here's a good general-purpose one:
+Consider creating `$HOME/.cvsrc` if you don't have one already; the
+plugin doesn't need it, but you yourself might. Here's a good
+general-purpose one:
cvs -q
checkout -P
### Implementation details
* [[ikiwiki-makerepo]]:
- * creates a repository,
- * imports `$SRCDIR` into top-level module `ikiwiki` (vendor tag IKIWIKI, release tag PRE_CVS),
- * configures the post-commit hook in `CVSROOT/loginfo`.
+ * creates a repository,
+ * imports `$SRCDIR` into top-level module `ikiwiki` (vendor tag
+ IKIWIKI, release tag PRE_CVS),
+ * configures the post-commit hook in `CVSROOT/loginfo`.
### To do
-* Have `ikiwiki-makerepo` set up NetBSD-like `log_accum` and `commit_prep` scripts that coalesce commits into changesets. Reasons:
- 7. Obviates the need to scrape the repo's complete history to determine the last N changesets. (Repositories without such records can fall back on the `cvsps` and `File::ReadBackwards` code.)
- 7. Arranges for ikiwiki to be run once per changeset, rather than CVS's once per committed file (!), which is a waste at best and bug-inducing at worst. (Currently, on multi-directory commits, only the first directory's changes get mentioned in [[recentchanges|plugins/recentchanges]].)
-* Perhaps prevent web edits from attempting to create `.../CVS/foo.mdwn` (and `.../cvs/foo.mdwn` on case-insensitive filesystems); thanks to the CVS metadata directory, the attempt will fail anyway (and much more confusingly) if we don't.
+* Expand test coverage and fix bugs.
+* Have `ikiwiki-makerepo` set up NetBSD-like `log_accum` and
+ `commit_prep` scripts that coalesce commits into changesets. Reasons:
+ 7. Obviates the need to scrape the repo's complete history to
+ determine the last N changesets. (Repositories without such
+ records can fall back on the `cvsps` and `File::ReadBackwards`
+ code.)
+ 7. Arranges for ikiwiki to be run once per changeset, rather
+ than CVS's once per committed file (!), which is a waste at
+ best and bug-inducing at worst. (Currently, on multi-directory
+ commits, only the first directory's changes get mentioned
+ in [[recentchanges|plugins/recentchanges]].)
+* Perhaps prevent web edits from attempting to create `.../CVS/foo.mdwn`
+ (and `.../cvs/foo.mdwn` on case-insensitive filesystems); thanks
+ to the CVS metadata directory, the attempt will fail anyway (and
+ much more confusingly) if we don't.
> that. --[[Joey]]
>> Done. --[[schmonz]].
+
+----
+
+I'm attempting to bring some polish to this plugin, starting with
+fuller test coverage. In preparation, I've refactored the tests a
+bunch (and shuffled the code a bit) in my branch. I'm worried,
+however, that my misunderstanding of `git rebase` may have made my
+branch harder for you to pull.
+
+Before I go writing a whole swack of test cases, could you merge
+my latest? Through at least ad0e56cdcaaf76bc68d1b5c56e6845307b51c44a
+there should be no functional change. --[[schmonz]]
+
+Never mind, I was able to convince myself (by cloning `origin`
+afresh and merging from `schmonz/cvs`). The history is a little
+gross but the before-and-after diff looks right.
+
+Bugs found and fixed so far:
+
+* Stop treating text files as binary (`-kb`) on `rcs_add()`
+ (ac8eab29e8394aca4c0b23a6687ec947ea1ac869)
+
+> Merged to current head. --[[Joey]]
--- /dev/null
+Test it just now!
* [[!shortcut name=archive url="http://web.archive.org/*/%S"]]
* [[!shortcut name=gmap url="https://maps.google.com/maps?q=%s"]]
* [[!shortcut name=gmsg url="https://groups.google.com/groups?selm=%s"]]
-* [[!shortcut name=wikipedia url="https://en.wikimedia.org/wiki/%s"]]
+* [[!shortcut name=wikipedia url="https://en.wikipedia.org/wiki/%s"]]
* [[!shortcut name=wikitravel url="https://wikitravel.org/en/%s"]]
* [[!shortcut name=wiktionary url="https://en.wiktionary.org/wiki/%s"]]
* [[!shortcut name=debbug url="http://bugs.debian.org/%S" desc="Debian bug #%s"]]
>>> (Upskirt, discount... Who comes up with these names? Discount also
>>> features a "NOPANTS" option.) --[[Joey]]
+>>>> Thanks for doing this; it's given a well-needed speedup to my huge site.
+>>>>
+>>>> (At least "Discount" is related to "Mark Down" but I don't fathom "Upskirt" either.)
+>>>> --[[KathrynAndersen]]
+
[[wishlist]]
We could use a big machine, with plenty of CPUs. Could some multi-threading support be added to ikiwiki, by forking out all the external heavy plugins (imagemagick, tex, ...) and/or by processing pages in parallel?
Disclaimer: I know nothing of the Perl approach to parallel processing.
+
+> I agree that it would be lovely to be able to use multiple processors to speed up rebuilds on big sites (I have a big site myself), but, taking a quick look at what Perl threads entails, and taking into acount what I've seen of the code of IkiWiki, it would take a massive rewrite to make IkiWiki thread-safe - the API would have to be completely rewritten - and then more work again to introduce threading itself. So my unofficial humble opinion is that it's unlikely to be done.
+> Which is a pity, and I hope I'm mistaken about it.
+> --[[KathrynAndersen]]
+
+> > I have much less experience with the internals of Ikiwiki, much
+> > less Multi-threading perl, but I agree that to make Ikiwiki thread
+> > safe and to make the modifications to really take advantage of the
+> > threads is probably beyond the realm of reasonable
+> > expectations. Having said that, I wonder if there aren't ways to
+> > make Ikiwiki perform better for these big cases where the only
+> > option is to wait for it to grind through everything. Something
+> > along the lines of doing all of the aggregation and dependency
+> > heavy stuff early on, and then doing all of the page rendering
+> > stuff at the end quasi-asynchronously? Or am I way off in the deep
+> > end.
+> >
+> > From a practical perspective, it seems like these massive rebuild
+> > situations represent a really small subset of ikiwiki builds. Most
+> > sites are pretty small, and most sites need full rebuilds very
+> > very infrequently. In that scope, 10 minute rebuilds aren't that
+> > bad seeming. In terms of performance challenges, it's the one page
+> > with 3-5 dependency that takes 10 seconds (say) to rebuild that's
+> > a larger challenge for Ikiwiki as a whole. At the same time, I'd
+> > be willing to bet that performance benefits for these really big
+> > repositories for using fast disks (i.e. SSDs) could probably just
+> > about meet the benefit of most of the threading/async work.
+> >
+> > --[[tychoish]]
+
+>>> It's at this point that doing profiling for a particular site would come
+>>> in, because it would depend on the site content and how exactly IkiWiki is
+>>> being used as to what the performance bottlenecks would be. For the
+>>> original poster, it would be image processing. For me, it tends to be
+>>> PageSpecs, because I have a lot of maps and reports.
+
+>>> But I sincerely don't think that Disk I/O is the main bottleneck, not when
+>>> the original poster mentions CPU usage, and also in my experience, I see
+>>> IkiWiki chewing up 100% CPU usage one CPU, while the others remain idle. I
+>>> haven't noticed slowdowns due to waiting for disk I/O, whether that be a
+>>> system with HD or SSD storage.
+
+>>> I agree that large sites are probably not the most common use-case, but it
+>>> can be a chicken-and-egg situation with large sites and complete rebuilds,
+>>> since it can often be the case with a large site that rebuilding based on
+>>> dependencies takes *longer* than rebuilding the site from scratch, simply
+>>> because there are so many pages that are interdependent. It's not always
+>>> the number of pages itself, but how the site is being used. If IkiWiki is
+>>> used with the absolute minimum number of page-dependencies - that is, no
+>>> maps, no sitemaps, no trails, no tags, no backlinks, no albums - then one
+>>> can have a very large number of pages without having performance problems.
+>>> But when you have a change in PageA affecting PageB which affects PageC,
+>>> PageD, PageE and PageF, then performance can drop off horribly. And it's a
+>>> trade-off, because having features that interlink pages automatically is
+>>> really nifty ad useful - but they have a price.
+
+>>> I'm not really sure what the best solution is. Me, I profile my IkiWiki builds and try to tweak performance for them... but there's only so much I can do.
+>>> --[[KathrynAndersen]]
+
+>>>> IMHO, the best way to get a multithreaded ikiwiki is to rewrite it
+>>>> in haskell, using as much pure code as possible. Many avenues
+>>>> then would open up to taking advantage of haskell's ability to
+>>>> parallize pure code.
+>>>>
+>>>> With that said, we already have some nice invariants that could be
+>>>> used to parallelize page builds. In particular, we know that
+>>>> page A never needs state built up while building page B, for any
+>>>> pages A and B that don't have a dependency relationship -- and ikiwiki
+>>>> tracks such dependency relationships, although not currently in a form
+>>>> that makes it very easy (or fast..) to pick out such groups of
+>>>> unrelated pages.
+>>>>
+>>>> OTOH, there are problems.. building page A can result in changes to
+>>>> ikiwiki's state; building page B can result in other changes. All
+>>>> such changes would have to be made thread-safely. And would the
+>>>> resulting lock contention result in a program that ran any faster
+>>>> once parallelized?
+>>>>
+>>>> Which is why [[rewrite_ikiwiki_in_haskell]], while pretty insane, is
+>>>> something I keep thinking about. If only I had a spare year..
+>>>> --[[Joey]]
a bunch of haskell libraries. OTOH, it might be possible to build a
static binary at home and upload it, thus avoiding a messy installation
procedure entirely.
-* I can barely code in haskell yet. I'm probably about 100x faster at
- programming in perl. I need to get some more practical experience before
- I´m fast and seasoned enough in haskell to attempt such a project.
- (And so far, progress at learning has been slow and I have not managed
- to write anything serious in haskell.) --[[Joey]]
+ --[[Joey]]
>>> It doesn't really. I recently (re-)read about couchdb and thought that
>>> what it was trying to do had some comparisons with the thinking going on
>>> in [[todo/structured_page_data]]. -- [[Jon]]
+
+-----
+
+I'm torn about this idea, if it's actually serious. I'm very comfortable
+programming in Perl, and have written quite a few modules for IkiWiki, and
+it would be a huge pain to have to start from scratch all over again. On
+the other hand, this could be a motivation for me to learn Haskell. My
+only encounter with Haskell has been a brief time when I was using the
+Xmonad window manager, but it looks like an interesting language.
+Functional programming is cool.
+
+There are a lot of interesting plusses for Haskell you note (in the parent
+page), but it's true that the idea is horribly daunting (as [[Joey]] said
+"If only I had a spare year"). Is there any way that you could "start
+small"? Because nothing will ever happen if the task is too daunting to
+even start.
+
+> This seems destined to remain a thought experiment unless something like
+> that can be done, or I get a serious case of second system disease.
+>
+> I've considered doing things like using the external plugin interface
+> to run a separate haskell program, which would allow implementing
+> arbitrary plugins in haskell (starting with a pandoc plugin..),
+> and could perhaps grow to subsume the perl code. However, this would
+> stick us with the perl data structures, which are not a very good fit
+> for haskell. --[[Joey]]
+
+On further thought... perhaps it would be easier to fork or contribute to
+an existing Haskell-based wiki, such as <a
+href="http://jaspervdj.be/hakyll">Hakyll</a>?
+
+--[[KathrynAndersen]]
+
+> As far as I know there are no other wikis (haskell or otherwise)
+> that are wiki compilers. Since we know from experience that dealing
+> with static compilation turns out to be one of the trickiest parts of
+> ikiwiki, I'm doubtful about trying to bolt that into one. --[[Joey]]
+
+>> Haykll isn't a wiki but it does do static compilation. The missing
+>> parts are: the web interface, the wiki link processing, and page
+>> dependency stuff. -- [[tychoish]]
> Ikiwiki does not support git submodules.
>
-> You can use the [[ikiwiki/plugin/underlay]] plugin to merge the
+> You can use the [[plugins/underlay]] plugin to merge the
> contents of other directories into your wiki's source. --[[Joey]]
-[Amitai Schlair](http://www.netbsd.org/~schmonz/) finds himself
-using ikiwiki for all sorts of things. His attempts at contributing:
+[Amitai Schlair](http://www.schmonz.com/) finds himself using ikiwiki
+for all sorts of things. His attempts at contributing:
[[!map
pages="!*/Discussion and ((link(users/schmonz) and plugins/*) or rcs/cvs)"
if (! defined $var || ! defined $val) {
die gettext("usage: --set-yaml var=value"), "\n";
}
- eval q{use YAML::Any};
- eval q{use YAML} if $@;
+ eval q{use YAML::XS; use Encode};
die $@ if $@;
- eval q{$YAML::Syck::ImplicitUnicode=1};
- $config{$var}=Load($val."\n");
+ $config{$var}=Load(encode_utf8($val)."\n");
},
"version" => sub {
print "ikiwiki version $IkiWiki::version\n";
Name: ikiwiki
-Version: 3.20111229
+Version: 3.20120115
Release: 1%{?dist}
Summary: A wiki compiler
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-12-29 12:03-0400\n"
+"POT-Creation-Date: 2012-01-15 16:40-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
msgid "bad attachment filename"
msgstr ""
-#: ../IkiWiki/Plugin/attachment.pm:295
+#: ../IkiWiki/Plugin/attachment.pm:296
msgid "attachment upload"
msgstr ""
-#: ../IkiWiki/Plugin/attachment.pm:346
+#: ../IkiWiki/Plugin/attachment.pm:347
msgid "this attachment is not yet saved"
msgstr ""
-#: ../IkiWiki/Plugin/attachment.pm:363
+#: ../IkiWiki/Plugin/attachment.pm:365
msgid "just uploaded"
msgstr ""
msgid "multimarkdown is enabled, but Text::MultiMarkdown is not installed"
msgstr ""
-#: ../IkiWiki/Plugin/mdwn.pm:70
+#: ../IkiWiki/Plugin/mdwn.pm:88
#, perl-format
msgid "failed to load Markdown.pm perl module (%s) or /usr/bin/markdown (%s)"
msgstr ""
#!/usr/bin/perl
use warnings;
use strict;
-my $dir;
-BEGIN {
- $dir="/tmp/ikiwiki-test-cvs.$$";
- my $cvs=`which cvs`;
- chomp $cvs;
- my $cvsps=`which cvsps`;
- chomp $cvsps;
- if (! -x $cvs || ! -x $cvsps) {
- eval q{
- use Test::More skip_all => "cvs or cvsps not available"
- }
- }
- if (! mkdir($dir)) {
- die $@;
+use Test::More; my $total_tests = 40;
+use IkiWiki;
+
+my $default_test_methods = '^test_*';
+my @required_programs = qw(
+ cvs
+ cvsps
+);
+my @required_modules = qw(
+ File::chdir
+ File::MimeInfo
+ Date::Parse
+ File::Temp
+ File::ReadBackwards
+);
+my $dir = "/tmp/ikiwiki-test-cvs.$$";
+
+# TESTS FOR GENERAL META-BEHAVIOR
+
+sub test_web_comments {
+ # how much of the web-edit workflow are we actually testing?
+ # because we want to test comments:
+ # - when the first comment for page.mdwn is added, and page/ is
+ # created to hold the comment, page/ isn't added to CVS control,
+ # so the comment isn't either
+ # - side effect for moderated comments: after approval they
+ # show up normally AND are still pending, too
+ # - comments.pm treats rcs_commit_staged() as returning conflicts?
+}
+
+sub test_chdir_magic {
+ # cvs.pm operations are always occurring inside $config{srcdir}
+ # other ikiwiki operations are occurring wherever, and are unaffected
+ # when are we bothering with "local $CWD" and when aren't we?
+}
+
+sub test_cvs_info {
+ # inspect "Repository revision" (used in code)
+ # inspect "Sticky Options" (used in tests to verify existence of "-kb")
+}
+
+sub test_cvs_run_cvs {
+ # extract the stdout-redirect thing
+ # - prove that it silences stdout
+ # - prove that stderr comes through just fine
+ # prove that when cvs exits nonzero (fail), function exits false
+ # prove that when cvs exits zero (success), function exits true
+ # always pass -f, just in case
+ # steal from git.pm: safe_git(), run_or_{die,cry,non}
+ # - open() instead of system()
+ # always call cvs_run_cvs(), don't ever run 'cvs' directly
+}
+
+sub test_cvs_run_cvsps {
+ # parameterize command like run_cvs()
+ # expose config vars for e.g. "--cvs-direct -z 30"
+ # always pass -x (unless proven otherwise)
+ # always pass -b HEAD (configurable like gitmaster_branch?)
+}
+
+sub test_cvs_parse_cvsps {
+ # extract method from rcs_recentchanges
+ # document expected changeset format
+ # document expected changeset delimiter
+ # try: cvsps -q -x -p && ls | sort -rn | head -100
+ # - benchmark against current impl (that uses File::ReadBackwards)
+}
+
+sub test_cvs_parse_log_accum {
+ # add new, preferred method for rcs_recentchanges to use
+ # teach log_accum to record commits (into transient?)
+ # script cvsps to bootstrap (or replace?) commit history
+ # teach ikiwiki-makerepo to set up log_accum and commit_prep
+ # why are NetBSD commit mails unreliable?
+ # - is it working for CVS commits and failing for web commits?
+}
+
+sub test_cvs_is_controlling {
+ # with no args:
+ # - if srcdir is in CVS, return true
+ # - else, return false
+ # with a dir arg:
+ # - if dir is in CVS, return true
+ # - else, return false
+ # with a file arg:
+ # - is there anything that wants the answer? if so, answer
+ # - else, die
+}
+
+
+# TESTS FOR GENERAL PLUGIN API CALLS
+
+sub test_checkconfig {
+ # undef cvspath, expect "ikiwiki"
+ # define cvspath normally, get it back
+ # define cvspath in a subdir, get it back?
+ # define cvspath with extra slashes, get sanitized version back
+ # - yoink test_extra_path_slashes
+ # undef cvs_wrapper, expect $config{wrappers} same size as before
+
+ my $initial_cvspath = $config{cvspath};
+ $config{cvspath} = "/ikiwiki//";
+ IkiWiki::checkconfig();
+ is(
+ $config{cvspath},
+ $initial_cvspath,
+ q{rcs_recentchanges assumes checkconfig has sanitized cvspath},
+ );
+}
+
+sub test_getsetup {
+ # anything worth testing?
+}
+
+sub test_genwrapper {
+ # testable directly? affects rcs_add, but are we exercising this?
+}
+
+
+# TESTS FOR VCS PLUGIN API CALLS
+
+sub test_rcs_update {
+ # can it assume we're under CVS control? or must it check?
+ # anything else worth testing?
+}
+
+sub test_rcs_prepedit {
+ # can it assume we're under CVS control? or must it check?
+ # for existing file, returns latest revision in repo
+ # - what's this used for? should it return latest revision in checkout?
+ # for new file, returns empty string
+}
+
+sub test_rcs_commit {
+ # can it assume we're under CVS control? or must it check?
+ # if someone else changed the page since rcs_prepedit was called:
+ # - try to merge into our working copy
+ # - if merge succeeds, proceed to commit
+ # - else, return page content with the conflict markers in it
+ # commit:
+ # - if success, return undef
+ # - else, revert + return content with the conflict markers in it
+ # git.pm receives "session" param -- useful here?
+ # web commits start with "web commit {by,from} "
+ # seeing File::chdir errors on commit?
+}
+
+sub test_rcs_commit_staged {
+ # if commit succeeds, return undef
+ # else, warn and return error message (really? or just non-undef?)
+}
+
+sub test_rcs_add {
+ my $message = "add a top-level ASCII (non-UTF-8) page via VCS API";
+ writefile('test0.mdwn', $config{srcdir}, "* some plain ASCII text");
+ IkiWiki::rcs_add("test0.mdwn");
+ IkiWiki::rcs_commit(
+ file => "test0.mdwn",
+ message => $message,
+ token => "moo",
+ );
+ is_newly_added("test0.mdwn");
+ is_in_keyword_substitution_mode("test0.mdwn", undef);
+ my @changes = IkiWiki::rcs_recentchanges(3);
+ is_total_number_of_changes(\@changes, 1);
+ is_most_recent_change(\@changes, "test0", $message);
+
+ $message = "add a top-level dir via VCS API";
+ my $dir1 = "test3";
+ can_mkdir($dir1);
+ IkiWiki::rcs_add($dir1);
+ # XXX test that the wrapper hangs here without our genwrapper()
+ # XXX test that the wrapper doesn't hang here with it
+ @changes = IkiWiki::rcs_recentchanges(3);
+ is_total_number_of_changes(\@changes, 1); # despite the dir add
+ IkiWiki::rcs_commit(
+ file => $dir1,
+ message => $message,
+ token => "oom",
+ );
+ @changes = IkiWiki::rcs_recentchanges(3);
+ is_total_number_of_changes(\@changes, 1); # dirs aren't tracked
+
+ $message = "add a non-ASCII (UTF-8) text file in an un-added dir";
+ my $dir2 = "test4/test5";
+ can_mkdir($_) for ('test4', $dir2);
+ writefile("$dir2/test1.mdwn", $config{srcdir},readfile("t/test1.mdwn"));
+ IkiWiki::rcs_add("$dir2/test1.mdwn");
+ IkiWiki::rcs_commit(
+ file => "$dir2/test1.mdwn",
+ message => $message,
+ token => "omo",
+ );
+ is_newly_added("$dir2/test1.mdwn");
+ is_in_keyword_substitution_mode("$dir2/test1.mdwn", undef);
+ @changes = IkiWiki::rcs_recentchanges(3);
+ is_total_number_of_changes(\@changes, 2);
+ is_most_recent_change(\@changes, "$dir2/test1", $message);
+
+ $message = "add a binary file in an un-added dir, and commit_staged";
+ my $dir3 = "test6";
+ my $file = "$dir3/test7.ico";
+ can_mkdir($dir3);
+ my $bindata_in = readfile("doc/favicon.ico", 1);
+ my $bindata_out = sub { readfile($config{srcdir} . "/$file", 1) };
+ writefile($file, $config{srcdir}, $bindata_in, 1);
+ is(&$bindata_out(), $bindata_in, q{binary files match before commit});
+ IkiWiki::rcs_add($file);
+ IkiWiki::rcs_commit_staged(message => $message);
+ is_newly_added($file);
+ is_in_keyword_substitution_mode($file, q{-kb});
+ is(&$bindata_out(), $bindata_in, q{binary files match after commit});
+ @changes = IkiWiki::rcs_recentchanges(3);
+ is_total_number_of_changes(\@changes, 3);
+ is_most_recent_change(\@changes, $file, $message);
+ ok(
+ unlink($config{srcdir} . "/$file"),
+ q{can remove file in order to re-fetch it from repo},
+ );
+ ok(! -e $config{srcdir} . "/$file", q{really removed file});
+ IkiWiki::rcs_update();
+ is(&$bindata_out(), $bindata_in, q{binary files match after re-fetch});
+
+ $message = "add a UTF-8 and a binary file in different dirs";
+ my $file1 = "test8/test9.mdwn";
+ my $file2 = "test10/test11.ico";
+ can_mkdir(qw(test8 test10));
+ writefile($file1, $config{srcdir}, readfile('t/test2.mdwn'));
+ writefile($file2, $config{srcdir}, $bindata_in, 1);
+ IkiWiki::rcs_add($_) for ($file1, $file2);
+ IkiWiki::rcs_commit_staged(message => $message);
+ is_newly_added($_) for ($file1, $file2);
+ is_in_keyword_substitution_mode($file1, undef);
+ is_in_keyword_substitution_mode($file2, '-kb');
+ @changes = IkiWiki::rcs_recentchanges(3);
+ is_total_number_of_changes(\@changes, 3);
+ @changes = IkiWiki::rcs_recentchanges(4);
+ is_total_number_of_changes(\@changes, 4);
+ # XXX test for both files in the commit, and no other files
+ is_most_recent_change(\@changes, $file2, $message);
+
+ # prevent web edits from attempting to create .../CVS/foo.mdwn
+ # on case-insensitive filesystems, also prevent .../cvs/foo.mdwn
+ # unless your "CVS" is something else and we've made it configurable
+
+ # can it assume we're under CVS control? or must it check?
+
+ # extract method: filetype-guessing
+ # add a binary file, remove it, add a text file by same name, no -kb?
+ # add a text file, remove it, add a binary file by same name, -kb?
+}
+
+sub test_rcs_remove {
+ # can it assume we're under CVS control? or must it check?
+ # remove a top-level file
+ # - rcs_commit
+ # - inspect recentchanges: one new change, file removed
+ # remove two files (in different dirs)
+ # - rcs_commit_staged
+ # - inspect recentchanges: one new change, both files removed
+}
+
+sub test_rcs_rename {
+ # can it assume we're under CVS control? or must it check?
+ # rename a file in the same dir
+ # - rcs_commit_staged
+ # - inspect recentchanges: one new change, one file removed, one added
+ # rename a file into a different dir
+ # - rcs_commit_staged
+ # - inspect recentchanges: one new change, one file removed, one added
+ # rename a file into a not-yet-existing dir
+ # - rcs_commit_staged
+ # - inspect recentchanges: one new change, one file removed, one added
+ # is it safe to use "mv"? what if $dest is somehow outside the wiki?
+}
+
+sub test_rcs_recentchanges {
+ my $message = "Add a page via CVS directly";
+ writefile('test2.mdwn', $config{srcdir}, readfile("t/test2.mdwn"));
+ system "cd $config{srcdir}"
+ . " && cvs add test2.mdwn >/dev/null 2>&1";
+ system "cd $config{srcdir}"
+ . " && cvs commit -m \"$message\" test2.mdwn >/dev/null";
+
+ my @changes = IkiWiki::rcs_recentchanges(3);
+ is(
+ $#changes,
+ 0,
+ q{total commits: 1},
+ );
+ is(
+ $changes[0]{message}[0]{"line"},
+ $message,
+ q{most recent commit's first message line matches},
+ );
+ is(
+ $changes[0]{pages}[0]{"page"},
+ "test2",
+ q{most recent commit's first pagename matches},
+ );
+
+ # CVS commits run ikiwiki once for every committed file (!)
+ # - commit_prep alone should fix this
+ # CVS multi-dir commits show only the first dir in recentchanges
+ # - commit_prep might also fix this?
+ # CVS post-commit hook is amped off to avoid locking against itself
+ # - commit_prep probably doesn't fix this... but maybe?
+ # can it assume we're under CVS control? or must it check?
+ # don't worry whether we're called with a number (we always are)
+ # other rcs tests already inspect much of the returned structure
+ # CVS commits say "cvs" and get the right committer
+ # web commits say "web" and get the right committer
+ # - and don't start with "web commit {by,from} "
+ # "nickname" -- can we ever meaningfully set this?
+
+ # prefer log_accum, then cvsps, else die
+ # run the high-level recentchanges tests 2x (once for each method)
+ # - including in other test subs that check recentchanges?
+}
+
+sub test_rcs_diff {
+ # can it assume we're under CVS control? or must it check?
+ # in list context, return all lines (with \n), up to $maxlines if set
+ # in scalar context, return the whole diff, up to $maxlines if set
+}
+
+sub test_rcs_getctime {
+ # can it assume we're under CVS control? or must it check?
+ # given a file, find its creation time, else return 0
+ # first implement in the obvious way
+ # then cache
+}
+
+sub test_rcs_getmtime {
+ # can it assume we're under CVS control? or must it check?
+ # given a file, find its modification time, else return 0
+ # first implement in the obvious way
+ # then cache
+}
+
+sub test_rcs_receive {
+ pass(q{rcs_receive doesn't make sense for CVS});
+}
+
+sub test_rcs_preprevert {
+ # can it assume we're under CVS control? or must it check?
+ # given a patchset number, return structure describing what'd happen:
+ # - see doc/plugins/write.mdwn:rcs_receive()
+ # don't forget about attachments
+}
+
+sub test_rcs_revert {
+ # can it assume we're under CVS control? or must it check?
+ # given a patchset number, stage the revert for rcs_commit_staged()
+ # if commit succeeds, return undef
+ # else, warn and return error message (really? or just non-undef?)
+}
+
+sub main {
+ my $test_methods = defined $ENV{TEST_METHOD}
+ ? $ENV{TEST_METHOD}
+ : $default_test_methods;
+
+ _startup($test_methods eq $default_test_methods);
+ _runtests(_get_matching_test_subs($test_methods));
+ _shutdown($test_methods eq $default_test_methods);
+}
+
+main();
+
+
+# INTERNAL SUPPORT ROUTINES
+
+sub _plan_for_test_more {
+ my $can_plan = shift;
+
+ foreach my $program (@required_programs) {
+ my $program_path = `which $program`;
+ chomp $program_path;
+ return plan(skip_all => "$program not available")
+ unless -x $program_path;
}
- foreach my $module ('File::ReadBackwards', 'File::MimeInfo') {
+
+ foreach my $module (@required_modules) {
eval qq{use $module};
- if ($@) {
- eval qq{
- use Test::More skip_all => "$module not available"
- }
- }
+ return plan(skip_all => "$module not available")
+ if $@;
}
+
+ return plan(skip_all => "can't create $dir: $!")
+ unless mkdir($dir);
+ return plan(skip_all => "can't remove $dir: $!")
+ unless rmdir($dir);
+
+ return unless $can_plan;
+
+ return plan(tests => $total_tests);
+}
+
+# http://stackoverflow.com/questions/607282/whats-the-best-way-to-discover-all-subroutines-a-perl-module-has
+
+use B qw/svref_2object/;
+
+sub in_package {
+ my ($coderef, $package) = @_;
+ my $cv = svref_2object($coderef);
+ return if not $cv->isa('B::CV') or $cv->GV->isa('B::SPECIAL');
+ return $cv->GV->STASH->NAME eq $package;
+}
+
+sub list_module {
+ my $module = shift;
+ no strict 'refs';
+ return grep {
+ defined &{"$module\::$_"} and in_package(\&{*$_}, $module)
+ } keys %{"$module\::"};
}
-use Test::More tests => 12;
-
-BEGIN { use_ok("IkiWiki"); }
-
-%config=IkiWiki::defaultconfig();
-$config{rcs} = "cvs";
-$config{srcdir} = "$dir/src";
-$config{cvsrepo} = "$dir/repo";
-$config{cvspath} = "ikiwiki";
-IkiWiki::loadplugins();
-IkiWiki::checkconfig();
-
-my $cvsrepo = "$dir/repo";
-
-system "cvs -d $cvsrepo init >/dev/null";
-system "mkdir $dir/ikiwiki >/dev/null";
-system "cd $dir/ikiwiki && cvs -d $cvsrepo import -m import ikiwiki VENDOR RELEASE >/dev/null";
-system "rm -rf $dir/ikiwiki >/dev/null";
-system "cvs -d $cvsrepo co -d $config{srcdir} ikiwiki >/dev/null";
-
-# Web commit
-my $test1 = readfile("t/test1.mdwn");
-writefile('test1.mdwn', $config{srcdir}, $test1);
-IkiWiki::rcs_add("test1.mdwn");
-IkiWiki::rcs_commit(
- files => "test1.mdwn",
- message => "Added the first page",
- token => "moo"
-);
-my @changes;
-@changes = IkiWiki::rcs_recentchanges(3);
-
-is($#changes, 0);
-is($changes[0]{message}[0]{"line"}, "Added the first page");
-is($changes[0]{pages}[0]{"page"}, "test1");
-
-# Manual commit
-my $message = "Added the second page";
-
-my $test2 = readfile("t/test2.mdwn");
-writefile('test2.mdwn', $config{srcdir}, $test2);
-system "cd $config{srcdir} && cvs add test2.mdwn >/dev/null 2>&1";
-system "cd $config{srcdir} && cvs commit -m \"$message\" test2.mdwn >/dev/null";
-
-@changes = IkiWiki::rcs_recentchanges(3);
-is($#changes, 1);
-is($changes[0]{message}[0]{"line"}, $message);
-is($changes[0]{pages}[0]{"page"}, "test2");
-is($changes[1]{pages}[0]{"page"}, "test1");
-
-# extra slashes in the path shouldn't break things
-$config{cvspath} = "/ikiwiki//";
-IkiWiki::checkconfig();
-@changes = IkiWiki::rcs_recentchanges(3);
-is($#changes, 1);
-is($changes[0]{message}[0]{"line"}, $message);
-is($changes[0]{pages}[0]{"page"}, "test2");
-is($changes[1]{pages}[0]{"page"}, "test1");
-
-system "rm -rf $dir";
+
+# support for xUnit-style testing, a la Test::Class
+
+sub _startup {
+ my $can_plan = shift;
+ _plan_for_test_more($can_plan);
+ _generate_test_config();
+}
+
+sub _shutdown {
+ my $had_plan = shift;
+ done_testing() unless $had_plan;
+}
+
+sub _setup {
+ _generate_test_repo();
+}
+
+sub _teardown {
+ system "rm -rf $dir";
+}
+
+sub _runtests {
+ my @coderefs = (@_);
+ for (@coderefs) {
+ _setup();
+ $_->();
+ _teardown();
+ }
+}
+
+sub _get_matching_test_subs {
+ my $re = shift;
+ no strict 'refs';
+ return map { \&{*$_} } grep { /$re/ } sort(list_module('main'));
+}
+
+sub _generate_test_config {
+ %config = IkiWiki::defaultconfig();
+ $config{rcs} = "cvs";
+ $config{srcdir} = "$dir/src";
+ $config{cvsrepo} = "$dir/repo";
+ $config{cvspath} = "ikiwiki";
+ IkiWiki::loadplugins();
+ IkiWiki::checkconfig();
+}
+
+sub _generate_test_repo {
+ die "can't create $dir: $!"
+ unless mkdir($dir);
+
+ my $cvs = "cvs -d $config{cvsrepo}";
+ my $dn = ">/dev/null";
+ system "$cvs init $dn";
+ system "mkdir $dir/$config{cvspath} $dn";
+ system "cd $dir/$config{cvspath} && "
+ . "$cvs import -m import $config{cvspath} VENDOR RELEASE $dn";
+ system "rm -rf $dir/$config{cvspath} $dn";
+ system "$cvs co -d $config{srcdir} $config{cvspath} $dn";
+}
+
+sub can_mkdir {
+ my $dir = shift;
+ ok(
+ mkdir($config{srcdir} . "/$dir"),
+ qq{can mkdir $dir},
+ );
+}
+
+sub is_newly_added {
+ my $file = shift;
+ is(
+ IkiWiki::Plugin::cvs::cvs_info("Repository revision", $file),
+ '1.1',
+ qq{$file is newly added to CVS},
+ );
+}
+
+sub is_in_keyword_substitution_mode {
+ my ($file, $mode) = @_;
+ $mode = '(none)' unless defined $mode;
+ is(
+ IkiWiki::Plugin::cvs::cvs_info("Sticky Options", $file),
+ $mode,
+ qq{$file is in CVS with expected keyword substitution mode},
+ );
+}
+
+sub is_total_number_of_changes {
+ my ($changes, $expected_total) = @_;
+ is(
+ $#{$changes},
+ $expected_total - 1,
+ qq{total commits == $expected_total},
+ );
+}
+
+sub is_most_recent_change {
+ my ($changes, $page, $message) = @_;
+ is(
+ $changes->[0]{message}[0]{"line"},
+ $message,
+ q{most recent commit's first message line matches},
+ );
+ is(
+ $changes->[0]{pages}[0]{"page"},
+ $page,
+ q{most recent commit's first pagename matches},
+ );
+}