summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2008-11-12 12:41:37 +0100
committerYorhel <git@yorhel.nl>2008-11-12 12:41:37 +0100
commit7f898c439eb139537545eda6eb3ccc580ac8460b (patch)
treeed1f197846ca2b95db5135fa2283746a4088ba62 /lib
parentfadb037c536c60c91bf978c2604ca9eab818f3ff (diff)
Created VNDB::Func for a few small and often used functions, split CommonHTML in LayoutHTML, and abstracted the browser generation
I was planning to commit often, but I still end up doing everything in one commit >.>
Diffstat (limited to 'lib')
-rw-r--r--lib/VNDB/Func.pm26
-rw-r--r--lib/VNDB/Handler/Users.pm84
-rw-r--r--lib/VNDB/Util/CommonHTML.pm227
-rw-r--r--lib/VNDB/Util/LayoutHTML.pm146
4 files changed, 281 insertions, 202 deletions
diff --git a/lib/VNDB/Func.pm b/lib/VNDB/Func.pm
new file mode 100644
index 00000000..9e948e0d
--- /dev/null
+++ b/lib/VNDB/Func.pm
@@ -0,0 +1,26 @@
+
+package VNDB::Func;
+
+use strict;
+use warnings;
+use Exporter 'import';
+use POSIX 'strftime';
+our @EXPORT = qw| shorten date |;
+
+
+# I would've done this as a #define if this was C...
+sub shorten {
+ my($str, $len) = @_;
+ return length($str) > $len ? substr($str, 0, $len-3).'...' : $str;
+}
+
+
+# argument: unix timestamp
+# return value: yyyy-mm-dd
+# (maybe an idea to use cgit-style ages for recent timestamps)
+sub date {
+ return strftime '%Y-%m-%d', gmtime shift;
+}
+
+
+1;
diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm
index 9a8929df..1dc014d3 100644
--- a/lib/VNDB/Handler/Users.pm
+++ b/lib/VNDB/Handler/Users.pm
@@ -5,7 +5,7 @@ use strict;
use warnings;
use YAWF ':html';
use Digest::MD5 'md5_hex';
-use POSIX 'strftime';
+use VNDB::Func;
YAWF::register(
@@ -339,54 +339,25 @@ sub list {
page => $f->{p},
);
- if($f->{p} > 1 || $np) {
- ul class => 'maintabs notfirst';
- if($f->{p} > 1) {
- li class => 'left';
- a href => "/u/list/$char?o=$f->{o}&s=$f->{s}&p=".($f->{p}-1), '<- previous';
- end;
- }
- if($np) {
- li;
- a href => "/u/list/$char?o=$f->{o}&s=$f->{s}&p=".($f->{p}+1), 'next ->';
- end;
- }
- end;
- }
-
- div class => 'mainbox browse';
- table;
- thead;
- Tr;
- td class => 'tc1';
- txt 'Username ';
- lit $f->{s} eq 'username' && $f->{o} eq 'a' ? "\x{25B4}" : qq|<a href="/u/list/$char?o=a&s=username">\x{25B4}</a>|;
- lit $f->{s} eq 'username' && $f->{o} eq 'd' ? "\x{25BE}" : qq|<a href="/u/list/$char?o=d&s=username">\x{25BE}</a>|;
- end;
- td class => 'tc2';
- txt 'Registered ';
- lit $f->{s} eq 'registered' && $f->{o} eq 'a' ? "\x{25B4}" : qq|<a href="/u/list/$char?o=a&s=registered">\x{25B4}</a>|;
- lit $f->{s} eq 'registered' && $f->{o} eq 'd' ? "\x{25BE}" : qq|<a href="/u/list/$char?o=d&s=registered">\x{25BE}</a>|;
- end;
- td class => 'tc3';
- txt 'Votes ';
- lit $f->{s} eq 'votes' && $f->{o} eq 'a' ? "\x{25B4}" : qq|<a href="/u/list/$char?o=a&s=votes">\x{25B4}</a>|;
- lit $f->{s} eq 'votes' && $f->{o} eq 'd' ? "\x{25BE}" : qq|<a href="/u/list/$char?o=d&s=votes">\x{25BE}</a>|;
- end;
- td class => 'tc4';
- txt 'Edits ';
- lit $f->{s} eq 'changes' && $f->{o} eq 'a' ? "\x{25B4}" : qq|<a href="/u/list/$char?o=a&s=changes">\x{25B4}</a>|;
- lit $f->{s} eq 'changes' && $f->{o} eq 'd' ? "\x{25BE}" : qq|<a href="/u/list/$char?o=d&s=changes">\x{25BE}</a>|;
- end;
- end;
- end;
- for(0..$#$list) {
- my $l = $list->[$_];
- Tr $_ % 2 ? (class => 'odd') : ();
+ $self->htmlBrowse(
+ items => $list,
+ options => $f,
+ nextpage => $np,
+ pageurl => "/u/list/$char?o=$f->{o}&s=$f->{s}",
+ sorturl => "/u/list/$char",
+ header => [
+ [ 'Username', 'username' ],
+ [ 'Registered', 'registered' ],
+ [ 'Votes', 'votes' ],
+ [ 'Edits', 'changes' ],
+ ],
+ row => sub {
+ my($s, $n, $l) = @_;
+ Tr $n % 2 ? (class => 'odd') : ();
td class => 'tc1';
a href => '/u'.$l->{id}, $l->{username};
end;
- td class => 'tc2', strftime '%Y-%m-%d', gmtime $l->{registered};
+ td class => 'tc2', date $l->{registered};
td class => 'tc3';
lit !$l->{show_list} ? '-' : !$l->{c_votes} ? 0 :
qq|<a href="/u$l->{id}/list">$l->{c_votes}</a>|;
@@ -395,25 +366,8 @@ sub list {
lit !$l->{c_changes} ? 0 : qq|<a href="/u$l->{id}/hist">$l->{c_changes}</a>|;
end;
end;
- }
- end;
- end;
-
- if($f->{p} > 1 || $np) {
- ul class => 'maintabs bottom';
- if($f->{p} > 1) {
- li class => 'left';
- a href => "/u/list/$char?o=$f->{o}&s=$f->{s}&p=".($f->{p}-1), '<- previous';
- end;
- }
- if($np) {
- li;
- a href => "/u/list/$char?o=$f->{o}&s=$f->{s}&p=".($f->{p}+1), 'next ->';
- end;
- }
- end;
- }
-
+ },
+ );
$self->htmlFooter;
}
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index 1f0c3edb..bb385916 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -6,143 +6,7 @@ use warnings;
use YAWF ':html';
use Exporter 'import';
-our @EXPORT = qw|
- htmlHeader htmlFooter htmlMainTabs htmlDenied
-|;
-
-
-sub htmlHeader { # %options->{ title }
- my($self, %o) = @_;
-
- # heading
- html;
- head;
- title $o{title};
- Link rel => 'shortcut icon', href => '/favicon.ico', type => 'image/x-icon';
- Link rel => 'stylesheet', href => $self->{url_static}.'/f/style.css', type => 'text/css', media => 'all';
- script type => 'text/javascript', src => $self->{url_static}.'/f/script.js';
- # most browsers don't like a self-closing <script> tag...
- end;
- end;
- body;
- div id => 'bgright', ' ';
- div id => 'header';
- h1;
- a href => '/', 'the visual novel database';
- end;
- end;
-
- _menu($self, %o);
-
- div id => 'maincontent';
-}
-
-
-sub _menu {
- my $self = shift;
-
- div id => 'menulist';
-
- div class => 'menubox';
- h2 'Menu';
- div;
- for (
- [ '/' => 'Home' ],
- [ '#' => 'Visual Novels' ],
- [ '#' => 'Producers' ],
- [ '/u/list/all' => 'Users' ],
- [ '#' => 'Recent Changes' ],
- [ '#' => 'Discussion Board' ],
- [ '#' => 'FAQ' ]) {
- a href => $$_[0], $$_[1];
- br;
- }
- end;
- end;
-
- div class => 'menubox';
- if($self->authInfo->{id}) {
- my $uid = sprintf '/u%d', $self->authInfo->{id};
- h2;
- a href => $uid, ucfirst $self->authInfo->{username};
- txt ' ('.$self->{user_ranks}[$self->authInfo->{rank}][0].')';
- end;
- div;
- a href => "$uid/edit", 'My Profile'; br;
- a href => "$uid/list", 'My Visual Novel List'; br;
- a href => "$uid/wish", 'My Wishlist'; br;
- a href => "/t$uid", 'My Messages'; br;
- a href => "$uid/hist", 'My Recent Changes'; br;
- br;
- a href => '/v/new', 'Add Visual Novel'; br;
- a href => '/p/new', 'Add Producer'; br;
- br;
- a href => '/u/logout', 'Logout';
- end;
- } else {
- h2;
- a href => '/u/login', 'Login';
- end;
- div;
- form action => '/nospam?/u/login', id => 'loginform', method => 'post';
- fieldset;
- legend 'Login';
- input type => 'text', class => 'text', id => 'username', name => 'usrname';
- input type => 'password', class => 'text', id => 'userpass', name => 'usrpass';
- input type => 'submit', class => 'submit', value => 'Login';
- end;
- end;
- p;
- lit 'Need to <a href="/u/register">register</a>,<br />';
- lit 'or <a href="/u/newpass">forgot your password?</a>';
- end;
- end;
- }
- end;
-
- my @stats = (
- [ vn => 'Visual Novels' ],
- [ releases => 'Releases' ],
- [ producers => 'Producers' ],
- [ users => 'Users' ],
- [ threads => 'Threads' ],
- [ posts => 'Posts' ],
- );
- my $stats = $self->dbStats(map $$_[0], @stats);
- div class => 'menubox';
- h2 'Database Statistics';
- div;
- dl;
- for (@stats) {
- dt $$_[1];
- dd $stats->{$$_[0]};
- }
- end;
- br style => 'clear: left';
- end;
- end;
- end;
-}
-
-
-sub htmlFooter {
- my $self = shift;
- end; # /div maincontent
- end; # /body
- end; # /html
-
- # write the SQL queries as a HTML comment when debugging is enabled
- if($self->debug) {
- lit "\n<!--\n SQL Queries:\n";
- for (@{$self->{_YAWF}{DB}{queries}}) {
- my $q = !ref $_->[0] ? $_->[0] :
- $_->[0][0].(exists $_->[0][1] ? ' | "'.join('", "', @{$_->[0]}[1..$#{$_->[0]}]).'"' : '');
- $q =~ s/^\s//g;
- lit sprintf " [%6.2fms] %s\n", $_->[1]*1000, $q;
- }
- lit "-->\n";
- }
-}
+our @EXPORT = qw|htmlMainTabs htmlDenied htmlBrowse|;
# generates the "main tabs". These are the commonly used tabs for
@@ -216,4 +80,93 @@ sub htmlDenied {
}
+# generates a browse box, arguments:
+# items => arrayref with the list items
+# options => hashref containing at least the keys s (sort key), o (order) and p (page)
+# nextpage => whether there's a next page or not
+# sorturl => base URL to append the sort options to
+# pageurl => base URL to append the page option to
+# header =>
+# can be either an arrayref or subroutine reference,
+# in the case of a subroutine, it will be called when the header should be written,
+# in the case of an arrayref, the array should contain the header items. Each item
+# can again be either an arrayref or subroutine ref. The arrayref would consist of
+# two elements: the name of the header, and the name of the sorting column if it can
+# be sorted
+# row => subroutine ref, which is called for each item in $list, arguments will be
+# $self, $item_number (starting from 0), $item_value
+sub htmlBrowse {
+ my($self, %opt) = @_;
+
+ $opt{sorturl} .= $opt{sorturl} =~ /\?/ ? '&' : '?';
+ $opt{pageurl} .= $opt{pageurl} =~ /\?/ ? '&p=' : '?p=';
+
+ # top navigation
+ if($opt{options}{p} > 1 || $opt{nextpage}) {
+ ul class => 'maintabs notfirst';
+ if($opt{options}{p} > 1) {
+ li class => 'left';
+ a href => $opt{pageurl}.($opt{options}{p}-1), '<- previous';
+ end;
+ }
+ if($opt{nextpage}) {
+ li;
+ a href => $opt{pageurl}.($opt{options}{p}+1), 'next ->';
+ end;
+ }
+ end;
+ }
+
+ div class => 'mainbox browse';
+ table;
+
+ # header
+ thead;
+ Tr;
+ if(ref $opt{header} eq 'CODE') {
+ $opt{header}->($self);
+ } else {
+ for(0..$#{$opt{header}}) {
+ if(ref $opt{header}[$_] eq 'CODE') {
+ $opt{header}[$_]->($self, $_+1);
+ } else {
+ td class => 'tc'.($_+1);
+ lit $opt{header}[$_][0];
+ if($opt{header}[$_][1]) {
+ lit ' ';
+ lit $opt{options}{s} eq $opt{header}[$_][1] && $opt{options}{o} eq 'a' ? "\x{25B4}" : qq|<a href="$opt{sorturl}o=a&s=$opt{header}[$_][1]">\x{25B4}</a>|;
+ lit $opt{options}{s} eq $opt{header}[$_][1] && $opt{options}{o} eq 'd' ? "\x{25BE}" : qq|<a href="$opt{sorturl}o=d&s=$opt{header}[$_][1]">\x{25BE}</a>|;
+ }
+ end;
+ }
+ }
+ }
+ end;
+ end;
+
+ # rows
+ $opt{row}->($self, $_+1, $opt{items}[$_])
+ for 0..$#{$opt{items}};
+
+ end;
+ end;
+
+ # bottom navigation
+ if($opt{options}{p} > 1 || $opt{nextpage}) {
+ ul class => 'maintabs bottom';
+ if($opt{options}{p} > 1) {
+ li class => 'left';
+ a href => $opt{pageurl}.($opt{options}{p}-1), '<- previous';
+ end;
+ }
+ if($opt{nextpage}) {
+ li;
+ a href => $opt{pageurl}.($opt{options}{p}+1), 'next ->';
+ end;
+ }
+ end;
+ }
+}
+
+
1;
diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm
new file mode 100644
index 00000000..f72eef88
--- /dev/null
+++ b/lib/VNDB/Util/LayoutHTML.pm
@@ -0,0 +1,146 @@
+
+package VNDB::Util::LayoutHTML;
+
+use strict;
+use warnings;
+use YAWF ':html';
+use Exporter 'import';
+
+our @EXPORT = qw|htmlHeader htmlFooter|;
+
+
+sub htmlHeader { # %options->{ title }
+ my($self, %o) = @_;
+
+ # heading
+ html;
+ head;
+ title $o{title};
+ Link rel => 'shortcut icon', href => '/favicon.ico', type => 'image/x-icon';
+ Link rel => 'stylesheet', href => $self->{url_static}.'/f/style.css', type => 'text/css', media => 'all';
+ script type => 'text/javascript', src => $self->{url_static}.'/f/script.js';
+ # most browsers don't like a self-closing <script> tag...
+ end;
+ end;
+ body;
+ div id => 'bgright', ' ';
+ div id => 'header';
+ h1;
+ a href => '/', 'the visual novel database';
+ end;
+ end;
+
+ _menu($self, %o);
+
+ div id => 'maincontent';
+}
+
+
+sub _menu {
+ my $self = shift;
+
+ div id => 'menulist';
+
+ div class => 'menubox';
+ h2 'Menu';
+ div;
+ for (
+ [ '/' => 'Home' ],
+ [ '#' => 'Visual Novels' ],
+ [ '#' => 'Producers' ],
+ [ '/u/list/all' => 'Users' ],
+ [ '#' => 'Recent Changes' ],
+ [ '#' => 'Discussion Board' ],
+ [ '#' => 'FAQ' ]) {
+ a href => $$_[0], $$_[1];
+ br;
+ }
+ end;
+ end;
+
+ div class => 'menubox';
+ if($self->authInfo->{id}) {
+ my $uid = sprintf '/u%d', $self->authInfo->{id};
+ h2;
+ a href => $uid, ucfirst $self->authInfo->{username};
+ txt ' ('.$self->{user_ranks}[$self->authInfo->{rank}][0].')';
+ end;
+ div;
+ a href => "$uid/edit", 'My Profile'; br;
+ a href => "$uid/list", 'My Visual Novel List'; br;
+ a href => "$uid/wish", 'My Wishlist'; br;
+ a href => "/t$uid", 'My Messages'; br;
+ a href => "$uid/hist", 'My Recent Changes'; br;
+ br;
+ a href => '/v/new', 'Add Visual Novel'; br;
+ a href => '/p/new', 'Add Producer'; br;
+ br;
+ a href => '/u/logout', 'Logout';
+ end;
+ } else {
+ h2;
+ a href => '/u/login', 'Login';
+ end;
+ div;
+ form action => '/nospam?/u/login', id => 'loginform', method => 'post';
+ fieldset;
+ legend 'Login';
+ input type => 'text', class => 'text', id => 'username', name => 'usrname';
+ input type => 'password', class => 'text', id => 'userpass', name => 'usrpass';
+ input type => 'submit', class => 'submit', value => 'Login';
+ end;
+ end;
+ p;
+ lit 'Need to <a href="/u/register">register</a>,<br />';
+ lit 'or <a href="/u/newpass">forgot your password?</a>';
+ end;
+ end;
+ }
+ end;
+
+ my @stats = (
+ [ vn => 'Visual Novels' ],
+ [ releases => 'Releases' ],
+ [ producers => 'Producers' ],
+ [ users => 'Users' ],
+ [ threads => 'Threads' ],
+ [ posts => 'Posts' ],
+ );
+ my $stats = $self->dbStats(map $$_[0], @stats);
+ div class => 'menubox';
+ h2 'Database Statistics';
+ div;
+ dl;
+ for (@stats) {
+ dt $$_[1];
+ dd $stats->{$$_[0]};
+ }
+ end;
+ br style => 'clear: left';
+ end;
+ end;
+ end;
+}
+
+
+sub htmlFooter {
+ my $self = shift;
+ end; # /div maincontent
+ end; # /body
+ end; # /html
+
+ # write the SQL queries as a HTML comment when debugging is enabled
+ if($self->debug) {
+ lit "\n<!--\n SQL Queries:\n";
+ for (@{$self->{_YAWF}{DB}{queries}}) {
+ my $q = !ref $_->[0] ? $_->[0] :
+ $_->[0][0].(exists $_->[0][1] ? ' | "'.join('", "', @{$_->[0]}[1..$#{$_->[0]}]).'"' : '');
+ $q =~ s/^\s//g;
+ lit sprintf " [%6.2fms] %s\n", $_->[1]*1000, $q;
+ }
+ lit "-->\n";
+ }
+}
+
+
+1;