diff options
author | Yorhel <git@yorhel.nl> | 2019-09-27 11:06:56 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2019-09-27 11:07:05 +0200 |
commit | da2ee73149aade8bc88bc4f87a424dc60348c7fc (patch) | |
tree | 457d8512112c58c45ccb72822e7bc617228db111 | |
parent | 092158fee02c3516ae08f7efcef8f1774bf823e5 (diff) |
v2rw: Convert history listings
And I changed the filter selection into a more form-like thing. It's
slightly more powerful, but not sure it's such a huge improvement in
terms of UI. Everything should be identical apart from that.
-rw-r--r-- | data/style.css | 7 | ||||
-rw-r--r-- | lib/VNDB/DB/Docs.pm | 43 | ||||
-rw-r--r-- | lib/VNDB/Handler/Misc.pm | 100 | ||||
-rw-r--r-- | lib/VNDB/Util/CommonHTML.pm | 1 | ||||
-rw-r--r-- | lib/VNWeb/DB.pm | 8 | ||||
-rw-r--r-- | lib/VNWeb/HTML.pm | 56 | ||||
-rw-r--r-- | lib/VNWeb/Misc/History.pm | 213 | ||||
-rw-r--r-- | lib/VNWeb/Prelude.pm | 4 | ||||
-rw-r--r-- | lib/VNWeb/Validation.pm | 1 |
9 files changed, 282 insertions, 151 deletions
diff --git a/data/style.css b/data/style.css index afa16fb1..eb1830a0 100644 --- a/data/style.css +++ b/data/style.css @@ -42,7 +42,7 @@ table.stripe tbody tr:nth-child(odd):not(.nostripe), #debug h2 { color: #f00!important; font-size: 20px; } #debug, #debug a { color: #fff!important; } -.visuallyhidden { +.visuallyhidden, p.linkradio input { position: absolute !important; left: 0; height: 1px; width: 1px; @@ -153,6 +153,11 @@ table.formtable td { padding: 0; } table.formtable tr.newfield td { padding-top: 5px; } table.formtable tr.newpart td { padding-top: 20px; font-weight: bold; } +p.linkradio { padding: 2px } +p.linkradio label { color: $link$; cursor: pointer } +p.linkradio input:checked + label { color: $maintext$ } +p.linkradio em { font-weight: normal; font-style: normal; color: $grayedout$ } + div.spinner { content: ''; border: 3px solid #9eaebd; border-bottom-color: transparent; border-radius: 100%; animation: spin 1s infinite linear; width: 14px; height: 14px; display: inline-block; margin: auto } @keyframes spin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } } diff --git a/lib/VNDB/DB/Docs.pm b/lib/VNDB/DB/Docs.pm deleted file mode 100644 index d7c220ff..00000000 --- a/lib/VNDB/DB/Docs.pm +++ /dev/null @@ -1,43 +0,0 @@ - -package VNDB::DB::Docs; - -use strict; -use warnings; -use Exporter 'import'; - -our @EXPORT = qw|dbDocGet dbDocGetRev|; - - -# Can only fetch a single document. -# $doc = $self->dbDocGet(id => $id); -sub dbDocGet { - my $self = shift; - my %o = @_; - - my $r = $self->dbAll('SELECT id, title, content FROM docs WHERE id = ?', $o{id}); - return wantarray ? ($r, 0) : $r; -} - - -# options: id, rev -sub dbDocGetRev { - my $self = shift; - my %o = @_; - - $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'d\' AND itemid = ?', $o{id})->{rev}; - - my $r = $self->dbAll(q| - SELECT de.id, d.title, d.content, de.hidden, de.locked, - extract('epoch' from c.added) as added, c.requester, c.comments, u.username, c.rev, c.ihid, c.ilock, c.id AS cid, - NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev - FROM changes c - JOIN docs de ON de.id = c.itemid - JOIN docs_hist d ON d.chid = c.id - JOIN users u ON u.id = c.requester - WHERE c.type = 'd' AND c.itemid = ? AND c.rev = ?|, - $o{id}, $o{rev} - ); - return wantarray ? ($r, 0) : $r; -} - -1; diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm index 666e71bd..9b4073ce 100644 --- a/lib/VNDB/Handler/Misc.pm +++ b/lib/VNDB/Handler/Misc.pm @@ -11,7 +11,6 @@ use VNDB::Types; TUWF::register( qr{}, \&homepage, - qr{(?:([upvrcsd])([1-9]\d*)/)?hist},\&history, qr{nospam}, \&nospam, qr{xml/prefs\.xml}, \&prefs, qr{opensearch\.xml}, \&opensearch, @@ -194,105 +193,6 @@ sub homepage { } -sub history { - my($self, $type, $id) = @_; - $type ||= ''; - $id ||= 0; - - my $f = $self->formValidate( - { get => 'p', required => 0, default => 1, template => 'page' }, - { get => 'm', required => 0, default => !$type, enum => [ 0, 1 ] }, - { get => 'h', required => 0, default => 0, enum => [ -1..1 ] }, - { get => 't', required => 0, default => '', enum => [qw|v r p c s d a|] }, - { get => 'e', required => 0, default => 0, enum => [ -1..1 ] }, - { get => 'r', required => 0, default => 0, enum => [ 0, 1 ] }, - ); - return $self->resNotFound if $f->{_err}; - - # get item object and title - my $obj = $type eq 'u' ? $self->dbUserGet(uid => $id, what => 'hide_list')->[0] : - $type eq 'p' ? $self->dbProducerGet(id => $id)->[0] : - $type eq 'r' ? $self->dbReleaseGet(id => $id)->[0] : - $type eq 'c' ? $self->dbCharGet(id => $id)->[0] : - $type eq 's' ? $self->dbStaffGet(id => $id)->[0] : - $type eq 'd' ? $self->dbDocGet(id => $id)->[0] : - $type eq 'v' ? $self->dbVNGet(id => $id)->[0] : undef; - return $self->resNotFound if $type && !$obj->{id}; - my $title = $type ? 'Edit history of '.($obj->{title} || $obj->{name} || $obj->{username}) : 'Recent changes'; - - # get the edit history - my($list, $np) = $self->dbRevisionGet( - $type && $type ne 'u' ? ( type => $type, itemid => $id ) : (), - $type eq 'u' ? ( uid => $id ) : (), - $f->{t} ? ( type => $f->{t} eq 'a' ? [qw|v r p s d|] : $f->{t} ) : (), - page => $f->{p}, - results => 50, - auto => $f->{m}, - hidden => $type && $type ne 'u' ? 0 : $f->{h}, - edit => $f->{e}, - releases => $f->{r}, - ); - - $self->htmlHeader(title => $title, noindex => 1, feeds => [ 'changes' ]); - $self->htmlMainTabs($type, $obj, 'hist') if $type; - - # url generator - my $u = sub { - my($n, $v) = @_; - $n ||= ''; - local $_ = ($type ? "/$type$id" : '').'/hist'; - $_ .= '?m='.($n eq 'm' ? $v : $f->{m}); - $_ .= ';h='.($n eq 'h' ? $v : $f->{h}); - $_ .= ';t='.($n eq 't' ? $v : $f->{t}); - $_ .= ';e='.($n eq 'e' ? $v : $f->{e}); - $_ .= ';r='.($n eq 'r' ? $v : $f->{r}); - }; - - # filters - div class => 'mainbox'; - h1 $title; - if($type ne 'u') { - p class => 'browseopts'; - a !$f->{m} ? (class => 'optselected') : (), href => $u->(m => 0), 'Show automated edits'; - a $f->{m} ? (class => 'optselected') : (), href => $u->(m => 1), 'Hide automated edits'; - end; - } - if(!$type || $type eq 'u') { - if($self->authCan('dbmod')) { - p class => 'browseopts'; - a $f->{h} == 1 ? (class => 'optselected') : (), href => $u->(h => 1), 'Hide deleted items'; - a $f->{h} == -1 ? (class => 'optselected') : (), href => $u->(h => -1), 'Show deleted items'; - end; - } - p class => 'browseopts'; - a !$f->{t} ? (class => 'optselected') : (), href => $u->(t => ''), 'Show all items'; - a $f->{t} eq 'v' ? (class => 'optselected') : (), href => $u->(t => 'v'), 'Only visual novels'; - a $f->{t} eq 'r' ? (class => 'optselected') : (), href => $u->(t => 'r'), 'Only releases'; - a $f->{t} eq 'p' ? (class => 'optselected') : (), href => $u->(t => 'p'), 'Only producers'; - a $f->{t} eq 's' ? (class => 'optselected') : (), href => $u->(t => 's'), 'Only staff'; - a $f->{t} eq 'c' ? (class => 'optselected') : (), href => $u->(t => 'c'), 'Only characters'; - a $f->{t} eq 'd' ? (class => 'optselected') : (), href => $u->(t => 'd'), 'Only docs'; - a $f->{t} eq 'a' ? (class => 'optselected') : (), href => $u->(t => 'a'), 'All except characters'; - end; - p class => 'browseopts'; - a !$f->{e} ? (class => 'optselected') : (), href => $u->(e => 0), 'Show all changes'; - a $f->{e} == 1 ? (class => 'optselected') : (), href => $u->(e => 1), 'Only edits'; - a $f->{e} == -1 ? (class => 'optselected') : (), href => $u->(e => -1), 'Only newly created pages'; - end; - } - if($type eq 'v') { - p class => 'browseopts'; - a !$f->{r} ? (class => 'optselected') : (), href => $u->(r => 0), 'Exclude edits of releases'; - a $f->{r} ? (class => 'optselected') : (), href => $u->(r => 1), 'Include edits of releases'; - end; - } - end 'div'; - - $self->htmlBrowseHist($list, $f, $np, $u->()); - $self->htmlFooter; -} - - sub nospam { my $self = shift; $self->htmlHeader(title => 'Could not send form', noindex => 1); diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm index 4b80eb21..7a6e8620 100644 --- a/lib/VNDB/Util/CommonHTML.pm +++ b/lib/VNDB/Util/CommonHTML.pm @@ -123,7 +123,6 @@ sub htmlHiddenMessage { : $type eq 'r' ? $self->dbReleaseGetRev(id => $obj->{id})->[0]{comments} : $type eq 'c' ? $self->dbCharGetRev(id => $obj->{id})->[0]{comments} : $type eq 's' ? $self->dbStaffGetRev(id => $obj->{id})->[0]{comments} - : $type eq 'd' ? $self->dbDocGetRev(id => $obj->{id})->[0]{comments} : $self->dbProducerGetRev(id => $obj->{id})->[0]{comments}; div class => 'mainbox'; h1 $obj->{title}||$obj->{name}; diff --git a/lib/VNWeb/DB.pm b/lib/VNWeb/DB.pm index 7ef2e161..0d5616e0 100644 --- a/lib/VNWeb/DB.pm +++ b/lib/VNWeb/DB.pm @@ -10,7 +10,7 @@ use VNDB::Schema; our @EXPORT = qw/ sql - sql_join sql_comma sql_and sql_array sql_func sql_fromhex sql_tohex sql_fromtime sql_totime + sql_join sql_comma sql_and sql_or sql_array sql_func sql_fromhex sql_tohex sql_fromtime sql_totime enrich enrich_merge enrich_flatten db_entry db_edit /; @@ -49,15 +49,15 @@ $Carp::Internal{ (__PACKAGE__) }++; sub sql_join { my $sep = shift; my @args = map +($sep, $_), @_; - shift @args; - return @args; + sql @args[1..$#args]; } # Join multiple arguments together with a comma, for use in a SELECT or IN # clause or function arguments. sub sql_comma { sql_join ',', @_ } -sub sql_and { sql_join 'AND', map sql('(', $_, ')'), @_ } +sub sql_and { @_ ? sql_join 'AND', map sql('(', $_, ')'), @_ : sql '1=1' } +sub sql_or { @_ ? sql_join 'OR', map sql('(', $_, ')'), @_ : sql '1=0' } # Construct a PostgreSQL array type from the function arguments. sub sql_array { 'ARRAY[', sql_join(',', map \$_, @_), ']' } diff --git a/lib/VNWeb/HTML.pm b/lib/VNWeb/HTML.pm index d1498c1e..2c89d3b9 100644 --- a/lib/VNWeb/HTML.pm +++ b/lib/VNWeb/HTML.pm @@ -8,6 +8,7 @@ use Encode 'encode_utf8'; use JSON::XS; use TUWF ':html5_', 'uri_escape', 'html_escape', 'mkclass'; use Exporter 'import'; +use POSIX 'ceil'; use JSON::XS; use VNDB::Config; use VNDB::BBCode; @@ -24,6 +25,7 @@ our @EXPORT = qw/ elm_ framework_ revision_ + paginate_ /; @@ -249,8 +251,18 @@ sub _maintabs_ { WHERE}, { 'tb.type' => $t, 'tb.iid' => $o->{id}, 't.hidden' => 0, 't.private' => 0 }); t disc => "/t/$id", "discussions ($cnt)"; }; - - # TODO: User lists + t posts => "/$id/posts", 'posts' if $t eq 'u'; + + do { + t wish => "/$id/wish", 'wishlist'; + t votes => "/$id/votes", 'votes'; + t list => "/$id/list", 'list'; + } if $t eq 'u' && ( + auth->permUsermod || (auth && auth->uid == $o->{id}) + || !(exists $o->{hide_list} + ? $o->{hide_list} + : tuwf->dbVali('SELECT value FROM users_prefs WHERE', { uid => $o->{id}, key => 'hide_list' })) + ); t tagmod => "/$id/tagmod", 'modify tags' if $t eq 'v' && auth->permTag && !$o->{entry_hidden}; t copy => "/$id/copy", 'copy' if $t =~ /[rc]/ && can_edit $t, $o; @@ -525,4 +537,44 @@ sub revision_ { }; } + +# Creates next/previous buttons (tabs), if needed. +# Arguments: +# url generator (code reference that takes $_ and returns a url for that page). +# current page number (1..n), +# nextpage (0/1 or, if the full count is known: [$total, $perpage]), +# alignment (t/b) +sub paginate_ { + my($url, $p, $np, $al) = @_; + my($cnt, $pp) = ref($np) ? @$np : ($p+$np, 1); + return if $p == 1 && $cnt <= $pp; + + my sub tab_ { + my($left, $page, $label) = @_; + li_ mkclass(left => $left), sub { + local $_ = $page; + my $u = $url->(); + a_ href => $u, $label; + } + } + my sub ell_ { + my($left) = @_; + li_ mkclass(ellipsis => 1, left => $left), sub { b_ '⋯' }; + } + my $nc = 5; # max. number of buttons on each side + + ul_ class => 'maintabs browsetabs ' . ($al eq 't' ? 'notfirst' : 'bottom'), sub { + $p > 2 and ref $np and tab_ 1, 1, '« first'; + $p > $nc+1 and ref $np and ell_ 1; + $p > $_ and ref $np and tab_ 1, $p-$_, $p-$_ for (reverse 2..($nc>$p-2?$p-2:$nc-1)); + $p > 1 and tab_ 1, $p-1, '‹ previous'; + + my $l = ceil($cnt/$pp)-$p+1; + $l > 2 and tab_ 0, $l+$p-1, 'last »'; + $l > $nc+1 and ell_ 0; + $l > $_ and tab_ 0, $p+$_, $p+$_ for (reverse 2..($nc>$l-2?$l-2:$nc-1)); + $l > 1 and tab_ 0, $p+1, 'next ›'; + } +} + 1; diff --git a/lib/VNWeb/Misc/History.pm b/lib/VNWeb/Misc/History.pm new file mode 100644 index 00000000..8c1fc8bc --- /dev/null +++ b/lib/VNWeb/Misc/History.pm @@ -0,0 +1,213 @@ +package VNWeb::Misc::History; + +use VNWeb::Prelude; + + +sub fetch { + my($type, $id, $filt) = @_; + + my $where = sql_and + !$type ? () + : $type eq 'u' ? sql 'c.requester =', \$id + : sql_or( + sql('c.type =', \$type, ' AND c.itemid =', \$id), + + # This may need an index on releases_vn_hist.vid + $type eq 'v' && $filt->{r} ? + sql 'c.id IN(SELECT chid FROM releases_vn_hist WHERE vid =', \$id, ')' : () + ), + + $filt->{t} && $filt->{t}->@* ? sql 'c.type IN', \$filt->{t} : (), + $filt->{m} ? sql 'c.requester <> 1' : (), + + $filt->{e} && $filt->{e} == 1 ? sql 'c.rev <> 1' : (), + $filt->{e} && $filt->{e} ==-1 ? sql 'c.rev = 1' : (), + + $filt->{h} ? sql $filt->{h} == 1 ? 'NOT' : '', + 'EXISTS(SELECT 1 FROM changes c_i + WHERE c_i.type = c.type AND c_i.itemid = c.itemid AND c_i.ihid + AND c_i.rev = (SELECT MAX(c_ii.rev) FROM changes c_ii WHERE c_ii.type = c.type AND c_ii.itemid = c.itemid))' : (); + + my($lst, $np) = tuwf->dbPagei({ page => $filt->{p}, results => 50 }, q{ + SELECT c.id, c.type, c.itemid, c.comments, c.rev,}, sql_totime('c.added'), q{ AS added + , c.requester, u.username + FROM changes c + JOIN users u ON c.requester = u.id + WHERE}, $where, q{ + ORDER BY c.id DESC + }); + + # Fetching the titles in a separate query is faster, for some reason. + enrich_merge id => sql(q{ + SELECT id, title, original FROM ( + SELECT chid, title, original FROM vn_hist + UNION ALL SELECT chid, title, original FROM releases_hist + UNION ALL SELECT chid, name, original FROM producers_hist + UNION ALL SELECT chid, name, original FROM chars_hist + UNION ALL SELECT chid, title, '' AS original FROM docs_hist + UNION ALL SELECT sh.chid, name, original FROM staff_hist sh JOIN staff_alias_hist sah ON sah.chid = sh.chid AND sah.aid = sh.aid + ) t(id, title, original) + WHERE id IN}), $lst; + ($lst, $np) +} + + +sub _filturl { + my($filt) = @_; + return '?'.join '&', map { + my $k = $_; + ref $filt->{$k} ? map "$k=$_", sort $filt->{$k}->@* : "$k=$filt->{$k}" + } sort keys %$filt; +} + + +sub tablebox_ { + my($type, $id, $filt) = @_; + + my($lst, $np) = fetch $type, $id, $filt; + + my sub url { _filturl {%$filt, p => $_} } + + paginate_ \&url, $filt->{p}, $np, 't'; + div_ class => 'mainbox browse history', sub { + table_ class => 'stripe', sub { + thead_ sub { tr_ sub { + td_ class => 'tc1_1', 'Rev.'; + td_ class => 'tc1_2', ''; + td_ class => 'tc2', 'Date'; + td_ class => 'tc3', 'User'; + td_ class => 'tc4', sub { txt_ 'Page'; debug_ $lst; }; + }}; + tr_ sub { + my $i = $_; + my $revurl = "/$i->{type}$i->{itemid}.$i->{rev}"; + + tr_ sub { + td_ class => 'tc1_1', sub { a_ href => $revurl, "$i->{type}$i->{itemid}" }; + td_ class => 'tc1_2', sub { a_ href => $revurl, ".$i->{rev}" }; + td_ class => 'tc2', fmtdate $i->{added}, 'full'; + td_ class => 'tc3', sub { user_ $i->{requester}, $i->{username} }; + td_ class => 'tc4', sub { + a_ href => $revurl, title => $i->{original}, shorten $i->{title}, 80; + b_ class => 'grayedout', sub { lit_ bb2html $i->{comments}, 150 }; + }; + } + } for @$lst; + }; + }; + paginate_ \&url, $filt->{p}, $np, 'b'; +} + + +sub filters_ { + my($type) = @_; + + my @types = ( + [ v => 'Visual novels' ], + [ r => 'Releases' ], + [ p => 'Producers' ], + [ s => 'Staff' ], + [ c => 'Characters' ], + [ d => 'Docs' ] + ); + + state $schema = tuwf->compile({ type => 'hash', keys => { + # Types + t => { type => 'array', scalar => 1, required => 0, default => [map $_->[0], @types], values => { enum => [(map $_->[0], @types), 'a'] } }, + m => { required => 0, enum => [ 0, 1 ] }, # Automated edits + h => { required => 0, default => 0, enum => [ -1..1 ] }, # Hidden items + e => { required => 0, default => 0, enum => [ -1..1 ] }, # Existing/new items + r => { required => 0, default => 0, enum => [ 0, 1 ] }, # Include releases + p => { page => 1 }, + }}); + my $filt = tuwf->validate(get => $schema)->data; + + $filt->{m} //= $type ? 0 : 1; # Exclude automated edits by default on the main 'recent changes' view. + + # For compat with old URLs, 't=a' means "everything except characters". Let's also weed out duplicates + my %t = map +($_, 1), map $_ eq 'a' ? (qw|v r p s d|) : ($_), $filt->{t}->@*; + $filt->{t} = keys %t == @types ? [] : [ keys %t ]; + + # Not all filters apply everywhere + delete @{$filt}{qw/ t e h /} if $type && $type ne 'u'; + delete $filt->{m} if $type eq 'u'; + delete $filt->{r} if $type ne 'v'; + + my sub opt_ { + my($key, $val, $label) = @_; + input_ type => 'radio', name => $key, id => "form_${key}{$val}", value => $val, + $filt->{$key} eq $val ? (checked => 'checked') : (); + label_ for => "form_${key}{$val}", $label; + }; + + form_ method => 'get', action => tuwf->reqPath(), sub { + table_ style => 'margin: 0 auto', sub { tr_ sub { + td_ style => 'padding: 10px', sub { + p_ class => 'linkradio', sub { + join_ \&br_, sub { + input_ type => 'checkbox', name => 't', value => $_->[0], id => "form_t$_->[0]", $t{$_->[0]}? (checked => 'checked') : (); + label_ for => "form_t$_->[0]", ' '.$_->[1] + }, @types; + } + } if exists $filt->{t}; + + td_ style => 'padding: 10px', sub { + p_ class => 'linkradio', sub { + opt_ e => 0, 'All'; em_ ' | '; + opt_ e => 1, 'Only changes to existing items'; em_ ' | '; + opt_ e =>-1, 'Only newly created items'; + } if exists $filt->{e}; + p_ class => 'linkradio', sub { + opt_ h => 0, 'All'; em_ ' | '; + opt_ h => 1, 'Only non-deleted items'; em_ ' | '; + opt_ h =>-1, 'Only deleted'; + } if exists $filt->{h}; + p_ class => 'linkradio', sub { + opt_ m => 0, 'Show'; em_ ' | '; + opt_ m => 1, 'Hide'; txt_ ' automated edits'; + } if exists $filt->{m}; + p_ class => 'linkradio', sub { + opt_ r => 0, 'Exclude'; em_ ' | '; + opt_ r => 1, 'Include'; txt_ ' releases'; + } if exists $filt->{r}; + input_ type => 'submit', class => 'submit', value => 'Update'; + debug_ $filt; + }; + }}; + }; + $filt; +} + + +TUWF::get qr{/(?:([upvrcsd])([1-9]\d*)/)?hist} => sub { + my($type, $id) = (tuwf->capture(1)||'', tuwf->capture(2)); + + my sub dbitem { + my($table, $title) = @_; + tuwf->dbRowi('SELECT id,', $title, ' AS title, hidden AS entry_hidden, locked AS entry_locked FROM', $table, 'WHERE id =', \$id); + }; + + my $obj = !$type ? undef : + $type eq 'u' ? tuwf->dbRowi('SELECT id, username AS title FROM users WHERE id =', \$id) : + $type eq 'p' ? dbitem producers => 'name' : + $type eq 'v' ? dbitem vn => 'title' : + $type eq 'r' ? dbitem releases => 'title' : + $type eq 'c' ? dbitem chars => 'name' : + $type eq 's' ? dbitem staff => '(SELECT name FROM staff_alias WHERE aid = staff.aid)' : + $type eq 'd' ? dbitem docs => 'title' : die; + + return tuwf->resNotFound if $type && !$obj->{id}; + + my $title = $type ? "Edit history of $obj->{title}" : 'Recent changes'; + framework_ title => $title, index => 0, type => $type, dbobj => $obj, tab => 'hist', + sub { + my $filt; + div_ class => 'mainbox', sub { + h1_ $title; + $filt = filters_ $type; + }; + tablebox_ $type, $id, $filt; + }; +}; + +1; diff --git a/lib/VNWeb/Prelude.pm b/lib/VNWeb/Prelude.pm index 26d0763e..506d0592 100644 --- a/lib/VNWeb/Prelude.pm +++ b/lib/VNWeb/Prelude.pm @@ -9,8 +9,10 @@ # use Time::HiRes 'time'; # # use VNDBUtil; +# use VNDB::BBCode; # use VNDB::Types; # use VNDB::Config; +# use VNDB::Func 'fmtdate'; # use VNWeb::Auth; # use VNWeb::HTML; # use VNWeb::DB; @@ -48,8 +50,10 @@ sub import { use Time::HiRes 'time'; use VNDBUtil; + use VNDB::BBCode; use VNDB::Types; use VNDB::Config; + use VNDB::Func 'fmtdate'; use VNWeb::Auth; use VNWeb::HTML; use VNWeb::DB; diff --git a/lib/VNWeb/Validation.pm b/lib/VNWeb/Validation.pm index 80af1d34..c649c7a3 100644 --- a/lib/VNWeb/Validation.pm +++ b/lib/VNWeb/Validation.pm @@ -15,6 +15,7 @@ our @EXPORT = qw/ TUWF::set custom_validations => { id => { uint => 1, max => 1<<40 }, editsum => { required => 1, length => [ 2, 5000 ] }, + page => { uint => 1, min => 1, max => 1000, required => 0, default => 1 }, }; |