From 126acb28340ddb4831b7e398cacba7f6957721b6 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Thu, 22 Oct 2009 13:45:08 +0200 Subject: Wrote a quick and dirty translation editor plugin See Plugin::TransAdmin for details. I wrote it as a plugin so that it can make use of the framework and functions of the main VNDB website code, but that it doesn't have to confirm to the rest of the code or have to be loaded into memory when unused. --- lib/VNDB/Plugin/TransAdmin.pm | 239 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 lib/VNDB/Plugin/TransAdmin.pm (limited to 'lib') diff --git a/lib/VNDB/Plugin/TransAdmin.pm b/lib/VNDB/Plugin/TransAdmin.pm new file mode 100644 index 00000000..5e9f8b1c --- /dev/null +++ b/lib/VNDB/Plugin/TransAdmin.pm @@ -0,0 +1,239 @@ +# This plugin provides a quick and dirty user interface to editing lang.txt, +# to use it, add the following to your data/config.pl: +# +# use VNDB::Plugin::TransAdmin; +# $VNDB::S{transadmin} = { +# => 'all' || || +# }; +# +# And then open /tladmin in your browser. +# Also make sure data/lang.txt is writable by the httpd process. +# English is considered the 'main' language, and cannot be edited using this interface. + +package VNDB::Plugin::TransAdmin; + +use strict; +use warnings; +use YAWF ':html'; +use LangFile; +use VNDB::Func; + + +my $langfile = "$VNDB::ROOT/data/lang.txt"; + + +YAWF::register( + qr{tladmin(?:/([a-z]+))?} => \&tladmin +); + + +sub uri_escape { + local $_ = shift; + s/ /%20/g; + s/\?/%3F/g; + s/;/%3B/g; + s/&/%26/g; + return $_; +} + + +sub _allowed { + my($self, $lang) = @_; + my $a = $self->{transadmin}{ $self->authInfo->{id} }; + return $a eq 'all' || $a eq $lang || ref($a) eq 'ARRAY' && grep $_ eq $lang, @$a; +} + + +sub tladmin { + my($self, $lang) = @_; + + $lang ||= ''; + return 404 if $lang && ($lang eq 'en' || !grep $_ eq $lang, $self->{l10n}->languages); + my $sect = $self->reqParam('sect')||''; + + my $uid = $self->authInfo->{id}; + return $self->htmlDenied if !$uid || !$self->{transadmin}{$uid}; + + if(!-w $langfile) { + $self->htmlHeader(title => 'Language file not writable', noindex => 1); + div class => 'mainbox'; + h1 'Language file not writable'; + div class => 'warning', 'Sorry, I do not have enough permission to write to the language file.'; + end; + $self->htmlFooter; + return; + } + + _savelang($self, $lang) if $lang && $self->reqMethod eq 'POST' && _allowed($self, $lang); + my($sects, $page) = _readlang($lang, $sect) if $lang; + + $self->htmlHeader(title => 'Quick-and-dirty Translation Editor', noindex => 1); + div class => 'mainbox'; + h1 'Quick-and-dirty Translation Editor'; + h2 class => 'alttitle', 'Step #1: Choose a language'; + p class => 'browseopts'; + a $lang eq $_ ? (class => 'optselected') : (), href => "/tladmin/$_", mt "_lang_$_" + for grep !/en/, $self->{l10n}->languages; + end; + _sections($self, $lang, $sect, $sects) if $lang; + end; + + _page($self, $lang, $sect, $page) if $lang && $sect; + + $self->htmlFooter; +} + + +sub _savelang { + my($self, $lang) = @_; + + # do everything in-memory, so we don't need write access to a temporary file + # (this has the downside that in the event something goes wrong, everything will be wiped) + my $f = LangFile->new(read => $langfile); + my @read; + push @read, $_ while (local $_ = $f->read); + $f->close; + + my @keys = $self->reqParam; + $f = LangFile->new(write => $langfile); + my $key; + for my $l (@read) { + $key = $l->[1] if $l->[0] eq 'key'; + if($l->[0] eq 'tl' && $l->[1] eq $lang && grep $key eq $_, @keys) { + $l->[2] = !$self->reqParam("check$key"); + $l->[3] = $self->reqParam($key); + $l->[3] =~ s/\r?\n/\n/g; + $l->[3] =~ s/\s+$//g; + } + $f->write(@$l); + } + $f->close; + + # re-read the file and regenerate the JS in case we're not running as CGI + if($INC{"FCGI.pm"}) { + VNDB::L10N::loadfile(); + VNDB::checkjs(); + } +} + + +sub _readlang { + my($lang, $sect) = @_; + my @sect; # [ title, count, unsync ] + my @page; # [ 'comment'||'line', || ( , , , ) ] + + my $f = LangFile->new(read => $langfile); + my($key, $insect); + while(my $l = $f->read) { + my $t = shift @$l; + + if($t eq 'space') { + if(join("\n", @$l) =~ /((#{30,90}\n)## +(.+) +##\n\2.+)^/ms) { + my $header = $1; + (my $title = $3) =~ s/\s+$//; + $title =~ s/\s+\([^)]+\)$//; + push @sect, [ $title, 0, 0 ]; + $insect = $title eq $sect; + push @page, [ 'comment', $header ] if $insect; + } elsif($insect) { + push @page, [ 'comment', join "\n", @$l ]; + } + } + + $sect[$#sect][1]++ if $t eq 'key'; + $sect[$#sect][2]++ if $t eq 'tl' && $l->[0] eq $lang && !$l->[1]; + + next if !$insect; + push @page, [ 'line', $l->[0] ] if $t eq 'key'; + $page[$#page][2] = $l->[2] if $t eq 'tl' && $l->[0] eq 'en'; + if($t eq 'tl' && $l->[0] eq $lang) { + $page[$#page][3] = $l->[1]; + $page[$#page][4] = $l->[2]; + } + } + $f->close; + return (\@sect, \@page); +} + + +sub _sections { + my($self, $lang, $sect, $list) = @_; + + br; + h2 class => 'alttitle', 'Step #2: Choose a section'; + div style => 'margin: 0 40px'; + for (@$list) { + div style => 'float: left; width: 200px;'; + a href => "/tladmin/$lang?sect=".uri_escape($_->[0]), $_->[0] if $sect ne $_->[0]; + txt $sect if $sect eq $_->[0]; + txt " "; + txt "0/$_->[1]" if !$_->[2]; + b class => 'standout', "$_->[2]/$_->[1]" if $_->[2]; + end; + } + clearfloat; + end; + br; +} + + +sub _page { + my($self, $lang, $sect, $page) = @_; + + form action => "/tladmin/$lang?sect=".uri_escape($sect), method => 'POST', 'accept-charset' => 'utf-8'; + div class => 'mainbox'; + h1 $sect; + + if(_allowed($self, $lang)) { + h2 class => 'alttitle', "Don't forget to hit the 'save' button to make your changes permament!"; + } else { + div class => 'warning'; + h2 'Read-only'; + p "You can't edit this language."; + end; + } + + for my $l (@$page) { + if($l->[0] eq 'comment') { + pre; + b class => 'grayedout', $l->[1]."\n"; + end; + next; + } + + my(undef, $key, $en, $sync, $tl) = @$l; + b class => $sync ? 'grayedout' : 'standout', ":$key"; + br; + div style => 'margin-left: 25px; font: 12px Tahoma; width: 700px; overflow-x: auto; white-space: nowrap', $en; + my $multi = $en =~ y/\n//; + + div style => 'width: 23px; float: left; text-align: right'; + input type => 'checkbox', name => "check$key", id => "check$key", !$sync ? (checked => 'checked') : (); + end; + div style => 'float: left'; + if($multi) { + $tl =~ s/&/&/; + $tl =~ s//>/; + textarea name => $key, id => $key, rows => $multi+2, style => 'width: 700px; height: auto; white-space: nowrap; border: none', wrap => 'off'; + lit $tl; + end; + } else { + input type => 'text', class => 'text', name => $key, id => $key, value => $tl, style => 'width: 700px; border: none'; + } + end; + clearfloat; + } + if(_allowed($self, $lang)) { + br;br; + fieldset class => 'submit'; + input type => 'submit', value => 'Save', class => 'submit'; + end; + } + end; + end; +} + + +1; + -- cgit v1.2.3