diff options
author | morkt <> | 2015-01-02 10:22:25 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2015-01-02 10:22:25 +0100 |
commit | 228cb96969a33a60c40dd4991a394460652010af (patch) | |
tree | 9d69e00562ae545b79ca8a899d2439c4800133d7 | |
parent | 03585d77637a7052d0af2cdb1e4be66358412b6a (diff) |
staff: Fix deleting of staff + use JSON to pass data + minor fixes
-rw-r--r-- | data/lang.txt | 3 | ||||
-rw-r--r-- | data/script.js | 150 | ||||
-rw-r--r-- | data/style.css | 5 | ||||
-rw-r--r-- | lib/VNDB/DB/Chars.pm | 7 | ||||
-rw-r--r-- | lib/VNDB/DB/VN.pm | 4 | ||||
-rw-r--r-- | lib/VNDB/Func.pm | 24 | ||||
-rw-r--r-- | lib/VNDB/Handler/Staff.pm | 35 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNEdit.pm | 73 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNPage.pm | 4 | ||||
-rw-r--r-- | lib/VNDB/Util/CommonHTML.pm | 1 | ||||
-rw-r--r-- | util/sql/staff.sql | 2 |
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(); |