summaryrefslogtreecommitdiff
path: root/data
diff options
context:
space:
mode:
authoryorhel <yorhel@1fe2e327-d9db-4752-bcf7-ef0cb4a1748b>2008-04-13 13:45:20 +0000
committeryorhel <yorhel@1fe2e327-d9db-4752-bcf7-ef0cb4a1748b>2008-04-13 13:45:20 +0000
commitd7046f5d38004ff20739798c18f5796c31676546 (patch)
tree1639e6a8c3b74588bff7be6aaf6cf5e04e3bc63f /data
W00t, VNDB on SVN!
git-svn-id: svn://vndb.org/vndb@1 1fe2e327-d9db-4752-bcf7-ef0cb4a1748b
Diffstat (limited to 'data')
-rw-r--r--data/tpl/defs.pl482
-rw-r--r--data/tpl/docs298
-rw-r--r--data/tpl/error45
-rw-r--r--data/tpl/faq75
-rw-r--r--data/tpl/hist103
-rw-r--r--data/tpl/home67
-rw-r--r--data/tpl/main14
-rw-r--r--data/tpl/myvotes30
-rw-r--r--data/tpl/page140
-rw-r--r--data/tpl/pbrowse45
-rw-r--r--data/tpl/pedit45
-rw-r--r--data/tpl/ppage58
-rw-r--r--data/tpl/redit70
-rw-r--r--data/tpl/rpage61
-rw-r--r--data/tpl/useredit34
-rw-r--r--data/tpl/userlist54
-rw-r--r--data/tpl/userlogin14
-rw-r--r--data/tpl/userpage13
-rw-r--r--data/tpl/userpass21
-rw-r--r--data/tpl/userreg38
-rw-r--r--data/tpl/vnbrowse87
-rw-r--r--data/tpl/vnedit94
-rw-r--r--data/tpl/vnlist74
-rw-r--r--data/tpl/vnpage171
-rw-r--r--data/tpl/vnpage_rel51
-rw-r--r--data/tpl/vnpage_rg11
-rw-r--r--data/tpl/vnpage_stats68
27 files changed, 2263 insertions, 0 deletions
diff --git a/data/tpl/defs.pl b/data/tpl/defs.pl
new file mode 100644
index 00000000..b68faab0
--- /dev/null
+++ b/data/tpl/defs.pl
@@ -0,0 +1,482 @@
+[[!
+
+use Time::CTime ();
+use Algorithm::Diff 'sdiff';
+use POSIX ('ceil', 'floor');
+
+my %p; # $X->{page} global page data
+my %d; # $X->{page}->{$p} local page data
+
+# redefine _hchar - usually a bad idea, but who cares
+sub _hchar {local$_=shift||return'';s/&/&amp;/g;s/</&lt;/g;s/>/&gt;/g;s/"/&quot;/g;s/\r?\n/ <br \/>\n/g;return$_;}
+
+sub formatdate {return _hchar(Time::CTime::strftime($_[0],gmtime($_[1]||0)))||'';}
+sub txt {local$_=shift||return'';s/&/&amp;/g;s/</&lt;/g;s/>/&gt;/g;return$_;}
+sub art2str {my$r='';$r.=($r?' & ':'').$_->{name}foreach (@{$_[0]->{artists}});return $_[1]?$r:_hchar($r);}
+sub calctime {my$r=shift;return'0:00:00'if!$r;my$x=sprintf'%d:%02d:%02d',int($r/3600),int(($r%3600)/60),($r%3600)%60;return $x;}
+sub shorten {local$_=shift||return'';return length>$_[0]?substr($_,0,$_[0]-3).'...':$_};
+
+# Date string format: yyyy-mm-dd
+# y = 0 -> Unknown
+# y = 9999 -> TBA (To Be Announced)
+# m = 0 -> Month + day unknown, year known
+# d = 0 -> Day unknown, month + year known
+sub datestr {
+ my $d = $_[0]||'00000000';
+ my @d = map { int } $1, $2, $3 if $d =~ /^([0-9]{4})([0-9]{2})([0-9]{2})$/;
+ return 'unknown' if $d[0] == 0;
+ my $r = sprintf !$d[1] ? '%04d' : !$d[2] ? '%04d-%02d' : '%04d-%02d-%02d', @d;
+ my $b = $r gt Time::CTime::strftime("%Y-%m-%d", gmtime());
+ $r = 'TBA' if $d[0] == 9999;
+ return ($b?'<b class="future">':'').$r.($b?'</b>':'');
+}
+sub mediastr {
+ return join(', ', map {
+ $_->{medium} =~ /^(cd|dvd|gdr|blr)$/
+ ? sprintf('%d %s%s', $_->{qty}, $VNDB::MED->{$_->{medium}}, $_->{qty}>1?'s':'')
+ : $VNDB::MED->{$_->{medium}}
+ } @{$_[0]});
+}
+sub sortbut { # url, col
+ my $r=' '; my $u = _hchar($_[0]);
+ $u .= $u =~ /\?/ ? ';' : '?';
+ for ('a', 'd') {
+ my $chr = $_ eq 'd' ? "\x{25BE}" : "\x{25B4}";
+ $r .= $d{order}[0] eq $_[1] && $d{order}[1] eq $_ ? $chr :
+ sprintf '<a href="%ss=%s;o=%s">%s</a>', $u, $_[1], $_, $chr;
+ }
+ return $r;
+}
+sub pagebut { # url
+ my @br; my $ng = $_[0] =~ /\?/ ? ';' : '?';
+ push @br, sprintf '<a href="%s">&lt;- previous</a>', $_[0].($d{page}-2 ? $ng.'p='.($d{page}-1) : '') if $d{page} > 1;
+ push @br, sprintf '<a href="%s">next -&gt;</a>', $_[0].$ng.'p='.($d{page}+1) if $d{npage};
+ return $#br >= 0 ? ('<p class="browse">( '.join(' | ', @br).' )</p>') : '';
+}
+sub wraplong { # text, margin
+ local $_ = $_[0];
+ my $m = $_[1]/2;
+ s/([^\s\r\n]{$m})([^\s\r\n])/$1 $2/g;
+ return $_;
+}
+
+
+sub wordsplit { # split a string into an array of words, but make sure to not split HTML tags
+# return [ split //, $_[0] ];
+ my @a;
+ my $in='';
+ for (split /\s+/, $_[0]) {
+ my $gt = () = />/g;
+ my $lt = () = /</g;
+ if($in && $gt > $lt) {
+ push @a, $in.$_;
+ $in='';
+ } elsif($lt > $gt || $in) {
+ $in .= $_.' ';
+ } else {
+ push @a, $_;
+ };
+ }
+ push @a, $in if $in;
+ return \@a;
+}
+
+sub cdiff { # obj1, obj2, @items->[ short, name, serialise, diff, [parsed_x, parsed_y] ]
+ my($x, $y, @items, @c) = @_;
+ # serialise = 0 -> integer, 1 -> string, CODEref -> code
+
+ my $type = defined $$y{minage} ? 'r' : defined $$y{length} ? 'v' : 'p';
+ my $pre = '<div id="revbrowse">'.
+ ($$y{next} ? qq|<a href="/$type$$y{id}?rev=$$y{next}" id="revnext">later revision -&gt;</a>| : '').
+ ($x ? qq|<a href="/$type$$y{id}?rev=$$x{cid}" id="revprev">&lt;- earlier revision</a>| : '').
+ qq|<a href="/$type$$y{id}" id="revmain">$type$$y{id}</a>&nbsp;</div>|;
+
+ if(!$x) { # just show info about the revision if there is no previous edit
+ return $pre.qq|<div id="tmc"><b>Revision $$y{cid}</b> (<a href="/$type$$y{id}/edit?rev=$$y{cid}">edit</a>)<br />By <a href="/u$$y{requester}">$$y{username}</a> on |.
+ formatdate('%Y-%m-%d at %R', $$y{added}).'<br /><b>Edit summary:</b><br /><br />'.
+ summary($$y{comments}, 0, '[no summary]').'</div>';
+ }
+ for (@items) {
+ $_->[4] = !$_->[2] ? $x->{$_->[0]}||'0' : !ref($_->[2]) ? _hchar(wraplong($x->{$_->[0]}||'[empty]',60)) : &{$_->[2]}($x->{$_->[0]})||'[empty]';
+ $_->[5] = !$_->[2] ? $y->{$_->[0]}||'0' : !ref($_->[2]) ? _hchar(wraplong($y->{$_->[0]}||'[empty]',60)) : &{$_->[2]}($y->{$_->[0]})||'[empty]';
+ push(@c, $_) if $_->[4] ne $_->[5];
+ if($_->[3] && $_->[4] ne $_->[5]) {
+ my($rx,$ry,$ch) = ('','','u');
+ for (sdiff(wordsplit($_->[4]), wordsplit($_->[5]))) {
+ if($ch ne $_->[0]) {
+ if($ch ne 'u') {
+ $rx .= '</b>';
+ $ry .= '</b>';
+ }
+ $rx .= '<b class="diff_del">' if $_->[0] eq '-' || $_->[0] eq 'c';
+ $ry .= '<b class="diff_add">' if $_->[0] eq '+' || $_->[0] eq 'c';
+ }
+ $ch = $_->[0];
+ $rx .= $_->[1].' ' if $ch ne '+';
+ $ry .= $_->[2].' ' if $ch ne '-';
+ }
+ $_->[4] = $rx;
+ $_->[5] = $ry;
+ }
+ }
+ return $pre.'<table id="tmc"><thead><tr><td class="tc1">&nbsp;</td>'.
+ qq|<td class="tc2"><b>Revision $$x{cid}</b> (<a href="/$type$$y{id}/edit?rev=$$x{cid}">edit</a>)<br />By <a href="/u$$x{requester}">$$x{username}</a> on |.formatdate('%Y-%m-%d at %R', $$x{added}).'</td>'.
+ qq|<td class="tc3"><b>Revision $$y{cid}</b> (<a href="/$type$$y{id}/edit?rev=$$y{cid}">edit</a>)<br />By <a href="/u$$y{requester}">$$y{username}</a> on |.formatdate('%Y-%m-%d at %R', $$y{added}).'</td>'.
+ '</tr><tr></tr><tr><td>&nbsp;</td><td colspan="2"><b>Edit summary of revision '.$$y{cid}.'</b><br /><br />'.summary($$y{comments}, 0, '[no summary]').'<br /><br /></td></tr></thead>'.
+ join('',map{
+ '<tr><td class="tc1">'.$_->[1].'</td><td class="tc2">'.$_->[4].'</td><td class="tc3">'.$_->[5].'</td></tr>'
+ } @c).'</table>';
+}
+
+
+sub summary { # cmd, len, def
+ return $_[2]||'' if !$_[0];
+ my $res = '';
+ my $len = 0;
+ my $as = 0;
+ for (split / /, $_[0]) {
+ next if !$_;
+ my $l = length;
+ s/\&/&amp;/g;
+ s/>/&gt;/g;
+ s/</&lt;/g;
+ while(s/\[url=((https?:\/\/|\/)[^\]>]+)\]/<a href="$1" rel="nofollow">/) {
+ $l -= length($1)+6;
+ $as++;
+ }
+ if(!$as && s/(http|https):\/\/(.+[0-9a-zA-Z\/])/<a href="$1:\/\/$2" rel="nofollow">link<\/a>/) {
+ $l = 4;
+ } elsif(!$as) {
+ s/^([uvpr][0-9]+)[^\w]*$/<a href="\/$1">$1<\/a>/;
+ }
+ while(s/\[\/url\]/<\/a>/) {
+ $l -= 6;
+ $as--;
+ }
+ $len += $l + 1;
+ last if $_[1] && $len > $_[1];
+ $res .= "$_ ";
+ }
+ $res =~ y/\r\n/ / if $_[1];
+ $res =~ s/\r?\n/<br \/>/g if !$_[1];
+ $res =~ s/ +$//;
+ $res .= '</a>' x $as if $as;
+ $res .= '...' if $_[1] && $len > $_[1];
+ return $res;
+}
+
+
+sub ttabs { # [vrp], obj, sel
+ my($t, $o, $s) = @_;
+ $s||='';
+ my @act = (
+ !$s?'%s':'<a href="/%s">%1$s</a>',
+ $$o{locked} ?
+ '<b>locked for editing</b>' : (),
+ $p{Authlock} ?
+ sprintf('<a href="/%%s/lock">%s</a>', $$o{locked} ? 'unlock' : 'lock') : (),
+ $p{Authdel} ? (
+ '<a href="/%s/del" id="idel">del</a>',
+ sprintf('<a href="/%%s/hide"%s>%s</a>', $t eq 'v' ? ' id="vhide"' : '', $$o{hidden} ? 'unhide' : 'hide')
+ ) : (),
+ !$$o{locked} || ($p{Authedit} && $p{Authlock}) ?
+ ($s eq 'edit' ? 'edit' : '<a href="/%s/edit" '.($t eq 'v' || $t eq 'r' ? 'class="dropdown" rel="editDD"':'').'>edit</a>') : (),
+
+ $p{Authhist} ?
+ ($s eq 'hist' ? 'history' : '<a href="/%s/hist">history</a>') : (),
+ );
+ return '<p class="mod">&lt; '.join(' - ', map { sprintf $_, $t.$$o{id} } @act).' &gt;</p>'.(
+ $t eq 'v' ? qq|
+<div id="editDD" class="dropdown">
+ <ul>
+ <li><a href="/v$$o{id}/edit" rel="nofollow">Edit all</a></li>
+ <li><a href="/v$$o{id}/edit?fh=info" rel="nofollow">General info</a></li>
+ <li><a href="/v$$o{id}/edit?fh=cat" rel="nofollow">Categories</a></li>
+ <li><a href="/v$$o{id}/edit?fh=rel" rel="nofollow">Relations</a></li>
+ <li><a href="/v$$o{id}/edit?fh=img" rel="nofollow">Upload image</a></li>
+ <li><a href="/v$$o{id}/add" rel="nofollow">Add release</a></li>
+ </ul>
+</div>| : $t eq 'r' ? qq|
+<div id="editDD" class="dropdown">
+ <ul>
+ <li><a href="/r$$o{id}/edit" rel="nofollow">Edit all</a></li>
+ <li><a href="/r$$o{id}/edit?fh=info" rel="nofollow">General info</a></li>
+ <li><a href="/r$$o{id}/edit?fh=pnm" rel="nofollow">Platforms &amp; media</a></li>
+ <li><a href="/r$$o{id}/edit?fh=prod" rel="nofollow">Producers</a></li>
+ <li><a href="/r$$o{id}/edit?fh=rel" rel="nofollow">Relations</a></li>
+ </ul>
+</div>| : ''
+ );
+}
+
+
+
+my %pagetitles = (
+ faq => 'Frequently Asked Questions',
+ userlogin => 'Login',
+ userreg => 'Register a new account',
+ userpass => 'Forgot your password?',
+ home => 'Visual Novel Database',
+ pbrowse => 'Browse producers',
+ userlist => 'Browse users',
+ myvotes => sub {
+ return $p{myvotes}{user}{username} eq $p{AuthUsername} ? 'My votes' : ('Votes by '.$p{myvotes}{user}{username}); },
+ userpage => sub {
+ return 'User: '.$p{userpage}{user}{username} },
+ vnlist => sub {
+ return $p{vnlist}{user}{username} eq $p{AuthUsername} ? 'My visual novel list' : ($p{vnlist}{user}{username}.'\'s visual novel list'); },
+ useredit => sub {
+ return !$p{useredit}{adm} ? 'My account' : 'Edit '.$p{useredit}{form}{username}.'\'s account'; },
+ ppage => sub {
+ return $p{ppage}{prod}{name} },
+ pedit => sub {
+ return $p{pedit}{id} ? sprintf('Edit %s', $p{pedit}{form}{name}) : 'Add a new producer'; },
+ vnedit => sub {
+ return $p{vnedit}{id} ? sprintf('Edit %s', $p{vnedit}{form}{title}) : 'Add a new visual novel'; },
+ redit => sub {
+ return $p{redit}{id} ? sprintf('Edit %s', $p{redit}{rel}{title}) : sprintf('Add release to %s', $p{redit}{vn}{title}); },
+ vnpage => sub { return $p{vnpage}{vn}{title}; },
+ vnrg => sub { return 'Relations for '.$p{vnrg}{vn}{title} },
+ vnstats => sub { return 'User statistics for '.$p{vnstats}{vn}{title} },
+ vnbrowse => sub {
+ return $p{vnbrowse}{chr} eq 'search' ? sprintf 'Search results for "%s"', $p{searchquery} :
+ $p{vnbrowse}{chr} eq 'cat' ? 'Browse categories' :
+ $p{vnbrowse}{chr} eq 'mod' ? 'Visual Novels awaiting moderation' :
+ $p{vnbrowse}{chr} eq 'all' ? 'Browse all visual novels' :
+ $p{vnbrowse}{chr} eq '0' ? 'Browse by char: Other' :
+ sprintf 'Browse by char: %s', uc $p{vnbrowse}{chr}; },
+ rpage => sub {
+ return $p{rpage}{rel}{romaji} || $p{rpage}{rel}{title} },
+ hist => sub {
+ return !$p{hist}{id} || !$p{hist}{type} ? 'Recent changes' :
+ $p{hist}{type} eq 'u' ? 'Recent changes by '.$p{hist}{title} : 'Edit history of '.$p{hist}{title}; },
+ docs => sub {
+ return (
+ 'Categories', 'Adding/editing a visual novel', 'Adding/editing a release',
+ 'Adding/editing a producer', 'General guidelines', 'Error parsing form',
+ )[$p{docs}{p}-1]||'' }
+);
+sub gettitle{$p{$_}&&($p{PageTitle}=ref($pagetitles{$_}) eq 'CODE' ? &{$pagetitles{$_}} : $pagetitles{$_}) for (keys%pagetitles);}
+
+
+#
+# F O R M E R R O R H A N D L I N G
+#
+my %formerr_names = (
+ mail => 'Email',
+ username => 'Username',
+ userpass => 'Password',
+ pass1 => 'Password',
+ pass2 => 'Password (second)',
+ title => 'Title',
+ desc => 'Description',
+ rel => 'Relation',
+ romaji => 'Romanized title',
+ lang => 'Language',
+ web => 'Website',
+ released => 'Release date',
+ platforms => 'Platforms',
+ media => 'Media',
+ name => 'Name',
+ vn => 'Visual novel relations',
+);
+my @formerr_msgs = (
+ sub { return sprintf 'Field "%s" is required.', @_ },
+ sub { return sprintf '%s should have at least %d characters.', @_ },
+ sub { return sprintf '%s is too large! Only %d characters allowed.', @_ },
+ sub { return
+ $_[1] eq 'mail' ? 'Invalid email address' :
+ $_[1] eq 'url' ? 'Invalid URL' :
+ $_[1] eq 'pname' ? sprintf('%s can only contain alfanumeric characters!', $_[0]) :
+ $_[1] eq 'asciiprint' ? sprintf('Only ASCII characters are allowed at %s', $_[0]) :
+ $_[1] eq 'int' ? sprintf('%s should be a number!', $_[0]) : '';
+ },
+ sub { return sprintf '%s: invalid item selected', @_ },
+ sub { return 'Invalid unicode, are you sure your browser works fine?' },
+);
+my %formerr_exeptions = (
+ loginerr => 'Invalid username or password',
+ badpass => 'Passwords do not match',
+ usrexists => 'Username already exists, please choose an other one',
+ mailexists => 'There already is a user with that email address, please request a new password if you forgot it',
+ nomail => 'No user found with that email address',
+ nojpeg => 'Image is not in JPEG format!',
+ toolarge => 'Image is too large (in filesize), try to compress it a little',
+ imgsize => 'Image is too large (in height/width), try to resize it a little',
+);
+sub formerr {
+ my @err = ref $_[0] eq 'ARRAY' ? @{$_[0]} : ();
+ return '' if $#err < 0;
+ my @msgs;
+ my $ret = '<span class="warning">
+ Error:<ul>';
+ $ret .= sprintf " <li>%s</li>\n",
+ /^([a-z0-9]+)_([0-9]+)_?(.*)$/ ? &{$formerr_msgs[$2-1]}($formerr_names{$1}, $3?$3:'') : $formerr_exeptions{$_}
+ foreach (@err);
+ $ret .= "</ul>\n</span>\n";
+}
+
+#
+# F O R M C R E A T I N G
+#
+
+# args = [
+# {
+# type => $type,
+# %options
+# }, ...
+# ], $formobj
+#
+# $type $formobj %options ( required, [ optional ] )
+# error X ( )
+# startform ( action, [ upload ] )
+# endform ( )
+# input X ( short, name, [ class, default ] )
+# pass ( short, name )
+# upload ( short, name, [ class ] )
+# hidden X ( short, [ value ] )
+# textarea X ( short, name, [ rows, cols, class ] )
+# select X ( short, name, options, [ class ] ) # options = arrayref of hashes with keys: short, name
+# as X ( name )
+# trans X ( )
+# submit ( [ text, short ] )
+# sub ( title )
+# check X ( short, name, [ value ] )
+# static ( text, raw [ name, class ] )
+# date X ( short, name )
+#
+sub cform {
+ my $obj = shift;
+ my $frm = shift;
+ my $ret = '';
+ my $csub = '';
+ for (@$obj) {
+ $_->{class} ||= '';
+ $_->{class} .= ' sf_'.$csub if $csub && $_->{class} !~ /nohid/;
+ $_->{class} .= ' formhid' if $csub && $frm->{_hid} && !$frm->{_hid}{$csub} && $_->{class} !~ /nohid/;
+ $_->{name} = '<i>*</i> '.$_->{name} if $_->{r};
+
+ # error
+ if($_->{type} eq 'error') {
+ $ret .= formerr($frm->{_err});
+ # startform
+ } elsif($_->{type} eq 'startform') {
+ $ret .= sprintf qq|<form action="/nospam?%s" method="post" accept-charset="utf-8"%s>\n|,
+ $_->{action}, $_->{upload} ? ' enctype="multipart/form-data"' : '';
+ $ret .= sprintf qq| <input type="hidden" class="hidden" name="fh" id="_hid" value="%s" />\n|,
+ $frm->{_hid} ? _hchar(join(',', keys %{$frm->{_hid}})) : '' if $_->{fh};
+ $ret .= qq|<p class="formnotice">Items denoted by a red asterisk (<i>*</i>) are required.</p>\n|
+ if scalar grep { $_->{r} } @$obj;
+ $ret .= "<ul>\n";
+ # endform
+ } elsif($_->{type} eq 'endform') {
+ $ret .= qq|</ul></form>\n|;
+ # input
+ } elsif($_->{type} eq 'input') {
+ $ret .= sprintf qq|<li%s>\n <label for="%s">%s</label>\n %s<input type="text" class="text" name="%2\$s" id="%2\$s" value="%s" />\n</li>\n|,
+ $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{short}, $_->{name}, $_->{pre} ? '<i>'.$_->{pre}.'</i>' : '',
+ _hchar($frm->{$_->{short}}?$frm->{$_->{short}}:$_->{default});
+ # pass
+ } elsif($_->{type} eq 'pass') {
+ $ret .= sprintf qq|<li%s>\n <label for="%s">%s</label>\n <input type="password" class="text" name="%2\$s" id="%2\$s" />\n</li>\n|,
+ $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{short}, $_->{name};
+ # upload
+ } elsif($_->{type} eq 'upload') {
+ $ret .= sprintf qq|<li%s>\n <label for="%s">%s</label>\n <input type="file" class="text" name="%2\$s" id="%2\$s" />\n</li>\n|,
+ $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{short}, $_->{name};
+ # hidden
+ } elsif($_->{type} eq 'hidden') {
+ $ret .= sprintf qq| <input type="hidden" class="hidden" name="%s" id="%1\$s" value="%s" />\n|,
+ $_->{short}, _hchar($_->{value} || $frm->{$_->{short}});
+ # textarea
+ } elsif($_->{type} eq 'textarea') {
+ $ret .= sprintf qq|<li%s>\n <label for="%s">%s</label>\n <textarea name="%2\$s" id="%2\$s" rows="%s" cols="%s">%s</textarea>\n</li>\n|,
+ $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{short}, $_->{name}, $_->{rows}||15, $_->{cols}||70, txt($frm->{$_->{short}});
+ # select
+ } elsif($_->{type} eq 'select') {
+ $ret .= sprintf qq|<li%s>\n <label for="%s">%s</label>\n <select name="%2\$s" id="%2\$s">\n%s</select>\n</li>\n|,
+ $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{short}, $_->{name}, eval {
+ my $r='';
+ for my $s (@{$_->{options}}) {
+ $r .= sprintf qq| <option value="%s"%s>%s</option>\n|,
+ $s->{short}, defined $frm->{$_->{short}} && $frm->{$_->{short}} eq $s->{short} ? ' selected="selected"' : '', $s->{name};
+ }
+ return $r;
+ };
+ # jssel
+ } elsif($_->{type} eq 'jssel') {
+ (my $oname = $_->{name}) =~ s/^<i>\*<\/i>//;
+ $ret .= sprintf
+ qq|<li%s>\n|
+ .qq| <label for="%s_select">%s</label>\n|
+ .qq| <select name="%s_select" id="%s_select" multiple="multiple" size="5" class="multiple">\n|
+ .qq| <option value="0_new" style="font-style: italic">Add %s...</option>\n|
+ .qq| </select>\n|
+ .qq| <div id="%s_conts">\n|
+ .qq| Loading...\n|
+ .qq| </div>\n|
+ .qq| <input type="hidden" name="%s" id="%s" class="hidden" value="%s" />\n|
+ .qq|</li>\n|,
+ $_->{class} ? ' class="'.$_->{class}.'"' : '',
+ $_->{sh}, $_->{name}, $_->{sh}, $_->{sh}, $oname, $_->{sh}, $_->{short}, $_->{short}, _hchar($frm->{$_->{short}});
+ # submit
+ } elsif($_->{type} eq 'submit') {
+ $ret .= sprintf qq|<li class="nolabel">\n <br /><input type="submit" class="submit" value="%s"%s />\n </li>\n|,
+ $_->{text} || 'Verstuur', $_->{short} ? sprintf(' name="%s" id="%1$s"', $_->{short}) : '';
+ # sub
+ } elsif($_->{type} eq 'sub') {
+ $ret .= sprintf qq|<li class="subform">\n <a href="#" class="s_%s">%s %s</a>\n</li>\n|,
+ $_->{short}, $frm->{_hid} && !$frm->{_hid}{$_->{short}} ? '&#9656;' : '&#9662;', $_->{title};
+ $csub = $_->{short};
+ # check
+ } elsif($_->{type} eq 'check') {
+ $ret .= sprintf qq|<li class="nolabel%s">\n <input type="checkbox" name="%s" id="%2\$s" value="%s"%s />\n <label for="%2\$s" class="checkbox">%s</label>\n</li>\n|,
+ $_->{class} ? ' '.$_->{class} : '',
+ $_->{short}, $_->{value} || 'true', $frm->{$_->{short}} ? ' checked="checked"' : '', $_->{name};
+ # static
+ } elsif($_->{type} eq 'static') {
+ $ret .= $_->{name}
+ ? sprintf qq|<li%s>\n <label>%s</label>\n <p>%s</p>\n</li>|, $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{name}, $_->{text}
+ : $_->{raw}
+ ? sprintf qq|<li%s>\n %s\n</li>|, $_->{class} ? ' class="'.$_->{class}.'"' : '', $_->{text}
+ : sprintf qq|<li class="nolabel%s">\n %s\n</li>|, $_->{class} ? ' '.$_->{class} : '', $_->{text};
+ # date
+ } elsif($_->{type} eq 'date') {
+ $ret .= sprintf qq|<li class="date%s">\n <label for="%s">%s</label>\n|,
+ $_->{class} ? ' '.$_->{class} : '', $_->{short}, $_->{name};
+ $ret .= sprintf qq| <select name="%s" id="%s">\n%s</select>\n|,
+ $_->{short}, $_->{short}, eval {
+ my $r='';
+ for my $s (0, 1990..((localtime())[5]+1905), 9999) {
+ $r .= sprintf qq| <option value="%s"%s>%s</option>\n|,
+ $s, $frm->{$_->{short}} && ($frm->{$_->{short}}[0]||0) == $s ? ' selected="selected"' : '',
+ !$s ? '-year-' : $s < 9999 ? $s : 'TBA';
+ }
+ return $r;
+ };
+ $ret .= sprintf qq| <select name="%s" id="%s_m">\n%s</select>\n|,
+ $_->{short}, $_->{short}, eval {
+ my $r='';
+ for my $s (0..12) {
+ $r .= sprintf qq| <option value="%s"%s>%s</option>\n|,
+ $s, $frm->{$_->{short}} && ($frm->{$_->{short}}[1]||0) == $s ? ' selected="selected"' : '',
+ $s ? $Time::CTime::MonthOfYear[$s-1] : '-month-';
+ }
+ return $r;
+ };
+ $ret .= sprintf qq| <select name="%s" id="%s_d">\n%s</select>\n</li>\n|,
+ $_->{short}, $_->{short}, eval {
+ my $r='';
+ for my $s (0..31) {
+ $r .= sprintf qq| <option value="%s"%s>%s</option>\n|,
+ $s, $frm->{$_->{short}} && ($frm->{$_->{short}}[2]||0) == $s ? ' selected="selected"' : '',
+ $s ? $s : '-day-';
+ }
+ return $r;
+ };
+ }
+ }
+ return $ret;
+}
+
+]]
diff --git a/data/tpl/docs b/data/tpl/docs
new file mode 100644
index 00000000..1ca812c2
--- /dev/null
+++ b/data/tpl/docs
@@ -0,0 +1,298 @@
+[[ if(0) { ]]
+<p class="mod">&lt; <a href="/d1">categories</a> - <a href="/d2">visual novels</a> - <a href="/d3">releases</a> - <a href="/d4">producers</a> - <a href="/d5">general guidelines</a> &gt;</p>
+[[ } ]]
+<h2>[[: $p{PageTitle} ]]</h2>
+<div id="dpage">
+
+[[ # C A T E G O R I E S
+ if($d{p} == 1) { ]]
+
+<h3>Elements</h3>
+<p>
+ ...own interpretation for now... (Should be documented at some time, too)
+</p>
+
+
+<h3>Gameplay</h3>
+<p>
+ This category is used to describe the gameplay or game engine.
+</p>
+<dl>
+ <dt>Visual Novel</dt><dd>
+ All games where the text is overlaid on the background and there is no special
+ dialog-box fall under this category. Can be abbreviated as VN or NVL.
+ </dd><dt>Adventure</dt><dd>
+ This is the opposite of the <i>Visual Novel</i> category: The text is presented
+ in a special window, usually at the bottom of the screen. In some (rare) cases
+ a game will switch between both styles, for these games both the <i>Visual Novel</i>
+ and <i>Adventure</i> categories should be selected. Can be abbreviated as ADV or AVG.
+ </dd><dt>Action</dt><dd>
+ This category indicates that the game includes a gameplay that challenges the
+ player's speed, dexterity and reaction time. Common examples are fighting games,
+ puzzles that should be solved within a short time limit, and shooter games.
+ </dd><dt>RPG</dt><dd>
+ Abbreviation for Role Playing Game. An RPG is a game in which you assume the
+ role of a character introduced to a vast world to be explored. Games typically
+ place emphasis on gaining equipment and experience points through fighting enemies
+ in order to advance through different levels.
+ </dd><dt>Strategy</dt><dd>
+ A strategy game is one that challenges the player to think critically in order
+ to achieve victory.
+ </dd><dt>Simulation</dt><dd>
+ A simulation game attempts to recreate aspects of reality and puts the player in
+ control.
+ </dd>
+</dl>
+
+<h3>Plot</h3>
+<p>
+ Indicates the plot type of a game. There are only two options: <i>Branching</i> and
+ <i>Linear</i>.
+</p>
+<dl>
+ <dt>Linear</d><dd>
+ A game with a linear plot has a static story; it is not possible to get different paths
+ or endings. Many games in this category do not prompt the player with choices and simply
+ tell the story as it is. This is, however, not a rule: it is also possible for a game
+ to provide choises, but they have no influence on the story itself. (e.g.
+ <a href="/v3">Utawarerumono</a>)
+ </dd><dt>Branching</dt><dd>
+ A game with a branching plot has a story whose path is directly affected by choices
+ made by the player during the game. These different paths are sometimes referred to
+ as "arcs" when they pertain to the stories of different female characters within a game.
+ </dd>
+</dl>
+
+<h3>Time</h3>
+<p>
+ Indicates the time period in which the story has been set.
+</p>
+<dl>
+ <dt>Future</dt><dd>
+ The game is set in a time beyond that of our own. Games may incorperate elements of
+ future technologies or events yet-to-come.
+ </dd><dt>Present</dt><dd>
+ The game is set in the current day.
+ </dd><dt>Past</dt><dd>
+ The game is set in a time before our own. Games may or may not adhere to historic fact.
+ </dd>
+</dl>
+
+<h3>Place</h3>
+<p>
+ Indicates the place in which the story is told.
+</p>
+<dl>
+ <dt>Earth</dt><dd>
+ The game takes place on our own planet.
+ </dd><dt>Fantasy World</dt><dd>
+ The game takes place on another world. The game's environment could be similar
+ to that of our own with a few significant changes, but it could also be
+ radically different.
+ </dd><dt>Space</dt><dd>
+ The game takes place in the vacuum of space between celestial bodies. For example,
+ this category can be used to define games where the characters may inhabit
+ spaceships that journey across the universe.
+ </dd>
+</dl>
+
+<h3>Sexual content</h3>
+<p>
+ Indicates the types of sexual content that the game contains.
+</p>
+<dl>
+ <dt>Sexual content</dt><dd>
+ This is a generic category to indicate the presence of any sexual content in the
+ game. If there is any such content, this category should be selected.
+ </dd><dt>Bestiality</dt><dd>
+ Sexual activity between characters and animals.<br />
+ <i>No catgirls, I guess?</i>
+ </dd><dt>Incest</dt><dd>
+ Sexual activity between members of the same family. Most of the time under the
+ justification of participants not blood related (step-sister etc.).
+ </dd><dt>Lolicon</dt><dd>
+ The usage of female characters with childlike features in sexual situations.
+ </dd><dt>Shotacon</dt><dd>
+ The usage of male characters with childlike features in sexual situations.
+ </dd><dt>Yaoi</dt><dd>
+ Sexual content depicting activity between males.
+ </dd><dt>Yuri</dt><dd>
+ Sexual content depicting activity between females.
+ </dd><dt>Rape</dt><dd>
+ Situation in which a character is made to engage in sexual activities against
+ their will.
+ </dd>
+</dl>
+
+
+
+
+[[ } # V I S U A L N O V E L A D D / E D I T
+ if($d{p} == 2) { ]]
+
+<p>
+ Blahblah about what we define as VN? Or should that be in <a href="/d5">General guidelines</a>?
+</p>
+
+<h3>General info</h3>
+<dl>
+ <dt>*Title</dt><dd>
+ ..
+ </dd><dt>Aliases</dt><dd>
+ ..
+ </dd><dt>*Description</dt><dd>
+ ..
+ </dd><dt>Length</dt><dd>
+ ..
+ </dd><dt>External links</dt><dd>
+ ..
+ </dd>
+</dl>
+
+<h3>Categories</h3>
+<p>
+ See <a href="/d1">Categories</a>.
+</p>
+
+<h3>Image</h3>
+<p>
+ <i>General image guidelines and when to use the NSFW warning</i>
+</p>
+
+<h3>Relations</h3>
+<p>
+ <i>When to add relation, and document direct and reverse relations</i><br />
+ <i>(Stolen from AniDB, needs some rewriting)</i>
+</p>
+<dl>
+ <dt>Sequel</dt><dd>
+ Continuation of the story. &lt;=&gt;<i>Prequel</i>.
+ </dd><dt>Prequel</dt><dd>
+ The story happens before the original story.&lt;=&gt;<i>Sequel</i>.
+ </dd><dt>Same setting</dt><dd>
+ Same universe/world/reality/timeline, completely different characters.
+ </dd><dt>Alternative setting</dt><dd>
+ Same characters, different universe/world/reality/timeline.
+ </dd><dt>Alternative version</dt><dd>
+ Same setting, same characters, story is told differently.
+ </dd><dt>Same characters</dt><dd>
+ Shares one or more characters, story is unrelated.
+ </dd><dt>Side story</dt><dd>
+ Takes place sometime during the parent storyline. &lt;=&gt;<i>Parent story</i>
+ </dd><dt>Parent story</dt><dd>
+ .. &lt;=&gt;<i>Side story</i>.
+ </dd><dt>Summary</dt><dd>
+ Summarizes full story, may contain additional stuff. &lt;=&gt;<i>Full story</i>.
+ </dd><dt>Full story</dt><dd>
+ Full version of the summarized story. &lt;=&gt;<i>Summary</i>.
+ </dd><dt>Other</dt><dd>
+ ..
+ </dd>
+</dl>
+
+
+
+
+
+
+[[ } # R E L E A S E A D D / E D I T
+ if($d{p} == 3) { ]]
+
+<p>
+ <i>When to add a release</i>
+</p>
+
+<h3>General info</h3>
+<dl>
+ <dt>*Type</dt><dd>
+ ..
+ </dd><dt>*Title (romaji)</dt><dd>
+ ..
+ </dd><dt>Original title</dt><dd>
+ ..
+ </dd><dt>*Language</dt><dd>
+ ..
+ </dd><dt>Official website</dt><dd>
+ ..
+ </dd><dt>Release date</dt><dd>
+ ..
+ </dd><dt>Age rating</dt><dd>
+ ..
+ </dd><dt>Notes</dt><dd>
+ ..
+ </dd>
+</dl>
+
+<h3>Platforms &amp; Media</h3>
+<dl>
+ <dt>Platforms</dt><dd>
+ ..
+ </dd><dt>Media</dt><dd>
+ ..
+ </dd>
+</dl>
+
+<h3>Producers</h3>
+..
+
+<h3>Visual novel relations</h3>
+..
+
+
+
+
+
+
+[[ } # P R O D U C E R A D D / E D I T
+ if($d{p} == 4) { ]]
+
+<p>
+ <i>When to add a producer and what to do with producer relations...</i>
+</p>
+
+<h3>General info</h3>
+<dl>
+ <dt>*Type</dt><dd>
+ ..
+ </dd><dt>*Name (romaji)</dt><dd>
+ ..
+ </dd><dt>Original name</dt><dd>
+ ..
+ </dd><dt>*Primary language</dt><dd>
+ ..
+ </dd><dt>Website</dt><dd>
+ ..
+ </dd><dt>Description</dt><dd>
+ ..
+ </dd>
+</dl>
+
+
+
+
+[[ } # G E N E R A L G U I D E L I N E S
+ if($d{p} == 5) { ]]
+
+
+Misc documentation:<br />
+- Romanisation and capitalization (http://wiki.anidb.net/w/Romanisation)<br />
+- What to do with fandisks<br />
+- Edit summary<br />
+- Quoting sources in descriptions<br />
+- Piracy<br />
+- Spoilers<br />
+
+
+
+
+[[ } # N O S P A M M E S S A G E
+ if($d{p} == 6) { ]]
+
+<span class="warning">
+ <b>Error:</b> The form could not be sent, please make sure you have Javascript
+ enabled in your browser!
+</span>
+
+
+[[ } ]]
+</div>
diff --git a/data/tpl/error b/data/tpl/error
new file mode 100644
index 00000000..76bd9462
--- /dev/null
+++ b/data/tpl/error
@@ -0,0 +1,45 @@
+<head>
+ <title>
+ [[ if($X->{error}->{code} == 1) { ]]VNDB offline
+ [[ } else { ]] ERROR: [[= $X->{error}->{code} ]][[ } ]]
+ </title>
+
+ <meta name="Robots" content="index, follow" />
+ <link href="/favicon.ico" type="image/x-icon" rel="shortcut icon" />
+
+ <style type="text/css">
+ body { background-color: #e0e0e0; padding: 0px; text-align: center; font-family: "Arial", Sans-serif; font-size: 11px; line-height: 16px; font-weight: normal; color: #242424; }
+ #wrapper { margin: 0px auto; margin-bottom: 20px; background-color: #fff; text-align: center; width: 672px; border: 1px solid #b8b8b8; }
+ #pre { background-color: #fff; text-align: left; padding: 0px 10px 5px 10px; margin: 5px 5px 5px 5px; border: 1px solid #e9e9e9; }
+ p { font-size: 11px; color: #242424; text-align: justify; padding-left: 17px; padding-right: 17px; }
+ h1 { font-size: 16px; font-weight: bold; color: #242424; padding-left: 17px; }
+ a { color: #33659E; text-decoration: none; }
+ a:hover { text-decoration: underline; }
+ img { border: 0; margin: 0; padding: 0; }
+ [[ if($X->{error}->{code} == 500) { ]]
+ #wrapper { width: 530px; }
+ [[ } ]]
+ </style>
+</head>
+<body>
+ <div id="wrapper">
+ <div id="pre">
+ [[ if($X->{error}->{code} > 300 && $X->{error}->{code} < 310) { ]]
+ <h1>Moved</h1>
+ <p>
+ Check <a href="[[% $X->{error}->{url} ]]">[[: $X->{error}->{url} ]]</a> for the new location.
+ </p>
+ [[ } elsif($X->{error}->{code} == 401) { ]]
+ <h1>Login required</h1>
+ <p>
+ <a href="/">Please login</a>
+ </p>
+ [[ } elsif($X->{error}->{code} == 1) { ]]
+ <h1>VNDB offline</h1>
+ <p>
+ [[: $X->{error}->{msg} ]]
+ </p>
+ [[ } ]]
+ </div>
+ </div>
+</body>
diff --git a/data/tpl/faq b/data/tpl/faq
new file mode 100644
index 00000000..1c64056e
--- /dev/null
+++ b/data/tpl/faq
@@ -0,0 +1,75 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+<br />
+<h3>What is a Visual Novel?</h3>
+<p>
+ A visual novel can be seen as a combination of a novel and a computer game:
+ they're computer games with a large text based storyline and only little
+ interaction of the player. A typical visual novel consists of text over
+ an anime-style background image and is accompanied by background music.
+ Throughout the game, the player usually has to answer a few questions which will
+ have an effect on the story, thus playing a visual novel a second time while
+ giving other answers may result in an entirely different plot.<br />
+ <br />
+ For more information see <a href="http://en.wikipedia.org/wiki/Visual_Novel">
+ the Wikipedia article on visual novels</a> or the description on
+ <a href="http://visual-novels.net/vn/index.php?option=com_content&task=view&id=259&Itemid=47">Visual-Novels.net</a>.
+ To get a general idea of the genre, try one of the free short visual novels from
+ <a href="http://at2006.haeleth.net/release.php">al|together 2006</a>.
+ <br /><br /><br />
+</p>
+
+
+<h3>How about Eroge, H-Games and Dating Sims?</h3>
+<p>
+ An eroge or H-game is basically any Japanese game that features sexual
+ content. Many visual novels are eroge and many eroge are visual novels,
+ but this is not a rule. The definition of dating sim is a bit more vague,
+ but it's usually the same as a visual novel, except that a dating sim
+ generally uses a gameplay based on statistics.<br />
+ <br />
+ There are no strict bounds to the definition of "visual novel", most
+ eroge and dating sims include elements of visual novels, but may -
+ strictly speaking - not be visual novels themselves. As VNDB aims to
+ be comprehensive, we simply accept any game that contains elements of a
+ visual novel and is produced by a Japanese or Japan-related company or
+ doujin cicle.
+ <br /><br /><br />
+</p>
+
+
+<h3>Why a Visual Novel Database?</h3>
+<p>
+ The internet is large, very large, but the number of English resources
+ related to visual novels is only very limited. VNDB attempts to collect
+ and present as much information as possible that would otherwise be very
+ hard to find for the English speaking audience. This way fans can easily
+ keep track of new releases and localizations of their favorite games,
+ while not having to browse numerous of indistinct Japanese websites.
+ <br /><br /><br />
+</p>
+
+
+<h3>How can I help VNDB?</h3>
+</p>
+ There are many ways to contribute to VNDB. First of all you can freely
+ edit all information found on this website, so if you find any errors
+ just click the "edit" link on the top right of the page. You can also
+ add new information (visual novels, producers, releases) to the database,
+ though please search the database before you do in order to prevent
+ duplicate pages.<br />
+ <br />
+ To discuss about new features or to help the development of the website
+ itself, feel free to browse the <a href="http://forum.vndb.org/">forums</a>
+ or join us on IRC at <a href="irc://irc.synirc.net/vndb">#vndb @ irc.synirc.net</a>.
+ If you aren't used to IRC or are just to lazy to install a client, you can
+ still join the chat using <a href="http://cgiirc.synirc.net/">the Webchat</a>.
+ Just choose a nickname, specify #vndb as channel and hit Login!
+ <br /><br /><br />
+</p>
+
+
+<h3>Where can I download the Visual Novels?</h3>
+<p>
+ Not here. We do not provide downloads nor links to resources that encourage
+ the illegal spreading of visual novels.
+</p>
diff --git a/data/tpl/hist b/data/tpl/hist
new file mode 100644
index 00000000..e02a149f
--- /dev/null
+++ b/data/tpl/hist
@@ -0,0 +1,103 @@
+[[= $d{type} && $d{type} ne 'u' ? ttabs($d{type}, $d{obj}, 'hist') : '' ]]-
+<h2 class="rss">[[: $p{PageTitle} ]]</h2>
+[[ if($d{type} eq 'u' && $#{$d{hist}} < 0) { ]]
+<p>
+ You haven't made any changes yet.
+</p>
+[[ } ]]
+<br /><br />
+
+[[
+ my $url = !$d{type} ? '/hist' : '/'.$d{type}.$d{id}.'/hist';
+ my $furl = $url.'?e='.$d{sele}.';t=';
+ my $eurl = $url.'?t='.$d{selt}.';e=';
+ my $purl = !$d{type}?$eurl.$d{sele}:$d{type} eq 'v' && $d{seli} ? $url.'?i=1' : $url;
+ my $rurl = $url.'/rss'.(!$d{type}?'?t='.$d{selt}.';e='.$d{sele}:$d{type} eq 'v' && $d{seli} ? '?i=1' : '');
+ local $_ = $d{selt};
+ my @fil = (
+ /a/ ? 'all items' : '<a href="%sa">all items</a>',
+ /v/ ? 'visual novels' : '<a href="%sv">visual novels</a>',
+ /r/ ? 'releases' : '<a href="%sr">releases</a>',
+ /p/ ? 'producers' : '<a href="%sp">producers</a>',
+ );
+ local $_ = $d{sele};
+ my @edi = (
+ /0/ ? 'all changes' : '<a href="%s0">all changes</a>',
+ /2/ ? 'edits only' : '<a href="%s2">edits only</a>',
+ /1/ ? 'newly created pages only' : '<a href="%s1">newly created pages only</a>',
+ );
+ local $_ = $d{seli};
+ my @inc = (
+ /0/ ? 'exclude' : '<a href="'.$url.'">exclude</a>',
+ /1/ ? 'include' : '<a href="'.$url.'?i=1">include</a>',
+ );
+]]
+
+[[ if(!$d{type}) { ]]-
+<p class="browse">
+ [[= join(' | ', map { sprintf $_, $furl } @fil) ]]<br />
+ [[= join(' | ', map { sprintf $_, $eurl } @edi) ]]<br /><br /><br />
+</p>
+[[ } if($d{type} eq 'v') { ]]-
+<p class="browse">
+ ([[= join(' | ', @inc) ]]) edits of releases.
+</p>
+[[ } ]]
+
+[[ if($d{act} eq 'r') { ]]
+<span class="msg">
+ Performed the mass-revert, please see the following list for details.
+</span>
+[[ } elsif($d{act} eq 'd') { ]]
+<span class="msg">
+ The following edits have been completely deleted.
+</span>
+[[ } ]]-
+
+
+<a class="rss" href="[[= $rurl ]]">RSS</a>
+[[= pagebut($purl) ]]
+[[ if(0 and $p{Authmod} || $p{Authdel}) { ]]
+<form method="post" action="[[= $purl ]]" class="tblf">
+[[ } ]]
+<table id="thi">
+ <thead><tr>
+ <td class="tc1">Rev.</td>
+ <td class="tc2">Date</td>
+ [[ if($d{type} ne 'u' || $d{act}) { ]]-
+ <td class="tc3">User</td>[[ } ]]-
+ [[ if(!$d{type} || $d{type} eq 'u' || $d{act} || ($d{type} eq 'v' && $d{seli})) { ]]-
+ <td class="tc4">Page</td>[[ } ]]-
+ [[ if($d{type} && !$d{act}) { ]]-
+ <td class="tc5">Summary</td>[[ } ]]-
+ [[ if($d{act} eq 'r') { ]]-
+ <td class="tc5">Action</td>[[ } ]]-
+ [[ if(0 and $p{Authmod}) { ]]-
+ <td class="tc6"><input type="checkbox" id="checkall" name="sel" value="all" /></td>[[ } ]]-
+ </tr></thead>
+
+ [[ for (@{$d{hist}}) { my $t = (qw|v r p|)[$_->{type}]; ]]-
+ <tr>
+ <td class="tc1"><a href="/[[= $t.$_->{iid} ]]?rev=[[= $_->{id} ]]">[[= $_->{id} ]]</a></td>
+ <td class="tc2">[[= formatdate('%Y-%m-%d %R', $_->{added}, 'dh') ]]</td>
+ [[ if($d{type} ne 'u' || $d{act}) { ]]-
+ <td class="tc3"><a href="/u[[= $_->{requester} ]]">[[: $_->{username} ]]</a></td>[[ } ]]-
+ [[ if(!$d{type} || $d{type} eq 'u' || $d{act}) { ]]-
+ <td class="tc4">[[= $_->{prev} ? $t.$_->{iid} : '<b>'.$t.$_->{iid}.'</b>' ]]:<a href="/[[= $t.$_->{iid} ]]?rev=[[= $_->{id} ]]" title="[[: $_->{ititle} ]]">[[: length($_->{ititle}) > 30 ? substr($_->{ititle},0,27).'...' : $_->{ititle} ]]</a></td>[[ } ]]-
+ [[ if($d{type} eq 'v' && $d{seli}) { ]]-
+ <td class="tc4"><a href="/[[= $t.$_->{iid} ]]" title="[[: $_->{ititle} ]]">[[= $_->{prev} ? $t.$_->{iid} : '<b>'.$t.$_->{iid}.'</b>' ]]</a></td>[[ } ]]-
+ [[ if($d{type} && !$d{act}) { ]]-
+ <td class="tc5">[[= summary($_->{comments}, $d{type} eq 'u' ? 40 : 60)||'[empty]' ]]</td>[[ } ]]-
+ [[ if($d{act} eq 'r') { ]]-
+ <td class="tc5">[[: $_->{_status} ]]</td>[[ } ]]-
+ [[ if(0 and $p{Authmod} && !$d{act}) { ]]-
+ <td class="tc6"><input type="checkbox" name="sel" value="[[= $_->{id} ]]" /></td>[[ } ]]-
+ </tr>
+ [[ } ]]
+
+</table>
+[[ if(0 and $p{Authmod}) { ]]<input type="submit" class="right" name="post" value="Mass revert" />[[ } ]]
+[[ if(0 and $p{Authdel}) { ]]<input type="submit" class="right" name="post" value="Mass delete" id="massdel" />[[ } ]]
+[[ if(0 and $p{Authmod} || $p{Authdel}) { ]]</form>[[ } ]]
+[[= pagebut($purl) ]]
+
diff --git a/data/tpl/home b/data/tpl/home
new file mode 100644
index 00000000..5d8cd763
--- /dev/null
+++ b/data/tpl/home
@@ -0,0 +1,67 @@
+<h2>Welcome to VNDB - The Visual Novel Database!</h2>
+<p class="desc">
+ <br />
+ VNDB.org strives to be a comprehensive database for information about visual novels and
+ eroge.<br />
+ This website is built as a wiki, meaning that anyone can freely add and contribute information
+ to the database, allowing us to create the largest, most accurate and most up-to-date visual novel
+ database on the web.<br />
+ Registered users are also able to keep track of a personal list of games they want to play or have finished
+ and they can vote on all visual novels.<br /><br />
+
+ Feel free to <a href="/v">browse around</a>, <a href="/u/register">register an account</a>
+ or to discuss about the database at our <a href="http://forum.vndb.org/">forums</a>.
+</p>
+
+<h3 class="home">VNDB 1.13!</h3>
+<p class="desc">
+ And it's time for an update again: This update makes it possible to specify how much
+ a category applies to a visual novel, adds a language filter to the category browser,
+ and fixes many, many bugs.
+ <br />
+ <a href="http://forum.vndb.org/index.php?topic=43.0">Read more...</a> - <a href="http://forum.vndb.org/index.php?board=7.0">news archive</a>.
+</p>
+
+<ul class="home">
+ <li><b>Recent changes</b></li>
+ [[ for (@{$d{recentedits}}) { my $t = (qw|v r p|)[$_->{type}]; ]]-
+ <li>[[= $t ]]:<a href="/[[= $t.$_->{iid}.'?rev='.$_->{id} ]]" title="[[: $_->{ititle} ]]">[[: shorten $_->{ititle}, 30 ]]</a></li>
+ [[ } ]]-
+</ul>
+
+<ul class="home">
+ <li><b>Recent votes</b></li>
+ [[ for (@{$d{recentvotes}}) { ]]-
+ <li><a href="/v[[= $_->{vid} ]]" title="[[: $_->{title} ]]">[[: shorten $_->{title}, 30 ]]</a> ([[= $_->{vote} ]])</li>
+ [[ } ]]
+</ul>
+
+<ul class="home">
+ <li><b>Most popular</b></li>
+ [[ for (@{$d{popular}}) { $_->{c_votes} =~ s#^([0-9]{2}.[0-9]{2}).+$#sprintf '%.1f', $1#e; ]]-
+ <li><a href="/v[[= $_->{id} ]]" title="[[: $_->{title} ]]">[[: shorten $_->{title}, 30 ]]</a> ([[= $_->{c_votes} ]])</li>
+ [[ } ]]
+</ul>
+
+<ul class="home break">
+ <li><b>Recently added visual novels</b></li>
+ [[ for (@{$d{recentvns}}) { ]]-
+ <li><a href="/v[[= $_->{iid} ]]" title="[[: $_->{ititle} ]]">[[: shorten $_->{ititle}, 30 ]]</a></li>
+ [[ } ]]-
+</ul>
+
+<ul class="home">
+ <li><b>Recently added producers</b></li>
+ [[ for (@{$d{recentps}}) { ]]-
+ <li><a href="/p[[= $_->{iid} ]]" title="[[: $_->{ititle} ]]">[[: shorten $_->{ititle}, 30 ]]</a></li>
+ [[ } ]]-
+</ul>
+
+<ul class="home">
+ <li><b>Random visual novels</b></li>
+ [[ for (@{$d{randomvns}}) { ]]-
+ <li><a href="/v[[= $_->{id} ]]" title="[[: $_->{title} ]]">[[: shorten $_->{title}, 30 ]]</a></li>
+ [[ } ]]-
+</ul>
+
+
diff --git a/data/tpl/main b/data/tpl/main
new file mode 100644
index 00000000..d52a576a
--- /dev/null
+++ b/data/tpl/main
@@ -0,0 +1,14 @@
+[[+ defs.pl ]]
+
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+-[[ if($X->{error}) { ]]
+ [[+ error ]]
+[[ } if($X->{page}) { %p = %{$X->{page}}; gettitle(); ]]
+ [[+ page ]]
+[[ } ]]-
+
+</html>
diff --git a/data/tpl/myvotes b/data/tpl/myvotes
new file mode 100644
index 00000000..9379e98e
--- /dev/null
+++ b/data/tpl/myvotes
@@ -0,0 +1,30 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+[[ if($#{$d{votes}} < 0) { ]]-
+<p>
+[[ if($d{user}{username} eq $p{AuthUsername}) { ]]
+ You haven't voted on anything yet...
+[[ } else { ]]
+ [[: $d{user}{username} ]]- hasn't voted on anything yet...
+[[ } ]]
+</p>
+[[ } else {
+ my $url = sprintf '/u%d/votes', $d{user}{id};
+ my $surl = sprintf '%s?s=%s&amp;o=%s', $url, $d{order}[0], $d{order}[1];
+]]
+[[= pagebut($surl) ]]-
+<table id="tmv">
+ <thead><tr>
+ <td class="tc1">Title [[= sortbut($url, 'title') ]]</td>
+ <td class="tc2">Vote [[= sortbut($url, 'vote') ]]</td>
+ <td class="tc3">Date [[= sortbut($url, 'date') ]]</td>
+ </tr></thead>
+ [[ for (@{$d{votes}}) { ]]-
+ <tr>
+ <td class="tc1"><a href="/v[[= $_->{vid} ]]">[[: $_->{title} ]]</a></td>
+ <td class="tc2">[[: $_->{vote} ]]</td>
+ <td class="tc3">[[= formatdate('%Y-%m-%d', $_->{date}, 'dh') ]]</td>
+ </tr>
+ [[ } ]]-
+</table>
+-[[= pagebut($surl) ]]
+[[ } ]]
diff --git a/data/tpl/page b/data/tpl/page
new file mode 100644
index 00000000..e6ce9e99
--- /dev/null
+++ b/data/tpl/page
@@ -0,0 +1,140 @@
+<head>
+ <title>[[: $p{PageTitle} ]]- :: VNDB</title>
+ <link href="[[: $p{st} ]]/files/style.css?[[= $VNDB::VERSION ]]" rel="stylesheet" type="text/css" media="screen" />
+ <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
+[[ if($p{redit} || $p{vnedit}) { ]]-
+ <script src="[[: $p{st} ]]/files/dyna.js?[[= $VNDB::VERSION ]]" type="text/javascript"></script>
+[[ } ]]-
+ <script src="[[: $p{st} ]]/files/def.js?[[= $VNDB::VERSION ]]" type="text/javascript"></script>
+[[ if($p{devshit}) { ]]-
+ <meta name="robots" content="noindex, nofollow" />
+[[ } elsif($p{userlist} || $p{userpage} || $p{myvotes} || $p{vnlist} || $p{hist} || ($p{vnpage} && $p{vnpage}{page} eq 'stats')
+ || grep { $p{$_} && $p{$_}{change} } qw|vnpage ppage rpage|) { ]]-
+ <meta name="robots" content="noindex, follow" />
+[[ }]]-
+
+[[if($p{hist}){ ]]
+ <link rel="alternate" type="application/rss+xml" title="Recent changes" href="
+ [[= (!$p{hist}{type}?'/hist':'/'.$p{hist}{type}.$p{hist}{id}.'/hist').'/rss'.(!$p{hist}{type}?'?t='.$p{hist}{selt}.';e='.$p{hist}{sele}:$p{hist}{type} eq 'v' && $p{hist}{seli} ? '?i=1':'') ]]" />
+[[ } ]]-
+</head>
+
+<body>
+<div id="header">
+ <form id="search" method="get" action="/v/search">
+ <fieldset>
+ <legend>Search</legend>
+ <input id="searchfield" type="text" name="q" value="[[: $p{searchquery} || 'search' ]]"[[= !$p{searchquery} ? ' style="color: #999"': '' ]]- />
+ <input id="searchsubmit" type="submit" value="Search" />
+ </fieldset>
+ </form>
+ <h1><a href="/">vndb.org</a> / #vndb @ irc.synirc.net <a href="http://vndb.org/">
+ [[ if($p{devshit}) { ]]<b style="color: red">The VNDB.org Testing Grounds</b>[[ } else { ]]<b>The Visual Novel Database</b>[[ } ]]</a></h1>
+</div>
+
+
+<div id="page">
+
+<div id="content">
+[[ # = noindex-tag (see above) ]]
+[[ if($p{home}) { %d = %{$p{home}}; ]] [[+ home ]][[ } ]]
+[[ if($p{faq}) { %d = %{$p{faq}}; ]] [[+ faq ]][[ } ]]
+[[ if($p{userlogin}) { %d = %{$p{userlogin}}; ]] [[+ userlogin ]][[ } ]]
+[[ if($p{userreg}) { %d = %{$p{userreg}}; ]] [[+ userreg ]][[ } ]]
+[[ if($p{userpass}) { %d = %{$p{userpass}}; ]] [[+ userpass ]][[ } ]]
+[[ if($p{useredit}) { %d = %{$p{useredit}}; ]] [[+ useredit ]][[ } ]]
+[[ if($p{userlist}) { %d = %{$p{userlist}}; ]] [[+ userlist ]][[ }# ]]
+[[ if($p{userpage}) { %d = %{$p{userpage}}; ]] [[+ userpage ]][[ }# ]]
+[[ if($p{vnpage}) { %d = %{$p{vnpage}}; ]] [[+ vnpage ]][[ } ]]
+[[ if($p{vnedit}) { %d = %{$p{vnedit}}; ]] [[+ vnedit ]][[ } ]]
+[[ if($p{redit}) { %d = %{$p{redit}}; ]] [[+ redit ]][[ } ]]
+[[ if($p{vnbrowse}) { %d = %{$p{vnbrowse}}; ]] [[+ vnbrowse ]][[ } ]]
+[[ if($p{pbrowse}) { %d = %{$p{pbrowse}}; ]] [[+ pbrowse ]][[ } ]]
+[[ if($p{pedit}) { %d = %{$p{pedit}}; ]] [[+ pedit ]][[ } ]]
+[[ if($p{ppage}) { %d = %{$p{ppage}}; ]] [[+ ppage ]][[ } ]]
+[[ if($p{myvotes}) { %d = %{$p{myvotes}}; ]] [[+ myvotes ]][[ }# ]]
+[[ if($p{vnlist}) { %d = %{$p{vnlist}}; ]] [[+ vnlist ]][[ }# ]]
+[[ if($p{hist}) { %d = %{$p{hist}}; ]] [[+ hist ]][[ }# ]]
+[[ if($p{rpage}) { %d = %{$p{rpage}}; ]] [[+ rpage ]][[ } ]]
+[[ if($p{docs}) { %d = %{$p{docs}}; ]] [[+ docs ]][[ } ]]
+</div>
+
+
+<div id="side"><div><div>
+
+ <h2>Menu</h2>
+ <ul>
+ <li><a href="/">Home</a></li>
+ <li><a href="/v">Visual Novels</a></li>
+ <li><a href="/p">Producers</a></li>
+ <li><a href="/v/cat">Categories</a></li>
+ <li><a href="/u/list">Users</a></li>
+ <li><a href="/hist">Recent changes</a></li>
+ <li><a href="/faq">FAQ</a></li>
+ <li><a href="http://forum.vndb.org/">Forum</a></li>
+ </ul>
+
+-[[ if(!$p{AuthLoggedin}) { ]]-
+ <h2>Login</h2>
+ <form method="post" action="/nospam?/u/login" id="loginform">
+ <fieldset>
+ <legend>Login</legend>
+ <input type="text" id="usrname" name="username" />
+ <input type="password" id="usrpass" name="userpass" />
+ <input type="submit" value="Login" />
+ </fieldset>
+ </form>
+ <p>
+ <a href="/u/register">register</a> or <a href="/u/newpass">forgot password?</a>
+ </p>
+[[ } else { ]]-
+ <h2>User menu</h2>
+ <ul>
+ <li>[[: $p{AuthUsername} ]]- ([[: $p{AuthRankname} ]])</li>
+ <li><a href="/u[[= $p{AuthId} ]]/edit">My profile</a></li>
+ <li><a href="/u[[= $p{AuthId} ]]/votes">My votes</a></li>
+ <li><a href="/u[[= $p{AuthId} ]]/list">My visual novel list</a></li>
+ <li><a href="/u[[= $p{AuthId} ]]/hist">My recent changes</a></li>
+ [[ if($p{Authedit}) { ]]-
+ <li>&nbsp;</li>
+ <li><a href="/v/new">Add visual novel</a></li>
+ <li><a href="/p/add">Add producer</a></li>
+ [[ } ]]
+ <li>&nbsp;</li>
+ <li><a href="/u/logout">Logout</a></li>
+ </ul>
+[[ } ]]-
+
+-[[ #</div></div><div><div> ]]
+ <h2>Statistics</h2>
+ <ul>
+ <li><b>[[= $p{Statvn}||0 ]]</b> visual novels</li>
+ <li><b>[[= $p{Statproducers}||0 ]]</b> producers</li>
+ <li><b>[[= $p{Statreleases}||0 ]]</b> releases</li>
+ <li><b>[[= $p{Statvotes}||0 ]]</b> votes</li>
+ <li><b>[[= $p{Statusers}||0 ]]</b> users</li>
+ </ul>
+[[ if(0) { ]] <h2>Most popular</h2>
+ <ul>[[ for (@{$p{popular}}) { ]]-
+ <li><a href="/v[[: $_->{id} ]]" title="[[: $_->{title} ]]">[[: length($_->{title})>30 ? (substr($_->{title}, 0, 27).'...') : $_->{title} ]]</a></li>[[ } ]]-
+ <li class="more"><a href="/v/all?s=votes&amp;o=d">More...</a></li>
+ </ul>[[ } ]]-
+</div></div></div>
+
+</div>
+
+<div id="footer">
+ <p>
+ vndb v[[: $VNDB::VERSION ]]- |
+ <a href="mailto:contact@vndb.org">contact@vndb.org</a> |
+ designed by <a href="http://www.freecsstemplates.org/">free css templates</a>.
+ </p>
+</div>
+
+-[[ if(0 && $p{devshit}) { ]]-
+ <pre id="debug">SQL Queries used:<br />
+[[= $p{devshit} ]]
+</pre>
+[[ } ]]-
+
+</body>
diff --git a/data/tpl/pbrowse b/data/tpl/pbrowse
new file mode 100644
index 00000000..71b40c82
--- /dev/null
+++ b/data/tpl/pbrowse
@@ -0,0 +1,45 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+<p class="chr">
+ -[[= $d{chr} ne 'all' ? '<a href="/p/all">all</a>' : 'all' ]]- |
+ [[ for('a'..'z', 0) { ]]-
+ -[[ if($d{chr} eq $_) { ]][[= $_?$_:'#' ]][[ } else { ]]<a href="/p/[[= $_ ]]">[[= $_?$_:'#' ]]</a>[[ } ]]
+ [[ } ]]-
+ <form id="psearch" method="get" action="/p" accept-charset="UTF-8">
+ <fieldset>
+ <input type="text" name="q" id="q" value="[[: $d{query} ]]" class="text" />
+ <input type="submit" value="Search!" />
+ </fieldset>
+ </form>
+</p>
+
+-[[ if($#{$d{prods}} < 0) { ]]
+<p>
+ No results again, life sucks... :'(
+</p>
+[[ } else {
+ my $url = sprintf '/p/%s', $d{chr};
+ $url .= '?q='.$d{query} if $d{query};
+]]
+[[= pagebut($url) ]]
+<table id="tpd">
+ <thead><tr>
+ <td class="tc1">Name</td>
+ <td class="tc2">Type</td>
+ <td class="tc3">Main language</td>
+ <td class="tc4">Website</td>
+ </tr></thead>
+[[ for (@{$d{prods}}) { ]]-
+ <tr>
+ <td class="tc1"><a href="/p[[= $_->{id} ]]">[[: $_->{name} ]]</a></td>
+ <td class="tc2">[[: $VNDB::PROT->{$_->{type}} ]]</td>
+ <td class="tc3">[[: $VNDB::LANG->{$_->{lang}} ]]</td>
+ <td class="tc4">
+ [[ if($_->{website}) { ]]
+ <a href="[[: $_->{website} ]]">[[: length($_->{website}) > 30 ? substr($_->{website}, 0, 27).'...' : $_->{website} ]]
+ [[ } else { ]]---[[ } ]]
+ </td>
+ </tr>
+[[ } ]]-
+</table>
+[[= pagebut($url) ]]
+[[ } ]]
diff --git a/data/tpl/pedit b/data/tpl/pedit
new file mode 100644
index 00000000..6ef398cf
--- /dev/null
+++ b/data/tpl/pedit
@@ -0,0 +1,45 @@
+[[= $d{id} ? ttabs('p', $d{prod}, 'edit') : '' ]]
+<h2>[[: $p{PageTitle} ]]</h2>
+-[[ if(!$d{id}) { ]]
+ <span class="msg">
+ Please search the database before adding a new producer in order to prevent duplicate entries.
+ </span>
+[[ } else { ]]
+ <span class="msg">
+ It is currently not possible to delete producers from the database, please
+ use the <a href="http://forum.vndb.org/index.php?board=5.0">forums</a> to request
+ a deletion. Also refer to the forums for more serious edits or discussions about changes.
+ </span>
+[[ } if($d{id} && $d{prod}{cid} != $d{prod}{latest}) { ]]
+ <span class="warning">
+ You are editing an old revision of this producer. If you save it, all changes made after
+ -[[= formatdate('%Y-%m-%d %R', $d{prod}{added}) ]]- will be removed!
+ </span>
+[[ } ]]
+
+-[[= cform([
+ { type => 'error' },
+ { type => 'startform', action => $d{id} ? '/p'.$d{id}.'/edit' : '/p/add' },
+
+ { type => 'sub', title => 'General info', short => 'info' },
+ { type => 'select', name => 'Type', short => 'type', r=>1, options => [ map {
+ { short => $_, name => $VNDB::PROT->{$_} } } sort keys %$VNDB::PROT ] },
+ { type => 'input', name => 'Name (romaji)', short => 'name', r=>1 },
+ { type => 'input', name => 'Original name', short => 'original' },
+ { type => 'static', text => q|
+ The original name of the producer, leave blank if it is already in the Latin alphabet.<br /><br />| },
+
+ { type => 'select', name => 'Primary language', short => 'lang', r=>1, options => [ map {
+ ({ short => $_, name => sprintf '%s (%s)', $_, $VNDB::LANG->{$_} }) } sort keys %{$VNDB::LANG} ] },
+
+ { type => 'input', name => 'Website', short => 'website' },
+ { type => 'textarea', name => 'Description', short => 'desc', rows => 7, cols => 60 },
+
+ { type => 'sub', title => 'Edit summary', short => 'com' },
+ { type => 'textarea', name => 'Edit summary', short => 'comm', rows => 3, cols => 60 },
+ { type => 'static', text => 'Please motivate your modifications and cite all sources.' },
+
+ { type => 'submit', text => $d{id} ? 'Edit' : 'Add' },
+ { type => 'endform' },
+
+], $d{form}) ]]
diff --git a/data/tpl/ppage b/data/tpl/ppage
new file mode 100644
index 00000000..e829682a
--- /dev/null
+++ b/data/tpl/ppage
@@ -0,0 +1,58 @@
+[[= ttabs('p', $d{prod}) ]]
+<h2>[[: $p{PageTitle} ]]</h2>
+
+[[ if($d{prod}{hidden}) { ]]-
+ <span class="warning">
+ This item has been deleted from the database. File a request on the
+ <a href="http://forum.vndb.org/index.php?board=5.0">forums</a>
+ to undelete this page.
+ </span>
+[[ } ]]
+[[ if(!$d{prod}{hidden} || $p{Authdel}) { ]]-
+
+
+
+[[ if($d{change}) { ]]
+[[= cdiff($d{prev}, $d{prod},
+ [ type => 'Type', sub { $VNDB::PROT->{$_[0]} } ],
+ [ name => 'Name (romaji)', 1 ],
+ [ original => 'Original name', 1 ],
+ [ lang => 'Language', sub { $VNDB::LANG->{$_[0]} } ],
+ [ website => 'Website', 1 ],
+ [ desc => 'Description', 1, 1 ],
+ ) ]]
+[[ } ]]
+
+<dl>
+ <dt>Name</dt><dd>[[ if($d{prod}{original}) { ]]
+ [[: $d{prod}{original} ]]- ([[: $d{prod}{name} ]])
+ [[ } else { ]][[: $d{prod}{name} ]][[ } ]]</dd>
+ <dt>Type</dt><dd>[[: $VNDB::PROT->{$d{prod}{type}} ]]</dd>
+ <dt>Primary lang.</dt><dd>[[: $VNDB::LANG->{$d{prod}{lang}} ]]</dd>
+[[ if($d{prod}{website}) { ]]-
+ <dt>Links</dt><dd><a href="[[: $d{prod}{website} ]]">Official homepage</a></dd>[[ } ]]-
+</dl>
+
+-[[ if($d{prod}{desc}) { ]]
+<p>[[= summary($d{prod}{desc}) ]]<br /><br /></p>
+[[ } ]]
+
+
+<h3>Visual novel relations</h3>
+[[ if($#{$d{vn}} < 0) { ]]-
+<p>
+ We have currently no visual novels related to this producer.
+</p>
+[[ } else { ]]-
+<ul>
+ [[ for (@{$d{vn}}) { ]]-
+ <li><a href="/v[[= $_->{id} ]]">[[: $_->{title} ]]</a>
+ [[ if($_->{date} ne "0000-00-00") { ]]- ([[= datestr($_->{date}) ]])[[ } ]]
+ </li>
+ [[ } ]]-
+</ul>
+[[ } ]]
+
+
+
+[[ } ]]
diff --git a/data/tpl/redit b/data/tpl/redit
new file mode 100644
index 00000000..0e83b670
--- /dev/null
+++ b/data/tpl/redit
@@ -0,0 +1,70 @@
+[[= $d{id} ? ttabs('r', $d{rel}, 'edit') : ttabs('v', $d{vn}, 'edit') ]]-
+<h2>[[: $p{PageTitle} ]]</h2>
+
+[[ if($d{id}) { ]]
+ <span class="msg">
+ It is currently not possible to delete releases from the database, please
+ use the <a href="http://forum.vndb.org/index.php?board=5.0">forums</a> to request
+ a deletion. Also refer to the forums for more serious edits or discussions about changes.
+ </span>
+[[ } if($d{id} && $d{rel}{cid} != $d{rel}{latest}) { ]]
+ <span class="warning">
+ You are editing an old revision of this producer. If you save it, all changes made after
+ -[[= formatdate('%Y-%m-%d %R', $d{rel}{added}) ]]- will be removed!
+ </span>
+[[ } ]]
+
+[[= cform( [
+ { type => 'error' },
+ { type => 'startform', action => $d{id} ? sprintf('/r%d/edit', $d{rel}{id}) : '/v'.$d{vn}{id}.'/add', fh => 1 },
+
+ { type => 'sub', title => 'General info', short => 'info' },
+ { type => 'select', name => 'Type', short => 'type', r=>1, options => [ map {
+ ({ short => $_, name => $VNDB::RTYP->[$_] }) } 0..$#{$VNDB::RTYP} ] },
+
+ { type => 'input', name => 'Title (romaji)', short => 'title', r=>1 },
+ { type => 'input', name => 'Original title', short => 'original' },
+ { type => 'static', text => q|
+ The original title of this release, leave blank if it already is in the Latin alphabet.<br /><br />| },
+
+ { type => 'select', name => 'Language', short => 'language', r=>1, options => [ map {
+ ({ short => $_, name => sprintf '%s (%s)', $_, $VNDB::LANG->{$_} }) } sort keys %{$VNDB::LANG} ] },
+
+ { type => 'input', name => 'Official website', short => 'website' },
+ { type => 'date', name => 'Release date', short => 'released' },
+ { type => 'static', text => 'Leave month or day blank if they are unknown<br /><br />' },
+ { type => 'select', name => 'Age rating', short => 'minage', options => [ map
+ { { name => $VNDB::VRAGES->{$_}, short => $_ } } sort { $a <=> $b } keys %$VNDB::VRAGES ] },
+ { type => 'textarea', name => 'Notes', short => 'notes', rows => 3, cols => 50 },
+ { type => 'static', text => 'Miscellaneous notes/comments, information that does not fit in the above fields. E.g.: Censored/uncensored or for which releases this patch applies. Max. 250 characters.' },
+
+ { type => 'sub', title => 'Platforms & Media', short => 'pnm' },
+ { type => 'static', raw => 1, text => '<label>Platforms</label><ul class="platforms">'.join('', map { my $p = $_;
+ '<li><input type="checkbox" name="platforms" value="'.$_.'" id="'.$_.'" '.
+ (($d{form}{platforms} && grep { $p eq $_ } @{$d{form}{platforms}}) ? 'checked="checked" ':'').'/>'.
+ '<acronym class="plat '.$_.'" title="'.$VNDB::PLAT->{$_}.'">'.$_.'</acronym>'.
+ '<label for="'.$_.'">'.$VNDB::PLAT->{$_}.'</label></li>'
+ } sort { $VNDB::PLAT->{$a} cmp $VNDB::PLAT->{$b} } keys %$VNDB::PLAT).'</ul>' },
+
+ { type => 'static', text => '<br />' },
+ { type => 'jssel', name => 'Media', sh => 'md', short => 'media' },
+
+ { type => 'sub', title => 'Producers', short => 'prod' },
+ { type => 'jssel', name => 'Producers', sh => 'pd', short => 'producers' },
+
+ { type => 'sub', title => 'Visual novel relations', short => 'rel'},
+ { type => 'jssel', name => 'Relations', sh => 'vn', short => 'vn', r=>1 },
+ { type => 'static', text => q|
+ Although a release usually contains only one visual novel, it is also possible
+ for one release to include several games. Use this field to specify which
+ visual novels are included in this release.| },
+
+
+ { type => 'sub', title => 'Edit summary', short => 'com' },
+ { type => 'textarea', name => 'Edit summary', short => 'comm', rows => 3, cols => 60 },
+ { type => 'static', text => 'Please motivate your modifications and cite all sources.' },
+
+ { type => 'submit', text => $d{id} ? 'Edit' : 'Add' },
+ { type => 'endform' },
+
+], $d{form}) ]]
diff --git a/data/tpl/rpage b/data/tpl/rpage
new file mode 100644
index 00000000..7ad3c4ea
--- /dev/null
+++ b/data/tpl/rpage
@@ -0,0 +1,61 @@
+[[= ttabs('r', $d{rel}) ]]
+<h2>[[: $p{PageTitle} ]]</h2>
+
+[[ if($d{rel}{hidden}) { ]]-
+ <span class="warning">
+ This item has been deleted from the database. File a request on the
+ <a href="http://forum.vndb.org/index.php?board=5.0">forums</a>
+ to undelete this page.
+ </span>
+[[ } ]]
+[[ if(!$d{rel}{hidden} || $p{Authdel}) { ]]-
+
+
+
+[[ if($d{change}) { ]]
+[[= cdiff($d{prev}, $d{rel},
+ [ vn => 'Relations', sub { join("<br />\n", map { $_->{title} } @{$_[0]}) } ],
+ [ type => 'Type', sub { $VNDB::RTYP->[$_[0] ] } ],
+ [ title => 'Title', 1 ],
+ [ original => 'Orig. title', 1 ],
+ [ language => 'Language', sub { $VNDB::LANG->{$_[0]} } ],
+ [ website => 'Website', \&summary ],
+ [ released => 'Release date', \&datestr ],
+ [ minage => 'Age rating', sub { $VNDB::VRAGES->{$_[0]} } ],
+ [ notes => 'Notes', 1 ],
+ [ platforms => 'Platforms', sub { join(', ', sort @{$_[0]}) } ],
+ [ media => 'Media', \&mediastr ],
+ [ producers => 'Producers', sub { join(', ', map { _hchar($_->{name}) } sort { $a->{name} cmp $b->{name} } @{$_[0]}) } ],
+ ) ]]
+[[ } ]]
+
+<dl>
+ <dt>Relation</dt><dd>[[= join('<br />', map { '<a href="/v'.$_->{vid}.'">'._hchar($_->{title}).'</a>' } @{$d{rel}{vn}}) ]]</dd>
+ <dt>Type</dt><dd>[[: $VNDB::RTYP->[$d{rel}{type}] ]]</dd>
+ <dt>Title</dt><dd>[[: $d{rel}{title} ]]</dd>
+[[ if($d{rel}{original}) { ]]-
+ <dt>Original Title</dt><dd>[[: $d{rel}{original} ]]</dd>[[ } ]]-
+ <dt>Language</dt><dd>[[: $VNDB::LANG->{$d{rel}{language}} ]]</dd>
+ <dt>Release date</dt><dd>[[= datestr($d{rel}{released}) ]]</dd>
+[[ if($d{rel}{minage} >= 0) { ]]-
+ <dt>Age rating</dt><dd>[[: $VNDB::VRAGES->{$d{rel}{minage}} ]]</dd>[[ } ]]-
+[[ if($#{$d{rel}{producers}} >= 0) { ]]-
+ <dt>Producer[[: $#{$d{rel}{producers}} > 0 ? 's' : '' ]]</dt><dd>[[= join(', ', map {
+ sprintf('<a href="/p%d">%s</a>', $_->{id}, _hchar($_->{name})) } @{$d{rel}{producers}})
+ ]]</dd>[[ } ]]-
+[[ if($#{$d{rel}{platforms}} >= 0) { ]]-
+ <dt>Platform[[: $#{$d{rel}{platforms}} > 0 ? 's' : '' ]]</dt><dd>[[: join(', ', map {
+ $VNDB::PLAT->{$_} } @{$d{rel}{platforms}}) ]]</dd>[[ } ]]-
+[[ if($#{$d{rel}{media}} >= 0) { ]]-
+ <dt>Medi[[: $#{$d{rel}{media}} > 0 ? 'a' : 'um' ]]</dt><dd>[[: mediastr($d{rel}{media}) ]]</dd>[[ } ]]-
+[[ if($d{rel}{website}) { ]]-
+ <dt>Links</dt><dd><a href="[[: $d{rel}{website} ]]">Official website</a></dd>[[ } ]]-
+</dl>
+
+[[ if($d{rel}{notes}) { ]]-
+<p>[[= summary($d{rel}{notes}) ]]<br /><br /></p>
+[[ } ]]-
+
+
+
+[[ } ]]
diff --git a/data/tpl/useredit b/data/tpl/useredit
new file mode 100644
index 00000000..f470ce0a
--- /dev/null
+++ b/data/tpl/useredit
@@ -0,0 +1,34 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+
+-[[ if($d{done}) { ]]
+<span class="msg">
+ Settings succesfully saved.
+</span>
+[[ } ]]
+-[[= cform( [
+ { type => 'error' },
+ { type => 'startform', action => '/u'.$d{user}.'/edit' },
+
+ { type => 'sub', title => 'General info', short => 'info' },
+ { type => 'static', name => 'Username', text => _hchar($d{form}{username}) },
+ { type => 'input', name => 'Email', short => 'mail' },
+
+ { type => 'sub', title => 'Change password', short => 'pass' },
+ { type => 'static', text => 'Leave blank to keep your current password.' },
+ { type => 'pass', name => 'Password', short => 'pass1' },
+ { type => 'pass', name => 'Confirm', short => 'pass2' },
+
+ { type => 'sub', title => 'Miscellaneous options', short => 'misc' },
+ { type => 'check', short => 'pvotes', name => sprintf 'Allow other people to see my votes (<a href="/u%d/votes">/u%1$d/votes</a>)', $d{user} },
+ { type => 'check', short => 'plist', name => sprintf 'Allow other people to see my visual novel list (<a href="/u%d/list">/u%1$d/list</a>)', $d{user} },
+ { type => 'check', short => 'pign_nsfw', name => 'Disable warnings for images that are not safe for work.' },
+
+ $d{adm} ? (
+ { type => 'sub', title => 'Admin', short => 'adm' },
+ { type => 'select', name => 'Rank', short => 'rank', options => [
+ map { { name => $VNDB::VNDBopts{ranks}[0][0][$_], short => $_ } } 1..($#{$VNDB::VNDBopts{ranks}}-1) ] },
+ ) : (),
+
+ { type => 'submit', text => 'Save' },
+ { type => 'endform' },
+], $d{form}) ]]
diff --git a/data/tpl/userlist b/data/tpl/userlist
new file mode 100644
index 00000000..4fcb12c7
--- /dev/null
+++ b/data/tpl/userlist
@@ -0,0 +1,54 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+<p class="chr">
+ -[[= $d{chr} ne 'all' ? '<a href="/u/list/all">all</a>' : 'all' ]]- |
+ [[ for('a'..'z', 0) { ]]-
+ -[[ if($d{chr} eq $_) { ]][[= $_?$_:'#' ]][[ } else { ]]<a href="/u/list/[[= $_ ]]">[[= $_?$_:'#' ]]</a>[[ } ]]
+ [[ } ]]-
+ <br /><br />
+</p>
+
+[[ if($#{$d{users}} < 0) { ]]-
+<p>
+ No users found...
+</p>
+[[ } else {
+ my $url = sprintf '/u/list/%s', $d{chr};
+ my $surl = sprintf '%s?s=%s&amp;o=%s', $url, $d{order}[0], $d{order}[1];
+]]
+[[= pagebut($surl) ]]-
+<table id="tul">
+ <thead><tr>
+ <td class="tc1">Username [[= sortbut($url, 'username') ]]</td>
+[[ if($p{Authuserlist}) { ]]-
+ <td class="tc2">Mail [[= sortbut($url, 'mail') ]]</td>
+ <td class="tc3">Rank [[= sortbut($url, 'rank') ]]</td>[[ } ]]-
+ <td class="tc4">Registered [[= sortbut($url, 'registered') ]]</td>
+ <td class="tc5">VN list</td>
+ <td class="tc6">Votes</td>
+ <td class="tc7">Changes</td>
+[[ if($p{Authuseredit}) { ]]-
+ <td class="tc8">&nbsp;</td>[[ } ]]-
+ </tr></thead>
+ [[ for (@{$d{users}}) { ]]-
+ <tr>
+ <td class="tc1"><a href="/u[[= $_->{id} ]]">[[: $_->{username} ]]</a></td>
+[[ if($p{Authuserlist}) { ]]-
+ <td class="tc2">[[: $_->{mail} ]]</td>
+ <td class="tc3">[[: $VNDB::VNDBopts{ranks}[0][0][$_->{rank}] ]]</td>[[ } ]]-
+ <td class="tc4">[[= formatdate('%Y-%m-%d', $_->{registered}, 'wd') ]]</td>
+ <td class="tc5">[[ if($_->{flags} & $VNDB::UFLAGS->{list} && $_->{vnlist}) { ]]
+ <a href="/u[[= $_->{id} ]]/list" title="[[: $_->{username} ]]'s visual novel list">[[= $_->{vnlist} ]]</a>
+ [[ } else { ]][[= $_->{flags} & $VNDB::UFLAGS->{list} ? 0 : '-' ]][[ } ]]</td>
+ <td class="tc6">[[ if($_->{flags} & $VNDB::UFLAGS->{votes} && $_->{votes}) { ]]
+ <a href="/u[[= $_->{id} ]]/votes" title="[[: $_->{username} ]]'s votes">[[= $_->{votes} ]]</a>
+ [[ } else { ]][[= $_->{flags} & $VNDB::UFLAGS->{votes} ? 0 : '-' ]][[ } ]]</td>
+ <td class="tc7">[[ if($_->{changes}) { ]]
+ <a href="/u[[= $_->{id} ]]/hist" title="Recent changes by -[[: $_->{username} ]]">[[= $_->{changes} ]]</a>
+ [[ } else { ]]0[[ } ]]</td>
+[[ if($p{Authuseredit}) { ]]-
+ <td class="tc8">( <a href="/u[[= $_->{id} ]]/edit">edit</a> )</td>[[ } ]]-
+ </tr>
+ [[ } ]]-
+</table>
+-[[= pagebut($surl) ]]-
+[[ } ]]
diff --git a/data/tpl/userlogin b/data/tpl/userlogin
new file mode 100644
index 00000000..b4af29d2
--- /dev/null
+++ b/data/tpl/userlogin
@@ -0,0 +1,14 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+-[[= cform( [
+ { type => 'error' },
+ { type => 'startform', action => '/u/login' },
+ { type => 'input', short => 'username', name => 'Username' },
+ { type => 'pass', short => 'userpass', name => 'Password' },
+ { type => 'submit', text => 'Login!' },
+ { type => 'endform' },
+], $d{log}) ]]-
+
+<p>
+ <br /><br />
+ <a href="/u/register">No account yet</a>, or <a href="/u/newpass">forgot your username or password?</a>
+</p>
diff --git a/data/tpl/userpage b/data/tpl/userpage
new file mode 100644
index 00000000..9b14efc9
--- /dev/null
+++ b/data/tpl/userpage
@@ -0,0 +1,13 @@
+[[
+ ($d{pv}, $d{pl}) = ($d{user}{flags} & $VNDB::UFLAGS->{votes}, $d{user}{flags} & $VNDB::UFLAGS->{list});
+]]
+<h2>[[: $p{PageTitle} ]]</h2>
+<dl>
+ <dt>Username</dt><dd>[[: $d{user}{username} ]]- (<a href="/u[[= $d{user}{id} ]]">u[[= $d{user}{id} ]]</a>)</dd>
+ <dt>Registered</dt><dd>[[= formatdate('%Y-%m-%d', $d{user}{registered}) ]]</dd>
+ <dt>Votes</dt><dd>[[= $d{pv} ? $d{user}{votes}.' (<a href="/u'.$d{user}{id}.'/votes">view all</a>)' : '(hidden)' ]]</dd>
+ <dt>VN List</dt><dd>[[= $d{pl} ? $d{user}{vnlist}.' (<a href="/u'.$d{user}{id}.'/list">view all</a>)' : '(hidden)' ]]</dd>
+ <dt>Changes</dt><dd>[[= $d{user}{changes}.($d{user}{changes}>0?' (<a href="/u'.$d{user}{id}.'/hist">recent changes</a>)':'') ]]</dd>
+</dl>
+
+[[= T_vnpage_stats($X) ]]
diff --git a/data/tpl/userpass b/data/tpl/userpass
new file mode 100644
index 00000000..c3b04840
--- /dev/null
+++ b/data/tpl/userpass
@@ -0,0 +1,21 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+<p>
+ You're lucky that vndb has a very advanced password recovery tool! Just
+ type your email address (the same one you used for your account), and
+ wait for an email!
+</p>
+
+-[[ if(!$d{done}) { ]]
+[[= cform( [
+ { type => 'error', },
+ { type => 'startform', action => '/u/newpass' },
+ { type => 'input', short => 'mail', name => 'Email' },
+ { type => 'submit', text => 'Gimme my password!' },
+ { type => 'endform' },
+], $d{pas} ) ]]
+
+[[ } else { ]]
+<span class="msg">
+ Your password succesfully been reset. Check your mail for instructions.
+</span>
+[[ } ]]
diff --git a/data/tpl/userreg b/data/tpl/userreg
new file mode 100644
index 00000000..68565470
--- /dev/null
+++ b/data/tpl/userreg
@@ -0,0 +1,38 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+
+-[[ if($d{denied}) { ]]
+[[ } ]]-
+
+<br /><br />
+<h3>Why should I register?</h3>
+<p>
+ Registered users have access to special features on this site:
+</p>
+<ul>
+ <li>You can keep track of the visual novels you'd like to play or have
+ finnished playing,</li>
+ <li>Vote on visual novels,</li>
+ <li>And more importantly: you can add and edit all information on the
+ website!</li>
+</ul>
+<p>
+ <br />
+ And of course, registering an account is (and will always remain)
+ completely free!
+ <br /><br />
+</p>
+
+-[[= cform( [
+ { type => 'error' },
+ { type => 'startform', action => '/u/register' },
+ { type => 'input', short => 'username', name => 'Username' },
+ { type => 'input', short => 'mail', name => 'Email' },
+ { type => 'static', text => q|
+ Your email address will only be used in case you lose your password, at least for now.
+ We will never send spam or newsletters unless you explicitly ask us for it.
+ | },
+ { type => 'pass', short => 'pass1', name => 'Password' },
+ { type => 'pass', short => 'pass2', name => 'Confirm pass.' },
+ { type => 'submit', text => 'Register!' },
+ { type => 'endform' },
+], $d{reg}) ]]
diff --git a/data/tpl/vnbrowse b/data/tpl/vnbrowse
new file mode 100644
index 00000000..79dd122e
--- /dev/null
+++ b/data/tpl/vnbrowse
@@ -0,0 +1,87 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+
+[[ if($d{chr} eq 'cat') { ]]-
+<ul id="cat">
+[[ for my $c (qw| e g p t l s |) { ]]-
+ -[[= $c ne 'l' && $c ne 'p' ? '<li>' : '<br />' ]][[: $VNDB::CAT->{$c}[0] ]]-
+ <ul>
+ [[ for (sort keys %{$VNDB::CAT->{$c}[1]}) { ]]-
+ <li class="cat_[[= $c.$_ ]][[= $d{incl} =~ /$c$_/ ? ' inc' : $d{excl} =~ /$c$_/ ? ' exc' : '' ]]">
+ [[: $VNDB::CAT->{$c}[1]{$_} ]]- ([[= $d{cat}{$c.$_} || 0 ]])</li>
+ [[ } ]]
+ </ul>[[= $c ne 't' && $c ne 'g' ? '</li>' : '' ]]-
+[[ } ]]-
+</ul>
+<div id="lfilter">
+ <b>Languages</b> (none selected means all)<br />
+[[ for (sort keys %{$d{lang}}) { next if !$d{lang}{$_}; ]]-
+ <input type="checkbox" name="lang_[[= $_ ]]" id="lang_[[= $_ ]]" value="1" -[[= $d{slang}=~/$_/?'checked="checked"':'' ]]>
+ <label for="lang_[[= $_ ]]">[[: $VNDB::LANG->{$_} ]]- ([[= $d{lang}{$_} ]])</label>
+[[ } ]]-
+</div>
+<br style="clear: left" />
+<input type="button" class="right" id="catsearch" name="catsearch" value="Search!" />
+<br style="clear: left" />
+<br />
+<br />
+
+[[ } elsif($d{chr} ne 'search') { ]]-
+<p class="chr">
+ -[[= $d{chr} ne 'all' ? '<a href="/v/all">all</a>' : 'all' ]]- |
+ [[ for('a'..'z', 0) { ]]-
+ -[[ if($d{chr} eq $_) { ]][[= $_?$_:'#' ]][[ } else { ]]<a href="/v/[[= $_ ]]">[[= $_?$_:'#' ]]</a>[[ } ]]
+ [[ } ]]-
+ <br /><br />
+</p>
+[[ } ]]-
+
+-[[ if($#{$d{vn}} < 0) { ]]
+<p>
+ -[[ if($d{chr} eq 'cat' && !$d{scat}[0][0] && !$d{scat}[0][1]) { ]]
+ Select some categories and hit the "Search" button to get a list of visual novels. Click on a
+ category again to exclude it.<br />
+ Please keep in mind that not all visual novels have the correct categories set, so you
+ may not always find what you are looking for.
+ [[ } else { ]]
+ No results again, life sucks... :'(
+ [[ } ]]-
+</p>
+[[ } else {
+ my %url = (
+ $p{searchquery} ? ( q => $p{searchquery} ) : (),
+ $d{incl} ? ( i => $d{incl} ) : (),
+ $d{excl} ? ( e => $d{excl} ) : (),
+ $d{slang} ? ( l => $d{slang} ) : (),
+ );
+ my %urls = ( %url,
+ $d{order}[0] ne 'title' ? ( s => $d{order}[0] ) : (),
+ $d{order}[1] ne 'a' ? ( o => $d{order}[1] ) : (),
+ );
+ my $url = sprintf '/v/%s', $d{chr};
+ my $urls = $url;
+ $urls .= '?'.join(';', map { $_.'='.$urls{$_} } keys %urls) if keys %urls;
+ $url .= '?'.join(';', map { $_.'='.$url{$_} } keys %url) if keys %url;
+]]
+
+[[= pagebut($urls) ]]
+<table id="tbv">
+ <thead><tr>
+ <td class="tc1">Title [[= sortbut($url, 'title') ]]</td>
+ <td class="tc2">Released [[= sortbut($url, 'released') ]]</td>
+ <td class="tc3">Languages</td>
+ <td class="tc4">Rating [[= sortbut($url, 'votes') ]]</td>
+ </thead></tr>
+ [[ for (@{$d{vn}}) {
+ $_->{c_votes} =~ s#^([0-9]{2}.[0-9]{2})\|([0-9]{4})$#$1 == 0 ? sprintf '- (%d)', $2 : sprintf '%.2f (%d)', $1, $2#e;
+ $_->{c_released} =~ s#^([0-9]{4})([0-9]{2}).+#$1==0?'N/A':$1==9999?'TBA':(($2&&$2>0?($Time::CTime::MoY[$2-1].' '):'').$1)#e;
+ ]]-
+ <tr>
+ <td class="tc1"><a href="/v[[= $_->{id} ]]">[[: $_->{title} ]]</a></td>
+ <td class="tc2">[[: $_->{c_released} ]]</td>
+ <td class="tc3">[[: $_->{c_languages} || 'N/A' ]]</td>
+ <td class="tc4">[[: $_->{c_votes} ]]</td>
+ </tr>
+ [[ } ]]-
+</table>
+[[= pagebut($urls) ]]
+[[ } ]]
diff --git a/data/tpl/vnedit b/data/tpl/vnedit
new file mode 100644
index 00000000..f3ae245c
--- /dev/null
+++ b/data/tpl/vnedit
@@ -0,0 +1,94 @@
+[[= $d{id} ? ttabs('v', $d{vn}, 'edit') : '' ]]-
+<h2>[[: $p{PageTitle} ]]</h2>
+
+[[ if(!$d{id}) { ]]
+ <span class="msg">Please search the database before adding a new visual novel
+ in order to prevent duplicate entries.</span>
+[[ } else { ]]
+ <span class="msg">
+ It is currently not possible to delete visual novels from the database, please
+ use the <a href="http://forum.vndb.org/index.php?board=5.0">forums</a> to request
+ a deletion. Also refer to the forums for more serious edits or discussions about changes.
+ </span>
+[[ } if($d{id} && $d{vn}{cid} != $d{vn}{latest}) { ]]
+ <span class="warning">
+ You are editing an old revision of this producer. If you save it, all changes made after
+ -[[= formatdate('%Y-%m-%d %R', $d{vn}{added}) ]]- will be removed!
+ </span>
+[[ } ]]
+
+
+-[[= cform([
+ { type => 'error' },
+ { type => 'startform', action => $d{id} ?( '/v'.$d{id}.'/edit') : '/v/new', upload => 1, fh => 1 },
+
+ { type => 'sub', title => 'General info', short => 'info' },
+ { type => 'input', name => 'Title', short => 'title', r=>1 },
+ { type => 'static', text => q|
+ Use official English title if available, use the romanized version of the official title otherwise.
+ Other titles can be added at a later time when specifying releases.<br /><br />| },
+
+ { type => 'textarea', name => 'Aliases', short => 'alias', rows => 2, cols => 60 },
+ { type => 'static', text => q|
+ Comma seperated list of alternative titles or abbreviations. Can include both official
+ (japanese/english) titles and unofficial titles used around net. <b>Titles that are listed in the releases do not have to be added here.</b><br /><br />| },
+
+ { type => 'textarea', name => 'Description', short => 'desc', rows => 7, cols => 70, r=>1 },
+ { type => 'static', text => q|
+ Short description of the main story. Please do not include spoilers, and don't forget to list the source
+ in case you didn't write the description yourself. ([url] BBCode tag is allowed)<br /><br />| },
+
+ { type => 'select', name => 'Length', short => 'length', class => 'longopts', options => [ map {
+ { short => $_,
+ name => !$_?$VNDB::VNLEN->[$_][0]:($VNDB::VNLEN->[$_][0].', '.$VNDB::VNLEN->[$_][1].' ('.$VNDB::VNLEN->[$_][2].')') } } 0..$#$VNDB::VNLEN
+ ] },
+ { type => 'static', text => '<br />' },
+ { type => 'input', name => 'External links', short => 'l_wp', pre => 'http://en.wikipedia.org/wiki/' },
+ { type => 'input', name => '&nbsp;', short => 'l_vnn', pre => 'http://visual-novels.net/vn/index.php?option=com_content&amp;task=view&amp;id=', class => 'shortopts' },
+ { type => 'input', name => '&nbsp;', short => 'l_cisv', pre => 'http://cisvisual.net/title/', class => 'shortopts' },
+
+ { type => 'sub', title => 'Categories', short => 'cat' },
+ { type => 'hidden', short => 'categories' },
+ { type => 'static', raw => 1, text => eval {
+ my $r = '<ul id="cat">';
+ for my $c (qw| e g p t l s |) {
+ $r .= ($c ne 'l' && $c ne 'p' ? '<li>' : '<br />').$VNDB::CAT->{$c}[0].'<ul>';
+ for (sort keys %{$VNDB::CAT->{$c}[1]}) {
+ $r .= sprintf '<li><a href="#" id="cat_%1$s"><b id="b_%1$s">-</b> %2$s</a></li>',
+ $c.$_, $VNDB::CAT->{$c}[1]{$_};
+ }
+ $r .= '</ul>'.($c ne 't' && $c ne 'g' ? '</li>' : '');
+ }
+ $r.'</ul>';
+ } },
+
+ { type => 'sub', title => 'Image', short => 'img' },
+ $d{id} ? (
+ { type => 'static', text => $d{vn}{image} ?
+ sprintf '<img src="%s/cv/%02d/%d.jpg" style="float: right" />', $p{st}, $d{vn}{image}%50, $d{vn}{image} :
+ 'No image uploaded yet...' },
+ ) : (),
+ { type => 'upload', name => $d{vn}{image} ? 'Change' : 'Upload', short => 'img' },
+ { type => 'static', text => q|
+ Preferably the cover of the CD/DVD/package. Image must be in JPEG format and at most 256x400px and 50KB.<br /><br />| },
+ { type => 'check', short => 'img_nsfw', name => '<b>NSFW.</b> Please check this option if the image contains nudity, gore, or is otherwise not safe in a work-friendly environment.' },
+
+ { type => 'sub', title => 'Visual novel relations', short => 'rel' },
+ { type => 'jssel', name => 'Relations', short => 'relations', sh => 'rl' },
+ { type => 'static', text => q|
+ <b>Direct relations:</b> Please only add direct relations. E.g. the sequel of a sequel does not have to be listed
+ here because it's already listed on an other visual novel that is in turn listed here. VNDB will handle these
+ relations automatically.<br />
+ <b>Reverse relations:</b> If you add a relation with an other visual novel here, the same (or "reverse") relation
+ will automatically be added to the other visual novel. For example: if you add Tsukihime as a prequel of Kagetsu Tohya,
+ Kagetsu Tohya will automatically be added as a sequel for Tsukihime.
+ |},
+
+ { type => 'sub', title => 'Edit summary', short => 'com' },
+ { type => 'textarea', name => 'Edit summary', short => 'comm', rows => 3, cols => 60 },
+ { type => 'static', text => 'Please motivate your modifications and cite all sources.' },
+
+ { type => 'submit', text => $d{id} ? 'Edit' : 'Add' },
+ { type => 'endform' },
+
+], $d{form}) ]]
diff --git a/data/tpl/vnlist b/data/tpl/vnlist
new file mode 100644
index 00000000..64db1c05
--- /dev/null
+++ b/data/tpl/vnlist
@@ -0,0 +1,74 @@
+<h2>[[: $p{PageTitle} ]]</h2>
+[[
+ my $url = sprintf '/u%d/list', $d{user}{id};
+ my $surl = sprintf '%s?s=%s;o=%s', $url, $d{order}[0], $d{order}[1];
+ my $purl = $surl . ';t='.$d{status};
+ my $sourl = $url . '?t='.$d{status};
+ my $furl = $purl . ';p='.$d{page};
+]]
+<p class="chr">
+ status: -[[ for (-1..$#$VNDB::LSTAT) { if($_ >= 0) { ]]- | -[[ }
+ if($d{status} == $_) { ]]<b>[[= $_ eq -1 ? 'all' : lc $VNDB::LSTAT->[$_] ]]</b>[[ }
+ else { ]]<a href="[[= $surl ]]&amp;t=[[= $_ ]]">[[= $_ eq -1 ? 'all' : lc $VNDB::LSTAT->[$_] ]]</a>[[ } } ]]
+ <br /><br />
+</p>
+
+
+[[ if($#{$d{list}} < 0) { ]]-
+<p>
+[[ if($d{status} >= 0) { ]]
+ No results found...
+[[ } elsif($d{user}{username} eq $p{AuthUsername}) { ]]
+ Your visual novel list is empty. You can keep track of all the visual novels
+ you'd like to play, you're currently playing, or you've finished. Just go to
+ a visual novel page and add it to your VN list!
+[[ } else { ]]
+ [[: $d{user}{username} ]]'s visual novel list is empty...
+[[ } ]]
+</p>
+
+[[ } else { ]]
+[[= pagebut($purl) ]]-
+[[ if($d{user}{username} eq $p{AuthUsername}) { ]]
+<form method="post" action="[[= $furl ]]" class="tblf">
+<input type="hidden" class="hidden" name="comments" id="comments" value="" />[[ } ]]
+<table id="tvl">
+ <thead><tr>
+ <td class="tc1">Title [[= sortbut($sourl, 'title') ]]</td>
+ <td class="tc2">Status</td>
+ <td class="tc3">Added [[= sortbut($sourl, 'date') ]]</td>
+ [[ if($d{user}{username} eq $p{AuthUsername}) { ]]-
+ <td class="tc4">Personal note</td>
+ <td class="tc5">&nbsp;</td>[[ } ]]-
+ </tr></thead>
+ [[ for (@{$d{list}}) { ]]-
+ <tr>
+ <td class="tc1"><a href="/v[[= $_->{vid} ]]" title="[[: $_->{title} ]]">[[: length($_->{title})>40 ? substr($_->{title},0, 37).'...' : $_->{title} ]]</a></td>
+ <td class="tc2">[[= $VNDB::LSTAT->[$_->{status}] ]]</td>
+ <td class="tc3">[[= formatdate('%Y-%m-%d', $_->{date}, 'dh') ]]</td>
+ [[ if($d{user}{username} eq $p{AuthUsername}) { ]]
+ <td class="tc4">[[: $_->{comments}||'-' ]]</td>
+ <td class="tc5"><input type="checkbox" name="sel" value="[[= $_->{vid} ]]" /></td>[[ } ]]
+ </tr>
+ [[ } ]]-
+</table>
+[[ if($d{user}{username} eq $p{AuthUsername}) { ]]
+<select id="vnlistchange" name="vnlistchange" class="right">
+ <option value="-3">- with selected -</option>
+ <option value="-1">Delete</option>
+ <option value="-2">Update personal note</option>
+ <optgroup label="Update status:">
+ [[ for (0..$#$VNDB::LSTAT) { ]]-
+ <option value="[[= $_ ]]">[[: $VNDB::LSTAT->[$_] ]]</option>
+ [[ } ]]
+ </optgroup>
+</select>
+</form>[[ } ]]
+-[[= pagebut($purl) ]]
+[[ } ]]-
+
+[[ if($d{user}{username} eq $p{AuthUsername}) { ]]-
+<p>
+ <br /><br />
+ NOTE: Your personal notes are only visible to you, other people can't see them.
+</p>[[ } ]]
diff --git a/data/tpl/vnpage b/data/tpl/vnpage
new file mode 100644
index 00000000..15cb3235
--- /dev/null
+++ b/data/tpl/vnpage
@@ -0,0 +1,171 @@
+[[= ttabs('v', $d{vn}) ]]
+<h2>[[: $d{vn}{title} ]]</h2>
+
+[[ if($d{vn}{hidden}) { ]]-
+ <span class="warning">
+ This item has been deleted from the database. File a request on the
+ <a href="http://forum.vndb.org/index.php?board=5.0">forums</a>
+ to undelete this page.
+ </span>
+[[ } ]]
+[[ if(!$d{vn}{hidden} || $p{Authdel}) { ]]-
+
+
+[[ if($d{change}) { ]]
+[[= cdiff($d{prev}, $d{vn},
+ [ title => 'Title', 1 ],
+ [ alias => 'Alias', 1 ],
+ [ desc => 'Description', 1, 1 ],
+ [ length => 'Length', sub { $VNDB::VNLEN->[$_[0] ][0] } ],
+ [ l_wp => 'Wikipedia link', sub { $_[0] ? '<a href="http://en.wikipedia.org/wiki/'.$_[0].'">'.$_[0].'</a>' : 'No link' } ],
+ [ l_vnn => 'V-N.net link', sub { $_[0] ? '<a href="http://visual-novels.net/vn/index.php?option=com_content&amp;task=view&amp;id='.$_[0].'">'.$_[0].'</a>' : 'No link' } ],
+ [ l_cisv => 'CISVisual link', sub { $_[0] ? '<a href="http://cisvisual.net/title/'.$_[0].'">'.$_[0].'</a>' : 'No link' } ],
+ [ categories => 'Categories', sub { join(' ', map { $VNDB::CAT->{substr($_->[0],0,1)}[1]{substr($_->[0],1,2)}.'('.$_->[1].')' } sort { $a->[0] cmp $b->[0] } @{$_[0]}) || 'No categories selected' }, 1 ],
+ [ relations => 'Relations', sub { join("<br />\n", map { $VNDB::VREL->[$_->{relation}].': '._hchar($_->{title}) } sort { $a->{id} <=> $b->{id} } @{$_[0]}) } ],
+ [ image => 'Image', sub { $_[0] ? sprintf '<img src="%s/cv/%02d/%d.jpg" />', $p{st}, $_[0]%50, $_[0] : 'No image'; } ],
+ [ img_nsfw => 'NSFW', sub { $_[0] ? 'Not safe' : 'Safe' } ]
+ ) ]]
+[[ } ]]-
+
+[[
+ my @lang;
+ for (@{$d{rel}}) {
+ my $l = $_->{language};
+ next if grep { $_ eq $l } @lang;
+ push @lang, $l;
+ }
+
+]]
+
+
+<div id="vnheader">
+<div>
+[[ if($d{vn}{image}) { ]]
+ [[ if($d{vn}{img_nsfw} && !$p{AuthNsfw}) { ]]
+ <img src="[[: $p{st} ]]/cv/nsfw.png" id="nsfw" class="[[: $p{st} ]]/cv/[[= sprintf '%02d/%d', $d{vn}{image}%50, $d{vn}{image} ]].jpg" />
+ [[ } else { ]]
+ <img src="[[: $p{st} ]]/cv/[[= sprintf '%02d/%d', $d{vn}{image}%50, $d{vn}{image} ]].jpg" alt="[[: $p{PageTitle} ]]" />
+ [[ } ]]
+[[ } else { ]]-
+ No image uploaded yet...
+[[ } ]]-
+</div>
+
+
+-[[ if($p{AuthLoggedin}) { ]]
+<p class="mod">&lt; user options -
+ <a href="/u[[= $p{AuthId} ]]/votes" rel="voteDD" class="dropdown">[[= $d{vote}{vid} ? 'your vote: '.$d{vote}{vote} : 'vote' ]]</a>
+- <a href="/u[[= $p{AuthId} ]]/list" rel="listDD" class="dropdown">[[= !$d{list}{vid} ? 'add to vn list' : 'status: '.lc $VNDB::LSTAT->[$d{list}{status}] ]]</a>
+&gt;</p>
+[[ } ]]-
+
+-[[
+ $d{vn}{c_votes} =~ s#^([0-9]{2}.[0-9]{2})\|([0-9]{4})$#$2 == 0 ? 'No votes yet' :
+ $1 == 0 ? sprintf 'N/A (%d vote%s)', $2, $2>1?'s':'' : sprintf '%.2f (%d vote%s)', $1, $2, $2>1?'s':''#e;
+
+ my @links = (
+ $d{vn}{l_wp} ? [ 'Wikipedia', 'http://en.wikipedia.org/wiki/%s', $d{vn}{l_wp} ] : (),
+ $d{vn}{l_vnn} ? [ 'V-N.net', 'http://visual-novels.net/vn/index.php?option=com_content&amp;task=view&amp;id=%d', $d{vn}{l_vnn} ] : (),
+ $d{vn}{l_cisv} ? [ 'CISVisual', 'http://cisvisual.net/title/%d', $d{vn}{l_cisv} ] : (),
+ );
+
+if($d{vn}{length} || $d{vn}{alias} || @links) { ]]
+ <h3>General info</h3>
+ <dl>
+ [[ if($d{vn}{length}) { ]]-
+ <dt>Length</dt><dd>[[: $VNDB::VNLEN->[$d{vn}{length}][0] ]]- ([[: $VNDB::VNLEN->[$d{vn}{length}][1] ]])</dd>[[ } ]]-
+ [[ if($d{vn}{alias}) { ]]-
+ <dt>Aliases</dt><dd>[[: $d{vn}{alias} ]]</dd>[[ } ]]-
+ [[ if(@links > 0) { ]]
+ <dt>Links</dt><dd>[[= join(', ', map { '<a href="'.sprintf($_->[1],$_->[2]).'">'.$_->[0].'</a>' } @links) ]]</dd>[[ } ]]-
+ </dl>
+[[ } ]]-
+
+ [[ if(@{$d{vn}{categories}}) { my %nolvl = (pli=>1,pbr=>1,gaa=>1,gab=>1); ]]-
+ <h3>Categories</h3>
+ <dl class="vncat">
+ [[ for (sort keys %$VNDB::CAT) {
+ my $c = $_;
+ my @c = map { my $s=$_;
+ my ($cs) = grep { $_->[0] eq $c.$s } @{$d{vn}{categories}};
+ $cs ? sprintf('<i class="crgn%d">%s</i>', $nolvl{$c.$_}?0:$cs->[1], $VNDB::CAT->{$c}[1]{$s})
+ : ()
+ } sort keys %{$VNDB::CAT->{$c}[1]};
+ if(@c) { ]]-
+ <dt>[[: $VNDB::CAT->{$c}[0] ]]</dt><dd>[[= join(', ', @c) ]]</dd>
+ [[ } } ]]
+ </dl>
+ [[ } ]]-
+
+ [[ if($#{$d{vn}{relations}} >= 0) { ]]-
+ <h3>[[= $d{page} eq 'rg' ? 'Relations' : '<a href="/v'.$d{vn}{id}.'/rg">Relations</a>' ]]</h3>
+ <dl class="vnrel">
+ [[ my $lrel = -1; my $i=0; for (sort { $a->{relation} <=> $b->{relation} } @{$d{vn}{relations}}) {
+ if($_->{relation} != $lrel) { $lrel=$_->{relation}; if($i) { ]]</dd>[[ } ]]-
+ <dt>[[: $VNDB::VREL->[$lrel] ]]</dt><dd><a href="/v[[= $_->{id} ]]">[[: $_->{title} ]]</a>
+ [[ } else { ]]<br /><a href="/v[[= $_->{id} ]]" title="[[: $_->{title} ]]">[[: shorten $_->{title}, 40 ]]</a>[[ }
+ ++$i;} ]]
+ </dl>
+ [[ } ]]-
+
+ [[ if(@lang && grep { @{$_->{producers}} } @{$d{rel}}) { ]]-
+ <h3>Producers</h3>
+ <dl>
+ [[ for my $l (@lang) { my %l;
+ $_->{language} eq $l && (%l = ( %l, map {
+ sprintf('<a href="/p%d" title="%s">%s</a>',
+ $_->{id}, _hchar($_->{name}), _hchar shorten $_->{name}, 30) => 1
+ } @{$_->{producers}} )) for (@{$d{rel}});
+ if(keys %l) { ]]-
+ <dt>[[: $VNDB::LANG->{$l} ]]</dt><dd>[[= join(' &amp; ', keys %l) ]]</dd>
+ [[ } } ]]
+ </dl>
+ [[ } ]]-
+
+ <h3>[[= $d{page} eq 'stats' ? 'User stats' : '<a href="/v'.$d{vn}{id}.'/stats">User stats</a>' ]]</h3>
+ <dl>
+ <dt>Rating</dt><dd>[[: $d{vn}{c_votes} ]]</dd>
+ </dl>
+</div>
+
+-[[
+ my @lnks = (
+ !$d{page} ? '<b>description &amp; releases</b>' : '<a href="/v'.$d{vn}{id}.'">description &amp; releases</a>',
+ $d{page} eq 'stats' ? '<b>stats</b>' : '<a href="/v'.$d{vn}{id}.'/stats">stats</a>',
+ $d{vn}{rgraph} ? (
+ $d{page} eq 'rg' ? '<b>relations</b>' : '<a href="/v'.$d{vn}{id}.'/rg">relations</a>',
+ ) : (),
+ );
+]]
+<p class="opts">- -[[= join(' - ', @lnks) ]]- -</p>
+
+[[ if(!$d{page}) { ]][[+ vnpage_rel ]][[ } ]]
+[[ if($d{page} eq 'stats') { ]][[+ vnpage_stats ]][[ } ]]
+[[ if($d{page} eq 'rg') { ]][[+ vnpage_rg ]][[ } ]]
+
+[[ if($p{AuthLoggedin}) { ]]-
+
+<div class="dropdown" id="voteDD">
+ <ul>
+ [[ if($d{vote}{vid}) { ]]-
+ <li><a href="/v[[= $d{vn}{id} ]]/vote?v=-1">revoke</a></li>
+ [[ } for (reverse 1..10) { ]]-
+ <li class="center"><a href="/v[[= $d{vn}{id} ]]/vote?v=[[= $_ ]]">[[= $_ ]]</a></li>
+ [[ } ]]
+ </ul>
+</div>
+
+<div class="dropdown" id="listDD">
+ <ul>
+ [[ for (0..$#$VNDB::LSTAT) { ]]-
+ <li><a href="/v[[= $d{vn}{id} ]]/list?s=[[= $_ ]]" [[= $_ == 6 ? ' id="askcomment"' : '' ]]>[[: $VNDB::LSTAT->[$_] ]]</a></li>
+ [[ } if($d{list}{vid}) { ]]-
+ <li><a href="/v[[= $d{vn}{id} ]]/list?s=-1">Remove</a></li>
+ [[ } ]]-
+ </ul>
+</div>
+
+[[ } ]]
+
+
+[[ } ]]
diff --git a/data/tpl/vnpage_rel b/data/tpl/vnpage_rel
new file mode 100644
index 00000000..f2570548
--- /dev/null
+++ b/data/tpl/vnpage_rel
@@ -0,0 +1,51 @@
+<h3>Description</h3>
+<p class="desc">
+ [[= summary($d{vn}{desc}) ]]
+ <br /><br /><br />
+</p>
+
+
+
+[[
+ my @lang;
+ for (@{$d{rel}}) {
+ my $l = $_->{language};
+ next if grep { $_ eq $l } @lang;
+ push @lang, $l;
+ }
+
+]]
+
+
+<h3>Releases
+[[ if((!$d{vn}{locked} && $p{Authedit}) || $p{Authlock}) { ]]- <p class="actions">(<a href="/v[[= $d{vn}{id} ]]/add">add release</a>)</p>[[ } ]]</h3>
+[[ if(@{$d{rel}}) { ]]-
+<table id="tre">
+[[ for(@lang) { my $l = $_; ]]-
+<tr class="lang">
+ <td colspan="6">[[: $VNDB::LANG->{$l} ]]</td>
+</tr>
+[[ for (@{$d{rel}}) { next if $l ne $_->{language}; ]]-
+ <tr>
+ <td class="tc1">[[= datestr($_->{released}) ]]</td>
+ <td class="tc2">[[= $_->{minage}<0 ? '' : $VNDB::VRAGES->{$_->{minage}} ]]</td>
+ <td class="tc3">[[= join('', map { $_ ne 'oth' ? '<acronym class="plat '.$_.'" title="'._hchar($VNDB::PLAT->{$_}).'">'.$_.'</acronym>' : () } sort @{$_->{platforms}}) ]]</td>
+ <td class="tc4"><acronym title="[[= $VNDB::RTYP->[$_->{type}] ]]- release">[[= lc substr($VNDB::RTYP->[$_->{type}],0,1) ]]</acronym></td>
+ <td class="tc5"><a href="/r[[= $_->{id} ]]" title="[[: $_->{original} || $_->{title} ]]">[[: shorten $_->{title},60 ]]</a></td>
+<!-- <td class="tc6">[[=
+ join(', ',
+ map {
+ sprintf('<a href="/p%d" title="%s">%s</a>', $_->{id}, _hchar($_->{name}), _hchar(shorten $_->{name},20)) } @{$_->{producers}}) ]]</td>-->
+ <td class="tc7">[[ if($_->{website}) { ]]<a href="[[: $_->{website} ]]"><acronym class="plat ext" title="WWW">www</acronym></a>[[ } ]]</td>
+ </tr>
+[[ } ]]-
+[[ } ]]-
+</table>
+[[ } else { ]]-
+<p>
+ This game has either not been released yet, or we just don't have information about
+ any releases.
+</p>
+[[ } ]]
+
+
diff --git a/data/tpl/vnpage_rg b/data/tpl/vnpage_rg
new file mode 100644
index 00000000..d988e226
--- /dev/null
+++ b/data/tpl/vnpage_rg
@@ -0,0 +1,11 @@
+<h3>Relations</h3>
+[[ if(!$d{vn}{rgraph}) { ]]
+ <p>
+ Relation graph has not been generated yet...
+ </p>
+[[ } else { ]]
+ [[= $d{vn}{rmap} ]]
+ <p id="relations">
+ <img src="[[= sprintf "%s/rg/%02d/%d.gif", $p{st}, $d{vn}{rgraph}%50, $d{vn}{rgraph} ]]" usemap="#rgraph" />
+ </p>
+[[ } ]]
diff --git a/data/tpl/vnpage_stats b/data/tpl/vnpage_stats
new file mode 100644
index 00000000..dde9aed3
--- /dev/null
+++ b/data/tpl/vnpage_stats
@@ -0,0 +1,68 @@
+<ul id="stats">
+[[
+ my $max = 1; my $total = 0; my $sum = 0;
+ for (0..$#{$d{votes}{graph}}) {
+ $total += $d{votes}{graph}[$_];
+ $max = $d{votes}{graph}[$_] if $d{votes}{graph}[$_] > $max;
+ $sum += ($_+1) * $d{votes}{graph}[$_];
+ }
+]]
+[[ if(!$d{user} || ($d{pv} && $d{user}{votes})) { ]]-
+<li><h3>Vote graph <b class="actions">[[= $total ]]- vote[[= $total==1?'':'s' ]]- total
+ [[= $total ? sprintf(', average: %.1f.', $sum/$total) : '' ]]</b></h3>
+<table id="tvg">
+[[ for (0..$#{$d{votes}{graph}}) { ]]-
+ <tr>
+ <td class="tc1">[[= $_+1 ]]</td>
+ <td class="tc2"><div style="width: -[[= ($d{votes}{graph}[$_]/$max)*270 + 5 ]]px">&nbsp;</div>[[= $d{votes}{graph}[$_] ]]</td>
+ </tr>
+[[ } ]]-
+</table></li>
+
+[[ if($#{$d{votes}{latest}} >= 0) { ]]
+<li><h3>Recent votes</h3>
+<table id="tvr">
+[[ for (@{$d{votes}{latest}}) { ]]-
+ <tr>
+ [[ if(!$d{user}) { ]]-
+ <td class="tc1"><a href="/u[[= $_->{uid} ]]">[[: $_->{username} ]]</a></td>
+ [[ } else { ]]-
+ <td class="tc1"><a href="/v[[= $_->{vid} ]]">[[: length($_->{title})>30?substr($_->{title},0,27).'...':$_->{title} ]]</a></td>
+ [[ } ]]-
+ <td class="tc2">[[= $_->{vote} ]]</td>
+ <td class="tc3">[[= formatdate('%Y-%m-%d %R', $_->{date}, 'dh') ]]</td>
+ </tr>
+[[ } ]]-
+</table></li>
+[[ } } ]]-
+
+-[[ $max = 1; $total = 0;
+ for (@{$d{lists}{graph}}) { $total += $_; $max = $_ if $_ > $max; } ]]
+[[ if(!$d{user} || ($d{pl} && $d{user}{vnlist})) { ]]-
+<li class="break"><h3>VN List stats <b class="actions">[[= $total ]]- -[[= $d{user}?'visual novel':'user' ]][[= $total==1?'':'s' ]]- total</b></h3>
+<table id="tus">
+ [[ for (0..$#$VNDB::LSTAT) { ]]-
+ <tr>
+ <td class="tc1">[[= $VNDB::LSTAT->[$_] ]]</td>
+ <td class="tc2"><div style="width: -[[= ($d{lists}{graph}[$_]/$max)*235 + 5 ]]px">&nbsp;</div>[[= $d{lists}{graph}[$_] ]]</td>
+ </tr>
+ [[ } ]]-
+</table></li>
+
+[[ if($#{$d{lists}{latest}} >= 0) { ]]
+<li><h3>Recent VN list additions</h3>
+<table id="tur">
+[[ for (@{$d{lists}{latest}}) { ]]-
+ <tr>
+ [[ if(!$d{user}) { ]]-
+ <td class="tc1"><a href="/u[[= $_->{uid} ]]">[[: $_->{username} ]]</a></td>
+ [[ } else { ]]-
+ <td class="tc1"><a href="/v[[= $_->{vid} ]]">[[: length($_->{title})>25?substr($_->{title},0,23).'...':$_->{title} ]]</a></td>
+ [[ } ]]-
+ <td class="tc2">[[= $VNDB::LSTAT->[$_->{status}] ]]</td>
+ <td class="tc3">[[= formatdate('%Y-%m-%d %R', $_->{date}, 'dh') ]]</td>
+ </tr>
+[[ } ]]-
+</table></li>
+[[ } } ]]-
+</ul>