diff options
Diffstat (limited to 'lib/VNWeb/Filters.pm')
-rw-r--r-- | lib/VNWeb/Filters.pm | 101 |
1 files changed, 23 insertions, 78 deletions
diff --git a/lib/VNWeb/Filters.pm b/lib/VNWeb/Filters.pm index 73f37e99..429a10f5 100644 --- a/lib/VNWeb/Filters.pm +++ b/lib/VNWeb/Filters.pm @@ -1,13 +1,13 @@ package VNWeb::Filters; -# This module implements validating and querying the old search filters. These -# filters are replaced with the new AdvSearch framework and this code only -# exists to convert old URLs. +# This module implements validating old search filters and converting them to +# the new AdvSearch system. It only exists for compatibility with old URLs. -use VNWeb::Prelude; +use TUWF; +use VNWeb::Validation; use Exporter 'import'; -our @EXPORT = qw/filter_parse filter_vn_query filter_release_query filter_vn_adv filter_release_adv filter_char_adv filter_staff_adv/; +our @EXPORT = qw/filter_parse filter_vn_adv filter_release_adv filter_char_adv filter_staff_adv/; my $VN = form_compile any => { @@ -87,17 +87,6 @@ my $STAFF = form_compile any => { }; -sub debug_validate { - my($type, $data) = @_; - my $s = {vn => $VN, release => $RELEASE, char => $CHAR, staff => $STAFF}->{$type}; - my $v = $s->validate($data); - if(!$v) { - warn sprintf "Filter validation failed!\nData: %s\nError: %s", JSON::XS->new->canonical->pretty->encode($data), JSON::XS->new->canonical->pretty->encode($v->err); - } else { - #warn sprintf "Filter validated: %sSerialized: %s", JSON::XS->new->canonical->pretty->encode($v->data), VNDB::Func::fil_serialize($v->data); - } -} - # Compatibility with old VN filters. Modifies the filter in-place and returns the number of changes made. sub filter_vn_compat { @@ -131,12 +120,29 @@ sub filter_release_compat { } + +my @fil_escape = split //, '_ !"#$%&\'()*+,-./:;<=>?@[\]^`{}~'; + +sub _fil_parse { + my $str = shift; + my %r; + for (split /\./, $str) { + next if !/^([a-z0-9_]+)-([a-zA-Z0-9_~\x81-\x{ffffff}]+)$/; + my($f, $v) = ($1, $2); + my @v = split /~/, $v; + s/_([0-9]{2})/$1 > $#fil_escape ? '' : $fil_escape[$1]/eg for(@v); + $r{$f} = @v > 1 ? \@v : $v[0] + } + return \%r; +} + + # Throws error on failure. sub filter_parse { my($type, $str) = @_; return {} if !$str; my $s = {v => $VN, r => $RELEASE, c => $CHAR, s => $STAFF}->{$type}; - my $data = ref $str ? $str : $str =~ /^{/ ? JSON::XS->new->decode($str) : VNDB::Func::fil_parse $str, keys $s->{known_keys}->%*; + my $data = ref $str ? $str : $str =~ /^{/ ? JSON::XS->new->decode($str) : _fil_parse $str; die "Invalid filter data: $str\n" if !$data; my $f = $s->validate($data)->data; filter_vn_compat $f if $type eq 'v'; @@ -145,67 +151,6 @@ sub filter_parse { } -# Returns an SQL expression for use in a WHERE clause. Assumption: 'v' is an alias to the vn table being queried. -sub filter_vn_query { - my($fil) = @_; - sql_and - defined $fil->{date_before} ? sql 'v.c_released <=', \$fil->{date_before} : (), - defined $fil->{date_after} ? sql 'v.c_released >=', \$fil->{date_after} : (), - defined $fil->{released} ? sql 'v.c_released', $fil->{released} ? '<=' : '>', \strftime('%Y%m%d', gmtime) : (), - defined $fil->{length} ? sql 'v.length IN', $fil->{length} : (), - defined $fil->{hasani} ? sql($fil->{hasani} ?'':'NOT', 'EXISTS(SELECT 1 FROM vn_anime iva WHERE iva.id = v.id)') : (), - defined $fil->{hasshot} ? sql($fil->{hasshot}?'':'NOT', 'EXISTS(SELECT 1 FROM vn_screenshots ivs WHERE ivs.id = v.id)') : (), - defined $fil->{tag_inc} ? sql - 'v.id IN(SELECT vid FROM tags_vn_inherit WHERE tag IN', $fil->{tag_inc}, 'AND spoiler <=', \$fil->{tagspoil}, 'GROUP BY vid HAVING COUNT(tag) =', scalar $fil->{tag_inc}->@*, ')' : (), - defined $fil->{tag_exc} ? sql 'v.id NOT IN(SELECT vid FROM tags_vn_inherit WHERE tag IN', $fil->{tag_exc}, ')' : (), - defined $fil->{lang} ? sql 'v.c_languages && ARRAY', $fil->{lang}, '::language[]' : (), - defined $fil->{olang} ? sql 'v.c_olang && ARRAY', $fil->{olang}, '::language[]' : (), - defined $fil->{plat} ? sql 'v.c_platforms && ARRAY', $fil->{plat}, '::platform[]' : (), - defined $fil->{staff_inc} ? sql 'v.id IN(SELECT ivs.id FROM vn_staff ivs JOIN staff_alias isa ON isa.aid = ivs.aid WHERE isa.id IN', $fil->{staff_inc}, ')' : (), - defined $fil->{staff_exc} ? sql 'v.id NOT IN(SELECT ivs.id FROM vn_staff ivs JOIN staff_alias isa ON isa.aid = ivs.aid WHERE isa.id IN', $fil->{staff_exc}, ')' : (), - auth ? ( - # TODO: onwish, voted and onlist should respect the label filters in users.ulist_* - defined $fil->{ul_notblack} ? sql 'v.id NOT IN(SELECT vid FROM ulist_vns_labels WHERE uid =', \auth->uid, 'AND lbl =', \6, ')' : (), - defined $fil->{ul_onwish} ? sql 'v.id', $fil->{ul_onwish}?'':'NOT', 'IN(SELECT vid FROM ulist_vns_labels WHERE uid =', \auth->uid, 'AND lbl =', \5, ')' : (), - defined $fil->{ul_voted} ? sql 'v.id', $fil->{ul_voted} ?'':'NOT', 'IN(SELECT vid FROM ulist_vns_labels WHERE uid =', \auth->uid, 'AND lbl =', \7, ')' : (), - defined $fil->{ul_onlist} ? sql 'v.id', $fil->{ul_onlist}?'':'NOT', 'IN(SELECT vid FROM ulist_vns WHERE uid =', \auth->uid, ')' : (), - ) : (), -} - - -# Assumption: 'r' is an alias to the release table being queried. -sub filter_release_query { - my($fil) = @_; - sql_and - defined $fil->{type} ? sql 'r.type =', \$fil->{type} : (), - defined $fil->{patch} ? sql($fil->{patch} ?'':'NOT', 'r.patch' ) : (), - defined $fil->{freeware} ? sql($fil->{freeware} ?'':'NOT', 'r.freeware' ) : (), - defined $fil->{doujin} ? sql($fil->{doujin} ?'':'NOT', 'r.doujin AND NOT r.patch') : (), - defined $fil->{uncensored} ? sql($fil->{uncensored}?'':'NOT', 'r.uncensored') : (), - defined $fil->{date_before} ? sql 'r.released <=', \$fil->{date_before} : (), - defined $fil->{date_after} ? sql 'r.released >=', \$fil->{date_after} : (), - defined $fil->{released} ? sql 'r.released', $fil->{released} ? '<=' : '>', \strftime('%Y%m%d', gmtime) : (), - defined $fil->{minage} ? sql 'r.minage IN', $fil->{minage} : (), - defined $fil->{lang} ? sql 'r.id IN(SELECT irl.id FROM releases_lang irl WHERE irl.lang IN', $fil->{lang}, ')' : (), - defined $fil->{olang} ? sql 'r.id IN(SELECT irv.id FROM releases_vn irv JOIN vn iv ON irv.vid = iv.id WHERE iv.c_olang && ARRAY', $fil->{olang}, '::language[])' : (), - defined $fil->{resolution} ? sql 'NOT r.patch AND ARRAY[r.reso_x,r.reso_y] IN', [ map $_ eq 'unknown' ? '{0,0}' : $_ eq 'nonstandard' ? '{0,1}' : '{'.(s/x/,/r).'}', $fil->{resolution}->@* ] : (), - defined $fil->{plat} ? sql_or( - grep( /^unk$/, $fil->{plat}->@*) ? sql 'NOT EXISTS(SELECT 1 FROM releases_platforms irp WHERE irp.id = r.id)' : (), - grep(!/^unk$/, $fil->{plat}->@*) ? sql 'r.id IN(SELECT irp.id FROM releases_platforms irp WHERE irp.platform IN', [grep !/^unk$/, $fil->{plat}->@*], ')' : (), - ) : (), - defined $fil->{prod_inc} ? sql 'r.id IN(SELECT irp.id FROM releases_producers irp WHERE irp.pid IN', $fil->{prod_inc}, ')' : (), - defined $fil->{prod_exc} ? sql 'r.id NOT IN(SELECT irp.id FROM releases_producers irp WHERE irp.pid IN', $fil->{prod_exc}, ')' : (), - defined $fil->{med} ? sql_or( - grep( /^unk$/, $fil->{med}->@*) ? sql 'NOT EXISTS(SELECT 1 FROM releases_media irm WHERE irm.id = r.id)' : (), - grep(!/^unk$/, $fil->{med}->@*) ? sql 'r.id IN(SELECT irm.id FROM releases_media irm WHERE irm.medium IN', [grep !/^unk$/, $fil->{med}->@*], ')' : (), - ) : (), - defined $fil->{voiced} ? sql 'NOT r.patch AND r.voiced IN', $fil->{voiced} : (), - defined $fil->{ani_story} ? sql 'NOT r.patch AND r.ani_story IN', $fil->{ani_story} : (), - defined $fil->{ani_ero} ? sql 'NOT r.patch AND r.ani_ero IN', $fil->{ani_ero} : (), - defined $fil->{engine} ? sql 'r.engine =', \$fil->{engine} : (), -} - - sub filter_vn_adv { my($fil) = @_; [ 'and', |