summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2019-11-14 13:26:03 +0100
committerYorhel <git@yorhel.nl>2019-11-14 13:31:15 +0100
commit389fe32c24adce3277548892b5d636c40ac35bc4 (patch)
treeb739ac49edbe9670b64d310676a32310a6f69440
parent06bacb61526f3945520dd344821d2aa7b85a5f43 (diff)
v2rw: Convert staff pages
This is where the ExtLink module comes in handy: generating the revision comparison thing is much easier now. Did find and fix a bunch of issues with the new revision box generator code, but that was to be expected, I hadn't tested that code well yet and this is its first more demanding use. Rest of this is a pretty direct rewrite, nothing too special.
-rw-r--r--lib/VNDB/DB/Staff.pm81
-rw-r--r--lib/VNDB/ExtLinks.pm12
-rw-r--r--lib/VNDB/Handler/Staff.pm165
-rw-r--r--lib/VNDB/Util/CommonHTML.pm1
-rw-r--r--lib/VNWeb/HTML.pm67
-rw-r--r--lib/VNWeb/Prelude.pm2
-rw-r--r--lib/VNWeb/Staff/Page.pm181
7 files changed, 256 insertions, 253 deletions
diff --git a/lib/VNDB/DB/Staff.pm b/lib/VNDB/DB/Staff.pm
index 87dbef43..5a393dbb 100644
--- a/lib/VNDB/DB/Staff.pm
+++ b/lib/VNDB/DB/Staff.pm
@@ -5,10 +5,9 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbStaffGet dbStaffGetRev|;
+our @EXPORT = qw|dbStaffGet |;
# options: results, page, id, aid, search, exact, truename, role, gender
-# what: extended changes roles aliases
sub dbStaffGet {
my $self = shift;
my %o = (
@@ -57,7 +56,6 @@ sub dbStaffGet {
);
my $select = 's.id, sa.aid, sa.name, sa.original, s.gender, s.lang';
- $select .= ', s.desc, s.l_wp, s.l_site, s.l_twitter, s.l_anidb, s.l_wikidata, s.l_pixiv, s.hidden, s.locked' if $o{what} =~ /extended/;
my($order, @order) = ('sa.name');
if($o{sort} && $o{sort} eq 'search') {
@@ -74,83 +72,8 @@ sub dbStaffGet {
$select, \%where, @order
);
- return _enrich($self, $r, $np, 0, $o{what});
-}
-
-
-sub dbStaffGetRev {
- my $self = shift;
- my %o = (what => '', @_);
-
- $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'s\' AND itemid = ?', $o{id})->{rev};
-
- my $select = 'c.itemid AS id, sa.aid, sa.name, sa.original, s.gender, s.lang';
- $select .= ', extract(\'epoch\' from c.added) as added, c.comments, c.rev, c.ihid, c.ilock, '.VNWeb::DB::sql_user();
- $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev';
- $select .= ', s.desc, s.l_wp, s.l_site, s.l_twitter, s.l_anidb, s.l_wikidata, s.l_pixiv, so.hidden, so.locked' if $o{what} =~ /extended/;
-
- my $r = $self->dbAll(q|
- SELECT !s
- FROM changes c
- JOIN staff so ON so.id = c.itemid
- JOIN staff_hist s ON s.chid = c.id
- JOIN staff_alias_hist sa ON sa.chid = c.id AND s.aid = sa.aid
- JOIN users u ON u.id = c.requester
- WHERE c.type = 's' AND c.itemid = ? AND c.rev = ?|,
- $select, $o{id}, $o{rev}
- );
-
- return _enrich($self, $r, 0, 1, $o{what});
-}
-
-
-sub _enrich {
- my($self, $r, $np, $rev, $what) = @_;
-
- # Role info is linked to VN revisions, so is independent of the selected staff revision
- if(@$r && $what =~ /roles/) {
- my %r = map {
- $_->{roles} = [];
- $_->{cast} = [];
- ($_->{id}, $_);
- } @$r;
-
- push @{$r{ delete $_->{id} }{roles}}, $_ for (@{$self->dbAll(q|
- SELECT sa.id, sa.aid, v.id AS vid, sa.name, sa.original, v.c_released, v.title, v.original AS t_original, vs.role, vs.note
- FROM vn_staff vs
- JOIN vn v ON v.id = vs.id
- JOIN staff_alias sa ON vs.aid = sa.aid
- WHERE sa.id IN(!l) AND NOT v.hidden
- ORDER BY v.c_released ASC, v.title ASC, vs.role ASC|, [ keys %r ]
- )});
- push @{$r{ delete $_->{id} }{cast}}, $_ for (@{$self->dbAll(q|
- SELECT sa.id, sa.aid, v.id AS vid, sa.name, sa.original, v.c_released, v.title, v.original AS t_original, c.id AS cid, c.name AS c_name, c.original AS c_original, vs.note
- FROM vn_seiyuu vs
- JOIN vn v ON v.id = vs.id
- JOIN chars c ON c.id = vs.cid
- JOIN staff_alias sa ON vs.aid = sa.aid
- WHERE sa.id IN(!l) AND NOT v.hidden
- ORDER BY v.c_released ASC, v.title ASC|, [ keys %r ]
- )});
- }
-
- if(@$r && $what =~ /aliases/) {
- my ($col, $hist, $colname) = $rev ? ('cid', '_hist', 'chid') : ('id', '', 'id');
- my %r = map {
- $_->{aliases} = [];
- ($_->{$col}, $_);
- } @$r;
-
- push @{$r{ delete $_->{xid} }{aliases}}, $_ for (@{$self->dbAll("
- SELECT s.$colname AS xid, sa.aid, sa.name, sa.original
- FROM staff_alias$hist sa
- JOIN staff$hist s ON s.$colname = sa.$colname
- WHERE s.$colname IN(!l) AND s.aid <> sa.aid
- ORDER BY sa.name ASC", [ keys %r ]
- )});
- }
-
return wantarray ? ($r, $np) : $r;
}
+
1;
diff --git a/lib/VNDB/ExtLinks.pm b/lib/VNDB/ExtLinks.pm
index 8e9bdf72..2280285a 100644
--- a/lib/VNDB/ExtLinks.pm
+++ b/lib/VNDB/ExtLinks.pm
@@ -5,7 +5,7 @@ use warnings;
use VNDB::Config;
use Exporter 'import';
-our @EXPORT = ('enrich_extlinks');
+our @EXPORT = ('enrich_extlinks', 'revision_extlinks');
# column name in wikidata table => \%info
@@ -225,4 +225,14 @@ sub enrich_extlinks {
}
+# Returns a list of @fields for use in VNWeb::HTML::revision_()
+sub revision_extlinks {
+ my($type) = @_;
+ map {
+ my($f, $p) = ($_, $LINKS{$type}{$_});
+ [ $f, $p->{label}, fmt => sub { TUWF::XML::a_ href => sprintf($p->{fmt}, $_), $_; }, empty => 0 ]
+ } sort keys $LINKS{$type}->%*
+}
+
+
1;
diff --git a/lib/VNDB/Handler/Staff.pm b/lib/VNDB/Handler/Staff.pm
index 7da42e8d..adab2be8 100644
--- a/lib/VNDB/Handler/Staff.pm
+++ b/lib/VNDB/Handler/Staff.pm
@@ -3,180 +3,17 @@ package VNDB::Handler::Staff;
use strict;
use warnings;
-use TUWF qw(:html :xml uri_escape xml_escape);
+use TUWF qw(:html :xml uri_escape);
use VNDB::Func;
use VNDB::Types;
-use VNDB::ExtLinks;
use List::Util qw(first);
TUWF::register(
- qr{s([1-9]\d*)(?:\.([1-9]\d*))?} => \&page,
qr{s/([a-z0]|all)} => \&list,
qr{xml/staff\.xml} => \&staffxml,
);
-sub page {
- my($self, $id, $rev) = @_;
-
- my $method = $rev ? 'dbStaffGetRev' : 'dbStaffGet';
- my $s = $self->$method(
- id => $id,
- what => 'extended aliases roles',
- $rev ? ( rev => $rev ) : ()
- )->[0];
- return $self->resNotFound if !$s->{id};
- enrich_extlinks s => $s;
-
- my $metadata = {
- 'og:title' => $s->{name},
- 'og:description' => bb2text $s->{desc},
- };
-
- $self->htmlHeader(title => $s->{name}, noindex => $rev, metadata => $metadata);
- $self->htmlMainTabs('s', $s) if $id;
- return if $self->htmlHiddenMessage('s', $s);
-
- if($rev) {
- my $prev = $rev && $rev > 1 && $self->dbStaffGetRev(id => $id, rev => $rev-1, what => 'extended aliases')->[0];
- $self->htmlRevision('s', $prev, $s,
- [ name => 'Name (romaji)', diff => 1 ],
- [ original => 'Original name', diff => 1 ],
- [ gender => 'Gender', serialize => sub { $GENDER{$_[0]} } ],
- [ lang => 'Language', serialize => sub { "$_[0] ($LANGUAGE{$_[0]})" } ],
- [ l_site => 'Official page', diff => 1 ],
- [ l_wp => 'Wikipedia link', htmlize => sub { $_[0] ? sprintf '<a href="http://en.wikipedia.org/wiki/%s">%1$s</a>', xml_escape $_[0] : '[empty]' }],
- [ l_wikidata=> 'Wikidata ID', htmlize => sub { $_[0] ? sprintf '<a href="https://www.wikidata.org/wiki/Q%d">Q%1$d</a>', $_[0] : '[empty]' } ],
- [ l_twitter => 'Twitter account', diff => 1 ],
- [ l_anidb => 'AniDB creator ID', serialize => sub { $_[0] // '' } ],
- [ l_pixiv => 'Pixiv', htmlize => sub { $_[0] ? sprintf '<a href="https://www.pixiv.net/member.php?id=%d">%1$d</a>', $_[0] : '[empty]' } ],
- [ desc => 'Description', diff => qr/[ ,\n\.]/ ],
- [ aliases => 'Aliases', join => '<br />', split => sub {
- map xml_escape(sprintf('%s%s', $_->{name}, $_->{original} ? ' ('.$_->{original}.')' : '')), @{$_[0]};
- }],
- );
- }
-
- div class => 'mainbox staffpage';
- $self->htmlItemMessage('s', $s);
- h1 $s->{name};
- h2 class => 'alttitle', lang => $s->{lang}, $s->{original} if $s->{original};
-
- # info table
- table class => 'stripe';
- thead;
- Tr;
- td colspan => 2;
- b style => 'margin-right: 10px', $s->{name};
- b class => 'grayedout', style => 'margin-right: 10px', lang => $s->{lang}, $s->{original} if $s->{original};
- cssicon "gen $s->{gender}", $GENDER{$s->{gender}} if $s->{gender} ne 'unknown';
- end;
- end;
- end;
- Tr;
- td class => 'key', 'Language';
- td $LANGUAGE{$s->{lang}};
- end;
- if(@{$s->{aliases}}) {
- Tr;
- td class => 'key', @{$s->{aliases}} == 1 ? 'Alias' : 'Aliases';
- td;
- table class => 'aliases';
- for my $alias (@{$s->{aliases}}) {
- Tr class => 'nostripe';
- td $alias->{original} ? () : (colspan => 2), class => 'key';
- txt $alias->{name};
- end;
- td lang => $s->{lang}, $alias->{original} if $alias->{original};
- end;
- }
- end;
- end;
- end;
- }
- if($s->{extlinks}->@*) {
- Tr;
- td class => 'key', 'Links';
- td;
- for($s->{extlinks}->@*) {
- a href => $_->[1], $_->[0];
- br if $_ != $s->{extlinks}[$#{$s->{extlinks}}];
- }
- end;
- end;
- }
- end 'table';
-
- # description
- p class => 'description';
- lit bb2html $s->{desc}, 0, 1;
- end;
- end;
-
- _roles($self, $s);
- _cast($self, $s);
- $self->htmlFooter;
-}
-
-
-sub _roles {
- my($self, $s) = @_;
- return if !@{$s->{roles}};
-
- h1 class => 'boxtitle', 'Credits';
- $self->htmlBrowse(
- items => $s->{roles},
- class => 'staffroles',
- header => [
- [ 'Title' ],
- [ 'Released' ],
- [ 'Role' ],
- [ 'As' ],
- [ 'Note' ],
- ],
- row => sub {
- my($r, $n, $l) = @_;
- Tr;
- td class => 'tc1'; a href => "/v$l->{vid}", title => $l->{t_original}||$l->{title}, shorten $l->{title}, 60; end;
- td class => 'tc2'; lit fmtdatestr $l->{c_released}; end;
- td class => 'tc3', $CREDIT_TYPE{$l->{role}};
- td class => 'tc4', title => $l->{original}||$l->{name}, $l->{name};
- td class => 'tc5', $l->{note};
- end;
- },
- );
-}
-
-
-sub _cast {
- my($self, $s) = @_;
- return if !@{$s->{cast}};
-
- h1 class => 'boxtitle', sprintf 'Voiced characters (%d)', scalar @{$s->{cast}};
- $self->htmlBrowse(
- items => $s->{cast},
- class => 'staffroles',
- header => [
- [ 'Title' ],
- [ 'Released' ],
- [ 'Cast' ],
- [ 'As' ],
- [ 'Note' ],
- ],
- row => sub {
- my($r, $n, $l) = @_;
- Tr;
- td class => 'tc1'; a href => "/v$l->{vid}", title => $l->{t_original}||$l->{title}, shorten $l->{title}, 60; end;
- td class => 'tc2'; lit fmtdatestr $l->{c_released}; end;
- td class => 'tc3'; a href => "/c$l->{cid}", title => $l->{c_original}, $l->{c_name}; end;
- td class => 'tc4', title => $l->{original}||$l->{name}, $l->{name};
- td class => 'tc5', $l->{note};
- end;
- },
- );
-}
-
-
sub list {
my ($self, $char) = @_;
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index b56a7c74..c6986c1c 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -116,7 +116,6 @@ sub htmlHiddenMessage {
my $editsum = $type eq 'v' ? $self->dbVNGetRev(id => $obj->{id})->[0]{comments}
: $type eq 'r' ? $self->dbReleaseGetRev(id => $obj->{id})->[0]{comments}
: $type eq 'c' ? $self->dbCharGetRev(id => $obj->{id})->[0]{comments}
- : $type eq 's' ? $self->dbStaffGetRev(id => $obj->{id})->[0]{comments}
: $self->dbProducerGetRev(id => $obj->{id})->[0]{comments};
div class => 'mainbox';
h1 $obj->{title}||$obj->{name};
diff --git a/lib/VNWeb/HTML.pm b/lib/VNWeb/HTML.pm
index 30813aac..2947041e 100644
--- a/lib/VNWeb/HTML.pm
+++ b/lib/VNWeb/HTML.pm
@@ -8,7 +8,7 @@ use Encode 'encode_utf8', 'decode_utf8';
use JSON::XS;
use TUWF ':html5_', 'uri_escape', 'html_escape', 'mkclass';
use Exporter 'import';
-use POSIX 'ceil';
+use POSIX 'ceil', 'strftime';
use Carp 'croak';
use JSON::XS;
use VNDB::Config;
@@ -23,12 +23,14 @@ our @EXPORT = qw/
debug_
join_
user_ user_displayname
+ rdate_
elm_
framework_
revision_
paginate_
sortable_
searchbox_
+ itemmsg_
editmsg_
/;
@@ -93,6 +95,22 @@ sub user_displayname {
}
+# Display a release date.
+sub rdate_ {
+ my $date = sprintf '%08d', shift||0;
+ my $future = $date > strftime '%Y%m%d', gmtime;
+ my($y, $m, $d) = ($1, $2, $3) if $date =~ /^([0-9]{4})([0-9]{2})([0-9]{2})$/;
+
+ my $str = $y == 0 ? 'unknown' :
+ $y == 9999 ? 'TBA' :
+ $m == 99 ? sprintf('%04d', $y) :
+ $d == 99 ? sprintf('%04d-%02d', $y, $m) :
+ sprintf('%04d-%02d-%02d', $y, $m, $d);
+
+ $future ? b_ class => 'future', $str : txt_ $str
+}
+
+
# Instantiate an Elm module
sub elm_ {
my($mod, $schema, $data, $placeholder) = @_;
@@ -426,6 +444,7 @@ sub framework_ {
sub _revision_header_ {
my($type, $obj) = @_;
b_ "Revision $obj->{chrev}";
+ debug_ $obj;
if(auth) {
lit_ ' (';
a_ href => "/$type$obj->{id}.$obj->{chrev}/edit", $obj->{chrev} == $obj->{maxrev} ? 'edit' : 'revert to';
@@ -445,8 +464,12 @@ sub _revision_header_ {
sub _revision_fmtval_ {
my($opt, $val) = @_;
- return i_ '[empty]' if !defined $val || !length $val;
+ return i_ '[empty]' if !defined $val || !length $val || (defined $opt->{empty} && $val eq $opt->{empty});
return lit_ html_escape $val if !$opt->{fmt};
+ if(ref $opt->{fmt} eq 'HASH') {
+ my $h = $opt->{fmt}{$val};
+ return txt_ ref $h eq 'HASH' ? $h->{txt} : $h || '[unknown]';
+ }
return txt_ $val ? 'True' : 'False' if $opt->{fmt} eq 'bool';
local $_ = $val;
$opt->{fmt}->();
@@ -488,9 +511,9 @@ sub _revision_fmtcol_ {
}
}
- } elsif(@$l > 2 && $i == 2 && ($ch eq '+' || $ch eq 'c')) {
+ } elsif(@$l > 1 && $i == 2 && ($ch eq '+' || $ch eq 'c')) {
b_ class => 'diff_add', sub { _revision_fmtval_ $opt, $val }
- } elsif(@$l > 2 && $i == 1 && ($ch eq '-' || $ch eq 'c')) {
+ } elsif(@$l > 1 && $i == 1 && ($ch eq '-' || $ch eq 'c')) {
b_ class => 'diff_del', sub { _revision_fmtval_ $opt, $val }
} elsif($ch eq 'c' || $ch eq 'u') {
_revision_fmtval_ $opt, $val;
@@ -500,6 +523,19 @@ sub _revision_fmtcol_ {
}
+# Recursively stringify scalars. This is generally a no-op, except when
+# serializing the data structure to JSON this will cause all numbers to be
+# formatted as strings. Not very useful for data exchange, but this allows for
+# creating proper canonicalized JSON where equivalent data structures serialize
+# to the same string. (TODO: Might as well write a function that hashes
+# recursive data structures and use that for comparison - a little bit more
+# work but less magical)
+sub _stringify_scalars_rec {
+ defined($_[0]) && !ref $_[0] ? "$_[0]" :
+ ref $_[0] eq 'HASH' ? map _stringify_scalars_rec($_), values $_[0]->%* :
+ ref $_[0] eq 'ARRAY' ? map _stringify_scalars_rec($_), $_[0]->@* : undef;
+}
+
sub _revision_diff_ {
my($type, $old, $new, $field, $name, %opt) = @_;
@@ -508,8 +544,8 @@ sub _revision_diff_ {
my @old = ref $old->{$field} eq 'ARRAY' ? $old->{$field}->@* : ($old->{$field});
my @new = ref $new->{$field} eq 'ARRAY' ? $new->{$field}->@* : ($new->{$field});
- my $JS = JSON::XS->new->utf8->allow_nonref;
- my $l = sdiff \@old, \@new, sub { $JS->encode($_[0]) };
+ my $JS = JSON::XS->new->utf8->canonical->allow_nonref;
+ my $l = sdiff \@old, \@new, sub { _stringify_scalars_rec($_[0]); $JS->encode($_[0]) };
return if !grep $_->[0] ne 'u', @$l;
# Now check if we should do a textual diff on the changed items.
@@ -582,9 +618,13 @@ sub _revision_cmp_ {
# [ field_name, display_name, %options ]
#
# Options:
-# fmt => 'bool'||sub {$_} - Formatting function for individual values.
+# fmt => 'bool'||\%HASH||sub {$_} - Formatting function for individual values.
# If not given, the field is rendered as plain text and changes are highlighted with a diff.
-# join => sub{} - HTML to join multi-value fields, defaults to \&br_.
+# \%HASH -> Look the field up in the hash table (values should be string or {txt=>string}.
+# sub($value) {$_} -> Custom formatting function, should output TUWF::XML data HTML.
+# join => sub{} - HTML to join multi-value fields, defaults to \&br_.
+# empty => str - What value should be considered "empty", e.g. (empty => 0) for integer fields.
+# undef or empty string are always considered empty values.
sub revision_ {
my($type, $new, $enrich, @fields) = @_;
@@ -688,6 +728,17 @@ sub searchbox_ {
}
+# Generate a message to display on an entry page when the entry has been locked or the user can't edit it.
+sub itemmsg_ {
+ my($type, $obj) = @_;
+ if($obj->{entry_locked}) {
+ p_ class => 'locked', 'Locked for editing';
+ } elsif(auth && !can_edit $type => $obj) {
+ p_ class => 'locked', 'You can not edit this page';
+ }
+}
+
+
# Generate the initial mainbox when adding or editing a database entry, with a
# friendly message pointing to the guidelines and stuff.
# Args: $type ('v','r', etc), $obj (from db_entry(), or undef for new page), $page_title, $is_this_a_copy?
diff --git a/lib/VNWeb/Prelude.pm b/lib/VNWeb/Prelude.pm
index c00e9afb..10fc3b8d 100644
--- a/lib/VNWeb/Prelude.pm
+++ b/lib/VNWeb/Prelude.pm
@@ -14,6 +14,7 @@
# use VNDB::Types;
# use VNDB::Config;
# use VNDB::Func 'fmtdate', 'fmtage', 'fmtvote', 'query_encode';
+# use VNDB::ExtLinks;
# use VNWeb::Auth;
# use VNWeb::HTML;
# use VNWeb::DB;
@@ -56,6 +57,7 @@ sub import {
use VNDB::Types;
use VNDB::Config;
use VNDB::Func 'fmtdate', 'fmtage', 'fmtvote', 'query_encode';
+ use VNDB::ExtLinks;
use VNWeb::Auth;
use VNWeb::HTML;
use VNWeb::DB;
diff --git a/lib/VNWeb/Staff/Page.pm b/lib/VNWeb/Staff/Page.pm
new file mode 100644
index 00000000..8a42f05f
--- /dev/null
+++ b/lib/VNWeb/Staff/Page.pm
@@ -0,0 +1,181 @@
+package VNWeb::Staff::Page;
+
+use VNWeb::Prelude;
+use VNWeb::Docs::Lib;
+
+
+sub enrich_item {
+ my($s) = @_;
+
+ # Add a 'main' flag to each alias
+ $_->{main} = $s->{aid} == $_->{aid} for $s->{alias}->@*;
+
+ # Sort aliases by name
+ $s->{alias} = [ sort { $a->{name} cmp $b->{name} || ($a->{original}||'') cmp ($b->{original}||'') } $s->{alias}->@* ];
+}
+
+
+sub _rev_ {
+ my($s) = @_;
+ revision_ s => $s, \&enrich_item,
+ [ alias => 'Names', fmt => sub {
+ txt_ $_->{name};
+ txt_ " ($_->{original})" if $_->{original};
+ b_ class => 'grayedout', ' (primary)' if $_->{main};
+ } ],
+ [ gender => 'Gender', fmt => \%GENDER ],
+ [ lang => 'Language', fmt => \%LANGUAGE ],
+ [ desc => 'Description' ],
+ revision_extlinks 's'
+}
+
+
+sub _infotable_ {
+ my($main, $s) = @_;
+ table_ class => 'stripe', sub {
+ thead_ sub { tr_ sub {
+ td_ colspan => 2, sub {
+ b_ style => 'margin-right: 10px', $main->{name};
+ b_ class => 'grayedout', style => 'margin-right: 10px', lang => $s->{lang}, $main->{original} if $main->{original};
+ abbr_ class => "icons gen $s->{gender}", title => $GENDER{$s->{gender}}, '' if $s->{gender} ne 'unknown';
+ }
+ } };
+
+ tr_ sub {
+ td_ class => 'key', 'Language';
+ td_ $LANGUAGE{$s->{lang}};
+ };
+
+ my @alias = grep !$_->{main}, $s->{alias}->@*;
+ tr_ sub {
+ td_ @alias == 1 ? 'Alias' : 'Aliases';
+ td_ sub {
+ table_ class => 'aliases', sub {
+ tr_ class => 'nostripe', sub {
+ td_ class => 'key', $_->{original} ? () : (colspan => 2), $_->{name};
+ td_ lang => $s->{lang}, $_->{original} if $_->{original};
+ } for @alias;
+ };
+ };
+ } if @alias;
+
+ tr_ sub {
+ td_ class => 'key', 'Links';
+ td_ sub {
+ join_ \&br_, sub { a_ href => $_->[1], $_->[0] }, $s->{extlinks}->@*;
+ };
+ } if $s->{extlinks}->@*;
+ };
+}
+
+
+sub _roles_ {
+ my($s) = @_;
+ my %alias = map +($_->{aid}, $_), $s->{alias}->@*;
+
+ my $roles = tuwf->dbAlli(q{
+ SELECT v.id, vs.aid, vs.role, vs.note, v.c_released, v.title, v.original
+ FROM vn_staff vs
+ JOIN vn v ON v.id = vs.id
+ WHERE vs.aid IN}, [ keys %alias ], q{
+ AND NOT v.hidden
+ ORDER BY v.c_released ASC, v.title ASC, vs.role ASC
+ });
+ return if !@$roles;
+
+ h1_ class => 'boxtitle', sprintf 'Credits (%d)', scalar @$roles;
+ div_ class => 'mainbox browse staffroles', sub {
+ table_ class => 'stripe', sub {
+ thead_ sub { tr_ sub {
+ td_ class => 'tc1', 'Title';
+ td_ class => 'tc2', 'Released';
+ td_ class => 'tc3', 'Role';
+ td_ class => 'tc4', 'As';
+ td_ class => 'tc5', 'Note';
+ }};
+ tr_ sub {
+ my($v, $a) = ($_, $alias{$_->{aid}});
+ td_ class => 'tc1', sub {
+ a_ href => "/v$v->{id}", title => $v->{original}||$v->{title}, shorten $v->{title}, 60;
+ };
+ td_ class => 'tc2', sub { rdate_ $v->{c_released} };
+ td_ class => 'tc3', $CREDIT_TYPE{$v->{role}};
+ td_ class => 'tc4', title => $a->{original}||$a->{name}, $a->{name};
+ td_ class => 'tc5', $v->{note};
+ } for @$roles;
+ };
+ };
+}
+
+
+sub _cast_ {
+ my($s) = @_;
+ my %alias = map +($_->{aid}, $_), $s->{alias}->@*;
+
+ my $cast = tuwf->dbAlli(q{
+ SELECT vs.aid, v.id, v.c_released, v.title, v.original, c.id AS cid, c.name AS c_name, c.original AS c_original, vs.note
+ FROM vn_seiyuu vs
+ JOIN vn v ON v.id = vs.id
+ JOIN chars c ON c.id = vs.cid
+ WHERE vs.aid IN}, [ keys %alias ], q{
+ AND NOT v.hidden
+ AND NOT c.hidden
+ ORDER BY v.c_released ASC, v.title ASC
+ });
+ return if !@$cast;
+
+ h1_ class => 'boxtitle', sprintf 'Voiced characters (%d)', scalar @$cast;
+ div_ class => 'mainbox browse staffroles', sub {
+ table_ class => 'stripe', sub {
+ thead_ sub { tr_ sub {
+ td_ class => 'tc1', 'Title';
+ td_ class => 'tc2', 'Released';
+ td_ class => 'tc3', 'Cast';
+ td_ class => 'tc4', 'As';
+ td_ class => 'tc5', 'Note';
+ }};
+ tr_ sub {
+ my($v, $a) = ($_, $alias{$_->{aid}});
+ td_ class => 'tc1', sub {
+ a_ href => "/v$v->{id}", title => $v->{original}||$v->{title}, shorten $v->{title}, 60;
+ };
+ td_ class => 'tc2', sub { rdate_ $v->{c_released} };
+ td_ class => 'tc3', sub {
+ a_ href => "/c$v->{cid}", title => $v->{c_original}||$v->{c_name}, $v->{c_name};
+ };
+ td_ class => 'tc4', title => $a->{original}||$a->{name}, $a->{name};
+ td_ class => 'tc5', $v->{note};
+ } for @$cast;
+ };
+ };
+}
+
+
+TUWF::get qr{/$RE{srev}} => sub {
+ my $s = db_entry s => tuwf->capture('id'), tuwf->capture('rev');
+ return tuwf->resNotFound if !$s;
+
+ enrich_item $s;
+ enrich_extlinks s => $s;
+ my($main) = grep $_->{aid} == $s->{aid}, $s->{alias}->@*;
+
+ framework_ title => $main->{name}, index => 1, type => 's', dbobj => $s, hiddenmsg => 1,
+ og => {
+ description => bb2text $s->{desc}
+ },
+ sub {
+ _rev_ $s if tuwf->capture('rev');
+ div_ class => 'mainbox staffpage', sub {
+ itemmsg_ s => $s;
+ h1_ sub { txt_ $main->{name}; debug_ $s };
+ h2_ class => 'alttitle', lang => $s->{lang}, $main->{original} if $main->{original};
+ _infotable_ $main, $s;
+ p_ class => 'description', sub { lit_ bb2html $s->{desc} };
+ };
+
+ _roles_ $s;
+ _cast_ $s;
+ };
+};
+
+1;