diff options
author | yorhel <yorhel@1fe2e327-d9db-4752-bcf7-ef0cb4a1748b> | 2008-07-08 07:27:56 +0000 |
---|---|---|
committer | yorhel <yorhel@1fe2e327-d9db-4752-bcf7-ef0cb4a1748b> | 2008-07-08 07:27:56 +0000 |
commit | db47e11b580efa1938de892e6c08ba47ba2c6cc4 (patch) | |
tree | 3d2133912d93585e7bf36b41a6b1f85c40a0031a | |
parent | 4c821b8138b0f9820cada6be7e051fe00434c54e (diff) |
Wrote an integrated messageboard, and fixed a few (very) small things along the way
git-svn-id: svn://vndb.org/vndb@54 1fe2e327-d9db-4752-bcf7-ef0cb4a1748b
-rw-r--r-- | data/tpl/defs.pl | 70 | ||||
-rw-r--r-- | data/tpl/faq | 75 | ||||
-rw-r--r-- | data/tpl/hist | 2 | ||||
-rw-r--r-- | data/tpl/home | 11 | ||||
-rw-r--r-- | data/tpl/main | 8 | ||||
-rw-r--r-- | data/tpl/myvotes | 1 | ||||
-rw-r--r-- | data/tpl/pedit | 5 | ||||
-rw-r--r-- | data/tpl/ppage | 3 | ||||
-rw-r--r-- | data/tpl/redit | 5 | ||||
-rw-r--r-- | data/tpl/rpage | 3 | ||||
-rw-r--r-- | data/tpl/tedit | 24 | ||||
-rw-r--r-- | data/tpl/tindex | 54 | ||||
-rw-r--r-- | data/tpl/ttag | 57 | ||||
-rw-r--r-- | data/tpl/tthread | 81 | ||||
-rw-r--r-- | data/tpl/useredit | 1 | ||||
-rw-r--r-- | data/tpl/userpage | 1 | ||||
-rw-r--r-- | data/tpl/vnedit | 5 | ||||
-rw-r--r-- | data/tpl/vnlist | 1 | ||||
-rw-r--r-- | data/tpl/vnpage | 18 | ||||
-rw-r--r-- | lib/ChangeLog | 5 | ||||
-rw-r--r-- | lib/Multi/IRC.pm | 35 | ||||
-rw-r--r-- | lib/VNDB.pm | 19 | ||||
-rw-r--r-- | lib/VNDB/Discussions.pm | 185 | ||||
-rw-r--r-- | lib/VNDB/HomePages.pm | 10 | ||||
-rw-r--r-- | lib/VNDB/Users.pm | 1 | ||||
-rw-r--r-- | lib/VNDB/Util/DB.pm | 201 | ||||
-rw-r--r-- | lib/VNDB/VN.pm | 2 | ||||
-rw-r--r-- | lib/global.pl | 22 | ||||
-rw-r--r-- | static/files/style.css | 21 | ||||
-rw-r--r-- | util/updates/update_1.19.sql | 28 |
30 files changed, 814 insertions, 140 deletions
diff --git a/data/tpl/defs.pl b/data/tpl/defs.pl index fcc1c443..46269347 100644 --- a/data/tpl/defs.pl +++ b/data/tpl/defs.pl @@ -59,6 +59,17 @@ sub wraplong { # text, margin s/([^\s\r\n]{$m})([^\s\r\n])/$1 $2/g; return $_; } +sub age { + my $a = time-$_[0]; + return sprintf '%d %s', + $a > 60*60*24*365*2 ? ( $a/60/60/24/365, 'years ago' ) : + $a > 60*60*24*(365/12)*2 ? ( $a/60/60/24/(365/12), 'months ago' ) : + $a > 60*60*24*7*2 ? ( $a/60/60/24/7, 'weeks ago' ) : + $a > 60*60*24*2 ? ( $a/60/60/24, 'days ago' ) : + $a > 60*60*2 ? ( $a/60/60, 'hours ago' ) : + $a > 60*2 ? ( $a/60, 'min ago' ) : + ( $a, 'sec ago' ) ; +} sub wordsplit { # split a string into an array of words, but make sure to not split HTML tags @@ -134,6 +145,7 @@ sub summary { # cmd, len, def my $res = ''; my $len = 0; my $as = 0; + my $raw = 0; (my $txt = $_[0]) =~ s/\r?\n/\n /g; for (split / /, $txt) { next if !defined $_ || $_ eq ''; @@ -141,19 +153,31 @@ sub summary { # cmd, len, def s/\&/&/g; s/>/>/g; s/</</g; - while(s/\[url=((https?:\/\/|\/)[^\]>]+)\]/<a href="$1" rel="nofollow">/i) { - $l -= length($1)+6; - $as++; + if(!$raw && s/^\[raw\]//) { + $l -= 5; + $raw++; } - if(!$as && s/(http|https):\/\/(.+[0-9a-zA-Z=\/])/<a href="$1:\/\/$2" rel="nofollow">link<\/a>/) { - $l = 4; - } elsif(!$as) { - s/^(.*[^\w]|)([dvpr][0-9]+)\.([0-9]+)([^\w].*|)$/$1<a href="\/$2.$3">$2.$3<\/a>$4/ || - s/^(.*[^\w]|)([duvpr][0-9]+)([^\w].*|)$/$1<a href="\/$2">$2<\/a>$3/; + if(!$raw) { + $l -= 9 while(s/\[spoiler\]/<b class="spoiler">/i); + $l -= 10 while(s/\[\/spoiler\]/<\/b>/i); + while(s/\[url=((https?:\/\/|\/)[^\]>]+)\]/<a href="$1" rel="nofollow">/i) { + $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/^(.*[^\w]|)([tdvpr][0-9]+)\.([0-9]+)([^\w].*|)$/$1<a href="\/$2.$3">$2.$3<\/a>$4/ || + s/^(.*[^\w]|)([tduvpr][0-9]+)([^\w].*|)$/$1<a href="\/$2">$2<\/a>$3/; + } + while(s/\[\/url\]/<\/a>/i) { + $l -= 6; + $as--; + } } - while(s/\[\/url\]/<\/a>/i) { + if(s/\[\/raw\]//) { $l -= 6; - $as--; + $raw=0; } $len += $l + 1; last if $_[1] && $len > $_[1]; @@ -168,21 +192,29 @@ sub summary { # cmd, len, def } -sub ttabs { # [vrp], obj, sel +sub ttabs { # [vrpu], 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} ? + $p{Authlock} && $t ne 'u' ? sprintf('<a href="/%%s/lock">%s</a>', $$o{locked} ? 'unlock' : 'lock') : (), - $p{Authdel} ? ( + $p{Authdel} && $t ne 'u' ? ( sprintf('<a href="/%%s/hide"%s>%s</a>', $t eq 'v' ? ' id="vhide"' : '', $$o{hidden} ? 'unhide' : 'hide') ) : (), - (!$$o{locked} && !$$o{hidden}) || ($p{Authedit} && $p{Authlock}) ? + ($t eq 'u' && $p{Authuseredit}) || ($t ne 'u' && (!$$o{locked} && !$$o{hidden}) || ($p{Authedit} && $p{Authlock})) ? ($s eq 'edit' ? 'edit' : '<a href="'.($p{Authedit}?'/%s/edit':'/u/register?n=1').'" '.($t eq 'v' || $t eq 'r' ? 'class="dropdown" rel="nofollow editDD"':'').'>edit</a>') : (), + $t eq 'u' ? ( + $o->{flags} & $VNDB::UFLAGS->{votes} ? ( $s eq 'vote' ? 'votes' : '<a href="/%s/votes">votes</a>', ) : (), + $o->{flags} & $VNDB::UFLAGS->{list} ? ( $s eq 'list' ? 'list' : '<a href="/%s/list">list</a>', ) : (), + ) : (), + + $t ne 'r' ? ( + $s eq 'disc' ? 'discussions' : '<a href="/t/%s">discussions</a>', ) : (), + $p{Authhist} ? ($s eq 'hist' ? 'history' : '<a href="/%s/hist">history</a>') : (), ); @@ -228,6 +260,14 @@ my %pagetitles = ( home => 'Visual Novel Database', pbrowse => 'Browse producers', userlist => 'Browse users', + tindex => 'Discussion board index', + ttag => sub { + return ($p{ttag}{obj} ? 'Related discussions for ' : '').$p{ttag}{title} }, + tthread => sub { + return $p{tthread}{t}{title} }, + tedit => sub { + return $p{tedit}{p} ? 'Edit post' : + $p{tedit}{t} ? 'Reply to thread' : 'Start a new thread' }, myvotes => sub { return $p{myvotes}{user}{username} eq $p{AuthUsername} ? 'My votes' : ('Votes by '.$p{myvotes}{user}{username}); }, userpage => sub { @@ -288,6 +328,7 @@ my %formerr_names = ( vn => 'Visual novel relations', l_vnn => 'Visual-novels.net link', comm => 'Edit summary', + msg => 'Message', ); my @formerr_msgs = ( sub { return sprintf 'Field "%s" is required.', @_ }, @@ -312,6 +353,7 @@ my %formerr_exeptions = ( nomail => 'No user found with that email address', nojpeg => 'Image is not in JPEG or PNG format!', toolarge => 'Image is too large (in filesize), try to compress it a little', + wrongtag => 'Wrong tag selected!', ); sub formerr { my @err = ref $_[0] eq 'ARRAY' ? @{$_[0]} : (); diff --git a/data/tpl/faq b/data/tpl/faq deleted file mode 100644 index 1c64056e..00000000 --- a/data/tpl/faq +++ /dev/null @@ -1,75 +0,0 @@ -<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 index 05780bb6..77acb6c2 100644 --- a/data/tpl/hist +++ b/data/tpl/hist @@ -1,4 +1,4 @@ -[[= $d{type} && $d{type} ne 'u' ? ttabs($d{type}, $d{obj}, 'hist') : '' ]]- +[[= $d{type} ? ttabs($d{type}, $d{obj}, 'hist') : '' ]]- <h2 class="rss">[[: $p{PageTitle} ]]</h2> [[ if($d{type} eq 'u' && $#{$d{hist}} < 0) { ]] <p> diff --git a/data/tpl/home b/data/tpl/home index ed0bca63..5ea730ce 100644 --- a/data/tpl/home +++ b/data/tpl/home @@ -10,16 +10,15 @@ 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>. + or to participate in the discussions about visual novels or VNDB on our <a href="/t">discussion board</a>. </p> -<h3 class="home">VNDB 1.18!</h3> +<h3 class="home">[[: $d{an}{title} ]]- + <p class="actions">by <a href="/u[[= $d{anpost}{uid} ]]">[[: $d{anpost}{username} ]]</a>, -[[= age $d{anpost}{date} ]]</p></h3> <p class="desc"> - Shortly after 1.17, I now present 1.18! This update includes a major change in how you refer - to specific revisions of an entry, adds the ability to select release dates back to 1980, NES - and MSX to the list of platforms, and a few warnings. + [[= summary $d{anpost}{msg}, 200 ]] <br /> - <a href="http://forum.vndb.org/index.php?topic=55.0">Read more...</a> - <a href="http://forum.vndb.org/index.php?board=7.0">news archive</a>. + <a href="/t[[= $d{an}{id} ]]">Read more...</a> - <a href="/t/an">news archive</a>. </p> <ul class="home"> diff --git a/data/tpl/main b/data/tpl/main index 9793c6c9..7d5fc6e5 100644 --- a/data/tpl/main +++ b/data/tpl/main @@ -15,6 +15,7 @@ [[ } ]]- <script src="[[: $p{st} ]]/files/def.js?[[= $VNDB::VERSION ]]" type="text/javascript"></script> [[ if($p{devshit}) { ]]- + <link rel="icon" href="/favicon.gif" type="image/gif" /> <meta name="robots" content="noindex, nofollow" /> [[ } elsif($p{userlist} || $p{userpage} || $p{userlogin} || $p{userreg} || $p{userpass} || $p{myvotes} || $p{vnlist} || $p{hist} || ($p{vnpage} && $p{vnpage}{page} eq 'stats') @@ -65,6 +66,10 @@ [[ if($p{hist}) { %d = %{$p{hist}}; ]] [[+ hist ]][[ }# ]] [[ if($p{rpage}) { %d = %{$p{rpage}}; ]] [[+ rpage ]][[ } ]] [[ if($p{docs}) { %d = %{$p{docs}}; ]] [[+ docs ]][[ } ]] +[[ if($p{tindex}) { %d = %{$p{tindex}}; ]] [[+ tindex ]][[ } ]] +[[ if($p{ttag}) { %d = %{$p{ttag}}; ]] [[+ ttag ]][[ } ]] +[[ if($p{tthread}) { %d = %{$p{tthread}}; ]] [[+ tthread ]][[ } ]] +[[ if($p{tedit}) { %d = %{$p{tedit}}; ]] [[+ tedit ]][[ } ]] [[ if($p{error}) { %d = %{$p{error}}; ]] [[+ error ]][[ } ]] </div> @@ -78,8 +83,8 @@ <li><a href="/p">Producers</a></li> <li><a href="/u/list">Users</a></li> <li><a href="/hist">Recent changes</a></li> + <li><a href="/t">Discussion board</a></li> <li><a href="/d6">FAQ</a></li> - <li><a href="http://forum.vndb.org/">Forum</a></li> </ul> -[[ if(!$p{AuthLoggedin}) { ]]- @@ -103,6 +108,7 @@ <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> + <li><a href="/t/u[[= $p{AuthId} ]]">My messages</a></li> [[ if($p{Authedit}) { ]]- <li> </li> <li><a href="/v/new">Add visual novel</a></li> diff --git a/data/tpl/myvotes b/data/tpl/myvotes index 9379e98e..231eaf25 100644 --- a/data/tpl/myvotes +++ b/data/tpl/myvotes @@ -1,3 +1,4 @@ +[[= ttabs('u', $d{user}, 'vote') ]] <h2>[[: $p{PageTitle} ]]</h2> [[ if($#{$d{votes}} < 0) { ]]- <p> diff --git a/data/tpl/pedit b/data/tpl/pedit index 363f9619..56c06b92 100644 --- a/data/tpl/pedit +++ b/data/tpl/pedit @@ -6,9 +6,8 @@ </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. + Please check the <a href="/t/p[[= $d{id} ]]">discussion board</a> <b>before</b> making + any changes! </span> [[ } if($d{id} && $d{prod}{cid} != $d{prod}{latest}) { ]] <span class="warning"> diff --git a/data/tpl/ppage b/data/tpl/ppage index e829682a..f45366fa 100644 --- a/data/tpl/ppage +++ b/data/tpl/ppage @@ -4,8 +4,7 @@ [[ 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. + <a href="/t/p[[= $d{prod}{id} ]]">discussion board</a> to undelete this page. </span> [[ } ]] [[ if(!$d{prod}{hidden} || $p{Authdel}) { ]]- diff --git a/data/tpl/redit b/data/tpl/redit index 73744517..32d618e3 100644 --- a/data/tpl/redit +++ b/data/tpl/redit @@ -3,9 +3,8 @@ [[ 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. + Please check the <a href="/t/v[[= $d{rel}{vn}[0]{vid} ]]">discussion board</a> <b>before</b> making + any changes! </span> [[ } if($d{id} && $d{rel}{cid} != $d{rel}{latest}) { ]] <span class="warning"> diff --git a/data/tpl/rpage b/data/tpl/rpage index 11432f80..54267204 100644 --- a/data/tpl/rpage +++ b/data/tpl/rpage @@ -4,8 +4,7 @@ [[ 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. + <a href="/t/v[[= $d{rel}{vn}[0]{vid} ]]">discussion board</a> to undelete this page. </span> [[ } ]] [[ if(!$d{rel}{hidden} || $p{Authdel}) { ]]- diff --git a/data/tpl/tedit b/data/tpl/tedit new file mode 100644 index 00000000..cfcd5a95 --- /dev/null +++ b/data/tpl/tedit @@ -0,0 +1,24 @@ +<h2>[[: $p{PageTitle} ]]</h2> + + +-[[= cform( [ + { type => 'error' }, + { type => 'startform', action => $d{p} ? '/t'.$d{t}{id}.'.'.$d{p}{num}.'/edit' : $d{t} ? '/t'.$d{t}{id}.'/reply' : '/t/'.$d{tag}.'/new' }, + { type => 'static', name => 'Username', text => '<a href="/u'.($d{p}?$d{p}{uid}:$p{AuthId}).'">'.($d{p}?$d{p}{username}:$p{AuthUsername}).'</a>' }, + $d{t} && !($d{p} && $d{p}{num} == 1) ? ( + { type => 'static', name => 'Topic', text => '<a href="/t'.$d{t}{id}.'">'.$d{t}{title}.'</a>.' } + ) : ( + { type => 'input', short => 'title', name => 'Thread title' }, + { type => 'input', short => 'tags', name => 'Tags' }, + $p{Authboardmod} ? ( + { type => 'check', short => 'lock', name => 'Locked' }, + ) : (), + ), + $p{Authboardmod} ? ( + { type => 'check', short => 'hide', name => 'Hidden' }, + ) : (), + { type => 'textarea', short => 'msg', name => 'Message', rows => 10, cols => 60 }, + { type => 'submit', text => 'Submit' }, + { type => 'endform' }, +], $d{form}) ]]- + diff --git a/data/tpl/tindex b/data/tpl/tindex new file mode 100644 index 00000000..ae5625b3 --- /dev/null +++ b/data/tpl/tindex @@ -0,0 +1,54 @@ +<h2>[[: $p{PageTitle} ]]</h2> + +[[ + my %desc = ( + an => 'Yorhel\'s place to make useless announcements...', + db => 'General discussions about VNDB.org. This is the place to be for feature requests and bug reports.', + v => 'Discussions about the visual novels in the database', + p => '...Producers', + r => '...Releases', + u => 'Messages to or discussions about other users on this site.' + ); +]] + + +[[ for my $tag (qw|an db v p u|) { ]]- + +<br /> +<h3>[[: $VNDB::DTAGS->{$tag} ]]</h3> +<!--<p class="desc"> + <i>[[: $desc{$tag} ]]</i> +</p>--> +[[ if(@{$d{$tag}}) { ]]- +<table id="tin"> + <!--<thead><tr> + <td class="tc1">Topic</td> + <td class="tc2">Replies</td> + <td class="tc3">Starter</td> + <td class="tc4">Last post</td> + </tr></thead>--> +[[ for (@{$d{$tag}}) { ]]- + <tr> + <td class="tc1"> + <a href="/t[[= $_->{id} ]]" title="[[= join ', ', sort map $$_[1]?$$_[0].$$_[1]:$$_[0], @{$_->{tags}} ]]">[[: $_->{title} ]]</a> + [[ if($_->{locked}) { ]]- <b>[locked]</b>[[ } ]] + </td> + <td class="tc2">[[= $_->{count}-1 ]]</td> + <td class="tc3"><a href="/u[[= $_->{uid} ]]">[[= $_->{username} ]]</a></td> + <td class="tc4"><a href="/u[[= $_->{luid} ]]">[[= $_->{lusername} ]]</a> @ + <a href="/t[[= $_->{id}.($_->{count}>$d{ppp}?'/'.ceil($_->{count}/$d{ppp}):'').'#'.$_->{count} ]]"> + [[= age $_->{ldate} ]]</a></td> + </tr> +[[ } ]] +</table> +<a class="right" href="/t/[[= $tag ]]">more...</a> +[[ } else { ]]- + <p>No threads yet. + -[[ if($tag eq 'db') { ]] + <a href="/t/[[= $tag ]]/new">Start a new thread.</a> + [[ } ]] + </p> +[[ } ]] +<br /> + +[[ } ]] diff --git a/data/tpl/ttag b/data/tpl/ttag new file mode 100644 index 00000000..3495d319 --- /dev/null +++ b/data/tpl/ttag @@ -0,0 +1,57 @@ +[[= $d{obj} ? ttabs($d{type}, $d{obj}, 'disc') : '' ]]- +<h2>[[: $p{PageTitle} ]]</h2> +<p> + <a href="/t">Discussion board</a> + > <a href="/t/[[= $d{type} ]]">[[: $VNDB::DTAGS->{$d{type}} ]]</a> + [[ if($d{obj}) { ]]- + > <b>[[= $d{tag} ]]</b>:<a href="/t/[[= $d{tag} ]]">[[: $d{title} ]]</a>[[ } ]]- +</p> + +[[ if(@{$d{t}}) { ]]- + +[[ if(!$d{obj} && $d{tag} !~ /(an|db)/) { + my @tags = grep $$_[0] eq $d{type}, map @{$_->{tags}}, @{$d{t}}; my %tags; my $i=0; ]]- +<dl> + <dt>Recent tags</dt> + <dd> + [[ for (@tags) { next if $tags{$$_[1]}++; last if $i++ == 5; ]]- + <b>[[= $$_[0].$$_[1] ]]</b>:<a href="/t/[[= $$_[0].$$_[1] ]]">[[: $$_[2] ]]</a><br />[[ } ]] + </dd> +</dl> +[[ } else { ]] +<br /> +[[ } ]] + + +[[= pagebut('/t/'.$d{tag}) ]] +<table id="tin"> + <thead><tr> + <td class="tc1">Topic</td> + <td class="tc2">#</td> + <td class="tc3">Starter</td> + <td class="tc4">Last post</td> + </tr></thead> +[[ for (@{$d{t}}) { ]]- + <tr> + <td class="tc1"><a href="/t[[= $_->{id} ]]" title="[[= join ', ', sort map $$_[1]?$$_[0].$$_[1]:$$_[0], @{$_->{tags}} ]]">[[: $_->{title} ]]</a></td> + <td class="tc2">[[= $_->{count}-1 ]]</td> + <td class="tc3"><a href="/u[[= $_->{uid} ]]">[[= $_->{username} ]]</a></td> + <td class="tc4"><a href="/u[[= $_->{luid} ]]">[[= $_->{lusername} ]]</a> @ + <a href="/t[[= $_->{id}.($_->{count}>$d{ppp}?'/'.ceil($_->{count}/$d{ppp}):'').'#'.$_->{count} ]]"> + [[= age $_->{ldate} ]]</a></td> + </tr> +[[ } ]] +</table> +[[= pagebut('/t/'.$d{tag}) ]] + +[[ } else { ]] +<p><br /> + <b>No related threads found.</b> +</p> +[[ } ]]- + +-[[ if($p{Authboard} && $d{tag} =~ /^(?:db|[vpru][0-9]+)$/) { ]]- +<br /> +<a class="right" href="/t/[[= $d{tag} ]]/new">create a new thread</a> +[[ } ]] + diff --git a/data/tpl/tthread b/data/tpl/tthread new file mode 100644 index 00000000..915474f7 --- /dev/null +++ b/data/tpl/tthread @@ -0,0 +1,81 @@ +<h2>[[: $p{PageTitle} ]]</h2> +[[ if($d{t}{hidden}) { ]] +<span class="warning">This thread has been deleted!</span> +[[ } ]] + +<dl> + <dt>Posted in</dt> + <dd> + [[ for (sort { $$a[0].$$a[1] cmp $$b[0].$$b[1] } @{$d{t}{tags}}) { ]]- + <a href="/t/[[= $$_[0] ]]">[[: $VNDB::DTAGS->{$$_[0]} ]]</a> + [[ if($$_[1]) { ]]- + > <b>[[= $$_[0].$$_[1] ]]</b>:<a href="/t/[[= $$_[0].$$_[1] ]]">[[: $$_[2] ]]</a>[[ } ]]- + <br /> + [[ } ]] + </dd> +</dl> + +<br /> + +[[ + my $pages=''; + my $lp = ceil($d{t}{count}/$d{ppp}); + if($d{t}{count} > $d{ppp}) { + my @pages = ( + $d{page} == 1 ? '<<' : '<a href="/t%d"><<</a>', + $lp > 2 ? (map + $d{page} == $_ ? $_ : '<a href="/t%d/'.$_.'">'.$_.'</a>', + 2..($lp-1) ) : (), + $d{page} == $lp ? '>>' : '<a href="/t%d/'.$lp.'">>></a>' + ); + $pages = '<p class="browse">'.join(' ', map sprintf($_,$d{t}{id}), @pages).'</p>'; + } +]] + +[[= $pages ]] +<table id="tth"> + [[ for (@{$d{p}}) { ]]- + <tr> + <td class="tc1"> + <a href="/t[[= $d{t}{id} ]].[[= $_->{num} ]]" name="[[= $_->{num} ]]">#[[= $_->{num} ]]</a> + [[ if(!$_->{hidden}) { ]]- + by <a href="/u[[= $_->{uid} ]]">[[: $_->{username} ]]</a><br /> + <i>[[= formatdate('%Y-%m-%d %R', $_->{date}) ]]</i> + [[ } ]] + </td> + <td class="tc2"> + [[ if($p{AuthId} == $_->{uid} && !$_->{hidden} || $p{Authboardmod}) { ]]- + <p class="mod">< <a href="/t[[= $d{t}{id}.'.'.$_->{num} ]]/edit">edit</a> ></p> + [[ } ]] + [[ if(!$_->{hidden}) { ]]- + [[= summary $_->{msg} ]] + [[ if($_->{edited}) { ]]<br /> + <i>last modified -[[= formatdate('%Y-%m-%d %R', $_->{edited}) ]]</i> + [[ } ]] + [[ } else { ]] + <b class="hidden">Post deleted.</b> + [[ } ]] + </td> + </tr> + [[ } ]]- +</table> +[[= $pages ]] + +<br /> +[[ if($lp == $d{page}) { ]] + [[ if($d{t}{locked}) { ]]- + <p> + This thread has been locked, you can't reply anymore. + </p> + [[ } elsif(!$p{AuthLoggedin}) { ]]- + <p> + You need to be <a href="/u/login">logged in</a> to reply to this thread. + </p> + [[ } elsif($p{Authboard} && $lp == $d{page}) { ]]- + <form action="/nospam?/t[[= $d{t}{id} ]]/reply" method="post" accept-charset="utf-8" id="qreply"> + <h3>Quick reply</h3> + <textarea name="msg" id="msg" rows="5" cols="70"></textarea> + <input type="submit" value="Post reply" /> + </form> + [[ } ]] +[[ } ]] diff --git a/data/tpl/useredit b/data/tpl/useredit index f470ce0a..16bc8c44 100644 --- a/data/tpl/useredit +++ b/data/tpl/useredit @@ -1,3 +1,4 @@ +[[= ttabs('u', $d{u}, 'edit') ]] <h2>[[: $p{PageTitle} ]]</h2> -[[ if($d{done}) { ]] diff --git a/data/tpl/userpage b/data/tpl/userpage index 9b14efc9..ef5fc55c 100644 --- a/data/tpl/userpage +++ b/data/tpl/userpage @@ -1,3 +1,4 @@ +[[= ttabs('u', $d{user}) ]] [[ ($d{pv}, $d{pl}) = ($d{user}{flags} & $VNDB::UFLAGS->{votes}, $d{user}{flags} & $VNDB::UFLAGS->{list}); ]] diff --git a/data/tpl/vnedit b/data/tpl/vnedit index c6cbd3ae..6412ad8f 100644 --- a/data/tpl/vnedit +++ b/data/tpl/vnedit @@ -6,9 +6,8 @@ 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. + Please check the <a href="/t/v[[= $d{id} ]]">discussion board</a> <b>before</b> making + any changes! </span> [[ } if($d{id} && $d{vn}{cid} != $d{vn}{latest}) { ]] <span class="warning"> diff --git a/data/tpl/vnlist b/data/tpl/vnlist index 64db1c05..0fb20509 100644 --- a/data/tpl/vnlist +++ b/data/tpl/vnlist @@ -1,3 +1,4 @@ +[[= ttabs('u', $d{user}, 'list') ]] <h2>[[: $p{PageTitle} ]]</h2> [[ my $url = sprintf '/u%d/list', $d{user}{id}; diff --git a/data/tpl/vnpage b/data/tpl/vnpage index 22fa98fc..551731a5 100644 --- a/data/tpl/vnpage +++ b/data/tpl/vnpage @@ -2,23 +2,23 @@ <h2>[[: $d{vn}{title} ]]</h2> --[[ if($p{AuthLoggedin}) { ]] -<p class="mod">< 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> -></p> -[[ } ]]- - [[ 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. + <a href="/t/v[[= $d{vn}{id} ]]">discussion board</a> to undelete this page. </span> [[ } ]] [[ if(!$d{vn}{hidden} || $p{Authdel}) { ]]- +-[[ if($p{AuthLoggedin}) { ]] +<p class="mod">< 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> +></p> +[[ } ]]- + + [[ if($d{change}) { ]] [[= cdiff($d{prev}, $d{vn}, diff --git a/lib/ChangeLog b/lib/ChangeLog index 6fe6f160..0a39be9a 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -6,6 +6,11 @@ TODO: (preferably with the option to re-add them when unhiding) + Add a link for the hidden 'h' option at /hist +1.19 - ? + - Integrated discussion board + - Colored diff for alies field + - 'ttabs' for user entries + 1.18 - 2008-07-02 (r51) - Releases, producers and visual novel items can't be fully deleted anymore - Hidden vote and vnlist items from the 'recent' lists on VN stat pages for diff --git a/lib/Multi/IRC.pm b/lib/Multi/IRC.pm index 2c95ddc9..708f4b06 100644 --- a/lib/Multi/IRC.pm +++ b/lib/Multi/IRC.pm @@ -146,24 +146,26 @@ sub vndbid { # dest, msg for (keys %{$_[HEAP]{log}}); # Four possible options: - # 1. [vpru]+ -> item page + # 1. [tvpru]+ -> item page # 2. [vpr]+.+ -> item revision # 3. d+ -> documentation page # 4. d+.+ -> documentation page # section + # 5. t+.+ -> reply to a thread my @formats = ( - BOLD.RED.'['.NORMAL.BOLD.'%s%d' .RED.']'.NORMAL.' %s ' .RED.'@'.NORMAL.LIGHT_GREY.' %s/%1$s%2$d'.NORMAL, - BOLD.RED.'['.NORMAL.BOLD.'%s%d.%d'.RED.']'.NORMAL.' %s '.RED.'by'.NORMAL.' %s '.RED.'@'.NORMAL.LIGHT_GREY.' %s/%1$s%2$d.%3$d'.NORMAL, - BOLD.RED.'['.NORMAL.BOLD.'d%d' .RED.']'.NORMAL.' %s ' .RED.'@'.NORMAL.LIGHT_GREY.' %s/d%1$d'.NORMAL, - BOLD.RED.'['.NORMAL.BOLD.'d%d.%d' .RED.']'.NORMAL.' %s '.RED.'->'.NORMAL.' %s '.RED.'@'.NORMAL.LIGHT_GREY.' %s/d%1$d#%2$d'.NORMAL, + BOLD.RED.'['.NORMAL.BOLD.'%s%d' .RED.']' .NORMAL.' %s ' .RED.'@'.NORMAL.LIGHT_GREY.' %s/%1$s%2$d'.NORMAL, + BOLD.RED.'['.NORMAL.BOLD.'%s%d.%d'.RED.']'.NORMAL.RED.' Edit of' .NORMAL.' %s '.RED.'by'.NORMAL.' %s '.RED.'@'.NORMAL.LIGHT_GREY.' %s/%1$s%2$d.%3$d'.NORMAL, + BOLD.RED.'['.NORMAL.BOLD.'d%d' .RED.']' .NORMAL.' %s ' .RED.'@'.NORMAL.LIGHT_GREY.' %s/d%1$d'.NORMAL, + BOLD.RED.'['.NORMAL.BOLD.'d%d.%d' .RED.']' .NORMAL.' %s '.RED.'->'.NORMAL.' %s '.RED.'@'.NORMAL.LIGHT_GREY.' %s/d%1$d#%2$d'.NORMAL, + BOLD.RED.'['.NORMAL.BOLD.'t%d.%d' .RED.']'.NORMAL.RED.' Reply to'.NORMAL.' %s '.RED.'by'.NORMAL.' %s '.RED.'@'.NORMAL.LIGHT_GREY.' %s/t%1$d.%2$d'.NORMAL, ); # get a list of possible IDs (a la sub summary in defs.pl) my @id; # [ type, id, ref ] for (split /[, ]/, $m) { next if length > 15 or m{[a-z]{3,6}://}i; # weed out URLs and too long things - push @id, /^(?:.*[^\w]|)([dvpr])([0-9]+)\.([0-9]+)(?:[^\w].*|)$/ ? [ $1, $2, $3 ] # matches 2 and 4 - : /^(?:.*[^\w]|)([duvpr])([0-9]+)(?:[^\w].*|)$/ ? [ $1, $2, 0 ] : (); # matches 1 and 3 + push @id, /^(?:.*[^\w]|)([dvprt])([0-9]+)\.([0-9]+)(?:[^\w].*|)$/ ? [ $1, $2, $3 ] # matches 2, 4 and 5 + : /^(?:.*[^\w]|)([dvprtu])([0-9]+)(?:[^\w].*|)$/ ? [ $1, $2, 0 ] : (); # matches 1 and 3 } # loop through the matched IDs and search the database @@ -174,11 +176,12 @@ sub vndbid { # dest, msg $_[HEAP]{log}{$t.$id.'.'.$rev} = time; # option 1: item page - if($t =~ /[vpru]/ && !$rev) { + if($t =~ /[vprtu]/ && !$rev) { my $s = $Multi::SQL->prepare( $t eq 'v' ? 'SELECT vr.title FROM vn_rev vr JOIN vn v ON v.latest = vr.id WHERE v.id = ?' : $t eq 'u' ? 'SELECT u.username AS title FROM users u WHERE u.id = ?' : $t eq 'p' ? 'SELECT pr.name AS title FROM producers_rev pr JOIN producers p ON p.latest = pr.id WHERE p.id = ?' : + $t eq 't' ? 'SELECT title FROM threads WHERE id = ?' : 'SELECT rr.title FROM releases_rev rr JOIN releases r ON r.latest = rr.id WHERE r.id = ?' ); $s->execute($id); @@ -231,6 +234,22 @@ sub vndbid { # dest, msg next if !$sub; $_[KERNEL]->post(circ => privmsg => $_[ARG0], sprintf $formats[3], $id, $rev, $title, $sub, $VNDB::VNDBopts{root_url}); + + # option 5: reply to a thread + } elsif($t eq 't' && $rev) { + my $s = $Multi::SQL->prepare(q| + SELECT t.title, u.username + FROM threads t + JOIN threads_posts tp ON tp.tid = t.id + JOIN users u ON u.id = tp.uid + WHERE t.id = ? + AND tp.num = ?|); + $s->execute($id, $rev); + my $r = $s->fetchrow_hashref; + $s->finish; + next if !$r || ref($r) ne 'HASH'; + $_[KERNEL]->post(circ => privmsg => $_[ARG0], sprintf $formats[4], + $id, $rev, $r->{title}, $r->{username}, $VNDB::VNDBopts{root_url}); } } } diff --git a/lib/VNDB.pm b/lib/VNDB.pm index 366ffa3e..91e8eb49 100644 --- a/lib/VNDB.pm +++ b/lib/VNDB.pm @@ -14,6 +14,7 @@ use VNDB::Util::Response; use VNDB::Util::DB; use VNDB::Util::Tools; use VNDB::Util::Auth; +use VNDB::Discussions; use VNDB::HomePages; use VNDB::Producers; use VNDB::Releases; @@ -90,6 +91,24 @@ my %VNDBuris = ( # wildcards: * -> (.+), + -> ([0-9]+) hist => {'*'=> sub { shift->History('p', shift, $_[1]) } }, }, 'p+.+' => sub { shift->PPage($_[0][0], $_[0][1]) }, + # discussions + t => { + '/' => sub { shift->TIndex }, + search => sub {}, # search? + '*' => { + '/' => sub { shift->TTag($_[1]) }, + new => sub { shift->TEdit(0, 0, $_[1]) }, + }, + }, + 't+' => { + '/' => sub { shift->TThread(shift) }, + reply => sub { shift->TEdit(shift) }, + '+' => sub { shift->TThread(shift, shift) }, + }, + 't+.+' => { + edit => sub { shift->TEdit($_[0][0], $_[0][1]) }, + '/' => sub { $_[0]->ResRedirect('/t'.$_[1][0].($_[1][1]>$_[0]->{postsperpage}?'/'.ceil($_[1][1]/$_[0]->{postsperpagee}):'').'#'.$_[1][1], 'perm') }, + }, # stuff (.xml extension to make sure they aren't counted as pageviews) xml => { 'producers.xml' => sub { shift->PXML }, diff --git a/lib/VNDB/Discussions.pm b/lib/VNDB/Discussions.pm new file mode 100644 index 00000000..233b41da --- /dev/null +++ b/lib/VNDB/Discussions.pm @@ -0,0 +1,185 @@ + +package VNDB::Discussions; + +use strict; +use warnings; +use Exporter 'import'; +use POSIX 'ceil'; + +use vars ('$VERSION', '@EXPORT'); +$VERSION = $VNDB::VERSION; +@EXPORT = qw| TThread TEdit TIndex TTag |; + + +sub TThread { + my $self = shift; + my $id = shift; + my $page = shift||1; + + my $t = $self->DBGetThreads(id => $id, what => 'tagtitles')->[0]; + return $self->ResNotFound if !$t || $t->{hidden} && !$self->AuthCan('boardmod'); + + my $p = $self->DBGetPosts(tid => $id, results => $self->{postsperpage}, page => $page); + return $self->ResNotFound if !$p->[0]; + + $self->ResAddTpl(tthread => { + t => $t, + ppp => $self->{postsperpage}, + page => $page, + p => $p, + }); +} + + +# tid num action +# 0 0 Start a new thread +# x 0 Reply to a thread +# x 1 Edit thread (and first post) +# x x Edit post +sub TEdit { + my $self = shift; + my $tid = shift||0; + my $num = shift||0; + my $tag = shift||''; + + my $t = $tid && $self->DBGetThreads(id => $tid, what => 'tags')->[0]; + return $self->ResNotFound if $tid && !$t; + + my $p = $num && $self->DBGetPosts(tid => $tid, num => $num)->[0]; + + my $frm = {}; + if($self->ReqMethod eq 'POST') { + $frm = $self->FormCheck( + { name => 'msg', required => 1, maxlength => 1024 }, + !$tid || $num == 1 ? ( + { name => 'title', required => 1, maxlength => 50 }, + { name => 'tags', required => 1, maxlength => 50 }, + ) : (), + $self->AuthCan('boardmod') ? ( + { name => 'hide', required => 0 }, + { name => 'lock', required => 0 } + ) : (), + ); + $frm->{msg} =~ s/[\r\s\n]$//g; + + my @tags = !$frm->{tags} || $frm->{_err} ? () : map { + $frm->{_err} = [ 'wrongtag' ] if + !/^([a-z]{1,2})([0-9]*)$/ || !$VNDB::DTAGS->{$1} + || $1 eq 'v' && (!$2 || !$self->DBGetVN(id => $2)->[0]) + #|| $1 eq 'r' && (!$2 || !$self->DBGetRelease(id => $2)->[0]) + || $1 eq 'p' && (!$2 || !$self->DBGetProducer(id => $2)->[0]) + || $1 eq 'u' && (!$2 || !$self->DBGetUser(id => $2)->[0]) + || $1 eq 'an' && !$self->AuthCan('boardmod'); + [ $1, $2||0 ] + } split / /, $frm->{tags}; + + if(!$frm->{_err}) { + my $otid = $tid; + if(!$tid || $num == 1) { + my @tags = + my %thread = ( + id => $tid, + title => $frm->{title}, + tags => \@tags, + hidden => $frm->{hide}, + locked => $frm->{lock}, + ); + $self->DBEditThread(%thread) if $tid; # edit thread + $tid = $self->DBAddThread(%thread) if !$tid; # create thread + } + + my %post = ( + tid => $tid, + num => !$otid ? 1 : $num, + msg => $frm->{msg}, + hidden => $num != 1 && $frm->{hide}, + ); + $self->DBEditPost(%post) if $num; # edit post + $num = $self->DBAddPost(%post) if !$num; # add post + + my $pagenum = ceil($num/$self->{postsperpage}); + $pagenum = $pagenum > 1 ? '/'.$pagenum : ''; + $self->ResRedirect('/t'.$tid.$pagenum.'#'.$num, 'POST'); + } + } + + if($p) { + $frm->{msg} ||= $p->{msg}; + $frm->{hide} = $p->{hidden}; + if($num == 1) { + $frm->{tags} ||= join ' ', sort map $_->[1]?$_->[0].$_->[1]:$_->[0], @{$t->{tags}}; + $frm->{title} ||= $t->{title}; + $frm->{lock} = $t->{locked}; + $frm->{hide} = $t->{hidden}; + } + } + $frm->{tags} ||= $tag; + + $self->ResAddTpl(tedit => { + t => $t, + p => $p, + tag => $tag, + form => $frm, + }); +} + + +sub TIndex { + my $self = shift; + + my %opts = ( + results => 6, + what => 'firstpost lastpost tags', + order => 'tp2.date DESC', + ); + + $self->ResAddTpl(tindex => { + ppp => $self->{postsperpage}, + map +($_, scalar $self->DBGetThreads(%opts, type => $_)), qw| an db v p u| + }); +} + + +sub TTag { + my $self = shift; + my $tag = shift; + my($type, $iid) = ($1, $2||0) if $tag =~ /^([a-z]{1,2})([0-9]*)$/; + + my $f = $self->FormCheck( + { name => 'p', required => 0, default => 1, template => 'int' }, + ); + + my $o = !$iid ? undef : + $type eq 'u' ? $self->DBGetUser(uid => $iid)->[0] : + $type eq 'v' ? $self->DBGetVN(id => $iid)->[0] : + #$type eq 'r' ? $self->DBGetRelease(id => $iid)->[0] : + $self->DBGetProducer(id => $iid)->[0]; + return $self->ResNotFound if $iid && !$o || !$VNDB::DTAGS->{$type}; + my $title = $o ? $o->{username} || $o->{romaji} || $o->{title} || $o->{name} : $VNDB::DTAGS->{$type}; + + my($t, $np) = $self->DBGetThreads( + type => $type, + iid => $iid, + results => 50, + page => $f->{p}, + what => 'firstpost lastpost tagtitles', + order => $tag eq 'an' ? 't.id DESC' : 'tp2.date DESC', + ); + + $self->ResAddTpl(ttag => { + page => $f->{p}, + npage => $np, + obj => $o, + type => $type, + iid => $iid, + title => $title, + tag => $tag, + t => $t, + ppp => $self->{postsperpage}, + }); +} + + + +1; + diff --git a/lib/VNDB/HomePages.pm b/lib/VNDB/HomePages.pm index 5c1ef3ef..7a70797a 100644 --- a/lib/VNDB/HomePages.pm +++ b/lib/VNDB/HomePages.pm @@ -13,14 +13,10 @@ $VERSION = $VNDB::VERSION; sub HomePage { my $self = shift; - # recent edits - # recently added visual novels - # recently added producers - # random visual novels - # recent votes - # popular visual novels - + my $an = $self->DBGetThreads(type => 'an', order => 't.id DESC', results => 1)->[0]; $self->ResAddTpl(home => { + an => $an, + anpost => $self->DBGetPosts(tid => $an->{id}, num => 1)->[0], recentedits => scalar $self->DBGetHist( results => 10, what => 'iid ititle'), recentvns => scalar $self->DBGetHist( results => 10, what => 'iid ititle', edits => 0, type => 'v'), recentps => scalar $self->DBGetHist( results => 10, what => 'iid ititle', edits => 0, type => 'p'), diff --git a/lib/VNDB/Users.pm b/lib/VNDB/Users.pm index 4262f59d..4b4e65ef 100644 --- a/lib/VNDB/Users.pm +++ b/lib/VNDB/Users.pm @@ -173,6 +173,7 @@ sub UsrEdit { done => $d, adm => $adm, user => $user, + u => $u }); } diff --git a/lib/VNDB/Util/DB.pm b/lib/VNDB/Util/DB.pm index a0c851bb..8391f58f 100644 --- a/lib/VNDB/Util/DB.pm +++ b/lib/VNDB/Util/DB.pm @@ -19,6 +19,7 @@ $VERSION = $VNDB::VERSION; DBGetVN DBAddVN DBEditVN DBHideVN DBUndefRG DBVNCache DBGetRelease DBAddRelease DBEditRelease DBHideRelease DBGetProducer DBGetProducerVN DBAddProducer DBEditProducer DBHideProducer + DBGetThreads DBGetPosts DBAddPost DBEditPost DBEditThread DBAddThread DBExec DBRow DBAll DBLastId |; @@ -1130,6 +1131,206 @@ sub DBHideProducer { # id, hidden + +#-----------------------------------------------------------------------------# +# D I S C U S S I O N S # +#-----------------------------------------------------------------------------# + + +sub DBGetThreads { # %options->{ id type iid results page what } + my($s, %o) = @_; + + $o{results} ||= 50; + $o{page} ||= 1; + $o{what} ||= ''; + $o{order} ||= 't.id DESC'; + + my %where = ( + $o{id} ? ( + 't.id = %d' => $o{id} ) : (), + !$o{id} ? ( + 't.hidden = 0' => 1 ) : (), + $o{type} && !$o{iid} ? ( + 't.id IN(SELECT tid FROM threads_tags WHERE type = !s)' => $o{type} ) : (), + $o{type} && $o{iid} ? ( + 'tt.type = !s' => $o{type}, 'tt.iid = %d' => $o{iid} ) : (), + ); + my $where = scalar keys %where ? 'WHERE !W' : ''; + + my $select = 't.id, t.title, t.count, t.locked, t.hidden'; + $select .= ', tp.uid, tp.date, u.username' if $o{what} =~ /firstpost/; + $select .= ', tp2.uid AS luid, tp2.date AS ldate, u2.username AS lusername' if $o{what} =~ /lastpost/; + + my @join; + push @join, 'JOIN threads_posts tp ON tp.tid = t.id AND tp.num = 1' if $o{what} =~ /firstpost/; + push @join, 'JOIN users u ON u.id = tp.uid' if $o{what} =~ /firstpost/; + push @join, 'JOIN threads_posts tp2 ON tp2.tid = t.id AND tp2.num = t.count' if $o{what} =~ /lastpost/; + push @join, 'JOIN users u2 ON u2.id = tp2.uid' if $o{what} =~ /lastpost/; + push @join, 'JOIN threads_tags tt ON tt.tid = t.id' if $o{type} && $o{iid}; + + my $r = $s->DBAll(qq| + SELECT $select + FROM threads t + @join + $where + ORDER BY %s + LIMIT %d OFFSET %d|, + $where ? \%where : (), + $o{order}, + $o{results}+(wantarray?1:0), $o{results}*($o{page}-1) + ); + + if($o{what} =~ /(tags|tagtitles)/ && $#$r >= 0) { + my %r = map { + $r->[$_]{tags} = []; + ($r->[$_]{id}, $_) + } 0..$#$r; + + if($o{what} =~ /tags/) { + ($_->{type}=~s/ +//||1) && push(@{$r->[$r{$_->{tid}}]{tags}}, [ $_->{type}, $_->{iid} ]) for (@{$s->DBAll(q| + SELECT tid, type, iid + FROM threads_tags + WHERE tid IN(!l)|, + [ keys %r ] + )}); + } + if($o{what} =~ /tagtitles/) { + ($_->{type}=~s/ +//||1) && push(@{$r->[$r{$_->{tid}}]{tags}}, [ $_->{type}, $_->{iid}, $_->{title} ]) for (@{$s->DBAll(q| + SELECT tt.tid, tt.type, tt.iid, COALESCE(u.username, vr.title, pr.name) AS title + FROM threads_tags tt + LEFT JOIN vn v ON tt.type = 'v' AND v.id = tt.iid + LEFT JOIN vn_rev vr ON vr.id = v.latest + LEFT JOIN producers p ON tt.type = 'p' AND p.id = tt.iid + LEFT JOIN producers_rev pr ON pr.id = p.latest + LEFT JOIN users u ON tt.type = 'u' AND u.id = tt.iid + WHERE tt.tid IN(!l)|, + [ keys %r ] + )}); + } + } + + return $r if !wantarray; + return ($r, 0) if $#$r < $o{results}; + pop @$r; + return ($r, 1); +} + + +sub DBGetPosts { # %options->{ tid num page results } + my($s, %o) = @_; + + $o{results} ||= 50; + $o{page} ||= 1; + + my %where = ( + 'tp.tid = %d' => $o{tid}, + $o{num} ? ( + 'tp.num = %d' => $o{num} ) : (), + ); + + my $r = $s->DBAll(q| + SELECT tp.num, tp.date, tp.edited, tp.msg, tp.hidden, tp.uid, u.username + FROM threads_posts tp + JOIN users u ON u.id = tp.uid + WHERE !W + ORDER BY tp.num ASC + LIMIT %d OFFSET %d|, + \%where, + $o{results}, $o{results}*($o{page}-1) + ); + + return $r if !wantarray; +} + + +sub DBAddPost { # %options->{ tid uid msg num } + my($s, %o) = @_; + + $o{num} ||= $s->DBRow('SELECT num FROM threads_posts WHERE tid = %d ORDER BY num DESC LIMIT 1', $o{tid})->{num}+1; + $o{uid} ||= $s->AuthInfo->{id}; + + $s->DBExec(q| + INSERT INTO threads_posts (tid, num, uid, msg) + VALUES(%d, %d, %d, !s)|, + @o{qw| tid num uid msg |} + ); + $s->DBExec(q| + UPDATE threads + SET count = count+1 + WHERE id = %d|, + $o{tid}); + + return $o{num}; +} + + +sub DBEditPost { # %options->{ tid num msg hidden } + my($s, %o) = @_; + + my %set = ( + 'msg = !s' => $o{msg}, + 'edited = %d' => time, + 'hidden = %d' => $o{hidden}?1:0, + ); + + $s->DBExec(q| + UPDATE threads_posts + SET !H + WHERE tid = %d + AND num = %d|, + \%set, $o{tid}, $o{num} + ); +} + + +sub DBEditThread { # %options->{ id title locked hidden tags } + my($s, %o) = @_; + + my %set = ( + 'title = !s' => $o{title}, + 'locked = %d' => $o{locked}?1:0, + 'hidden = %d' => $o{hidden}?1:0, + ); + + $s->DBExec(q| + UPDATE threads + SET !H + WHERE id = %d|, + \%set, $o{id}); + + if($o{tags}) { + $s->DBExec('DELETE FROM threads_tags WHERE tid = %d', $o{id}); + $s->DBExec(q| + INSERT INTO threads_tags (tid, type, iid) + VALUES (%d, !s, %d)|, + $o{id}, $_->[0], $_->[1]||0 + ) for (@{$o{tags}}); + } +} + + +sub DBAddThread { # %options->{ title tags } + my($s, %o) = @_; + + my $id = $s->DBRow(q| + INSERT INTO threads (title) + VALUES (!s) + RETURNING id|, $o{title} + )->{id}; + + $s->DBExec(q| + INSERT INTO threads_tags (tid, type, iid) + VALUES (%d, !s, %d)|, + $id, $_->[0], $_->[1] + ) for (@{$o{tags}}); + + return $id; +} + + + + + #-----------------------------------------------------------------------------# # U T I L I T I E S # #-----------------------------------------------------------------------------# diff --git a/lib/VNDB/VN.pm b/lib/VNDB/VN.pm index 2b0e6efc..365204d4 100644 --- a/lib/VNDB/VN.pm +++ b/lib/VNDB/VN.pm @@ -226,7 +226,7 @@ sub VNBrowse { if($chr eq 'search') { # VNDBID return $self->ResRedirect('/'.$1.$2.(!$3 ? '' : $1 eq 'd' ? '#'.$3 : '.'.$3), 'temp') - if $q =~ /^([vrpud])([0-9]+)(?:\.([0-9]+))?$/; + if $q =~ /^([vrptud])([0-9]+)(?:\.([0-9]+))?$/; if(!($q =~ s/^title://)) { # categories diff --git a/lib/global.pl b/lib/global.pl index 514640f9..a4c0d9ec 100644 --- a/lib/global.pl +++ b/lib/global.pl @@ -31,12 +31,13 @@ our %VNDBopts = ( }, ranks => [ [ [ qw| visitor loser user mod admin | ], [] ], - {map{$_,1}qw| hist |}, # 0 - visitor (not logged in) - {map{$_,1}qw| hist |}, # 1 - loser - {map{$_,1}qw| hist edit |}, # 2 - user - {map{$_,1}qw| hist edit mod lock |}, # 3 - mod - {map{$_,1}qw| hist edit mod lock del userlist useredit |}, # 4 - admin + {map{$_,1}qw| hist |}, # 0 - visitor (not logged in) + {map{$_,1}qw| hist |}, # 1 - loser + {map{$_,1}qw| hist board edit |}, # 2 - user + {map{$_,1}qw| hist board boardmod edit mod lock |}, # 3 - mod + {map{$_,1}qw| hist board boardmod edit mod lock del userlist useredit |}, # 4 - admin ], + postsperpage => 25, imgpath => '/www/vndb/static/cv', mappath => '/www/vndb/data/rg', docpath => '/www/vndb/data/docs', @@ -44,6 +45,17 @@ our %VNDBopts = ( $VNDBopts{ranks}[0][1] = { (map{$_,1} map { keys %{$VNDBopts{ranks}[$_]} } 1..5) }; +# I wonder why I even made this hash, almost everything is still hardcoded anyway... +our $DTAGS = { + an => 'Announcements', # 0 - usage restricted to boardmods + db => 'VNDB Discussions', # 0 + v => 'Visual novels', # vid + #r => 'Releases', # rid + p => 'Producers', # pid + u => 'Users', # uid +}; + + our $PLAT = { win => 'Windows', lin => 'Linux', diff --git a/static/files/style.css b/static/files/style.css index d890a47c..77bd7eba 100644 --- a/static/files/style.css +++ b/static/files/style.css @@ -766,6 +766,27 @@ b.diff_del { font-weight: normal; background-color: #fcc; } #tpd .tc1 { margin: 0; padding: 0; } + +/* message board */ + +#tth tr { border-top: 1px solid #999 } +#tth .tc1 { width: 150px; border-right: 1px solid #999; padding: 5px; } +#tth .tc2 { padding: 5px } +#tth p.mod { margin: -5px; font-size: 10px; } +#tth .tc2 i { font-size: 10px; float: right; } +#tth .tc2 b.hidden { font-size: 10px; color: #888; font-weight: normal; } +#tin .tc1 { width: 400px; } +#tin .tc2 { width: 25px; } +#tin .tc4 { text-align: right } +#tin .tc1 b { font-weight: normal; color: #888; } +#qreply { display: block; width: 100%; text-align: center } +#qreply textarea, #qreply input { float: none; margin: auto; } +a.right { float: right } +b.spoiler { font-weight: normal; color: #ccc; background-color: #ccc } +b.spoiler:hover { color: #000; background-color: #eee } + + + #debug { border-top: 1px solid #ffb4b4; background-color: #ffece3; diff --git a/util/updates/update_1.19.sql b/util/updates/update_1.19.sql new file mode 100644 index 00000000..4b5a08b5 --- /dev/null +++ b/util/updates/update_1.19.sql @@ -0,0 +1,28 @@ + + +-- Messageboard +CREATE TABLE threads ( + id SERIAL NOT NULL PRIMARY KEY, + title varchar(50) NOT NULL DEFAULT '', + count smallint NOT NULL DEFAULT 0, + locked smallint NOT NULL DEFAULT 0, + hidden smallint NOT NULL DEFAULT 0 +) WITHOUT OIDS; + +CREATE TABLE threads_tags ( + tid integer NOT NULL DEFAULT 0 REFERENCES threads (id) DEFERRABLE INITIALLY DEFERRED, + type char(2) NOT NULL DEFAULT 0, + iid integer NOT NULL DEFAULT 0 -- references to (vn|releases|producers|users).id +) WITHOUT OIDS; + +CREATE TABLE threads_posts ( + tid integer NOT NULL DEFAULT 0 REFERENCES threads (id) DEFERRABLE INITIALLY DEFERRED, + num integer NOT NULL DEFAULT 0, + uid integer NOT NULL DEFAULT 0 REFERENCES users (id) DEFERRABLE INITIALLY DEFERRED, + date bigint NOT NULL DEFAULT DATE_PART('epoch', NOW()), + edited bigint NOT NULL DEFAULT 0, + hidden smallint NOT NULL DEFAULT 0, + msg text NOT NULL DEFAULT '', + PRIMARY KEY(tid, num) +) WITHOUT OIDS; + |