diff options
Diffstat (limited to 'lib/VNDB')
-rw-r--r-- | lib/VNDB/DB/Discussions.pm | 12 | ||||
-rw-r--r-- | lib/VNDB/DB/Misc.pm | 40 | ||||
-rw-r--r-- | lib/VNDB/DB/Producers.pm | 37 | ||||
-rw-r--r-- | lib/VNDB/DB/Releases.pm | 14 | ||||
-rw-r--r-- | lib/VNDB/DB/Users.pm | 28 | ||||
-rw-r--r-- | lib/VNDB/DB/VN.pm | 13 | ||||
-rw-r--r-- | lib/VNDB/Func.pm | 4 | ||||
-rw-r--r-- | lib/VNDB/Handler/Discussions.pm | 7 | ||||
-rw-r--r-- | lib/VNDB/Handler/Misc.pm | 13 | ||||
-rw-r--r-- | lib/VNDB/Handler/Producers.pm | 160 | ||||
-rw-r--r-- | lib/VNDB/Handler/Releases.pm | 96 | ||||
-rw-r--r-- | lib/VNDB/Handler/Tags.pm | 45 | ||||
-rw-r--r-- | lib/VNDB/Handler/ULists.pm | 20 | ||||
-rw-r--r-- | lib/VNDB/Handler/Users.pm | 6 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNBrowse.pm | 6 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNEdit.pm | 45 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNPage.pm | 71 | ||||
-rw-r--r-- | lib/VNDB/L10N.pm | 115 | ||||
-rw-r--r-- | lib/VNDB/Plugin/TransAdmin.pm | 349 | ||||
-rw-r--r-- | lib/VNDB/Util/Auth.pm | 4 | ||||
-rw-r--r-- | lib/VNDB/Util/CommonHTML.pm | 66 | ||||
-rw-r--r-- | lib/VNDB/Util/LayoutHTML.pm | 20 |
22 files changed, 868 insertions, 303 deletions
diff --git a/lib/VNDB/DB/Discussions.pm b/lib/VNDB/DB/Discussions.pm index 26beaaee..60487098 100644 --- a/lib/VNDB/DB/Discussions.pm +++ b/lib/VNDB/DB/Discussions.pm @@ -5,7 +5,7 @@ use strict; use warnings; use Exporter 'import'; -our @EXPORT = qw|dbThreadGet dbThreadEdit dbThreadAdd dbPostGet dbPostEdit dbPostAdd dbThreadCount|; +our @EXPORT = qw|dbThreadGet dbThreadEdit dbThreadAdd dbPostGet dbPostEdit dbPostAdd dbThreadCount dbPostRead|; # Options: id, type, iid, results, page, what, notusers @@ -243,5 +243,15 @@ sub dbPostAdd { } +sub dbPostRead { # thread id, user id, last post number + my($s, $tid, $uid, $num) = @_; + $s->dbExec(q| + UPDATE threads_boards + SET lastread = ? + WHERE tid = ? AND type = 'u' AND iid = ?|, + $num, $tid, $uid); +} + + 1; diff --git a/lib/VNDB/DB/Misc.pm b/lib/VNDB/DB/Misc.pm index eeb860b0..b3522d72 100644 --- a/lib/VNDB/DB/Misc.pm +++ b/lib/VNDB/DB/Misc.pm @@ -6,7 +6,7 @@ use warnings; use Exporter 'import'; our @EXPORT = qw| - dbStats dbRevisionInsert dbItemInsert dbRevisionGet dbItemMod dbLanguages dbRandomQuote + dbStats dbRevisionInsert dbItemInsert dbRevisionGet dbItemMod dbRandomQuote |; @@ -24,12 +24,12 @@ sub dbStats { # This function leaves the DB in an inconsistent state, the actual revision # will have to be inserted directly after calling this function, otherwise # the commit will fail. -# Arguments: type [0..2], item ID, edit summary +# Arguments: type [vrp], item ID, edit summary # Returns: local revision, global revision sub dbRevisionInsert { my($self, $type, $iid, $editsum, $uid) = @_; - my $table = [qw|vn releases producers|]->[$type]; + my $table = {qw|v vn r releases p producers|}->{$type}; my $c = $self->dbRow(q| INSERT INTO changes (type, requester, ip, comments, rev) @@ -43,7 +43,7 @@ sub dbRevisionInsert { )) RETURNING id, rev|, $type, $uid||$self->authInfo->{id}, $self->reqIP, $editsum, - $table, [qw|v r p|]->[$type], $iid + $table, $type, $iid ); $self->dbExec(q|UPDATE !s SET latest = ? WHERE id = ?|, $table, $c->{id}, $iid); @@ -54,7 +54,7 @@ sub dbRevisionInsert { # Comparable to RevisionInsert, but creates a new item with a corresponding # change. Same things about inconsistent state, etc. -# Argumments: type [0..2], edit summary, [uid] +# Argumments: type [vrp], edit summary, [uid] # Returns: item id, global revision sub dbItemInsert { my($self, $type, $editsum, $uid) = @_; @@ -70,7 +70,7 @@ sub dbItemInsert { INSERT INTO !s (latest) VALUES (?) RETURNING id|, - [qw|vn releases producers|]->[$type], $cid + {qw|v vn r releases p producers|}->{$type}, $cid )->{id}; return ($iid, $cid); @@ -94,7 +94,7 @@ sub dbRevisionGet { '((c.type = ? AND vr.vid = ?) OR (c.type = ? AND rv.vid = ?))' => [0, $o{iid}, 1, $o{iid}], ) : ( $o{type} ? ( - 'c.type = ?' => { v=>0, r=>1, p=>2 }->{$o{type}} ) : (), + 'c.type = ?' => $o{type} ) : (), $o{iid} ? ( '!sr.!sid = ?' => [ $o{type}, $o{type}, $o{iid} ] ) : (), ), @@ -113,14 +113,14 @@ sub dbRevisionGet { my @join = ( $o{iid} || $o{what} =~ /item/ || $o{hidden} || $o{releases} ? ( - 'LEFT JOIN vn_rev vr ON c.type = 0 AND c.id = vr.id', - 'LEFT JOIN releases_rev rr ON c.type = 1 AND c.id = rr.id', - 'LEFT JOIN producers_rev pr ON c.type = 2 AND c.id = pr.id', + q{LEFT JOIN vn_rev vr ON c.type = 'v' AND c.id = vr.id}, + q{LEFT JOIN releases_rev rr ON c.type = 'r' AND c.id = rr.id}, + q{LEFT JOIN producers_rev pr ON c.type = 'p' AND c.id = pr.id}, ) : (), $o{hidden} || $o{releases} ? ( - 'LEFT JOIN vn v ON c.type = 0 AND vr.vid = v.id', - 'LEFT JOIN releases r ON c.type = 1 AND rr.rid = r.id', - 'LEFT JOIN producers p ON c.type = 2 AND pr.pid = p.id', + q{LEFT JOIN vn v ON c.type = 'v' AND vr.vid = v.id}, + q{LEFT JOIN releases r ON c.type = 'r' AND rr.rid = r.id}, + q{LEFT JOIN producers p ON c.type = 'p' AND pr.pid = p.id}, ) : (), $o{what} =~ /user/ ? 'JOIN users u ON c.requester = u.id' : (), $o{releases} ? 'LEFT JOIN releases_vn rv ON c.id = rv.rid' : (), @@ -160,20 +160,6 @@ sub dbItemMod { } -# Returns a list of languages actually in use -sub dbLanguages { - my $self = shift; - return [ - map $_->{lang}, @{$self->dbAll(q| - SELECT DISTINCT rl.lang - FROM releases r - JOIN releases_lang rl ON rl.rid = r.latest - WHERE r.hidden = FALSE| - )} - ]; -} - - # Returns a random quote (hashref with keys = vid, quote) sub dbRandomQuote { return $_[0]->dbRow(q| diff --git a/lib/VNDB/DB/Producers.pm b/lib/VNDB/DB/Producers.pm index 65d1fbca..7c7da3ba 100644 --- a/lib/VNDB/DB/Producers.pm +++ b/lib/VNDB/DB/Producers.pm @@ -9,7 +9,7 @@ our @EXPORT = qw|dbProducerGet dbProducerEdit dbProducerAdd|; # options: results, page, id, search, char, rev -# what: extended, changes, vn +# what: extended changes vn relations relgraph sub dbProducerGet { my $self = shift; my %o = ( @@ -40,10 +40,12 @@ sub dbProducerGet { push @join, $o{rev} ? 'JOIN producers p ON p.id = pr.pid' : 'JOIN producers p ON pr.id = p.latest'; push @join, 'JOIN changes c ON c.id = pr.id' if $o{what} =~ /changes/ || $o{rev}; push @join, 'JOIN users u ON u.id = c.requester' if $o{what} =~ /changes/; + push @join, 'JOIN relgraphs pg ON pg.id = p.rgraph' if $o{what} =~ /relgraph/; - my $select = 'p.id, pr.type, pr.name, pr.original, pr.lang'; + my $select = 'p.id, pr.type, pr.name, pr.original, pr.lang, pr.id AS cid, p.rgraph'; $select .= ', pr.desc, pr.alias, pr.website, p.hidden, p.locked' if $o{what} =~ /extended/; $select .= q|, extract('epoch' from c.added) as added, c.requester, c.comments, p.latest, pr.id AS cid, u.username, c.rev| if $o{what} =~ /changes/; + $select .= ', pg.svg' if $o{what} =~ /relgraph/; my($r, $np) = $self->dbPage(\%o, q| SELECT !s @@ -61,7 +63,8 @@ sub dbProducerGet { } 0..$#$r; push @{$r->[$r{$_->{pid}}]{vn}}, $_ for (@{$self->dbAll(q| - SELECT MAX(vp.pid) AS pid, v.id, MAX(vr.title) AS title, MAX(vr.original) AS original, MIN(rr.released) AS date + SELECT MAX(vp.pid) AS pid, v.id, MAX(vr.title) AS title, MAX(vr.original) AS original, MIN(rr.released) AS date, + MAX(CASE WHEN vp.developer = true THEN 1 ELSE 0 END) AS developer, MAX(CASE WHEN vp.publisher = true THEN 1 ELSE 0 END) AS publisher FROM releases_producers vp JOIN releases_rev rr ON rr.id = vp.rid JOIN releases r ON r.latest = rr.id @@ -77,6 +80,22 @@ sub dbProducerGet { )}); } + if(@$r && $o{what} =~ /relations/) { + my %r = map { + $r->[$_]{relations} = []; + ($r->[$_]{cid}, $_) + } 0..$#$r; + + push @{$r->[$r{$_->{pid1}}]{relations}}, $_ for(@{$self->dbAll(q| + SELECT rel.pid1, rel.pid2 AS id, rel.relation, pr.name, pr.original + FROM producers_relations rel + JOIN producers p ON rel.pid2 = p.id + JOIN producers_rev pr ON p.latest = pr.id + WHERE rel.pid1 IN(!l)|, + [ keys %r ] + )}); + } + return wantarray ? ($r, $np) : $r; } @@ -85,7 +104,7 @@ sub dbProducerGet { # returns: ( local revision, global revision ) sub dbProducerEdit { my($self, $pid, %o) = @_; - my($rev, $cid) = $self->dbRevisionInsert(2, $pid, $o{editsum}, $o{uid}); + my($rev, $cid) = $self->dbRevisionInsert('p', $pid, $o{editsum}, $o{uid}); insert_rev($self, $cid, $pid, \%o); return ($rev, $cid); } @@ -95,14 +114,14 @@ sub dbProducerEdit { # returns: ( item id, global revision ) sub dbProducerAdd { my($self, %o) = @_; - my($pid, $cid) = $self->dbItemInsert(2, $o{editsum}, $o{uid}); + my($pid, $cid) = $self->dbItemInsert('p', $o{editsum}, $o{uid}); insert_rev($self, $cid, $pid, \%o); return ($pid, $cid); } # helper function, inserts a producer revision -# Arguments: global revision, item id, { columns in producers_rev } +# Arguments: global revision, item id, { columns in producers_rev }, relations sub insert_rev { my($self, $cid, $pid, $o) = @_; $self->dbExec(q| @@ -110,6 +129,12 @@ sub insert_rev { VALUES (!l)|, [ $cid, $pid, @$o{qw| name original website type lang desc alias|} ] ); + + $self->dbExec(q| + INSERT INTO producers_relations (pid1, pid2, relation) + VALUES (?, ?, ?)|, + $cid, $_->[1], $_->[0] + ) for (@{$o->{relations}}); } diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm index 672596da..9260b787 100644 --- a/lib/VNDB/DB/Releases.pm +++ b/lib/VNDB/DB/Releases.pm @@ -117,7 +117,7 @@ sub dbReleaseGet { if($o{what} =~ /producers/) { push(@{$r->[$r{$_->{rid}}]{producers}}, $_) for (@{$self->dbAll(q| - SELECT rp.rid, p.id, pr.name, pr.original, pr.type + SELECT rp.rid, rp.developer, rp.publisher, p.id, pr.name, pr.original, pr.type FROM releases_producers rp JOIN producers p ON rp.pid = p.id JOIN producers_rev pr ON pr.id = p.latest @@ -137,7 +137,7 @@ sub dbReleaseGet { } if($o{what} =~ /media/) { - ($_->{medium}=~s/\s+//||1)&&push(@{$r->[$r{$_->{rid}}]{media}}, $_) for (@{$self->dbAll(q| + push(@{$r->[$r{$_->{rid}}]{media}}, $_) for (@{$self->dbAll(q| SELECT rid, medium, qty FROM releases_media WHERE rid IN(!l)|, @@ -154,7 +154,7 @@ sub dbReleaseGet { # returns: ( local revision, global revision ) sub dbReleaseEdit { my($self, $rid, %o) = @_; - my($rev, $cid) = $self->dbRevisionInsert(1, $rid, $o{editsum}, $o{uid}); + my($rev, $cid) = $self->dbRevisionInsert('r', $rid, $o{editsum}, $o{uid}); insert_rev($self, $cid, $rid, \%o); return ($rev, $cid); } @@ -164,7 +164,7 @@ sub dbReleaseEdit { # returns: ( item id, global revision ) sub dbReleaseAdd { my($self, %o) = @_; - my($rid, $cid) = $self->dbItemInsert(1, $o{editsum}, $o{uid}); + my($rid, $cid) = $self->dbItemInsert('r', $o{editsum}, $o{uid}); insert_rev($self, $cid, $rid, \%o); return ($rid, $cid); } @@ -189,9 +189,9 @@ sub insert_rev { ) for (@{$o->{languages}}); $self->dbExec(q| - INSERT INTO releases_producers (rid, pid) - VALUES (?, ?)|, - $cid, $_ + INSERT INTO releases_producers (rid, pid, developer, publisher) + VALUES (?, ?, ?, ?)|, + $cid, $_->[0], $_->[1]?1:0, $_->[2]?1:0 ) for (@{$o->{producers}}); $self->dbExec(q| diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm index b2cd1a31..593c6415 100644 --- a/lib/VNDB/DB/Users.pm +++ b/lib/VNDB/DB/Users.pm @@ -5,11 +5,11 @@ use strict; use warnings; use Exporter 'import'; -our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel dbSessionAdd dbSessionDel dbSessionCheck|; +our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel dbUserMessageCount dbSessionAdd dbSessionDel dbSessionCheck|; # %options->{ username passwd mail order uid ip registered search results page what } -# what: stats mymessages +# what: stats extended sub dbUserGet { my $s = shift; my %o = ( @@ -43,8 +43,12 @@ sub dbUserGet { ); my @select = ( - qw|id username mail rank salt c_votes c_changes show_nsfw show_list skin customcss ip c_tags ign_votes|, - q|encode(passwd, 'hex') AS passwd|, q|extract('epoch' from registered) as registered|, + qw|id username c_votes c_changes show_list c_tags|, + q|extract('epoch' from registered) as registered|, + $o{what} =~ /extended/ ? ( + qw|mail rank salt skin customcss show_nsfw ign_votes|, + q|encode(passwd, 'hex') AS passwd| + ) : (), $o{what} =~ /stats/ ? ( '(SELECT COUNT(*) FROM rlists WHERE uid = u.id) AS releasecount', '(SELECT COUNT(DISTINCT rv.vid) FROM rlists rl JOIN releases r ON rl.rid = r.id JOIN releases_vn rv ON rv.rid = r.latest WHERE uid = u.id) AS vncount', @@ -53,8 +57,6 @@ sub dbUserGet { '(SELECT COUNT(DISTINCT tag) FROM tags_vn WHERE uid = u.id) AS tagcount', '(SELECT COUNT(DISTINCT vid) FROM tags_vn WHERE uid = u.id) AS tagvncount', ) : (), - $o{what} =~ /mymessages/ ? - '(SELECT COUNT(*) FROM threads_boards tb JOIN threads t ON t.id = tb.tid WHERE tb.type = \'u\' AND tb.iid = u.id AND t.hidden = FALSE) AS mymessages' : (), ); my($r, $np) = $s->dbPage(\%o, q| @@ -110,6 +112,20 @@ sub dbUserDel { } +# Returns number of unread messages +sub dbUserMessageCount { # uid + my($s, $uid) = @_; + return $s->dbRow(q{ + SELECT SUM(tbi.count) AS cnt FROM ( + SELECT t.count-COALESCE(tb.lastread,0) + FROM threads_boards tb + JOIN threads t ON t.id = tb.tid AND NOT t.hidden + WHERE tb.type = 'u' AND tb.iid = ? + ) AS tbi (count) + }, $uid)->{cnt}||0; +} + + # Adds a session to the database # If no expiration is supplied the database default is used # uid, 40 character session token, expiration time (timestamp) diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm index b3603985..bb2c1275 100644 --- a/lib/VNDB/DB/VN.pm +++ b/lib/VNDB/DB/VN.pm @@ -5,6 +5,7 @@ use strict; use warnings; use Exporter 'import'; use VNDB::Func 'gtintype'; +use Encode 'decode_utf8'; our @EXPORT = qw|dbVNGet dbVNAdd dbVNEdit dbVNImageId dbVNCache dbScreenshotAdd dbScreenshotGet dbScreenshotRandom|; @@ -76,7 +77,7 @@ sub dbVNGet { $o{what} =~ /changes/ ? 'JOIN users u ON u.id = c.requester' : (), $o{what} =~ /relgraph/ ? - 'JOIN relgraph rg ON rg.id = v.rgraph' : (), + 'JOIN relgraphs vg ON vg.id = v.rgraph' : (), ); my $tag_ids = $o{tags_include} && join ',', @{$o{tags_include}[1]}; @@ -86,7 +87,7 @@ sub dbVNGet { qw|vr.alias vr.image vr.img_nsfw vr.length vr.desc vr.l_wp vr.l_encubed vr.l_renai vr.l_vnn| ) : (), $o{what} =~ /changes/ ? ( qw|c.requester c.comments v.latest u.username c.rev c.causedby|, q|extract('epoch' from c.added) as added|) : (), - $o{what} =~ /relgraph/ ? 'rg.cmap' : (), + $o{what} =~ /relgraph/ ? 'vg.svg' : (), $o{what} =~ /ranking/ ? '(SELECT COUNT(*)+1 FROM vn iv WHERE iv.hidden = false AND iv.c_popularity > v.c_popularity) AS ranking' : (), $tag_ids ? qq|(SELECT AVG(tvb.rating) FROM tags_vn_bayesian tvb WHERE tvb.tag IN($tag_ids) AND tvb.vid = v.id AND spoiler <= $o{tags_include}[0] GROUP BY tvb.vid) AS tagscore| : (), @@ -101,6 +102,10 @@ sub dbVNGet { join(', ', @select), join(' ', @join), \%where, $o{order}, ); + if($o{what} =~ /relgraph/) { + $_->{svg} = decode_utf8($_->{svg}) for @$r; + } + if(@$r && $o{what} =~ /(anime|relations|screenshots)/) { my %r = map { $r->[$_]{anime} = []; @@ -155,7 +160,7 @@ sub dbVNGet { # returns: ( local revision, global revision ) sub dbVNEdit { my($self, $id, %o) = @_; - my($rev, $cid) = $self->dbRevisionInsert(0, $id, $o{editsum}, $o{uid}); + my($rev, $cid) = $self->dbRevisionInsert('v', $id, $o{editsum}, $o{uid}); insert_rev($self, $cid, $id, \%o); return ($rev, $cid); } @@ -165,7 +170,7 @@ sub dbVNEdit { # returns: ( item id, global revision ) sub dbVNAdd { my($self, %o) = @_; - my($id, $cid) = $self->dbItemInsert(0, $o{editsum}, $o{uid}); + my($id, $cid) = $self->dbItemInsert('v', $o{editsum}, $o{uid}); insert_rev($self, $cid, $id, \%o); return ($id, $cid); } diff --git a/lib/VNDB/Func.pm b/lib/VNDB/Func.pm index cd4c4b62..ad38215e 100644 --- a/lib/VNDB/Func.pm +++ b/lib/VNDB/Func.pm @@ -149,10 +149,10 @@ sub gtintype { sub liststat { my $l = shift; return '' if !$l; - my $rs = $YAWF::OBJ->{vn_rstat}[$l->{rstat}]; + my $rs = mt('_rlst_rstat_'.$l->{rstat}); $rs = qq|<b class="done">$rs</b>| if $l->{rstat} == 2; # Obtained $rs = qq|<b class="todo">$rs</b>| if $l->{rstat} < 2; # Unknown/pending - my $vs = $YAWF::OBJ->{vn_vstat}[$l->{vstat}]; + my $vs = mt('_rlst_vstat_'.$l->{vstat}); $vs = qq|<b class="done">$vs</b>| if $l->{vstat} == 2; # Finished $vs = qq|<b class="todo">$vs</b>| if $l->{vstat} == 0 || $l->{vstat} == 4; # Unknown/dropped return "$rs / $vs"; diff --git a/lib/VNDB/Handler/Discussions.pm b/lib/VNDB/Handler/Discussions.pm index 1d74ac6a..12b55029 100644 --- a/lib/VNDB/Handler/Discussions.pm +++ b/lib/VNDB/Handler/Discussions.pm @@ -29,6 +29,11 @@ sub thread { my $p = $self->dbPostGet(tid => $tid, results => 25, page => $page, what => 'user'); return 404 if !$p->[0]; + # mark as read when this thread is posted in the board of the currently logged in user + my $uid = $self->authInfo->{id}; + $self->dbPostRead($t->{id}, $uid, $p->[$#$p]{num}) + if $uid && grep $_->{type} eq 'u' && $_->{iid} == $uid, @{$t->{boards}}; + $self->htmlHeader(title => $t->{title}); div class => 'mainbox'; @@ -293,7 +298,7 @@ sub board { order => $type eq 'an' ? 't.id DESC' : 'tpl.date DESC', ); - $self->htmlHeader(title => $title, noindex => !@$list); + $self->htmlHeader(title => $title, noindex => !@$list || $type eq 'u'); $self->htmlMainTabs($type, $obj, 'disc') if $iid; div class => 'mainbox'; diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm index 80a9e9ea..9625372b 100644 --- a/lib/VNDB/Handler/Misc.pm +++ b/lib/VNDB/Handler/Misc.pm @@ -66,10 +66,9 @@ sub homepage { my $changes = $self->dbRevisionGet(what => 'item user', results => 10, auto => 1, hidden => 1); ul; for (@$changes) { - my $t = (qw|v r p|)[$_->{type}]; li; - lit mt '_home_recentchanges_item', $t, - sprintf('<a href="%s" title="%s">%s</a>', "/$t$_->{iid}.$_->{rev}", + lit mt '_home_recentchanges_item', $_->{type}, + sprintf('<a href="%s" title="%s">%s</a>', "/$_->{type}$_->{iid}.$_->{rev}", xml_escape($_->{ioriginal}||$_->{ititle}), xml_escape shorten $_->{ititle}, 33), $_; end; @@ -295,6 +294,12 @@ sub docpage { close $F; $ii; }eg; + s{^:TOP5CONTRIB:$}{ + my $l = $self->dbUserGet(results => 6, order => 'c_changes DESC'); + '<dl>'.join('', map $_->{id} == 1 ? () : + sprintf('<dt><a href="/u%d">%s</a></dt><dd>%d</dd>', $_->{id}, $_->{username}, $_->{c_changes}), + @$l).'</dl>'; + }eg; } $self->htmlHeader(title => $title); @@ -331,7 +336,7 @@ sub itemmod { my $obj = $type eq 'v' ? $self->dbVNGet(id => $iid)->[0] : $type eq 'r' ? $self->dbReleaseGet(id => $iid, what => 'vn extended')->[0] : - $self->dbProducerGet(id => $iid)->[0]; + $self->dbProducerGet(id => $iid, what => 'extended')->[0]; return 404 if !$obj->{id}; $self->dbItemMod($type, $iid, $act eq 'hide' ? (hidden => !$obj->{hidden}) : (locked => !$obj->{locked})); diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm index e6477567..4e75962b 100644 --- a/lib/VNDB/Handler/Producers.pm +++ b/lib/VNDB/Handler/Producers.pm @@ -3,11 +3,12 @@ package VNDB::Handler::Producers; use strict; use warnings; -use YAWF ':html', ':xml'; +use YAWF ':html', ':xml', 'xml_escape'; use VNDB::Func; YAWF::register( + qr{p([1-9]\d*)/rg} => \&rg, qr{p([1-9]\d*)(?:\.([1-9]\d*))?} => \&page, qr{p(?:([1-9]\d*)(?:\.([1-9]\d*))?/edit|/new)} => \&edit, @@ -16,12 +17,34 @@ YAWF::register( ); +sub rg { + my($self, $pid) = @_; + + my $p = $self->dbProducerGet(id => $pid, what => 'relgraph')->[0]; + return 404 if !$p->{id} || !$p->{rgraph}; + + my $title = mt '_prodrg_title', $p->{name}; + return if $self->htmlRGHeader($title, 'p', $p); + + $p->{svg} =~ s/\$___(_prodrel_[a-z]+)____\$/mt $1/eg; + $p->{svg} =~ s/\$(_lang_[a-z]+)_\$/mt $1/eg; + $p->{svg} =~ s/\$(_ptype_[a-z]+)_\$/mt $1/eg; + + div class => 'mainbox'; + h1 $title; + p class => 'center'; + lit $p->{svg}; + end; + end; + $self->htmlFooter; +} + sub page { my($self, $pid, $rev) = @_; my $p = $self->dbProducerGet( id => $pid, - what => 'vn extended'.($rev ? ' changes' : ''), + what => 'vn extended relations'.($rev ? ' changes' : ''), $rev ? ( rev => $rev ) : () )->[0]; return 404 if !$p->{id}; @@ -31,7 +54,7 @@ sub page { return if $self->htmlHiddenMessage('p', $p); if($rev) { - my $prev = $rev && $rev > 1 && $self->dbProducerGet(id => $pid, rev => $rev-1, what => 'changes extended')->[0]; + my $prev = $rev && $rev > 1 && $self->dbProducerGet(id => $pid, rev => $rev-1, what => 'changes extended relations')->[0]; $self->htmlRevision('p', $prev, $p, [ type => serialize => sub { mt "_ptype_$_[0]" } ], [ name => diff => 1 ], @@ -40,6 +63,12 @@ sub page { [ lang => serialize => sub { "$_[0] (".mt("_lang_$_[0]").')' } ], [ website => diff => 1 ], [ desc => diff => 1 ], + [ relations => join => '<br />', split => sub { + my @r = map sprintf('%s: <a href="/p%d" title="%s">%s</a>', + mt("_prodrel_$_->{relation}"), $_->{id}, xml_escape($_->{original}||$_->{name}), xml_escape shorten $_->{name}, 40 + ), sort { $a->{id} <=> $b->{id} } @{$_[0]}; + return @r ? @r : (mt '_proddiff_none'); + }], ); } @@ -56,6 +85,23 @@ sub page { } end; + if(@{$p->{relations}}) { + my %rel; + push @{$rel{$_->{relation}}}, $_ + for (sort { $a->{name} cmp $b->{name} } @{$p->{relations}}); + p class => 'center'; + txt "\n"; + for my $r (sort keys %rel) { + txt mt("_prodrel_$r").': '; + for (@{$rel{$r}}) { + a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 40; + txt ', ' if $_ ne $rel{$r}[$#{$rel{$r}}]; + } + txt "\n"; + } + end; + } + if($p->{desc}) { p class => 'description'; lit bb2html $p->{desc}; @@ -75,6 +121,8 @@ sub page { lit $self->{l10n}->datestr($_->{date}); end; a href => "/v$_->{id}", title => $_->{original}, $_->{title}; + b class => 'grayedout', ' ('.join(', ', + $_->{developer} ? mt '_prodpage_dev' : (), $_->{publisher} ? mt '_prodpage_pub' : ()).')'; end; } end; @@ -89,36 +137,55 @@ sub page { sub edit { my($self, $pid, $rev) = @_; - my $p = $pid && $self->dbProducerGet(id => $pid, what => 'changes extended', $rev ? (rev => $rev) : ())->[0]; + my $p = $pid && $self->dbProducerGet(id => $pid, what => 'changes extended relations', $rev ? (rev => $rev) : ())->[0]; return 404 if $pid && !$p->{id}; $rev = undef if !$p || $p->{cid} == $p->{latest}; return $self->htmlDenied if !$self->authCan('edit') || $pid && ($p->{locked} && !$self->authCan('lock') || $p->{hidden} && !$self->authCan('del')); - my %b4 = !$pid ? () : map { $_ => $p->{$_} } qw|type name original lang website desc alias|; + my %b4 = !$pid ? () : ( + (map { $_ => $p->{$_} } qw|type name original lang website desc alias|), + prodrelations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{name}, sort { $a->{id} <=> $b->{id} } @{$p->{relations}}), + ); my $frm; if($self->reqMethod eq 'POST') { $frm = $self->formValidate( - { name => 'type', enum => $self->{producer_types} }, - { name => 'name', maxlength => 200 }, - { name => 'original', required => 0, maxlength => 200, default => '' }, - { name => 'alias', required => 0, maxlength => 500, default => '' }, - { name => 'lang', enum => $self->{languages} }, - { name => 'website', required => 0, template => 'url', default => '' }, - { name => 'desc', required => 0, maxlength => 5000, default => '' }, - { name => 'editsum', maxlength => 5000 }, + { name => 'type', enum => $self->{producer_types} }, + { name => 'name', maxlength => 200 }, + { name => 'original', required => 0, maxlength => 200, default => '' }, + { name => 'alias', required => 0, maxlength => 500, default => '' }, + { name => 'lang', enum => $self->{languages} }, + { name => 'website', required => 0, template => 'url', default => '' }, + { name => 'desc', required => 0, maxlength => 5000, default => '' }, + { name => 'prodrelations', required => 0, maxlength => 5000, default => '' }, + { name => 'editsum', maxlength => 5000 }, ); if(!$frm->{_err}) { + # parse + my $relations = [ map { /^([a-z]+),([0-9]+),(.+)$/ && (!$pid || $2 != $pid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{prodrelations} ]; + + # normalize + $frm->{prodrelations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations}; + return $self->resRedirect("/p$pid", 'post') if $pid && !grep $frm->{$_} ne $b4{$_}, keys %b4; + $frm->{relations} = $relations; $rev = 1; + my $cid; if($pid) { - ($rev) = $self->dbProducerEdit($pid, %$frm); + ($rev, $cid) = $self->dbProducerEdit($pid, %$frm); } else { - ($pid) = $self->dbProducerAdd(%$frm); + ($pid, $cid) = $self->dbProducerAdd(%$frm); + } + + # update reverse relations + if(!$pid && $#$relations >= 0 || $pid && $frm->{prodrelations} ne $b4{prodrelations}) { + my %old = $pid ? (map { $_->{id} => $_->{relation} } @{$p->{relations}}) : (); + my %new = map { $_->[1] => $_->[0] } @$relations; + _updreverse($self, \%old, \%new, $pid, $cid, $rev); } return $self->resRedirect("/p$pid.$rev", 'post'); @@ -133,7 +200,8 @@ sub edit { $self->htmlHeader(title => $title, noindex => 1); $self->htmlMainTabs('p', $p, 'edit') if $pid; $self->htmlEditMessage('p', $p, $title); - $self->htmlForm({ frm => $frm, action => $pid ? "/p$pid/edit" : '/p/new', editsum => 1 }, 'pedit_geninfo' => [mt('_pedit_form_generalinfo'), + $self->htmlForm({ frm => $frm, action => $pid ? "/p$pid/edit" : '/p/new', editsum => 1 }, + 'pedit_geninfo' => [ mt('_pedit_form_generalinfo'), [ select => name => mt('_pedit_form_type'), short => 'type', options => [ map [ $_, mt "_ptype_$_" ], sort @{$self->{producer_types}} ] ], [ input => name => mt('_pedit_form_name'), short => 'name' ], @@ -145,10 +213,70 @@ sub edit { options => [ map [ $_, "$_ (".mt("_lang_$_").')' ], sort @{$self->{languages}} ] ], [ input => name => mt('_pedit_form_website'), short => 'website' ], [ text => name => mt('_pedit_form_desc').'<br /><b class="standout">'.mt('_inenglish').'</b>', short => 'desc', rows => 6 ], + ], 'pedit_rel' => [ mt('_pedit_form_rel'), + [ hidden => short => 'prodrelations' ], + [ static => nolabel => 1, content => sub { + h2 mt '_pedit_rel_sel'; + table; + tbody id => 'relation_tbl'; + # to be filled using javascript + end; + end; + + h2 mt '_pedit_rel_add'; + table; + Tr id => 'relation_new'; + td class => 'tc_prod'; + input type => 'text', class => 'text'; + end; + td class => 'tc_rel'; + Select; + option value => $_, mt "_prodrel_$_" + for (sort { $self->{prod_relations}{$a}[0] <=> $self->{prod_relations}{$b}[0] } keys %{$self->{prod_relations}}); + end; + end; + td class => 'tc_add'; + a href => '#', mt '_pedit_rel_addbut'; + end; + end; + end; + }], ]); $self->htmlFooter; } +# !IMPORTANT!: Don't forget to update this function when +# adding/removing fields to/from producer entries! +sub _updreverse { + my($self, $old, $new, $pid, $cid, $rev) = @_; + my %upd; + + # compare %old and %new + for (keys %$old, keys %$new) { + if(exists $$old{$_} and !exists $$new{$_}) { + $upd{$_} = undef; + } elsif((!exists $$old{$_} and exists $$new{$_}) || ($$old{$_} ne $$new{$_})) { + $upd{$_} = $self->{prod_relations}{$$new{$_}}[1]; + } + } + + return if !keys %upd; + + # edit all related producers + for my $i (keys %upd) { + my $r = $self->dbProducerGet(id => $i, what => 'extended relations')->[0]; + my @newrel = map $_->{id} != $pid ? [ $_->{relation}, $_->{id} ] : (), @{$r->{relations}}; + push @newrel, [ $upd{$i}, $pid ] if $upd{$i}; + $self->dbProducerEdit($i, + relations => \@newrel, + editsum => "Reverse relation update caused by revision p$pid.$rev", + causedby => $cid, + uid => 1, # Multi - hardcoded + ( map { $_ => $r->{$_} } qw|type name original lang website desc alias| ) + ); + } +} + sub list { my($self, $char) = @_; diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm index d916e056..7365f235 100644 --- a/lib/VNDB/Handler/Releases.pm +++ b/lib/VNDB/Handler/Releases.pm @@ -54,17 +54,16 @@ sub page { [ notes => diff => 1 ], [ platforms => join => ', ', split => sub { map mt("_plat_$_"), @{$_[0]} } ], [ media => join => ', ', split => sub { - map { - my $med = $self->{media}{$_->{medium}}; - $med->[1] ? sprintf('%d %s%s', $_->{qty}, $med->[0], $_->{qty}>1?'s':'') : $med->[0] - } @{$_[0]}; + map $self->{media}{$_->{medium}} ? $_->{qty}.' '.mt("_med_$_->{medium}", $_->{qty}) : mt("_med_$_->{medium}",1), @{$_[0]} } ], [ resolution => serialize => sub { $self->{resolutions}[$_[0]][0] } ], [ voiced => serialize => sub { mt '_voiced_'.$_[0] } ], [ ani_story => serialize => sub { mt '_animated_'.$_[0] } ], [ ani_ero => serialize => sub { mt '_animated_'.$_[0] } ], [ producers => join => '<br />', split => sub { - map sprintf('<a href="/p%d" title="%s">%s</a>', $_->{id}, $_->{original}||$_->{name}, shorten $_->{name}, 50), @{$_[0]}; + map sprintf('<a href="/p%d" title="%s">%s</a> (%s)', $_->{id}, $_->{original}||$_->{name}, shorten($_->{name}, 50), + join(', ', $_->{developer} ? mt '_reldiff_developer' :(), $_->{publisher} ? mt '_reldiff_publisher' :()) + ), @{$_[0]}; } ], ); } @@ -154,11 +153,9 @@ sub _infotable { if(@{$r->{media}}) { Tr ++$i % 2 ? (class => 'odd') : (); td mt '_relinfo_media', scalar @{$r->{media}}; - # TODO: TL the media - td join ', ', map { - my $med = $self->{media}{$_->{medium}}; - $med->[1] ? sprintf('%d %s%s', $_->{qty}, $med->[0], $_->{qty}>1?'s':'') : $med->[0] - } @{$r->{media}}; + td join ', ', map + $self->{media}{$_->{medium}} ? $_->{qty}.' '.mt("_med_$_->{medium}", $_->{qty}) : mt("_med_$_->{medium}",1), + @{$r->{media}}; end; } @@ -199,16 +196,19 @@ sub _infotable { end; } - if(@{$r->{producers}}) { - Tr ++$i % 2 ? (class => 'odd') : (); - td mt '_relinfo_producer', scalar @{$r->{producers}}; - td; - for (@{$r->{producers}}) { - a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 60; - br if $_ != $r->{producers}[$#{$r->{producers}}]; - } - end; - end; + for my $t (qw|developer publisher|) { + my @prod = grep $_->{$t}, @{$r->{producers}}; + if(@prod) { + Tr ++$i % 2 ? (class => 'odd') : (); + td mt "_relinfo_$t", scalar @prod; + td; + for (@prod) { + a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 60; + br if $_ != $prod[$#prod]; + } + end; + end; + } } if($r->{gtin}) { @@ -241,14 +241,14 @@ sub _infotable { td; Select id => 'listsel', name => 'listsel'; option mt !$rl ? '_relinfo_user_notlist' : - ('_relinfo_user_inlist', $self->{vn_rstat}[$rl->{rstat}], $self->{vn_vstat}[$rl->{vstat}]); + ('_relinfo_user_inlist', mt('_rlst_rstat_'.$rl->{rstat}), mt('_rlst_vstat_'.$rl->{vstat})); optgroup label => mt '_relinfo_user_setr'; - option value => "r$_", $self->{vn_rstat}[$_] - for (0..$#{$self->{vn_rstat}}); + option value => "r$_", mt '_rlst_rstat_'.$_ + for (@{$self->{rlst_rstat}}); end; optgroup label => mt '_relinfo_user_setv'; - option value => "v$_", $self->{vn_vstat}[$_] - for (0..$#{$self->{vn_vstat}}); + option value => "v$_", mt '_rlst_vstat_'.$_ + for (@{$self->{rlst_vstat}}); end; option value => 'del', mt '_relinfo_user_del' if $rl; end; @@ -289,7 +289,10 @@ sub edit { (map { $_ => $r->{$_} } qw|type title original gtin catalog languages website released notes minage platforms patch resolution voiced freeware doujin ani_story ani_ero|), media => join(',', sort map "$_->{medium} $_->{qty}", @{$r->{media}}), - producers => join('|||', map "$_->{id},$_->{name}", sort { $a->{id} <=> $b->{id} } @{$r->{producers}}), + producers => join('|||', map + sprintf('%d,%d,%s', $_->{id}, ($_->{developer}?1:0)+($_->{publisher}?2:0), $_->{name}), + sort { $a->{id} <=> $b->{id} } @{$r->{producers}} + ), ); $b4{vn} = join('|||', map "$_->{vid},$_->{title}", @$vn); my $frm; @@ -325,7 +328,7 @@ sub edit { if(!$frm->{_err}) { # de-serialize $media = [ map [ split / / ], split /,/, $frm->{media} ]; - $producers = [ map { /^([0-9]+)/ ? $1 : () } split /\|\|\|/, $frm->{producers} ]; + $producers = [ map { /^([0-9]+),([1-3])/ ? [ $1, $2&1?1:0, $2&2?1:0] : () } split /\|\|\|/, $frm->{producers} ]; $new_vn = [ map { /^([0-9]+)/ ? $1 : () } split /\|\|\|/, $frm->{vn} ]; $frm->{platforms} = [ grep $_, @{$frm->{platforms}} ]; $frm->{$_} = $frm->{$_} ? 1 : 0 for (qw|patch freeware doujin|); @@ -335,7 +338,7 @@ sub edit { my $same = $rid && (join(',', sort @{$b4{platforms}}) eq join(',', sort @{$frm->{platforms}})) && - (join(',', sort @$producers) eq join(',', sort map $_->{id}, @{$r->{producers}})) && + (join(',', map join(' ', @$_), sort { $a->[0] <=> $b->[0] } @$producers) eq join(',', sort map sprintf('%d %d %d',$_->{id}, $_->{developer}?1:0, $_->{publisher}?1:0), sort { $a->{id} <=> $b->{id} } @{$r->{producers}})) && (join(',', sort @$new_vn) eq join(',', sort map $_->{vid}, @$vn)) && (join(',', sort @{$b4{languages}}) eq join(',', sort @{$frm->{languages}})) && !grep !/^(platforms|producers|vn|languages)$/ && $frm->{$_} ne $b4{$_}, keys %b4; @@ -370,7 +373,7 @@ sub edit { $frm->{original} = $v->{original} if !defined $frm->{original} && !$r; my $title = mt $rid ? ($copy ? '_redit_title_copy' : '_redit_title_edit', $r->{title}) : ('_redit_title_add', $v->{title}); - $self->htmlHeader(js => 'forms', title => $title, noindex => 1); + $self->htmlHeader(title => $title, noindex => 1); $self->htmlMainTabs('r', $r, $copy ? 'copy' : 'edit') if $rid; $self->htmlMainTabs('v', $v, 'edit') if $vid; $self->htmlEditMessage('r', $r, $title, $copy); @@ -435,7 +438,7 @@ sub _form { h2 mt '_redit_form_media'; div id => 'media_div'; Select; - option value => $_, class => $self->{media}{$_}[1] ? 'qty' : 'noqty', $self->{media}{$_}[0] + option value => $_, class => $self->{media}{$_} ? 'qty' : 'noqty', mt "_med_$_", 1 for (sort keys %{$self->{media}}); end; end; @@ -446,13 +449,17 @@ sub _form { [ hidden => short => 'producers' ], [ static => nolabel => 1, content => sub { h2 mt('_redit_form_prod_sel'); - div id => 'producerssel'; - end; + table; tbody id => 'producer_tbl'; end; end; h2 mt('_redit_form_prod_add'); - div; - input type => 'text', class => 'text'; - a href => '#', 'add'; - end; + table; Tr; + td class => 'tc_name'; input id => 'producer_input', type => 'text', class => 'text'; end; + td class => 'tc_role'; Select id => 'producer_role'; + option value => 1, mt '_redit_form_prod_dev'; + option value => 2, selected => 'selected', mt '_redit_form_prod_pub'; + option value => 3, mt '_redit_form_prod_both'; + end; end; + td class => 'tc_add'; a id => 'producer_add', href => '#', mt '_redit_form_prod_addbut'; end; + end; end; }], ], @@ -460,12 +467,11 @@ sub _form { [ hidden => short => 'vn' ], [ static => nolabel => 1, content => sub { h2 mt('_redit_form_vn_sel'); - div id => 'vnsel'; - end; + table; tbody id => 'vn_tbl'; end; end; h2 mt('_redit_form_vn_add'); div; - input type => 'text', class => 'text'; - a href => '#', 'add'; + input id => 'vn_input', type => 'text', class => 'text'; + a href => '#', id => 'vn_add', mt '_redit_form_vn_addbut'; end; }], ], @@ -484,7 +490,7 @@ sub browse { { name => 'ln', required => 0, multi => 1, default => '', enum => $self->{languages} }, { name => 'pl', required => 0, multi => 1, default => '', enum => $self->{platforms} }, { name => 'me', required => 0, multi => 1, default => '', enum => [ keys %{$self->{media}} ] }, - { name => 'tp', required => 0, default => -1, enum => [ -1, @{$self->{release_types}} ] }, + { name => 'tp', required => 0, default => '', enum => [ '', @{$self->{release_types}} ] }, { name => 'pa', required => 0, default => 0, enum => [ 0..2 ] }, { name => 'fw', required => 0, default => 0, enum => [ 0..2 ] }, { name => 'do', required => 0, default => 0, enum => [ 0..2 ] }, @@ -503,7 +509,7 @@ sub browse { $f->{ln}[0] ? (languages => $f->{ln}) : (), $f->{me}[0] ? (media => $f->{me}) : (), $f->{re}[0] ? (resolutions => $f->{re} ) : (), - $f->{tp} >= 0 ? (type => $f->{tp}) : (), + $f->{tp} ? (type => $f->{tp}) : (), $f->{ma_a} || $f->{ma_m} ? (minage => [$f->{ma_m}, $f->{ma_a}]) : (), $f->{pa} ? (patch => $f->{pa}) : (), $f->{fw} ? (freeware => $f->{fw}) : (), @@ -614,7 +620,7 @@ sub _filters { end; end; $self->htmlFormPart($f, [ select => short => 'tp', name => mt('_rbrowse_type'), - options => [ [-1, mt '_rbrowse_all'], map [ $_, mt "_rtype_$_" ], @{$self->{release_types}} ]]); + options => [ ['', mt '_rbrowse_all'], map [ $_, mt "_rtype_$_" ], @{$self->{release_types}} ]]); $self->htmlFormPart($f, [ select => short => 'pa', name => mt('_rbrowse_patch'), options => [ [0, mt '_rbrowse_all' ], [1, mt '_rbrowse_patchonly'], [2, mt '_rbrowse_patchnone']]]); $self->htmlFormPart($f, [ select => short => 'fw', name => mt('_rbrowse_freeware'), @@ -629,7 +635,7 @@ sub _filters { txt mt '_rbrowse_languages'; b ' ('.mt('_rbrowse_boolor').')'; end; - for my $i (sort @{$self->dbLanguages}) { + for my $i (@{$self->{languages}}) { span; input type => 'checkbox', name => 'ln', value => $i, id => "lang_$i", grep($_ eq $i, @{$f->{ln}}) ? (checked => 'checked') : (); label for => "lang_$i"; @@ -660,7 +666,7 @@ sub _filters { for my $i (sort keys %{$self->{media}}) { span; input type => 'checkbox', name => 'me', value => $i, id => "med_$i", grep($_ eq $i, @{$f->{me}}) ? (checked => 'checked') : (); - label for => "med_$i", $self->{media}{$i}[0]; + label for => "med_$i", mt "_med_$i", 1; end; } diff --git a/lib/VNDB/Handler/Tags.pm b/lib/VNDB/Handler/Tags.pm index ae240d38..4c969436 100644 --- a/lib/VNDB/Handler/Tags.pm +++ b/lib/VNDB/Handler/Tags.pm @@ -36,7 +36,7 @@ sub tagpage { ); return 404 if $f->{_err}; my $tagspoil = $self->reqCookie('tagspoil'); - $f->{m} = $tagspoil =~ /^[0-2]$/ ? $tagspoil : 1 if $f->{m} == -1; + $f->{m} = $tagspoil =~ /^[0-2]$/ ? $tagspoil : 0 if $f->{m} == -1; my($list, $np) = $t->{meta} || $t->{state} != 2 ? ([],0) : $self->dbTagVNs( tag => $tag, @@ -422,7 +422,7 @@ sub vntagmod { my $frm; my $title = mt '_tagv_title', $v->{title}; - $self->htmlHeader(title => $title, noindex => 1, js => 'forms'); + $self->htmlHeader(title => $title, noindex => 1); $self->htmlMainTabs('v', $v, 'tagmod'); div class => 'mainbox'; h1 $title; @@ -438,43 +438,44 @@ sub vntagmod { $self->htmlForm({ frm => $frm, action => "/v$vid/tagmod", nosubmit => 1 }, tagmod => [ mt('_tagv_frm_title'), [ hidden => short => 'taglinks', value => '' ], [ static => nolabel => 1, content => sub { - table id => 'tagtable'; + table class => 'tgl'; thead; Tr; td ''; - td colspan => 2, class => 'tc2_1', mt '_tagv_col_you'; - td colspan => 2, class => 'tc3_1', mt '_tagv_col_others'; + td colspan => 2, class => 'tc_you', mt '_tagv_col_you'; + td colspan => 2, class => 'tc_others', mt '_tagv_col_others'; end; Tr; - my $i=0; - td class => 'tc'.++$i, mt '_tagv_col_'.$_ for(qw|tag rating spoiler rating spoiler|); + td class => 'tc_tagname', mt '_tagv_col_tag'; + td class => 'tc_myvote', mt '_tagv_col_rating'; + td class => 'tc_myspoil', mt '_tagv_col_spoiler'; + td class => 'tc_allvote', mt '_tagv_col_rating'; + td class => 'tc_allspoil', mt '_tagv_col_spoiler'; end; end; tfoot; Tr; td colspan => 5; input type => 'submit', class => 'submit', value => mt('_tagv_save'), style => 'float: right'; - input type => 'text', class => 'text', name => 'addtag', value => ''; - input type => 'button', class => 'submit', value => mt '_tagv_add'; + input id => 'tagmod_tag', type => 'text', class => 'text', value => ''; + input id => 'tagmod_add', type => 'button', class => 'submit', value => mt '_tagv_add'; br; p; lit mt '_tagv_addmsg'; end; end; end; end; - tbody; + tbody id => 'tagtable'; for my $t (sort { $a->{name} cmp $b->{name} } @$tags) { my $m = (grep $_->{tag} == $t->{id}, @$my)[0] || {}; - Tr; - td class => 'tc1'; - a href => "/g$t->{id}", $t->{name}; - end; - td class => 'tc2', $m->{vote}||0; - td class => 'tc3', defined $m->{spoiler} ? $m->{spoiler} : -1; - td class => 'tc4'; + Tr id => "tgl_$t->{id}"; + td class => 'tc_tagname'; a href => "/g$t->{id}", $t->{name}; end; + td class => 'tc_myvote', $m->{vote}||0; + td class => 'tc_myspoil', defined $m->{spoiler} ? $m->{spoiler} : -1; + td class => 'tc_allvote'; tagscore !$m->{vote} ? $t->{rating} : $t->{cnt} == 1 ? 0 : ($t->{rating}*$t->{cnt} - $m->{vote}) / ($t->{cnt}-1); i ' ('.($t->{cnt} - ($m->{vote} ? 1 : 0)).')'; end; - td class => 'tc5', sprintf '%.2f', $t->{spoiler}; + td class => 'tc_allspoil', sprintf '%.2f', $t->{spoiler}; end; } end; @@ -529,7 +530,7 @@ sub usertags { header => [ sub { td class => 'tc1'; - b id => 'relhidall'; + b id => 'expandall'; lit '<i>▸</i> '.mt('_tagu_col_num').' '; end; lit $f->{s} eq 'cnt' && $f->{o} eq 'a' ? "\x{25B4}" : qq|<a href="/u$u->{id}/tags?o=a;s=cnt">\x{25B4}</a>|; @@ -542,7 +543,7 @@ sub usertags { row => sub { my($s, $n, $l) = @_; Tr $n % 2 ? (class => 'odd') : (); - td class => 'tc1 relhid_but', id => "tag$l->{id}"; + td class => 'tc1 collapse_but', id => "tag$l->{id}"; lit "<i>▸</i> $l->{cnt}"; end; td class => 'tc2', colspan => 2; @@ -550,7 +551,7 @@ sub usertags { end; end; for(@{$l->{vns}}) { - Tr class => "relhid tag$l->{id}"; + Tr class => "collapse collapse_tag$l->{id}"; td class => 'tc1_1'; tagscore $_->{vote}; end; @@ -652,7 +653,7 @@ sub tagxml { my($list, $np) = $self->dbTagGet( $q =~ /^g([1-9]\d*)/ ? (id => $1) : $q =~ /^name:(.+)$/ ? (name => $1) : (search => $q), - results => 10, + results => 15, page => 1, ); diff --git a/lib/VNDB/Handler/ULists.pm b/lib/VNDB/Handler/ULists.pm index 7d1a9304..6a0d6c9e 100644 --- a/lib/VNDB/Handler/ULists.pm +++ b/lib/VNDB/Handler/ULists.pm @@ -69,7 +69,7 @@ sub rlist { return $self->htmlDenied() if !$uid; my $f = $self->formValidate( - { name => 'e', required => 1, enum => [ 'del', map("r$_", 0..$#{$self->{vn_rstat}}), map("v$_", 0..$#{$self->{vn_vstat}}) ] }, + { name => 'e', required => 1, enum => [ 'del', map("r$_", @{$self->{rlst_rstat}}), map("v$_", @{$self->{rlst_vstat}}) ] }, ); return 404 if $f->{_err}; @@ -212,7 +212,7 @@ sub vnlist { if($own && $self->reqMethod eq 'POST') { my $frm = $self->formValidate( { name => 'sel', required => 0, default => 0, multi => 1, template => 'int' }, - { name => 'batchedit', required => 1, enum => [ 'del', map("r$_", 0..$#{$self->{vn_rstat}}), map("v$_", 0..$#{$self->{vn_vstat}}) ] }, + { name => 'batchedit', required => 1, enum => [ 'del', map("r$_", @{$self->{rlst_rstat}}), map("v$_", @{$self->{rlst_vstat}}) ] }, ); if(!$frm->{_err} && @{$frm->{sel}} && $frm->{sel}[0]) { $self->dbVNListDel($uid, $frm->{sel}) if $frm->{batchedit} eq 'del'; @@ -229,7 +229,7 @@ sub vnlist { uid => $uid, results => 50, page => $f->{p}, - order => $f->{s}.' '.($f->{o} eq 'd' ? 'DESC' : 'ASC'), + order => $f->{s}.' '.($f->{o} eq 'd' ? 'DESC' : 'ASC').($f->{s} eq 'vote' ? ', title ASC' : ''), voted => $f->{v}, $f->{c} ne 'all' ? (char => $f->{c}) : (), ); @@ -285,7 +285,7 @@ sub _vnlist_browse { pageurl => $url->('page'), header => [ [ mt('_rlist_col_title') => 'title', 3 ], - sub { td class => 'tc2', id => 'relhidall'; lit '<i>▸</i>'.mt('_rlist_col_releases').'*'; end; }, + sub { td class => 'tc2', id => 'expandall'; lit '<i>▸</i>'.mt('_rlist_col_releases').'*'; end; }, [ mt('_rlist_col_vote') => 'vote' ], ], row => sub { @@ -294,7 +294,7 @@ sub _vnlist_browse { td class => 'tc1', colspan => 3; a href => "/v$i->{vid}", title => $i->{original}||$i->{title}, shorten $i->{title}, 70; end; - td class => 'tc2'.(@{$i->{rels}} ? ' relhid_but' : ''), id => 'vid'.$i->{vid}; + td class => 'tc2'.(@{$i->{rels}} ? ' collapse_but' : ''), id => 'vid'.$i->{vid}; lit '<i>▸</i>'; my $obtained = grep $_->{rstat}==2, @{$i->{rels}}; my $finished = grep $_->{vstat}==2, @{$i->{rels}}; @@ -307,7 +307,7 @@ sub _vnlist_browse { end; for (@{$i->{rels}}) { - Tr class => "relhid vid$i->{vid}"; + Tr class => "collapse relhid collapse_vid$i->{vid}"; td class => 'tc1'.($own ? ' own' : ''); input type => 'checkbox', name => 'sel', value => $_->{rid} if $own; @@ -333,12 +333,12 @@ sub _vnlist_browse { Select id => 'batchedit', name => 'batchedit'; option mt '_rlist_selection'; optgroup label => mt '_rlist_changerel'; - option value => "r$_", $self->{vn_rstat}[$_] - for (0..$#{$self->{vn_rstat}}); + option value => "r$_", mt "_rlst_rstat_$_" + for (@{$self->{rlst_rstat}}); end; optgroup label => mt '_rlist_changeplay'; - option value => "v$_", $self->{vn_vstat}[$_] - for (0..$#{$self->{vn_vstat}}); + option value => "v$_", mt "_rlst_vstat_$_" + for (@{$self->{rlst_vstat}}); end; option value => 'del', mt '_rlist_del'; end; diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm index d6193ddc..39aecbfc 100644 --- a/lib/VNDB/Handler/Users.pm +++ b/lib/VNDB/Handler/Users.pm @@ -267,7 +267,7 @@ sub edit { return $self->htmlDenied if !$self->authInfo->{id} || $self->authInfo->{id} != $uid && !$self->authCan('usermod'); # fetch user info (cached if uid == loggedin uid) - my $u = $self->authInfo->{id} == $uid ? $self->authInfo : $self->dbUserGet(uid => $uid)->[0]; + my $u = $self->authInfo->{id} == $uid ? $self->authInfo : $self->dbUserGet(uid => $uid, what => 'extended')->[0]; return 404 if !$u->{id}; # check POST data @@ -386,7 +386,7 @@ sub posts { [ '' ], [ '' ], [ mt '_uposts_col_date' ], - sub { td; a href => '#', id => 'history_comments', 'expand'; txt mt '_uposts_col_title'; end; } + sub { td; a href => '#', id => 'expandlist', mt '_js_expand'; txt mt '_uposts_col_title'; end; } ], row => sub { my($s, $n, $l) = @_; @@ -396,7 +396,7 @@ sub posts { td class => 'tc3', $self->{l10n}->date($l->{date}); td class => 'tc4'; a href => "/t$l->{tid}.$l->{num}", $l->{title}; end; end; - Tr class => $n % 2 ? 'editsum odd hidden' : 'editsum hidden'; + Tr class => $n % 2 ? 'collapse msgsum odd hidden' : 'collapse msgsum hidden'; td colspan => 4; lit bb2html $l->{msg}, 150; end; diff --git a/lib/VNDB/Handler/VNBrowse.pm b/lib/VNDB/Handler/VNBrowse.pm index 2a6d6cd7..bca21151 100644 --- a/lib/VNDB/Handler/VNBrowse.pm +++ b/lib/VNDB/Handler/VNBrowse.pm @@ -25,7 +25,7 @@ sub list { { name => 'pl', required => 0, multi => 1, enum => $self->{platforms}, default => '' }, { name => 'ti', required => 0, default => '', maxlength => 200 }, { name => 'te', required => 0, default => '', maxlength => 200 }, - { name => 'sp', required => 0, default => $self->reqCookie('tagspoil') =~ /^([0-2])$/ ? $1 : 1, enum => [0..2] }, + { name => 'sp', required => 0, default => $self->reqCookie('tagspoil') =~ /^([0-2])$/ ? $1 : 0, enum => [0..2] }, ); return 404 if $f->{_err}; $f->{q} ||= $f->{sq}; @@ -70,7 +70,7 @@ sub list { $self->resRedirect('/v'.$list->[0]{id}, 'temp') if $f->{q} && @$list == 1; - $self->htmlHeader(title => mt('_vnbrowse_title'), search => $f->{q}, js => 'forms'); + $self->htmlHeader(title => mt('_vnbrowse_title'), search => $f->{q}); _filters($self, $f, $char, \@ignored); my $url = "/v/$char?q=$f->{q};ti=$f->{ti};te=$f->{te}"; @@ -162,7 +162,7 @@ sub _filters { txt mt '_vnbrowse_lang'; b ' ('.mt('_vnbrowse_boolor').')'; end; - for my $i (sort @{$self->dbLanguages}) { + for my $i (@{$self->{languages}}) { span; input type => 'checkbox', name => 'ln', value => $i, id => "lang_$i", (scalar grep $_ eq $i, @{$f->{ln}}) ? (checked => 'checked') : (); diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm index f09d6ddd..ed7068dc 100644 --- a/lib/VNDB/Handler/VNEdit.pm +++ b/lib/VNDB/Handler/VNEdit.pm @@ -28,7 +28,7 @@ sub edit { my %b4 = !$vid ? () : ( (map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai l_vnn img_nsfw|), anime => join(' ', sort { $a <=> $b } map $_->{id}, @{$v->{anime}}), - relations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}), + vnrelations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}), screenshots => join(' ', map sprintf('%d,%d,%d', $_->{id}, $_->{nsfw}?1:0, $_->{rid}), @{$v->{screenshots}}), ); @@ -46,7 +46,7 @@ sub edit { { name => 'l_vnn', required => 0, default => $b4{l_vnn}||0, template => 'int' }, { name => 'anime', required => 0, default => '' }, { name => 'img_nsfw', required => 0, default => 0 }, - { name => 'relations', required => 0, default => '', maxlength => 5000 }, + { name => 'vnrelations', required => 0, default => '', maxlength => 5000 }, { name => 'screenshots', required => 0, default => '', maxlength => 1000 }, { name => 'editsum', maxlength => 5000 }, ); @@ -57,11 +57,11 @@ sub edit { if(!$frm->{_err}) { # parse and re-sort fields that have multiple representations of the same information my $anime = { map +($_=>1), grep /^[0-9]+$/, split /[ ,]+/, $frm->{anime} }; - my $relations = [ map { /^([0-9]+),([0-9]+),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{relations} ]; + my $relations = [ map { /^([a-z]+),([0-9]+),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{vnrelations} ]; my $screenshots = [ map /^[0-9]+,[01],[0-9]+$/ ? [split /,/] : (), split / +/, $frm->{screenshots} ]; $frm->{anime} = join ' ', sort { $a <=> $b } keys %$anime; - $frm->{relations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations}; + $frm->{vnrelations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations}; $frm->{img_nsfw} = $frm->{img_nsfw} ? 1 : 0; $frm->{screenshots} = join ' ', map sprintf('%d,%d,%d', $_->[0], $_->[1]?1:0, $_->[2]), sort { $a->[0] <=> $b->[0] } @$screenshots; @@ -83,7 +83,7 @@ sub edit { ($nvid, $cid) = $self->dbVNAdd(%args) if !$vid; # update reverse relations & relation graph - if(!$vid && $#$relations >= 0 || $vid && $frm->{relations} ne $b4{relations}) { + if(!$vid && $#$relations >= 0 || $vid && $frm->{vnrelations} ne $b4{vnrelations}) { my %old = $vid ? (map { $_->{id} => $_->{relation} } @{$v->{relations}}) : (); my %new = map { $_->[1] => $_->[0] } @$relations; _updreverse($self, \%old, \%new, $nvid, $cid, $nrev); @@ -97,7 +97,7 @@ sub edit { $frm->{editsum} = sprintf 'Reverted to revision v%d.%d', $vid, $rev if $rev && !defined $frm->{editsum}; my $title = $vid ? mt('_vnedit_title_edit', $v->{title}) : mt '_vnedit_title_add'; - $self->htmlHeader(js => 'forms', title => $title, noindex => 1); + $self->htmlHeader(title => $title, noindex => 1); $self->htmlMainTabs('v', $v, 'edit') if $vid; $self->htmlEditMessage('v', $v, $title); _form($self, $v, $frm); @@ -183,7 +183,7 @@ sub _form { ], vn_rel => [ mt('_vnedit_rel'), - [ hidden => short => 'relations' ], + [ hidden => short => 'vnrelations' ], [ static => nolabel => 1, content => sub { h2 mt '_vnedit_rel_sel'; table; @@ -193,22 +193,22 @@ sub _form { end; h2 mt '_vnedit_rel_add'; - # TODO: localize JS relartion selector table; Tr id => 'relation_new'; - td class => 'tc1'; + td class => 'tc_vn'; input type => 'text', class => 'text'; end; - td class => 'tc2'; - txt ' is a '; + td class => 'tc_rel'; + txt mt('_vnedit_rel_isa').' '; Select; - option value => $_, $self->{vn_relations}[$_][0] for (0..$#{$self->{vn_relations}}); + option value => $_, mt "_vnrel_$_" + for (sort { $self->{vn_relations}{$a}[0] <=> $self->{vn_relations}{$b}[0] } keys %{$self->{vn_relations}}); end; - txt ' of'; + txt ' '.mt '_vnedit_rel_of'; end; - td class => 'tc3', $v ? $v->{title} : ''; - td class => 'tc4'; - a href => '#', 'add'; + td class => 'tc_title', $v ? $v->{title} : ''; + td class => 'tc_add'; + a href => '#', mt '_vnedit_rel_addbut'; end; end; end; @@ -219,10 +219,9 @@ sub _form { [ hidden => short => 'screenshots' ], [ static => nolabel => 1, content => sub { div class => 'warning'; - lit mt '_vnedit_scr_msg'; + lit mt '_vnedit_scrmsg'; end; br; - # TODO: localize screenshot uploader table; tbody id => 'scr_table', ''; end; @@ -250,11 +249,9 @@ sub _updreverse { # compare %old and %new for (keys %$old, keys %$new) { if(exists $$old{$_} and !exists $$new{$_}) { - $upd{$_} = -1; - } elsif((!exists $$old{$_} and exists $$new{$_}) || ($$old{$_} != $$new{$_})) { - $upd{$_} = $$new{$_}; - if ($self->{vn_relations}[$upd{$_} ][1]) { $upd{$_}-- } - elsif($self->{vn_relations}[$upd{$_}+1][1]) { $upd{$_}++ } + $upd{$_} = undef; + } elsif((!exists $$old{$_} and exists $$new{$_}) || ($$old{$_} ne $$new{$_})) { + $upd{$_} = $self->{vn_relations}{$$new{$_}}[1]; } } @@ -264,7 +261,7 @@ sub _updreverse { for my $i (keys %upd) { my $r = $self->dbVNGet(id => $i, what => 'extended relations anime screenshots')->[0]; my @newrel = map $_->{id} != $vid ? [ $_->{relation}, $_->{id} ] : (), @{$r->{relations}}; - push @newrel, [ $upd{$i}, $vid ] if $upd{$i} != -1; + push @newrel, [ $upd{$i}, $vid ] if $upd{$i}; $self->dbVNEdit($i, relations => \@newrel, editsum => "Reverse relation update caused by revision v$vid.$rev", diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm index 361963a8..ec5f4afc 100644 --- a/lib/VNDB/Handler/VNPage.pm +++ b/lib/VNDB/Handler/VNPage.pm @@ -27,16 +27,17 @@ sub rg { return 404 if !$v->{id} || !$v->{rgraph}; my $title = mt '_vnrg_title', $v->{title}; - $self->htmlHeader(title => $title); - $self->htmlMainTabs('v', $v, 'rg'); + return if $self->htmlRGHeader($title, 'v', $v); + + $v->{svg} =~ s/\$___(_vnrel_[a-z]+)____\$/mt $1/eg; + div class => 'mainbox'; h1 $title; - lit $v->{cmap}; p class => 'center'; - img src => sprintf('%s/rg/%02d/%d.png', $self->{url_static}, $v->{rgraph}%100, $v->{rgraph}), - alt => $title, usemap => '#rgraph'; + lit $v->{svg}; end; end; + $self->htmlFooter; } @@ -148,15 +149,16 @@ sub page { my $t = $self->dbTagStats(vid => $v->{id}, order => 'avg(tv.vote) DESC', minrating => 0, results => 999); if(@$t) { div id => 'tagops'; - a href => '#', mt '_vnpage_tags_spoil0'; - a href => '#', class => 'tsel', mt '_vnpage_tags_spoil1'; + # NOTE: order of these links is hardcoded in JS + a href => '#', class => 'tsel', mt '_vnpage_tags_spoil0'; + a href => '#', mt '_vnpage_tags_spoil1'; a href => '#', mt '_vnpage_tags_spoil2'; a href => '#', class => 'sec', mt '_vnpage_tags_summary'; a href => '#', mt '_vnpage_tags_all'; end; div id => 'vntags'; for (@$t) { - span class => sprintf 'tagspl%.0f %s', $_->{spoiler}, $_->{spoiler} > 1 ? 'hidden' : ''; + span class => sprintf 'tagspl%.0f %s', $_->{spoiler}, $_->{spoiler} > 0 ? 'hidden' : ''; a href => "/g$_->{id}", style => sprintf('font-size: %dpx', $_->{rating}*3.5+6), $_->{name}; b class => 'grayedout', sprintf ' %.1f', $_->{rating}; end; @@ -199,7 +201,7 @@ sub _revision { }], [ relations => join => '<br />', split => sub { my @r = map sprintf('%s: <a href="/v%d" title="%s">%s</a>', - $self->{vn_relations}[$_->{relation}][0], $_->{id}, xml_escape($_->{original}||$_->{title}), xml_escape shorten $_->{title}, 40 + mt("_vnrel_$_->{relation}"), $_->{id}, xml_escape($_->{original}||$_->{title}), xml_escape shorten $_->{title}, 40 ), sort { $a->{id} <=> $b->{id} } @{$_[0]}; return @r ? @r : (mt '_vndiff_none'); }], @@ -229,27 +231,42 @@ sub _revision { sub _producers { my($self, $i, $r) = @_; - return if !grep @{$_->{producers}}, @$r; my %lang; my @lang = grep !$lang{$_}++, map @{$_->{languages}}, @$r; - Tr ++$$i % 2 ? (class => 'odd') : (); - td mt '_vnpage_producers'; - td; - for my $l (@lang) { - my %p = map { $_->{id} => $_ } map @{$_->{producers}}, grep grep($_ eq $l, @{$_->{languages}}), @$r; - my @p = values %p; - next if !@p; - cssicon "lang $l", mt "_lang_$l"; - for (@p) { + if(grep $_->{developer}, map @{$_->{producers}}, @$r) { + my %dev = map $_->{developer} ? ($_->{id} => $_) : (), map @{$_->{producers}}, @$r; + my @dev = values %dev; + Tr ++$$i % 2 ? (class => 'odd') : (); + td mt "_vnpage_developer"; + td; + for (@dev) { a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 30; - txt ' & ' if $_ != $p[$#p]; + txt ' & ' if $_ != $dev[$#dev]; } - txt "\n"; - } - end; - end; + end; + end; + } + + if(grep $_->{publisher}, map @{$_->{producers}}, @$r) { + Tr ++$$i % 2 ? (class => 'odd') : (); + td mt "_vnpage_publisher"; + td; + for my $l (@lang) { + my %p = map $_->{publisher} ? ($_->{id} => $_) : (), map @{$_->{producers}}, grep grep($_ eq $l, @{$_->{languages}}), @$r; + my @p = values %p; + next if !@p; + cssicon "lang $l", mt "_lang_$l"; + for (@p) { + a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 30; + txt ' & ' if $_ != $p[$#p]; + } + txt "\n"; + } + end; + end; + } } @@ -266,7 +283,7 @@ sub _relations { td class => 'relations'; dl; for(sort keys %rel) { - dt $self->{vn_relations}[$_][0]; + dt mt "_vnrel_$_"; dd; for (@{$rel{$_}}) { a href => "/v$_->{id}", title => $_->{original}||$_->{title}, shorten $_->{title}, 40; @@ -306,7 +323,7 @@ sub _anime { txt '] '; end; acronym title => $_->{title_kanji}||$_->{title_romaji}, shorten $_->{title_romaji}, 50; - b ' ('.(defined $_->{type} ? $self->{anime_types}[$_->{type}].', ' : '').$_->{year}.')'; + b ' ('.(defined $_->{type} ? mt("_animetype_$_->{type}").', ' : '').$_->{year}.')'; txt "\n"; } } @@ -395,7 +412,7 @@ sub _releases { end; td class => 'tc5'; if($self->authInfo->{id}) { - a href => "/r$rel->{id}", id => "rlsel_$rel->{id}"; + a href => "/r$rel->{id}", id => "rlsel_$rel->{id}", class => 'vnrlsel'; lit $rel->{ulist} ? liststat $rel->{ulist} : '--'; end; } else { diff --git a/lib/VNDB/L10N.pm b/lib/VNDB/L10N.pm index d4ff872c..8698cb74 100644 --- a/lib/VNDB/L10N.pm +++ b/lib/VNDB/L10N.pm @@ -5,12 +5,13 @@ use warnings; { package VNDB::L10N; use base 'Locale::Maketext'; + use LangFile; sub fallback_languages { ('en') }; # used for the language switch interface, language tags must # be the same as in the languages hash in global.pl - sub languages { ('en', 'ru') } + sub languages { qw{ cs en hu ru } } sub maketext { my $r = eval { shift->SUPER::maketext(@_) }; @@ -21,59 +22,29 @@ use warnings; # can be called as either a subroutine or a method sub loadfile { - my %lang = ( - en => \%VNDB::L10N::en::Lexicon, - ru => \%VNDB::L10N::ru::Lexicon, - ); - - open my $F, '<:utf8', $VNDB::ROOT.'/data/lang.txt' or die "Opening language file: $!\n"; - my($empty, $line, $key, $lang) = (0, 0); - while(<$F>) { - chomp; - $line++; - - # ignore intro - if(!defined $key) { - $key = 0 if /^\/intro$/; - next; + my %lang = do { + no strict 'refs'; + map +($_, \%{"VNDB::L10N::${_}::Lexicon"}), languages + }; + my $r = LangFile->new(read => "$VNDB::ROOT/data/lang.txt"); + my $key; + while(my $l = $r->read) { + my($t, @l) = @$l; + $key = $l[0] if $t eq 'key'; + if($t eq 'tl') { + my($lang, undef, $text) = @l; + next if !$text; + die "Unknown language \"$l->[1]\"\n" if !$lang{$lang}; + die "Unknown key for translation \"$lang: $text\"\n" if !$key; + $lang{$lang}{$key} = $text; } - # ignore comments - next if /^#/; - # key - if(/^:(.+)$/) { - $key = $1; - $lang = undef; - $empty = 0; - next; - } - # locale string - if(/^([a-z_-]{2,7})[ *]: (.+)$/) { - $lang = $1; - die "Unknown language on #$line: $lang\n" if !$lang{$lang}; - die "Unknown key for locale on #$line\n" if !$key; - $lang{$lang}{$key} = $2; - $empty = 0; - next; - } - # multi-line locale string - if($lang && /^\s+([^\s].*)$/) { - $lang{$lang}{$key} .= ''.("\n"x$empty)."\n$1"; - $empty = 0; - next; - } - # empty string (count them in case they're part of a multi-line locale string) - if(/^\s*$/) { - $empty++; - next; - } - # something we didn't expect - die "Don't know what to do with line $line\n" unless /^([a-z_-]{2,7})[ *]:/; } - close $F; + $r->close; } } + { package VNDB::L10N::en; use base 'VNDB::L10N'; @@ -88,8 +59,9 @@ use warnings; # Argument: unix timestamp # Returns: age sub age { - my $a = time-$_[1]; - return sprintf '%d %s ago', + my($self, $time) = @_; + my $a = time-$time; + my @f = $a > 60*60*24*365*2 ? ( $a/60/60/24/365, 'years' ) : $a > 60*60*24*(365/12)*2 ? ( $a/60/60/24/(365/12), 'months' ) : $a > 60*60*24*7*2 ? ( $a/60/60/24/7, 'weeks' ) : @@ -97,6 +69,7 @@ use warnings; $a > 60*60*2 ? ( $a/60/60, 'hours' ) : $a > 60*2 ? ( $a/60, 'min' ) : ( $a, 'sec' ); + return $self->maketext("_age_$f[1]", int $f[0]); } # argument: unix timestamp and optional format (compact/full) @@ -155,6 +128,30 @@ use warnings; { + package VNDB::L10N::cs; + use base 'VNDB::L10N::en'; + our %Lexicon; + + sub quant { + my($self, $num, $single, $couple, $lots) = @_; + return $lots if ($num % 100) >= 11 && ($num % 100) <= 14; + return $single if ($num % 10) == 1; + return $couple if ($num % 10) >= 2 && ($num % 10) <= 4; + return $lots; + } +} + + + +{ + package VNDB::L10N::hu; + use base 'VNDB::L10N::en'; + our %Lexicon; +} + + + +{ package VNDB::L10N::ru; use base 'VNDB::L10N::en'; our %Lexicon; @@ -165,25 +162,9 @@ use warnings; return $couple if ($num % 10) >= 2 && ($num % 10) <= 4 && !(($num % 100) >= 12 && ($num % 100) <= 14); return $lots; } - - sub age { - my $self = shift; - my $a = time-shift; - use utf8; - my @l = ( - $a > 60*60*24*365*2 ? ( $a/60/60/24/365, 'год', 'года', 'лет' ) : - $a > 60*60*24*(365/12)*2 ? ( $a/60/60/24/(365/12), 'месяц', 'месяца', 'месяцев' ) : - $a > 60*60*24*7*2 ? ( $a/60/60/24/7, 'неделя', 'недели', 'недель' ) : - $a > 60*60*24*2 ? ( $a/60/60/24, 'день', 'дня', 'дней' ) : - $a > 60*60*2 ? ( $a/60/60, 'час', 'часа', 'часов' ) : - $a > 60*2 ? ( $a/60, 'минута', 'минуты', 'минут' ) : - ( $a, 'секунда', 'секунды', 'секунд' ) - ); - return sprintf '%d %s назад', $l[0], $self->quant(@l); - } - } + 1; diff --git a/lib/VNDB/Plugin/TransAdmin.pm b/lib/VNDB/Plugin/TransAdmin.pm new file mode 100644 index 00000000..ed5ebb36 --- /dev/null +++ b/lib/VNDB/Plugin/TransAdmin.pm @@ -0,0 +1,349 @@ +# This plugin provides a quick and dirty user interface to editing lang.txt, +# to use it, add the following to your data/config.pl: +# +# if($INC{"YAWF.pm"}) { +# require VNDB::Plugin::TransAdmin; +# $VNDB::S{transadmin} = { +# <userid> => 'all' || <language> || <arrayref with languages> +# }; +# } +# +# And then open /tladmin in your browser. +# Also make sure data/lang.txt and data/docs/* are 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 ||= ''; + my $intro = $lang =~ s/intro//; + return 404 if $lang && ($lang eq 'en' || !grep $_ eq $lang, $self->{l10n}->languages); + my $sect = $self->reqParam('sect')||''; + my $doc = $self->reqParam('doc')||''; + + my $uid = $self->authInfo->{id}; + return $self->htmlDenied if !$uid || !$self->{transadmin}{$uid}; + + if(!-w $langfile || !-w "$VNDB::ROOT/data/docs" || grep /\.[a-z]{2}$/ && !-w $_, glob "$VNDB::ROOT/data/docs/*") { + $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 files.'; + end; + $self->htmlFooter; + return; + } + + _savelang($self, $lang) if $lang && $sect && $self->reqMethod eq 'POST' && _allowed($self, $lang); + _savedoc($self, $lang, $doc) if $lang && $doc && $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'; + a class => 'addnew', href => '/tladmin/intro', 'README'; + 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; + _docs($lang, $doc) if $lang; + end; + + _intro() if $intro; + _section($self, $lang, $sect, $page) if $lang && $sect; + _doc($self, $lang, $doc) if $lang && $doc; + + $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', <comment>|| ( <key>, <en>, <sync>, <tl> ) ] + + 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 _intro { + my $f = LangFile->new(read => $langfile); + my $intro = $f->read; + $intro = join "\n", @$intro[1..$#$intro]; + $f->close; + div class => 'mainbox'; + h1 'Introduction to the language file'; + pre $intro; + end; +} + + +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; + br; +} + + +sub _section { + 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/&/&/g; + $tl =~ s/</</g; + $tl =~ s/>/>/g; + 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; +} + + +sub _savedoc { + my($self, $lang, $doc) = @_; + + my $file = "$VNDB::ROOT/data/docs/$doc.$lang"; + + open my $f, '<:utf8', "$VNDB::ROOT/data/docs/$doc" or die $!; + my $en = join '', <$f>; + close $f; + + my $tl = $self->reqParam('tl'); + $tl =~ s/\r?\n/\n/g; + + return -e $file && unlink $file if $tl eq $en; + + open $f, '>:utf8', $file or die $!; + print $f $tl; + close $f; + chmod 0666, $file; +} + + +sub _docs { + my($lang, $doc) = @_; + + my @d = map /\.[a-z]{2}$/ || /\/8$/ ? () : s{^.+\/([^/]+)$}{$1} && $_, glob "$VNDB::ROOT/data/docs/*"; + + h2 class => 'alttitle', '...or a doc page'; + div style => 'margin: 0 40px'; + for (sort { $a =~ /^\d+$/ && $b =~ /^\d+$/ ? $a <=> $b : $a cmp $b } @d) { + div style => 'float: left; width: 60px;'; + a href => "/tladmin/$lang?doc=$_", $_ if $_ ne $doc; + txt $_ if $_ eq $doc; + end; + } + clearfloat; + end; +} + + +sub _doc { + my($self, $lang, $doc) = @_; + + open my $f, '<:utf8', "$VNDB::ROOT/data/docs/$doc" or die $!; + my $en = join '', <$f>; + close $f; + + my $tl = $en; + if(open $f, '<:utf8', "$VNDB::ROOT/data/docs/$doc.$lang") { + $tl = join '', <$f>; + close $f; + } + $tl =~ s/&/&/g; + $tl =~ s/</</g; + $tl =~ s/>/>/g; + + + form action => "/tladmin/$lang?doc=$doc", method => 'POST', 'accept-charset' => 'utf-8'; + div class => 'mainbox'; + a class => 'addnew', href => "/d$doc", "View current page" if $doc =~ /^\d+$/; + h1 "Translating page $doc"; + h2 class => 'alttitle', 'Left = English, Right = translation'; + + if(!_allowed($self, $lang)) { + div class => 'warning'; + h2 'Read-only'; + p "You can't edit this language."; + end; + } + + div style => 'width: 48%; margin-right: 10px; overflow-y: auto; float: left'; + pre style => 'font: 12px Tahoma', $en; + end; + textarea name => 'tl', id => 'tl', rows => ($en =~ y/\n//), + style => 'border: none; float: left; width: 49%; white-space: nowrap', wrap => 'off'; + lit $tl; + end; + clearfloat; + if(_allowed($self, $lang)) { + br; + fieldset class => 'submit'; + input type => 'submit', value => 'Save', class => 'submit'; + end; + } + end; + end; +} + + +1; + diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm index c4daffd9..a3bf7c29 100644 --- a/lib/VNDB/Util/Auth.pm +++ b/lib/VNDB/Util/Auth.pm @@ -26,7 +26,7 @@ sub authInit { my $token = substr($cookie, 0, 40); my $uid = substr($cookie, 40); return _rmcookie($self) if $uid !~ /^\d+$/ || !$self->dbSessionCheck($uid, $token); - $self->{_auth} = $self->dbUserGet(uid => $uid, what => 'mymessages')->[0]; + $self->{_auth} = $self->dbUserGet(uid => $uid, what => 'extended')->[0]; } @@ -95,7 +95,7 @@ sub _authCheck { return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass; - my $d = $self->dbUserGet(username => $user, what => 'mymessages')->[0]; + my $d = $self->dbUserGet(username => $user, what => 'extended')->[0]; return 0 if !defined $d->{id} || !$d->{rank}; if(_authEncryptPass($self, $pass, $d->{salt}) eq $d->{passwd}) { diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm index b1eb6432..ad99d32d 100644 --- a/lib/VNDB/Util/CommonHTML.pm +++ b/lib/VNDB/Util/CommonHTML.pm @@ -12,7 +12,7 @@ use POSIX 'ceil'; our @EXPORT = qw| htmlMainTabs htmlDenied htmlHiddenMessage htmlBrowse htmlBrowseNavigate - htmlRevision htmlEditMessage htmlItemMessage htmlVoteStats htmlHistory htmlSearchBox + htmlRevision htmlEditMessage htmlItemMessage htmlVoteStats htmlHistory htmlSearchBox htmlRGHeader |; @@ -101,7 +101,7 @@ sub htmlMainTabs { end; } - if($type eq 'v' && $obj->{rgraph}) { + if($type =~ /[vp]/ && $obj->{rgraph}) { li $sel eq 'rg' ? (class => 'tabselected') : (); a href => "/$id/rg", mt '_mtabs_relations'; end; @@ -501,16 +501,15 @@ sub htmlHistory { sub { td colspan => 2, class => 'tc1', mt '_hist_col_rev' }, [ mt '_hist_col_date' ], [ mt '_hist_col_user' ], - sub { td; a href => '#', id => 'history_comments', 'expand'; txt mt '_hist_col_page'; end; } + sub { td; a href => '#', id => 'expandlist', mt '_js_expand'; txt mt '_hist_col_page'; end; } ], row => sub { my($s, $n, $i) = @_; - my $tc = [qw|v r p|]->[$i->{type}]; - my $revurl = "/$tc$i->{iid}.$i->{rev}"; + my $revurl = "/$i->{type}$i->{iid}.$i->{rev}"; Tr $n % 2 ? ( class => 'odd' ) : (); td class => 'tc1_1'; - a href => $revurl, "$tc$i->{iid}"; + a href => $revurl, "$i->{type}$i->{iid}"; end; td class => 'tc1_2'; a href => $revurl, ".$i->{rev}"; @@ -524,7 +523,7 @@ sub htmlHistory { end; end; if($i->{comments}) { - Tr class => $n % 2 ? 'editsum odd hidden' : 'editsum hidden'; + Tr class => $n % 2 ? 'collapse msgsum odd hidden' : 'collapse msgsum hidden'; td colspan => 5; lit bb2html $i->{comments}, 150; end; @@ -538,13 +537,20 @@ sub htmlHistory { sub htmlSearchBox { my($self, $sel, $v) = @_; + # escape search query for use as a query string value + (my $q = $v||'') =~ s/&/%26/g; + $q =~ s/\?/%3F/g; + $q =~ s/;/%3B/g; + $q =~ s/ /%20/g; + $q = "?q=$q" if $q; + fieldset class => 'search'; p class => 'searchtabs'; - a href => '/v/all', $sel eq 'v' ? (class => 'sel') : (), mt '_searchbox_vn'; - a href => '/r', $sel eq 'r' ? (class => 'sel') : (), mt '_searchbox_releases'; - a href => '/p/all', $sel eq 'p' ? (class => 'sel') : (), mt '_searchbox_producers'; - a href => '/g', $sel eq 'g' ? (class => 'sel') : (), mt '_searchbox_tags'; - a href => '/u/all', $sel eq 'u' ? (class => 'sel') : (), mt '_searchbox_users'; + a href => "/v/all$q", $sel eq 'v' ? (class => 'sel') : (), mt '_searchbox_vn'; + a href => "/r$q", $sel eq 'r' ? (class => 'sel') : (), mt '_searchbox_releases'; + a href => "/p/all$q", $sel eq 'p' ? (class => 'sel') : (), mt '_searchbox_producers'; + a href => '/g'.($q?"/list$q":''), $sel eq 'g' ? (class => 'sel') : (), mt '_searchbox_tags'; + a href => "/u/all$q", $sel eq 'u' ? (class => 'sel') : (), mt '_searchbox_users'; end; input type => 'text', name => 'q', id => 'q', class => 'text', value => $v; input type => 'submit', class => 'submit', value => mt '_searchbox_submit'; @@ -552,5 +558,41 @@ sub htmlSearchBox { } +sub htmlRGHeader { + my($self, $title, $type, $obj) = @_; + + if(($self->reqHeader('Accept')||'') !~ /application\/xhtml\+xml/) { + $self->htmlHeader(title => $title); + $self->htmlMainTabs($type, $obj, 'rg'); + div class => 'mainbox'; + h1 $title; + div class => 'warning'; + h2 mt '_rg_notsupp'; + p mt '_rg_notsupp_msg'; + end; + end; + $self->htmlFooter; + return 1; + } + $self->resHeader('Content-Type' => 'application/xhtml+xml; charset=UTF-8'); + + # This is a REALLY ugly hack, need find a proper solution in YAWF + no warnings 'redefine'; + my $sub = \&YAWF::XML::html; + *YAWF::XML::html = sub () { + lit q|<!DOCTYPE html PUBLIC + "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" + "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">|; + tag 'html', + xmlns => "http://www.w3.org/1999/xhtml", + 'xmlns:svg' => 'http://www.w3.org/2000/svg', + 'xmlns:xlink' => 'http://www.w3.org/1999/xlink'; + }; + $self->htmlHeader(title => $title); + *YAWF::XML::html = $sub; + $self->htmlMainTabs($type, $obj, 'rg'); + return 0; +} + 1; diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm index 85971ba9..084b9a4e 100644 --- a/lib/VNDB/Util/LayoutHTML.pm +++ b/lib/VNDB/Util/LayoutHTML.pm @@ -10,7 +10,7 @@ use VNDB::Func; our @EXPORT = qw|htmlHeader htmlFooter|; -sub htmlHeader { # %options->{ title, js, noindex, search } +sub htmlHeader { # %options->{ title, noindex, search } my($self, %o) = @_; my $skin = $self->reqParam('skin') || $self->authInfo->{skin} || $self->{skin_default}; $skin = $self->{skin_default} if !$self->{skins}{$skin} || !-d "$VNDB::ROOT/static/s/$skin"; @@ -22,12 +22,6 @@ sub htmlHeader { # %options->{ title, js, noindex, search } Link rel => 'shortcut icon', href => '/favicon.ico', type => 'image/x-icon'; Link rel => 'stylesheet', href => $self->{url_static}.'/s/'.$skin.'/style.css?'.$self->{version}, type => 'text/css', media => 'all'; Link rel => 'search', type => 'application/opensearchdescription+xml', title => 'VNDB VN Search', href => $self->{url}.'/opensearch.xml'; - if($o{js}) { - script type => 'text/javascript', src => $self->{url_static}.'/f/forms.js?'.$self->{version}; end; - } - script type => 'text/javascript', src => $self->{url_static}.'/f/script.js?'.$self->{version}; - # most browsers don't like a self-closing <script> tag... - end; if($self->authInfo->{customcss}) { (my $css = $self->authInfo->{customcss}) =~ s/\n/ /g; style type => 'text/css', $css; @@ -55,12 +49,8 @@ sub _menu { div class => 'menubox'; h2; - span; - for (grep $self->{l10n}->language_tag() ne $_, $self->{l10n}->languages()) { - a href => "?l10n=$_"; - cssicon "lang $_", mt "_lang_$_"; # NOTE: should actually be in the destination language... - end; - } + a href => "#", id => 'lang_select'; + cssicon "lang ".$self->{l10n}->language_tag(), mt "_lang_".$self->{l10n}->language_tag(); end; txt mt '_menu'; end; @@ -89,6 +79,7 @@ sub _menu { div class => 'menubox'; if($self->authInfo->{id}) { + my $msg = $self->dbUserMessageCount($self->authInfo->{id}); my $uid = sprintf '/u%d', $self->authInfo->{id}; h2; a href => $uid, ucfirst $self->authInfo->{username}; @@ -99,7 +90,7 @@ sub _menu { a href => "$uid/edit", mt '_menu_myprofile'; br; a href => "$uid/list", mt '_menu_myvnlist'; br; a href => "$uid/wish", mt '_menu_mywishlist'; br; - a href => "/t$uid", mt '_menu_mymessages', $self->authInfo->{mymessages}; br; + a href => "/t$uid", $msg ? (class => 'standout') : (), mt '_menu_mymessages', $msg; br; a href => "$uid/hist", mt '_menu_mychanges'; br; a href => "$uid/tags", mt '_menu_mytags'; br; br; @@ -163,6 +154,7 @@ sub htmlFooter { a href => $self->{source_url}, mt '_footer_source'; end; end; # /div maincontent + script type => 'text/javascript', src => $self->{url_static}.'/f/script.js?'.$self->{version}, ''; end; # /body end; # /html |