diff options
Diffstat (limited to 'lib/VNWeb')
-rw-r--r-- | lib/VNWeb/HTML.pm | 37 | ||||
-rw-r--r-- | lib/VNWeb/Misc/History.pm | 11 | ||||
-rw-r--r-- | lib/VNWeb/Prelude.pm | 4 | ||||
-rw-r--r-- | lib/VNWeb/User/List.pm | 96 | ||||
-rw-r--r-- | lib/VNWeb/Validation.pm | 1 |
5 files changed, 135 insertions, 14 deletions
diff --git a/lib/VNWeb/HTML.pm b/lib/VNWeb/HTML.pm index 08601252..8e7ffa3c 100644 --- a/lib/VNWeb/HTML.pm +++ b/lib/VNWeb/HTML.pm @@ -26,6 +26,8 @@ our @EXPORT = qw/ framework_ revision_ paginate_ + sortable_ + searchbox_ /; @@ -598,7 +600,7 @@ sub revision_ { # Creates next/previous buttons (tabs), if needed. # Arguments: -# url generator (code reference that takes $_ and returns a url for that page). +# url generator (code reference that takes ('p', $pagenumber) as arguments with $_=$pagenumber 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) @@ -611,7 +613,7 @@ sub paginate_ { my($left, $page, $label) = @_; li_ mkclass(left => $left), sub { local $_ = $page; - my $u = $url->(); + my $u = $url->(p => $page); a_ href => $u, $label; } } @@ -635,4 +637,35 @@ sub paginate_ { } } + +# Generate sort buttons for a table header. This function assumes that sorting +# options are given as query parameters: 's' for the $column_name to sort on +# and 'o' for order ('a'sc/'d'esc). +# Options: $column_title, $column_name, $opt, $url +# Where $url is a function that is given ('p', undef, 's', $column_name, 'o', $order) and returns a URL. +sub sortable_ { + my($name, $opt, $url) = @_; + $opt->{s} eq $name && $opt->{o} eq 'a' ? txt_ ' ▴' : a_ href => $url->(p => undef, s => $name, o => 'a'), ' ▴'; + $opt->{s} eq $name && $opt->{o} eq 'd' ? txt_ '▾' : a_ href => $url->(p => undef, s => $name, o => 'd'), '▾'; +} + + +sub searchbox_ { + my($sel, $value) = @_; + fieldset_ class => 'search', sub { + p_ id => 'searchtabs', sub { + a_ href => '/v/all', $sel eq 'v' ? (class => 'sel') : (), 'Visual novels'; + a_ href => '/r', $sel eq 'r' ? (class => 'sel') : (), 'Releases'; + a_ href => '/p/all', $sel eq 'p' ? (class => 'sel') : (), 'Producers'; + a_ href => '/s/all', $sel eq 's' ? (class => 'sel') : (), 'Staff'; + a_ href => '/c/all', $sel eq 'c' ? (class => 'sel') : (), 'Characters'; + a_ href => '/g', $sel eq 'g' ? (class => 'sel') : (), 'Tags'; + a_ href => '/i', $sel eq 'i' ? (class => 'sel') : (), 'Traits'; + a_ href => '/u/all', $sel eq 'u' ? (class => 'sel') : (), 'Users'; + }; + input_ type => 'text', name => 'q', id => 'q', class => 'text', value => $value; + input_ type => 'submit', class => 'submit', value => 'Search!'; + }; +} + 1; diff --git a/lib/VNWeb/Misc/History.pm b/lib/VNWeb/Misc/History.pm index 5251d4ea..c96d07cc 100644 --- a/lib/VNWeb/Misc/History.pm +++ b/lib/VNWeb/Misc/History.pm @@ -51,15 +51,6 @@ sub fetch { } -sub _filturl { - my($filt) = @_; - return '?'.join '&', map { - my $k = $_; - ref $filt->{$k} ? map "$k=$_", sort $filt->{$k}->@* : "$k=$filt->{$k}" - } sort keys %$filt; -} - - # Also used by User::Page. # %opt: nopage => 1/0, results => $num sub tablebox_ { @@ -67,7 +58,7 @@ sub tablebox_ { my($lst, $np) = fetch $type, $id, $filt, \%opt; - my sub url { _filturl {%$filt, p => $_} } + my sub url { '?'.query_encode %$filt, p => $_ } paginate_ \&url, $filt->{p}, $np, 't' unless $opt{nopage}; div_ class => 'mainbox browse history', sub { diff --git a/lib/VNWeb/Prelude.pm b/lib/VNWeb/Prelude.pm index 66f78425..c00e9afb 100644 --- a/lib/VNWeb/Prelude.pm +++ b/lib/VNWeb/Prelude.pm @@ -13,7 +13,7 @@ # use VNDB::BBCode; # use VNDB::Types; # use VNDB::Config; -# use VNDB::Func 'fmtdate', 'fmtage', 'fmtvote'; +# use VNDB::Func 'fmtdate', 'fmtage', 'fmtvote', 'query_encode'; # use VNWeb::Auth; # use VNWeb::HTML; # use VNWeb::DB; @@ -55,7 +55,7 @@ sub import { use VNDB::BBCode; use VNDB::Types; use VNDB::Config; - use VNDB::Func 'fmtdate', 'fmtage', 'fmtvote'; + use VNDB::Func 'fmtdate', 'fmtage', 'fmtvote', 'query_encode'; use VNWeb::Auth; use VNWeb::HTML; use VNWeb::DB; diff --git a/lib/VNWeb/User/List.pm b/lib/VNWeb/User/List.pm new file mode 100644 index 00000000..dc34fe38 --- /dev/null +++ b/lib/VNWeb/User/List.pm @@ -0,0 +1,96 @@ +package VNWeb::User::List; + +use VNWeb::Prelude; + + +sub listing_ { + my($opt, $list, $count) = @_; + + my sub url { '?'.query_encode %$opt, @_ } + + paginate_ \&url, $opt->{p}, [$count, 50], 't'; + div_ class => 'mainbox browse', sub { + table_ class => 'stripe', sub { + thead_ sub { tr_ sub { + td_ class => 'tc1', sub { txt_ 'Username'; sortable_ 'username', $opt, \&url }; + td_ class => 'tc2', sub { txt_ 'Registered'; sortable_ 'registered', $opt, \&url }; + td_ class => 'tc3', sub { txt_ 'Votes'; sortable_ 'votes', $opt, \&url }; + td_ class => 'tc4', sub { txt_ 'Edits'; sortable_ 'changes', $opt, \&url }; + td_ class => 'tc5', sub { txt_ 'Tags'; sortable_ 'tags', $opt, \&url }; + } }; + tr_ sub { + my $l = $_; + td_ class => 'tc1', sub { user_ $l }; + td_ class => 'tc2', fmtdate $l->{registered}; + td_ mkclass(tc3 => 1, linethrough => $l->{hide_list} && auth->permUsermod), sub { + if($l->{hide_list} && !auth->permUsermod) { + txt_ '-'; + } elsif(!$l->{c_votes}) { + txt_ '0'; + } else { + a_ href => "/u$l->{user_id}/votes", $l->{c_votes}; + } + }; + td_ class => 'tc4', sub { + txt_ '-' if !$l->{c_changes}; + a_ href => "/u$l->{user_id}/hist", $l->{c_changes} if $l->{c_changes}; + }; + td_ class => 'tc5', sub { + txt_ '-' if !$l->{c_tags}; + a_ href => "/g/links?u=$l->{user_id}", $l->{c_tags} if $l->{c_tags}; + }; + } for @$list; + }; + }; + paginate_ \&url, $opt->{p}, [$count, 50], 'b'; +} + + +TUWF::get qr{/u/(?<char>[0a-z]|all)}, sub { + my $char = tuwf->capture('char'); + + my $opt = eval { tuwf->validate(get => + p => { upage => 1 }, + s => { required => 0, default => 'registered', enum => [qw[username registered votes changes tags]] }, + o => { required => 0, default => 'd', enum => [qw[a d]] }, + q => { required => 0, default => '' }, + )->data } || return tuwf->resNotFound; + + my @where = ( + $char eq 'all' ? () : $char eq '0' ? "ascii(username) not between ascii('a') and ascii('z')" : "username like '$char%'", + $opt->{q} ? sql_or( + $opt->{q} =~ /^u?([0-9]+)$/ ? sql 'id =', \"$1" : (), + sql 'position(', \$opt->{q}, 'in username) > 0' + ) : () + ); + + my($list) = tuwf->dbPagei({ results => 50, page => $opt->{p} }, + 'SELECT', sql_user(), ',', sql_totime('registered'), 'as registered, c_votes, c_changes, c_tags, hide_list + FROM users u + WHERE', sql_and('id > 0', @where), + 'ORDER BY', { + username => 'username', + registered => 'id', + votes => auth->permUsermod ? 'c_votes' : 'hide_list, c_votes', + changes => 'c_changes', + tags => 'c_tags' + }->{$opt->{s}}, $opt->{o} eq 'd' ? 'DESC' : 'ASC' + ); + my $count = @where ? tuwf->dbVali('SELECT count(*) FROM users WHERE', sql_and @where) : tuwf->{stats}{users}; + + framework_ title => 'Browse users', index => 0, sub { + div_ class => 'mainbox', sub { + h1_ 'Browse users'; + form_ action => '/u/all', method => 'get', sub { + searchbox_ u => $opt->{q}; + }; + p_ class => 'browseopts', sub { + a_ href => "/u/$_", $_ eq $char ? (class => 'optselected') : (), $_ eq 'all' ? 'ALL' : $_ ? uc $_ : '#' + for ('all', 'a'..'z', 0); + }; + }; + listing_ $opt, $list, $count if $count; + }; +}; + +1; diff --git a/lib/VNWeb/Validation.pm b/lib/VNWeb/Validation.pm index 5ddc1ac3..eb29a52c 100644 --- a/lib/VNWeb/Validation.pm +++ b/lib/VNWeb/Validation.pm @@ -19,6 +19,7 @@ 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 }, + upage => { uint => 1, min => 1, required => 0, default => 1 }, # pagination without a maximum username => { regex => qr/^(?!-*[a-z][0-9]+-*$)[a-z0-9-]*$/, minlength => 2, maxlength => 15 }, password => { length => [ 4, 500 ] }, }; |