X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/blobdiff_plain/5412a95fd00017fb95037140393b864d38e2be0b..949c4c0c3a9d188e263068e675e0f2416f313c15:/IkiWiki/Plugin/po.pm diff --git a/IkiWiki/Plugin/po.pm b/IkiWiki/Plugin/po.pm index 205066341..d741a7928 100644 --- a/IkiWiki/Plugin/po.pm +++ b/IkiWiki/Plugin/po.pm @@ -36,9 +36,12 @@ sub import { hook(type => "htmlize", id => "po", call => \&htmlize); hook(type => "pagetemplate", id => "po", call => \&pagetemplate, last => 1); hook(type => "postscan", id => "po", call => \&postscan); - hook(type => "rename", id => "po", call => \&renamepages); + hook(type => "rename", id => "po", call => \&renamepages, first => 1); hook(type => "delete", id => "po", call => \&mydelete); hook(type => "change", id => "po", call => \&change); + hook(type => "cansave", id => "po", call => \&cansave); + hook(type => "canremove", id => "po", call => \&canremove); + hook(type => "canrename", id => "po", call => \&canrename); hook(type => "editcontent", id => "po", call => \&editcontent); $origsubs{'bestlink'}=\&IkiWiki::bestlink; @@ -221,50 +224,11 @@ sub filter (@) { my $page = $params{page}; my $destpage = $params{destpage}; - my $content = decode_utf8(encode_utf8($params{content})); - - return $content if ( ! istranslation($page) - || alreadyfiltered($page, $destpage) ); - - # CRLF line terminators make poor Locale::Po4a feel bad - $content=~s/\r\n/\n/g; - - # There are incompatibilities between some File::Temp versions - # (including 0.18, bundled with Lenny's perl-modules package) - # and others (e.g. 0.20, previously present in the archive as - # a standalone package): under certain circumstances, some - # return a relative filename, whereas others return an absolute one; - # we here use this module in a way that is at least compatible - # with 0.18 and 0.20. Beware, hit'n'run refactorers! - my $infile = new File::Temp(TEMPLATE => "ikiwiki-po-filter-in.XXXXXXXXXX", - DIR => File::Spec->tmpdir, - UNLINK => 1)->filename; - my $outfile = new File::Temp(TEMPLATE => "ikiwiki-po-filter-out.XXXXXXXXXX", - DIR => File::Spec->tmpdir, - UNLINK => 1)->filename; - - writefile(basename($infile), File::Spec->tmpdir, $content); - - my $masterfile = srcfile($pagesources{masterpage($page)}); - my %options = ( - "markdown" => (pagetype($masterfile) eq 'mdwn') ? 1 : 0, - ); - my $doc=Locale::Po4a::Chooser::new('text',%options); - $doc->process( - 'po_in_name' => [ $infile ], - 'file_in_name' => [ $masterfile ], - 'file_in_charset' => 'utf-8', - 'file_out_charset' => 'utf-8', - ) or error("[po/filter:$page]: failed to translate"); - $doc->write($outfile) or error("[po/filter:$page] could not write $outfile"); - $content = readfile($outfile) or error("[po/filter:$page] could not read $outfile"); - - # Unlinking should happen automatically, thanks to File::Temp, - # but it does not work here, probably because of the way writefile() - # and Locale::Po4a::write() work. - unlink $infile, $outfile; - - setalreadyfiltered($page, $destpage); + my $content = $params{content}; + if (istranslation($page) && ! alreadyfiltered($page, $destpage)) { + $content = po_to_markup($page, $content); + setalreadyfiltered($page, $destpage); + } return $content; } @@ -345,10 +309,24 @@ sub postscan (@) { } # Add the renamed page translations to the list of to-be-renamed pages. -sub renamepages() { - my $torename=shift; +sub renamepages($$$) { + my ($torename, $cgi, $session) = (shift, shift, shift); + + # copy the initial array, so that we can iterate on it AND + # modify it at the same time, without iterating on the items we + # pushed on it ourselves my @torename=@{$torename}; + # Save the page(s) the user asked to rename, so that our + # canrename hook can tell the difference between: + # - a translation being renamed as a consequence of its master page + # being renamed + # - a user trying to directly rename a translation + # This is why this hook has to be run first, before @torename is modified + # by other plugins. + $session->param(po_orig_torename => [ @torename ]); + IkiWiki::cgi_savesession($session); + foreach my $rename (@torename) { next unless istranslatable($rename->{src}); my %otherpages=%{otherlanguages($rename->{src})}; @@ -406,6 +384,52 @@ sub change(@) { } } +sub cansave ($$$$) { + my ($page, $content, $cgi, $session) = (shift, shift, shift, shift); + + if (istranslation($page)) { + my $res = isvalidpo($content); + if ($res) { + return undef; + } + else { + return "$res"; + } + } + return undef; +} + +sub canremove ($$$) { + my ($page, $cgi, $session) = (shift, shift, shift); + + if (istranslation($page)) { + return gettext("Can not remove a translation. Removing the master page, ". + "though, removes its translations as well."); + } + return undef; +} + +sub canrename ($$@) { + my ($cgi, $session) = (shift, shift); + my %params = @_; + + if (istranslation($params{src})) { + my $masterpage = masterpage($params{src}); + # Tell the difference between: + # - a translation being renamed as a consequence of its master page + # being renamed, which is allowed + # - a user trying to directly rename a translation, which is forbidden + # by looking for the master page in the list of to-be-renamed pages we + # saved early in the renaming process. + my $orig_torename = $session->param("po_orig_torename"); + unless (scalar grep { $_->{src} eq $masterpage } @{$orig_torename}) { + return gettext("Can not rename a translation. Renaming the master page, ". + "though, renames its translations as well."); + } + } + return undef; +} + # As we're previewing or saving a page, the content may have # changed, so tell the next filter() invocation it must not be lazy. sub editcontent () { @@ -859,6 +883,108 @@ sub commit_and_refresh ($$) { IkiWiki::saveindex(); } +# on success, returns the filtered content. +# on error, if $nonfatal, warn and return undef; else, error out. +sub po_to_markup ($$;$) { + my ($page, $content) = (shift, shift); + my $nonfatal = shift; + + $content = '' unless defined $content; + $content = decode_utf8(encode_utf8($content)); + # CRLF line terminators make poor Locale::Po4a feel bad + $content=~s/\r\n/\n/g; + + # There are incompatibilities between some File::Temp versions + # (including 0.18, bundled with Lenny's perl-modules package) + # and others (e.g. 0.20, previously present in the archive as + # a standalone package): under certain circumstances, some + # return a relative filename, whereas others return an absolute one; + # we here use this module in a way that is at least compatible + # with 0.18 and 0.20. Beware, hit'n'run refactorers! + my $infile = new File::Temp(TEMPLATE => "ikiwiki-po-filter-in.XXXXXXXXXX", + DIR => File::Spec->tmpdir, + UNLINK => 1)->filename; + my $outfile = new File::Temp(TEMPLATE => "ikiwiki-po-filter-out.XXXXXXXXXX", + DIR => File::Spec->tmpdir, + UNLINK => 1)->filename; + + sub failure ($) { + my $msg = '[po/po_to_markup:'.$page.'] ' . shift; + if ($nonfatal) { + warn $msg; + return undef; + } + error($msg, sub { unlink $infile, $outfile}); + } + + writefile(basename($infile), File::Spec->tmpdir, $content) + or return failure("failed to write $infile"); + + my $masterfile = srcfile($pagesources{masterpage($page)}); + my %options = ( + "markdown" => (pagetype($masterfile) eq 'mdwn') ? 1 : 0, + ); + my $doc=Locale::Po4a::Chooser::new('text',%options); + $doc->process( + 'po_in_name' => [ $infile ], + 'file_in_name' => [ $masterfile ], + 'file_in_charset' => 'utf-8', + 'file_out_charset' => 'utf-8', + ) or return failure("failed to translate"); + $doc->write($outfile) or return failure("could not write $outfile"); + + $content = readfile($outfile) or return failure("could not read $outfile"); + + # Unlinking should happen automatically, thanks to File::Temp, + # but it does not work here, probably because of the way writefile() + # and Locale::Po4a::write() work. + unlink $infile, $outfile; + + return $content; +} + +# returns a SuccessReason or FailReason object +sub isvalidpo ($) { + my $content = shift; + + # NB: we don't use po_to_markup here, since Po4a parser does + # not mind invalid PO content + $content = '' unless defined $content; + $content = decode_utf8(encode_utf8($content)); + + # There are incompatibilities between some File::Temp versions + # (including 0.18, bundled with Lenny's perl-modules package) + # and others (e.g. 0.20, previously present in the archive as + # a standalone package): under certain circumstances, some + # return a relative filename, whereas others return an absolute one; + # we here use this module in a way that is at least compatible + # with 0.18 and 0.20. Beware, hit'n'run refactorers! + my $infile = new File::Temp(TEMPLATE => "ikiwiki-po-isvalidpo.XXXXXXXXXX", + DIR => File::Spec->tmpdir, + UNLINK => 1)->filename; + + sub failure ($) { + my $msg = '[po/isvalidpo] ' . shift; + unlink $infile; + return IkiWiki::FailReason->new("$msg"); + } + + writefile(basename($infile), File::Spec->tmpdir, $content) + or return failure("failed to write $infile"); + + my $res = (system("msgfmt", "--check", $infile, "-o", "/dev/null") == 0); + + # Unlinking should happen automatically, thanks to File::Temp, + # but it does not work here, probably because of the way writefile() + # and Locale::Po4a::write() work. + unlink $infile; + + if ($res) { + return IkiWiki::SuccessReason->new("valid gettext data"); + } + return IkiWiki::FailReason->new("invalid gettext data"); +} + # ,---- # | PageSpec's # `----