diff options
29 files changed, 521 insertions, 115 deletions
@@ -1,5 +1,7 @@ /data/config.pl /data/log/ +/static/s/*/style.css +/static/s/*/boxbg.png /static/cv/ /static/rg/ /static/sf/ diff --git a/data/docs/9 b/data/docs/9 index 6029ef1b..fd6da75e 100644 --- a/data/docs/9 +++ b/data/docs/9 @@ -1,11 +1,13 @@ :TITLE:Discussion board -:INC:notfinished :INC:index :SUB:Introduction <p> - <i>todo...</i> + VNDB has a nicely integrated discussion board which can be used for, well, + discussions. As we're not using any popular and freely available forum software + and have instead written something by ourselves, this discussion board has a + few slight differences with the popular boards you're used to. </p> @@ -53,6 +55,12 @@ </dd><dt>[spoiler]</dt><dd> The [spoiler]-tag should be used to hide information that could spoil the enjoyment of playing the visual novel for people who haven't done so yet. + </dd><dt>[quote]</dt><dd> + When quoting other people, put the quoted message inside a [quote] .. [/quote] block. Please + note that the popular [quote=source]-syntax doesn't work on VNDB. (yet) + </dd><dt>[raw]</dt><dd> + Show off your formatting code skills by putting anything you don't want to have formatted in a [raw] + tag. Any of the formatting codes mentioned above are ignored within a [raw] .. [/raw] block. </dd> </dl> <p> diff --git a/data/global.pl b/data/global.pl index bb4bfe4a..2c8a7a56 100644 --- a/data/global.pl +++ b/data/global.pl @@ -13,11 +13,12 @@ our %O = ( # VNDB-specific options (object_data) -our %S = ( +our %S = (%S, version => `cd $VNDB::ROOT; git describe` =~ /^(.+)$/ && $1, url => 'http://vndb.org', url_static => 'http://s.vndb.org', site_title => 'Yet another VNDB clone', + skin_default => 'test', cookie_domain => '.vndb.org', cookie_key => 'any-private-string-here', sharedmem_key => 'VNDB', @@ -203,6 +204,7 @@ our %S = ( 'low', 'blacklist', ], + # note: keep these synchronised in script.js vn_rstat => [ 'Unknown', 'Pending', diff --git a/static/f/style.css b/data/skingen/style.css index 92062a76..ecbba8a7 100644 --- a/static/f/style.css +++ b/data/skingen/style.css @@ -9,15 +9,15 @@ body, td { font: 8pt "Tahoma"; } body { - background: #000 url(/f/bg.jpg) no-repeat; - color: #ddd; + $_bodybg$; + color: $maintext$ } a { - color: #fff; + color: $maintext$; text-decoration: none; } a:hover { - border-bottom: 1px dotted #fff; + border-bottom: 1px dotted $maintext$; } table { border-collapse: collapse; @@ -27,7 +27,7 @@ table td { padding: 3px; } table tr.odd { - background: url(/f/boxbg.png) repeat; + background: url($_boxbg$) repeat; } img { border: none; @@ -37,9 +37,7 @@ img { position: absolute; top: 0px; right: 0px; - width: 433px; - height: 140px; - background: url(/f/bgright.jpg) no-repeat; + $_bgright$ } #header { @@ -51,7 +49,7 @@ img { font-family: "Futura", "Century New Gothic", "Arial", Serif; font-size: 19pt; font-style: italic; - color: #135; + color: $maintitle$; border: none!important; } #header h1.debug a { @@ -61,10 +59,10 @@ img { #footer { margin: 15px auto 0 auto; text-align: center; - color: #135; + color: $footer$; } #footer a { - color: #135; + color: $footer$; text-decoration: underline; } @@ -73,7 +71,7 @@ ul, ol { } p.locked { float: right; - color: #e44; + color: $standout$; font-style: italic; margin: 0!important; } @@ -84,14 +82,27 @@ p.locked { p.description { margin: 10px 100px!important; } -b.done { font-weight: normal; color: #0c0 } -b.todo { font-weight: normal; color: #c00 } +b.done { font-weight: normal; color: $statok$ } +b.todo { font-weight: normal; color: $statnok$ } .clearfloat { clear: both; height: 0; } +b.spoiler, b.spoiler a { + color: #000; + background-color: #000; + font-weight: normal; +} +b.spoiler_shown { font-weight: normal } +div.quote { + padding: 1px 5px; + margin: 0px 10px; + color: $grayedout$; + border-left: 1px dotted $border$; +} + @@ -105,9 +116,9 @@ legend { display: none; } input.text, input.submit, select, textarea { - background-color: #135; - color: #ccc; - border: 1px solid #36A; + background-color: $secbg$; + color: $maintext$; + border: 1px solid $secborder$; font: 9pt "Tahoma"; margin: 1px; } @@ -116,7 +127,7 @@ optgroup option { font-style: normal; } input.submit { - background-color: #123; + background: url($_boxbg$) repeat; padding: 1px; } input.text, select { @@ -178,23 +189,23 @@ td.field label { font-size: 11pt; } #maincontent h1 { - color: #258; + color: $boxtitle$; font-size: 14pt; margin: -5px 0 15px 0; } #maincontent h2.alttitle { - color: #fff; + color: $alttitle$; margin: -17px 0 15px 15px; font-weight: normal; } #maincontent div.mainbox { - border: 1px solid #258; + border: 1px solid $border$; margin: 21px 0 -10px 0; padding: 5px; - background: url(/f/boxbg.png) repeat; + background: url($_boxbg$) repeat; } #maincontent div.mainbox a { - color: #7bd; + color: $link$; } #maincontent p { margin: 3px 20px; @@ -212,7 +223,7 @@ p.center { } b.future { font-weight: normal; - color: #e44; + color: $standout$; } @@ -228,20 +239,20 @@ b.future { } #menulist div.menubox { margin: 0 0 10px 0; - border: 1px solid #258; - background: url(boxbg.png) repeat; + border: 1px solid $border$; + background: url($_boxbg$) repeat; } #menulist div.menubox div { padding: 2px 7px; } #menulist h2 { - border-bottom: 1px solid #258; - background-color: #234; + border-bottom: 1px solid $border$; + background: url($_boxbg$) repeat; padding: 1px 3px; } #menulist h2, #menulist h2 a { font-size: 8pt; - color: #fff; + color: $maintext$; } #menulist dt { display: block; @@ -295,18 +306,18 @@ b.future { float: right; display: block; height: 14px; - border: 1px solid #258; + border: 1px solid $border$; border-bottom: none; padding: 1px 7px 5px 7px; margin: 0 0 0 10px; - background-color: #012; + background-color: $tabbg$; } #maincontent ul.maintabs.notfirst li a { margin-top: 20px; } #maincontent ul.maintabs.bottom li a { margin-top: 10px; - border-bottom: 1px solid #258; + border-bottom: 1px solid $border$; border-top: none; padding: 4px 7px 2px 7px; } @@ -317,7 +328,7 @@ b.future { } #maincontent ul.maintabs li.tabselected a, #maincontent ul.maintabs li a:hover { - background-color: #0c1925; + background-color: $_blendbg$; padding-bottom: 6px; } #maincontent ul.maintabs.bottom li.tabselected a, @@ -380,13 +391,14 @@ p.browseopts { text-align: center; padding: 2px; } -p.browseopts a { +#maincontent p.browseopts a { padding: 1px 3px; - color: #fff!important; - border: 1px solid #258; + color: $maintext$; + border: 1px solid $border$; margin: 0 2px; } -p.browseopts a.optselected, p.browseopts a:hover { +#maincontent p.browseopts a.optselected, +#maincontent p.browseopts a:hover { border: 0; padding: 2px 4px; } @@ -401,7 +413,7 @@ div.mainbox.browse table td.tc1 { } table thead td { font-weight: bold; - background-color: #135; + background-color: $secbg$; } form.search { display: block; @@ -428,7 +440,7 @@ div.mainbox.history td.tc1_2 { div.mainbox.history td.tc2 { width: 65px; } div.mainbox.history td.tc3 { width: 90px } div.mainbox.history td.editsum { - color: #258; + color: $grayedout$; padding-top: 0; text-align: right; } @@ -448,12 +460,12 @@ div.thread table { width: 100%; } div.thread td { - border-bottom: 1px solid #258; + border-bottom: 1px solid $border$; } div.thread td.tc1 { width: 150px; padding: 5px 10px; - border-right: 1px solid #258; + border-right: 1px solid $border$; } div.thread td.tc2 { padding: 10px 20px 10px 10px; @@ -463,17 +475,17 @@ div.thread tr.deleted td { } div.thread i.deleted { font-style: normal; - color: #258; + color: $grayedout$; } div.thread i.lastmod { float: right; font-size: 7pt; - color: #258; + color: $grayedout$; margin: 0 -10px -5px 0; } div.thread i.edit { float: right; - color: #258; + color: $grayedout$; font-style: normal; margin: -10px -10px 0 0; } @@ -490,7 +502,7 @@ div.mainbox.discussions td.tags { padding-left: 60px; } div.mainbox.discussions td.tags a { - color: #258!important + color: $grayedout$!important } div.discussions td.tc2 { width: 50px; } div.discussions td.tc3 { width: 90px; } @@ -499,7 +511,7 @@ div.discussions td.tc4 { width: 170px; } font-family: "Futura", "Century New Gothic", "Arial", Serif; font-weight: bold; font-style: italic; - color: #258; + color: $grayedout$; font-size: 13pt; margin: 20px 0 -20px 0; } @@ -549,9 +561,10 @@ div.vndetails table dd { margin-left: 90px; } .catlvl_0, .catlvl_1, .catlvl_2, .catlvl_3 { font-style: normal; } -.catlvl_1 { color: #444!important } -.catlvl_2 { color: #777 } -.catlvl_3 { color: #fff } +.catlvl_1 { color: $catlevel1$!important } +.catlvl_2 { color: $catlevel2$!important } +.catlvl_3 { color: $catlevel3$!important } + div.vndetails td.relations dt { float: none; font-style: normal; @@ -570,7 +583,7 @@ div.vndescription { } div.vndescription h2 { margin: 0 30px!important; - border-top: 1px solid #258; + border-top: 1px solid $border$; padding: 3px 70px; font-weight: bold!important; } @@ -582,7 +595,7 @@ div.vndescription p { width: 100%; } .releases tr.lang td, #screenshots tr.rel td { - background: url(/f/boxbg.png) repeat; + background: url($_boxbg$) repeat; font-weight: bold; } .releases td.tc1 { @@ -599,10 +612,10 @@ div.vndescription p { width: 90px; } .releases td.tc5 { - text-align: right; + width: 140px; } .releases td.tc5 a { - color: #ddd!important; + color: $maintext$!important; border: 0; } .releases td.tc6 { @@ -625,8 +638,8 @@ a.addnew { } #screenshots td.scr div i { font-size: 7pt; } #screenshots td.scr img { border: 3px solid transparent; } -#screenshots td.scr div.nsfw img { border: 3px solid #C00; } -#screenshots td.scr a:hover img { border: 3px solid #258; } +#screenshots td.scr div.nsfw img { border: 3px solid $statnok$; } +#screenshots td.scr a:hover img { border: 3px solid $border$; } #screenshots td.scr a { border: none; } #screenshots div.hidden { display: none } #screenshots #nsfwshown { font-style: normal } @@ -635,6 +648,14 @@ a.addnew { margin: 0; } +#vldd { position: absolute; left: -500px; border: 1px solid $border$; background-color: $secbg$; width: 180px; } +#vldd ul { float: left; width: 90px; list-style-type: none; margin: 0; padding: 0 } +#vldd li b { display: block; font-weight: normal; padding-left: 5px; } +#vldd li i { display: block; font-style: normal; padding-left: 10px; } +#vldd li a { display: block; padding-left: 10px; color: $link$; border: 0; } +#vldd li a:hover { background: url($_boxbg$) repeat } +#vldd ul.full { width: 180px; text-align: center; } +#vldd ul.full li a { padding: 0 } @@ -645,7 +666,7 @@ a.addnew { .votegraph { float: left; margin-right: 20px } .votegraph td { padding: 0 2px; } .votegraph td.number { text-align: right } -.votegraph td div { float: left; height: 14px; background-color: #258; margin-right: 2px; } +.votegraph td div { float: left; height: 14px; background-color: $border$; margin-right: 2px; } .votestats thead td { background: transparent; text-align: center; padding: 2px; } .votestats tfoot td { text-align: right } @@ -680,6 +701,9 @@ a.addnew { list-style-type: none; padding: 0; } +#maincontent #jt_box_categories li li a { + color: $grayedout$; +} a.help { vertical-align: super; font-size: 6pt; @@ -688,7 +712,6 @@ a.help { padding-left: 1px; } #maincontent #jt_box_categories li li a { - color: #ccc; text-decoration: none; display: block; width: 160px; @@ -706,15 +729,16 @@ a.help { #jt_box_relations td.tc2 select { width: 130px; } #ds_box { - border: 1px solid #258; + border: 1px solid $border$; border-top: none; - background-color: #13273a; + background-color: $secbg$; + cursor: pointer; } #ds_box b { padding: 2px 0 0 10px; } #ds_box tr.selected { - background: url(/f/boxbg.png) repeat; + background: url($_boxbg$) repeat; } #ds_box table { width: 100%; @@ -754,7 +778,7 @@ a.help { width: 90%; padding: 0 30px 5px 30px; margin: 0 auto; - border-top: 1px solid #258; + border-top: 1px solid $border$; } #advoptions.hidden { display: none } #advoptions h2 { @@ -797,8 +821,8 @@ ul#catselect li li { list-style-type: none; background: url(/f/select.png) no-repeat; } -ul#catselect li li.inc { background-position: 0px -16px; color: #0c0; } -ul#catselect li li.exc { background-position: 0px -33px; color: #c00; } +ul#catselect li li.inc { background-position: 0px -16px; color: $statok$; } +ul#catselect li li.exc { background-position: 0px -33px; color: $statnok$; } @@ -866,7 +890,7 @@ ul#catselect li li.exc { background-position: 0px -33px; color: #c00; } #jt_box_visual_novels div, #jt_box_producers div { padding-left: 20px } #jt_box_visual_novels input, #jt_box_producers input { margin-right: 10px; width: 300px } #jt_box_visual_novels span, #jt_box_producers span { clear: left; display: block; padding: 2px; } -#jt_box_visual_novels span.odd, #jt_box_producers span.odd { background: url(/f/boxbg.png) repeat; } +#jt_box_visual_novels span.odd, #jt_box_producers span.odd { background: url($_boxbg$) repeat; } #jt_box_visual_novels i, #jt_box_producers i { font-style: normal; display: block; float: left; width: 310px } #jt_box_visual_novels b, #jt_box_producers b { font-weight: normal; } @@ -879,11 +903,11 @@ ul#catselect li li.exc { background-position: 0px -33px; color: #c00; } .docs h3 { margin-top: 25px; } .docs dd { padding-bottom: 5px; margin-left: 120px; } .docs dt { float: left } -.docs ul.index { display: block; float: right; width: 150px; padding: 2px; margin: 0 0 10px 5px; background: url(/f/boxbg.png) repeat; border: 1px solid #258; } +.docs ul.index { display: block; float: right; width: 150px; padding: 2px; margin: 0 0 10px 5px; background: url($_boxbg$) repeat; border: 1px solid $border$; } .docs ul.index li { list-style-type: none; } .docs ul.index li a { margin: 0 0 0 10px; } .docs .retired { text-decoration: line-through; } -.docs dt b { color: #258; font-weight: normal; font-style: normal; font-size: 12px; } +.docs dt b { color: $grayedout$; font-weight: normal; font-style: normal; font-size: 12px; } @@ -930,12 +954,12 @@ ul#catselect li li.exc { background-position: 0px -33px; color: #c00; } div.warning, div.notice { margin: 5px 10%; padding: 15px; - background-color: #534; - border: 1px solid #C00; + background-color: $warnbg$; + border: 1px solid $warnborder$; } div.notice { - background-color: #354; - border: 1px solid #0C0; + background-color: $noticebg$; + border: 1px solid $noticeborder$; } div.warning ul, div.notice ul { margin-left: 0; @@ -958,11 +982,10 @@ div.revision { padding-bottom: 10px!important; } div.revision div, div.revision table { - border: 1px solid #258; + border: 1px solid $border$; margin: 0 auto; width: 90%; - /*background: url(/f/boxbg.png) repeat;*/ - background-color: #13273a; + background-color: $secbg$; clear: both; } div.revision table thead tr td { @@ -971,7 +994,7 @@ div.revision table thead tr td { font-weight: normal; } div.revision table td { - border-right: 1px solid #258; + border-right: 1px solid $border$; padding: 5px; } div.revision td.tcval { @@ -983,11 +1006,11 @@ div.revision div { } .diff_add { font-weight: normal; - background-color: #354; + background-color: $diffadd$; } .diff_del { font-weight: normal; - background-color: #534; + background-color: $diffdel$; } div.revision .next { float: right; @@ -1010,8 +1033,8 @@ div#iv_view { display: none; top: -5000px; left: -5000px; - background: url(/f/boxbg.png) repeat; - border: 1px solid #258; + background: url($_boxbg$) repeat; + border: 1px solid $border$; padding: 5px; text-align: center; } @@ -1028,7 +1051,7 @@ div#iv_view { top: -50px; width: 100px; padding: 3px; - background-color: #f5f5f5; + background-color: #f5f5f5; /* no real need to skin this */ text-align: center; border: 1px solid #ccc; color: #000; diff --git a/lib/ChangeLog b/lib/ChangeLog index 5eb214ff..92532247 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,10 +1,12 @@ -TODO: - + The current implementation of the hidden flag *SUCKS*, seriously needs - some revising: - + Add a 'change' when hiding/unhiding an item - + Remove all references to an item when it's hidden - (preferably with the option to re-add them when unhiding) - + Add a link for the hidden 'h' option at /hist +2.1 - 2008-12-29 + - Skin support + - 'show all items' tab to large forms + - Allow items to be selected using the mouse on the dropdown search + - [spoiler] tag produces mouseover-style spoilers instead of ROT13 + - Fixed tiny timezone-related bug + - Re-added release list dropdown on VN pages + - Added [quote] tag to bb2html + - fixed URL parser in bb2html 2.0 - 2008-12-20 - New layout diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm index b2c78d5e..6af2f736 100644 --- a/lib/VNDB/DB/Releases.pm +++ b/lib/VNDB/DB/Releases.pm @@ -3,6 +3,7 @@ package VNDB::DB::Releases; use strict; use warnings; +use POSIX 'strftime'; use Exporter 'import'; our @EXPORT = qw|dbReleaseGet dbReleaseAdd dbReleaseEdit|; @@ -27,7 +28,7 @@ sub dbReleaseGet { $o{vid} ? ( 'rv.vid = ?' => $o{vid} ) : (), defined $o{unreleased} ? ( - q|rr.released !s TO_CHAR('today'::timestamp, 'YYYYMMDD')::integer| => $o{unreleased} ? '>' : '<=' ) : (), + q|rr.released !s ?| => [ $o{unreleased} ? '>' : '<=', strftime('%Y%m%d', gmtime) ] ) : (), ); my @join = ( diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm index 501bb8bb..6f1a13c6 100644 --- a/lib/VNDB/DB/Users.pm +++ b/lib/VNDB/DB/Users.pm @@ -64,7 +64,7 @@ sub dbUserEdit { my %h; defined $o{$_} && ($h{$_.' = ?'} = $o{$_}) - for (qw| username mail rank show_nsfw show_list |); + for (qw| username mail rank show_nsfw show_list skin |); $h{'passwd = decode(?, \'hex\')'} = $o{passwd} if defined $o{passwd}; diff --git a/lib/VNDB/Func.pm b/lib/VNDB/Func.pm index 9abacad0..330dad7d 100644 --- a/lib/VNDB/Func.pm +++ b/lib/VNDB/Func.pm @@ -86,6 +86,7 @@ sub userstr { # [url=..] [/url] # [raw] .. [/raw] # [spoiler] .. [/spoiler] +# [quote] .. [/quote] # v+, v+.+ # http://../ sub bb2html { @@ -94,11 +95,10 @@ sub bb2html { $raw =~ s/\r//g; return '' if !$raw && $raw ne "0"; - my($result, $length, @open) = ('', 0, 'first'); + my($result, $length, $rmnewline, @open) = ('', 0, 0, 'first'); my $e = sub { local $_ = shift; - tr/A-Za-z/N-ZA-Mn-za-m/ if !@_ && grep /spoiler/, @open; s/&/&/g; s/>/>/g; s/</</g; @@ -109,13 +109,34 @@ sub bb2html { for (split /(\s|\n|\[[^\]]+\])/, $raw) { next if !defined $_; + next if $_ eq ''; + + $rmnewline = s/\n//g if $rmnewline; + next if $_ eq ''; my $lit = $_; if($open[$#open] ne 'raw') { if ($_ eq '[raw]') { push @open, 'raw'; next } - elsif ($_ eq '[spoiler]') { push @open, 'spoiler'; next } - elsif ($_ eq '[/spoiler]') { pop @open if $open[$#open] eq 'spoiler'; next } - elsif ($_ eq '[/url]') { + elsif ($_ eq '[spoiler]') { push @open, 'spoiler'; $result .= '<b class="spoiler">'; next } + elsif ($_ eq '[quote]') { + push @open, 'quote'; + $result .= '<div class="quote">' if !$maxlength; + $rmnewline++; + next + } elsif ($_ eq '[/spoiler]') { + if($open[$#open] eq 'spoiler') { + $result .= '</b>'; + pop @open; + } + next; + } elsif ($_ eq '[/quote]') { + if($open[$#open] eq 'quote') { + $result .= '</div>' if !$maxlength; + $rmnewline++; + pop @open; + } + next; + } elsif($_ eq '[/url]') { if($open[$#open] eq 'url') { $result .= '</a>'; pop @open; @@ -126,7 +147,7 @@ sub bb2html { push @open, 'url'; next; } elsif(!grep(/url/, @open) && - s{(.*)(http|https)://(.+[0-9a-zA-Z=/])(.*)} + s{(.*)(http|https)://(.+[\d\w=/-])(.*)} {$e->($1).qq|<a href="$2://|.$e->($3, 1).'" rel="nofollow">'.$e->('link').'</a>'.$e->($4)}e) { $length += 4; last if $maxlength && $length > $maxlength; @@ -151,7 +172,7 @@ sub bb2html { $result .= $e->($_); } - $result .= '</a>' + $result .= $_ eq 'url' ? '</a>' : $_ eq 'quote' ? '</div>' : '</b>' while((local $_ = pop @open) ne 'first'); $result .= '...' if $maxlength && $length > $maxlength; diff --git a/lib/VNDB/Handler/Discussions.pm b/lib/VNDB/Handler/Discussions.pm index 286e3ee5..06693590 100644 --- a/lib/VNDB/Handler/Discussions.pm +++ b/lib/VNDB/Handler/Discussions.pm @@ -54,7 +54,7 @@ sub thread { table; for my $i (0..$#$p) { local $_ = $p->[$i]; - my $class = $i % 2 == 0 ? 'odd ' : ''; + my $class = $i % 2 ? 'odd ' : ''; $class .= 'deleted' if $_->{hidden}; Tr class => $class; td class => 'tc1'; diff --git a/lib/VNDB/Handler/ULists.pm b/lib/VNDB/Handler/ULists.pm index 5353dbc9..f86cba83 100644 --- a/lib/VNDB/Handler/ULists.pm +++ b/lib/VNDB/Handler/ULists.pm @@ -70,7 +70,8 @@ sub rlist { $f->{e} =~ /^([rv])(\d+)$/ && $1 eq 'r' ? (rstat => $2) : (vstat => $2) ) if $f->{e} ne 'del'; - $self->resRedirect('/r'.$id, 'temp'); + (my $ref = $self->reqHeader('Referer')||"/r$id") =~ s/^\Q$self->{url}//; + $self->resRedirect($ref, 'temp'); } diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm index daba59e6..f2bb0fa6 100644 --- a/lib/VNDB/Handler/Users.pm +++ b/lib/VNDB/Handler/Users.pm @@ -299,6 +299,7 @@ sub edit { { name => 'mail', template => 'mail' }, { name => 'usrpass', required => 0, minlength => 4, maxlength => 64, template => 'asciiprint' }, { name => 'usrpass2', required => 0, minlength => 4, maxlength => 64, template => 'asciiprint' }, + { name => 'skin', enum => [ '', keys %{$self->{skins}} ], required => 0, default => '' }, { name => 'flags_list', required => 0, default => 0 }, { name => 'flags_nsfw', required => 0, default => 0 }, ); @@ -308,6 +309,7 @@ sub edit { $o{username} = $frm->{usrname} if $frm->{usrname}; $o{rank} = $frm->{rank} if $frm->{rank}; $o{mail} = $frm->{mail}; + $o{skin} = $frm->{skin}; $o{passwd} = md5_hex($frm->{usrpass}) if $frm->{usrpass}; $o{show_list} = $frm->{flags_list} ? 1 : 0; $o{show_nsfw} = $frm->{flags_nsfw} ? 1 : 0; @@ -321,6 +323,7 @@ sub edit { $frm->{usrname} ||= $u->{username}; $frm->{rank} ||= $u->{rank}; $frm->{mail} ||= $u->{mail}; + $frm->{skin} ||= $u->{skin}; $frm->{flags_list} = $u->{show_list} if !defined $frm->{flags_list}; $frm->{flags_nsfw} = $u->{show_nsfw} if !defined $frm->{flags_nsfw}; @@ -341,7 +344,7 @@ sub edit { $self->authCan('usermod') ? ( [ input => short => 'usrname', name => 'Username' ], [ select => short => 'rank', name => 'Rank', options => [ - map [ $_, $self->{user_ranks}[$_][0] ], 1..$#{$self->{user_ranks}} ] ], + map [ $_, $self->{user_ranks}[$_][0] ], 1..$#{$self->{user_ranks}} ] ], ) : ( [ static => label => 'Username', content => $frm->{usrname} ], ), @@ -353,6 +356,8 @@ sub edit { [ passwd => short => 'usrpass2', name => 'Confirm pass.' ], [ part => title => 'Options' ], + [ select => short => 'skin', name => 'Prefered skin', options => [ + map [ $_ eq $self->{skin_default} ? '' : $_, $self->{skins}{$_} ], sort { $self->{skins}{$a} cmp $self->{skins}{$b} } keys %{$self->{skins}} ] ], [ check => short => 'flags_list', name => qq|Allow other people to see my visual novel list (<a href="/u$uid/list">/u$uid/list</a>) |. qq|and wishlist (<a href="/u$uid/wish">/u$uid/wish</a>)| ], diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm index 35a9ac8c..b53b60ee 100644 --- a/lib/VNDB/Handler/VNPage.pm +++ b/lib/VNDB/Handler/VNPage.pm @@ -397,9 +397,9 @@ sub _releases { a href => "/r$rel->{id}", title => $rel->{original}||$rel->{title}, $rel->{title}; end; td class => 'tc5'; - if($rel->{ulist}) { - a href => "/r$rel->{id}"; - lit liststat $rel->{ulist}; + if($self->authInfo->{id}) { + a href => "/r$rel->{id}", id => "rlsel_$rel->{id}"; + lit $rel->{ulist} ? liststat $rel->{ulist} : '--'; end; } else { txt ' '; diff --git a/lib/VNDB/Util/FormHTML.pm b/lib/VNDB/Util/FormHTML.pm index 80b8c4f3..ff23682d 100644 --- a/lib/VNDB/Util/FormHTML.pm +++ b/lib/VNDB/Util/FormHTML.pm @@ -216,6 +216,9 @@ sub htmlForm { a href => "#$short", id => "jt_sel_$short", $subs[$_*2]; end; } + li class => 'left'; + a href => '#all', id => 'jt_sel_all', 'All items'; + end; end; } diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm index e5ced8f6..30a10ce3 100644 --- a/lib/VNDB/Util/LayoutHTML.pm +++ b/lib/VNDB/Util/LayoutHTML.pm @@ -12,13 +12,15 @@ our @EXPORT = qw|htmlHeader htmlFooter|; sub htmlHeader { # %options->{ title, js, noindex, search } my($self, %o) = @_; + my $skin = $self->authInfo->{skin} || $self->{skin_default}; + $skin = $self->{skin_default} if !-d "$VNDB::ROOT/static/s/$skin"; # 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'; + Link rel => 'stylesheet', href => $self->{url_static}.'/s/'.$skin.'/style.css', type => 'text/css', media => 'all'; if($o{js}) { script type => 'text/javascript', src => $self->{url_static}.'/f/forms.js'; end; } diff --git a/static/f/boxbg.png b/static/f/boxbg.png Binary files differdeleted file mode 100644 index 43b546b5..00000000 --- a/static/f/boxbg.png +++ /dev/null diff --git a/static/f/forms.js b/static/f/forms.js index 2408dd2e..cd65058d 100644 --- a/static/f/forms.js +++ b/static/f/forms.js @@ -75,12 +75,7 @@ function catLoad() { } function catSet(id, rnk) { - // doesn't work very nice with skins... - var c = rnk == 0 ? '' : - rnk == 1 ? '#0c0' : - rnk == 2 ? '#cc0' : '#c00'; - x('b_'+id).style.color = c; - x('cat_'+id).style.color = c; + x('cat_'+id).className = 'catlvl_'+rnk; x('b_'+id).innerHTML = rnk; } @@ -97,8 +92,11 @@ function catSet(id, rnk) { function dsInit(obj, url, trfunc, serfunc, retfunc) { obj.onkeydown = dsKeyDown; obj.onblur = function() { - if(x('ds_box')) - x('ds_box').style.top = '-500px'; + // timeout to make sure the tr.onclick event is called before we've hidden the object + setTimeout(function () { + if(x('ds_box')) + x('ds_box').style.top = '-500px'; + }, 500) }; // all local data is stored in the DOM input object obj.returnFunc = retfunc; @@ -228,6 +226,18 @@ function dsResults(hr, obj) { tr.itemData = l[i]; if(obj.selectedId == id) tr.setAttribute('class', 'selected'); + tr.onmouseover = function() { + obj.selectedId = this.id.substr(7); + var l = x('ds_box').getElementsByTagName('tr'); + for(var i=0;i<l.length;i++) + l[i].className = l[i].id == 'ds_box_'+obj.selectedId ? 'selected' : ''; + }; + tr.onclick = function() { + obj.value = obj.serFunc(this.itemData); + if(x('ds_box')) + x('ds_box').style.top = '-500px'; + obj.selectedId = 0; + }; obj.trFunc(l[i], tr); tb.appendChild(tr); } diff --git a/static/f/script.js b/static/f/script.js index 8ff2fe7f..68d40f75 100644 --- a/static/f/script.js +++ b/static/f/script.js @@ -15,6 +15,10 @@ clearInterval(t);f()}},10);window.onload=f;} + + +/* A D V A N C E D S E A R C H */ + function searchInit() { cl('advselect', function() { var e = x('advoptions'); @@ -184,6 +188,66 @@ function ivClose() { + +/* V N L I S T D R O P D O W N */ + +var rstat = [ 'Unknown', 'Pending', 'Obtained', 'On loan', 'Deleted' ]; +var vstat = [ 'Unknown', 'Playing', 'Finished', 'Stalled', 'Dropped' ]; +function vlDropDown(e) { + e = e || window.event; + var tg = e.target || e.srcElement; + while(tg && (tg.nodeType == 3 || tg.nodeName.toLowerCase() != 'a')) + tg = tg.parentNode; + + var o = x('vldd'); + if(!o && (!tg || tg.id.substr(0,6) != 'rlsel_')) + return; + + if(o) { + var mouseX = e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft); + var mouseY = e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop); + if((mouseX < ddx-5 || mouseX > ddx+o.offsetWidth+100 || mouseY < ddy-5 || mouseY > ddy+o.offsetHeight+5) + || (tg && tg.id.substr(0,6) == 'rlsel_' && tg.id != 'rlsel_'+o.relId)) { + document.body.removeChild(o); + o = null; + } + } + if(!o && tg) { + o = tg; + ddx = ddy = 0; + do { + ddx += o.offsetLeft; + ddy += o.offsetTop; + } while(o = o.offsetParent); + ddx -= 185; + + var cu = '/r'+tg.id.substr(6)+'/list?e='; + var st = tg.innerHTML.split(' / '); + var r = '<ul><li><b>Release status</b></li>'; + for(var i=0;i<rstat.length;i++) + r += st[0] && st[0].indexOf(rstat[i]) >= 0 ? '<li><i>'+rstat[i]+'</i></li>' : '<li><a href="'+cu+'r'+i+'">'+rstat[i]+'</a></li>'; + r += '</ul><ul><li><b>Play status</b></li>'; + for(var i=0;i<vstat.length;i++) + r += st[1] && st[1].indexOf(vstat[i]) >= 0 ? '<li><i>'+vstat[i]+'</i></li>' : '<li><a href="'+cu+'v'+i+'">'+vstat[i]+'</a></li>'; + r += '</ul>'; + if(tg.innerHTML != '--') + r += '<ul class="full"><li><a href="'+cu+'del">Remove from VN list</a></li></ul>'; + + o = document.createElement('div'); + o.id = 'vldd'; + o.relId = tg.id.substr(6); + o.style.left = ddx+'px'; + o.style.top = ddy+'px'; + o.innerHTML = r; + document.body.appendChild(o); + } +} + + + + + + /* J A V A S C R I P T T A B S */ function jtInit() { @@ -213,7 +277,8 @@ function jtSel(which, nolink) { for(var i=0;i<l.length;i++) if(l[i].id.substr(0,7) == 'jt_sel_') { var name = l[i].id.substr(7); - x('jt_box_'+name).style.display = name == which ? 'block' : 'none'; + if(name != 'all') + x('jt_box_'+name).style.display = name == which || which == 'all' ? 'block' : 'none'; var o = x('jt_sel_'+name).parentNode; if(o.className.indexOf('tabselected') >= 0) { if(name != which) @@ -370,10 +435,26 @@ DOMLoad(function() { // initialize image viewer ivInit(); + // vnlist dropdown + var l = document.getElementsByTagName('a'); + for(var i=0;i<l.length;i++) + if(l[i].id.substr(0,6) == 'rlsel_') { + document.onmousemove = vlDropDown; + break; + } + // Javascript tabs if(x('jt_select')) jtInit(); + // spoiler tags + l = document.getElementsByTagName('b'); + for(i=0;i<l.length;i++) + if(l[i].className == 'spoiler') { + l[i].onmouseover = function() { this.className = 'spoiler_shown' }; + l[i].onmouseout = function() { this.className = 'spoiler' }; + } + // forms.js if(x('categories')) catLoad(); diff --git a/static/f/bg.jpg b/static/s/angel/bg.jpg Binary files differindex f91e414e..f91e414e 100644 --- a/static/f/bg.jpg +++ b/static/s/angel/bg.jpg diff --git a/static/f/bgright.jpg b/static/s/angel/bgright.jpg Binary files differindex e1dff0cd..e1dff0cd 100644 --- a/static/f/bgright.jpg +++ b/static/s/angel/bgright.jpg diff --git a/static/s/angel/conf b/static/s/angel/conf new file mode 100644 index 00000000..72df41b0 --- /dev/null +++ b/static/s/angel/conf @@ -0,0 +1,39 @@ +name Angelic Serenade (dark blue) + +// text +maintext #ddd // primary text color (also used for the menu links) +grayedout #258 // color used for grayed-out/non-important things +standout #e44 // color of 'stand-out' text +link #7bd // primary link color (not used for the menu links) +statok #0c0 // generic 'ok' text color (used for vnlist statuses & category browser) +statnok #c00 // generoc 'not ok' text color (used for above, and as border for NSFW screenshots) +footer #135 // text color of the footer + +// titles +maintitle #135 // text color of the site title +boxtitle #258 // box titles +alttitle #fff // alternative title + +// bg colors +bodybg #000 // main background color +tabbg #012 // background color of inactive tabs +secbg #0d2741 // secondary background color (used on input fields and table headers) +secborder #35A // secondary border color (used on input fields) +border #258 // primary border color +boxbg #112233BC // RGBA, background color of the boxes, stacked for menu box titles and odd row numers + +// images (0 = no image) +imglefttop bg.jpg +imgrighttop bgright.jpg + +// misc colors +catlevel1 #444 // category levels +catlevel2 #777 +catlevel3 #fff +diffadd #354 // background color of changes in the diff viewer +diffdel #534 +warnbg #534 // background color of a warning box +warnborder #c00 // ..border +noticebg #354 // notice box +noticeborder #0c0 // ...and border + diff --git a/static/s/grey/bg.jpg b/static/s/grey/bg.jpg Binary files differnew file mode 100644 index 00000000..8d61ac55 --- /dev/null +++ b/static/s/grey/bg.jpg diff --git a/static/s/grey/bgright.jpg b/static/s/grey/bgright.jpg Binary files differnew file mode 100644 index 00000000..c52499ea --- /dev/null +++ b/static/s/grey/bgright.jpg diff --git a/static/s/grey/conf b/static/s/grey/conf new file mode 100644 index 00000000..10675446 --- /dev/null +++ b/static/s/grey/conf @@ -0,0 +1,39 @@ +name Touhou (grey) + +// text +maintext #222 +grayedout #666 +standout #500 +link #005 +statok #050 +statnok #500 +footer #bbb + +// titles +maintitle #ccc +boxtitle #444 +alttitle #000 + +// bg colors +bodybg #fff +tabbg #ddd +secbg #bbb +secborder #000 +border #666 +boxbg #dddddddd + +// images (0 = no image) +imglefttop bg.jpg +imgrighttop bgright.jpg + +// misc colors +catlevel1 #555 +catlevel2 #333 +catlevel3 #000 +diffadd #cfc +diffdel #fcc +warnbg #fcc +warnborder #c00 +noticebg #cfc +noticeborder #0c0 + diff --git a/static/s/lb/bg.jpg b/static/s/lb/bg.jpg Binary files differnew file mode 100644 index 00000000..94a7874d --- /dev/null +++ b/static/s/lb/bg.jpg diff --git a/static/s/lb/conf b/static/s/lb/conf new file mode 100644 index 00000000..7cdcd16a --- /dev/null +++ b/static/s/lb/conf @@ -0,0 +1,39 @@ +name Little Busters! (pink) + +// text +maintext #408 +grayedout #670159 +standout #e44 +link #a2d +statok #0c0 +statnok #c00 +footer #f78de7 + +// titles +maintitle #f78de7 +boxtitle #670159 +alttitle #5328a7 + +// bg colors +bodybg #fff +tabbg #f78de7 +secbg #f78de7 +secborder #670159 +border #f76ee2 +boxbg #f7b6edcc + +// images (0 = no image) +imglefttop bg.jpg +imgrighttop 0 + +// misc colors +catlevel1 #444 +catlevel2 #d28 +catlevel3 #999 +diffadd #cfc +diffdel #fcc +warnbg #fff +warnborder #c00 +noticebg #f7b6ed +noticeborder #670159 + diff --git a/util/dump.sql b/util/dump.sql index 848ddd34..719f0f75 100644 --- a/util/dump.sql +++ b/util/dump.sql @@ -184,7 +184,8 @@ CREATE TABLE users ( show_nsfw boolean NOT NULL DEFAULT FALSE, show_list boolean NOT NULL DEFAULT TRUE, c_votes integer NOT NULL DEFAULT 0, - c_changes integer NOT NULL DEFAULT 0 + c_changes integer NOT NULL DEFAULT 0, + skin varchar(128) NOT NULL DEFAULT '' ); -- vn diff --git a/util/skingen.pl b/util/skingen.pl new file mode 100755 index 00000000..9c06b077 --- /dev/null +++ b/util/skingen.pl @@ -0,0 +1,93 @@ +#!/usr/bin/perl + +package VNDB; + +use strict; +use warnings; +use Cwd 'abs_path'; +use Data::Dumper 'Dumper'; +use Image::Magick; + + +our($ROOT, %O); +BEGIN { ($ROOT = abs_path $0) =~ s{/util/skingen\.pl$}{}; } +require $ROOT.'/data/global.pl'; + + +if(@ARGV) { + writeskin(readskin($_)) for (@ARGV); +} else { + /([^\/]+)$/ && writeskin(readskin($1)) for (glob($ROOT.'/static/s/*')); +} + + +sub readskin { # skin name + my $name = shift; + my %o; + open my $F, '<', $ROOT.'/static/s/'.$name.'/conf' or die $!; + while(<$F>) { + chomp; + s{[\t\s]*//.+$}{}; + next if !/^([a-z0-9]+)[\t\s]+(.+)$/; + $o{$1} = $2; + } + close $F; + $o{_name} = $name; + return \%o; +} + + +sub writeskin { # $obj + my $o = shift; + + # fix image locations + $o->{$_} && ($o->{$_} = '/s/'.$o->{_name}.'/'.$o->{$_}) for (qw|imglefttop imgrighttop|); + + # get the right top image + if($o->{imgrighttop}) { + my $img = Image::Magick->new; + $img->Read($ROOT.'/static'.$o->{imgrighttop}); + $o->{_bgright} = sprintf 'background: url(%s) no-repeat; width: %dpx; height: %dpx', + $o->{imgrighttop}, $img->Get('width'), $img->Get('height'); + } else { + $o->{_bgright} = 'display: none'; + } + + # body background + if(!$o->{imglefttop}) { + $o->{_bodybg} = "background-color: $o->{bodybg}"; + } else { + $o->{_bodybg} = "background: $o->{bodybg} url($o->{imglefttop}) no-repeat"; + } + + + # create boxbg.png + my $img = Image::Magick->new(size => '1x1'); + $img->Read('xc:'.$o->{boxbg}); + $img->Write(filename => $ROOT.'/static/s/'.$o->{_name}.'/boxbg.png'); + $o->{_boxbg} = '/s/'.$o->{_name}.'/boxbg.png'; + + # get the blend color + $img = Image::Magick->new(size => '1x1'); + $img->Read('xc:'.$o->{bodybg}, 'xc:'.$o->{boxbg}); + $img = $img->Flatten(); + $o->{_blendbg} = '#'.join '', map sprintf('%02x', $_*255), $img->GetPixel(x=>1,y=>1); + + # write the CSS + open my $CSS, '<', "$ROOT/data/skingen/style.css" or die $!; + open my $SKIN, '>', "$ROOT/static/s/$o->{_name}/style.css" or die $!; + while((my $d = <$CSS>)) { + if($O{debug}) { + chomp $d; + $d =~ s/^\s*/ /; + $d =~ s{/\*.+\*/}{}; # NOTE: multiline comments or multiple comments per line won't work + next if $d !~ /[^\s\t]/; + } + $d =~ s/\$$_\$/$o->{$_}/g for (keys %$o); + print $SKIN $d; + } + close $SKIN; + close $CSS; +} + + diff --git a/util/updates/update_2.1.sql b/util/updates/update_2.1.sql new file mode 100644 index 00000000..7ae8fecf --- /dev/null +++ b/util/updates/update_2.1.sql @@ -0,0 +1,4 @@ + +-- skin selector +ALTER TABLE users ADD COLUMN skin varchar(128) NOT NULL DEFAULT ''; + diff --git a/util/vndb.pl b/util/vndb.pl index 5ef0bf40..a9ac36b4 100755 --- a/util/vndb.pl +++ b/util/vndb.pl @@ -22,6 +22,11 @@ use YAWF ':html'; our(%O, %S); +# load and (if required) regenerate the skins +# NOTE: $S{skins} can be modified in data/config.pl, allowing deletion of skins or forcing only one skin +$S{skins} = readskins(); + + # load settings from global.pl require $ROOT.'/data/global.pl'; @@ -73,3 +78,28 @@ sub handle404 { } +sub readskins { + my %skins; # dirname => skin name + my $regen = 0; + my $lasttemplate = [stat "$ROOT/data/skingen/style.css"]->[9]; + for my $f (glob "$ROOT/static/s/*") { + my $n = $1 if $f =~ m{([^/]+)$}; + open my $F, '<', "$f/conf" or die $!; + while(<$F>) { + chomp; + s{[\t\s]*//.*$}{}; + next if !/^name[\t\s]+(.+)$/; + $skins{$n} = $1; + last; + } + close $F; + + my $lastgen = [stat "$f/style.css"]->[9]; + $regen = 1 if (!-f "$f/style.css" && -x $f) + || ([stat "$f/conf"]->[9] > $lastgen || $lasttemplate > $lastgen) && -w "$f/style.css"; + } + # note: this only works if the current process has write access to the skins + `$ROOT/util/skingen.pl` if $regen; + return \%skins; +} + |