diff options
Diffstat (limited to 'lib/VNWeb/Images/Lib.pm')
-rw-r--r-- | lib/VNWeb/Images/Lib.pm | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/VNWeb/Images/Lib.pm b/lib/VNWeb/Images/Lib.pm new file mode 100644 index 00000000..0170d37e --- /dev/null +++ b/lib/VNWeb/Images/Lib.pm @@ -0,0 +1,166 @@ +package VNWeb::Images::Lib; + +use VNWeb::Prelude; +use Exporter 'import'; + +our @EXPORT = qw/enrich_image validate_token image_flagging_display image_hidden image_ enrich_image_obj/; + + +my @SEX = qw/Safe Suggestive Explicit/; +my @VIO = qw/Tame Violent Brutal /; + +# Enrich images so that they match the format expected by the 'ImageResult' Elm +# API response. +# +# Also adds signed tokens to the image list - indicating that the current user +# is permitted to vote on these images. These tokens ensure that non-moderators +# can only vote on images that they have been randomly assigned, thus +# preventing possible abuse when a single person uses multiple accounts to +# influence the rating of a single image. +sub enrich_image { + my($canvote, $l) = @_; + enrich_merge id => sub { sql q{ + SELECT i.id, i.width, i.height, i.c_votecount AS votecount + , i.c_sexual_avg::real/100 AS sexual_avg, i.c_sexual_stddev::real/100 AS sexual_stddev + , i.c_violence_avg::real/100 AS violence_avg, i.c_violence_stddev::real/100 AS violence_stddev + , iv.sexual AS my_sexual, iv.violence AS my_violence + , COALESCE(EXISTS(SELECT 1 FROM image_votes iv0 WHERE iv0.id = i.id AND iv0.ignore) AND NOT iv.ignore, FALSE) AS my_overrule + , COALESCE(v.id, c.id, vsv.id) AS entry_id + , COALESCE(v.title[1+1], c.title[1+1], vsv.title[1+1]) AS entry_title + FROM images i + LEFT JOIN image_votes iv ON iv.id = i.id AND iv.uid =}, \auth->uid, q{ + LEFT JOIN}, vnt, q{v ON i.id BETWEEN 'cv1' AND vndbid_max('cv') AND v.image = i.id + LEFT JOIN}, charst, q{c ON i.id BETWEEN 'ch1' AND vndbid_max('ch') AND c.image = i.id + LEFT JOIN vn_screenshots vs ON i.id BETWEEN 'sf1' AND vndbid_max('sf') AND vs.scr = i.id + LEFT JOIN}, vnt, q{vsv ON i.id BETWEEN 'sf1' AND vndbid_max('sf') AND vsv.id = vs.id + WHERE i.id IN}, $_ + }, $l; + + enrich votes => id => id => sub { sql ' + SELECT iv.id, iv.uid, iv.sexual, iv.violence, iv.ignore OR (u.id IS NOT NULL AND NOT u.perm_imgvote) AS ignore, ', sql_user(), ' + FROM image_votes iv + LEFT JOIN users u ON u.id = iv.uid + WHERE iv.id IN', $_, + auth ? ('AND (iv.uid IS NULL OR iv.uid <> ', \auth->uid, ')') : (), ' + ORDER BY u.username' + }, $l; + + for(grep defined $_->{width}, @$l) { + $_->{entry} = $_->{entry_id} ? { id => $_->{entry_id}, title => $_->{entry_title} } : undef; + delete $_->{entry_id}; + delete $_->{entry_title}; + for my $v ($_->{votes}->@*) { + $v->{user} = xml_string sub { user_ $v }; # Easier than duplicating user_() in Elm + delete $v->{$_} for grep /^user_/, keys %$v; + } + $_->{token} = ($_->{votecount} == 0 && auth->permImgvote) || (ref $canvote eq 'CODE' ? $canvote->($_) : $canvote) ? auth->csrftoken(0, "imgvote-$_->{id}") : undef; + } +} + +# Validates the token generated by enrich_image; +sub validate_token { + my($l) = @_; + my $ok = 1; + $ok &&= $_->{token} && auth->csrfcheck($_->{token}, "imgvote-$_->{id}") for @$l; + $ok; +} + + +# Returns a string like 'Not flagged' or 'Safe / Tame (5)' +sub image_flagging_display { + my($img, $small) = @_; + !$img->{votecount} ? 'Not flagged' : + $small ? sprintf '%s / %s', $SEX[$img->{sexual}], $VIO[$img->{violence}] + : sprintf '%s / %s (%d)', $SEX[$img->{sexual}], $VIO[$img->{violence}], $img->{votecount} +} + + +# Returns whether the image is hidden according to the user's preferences. +# Return values: +# 0 -> visible +# 4 -> hidden for some reason +# 5 -> hidden because of sexual flag +# 6 -> hidden because of violence flag +# 7 -> hidden because both +sub image_hidden { + my($img) = @_; + my($sex,$vio) = $img->@{'sexual', 'violence'}; + my $sexp = auth->pref('max_sexual')||0; + my $viop = auth->pref('max_violence')||0; + my $sexh = $sex > $sexp && $sexp >= 0 if $img->{votecount}; + my $vioh = $vio > $viop if $img->{votecount}; + my $hidden = $sexp < 0 || $sexh || $vioh || (!$img->{votecount} && ($sexp < 2 || $viop < 2)); + $hidden ? 4 + ($sexh?1:0)+($vioh?2:0) : 0; +} + + +# Display (or not) an image with preference toggle and hover-information. +# Given $img is assumed to be an object generated by enrich_image_obj(). +# %opt: +# alt -> alt text +# width -> if different from original image +# height -> if different from original image +# url -> link the image to a page (if not hidden by settings) +# overlay -> CODE ref, html to replace the overlay with. +# XXX: Not all of these options are used, could clean up a few. +sub image_ { + my($img, %opt) = @_; + return p_ 'No image' if !$img; + + my($sex,$vio) = $img->@{'sexual', 'violence'}; + my($w,$h) = $opt{width} ? @opt{'width','height'} : @{$img}{'width', 'height'}; + my $hidden = image_hidden $img; + my $hide_on_click = $opt{url} ? $hidden : $sex || $vio || !$img->{votecount} || (auth->pref('max_sexual')||0) < 0; + my $small = $w*$h < 20000; + + label_ class => 'imghover', style => "width: ${w}px; height: ${h}px", sub { + input_ type => 'checkbox', class => 'hidden', $hidden ? () : (checked => 'checked') if $hide_on_click; + div_ class => 'imghover--visible', sub { + a_ href => $opt{url} if $opt{url}; + img_ src => imgurl($img->{id}), width => $w, height => $h, $opt{alt} ? (alt => $opt{alt}) : (); + end_ if $opt{url}; + if(!exists $opt{overlay}) { + a_ class => 'imghover--overlay', href => "/$img->{id}?view=".viewset(show_nsfw=>1), image_flagging_display $img, $small if auth; + span_ class => 'imghover--overlay', image_flagging_display $img, $small if !auth; + } elsif(ref $opt{overlay} eq 'CODE') { + $opt{overlay}->(); + } + }; + div_ class => 'imghover--warning', sub { + if($img->{votecount}) { + if(!$small) { + txt_ 'This image has been flagged as:'; + br_; br_; + } + txt_ 'Sexual: '; $hidden & 1 ? b_ $SEX[$sex] : txt_ $SEX[$sex]; + br_; + txt_ 'Violence: '; $hidden & 2 ? b_ $VIO[$vio] : txt_ $VIO[$vio]; + } else { + txt_ 'This image has not yet been flagged'; + } + if(!$small) { + br_; br_; + span_ class => 'fake_link', 'Show me anyway'; + br_; br_; + small_ 'This warning can be disabled in your account'; + } + } if $hide_on_click; + } +} + + +sub enrich_image_obj { + my $field = shift; + enrich_obj $field => id => 'SELECT id, width, height, c_votecount AS votecount, c_sexual_avg::real/100 AS sexual_avg, c_violence_avg::real/100 AS violence_avg FROM images WHERE id IN', @_; + + # Also add our final verdict. Still no clue why I chose these thresholds, but they seem to work. + for (map +(ref $_ eq 'ARRAY' ? @$_ : $_), @_) { + local $_ = $_->{$field}; + if(ref $_) { + $_->{sexual} = !$_->{votecount} ? 2 : $_->{sexual_avg} > 1.3 ? 2 : $_->{sexual_avg} > 0.4 ? 1 : 0; + $_->{violence} = !$_->{votecount} ? 2 : $_->{violence_avg} > 1.3 ? 2 : $_->{violence_avg} > 0.4 ? 1 : 0; + } + } +} + +1; |