summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryorhel <yorhel@1fe2e327-d9db-4752-bcf7-ef0cb4a1748b>2008-07-08 07:27:56 +0000
committeryorhel <yorhel@1fe2e327-d9db-4752-bcf7-ef0cb4a1748b>2008-07-08 07:27:56 +0000
commitdb47e11b580efa1938de892e6c08ba47ba2c6cc4 (patch)
tree3d2133912d93585e7bf36b41a6b1f85c40a0031a
parent4c821b8138b0f9820cada6be7e051fe00434c54e (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.pl70
-rw-r--r--data/tpl/faq75
-rw-r--r--data/tpl/hist2
-rw-r--r--data/tpl/home11
-rw-r--r--data/tpl/main8
-rw-r--r--data/tpl/myvotes1
-rw-r--r--data/tpl/pedit5
-rw-r--r--data/tpl/ppage3
-rw-r--r--data/tpl/redit5
-rw-r--r--data/tpl/rpage3
-rw-r--r--data/tpl/tedit24
-rw-r--r--data/tpl/tindex54
-rw-r--r--data/tpl/ttag57
-rw-r--r--data/tpl/tthread81
-rw-r--r--data/tpl/useredit1
-rw-r--r--data/tpl/userpage1
-rw-r--r--data/tpl/vnedit5
-rw-r--r--data/tpl/vnlist1
-rw-r--r--data/tpl/vnpage18
-rw-r--r--lib/ChangeLog5
-rw-r--r--lib/Multi/IRC.pm35
-rw-r--r--lib/VNDB.pm19
-rw-r--r--lib/VNDB/Discussions.pm185
-rw-r--r--lib/VNDB/HomePages.pm10
-rw-r--r--lib/VNDB/Users.pm1
-rw-r--r--lib/VNDB/Util/DB.pm201
-rw-r--r--lib/VNDB/VN.pm2
-rw-r--r--lib/global.pl22
-rw-r--r--static/files/style.css21
-rw-r--r--util/updates/update_1.19.sql28
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/\&/&amp;/g;
s/>/&gt;/g;
s/</&lt;/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>&nbsp;</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>
+ &gt; <a href="/t/[[= $d{type} ]]">[[: $VNDB::DTAGS->{$d{type}} ]]</a>
+ [[ if($d{obj}) { ]]-
+ &gt; <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]) { ]]-
+ &gt; <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 ? '&lt;&lt;' : '<a href="/t%d">&lt;&lt;</a>',
+ $lp > 2 ? (map
+ $d{page} == $_ ? $_ : '<a href="/t%d/'.$_.'">'.$_.'</a>',
+ 2..($lp-1) ) : (),
+ $d{page} == $lp ? '&gt;&gt;' : '<a href="/t%d/'.$lp.'">&gt;&gt;</a>'
+ );
+ $pages = '<p class="browse">'.join(' &nbsp;', 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">&lt; <a href="/t[[= $d{t}{id}.'.'.$_->{num} ]]/edit">edit</a> &gt;</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">&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>
-[[ } ]]-
-
[[ 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">&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>
+[[ } ]]-
+
+
[[ 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;
+