summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormorkt <>2015-01-02 10:22:25 +0100
committerYorhel <git@yorhel.nl>2015-01-02 10:22:25 +0100
commit228cb96969a33a60c40dd4991a394460652010af (patch)
tree9d69e00562ae545b79ca8a899d2439c4800133d7
parent03585d77637a7052d0af2cdb1e4be66358412b6a (diff)
staff: Fix deleting of staff + use JSON to pass data + minor fixes
-rw-r--r--data/lang.txt3
-rw-r--r--data/script.js150
-rw-r--r--data/style.css5
-rw-r--r--lib/VNDB/DB/Chars.pm7
-rw-r--r--lib/VNDB/DB/VN.pm4
-rw-r--r--lib/VNDB/Func.pm24
-rw-r--r--lib/VNDB/Handler/Staff.pm35
-rw-r--r--lib/VNDB/Handler/VNEdit.pm73
-rw-r--r--lib/VNDB/Handler/VNPage.pm4
-rw-r--r--lib/VNDB/Util/CommonHTML.pm1
-rw-r--r--util/sql/staff.sql2
11 files changed, 168 insertions, 140 deletions
diff --git a/data/lang.txt b/data/lang.txt
index 6b7ab0ae..46b9abaa 100644
--- a/data/lang.txt
+++ b/data/lang.txt
@@ -15532,5 +15532,8 @@ tr : [_1] sadece küçük harfler, rakamlar ve tire içerebilir; ve bir harf ile
uk : Поле [_1] може складатися лише з маленьких літер латинки, цифр і рисок, і має починатися з літери
it : [_1] può avere solo caratteri minuscoli e trattini, e deve iniziare con una lettera
+:_formerr_tpl_json
+en : [_1]: Malformed staff data
+
# vim: set ft=none:
diff --git a/data/script.js b/data/script.js
index b53e33fe..141db70c 100644
--- a/data/script.js
+++ b/data/script.js
@@ -2053,10 +2053,12 @@ if(byId('jt_box_chare_vns'))
/* S T A F F (/s+/edit) */
function salLoad () {
- var aliases = byId('aliases').value.split('|||');
- for (var i = 0; i < aliases.length && aliases[i].length > 1; i++) {
- var al = aliases[i].split(',');
- salAdd(al[0], al[1], al.slice(2).join(','));
+ var src = byId('aliases').value;
+ if(src) {
+ var aliases = JSON.parse(src);
+ for (var i = 0; i < aliases.length; i++) {
+ salAdd(aliases[i].aid, aliases[i].name, aliases[i].orig);
+ }
}
salEmpty();
@@ -2094,9 +2096,9 @@ function salSerialize() {
var name = byName(byClass(tbl[i], 'td', 'tc_name')[0], 'input')[0].value;
var orig = byName(byClass(tbl[i], 'td', 'tc_original')[0], 'input')[0].value;
var id = byName(byClass(tbl[i], 'td', 'tc_add')[0], 'input')[0].value;
- a[a.length] = [ id, name, orig ].join(',');
+ a.push({ aid:id, name:name, orig:orig });
}
- byId('aliases').value = a.join('|||');
+ byId('aliases').value = JSON.stringify(a);
return true;
}
@@ -2132,42 +2134,35 @@ if(byId('jt_box_staffe_aliases'))
/* S T A F F < - > V N L I N K I N G (/v+/edit#vn_staff) */
+// vnsStaffData maps alias id to staff data { NNN: { id: ..., aid: NNN, name: ...} }
+// used to fill form fields instead of ajax queries in vnsLoad() and vncLoad()
+var vnsStaffData = {};
+
function onSubmit(form, handler) {
var prev_handler = form.onsubmit;
form.onsubmit = function(e) {
if (prev_handler) prev_handler(e);
- handler(e);
+ return handler(e);
}
}
function vnsLoad() {
- var credits = byId('credits').value.split('|||');
- var q = []; // list of a=X paramters
- var s = {};
- for (var i = 0; i < credits.length && credits[i].length > 1; i++) {
- var c = credits[i].split('-', 3); // aid, role, note
- if (!s[c[0]]) {
- q.push('a='+c[0]);
- s[c[0]] = 1;
+ var staff_data = getText(byId('staffdata')||{});
+ if (staff_data)
+ vnsStaffData = JSON.parse(staff_data);
+ else
+ vnsStaffData = {};
+
+ var src = byId('credits').value;
+ if(src) {
+ var credits = JSON.parse(src);
+ for (var i = 0; i < credits.length; i++) {
+ var aid = credits[i].aid;
+ if (vnsStaffData[aid])
+ vnsAdd(vnsStaffData[aid], credits[i].role, credits[i].note);
}
}
- if (q.length > 0)
- ajax('/xml/staff.xml?'+q.join(';'), function(hs) {
- var staff = hs.responseXML.getElementsByTagName('item');
- var l = {};
- for (var i = 0; i < staff.length; i++) {
- var aid = staff[i].getAttribute('aid');
- l[aid] = staff[i];
- }
- for (var i = 0; i < credits.length; i++) {
- var c = credits[i].split('-');
- if (l[c[0]])
- vnsAdd(l[c[0]], c[1], c.slice(2).join('-'));
- }
- vnsEmpty();
- }, 1);
- else
- vnsEmpty();
+ vnsEmpty();
onSubmit(byName(byId('maincontent'), 'form')[0], vnsSerialize);
@@ -2179,8 +2174,6 @@ function vnsLoad() {
}
function vnsAdd(staff, role, note) {
- var sid = staff.getAttribute('id');
- var aid = staff.getAttribute('aid');
var tbl = byId('credits_tbl');
var rlist = tag('select', {onchange:vnsSerialize});
@@ -2188,10 +2181,10 @@ function vnsAdd(staff, role, note) {
rlist.appendChild(tag('option', {value:staff_roles[i], selected:staff_roles[i]==role},
mt('_credit_'+staff_roles[i])));
- tbl.appendChild(tag('tr', {id:'vns_a'+aid},
+ tbl.appendChild(tag('tr', {id:'vns_a'+staff.aid},
tag('td', {'class':'tc_name'},
- tag('input', {type:'hidden', value:aid}),
- tag('a', {href:'/s'+sid}, staff.firstChild.nodeValue)),
+ tag('input', {type:'hidden', value:staff.aid}),
+ tag('a', {href:'/s'+staff.id}, staff.name)),
tag('td', {'class':'tc_role'}, rlist),
tag('td', {'class':'tc_note'}, tag('input', {type:'text', 'class':'text', value:note})),
tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:vnsDel}, mt('_js_remove')))
@@ -2234,11 +2227,10 @@ function vnsSerialize() {
continue;
var aid = byName(byClass(l[i], 'tc_name')[0], 'input')[0];
var role = byName(byClass(l[i], 'tc_role')[0], 'select')[0];
- var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0].value;
- note = note.replace(/\|\|\|+/g, '||');
- c.push (aid.value+'-'+role.value+'-'+note);
+ var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0];
+ c.push({ aid:Number(aid.value), role:role.value, note:note.value });
}
- byId('credits').value = c.join('|||');
+ byId('credits').value = JSON.stringify(c);
return true;
}
@@ -2253,7 +2245,8 @@ function vnsDel() {
}
function vnsFormAdd(item) {
- vnsAdd(item, 'staff', '');
+ var s = { id:item.getAttribute('id'), aid:item.getAttribute('aid'), name:item.firstChild.nodeValue };
+ vnsAdd(s, 'staff', '');
return '';
}
@@ -2268,43 +2261,26 @@ if(byId('jt_box_vn_staff'))
var vncImportData = [];
function vncLoad() {
- var cast = byId('seiyuu').value.split('|||');
- var q = []; // list of a=X parameters
- var s = {};
- for (var i = 0; i < cast.length && cast[i].length > 1; i++) {
- var c = cast[i].split('-', 3); // aid, char, note
- if (!s[c[0]]) {
- q.push('a='+c[0]);
- s[c[0]] = 1;
+ var src = byId('seiyuu').value;
+ if(src) {
+ var cast = JSON.parse(src);
+ for (var i = 0; i < cast.length; i++) {
+ var aid = cast[i].aid;
+ if (vnsStaffData[aid]) // vnsStaffData is filled by vnsLoad()
+ vncAdd(vnsStaffData[aid], cast[i].cid, cast[i].note);
}
}
- if (q.length > 0)
- ajax('/xml/staff.xml?'+q.join(';'), function(hs) {
- var seiyuu = hs.responseXML.getElementsByTagName('item');
- var s = {};
- for (var i = 0; i < seiyuu.length; i++) {
- var aid = seiyuu[i].getAttribute('aid');
- s[aid] = seiyuu[i];
- }
- for (var i = 0; i < cast.length; i++) {
- var c = cast[i].split('-');
- if (s[c[0]])
- vncAdd(s[c[0]], c[1], c.slice(2).join('-'));
- }
- vncEmpty();
- }, 1);
- else
- vncEmpty();
+ vncEmpty();
vncImportData = [];
var cast_import = byId('cast_import');
if (cast_import) {
- var imp_data = byId('castimpdata').value.split('|||');
- for (var i = 0; i < imp_data.length; i++) {
- var c = imp_data[i].split('-');
- vncImportData.push({cid:c[0],sid:c[1],aid:c[2],name:c.slice(3).join('-')});
- }
- byName(cast_import, 'a')[0].onclick = vncImport;
+ var imp_data = getText(byId('castimpdata')||{});
+ if (imp_data) {
+ vncImportData = JSON.parse(imp_data);
+ byName(cast_import, 'a')[0].onclick = vncImport;
+ } else
+ cast_import.style.display = 'none';
}
onSubmit(byName(byId('maincontent'), 'form')[0], vncSerialize);
@@ -2335,29 +2311,23 @@ function vncImport() {
for (var cid in c) {
if (!c.hasOwnProperty(cid))
continue;
- var s = document.createElement('item');
- s.appendChild(document.createTextNode(c[cid].name));
- s.setAttribute('id', c[cid].sid);
- s.setAttribute('aid', c[cid].aid);
- vncAdd(s, cid, '');
+ vncAdd({ id:c[cid].sid, aid:c[cid].aid, name:c[cid].name }, cid, '');
}
return false;
}
function vncAdd(seiyuu, chr, note) {
- var sid = seiyuu.getAttribute('id');
- var aid = seiyuu.getAttribute('aid');
var tbl = byId('cast_tbl');
var csel = byId('cast_chars').cloneNode(true);
csel.removeAttribute('id');
csel.value = chr;
- tbl.appendChild(tag('tr', {id:'vnc_a'+aid},
+ tbl.appendChild(tag('tr', {id:'vnc_a'+seiyuu.aid},
tag('td', {'class':'tc_char'}, csel),
tag('td', {'class':'tc_name'},
- tag('input', {type:'hidden', value:aid}),
- tag('a', {href:'/s'+sid}, seiyuu.firstChild.nodeValue)),
+ tag('input', {type:'hidden', value:seiyuu.aid}),
+ tag('a', {href:'/s'+seiyuu.id}, seiyuu.name)),
tag('td', {'class':'tc_note'}, tag('input', {type:'text', 'class':'text', value:note})),
tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:vncDel}, mt('_js_remove')))
));
@@ -2367,9 +2337,10 @@ function vncAdd(seiyuu, chr, note) {
function vncFormAdd(item) {
var chr = byId('cast_chars').value;
- if (chr)
- vncAdd(item, chr, '');
- else
+ if (chr) {
+ var s = { id:item.getAttribute('id'), aid:item.getAttribute('aid'), name:item.firstChild.nodeValue };
+ vncAdd(s, chr, '');
+ } else
alert(mt('_vnedit_cast_nochar'));
return '';
}
@@ -2408,11 +2379,10 @@ function vncSerialize() {
continue;
var aid = byName(byClass(l[i], 'tc_name')[0], 'input')[0];
var role = byName(byClass(l[i], 'tc_char')[0], 'select')[0];
- var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0].value;
- note = note.replace(/\|\|\|+/g, '||');
- c.push (aid.value+'-'+role.value+'-'+note);
+ var note = byName(byClass(l[i], 'tc_note')[0], 'input')[0];
+ c.push({ aid:Number(aid.value), cid:Number(role.value), note:note.value });
}
- byId('seiyuu').value = c.join('|||');
+ byId('seiyuu').value = JSON.stringify(c);
return true;
}
diff --git a/data/style.css b/data/style.css
index 4ddaea48..5dd6dcaf 100644
--- a/data/style.css
+++ b/data/style.css
@@ -274,7 +274,7 @@ p#searchtabs a:hover, p#searchtabs a.sel {
padding: 1px 5px 2px 5px;
background: $secbg$ url($_boxbg$) repeat;
}
-#q { width: 450px }
+#q { width: 600px }
@@ -576,8 +576,7 @@ div.staffbrowse { padding-bottom: 10px }
.staffdesc h2, .staffroles h2 { margin: 0; padding: 5px 0; }
.staffdesc { display: table; margin-bottom: 10px; }
.staffdesc p { padding: 0 5px; }
-.staffroles td { white-space: nowrap; padding-left: 20px; padding-right: 20px; }
-.staffroles td.tc1 { min-width: 100px; }
+.staffroles td { padding-left: 20px; padding-right: 20px; }
.staffroles td.tc2 { min-width: 100px; }
diff --git a/lib/VNDB/DB/Chars.pm b/lib/VNDB/DB/Chars.pm
index 50923941..15f00675 100644
--- a/lib/VNDB/DB/Chars.pm
+++ b/lib/VNDB/DB/Chars.pm
@@ -119,8 +119,11 @@ sub dbCharGet {
JOIN vn_rev vr ON vr.id = vs.vid
JOIN vn v ON v.latest = vs.vid
!W
- ORDER BY v.c_released, sa.name|,
- { 'cr.id IN(!l)' => [[ keys %r ]], $o{vid} ? ('v.id = ?' => $o{vid}) : () }
+ ORDER BY v.c_released, sa.name|, {
+ 's.hidden = FALSE' => 1,
+ 'cr.id IN(!l)' => [[ keys %r ]],
+ $o{vid} ? ('v.id = ?' => $o{vid}) : (),
+ }
)});
}
}
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index edb55377..9ba27bbd 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -159,7 +159,7 @@ sub dbVNGet {
JOIN staff_alias sa ON vs.aid = sa.id
JOIN staff_rev sr ON sr.id = sa.rid
JOIN staff s ON sr.id = s.latest
- WHERE vs.vid IN(!l)
+ WHERE s.hidden = FALSE AND vs.vid IN(!l)
ORDER BY vs.role ASC, sa.name ASC|,
[ keys %r ]
)});
@@ -173,7 +173,7 @@ sub dbVNGet {
JOIN chars c ON c.id = vs.cid
JOIN chars_rev cr ON cr.id = c.latest
JOIN chars_vns cv ON cv.cid = cr.id AND cv.vid = vr.vid
- WHERE vs.vid IN(!l)
+ WHERE s.hidden = FALSE AND vs.vid IN(!l)
ORDER BY cv.role, cr.name|,
[ keys %r ]
)});
diff --git a/lib/VNDB/Func.pm b/lib/VNDB/Func.pm
index 54acfe4f..3b315c93 100644
--- a/lib/VNDB/Func.pm
+++ b/lib/VNDB/Func.pm
@@ -6,10 +6,12 @@ use warnings;
use TUWF ':html';
use Exporter 'import';
use POSIX 'strftime', 'ceil', 'floor';
+use JSON::XS;
use VNDBUtil;
our @EXPORT = (@VNDBUtil::EXPORT, qw|
clearfloat cssicon tagscore mt minage fil_parse fil_serialize parenttags
childtags charspoil imgpath imgurl fmtvote
+ jsonEncode jsonDecode script_json
mtvoiced mtani mtvnlen mtrlstat mtvnlstat mtbloodt
|);
@@ -202,6 +204,28 @@ sub fmtvote {
}
+my $JSON; # cache
+
+# JSON::XS::encode_json converts input to utf8, whereas these functions
+# operate on wide character strings.
+sub jsonEncode ($) {
+ ($JSON ||= JSON::XS->new)->encode(@_);
+}
+
+sub jsonDecode ($) {
+ ($JSON ||= JSON::XS->new)->decode(@_);
+}
+
+# Insert JSON-encoded data as script, arguments: id, object
+sub script_json {
+ script id => $_[0], type => 'application/json';
+ my $js = jsonEncode $_[1];
+ $js =~ s/</\\u003C/g; # escape HTML tags like </script> and <!--
+ lit $js;
+ end;
+}
+
+
# mt() wrappers for data-dependent translation strings that have a special
# value for 'unknown'.
sub mtvoiced { !$_[0] ? mt '_unknown' : mt '_voiced_'.$_[0]; }
diff --git a/lib/VNDB/Handler/Staff.pm b/lib/VNDB/Handler/Staff.pm
index 41b4e331..38b280c8 100644
--- a/lib/VNDB/Handler/Staff.pm
+++ b/lib/VNDB/Handler/Staff.pm
@@ -5,6 +5,7 @@ use strict;
use warnings;
use TUWF qw(:html :xml xml_escape);
use VNDB::Func;
+use List::Util qw(first);
TUWF::register(
qr{s([1-9]\d*)(?:\.([1-9]\d*))?} => \&page,
@@ -43,7 +44,7 @@ sub page {
# return $_[0] ? sprintf '<img src="%s" />', imgurl(ch => $_[0]) : mt '_stdiff_image_none';
# }],
[ aliases => join => '<br />', split => sub {
- map sprintf('%s%s', $_->{name}, $_->{original} ? ' ('.$_->{original}.')' : ''), @{$_[0]};
+ map xml_escape(sprintf('%s%s', $_->{name}, $_->{original} ? ' ('.$_->{original}.')' : '')), @{$_[0]};
}],
);
}
@@ -100,7 +101,7 @@ sub page {
if (@{$s->{roles}}) {
h2 mt '_staff_credits';
- my $has_notes = grep { $_->{note} || $_->{name} ne $s->{name} } @{$s->{roles}};
+ my $has_notes = first { $_->{note} || $_->{name} ne $s->{name} } @{$s->{roles}};
table class => 'stripe staffroles';
thead;
Tr;
@@ -146,7 +147,7 @@ sub page {
}
if (@{$s->{cast}}) {
h2 mt '_staff_voiced';
- my $has_notes = grep { $_->{note} || $_->{name} ne $s->{name} } @{$s->{cast}};
+ my $has_notes = first { $_->{note} || $_->{name} ne $s->{name} } @{$s->{cast}};
table class => 'stripe staffroles';
thead;
Tr;
@@ -195,8 +196,10 @@ sub edit {
my %b4 = !$sid ? () : (
(map { $_ => $s->{$_} } qw|aid name original gender lang desc l_wp ihid ilock|),
- aliases => join('|||', map sprintf('%d,%s,%s', $_->{id}, $_->{name}, $_->{original}),
- sort { $a->{name} <=> $b->{name} } @{$s->{aliases}}),
+ aliases => jsonEncode [
+ map +{ aid => $_->{id}, name => $_->{name}, orig => $_->{original} },
+ sort { $a->{name} <=> $b->{name} } @{$s->{aliases}}
+ ],
);
my $frm;
@@ -217,25 +220,29 @@ sub edit {
{ post => 'ilock', required => 0 },
);
push @{$frm->{_err}}, 'badeditsum' if !$frm->{editsum} || lc($frm->{editsum}) eq lc($frm->{desc});
- my @aliases = map { /^(\d+),([^,]*),(.*)$/ ? [ $1, $2, $3 ]: () } split /\|\|\|/, $frm->{aliases};
- for my $a (@aliases) {
- # check for empty aliases
- if($a->[1] =~ /^\s*$/) {
- push @{$frm->{_err}}, ['alias_name', 'required'];
- last;
+
+ my $aliases = eval { jsonDecode $frm->{aliases} };
+ push @{$frm->{_err}}, [ 'aliases', 'template', 'json' ] if $@;
+ if(!$frm->{_err}) {
+ for my $a (@$aliases) {
+ # check for empty aliases
+ if($a->{name} =~ /^\s*$/) {
+ push @{$frm->{_err}}, ['alias_name', 'required'];
+ last;
+ }
}
}
if(!$frm->{_err}) {
# parse and normalize
- $frm->{aliases} = join('|||', map sprintf('%d,%s,%s', @$_), @aliases);
+ $frm->{aliases} = jsonEncode $aliases;
$frm->{ihid} = $frm->{ihid} ?1:0;
$frm->{ilock} = $frm->{ilock}?1:0;
return $self->resRedirect("/s$sid", 'post')
- if $sid && !grep $frm->{$_} ne $b4{$_}, keys %b4;
+ if $sid && !first { $frm->{$_} ne $b4{$_} } keys %b4;
}
if(!$frm->{_err}) {
- $frm->{aliases} = \@aliases;
+ $frm->{aliases} = [ map [ @{$_}{qw|aid name orig|} ], @$aliases ];
my $nrev = $self->dbItemEdit ('s' => $sid ? $s->{cid} : undef, %$frm);
return $self->resRedirect("/s$nrev->{iid}.$nrev->{rev}", 'post');
}
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index caf7e8a3..a7900628 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -88,8 +88,14 @@ sub edit {
my %b4 = !$vid ? () : (
(map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai image img_nsfw ihid ilock|),
- credits => join('|||', map join('-', $_->{aid}, $_->{role}, $_->{note}), @{$v->{credits}}),
- seiyuu => join('|||', map join('-', $_->{aid}, $_->{cid}, $_->{note}), @{$v->{seiyuu}}),
+ credits => jsonEncode [
+ map { my $c = $_; +{ map { $_ => $c->{$_} } qw|aid role note| } }
+ sort { $a->{aid} <=> $b->{aid} || $a->{role} cmp $b->{role} } @{$v->{credits}}
+ ],
+ seiyuu => jsonEncode [
+ map { my $c = $_; +{ map { $_ => $c->{$_} } qw|aid cid note| } }
+ sort { $a->{aid} <=> $b->{aid} || $a->{cid} <=> $b->{cid} } @{$v->{seiyuu}}
+ ],
anime => join(' ', sort { $a <=> $b } map $_->{id}, @{$v->{anime}}),
vnrelations => join('|||', map $_->{relation}.','.$_->{id}.','.($_->{official}?1:0).','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}),
screenshots => join(' ', map sprintf('%d,%d,%d', $_->{id}, $_->{nsfw}?1:0, $_->{rid}), @{$v->{screenshots}}),
@@ -124,13 +130,35 @@ sub edit {
# handle image upload
$frm->{image} = _uploadimage($self, $frm) if !$nosubmit;
+ my (@credits, @seiyuu);
+ if(!$nosubmit && !$frm->{_err}) {
+ eval { # catch json decoding errors
+ my $raw_c = $frm->{credits} ? jsonDecode $frm->{credits} : [];
+ my $raw_s = $frm->{seiyuu} ? jsonDecode $frm->{seiyuu} : [];
+
+ # check for duplicate credits
+ my $last_c;
+ for my $c (sort { $a->{aid} <=> $b->{aid} || $a->{role} cmp $b->{role} } @$raw_c) {
+ # discard entries with identical name & role
+ next if $last_c->{aid} == $c->{aid} && $last_c->{role} eq $c->{role};
+ push @credits, $c;
+ $last_c = $c;
+ }
+
+ my $last_s;
+ for my $s (sort { $a->{aid} <=> $b->{aid} || $a->{cid} <=> $b->{cid} } @$raw_s) {
+ next if $last_s->{aid} == $s->{aid} && $last_s->{cid} == $s->{cid};
+ push @seiyuu, $s;
+ $last_s = $s;
+ }
+ };
+ push @{$frm->{_err}}, [ 'credits', 'template', 'json' ] if $@;
+ }
if(!$nosubmit && !$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 { /^([a-z]+),([0-9]+),([01]),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3, $4 ] : () } split /\|\|\|/, $frm->{vnrelations} ];
my $screenshots = [ map /^[0-9]+,[01],[0-9]+$/ ? [split /,/] : (), split / +/, $frm->{screenshots} ];
- my $credits = [ map { /^(\d+)-([^-]+)-(.*)$/ ? [ $1, $2, $3 ]: () } split /\|\|\|/, $frm->{credits} ];
- my $seiyuu = [ map { /^(\d+)-(\d+)-(.*)$/ ? [ $1, $2, $3 ]: () } split /\|\|\|/, $frm->{seiyuu} ];
$frm->{ihid} = $frm->{ihid}?1:0;
$frm->{ilock} = $frm->{ilock}?1:0;
@@ -139,24 +167,8 @@ sub edit {
$frm->{vnrelations} = join '|||', map $_->[0].','.$_->[1].','.($_->[2]?1:0).','.$_->[3], 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;
-
- # check for duplicate credits
- my($checked_c, $last_c) = ([], []);
- for my $c (sort { $a->[0] <=> $b->[0] || $a->[1] cmp $b->[1] } @$credits) {
- # discard entries with identical name & role
- next if $last_c->[0] == $c->[0] && $last_c->[1] eq $c->[1];
- push @$checked_c, $c;
- $last_c = $c;
- }
- $frm->{credits} = join '|||', map sprintf('%d-%s-%s', @$_), @$checked_c;
-
- my($checked_s, $last_s) = ([], []);
- for my $s (sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @$seiyuu) {
- next if $last_s->[0] == $s->[0] && $last_s->[1] == $s->[1];
- push @$checked_s, $s;
- $last_s = $s;
- }
- $frm->{seiyuu} = join '|||', map sprintf('%d-%d-%s', @$_), @$checked_s;
+ $frm->{credits} = jsonEncode \@credits;
+ $frm->{seiyuu} = jsonEncode \@seiyuu;
# weed out duplicate aliases
my %alias;
@@ -173,8 +185,8 @@ sub edit {
# perform the edit/add
my $nrev = $self->dbItemEdit(v => $vid ? $v->{cid} : undef,
(map { $_ => $frm->{$_} } qw|title original image alias desc length l_wp l_encubed l_renai editsum img_nsfw ihid ilock|),
- credits => $checked_c,
- seiyuu => $checked_s,
+ credits => [ map [ @{$_}{qw|aid role note|} ], @credits ],
+ seiyuu => [ map [ @{$_}{qw|aid cid note|} ], @seiyuu ],
anime => [ keys %$anime ],
relations => $relations,
screenshots => $screenshots,
@@ -289,6 +301,13 @@ sub _form {
vn_staff => [ mt('_vnedit_staff'),
[ hidden => short => 'credits' ],
[ static => nolabel => 1, content => sub {
+ # propagate staff ids and names to javascript
+ my %staff_data;
+ for my $c (@{$v->{credits}}, @{$v->{seiyuu}}) {
+ $staff_data{$c->{aid}} //= { map +($_ => $c->{$_}), qw|id aid name| };
+ }
+ script_json staffdata => \%staff_data if %staff_data;
+
div class => 'warning';
lit mt '_vnedit_staff_msg';
end;
@@ -309,11 +328,11 @@ sub _form {
# would be empty anyway.
@{$chars} ? (vn_cast => [ mt('_vnedit_cast'),
[ hidden => short => 'seiyuu' ],
- [ hidden => short => 'castimpdata', value => do {
- join '|||', map join('-', $_->{cid}, $_->{sid}, $_->{aid}, $_->{name}), @$import;
- }],
[ static => nolabel => 1, content => sub {
if (@$import) {
+ script_json castimpdata => [
+ map { my $c = $_; +{ map { $_ => $c->{$_} } qw|cid sid aid name| } } @$import
+ ];
div id => 'cast_import';
a href => '#', title => mt('_vnedit_cast_import_title'), mt '_vnedit_cast_import';
end;
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index ddf605f4..89b48a91 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -710,14 +710,14 @@ sub _revision {
}],
[ credits => join => '<br />', split => sub {
my @r = map sprintf('<a href="/s%d" title="%s">%s</a> [%s]%s',
- $_->{id}, xml_escape($_->{original}||$_->{name}), xml_escape($_->{name}), mt("_credit_$_->{role}"), $_->{note} ? ' ['.shorten($_->{note}, 20).']' : ''), sort { $a->{id} <=> $b->{id} } @{$_[0]};
+ $_->{id}, xml_escape($_->{original}||$_->{name}), xml_escape($_->{name}), mt("_credit_$_->{role}"), $_->{note} ? ' ['.xml_escape(shorten($_->{note}, 20)).']' : ''), sort { $a->{id} <=> $b->{id} } @{$_[0]};
return @r ? @r : (mt '_revision_empty');
}],
[ seiyuu => join => '<br />', split => sub {
my @r = map sprintf('<a href="/s%d" title="%s">%s</a> %s%s',
$_->{id}, xml_escape($_->{original}||$_->{name}), xml_escape($_->{name}),
mt('_staff_as', xml_escape($_->{cname})),
- $_->{note} ? ' ['.shorten($_->{note}, 20).']' : ''),
+ $_->{note} ? ' ['.xml_escape(shorten($_->{note}, 20)).']' : ''),
sort { $a->{id} <=> $b->{id} } @{$_[0]};
return @r ? @r : (mt '_revision_empty');
}],
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index 8474f9c7..d535472d 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -137,6 +137,7 @@ sub htmlHiddenMessage {
my $editsum = $type eq 'v' ? $self->dbVNGet(id => $obj->{id}, what => 'changes')->[0]{comments}
: $type eq 'r' ? $self->dbReleaseGet(id => $obj->{id}, what => 'changes')->[0]{comments}
: $type eq 'c' ? $self->dbCharGet(id => $obj->{id}, what => 'changes')->[0]{comments}
+ : $type eq 's' ? $self->dbStaffGet(id => $obj->{id}, what => 'changes')->[0]{comments}
: $self->dbProducerGet(id => $obj->{id}, what => 'changes')->[0]{comments};
div class => 'mainbox';
h1 $obj->{title}||$obj->{name};
diff --git a/util/sql/staff.sql b/util/sql/staff.sql
index c4ff41ba..7c99971e 100644
--- a/util/sql/staff.sql
+++ b/util/sql/staff.sql
@@ -56,3 +56,5 @@ ALTER TABLE vn_seiyuu ADD FOREIGN KEY (vid) REFERENCES vn_rev
CREATE INDEX vn_staff_vid ON vn_staff (vid);
CREATE INDEX vn_staff_aid ON vn_staff (aid);
+
+CREATE TRIGGER hidlock_update BEFORE UPDATE ON staff FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();