diff options
Diffstat (limited to 'lib/VNWeb/Chars/Page.pm')
-rw-r--r-- | lib/VNWeb/Chars/Page.pm | 192 |
1 files changed, 125 insertions, 67 deletions
diff --git a/lib/VNWeb/Chars/Page.pm b/lib/VNWeb/Chars/Page.pm index 8a9966ae..e6ffc7e7 100644 --- a/lib/VNWeb/Chars/Page.pm +++ b/lib/VNWeb/Chars/Page.pm @@ -7,27 +7,57 @@ use VNWeb::Images::Lib qw/image_ enrich_image_obj/; sub enrich_seiyuu { my($vid, @chars) = @_; enrich seiyuu => id => cid => sub { sql ' - SELECT DISTINCT vs.cid, sa.id, sa.name, sa.original, vs.note + SELECT DISTINCT vs.cid, sa.id, sa.title, sa.sorttitle, vs.note FROM vn_seiyuu vs - JOIN staff_alias sa ON sa.aid = vs.aid - WHERE vs.cid IN', $_, $vid ? ('AND vs.id =', \$vid) : (), ' - ORDER BY sa.name' + ', $vid ? () : ('JOIN vn v ON v.id = vs.id'), ' + JOIN', staff_aliast, 'sa ON sa.aid = vs.aid + WHERE ', $vid ? ('vs.id =', \$vid) : ('NOT v.hidden'), 'AND vs.cid IN', $_, ' + ORDER BY sa.sorttitle' }, @chars; } +sub sql_trait_overrides { + sql '( + WITH RECURSIVE trait_overrides (tid, spoil, color, childs, lvl) AS ( + SELECT tid, spoil, color, childs, 0 FROM users_prefs_traits WHERE id =', \auth->uid, ' + UNION ALL + SELECT tp.id, x.spoil, x.color, true, lvl+1 + FROM trait_overrides x + JOIN traits_parents tp ON tp.parent = x.tid + WHERE x.childs + ) SELECT DISTINCT ON(tid) tid, spoil, color FROM trait_overrides ORDER BY tid, lvl + )'; +} sub enrich_item { my($c) = @_; enrich_image_obj image => $c; - enrich_merge vid => 'SELECT id AS vid, title, original FROM vn WHERE id IN', $c->{vns}; - enrich_merge rid => 'SELECT id AS rid, title AS rtitle, original AS roriginal FROM releases WHERE id IN', grep $_->{rid}, $c->{vns}->@*; - enrich_merge tid => - 'SELECT t.id AS tid, t.name, t.sexual, coalesce(g.id, t.id) AS group, coalesce(g.name, t.name) AS groupname, coalesce(g.order,0) AS order - FROM traits t LEFT JOIN traits g ON t.group = g.id WHERE t.id IN', $c->{traits}; - - $c->{vns} = [ sort { $a->{title} cmp $b->{title} || $a->{vid} <=> $b->{vid} || ($a->{rid}||999999) <=> ($b->{rid}||999999) } $c->{vns}->@* ]; + enrich_merge vid => sql('SELECT id AS vid, title, sorttitle, c_released AS vn_released FROM', vnt, 'v WHERE id IN'), $c->{vns}; + enrich_merge rid => sql('SELECT id AS rid, title AS rtitle, released AS rel_released FROM', releasest, 'r WHERE id IN'), grep $_->{rid}, $c->{vns}->@*; + + # Even with trait overrides, we'll want to see the raw data in revision diffs, + # so fetch the raw spoil as a separate column and do filtering/processing later. + enrich_merge tid => sub { sql ' + SELECT t.id AS tid, t.name, t.hidden, t.locked, t.applicable, t.sexual, x.spoil AS override, x.color + , coalesce(g.id, t.id) AS group, coalesce(g.name, t.name) AS groupname, coalesce(g.gorder,0) AS order + FROM traits t + LEFT JOIN traits g ON t.gid = g.id + LEFT JOIN', sql_trait_overrides(), 'x ON x.tid = t.id + WHERE t.id IN', $_ + }, $c->{traits}; + + $c->{vns} = [ sort { $a->{vn_released} <=> $b->{vn_released} || ($a->{rel_released}||0) <=> ($b->{rel_released}||0) + || $a->{sorttitle} cmp $b->{sorttitle} || idcmp($a->{vid}, $b->{vid}) || idcmp($a->{rid}||'r999999', $b->{rid}||'r999999') } $c->{vns}->@* ]; $c->{traits} = [ sort { $a->{order} <=> $b->{order} || $a->{groupname} cmp $b->{groupname} || $a->{name} cmp $b->{name} } $c->{traits}->@* ]; + + $c->{quotes} = tuwf->dbAlli(' + SELECT q.vid, q.id, q.score, q.quote,', sql_totime('q.added'), 'AS added, q.addedby + FROM quotes q + WHERE NOT q.hidden AND vid IN', [map $_->{vid}, $c->{vns}->@*], 'AND q.cid =', \$c->{id}, ' + ORDER BY q.score DESC, q.quote + '); + enrich_merge id => sql('SELECT id, vote FROM quotes_votes WHERE uid =', \auth->uid, 'AND id IN'), $c->{quotes} if auth; } @@ -36,27 +66,29 @@ sub enrich_item { sub fetch_chars { my($vid, $where) = @_; my $l = tuwf->dbAlli(' - SELECT id, name, original, alias, "desc", gender, spoil_gender, b_month, b_day, s_bust, s_waist, s_hip, height, weight, bloodt, cup_size, age, image - FROM chars WHERE NOT hidden AND (', $where, ') - ORDER BY name + SELECT id, title, alias, description, gender, spoil_gender, b_month, b_day, s_bust, s_waist, s_hip, height, weight, bloodt, cup_size, age, image + FROM', charst, 'c WHERE NOT hidden AND (', $where, ') + ORDER BY sorttitle '); enrich vns => id => id => sub { sql ' - SELECT cv.id, cv.vid, cv.rid, cv.spoil, cv.role, v.title, v.original, r.title AS rtitle, r.original AS roriginal + SELECT cv.id, cv.vid, cv.rid, cv.spoil, cv.role, v.title, r.title AS rtitle FROM chars_vns cv - JOIN vn v ON v.id = cv.vid - LEFT JOIN releases r ON r.id = cv.rid + JOIN', vnt, 'v ON v.id = cv.vid + LEFT JOIN', releasest, 'r ON r.id = cv.rid WHERE cv.id IN', $_, $vid ? ('AND cv.vid =', \$vid) : (), ' - ORDER BY v.title, cv.vid, cv.rid NULLS LAST' + ORDER BY v.c_released, r.released, v.sorttitle, cv.vid, cv.rid NULLS LAST' }, $l; enrich traits => id => id => sub { sql ' - SELECT ct.id, ct.tid, ct.spoil, t.name, t.sexual, coalesce(g.id, t.id) AS group, coalesce(g.name, t.name) AS groupname, coalesce(g.order,0) AS order + SELECT ct.id, ct.tid, ct.spoil, x.spoil AS override, x.color, ct.lie, t.name, t.hidden, t.locked, t.sexual + , coalesce(g.id, t.id) AS group, coalesce(g.name, t.name) AS groupname, coalesce(g.gorder,0) AS order FROM chars_traits ct JOIN traits t ON t.id = ct.tid - LEFT JOIN traits g ON t.group = g.id - WHERE ct.id IN', $_, ' - ORDER BY g.order NULLS FIRST, coalesce(g.name, t.name), t.name' + LEFT JOIN traits g ON t.gid = g.id + LEFT JOIN', sql_trait_overrides(), 'x ON x.tid = ct.tid + WHERE x.spoil IS DISTINCT FROM 1+1+1 AND ct.id IN', $_, ' + ORDER BY g.gorder NULLS FIRST, coalesce(g.name, t.name), t.name' }, $l; enrich_seiyuu $vid, $l; @@ -67,40 +99,43 @@ sub fetch_chars { sub _rev_ { my($c) = @_; - revision_ c => $c, \&enrich_item, + revision_ $c, \&enrich_item, [ name => 'Name' ], - [ original => 'Original name' ], + [ latin => 'Name (latin)' ], [ alias => 'Aliases' ], - [ desc => 'Description' ], + [ description=> 'Description' ], [ gender => 'Sex', fmt => \%GENDER ], [ spoil_gender=> 'Sex (spoiler)',fmt => \%GENDER ], [ b_month => 'Birthday/month',empty => 0 ], [ b_day => 'Birthday/day', empty => 0 ], [ s_bust => 'Bust', empty => 0 ], [ s_waist => 'Waist', empty => 0 ], - [ s_hip => 'Hip', empty => 0 ], + [ s_hip => 'Hips', empty => 0 ], [ height => 'Height', empty => 0 ], [ weight => 'Weight', ], [ bloodt => 'Blood type', fmt => \%BLOOD_TYPE ], [ cup_size => 'Cup size', fmt => \%CUP_SIZE ], [ age => 'Age', ], [ main => 'Instance of', empty => 0, fmt => sub { - my $c = tuwf->dbRowi('SELECT id, name, original FROM chars WHERE id =', \$_); - a_ href => "/c$c->{id}", title => $c->{name}, "c$c->{id}" + my $c = tuwf->dbRowi('SELECT id, title FROM', charst, 'c WHERE id =', \$_); + a_ href => "/$c->{id}", title => $c->{title}[1], $c->{id} } ], [ main_spoil => 'Spoiler', fmt => sub { txt_ fmtspoil $_ } ], [ image => 'Image', fmt => sub { image_ $_ } ], [ vns => 'Visual novels', fmt => sub { - a_ href => "/v$_->{vid}", title => $_->{original}||$_->{title}, "v$_->{vid}"; + a_ href => "/$_->{vid}", tlang(@{$_->{title}}[0,1]), title => $_->{title}[1], $_->{vid}; if($_->{rid}) { - txt_ ' ['; a_ href => "/r$_->{rid}", "r$_->{rid}"; txt_ ']'; + txt_ ' ['; a_ href => "/$_->{rid}", $_->{rid}; txt_ ']'; } txt_ " $CHAR_ROLE{$_->{role}}{txt} (".fmtspoil($_->{spoil}).')'; } ], [ traits => 'Traits', fmt => sub { - b_ class => 'grayedout', "$_->{groupname} / " if $_->{group} != $_->{tid}; - a_ href => "/i$_->{tid}", $_->{name}; - txt_ ' ('.fmtspoil($_->{spoil}).')'; + small_ "$_->{groupname} / " if $_->{group} ne $_->{tid}; + a_ href => "/$_->{tid}", $_->{name}; + txt_ ' ('.fmtspoil($_->{spoil}).($_->{lie} ? ', lie':'').')'; + b_ ' (awaiting moderation)' if $_->{hidden} && !$_->{locked}; + b_ ' (trait deleted)' if $_->{hidden} && $_->{locked}; + b_ ' (not applicable)' if !$_->{applicable}; } ], } @@ -110,23 +145,26 @@ sub chartable_ { my($c, $link, $sep, $vn) = @_; my $view = viewget; + my @visvns = grep $_->{spoil} <= $view->{spoilers}, $c->{vns}->@*; + div_ mkclass(chardetails => 1, charsep => $sep), sub { - div_ class => 'charimg', sub { image_ $c->{image}, alt => $c->{name} }; + div_ class => 'charimg', sub { image_ $c->{image}, alt => $c->{title}[1] }; table_ class => 'stripe', sub { thead_ sub { tr_ sub { td_ colspan => 2, sub { $link - ? a_ href => "/c$c->{id}", style => 'margin-right: 10px; font-weight: bold', $c->{name} - : b_ style => 'margin-right: 10px', $c->{name}; - b_ class => 'grayedout', style => 'margin-right: 10px', $c->{original} if $c->{original}; - abbr_ class => "icons gen $c->{gender}", title => $GENDER{$c->{gender}}, '' if $c->{gender} ne 'unknown'; + ? a_ href => "/$c->{id}", style => 'margin-right: 10px; font-weight: bold', tlang($c->{title}[0], $c->{title}[1]), $c->{title}[1] + : span_ style => 'margin-right: 10px', tlang($c->{title}[0], $c->{title}[1]), $c->{title}[1]; + small_ style => 'margin-right: 10px', tlang($c->{title}[2], $c->{title}[3]), $c->{title}[3] if $c->{title}[3] ne $c->{title}[1]; + abbr_ class => "icon-gen-$c->{gender}", title => $GENDER{$c->{gender}}, '' if $c->{gender} ne 'unknown'; if($view->{spoilers} == 2 && defined $c->{spoil_gender}) { txt_ '('; - abbr_ class => "icons gen $c->{spoil_gender}", title => $GENDER{$c->{spoil_gender}}, '' if $c->{spoil_gender} ne 'unknown'; + abbr_ class => "icon-gen-$c->{spoil_gender}", title => $GENDER{$c->{spoil_gender}}, '' if $c->{spoil_gender} ne 'unknown'; txt_ 'unknown' if $c->{spoil_gender} eq 'unknown'; spoil_ 2; txt_ ')'; } span_ $BLOOD_TYPE{$c->{bloodt}} if $c->{bloodt} ne 'unknown'; + debug_ $c; }}}; tr_ sub { @@ -155,22 +193,28 @@ sub chartable_ { } if defined $c->{age}; my @groups; - for(grep $_->{spoil} <= $view->{spoilers} && (!$_->{sexual} || $view->{traits_sexual}), $c->{traits}->@*) { - push @groups, $_ if !@groups || $groups[$#groups]{group} != $_->{group}; + for(grep !$_->{hidden} && ($_->{override}//$_->{spoil}) <= $view->{spoilers} && (!$_->{sexual} || $view->{traits_sexual}), $c->{traits}->@*) { + push @groups, $_ if !@groups || $groups[$#groups]{group} ne $_->{group}; push $groups[$#groups]{traits}->@*, $_; } - tr_ class => "trait_group_i$_->{group}", sub { - td_ class => 'key', sub { a_ href => "/i$_->{group}", $_->{groupname} }; - td_ sub { join_ ', ', sub { a_ href => "/i$_->{tid}", $_->{name}; spoil_ $_->{spoil} }, $_->{traits}->@* }; + tr_ class => "trait_group_$_->{group}", sub { + td_ class => 'key', sub { a_ href => "/$_->{group}", $_->{groupname} }; + td_ sub { join_ ', ', sub { + a_ href => "/$_->{tid}", mkclass( + $_->{color} ? ($_->{color}, $_->{color} =~ /standout|grayedout/ ? 1 : 0) : (), + lie => $_->{lie} && (($_->{override}//1) <= 0 || $view->{spoilers} >= 2), + ), ($_->{color}//'') =~ /^#/ ? (style => "color: $_->{color}") : (), + $_->{name}; + spoil_ $_->{spoil}; + }, $_->{traits}->@* }; } for @groups; - my @visvns = grep $_->{spoil} <= $view->{spoilers}, $c->{vns}->@*; tr_ sub { td_ class => 'key', $vn ? 'Releases' : 'Visual novels'; td_ sub { my @vns; for(@visvns) { - push @vns, $_ if !@vns || $vns[$#vns]{vid} != $_->{vid}; + push @vns, $_ if !@vns || $vns[$#vns]{vid} ne $_->{vid}; push $vns[$#vns]{rels}->@*, $_; } join_ \&br_, sub { @@ -178,18 +222,18 @@ sub chartable_ { # Just a VN link, no releases if(!$vn && $v->{rels}->@* == 1 && !$v->{rels}[0]{rid}) { txt_ $CHAR_ROLE{$v->{role}}{txt}.' - '; - a_ href => "/v$v->{vid}", title => $v->{original}||$v->{title}, $v->{title}; + a_ href => "/$v->{vid}", tattr $v; spoil_ $v->{spoil}; # With releases } else { - a_ href => "/v$v->{vid}", title => $v->{original}||$v->{title}, $v->{title} if !$vn; + a_ href => "/$v->{vid}", tattr $v if !$vn; br_ if !$vn; join_ \&br_, sub { - b_ class => 'grayedout', '> '; + small_ '> '; txt_ $CHAR_ROLE{$_->{role}}{txt}.' - '; if($_->{rid}) { - b_ class => 'grayedout', "r$_->{rid}:"; - a_ href => "/r$_->{rid}", title => $_->{roriginal}||$_->{rtitle}, $_->{rtitle}; + small_ "$_->{rid}:"; + a_ href => "/$_->{rid}", tattr $_->{rtitle}; } else { txt_ 'All other releases'; } @@ -204,7 +248,7 @@ sub chartable_ { td_ class => 'key', 'Voiced by'; td_ sub { join_ \&br_, sub { - a_ href => "/s$_->{id}", title => $_->{original}||$_->{name}, $_->{name}; + a_ href => "/$_->{id}", tattr $_; txt_ " ($_->{note})" if $_->{note}; }, $c->{seiyuu}->@*; }; @@ -213,17 +257,30 @@ sub chartable_ { tr_ class => 'nostripe', sub { td_ colspan => 2, class => 'chardesc', sub { h2_ 'Description'; - p_ sub { lit_ bb_format $c->{desc}, replacespoil => $view->{spoilers} != 2, keepspoil => $view->{spoilers} == 2 }; + p_ sub { lit_ bb_format $c->{description}, replacespoil => $view->{spoilers} != 2, keepspoil => $view->{spoilers} == 2 }; }; - } if $c->{desc}; + } if $c->{description}; + }; }; clearfloat_; + + my %visvns = map +($_->{vid}, 1), @visvns; + my @quotes = grep $visvns{$_->{vid}}, $c->{quotes}->@*; + div_ class => 'charquotes', sub { + h2_ 'Quotes'; + table_ sub { + tr_ sub { + td_ sub { VNWeb::VN::Quotes::votething_($_) }; + td_ $_->{quote}; + } for @quotes; + }; + } if @quotes; } TUWF::get qr{/$RE{crev}} => sub { - my $c = db_entry c => tuwf->capture('id'), tuwf->capture('rev'); + my $c = db_entry tuwf->captures('id','rev'); return tuwf->resNotFound if !$c; enrich_item $c; @@ -241,38 +298,39 @@ TUWF::get qr{/$RE{crev}} => sub { my $max_spoil = max( $inst_maxspoil||0, - (map $_->{spoil}, $c->{traits}->@*), + (map $_->{override}//($_->{lie}?2:$_->{spoil}), grep !$_->{hidden} && !(($_->{override}//0) == 3), $c->{traits}->@*), (map $_->{spoil}, $c->{vns}->@*), defined $c->{spoil_gender} ? 2 : 0, - $c->{desc} =~ /\[spoiler\]/i ? 2 : 0, # crude + $c->{description} =~ /\[spoiler\]/i ? 2 : 0, # crude ); # Only display the sexual traits toggle when there are sexual traits within the current spoiler level. - my $has_sex = grep $_->{spoil} <= $view->{spoilers} && $_->{sexual}, map $_->{traits}->@*, $c, @$inst; + my $has_sex = grep !$_->{hidden} && $_->{sexual} && ($_->{override}//$_->{spoil}) <= $view->{spoilers}, map $_->{traits}->@*, $c, @$inst; - framework_ title => $c->{name}, index => !tuwf->capture('rev'), type => 'c', dbobj => $c, hiddenmsg => 1, + $c->{title} = titleprefs_swap tuwf->dbVali('SELECT c_lang FROM chars WHERE id =', \$c->{id}), @{$c}{qw/ name latin /}; + framework_ title => $c->{title}[1], index => !tuwf->capture('rev'), dbobj => $c, hiddenmsg => 1, og => { - description => bb_format($c->{desc}, text => 1), - image => $c->{image} && $c->{image}{votecount} && !$c->{image}{sexual} && !$c->{image}{violence} ? tuwf->imgurl($c->{image}{id}) : undef, + description => bb_format($c->{description}, text => 1), + image => $c->{image} && $c->{image}{votecount} && !$c->{image}{sexual} && !$c->{image}{violence} ? imgurl($c->{image}{id}) : undef, }, sub { _rev_ $c if tuwf->capture('rev'); - div_ class => 'mainbox', sub { - itemmsg_ c => $c; - h1_ sub { txt_ $c->{name}; debug_ $c }; - h2_ class => 'alttitle', $c->{original} if length $c->{original}; + article_ sub { + itemmsg_ $c; + h1_ tlang(@{$c->{title}}[0,1]), $c->{title}[1]; + h2_ class => 'alttitle', tlang(@{$c->{title}}[2,3]), $c->{title}[3] if $c->{title}[3] && $c->{title}[3] ne $c->{title}[1]; p_ class => 'chardetailopts', sub { if($max_spoil) { a_ mkclass(checked => $view->{spoilers} == 0), href => '?view='.viewset(spoilers=>0, traits_sexual => $view->{traits_sexual}), 'Hide spoilers'; a_ mkclass(checked => $view->{spoilers} == 1), href => '?view='.viewset(spoilers=>1, traits_sexual => $view->{traits_sexual}), 'Show minor spoilers'; a_ mkclass(standout =>$view->{spoilers} == 2), href => '?view='.viewset(spoilers=>2, traits_sexual => $view->{traits_sexual}), 'Spoil me!' if $max_spoil == 2; } - b_ class => 'grayedout', ' | ' if $has_sex && $max_spoil; + small_ ' | ' if $has_sex && $max_spoil; a_ mkclass(checked => $view->{traits_sexual}), href => '?view='.viewset(spoilers => $view->{spoilers}, traits_sexual=>!$view->{traits_sexual}), 'Show sexual traits' if $has_sex; }; chartable_ $c; }; - div_ class => 'mainbox', sub { + article_ sub { h1_ 'Other instances'; chartable_ $_, 1, $_ != $inst->[0] for @$inst; } if @$inst; |