summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Multi/API.pm15
-rw-r--r--lib/Multi/IRC.pm30
-rw-r--r--lib/Multi/Maintenance.pm11
-rw-r--r--lib/Multi/RG.pm4
-rw-r--r--lib/SkinFile.pm74
-rw-r--r--lib/VNDB/DB/Discussions.pm12
-rw-r--r--lib/VNDB/DB/Misc.pm24
-rw-r--r--lib/VNDB/DB/Producers.pm2
-rw-r--r--lib/VNDB/DB/Releases.pm2
-rw-r--r--lib/VNDB/DB/ULists.pm2
-rw-r--r--lib/VNDB/DB/Users.pm98
-rw-r--r--lib/VNDB/DB/VN.pm8
-rw-r--r--lib/VNDB/Handler/Discussions.pm6
-rw-r--r--lib/VNDB/Handler/Misc.pm52
-rw-r--r--lib/VNDB/Handler/Producers.pm7
-rw-r--r--lib/VNDB/Handler/Releases.pm10
-rw-r--r--lib/VNDB/Handler/Tags.pm2
-rw-r--r--lib/VNDB/Handler/Users.pm143
-rw-r--r--lib/VNDB/Handler/VNEdit.pm9
-rw-r--r--lib/VNDB/Handler/VNPage.pm42
-rw-r--r--lib/VNDB/Util/Auth.pm11
-rw-r--r--lib/VNDB/Util/BrowseHTML.pm2
-rw-r--r--lib/VNDB/Util/CommonHTML.pm45
-rw-r--r--lib/VNDB/Util/FormHTML.pm14
-rw-r--r--lib/VNDB/Util/LayoutHTML.pm5
25 files changed, 448 insertions, 182 deletions
diff --git a/lib/Multi/API.pm b/lib/Multi/API.pm
index b6100dfa..be1191c6 100644
--- a/lib/Multi/API.pm
+++ b/lib/Multi/API.pm
@@ -108,7 +108,7 @@ sub filtertosql {
# get the type that matches
$t = (grep +(
# wrong operator? don't even look further!
- !$_->[2]{$op} ? 0
+ !defined($_->[2]{$op}) ? 0
# undef
: !defined($_->[0]) ? !defined($value)
# int
@@ -423,7 +423,7 @@ sub get_vn {
for (grep !/^(basic|details|anime|relations)$/, @{$get->{info}});
my $select = 'v.id, v.latest';
- $select .= ', vr.title, vr.original, v.c_released, v.c_languages, v.c_platforms' if grep /basic/, @{$get->{info}};
+ $select .= ', vr.title, vr.original, v.c_released, v.c_languages::text[], v.c_platforms' if grep /basic/, @{$get->{info}};
$select .= ', vr.alias AS aliases, vr.length, vr.desc AS description, vr.l_wp, vr.l_encubed, vr.l_renai' if grep /details/, @{$get->{info}};
my @placeholders;
@@ -446,11 +446,10 @@ sub get_vn {
[ str => 'v.c_platforms :op: :value:', {'=' => 'LIKE', '!=' => 'NOT LIKE'}, process => \'like' ],
[ stra => '(:value:)', {'=', 1}, join => ' OR ', serialize => 'v.c_platforms LIKE :value:', \'like' ],
[ stra => '(:value:)', {'!=',1}, join => ' AND ', serialize => 'v.c_platforms NOT LIKE :value:', \'like' ],
- ], [ 'languages', # rather similar to platforms
- [ undef, "v.c_languages :op: ''", {qw|= = != <>|} ],
- [ str => 'v.c_languages :op: :value:', {'=' => 'LIKE', '!=' => 'NOT LIKE'}, process => \'like' ],
- [ stra => '(:value:)', {'=', 1}, join => ' OR ', serialize => 'v.c_languages LIKE :value:', process => \'like' ],
- [ stra => '(:value:)', {'!=',1}, join => ' AND ', serialize => 'v.c_languages NOT LIKE :value:', process => \'like' ],
+ ], [ 'languages',
+ [ undef, "v.c_languages :op: '{}'", {qw|= = != <>|} ],
+ [ str => ':op: (v.c_languages && ARRAY[:value:]::language[])', {'=' => '', '!=' => 'NOT'} ],
+ [ stra => ':op: (v.c_languages && ARRAY[:value:]::language[])', {'=' => '', '!=' => 'NOT'}, join => ',' ],
], [ 'search',
[ str => '(vr.title ILIKE :value: OR vr.alias ILIKE :value: OR v.id IN(
SELECT rv.vid FROM releases r JOIN releases_rev rr ON rr.id = r.latest JOIN releases_vn rv ON rv.rid = rr.id
@@ -484,7 +483,7 @@ sub get_vn_res {
if(grep /basic/, @{$get->{info}}) {
$_->{original} ||= undef;
$_->{platforms} = [ split /\//, delete $_->{c_platforms} ];
- $_->{languages} = [ split /\//, delete $_->{c_languages} ];
+ $_->{languages} = delete $_->{c_languages};
$_->{released} = formatdate delete $_->{c_released};
}
if(grep /details/, @{$get->{info}}) {
diff --git a/lib/Multi/IRC.pm b/lib/Multi/IRC.pm
index 7d74ded7..0b19ed05 100644
--- a/lib/Multi/IRC.pm
+++ b/lib/Multi/IRC.pm
@@ -310,7 +310,7 @@ sub notify_result { # num, res
$_[HEAP]{lastpost} = $r->{lastpost} if $r->{lastpost};
$_[HEAP]{lasttag} = $r->{lasttag} if $r->{lasttag};
return if !keys %{$_[HEAP]{notify}};
- $_[KERNEL]->yield(formatid => $_[ARG0], $_[ARG1], [ keys %{$_[HEAP]{notify}} ]);
+ $_[KERNEL]->yield(formatid => $_[ARG0], $_[ARG1], [ [ keys %{$_[HEAP]{notify}} ], 1 ]);
}
@@ -372,7 +372,7 @@ sub cmd_vn_results { # num, res, \@_
return $_[KERNEL]->yield(reply => $_[ARG2][DEST], sprintf(
'Too many results found, see %s/v/all?q=%s', $VNDB::S{url}, uri_escape_utf8($_[ARG2][ARG])
), $_[ARG2][USER]) if $_[ARG0] > 5;
- $_[KERNEL]->yield(formatid => $_[ARG0], $_[ARG1], $_[ARG2][DEST]);
+ $_[KERNEL]->yield(formatid => $_[ARG0], $_[ARG1], [$_[ARG2][DEST]]);
}
@@ -397,7 +397,7 @@ sub cmd_p_results { # num, res, \@_
return $_[KERNEL]->yield(reply => $_[ARG2][DEST], sprintf(
'Too many results found, see %s/p/all?q=%s', $VNDB::S{url}, uri_escape_utf8($_[ARG2][ARG])
), $_[ARG2][USER]) if $_[ARG0] > 5;
- $_[KERNEL]->yield(formatid => $_[ARG0], $_[ARG1], $_[ARG2][DEST]);
+ $_[KERNEL]->yield(formatid => $_[ARG0], $_[ARG1], [$_[ARG2][DEST]]);
}
@@ -521,7 +521,7 @@ sub vndbid { # dest, msg
$t eq 't' ? 'title, '.GETBOARDS.' FROM threads t WHERE id = ?' :
$t eq 'g' ? 'name AS title FROM tags WHERE id = ?' :
'rr.title FROM releases_rev rr JOIN releases r ON r.latest = rr.id WHERE r.id = ?'),
- [ $t, $id, $id ], 'formatid', $dest
+ [ $t, $id, $id ], 'formatid', [$dest]
) if !$rev && $t =~ /[vprtug]/;
# edit/insert of vn/release/producer or discussion board post
@@ -530,7 +530,7 @@ sub vndbid { # dest, msg
$t eq 'r' ? 'rr.title, u.username, c.comments FROM changes c JOIN releases_rev rr ON c.id = rr.id JOIN users u ON u.id = c.requester WHERE rr.rid = ? AND c.rev = ?' :
$t eq 'p' ? 'pr.name AS title, u.username, c.comments FROM changes c JOIN producers_rev pr ON c.id = pr.id JOIN users u ON u.id = c.requester WHERE pr.pid = ? AND c.rev = ?' :
't.title, u.username, '.GETBOARDS.' 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 = ?'),
- [ $t, $id, $rev, $id, $rev], 'formatid', $dest
+ [ $t, $id, $rev, $id, $rev], 'formatid', [$dest]
) if $rev && $t =~ /[vprt]/;
# documentation page (need to parse the doc pages manually here)
@@ -545,7 +545,7 @@ sub vndbid { # dest, msg
}
close $F;
next if $rev && !$sub;
- $_[KERNEL]->yield(formatid => 1, [{type => 'd', id => $id, title => $title, rev => $rev, section => $sub}], $dest);
+ $_[KERNEL]->yield(formatid => 1, [{type => 'd', id => $id, title => $title, rev => $rev, section => $sub}], [$dest]);
}
}
}
@@ -561,7 +561,9 @@ sub vndbid { # dest, msg
# boards (optional) board titles the thread has been posted in
# comments (optional) edit summary
sub formatid {
- my($num, $res, $dest) = @_[ARG0..$#_];
+ my($num, $res, $arg) = @_[ARG0..$#_];
+ my($dest, $notify) = @$arg;
+ my $c = $notify ? LIGHT_BLUE : RED;
# only the types for which creation/edit announcements matter
my %types = (
@@ -577,11 +579,11 @@ sub formatid {
# (always) [x+.+]
my @msg = (
- BOLD.RED.'['.NORMAL.BOLD.$id.RED.']'.NORMAL
+ BOLD.$c.'['.NORMAL.BOLD.$id.$c.']'.NORMAL
);
# (only if username key is present) Edit of / New item / reply to / whatever
- push @msg, RED.(
+ push @msg, $c.(
($_->{rev}||1) == 1 ? 'New '.$types{$_->{type}} :
$_->{type} eq 't' ? 'Reply to' : 'Edit of'
).NORMAL if $_->{username};
@@ -590,22 +592,22 @@ sub formatid {
push @msg, $_->{title};
# (only if boards key is present) Posted in [boards]
- push @msg, RED.'Posted in'.NORMAL.' '.$_->{boards} if $_->{boards};
+ push @msg, $c.'Posted in'.NORMAL.' '.$_->{boards} if $_->{boards};
# (only if username key is present) By [username]
- push @msg, RED.'By'.NORMAL.' '.$_->{username} if $_->{username};
+ push @msg, $c.'By'.NORMAL.' '.$_->{username} if $_->{username};
# (only if comments key is present) Summary:
$_->{comments} =~ s/\n/ /g if $_->{comments};
- push @msg, RED.'Summary:'.NORMAL.' '.(
+ push @msg, $c.'Summary:'.NORMAL.' '.(
length $_->{comments} > 40 ? substr($_->{comments}, 0, 37).'...' : $_->{comments}
) if defined $_->{comments};
# (for d+.+) -> section title
- push @msg, RED.'->'.NORMAL.' '.$_->{section} if $_->{section};
+ push @msg, $c.'->'.NORMAL.' '.$_->{section} if $_->{section};
# (always) @ URL
- push @msg, RED.'@ '.NORMAL.LIGHT_GREY.$VNDB::S{url}.'/'.$id.NORMAL;
+ push @msg, $c.'@ '.NORMAL.LIGHT_GREY.$VNDB::S{url}.'/'.$id.NORMAL;
# now post it
$_[KERNEL]->yield(reply => $dest, join ' ', @msg);
diff --git a/lib/Multi/Maintenance.pm b/lib/Multi/Maintenance.pm
index 864239dd..be267aba 100644
--- a/lib/Multi/Maintenance.pm
+++ b/lib/Multi/Maintenance.pm
@@ -17,12 +17,12 @@ sub spawn {
package_states => [
$p => [qw|
_start shutdown set_daily daily set_monthly monthly log_stats
- vncache_inc tagcache vnpopularity vnrating cleangraphs
+ vncache_inc tagcache vnpopularity vnrating cleangraphs cleansessions
vncache_full usercache statscache logrotate
|],
],
heap => {
- daily => [qw|vncache_inc tagcache vnpopularity vnrating cleangraphs|],
+ daily => [qw|vncache_inc tagcache vnpopularity vnrating cleangraphs cleansessions|],
monthly => [qw|vncache_full usercache statscache logrotate|],
@_,
},
@@ -151,6 +151,13 @@ sub cleangraphs {
}
+sub cleansessions {
+ $_[KERNEL]->post(pg => do =>
+ q|DELETE FROM sessions WHERE lastused < NOW()-'1 month'::interval|,
+ undef, 'log_stats', 'cleansessions');
+}
+
+
#
# M O N T H L Y J O B S
#
diff --git a/lib/Multi/RG.pm b/lib/Multi/RG.pm
index 76408d92..d72bb516 100644
--- a/lib/Multi/RG.pm
+++ b/lib/Multi/RG.pm
@@ -97,7 +97,7 @@ sub getrel { # num, res, id
if(!grep !$_, values %{$_[HEAP]{nodes}}) {
my $ids = join(', ', map '?', keys %{$_[HEAP]{nodes}});
$_[KERNEL]->post(pg => query => $_[HEAP]{type} eq 'v'
- ? "SELECT v.id, vr.title, v.c_released AS date, v.c_languages AS lang FROM vn v JOIN vn_rev vr ON vr.id = v.latest WHERE v.id IN($ids) ORDER BY v.c_released"
+ ? "SELECT v.id, vr.title, v.c_released AS date, v.c_languages::text[] AS lang FROM vn v JOIN vn_rev vr ON vr.id = v.latest WHERE v.id IN($ids) ORDER BY v.c_released"
: "SELECT p.id, pr.name, pr.lang, pr.type FROM producers p JOIN producers_rev pr ON pr.id = p.latest WHERE p.id IN($ids) ORDER BY pr.name",
[ keys %{$_[HEAP]{nodes}} ], 'builddot');
}
@@ -252,7 +252,7 @@ sub _vnnode {
q|<TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="2"><FONT POINT-SIZE="%d"> %s </FONT></TD></TR>|.
q|<TR><TD> %s </TD><TD> %s </TD></TR>|.
qq|</TABLE>> ]\n|,
- $_->{id}, encode_utf8($tooltip), $heap->{fsize}[2], encode_utf8($title), $date, $n->{lang}||'N/A';
+ $_->{id}, encode_utf8($tooltip), $heap->{fsize}[2], encode_utf8($title), $date, join('/', @{$n->{lang}})||'N/A';
}
diff --git a/lib/SkinFile.pm b/lib/SkinFile.pm
new file mode 100644
index 00000000..78608f89
--- /dev/null
+++ b/lib/SkinFile.pm
@@ -0,0 +1,74 @@
+
+package SkinFile;
+
+use strict;
+use warnings;
+use Fcntl 'LOCK_SH', 'SEEK_SET';
+
+
+sub new {
+ my($class, $root, $open) = @_;
+ my $self = bless { root => $root }, $class;
+ $self->open($open) if $open;
+ return $self;
+}
+
+
+sub list {
+ return map /\/([^\/]+)\/conf/?$1:(), glob "$_[0]{root}/*/conf";
+}
+
+
+sub open {
+ my($self, $dir, $force) = @_;
+ return if $self->{"s_$dir"} && !$force;
+ my %o;
+ open my $F, '<:utf8', "$self->{root}/$dir/conf" or die $!;
+ flock $F, LOCK_SH or die $!;
+ seek $F, 0, SEEK_SET or die $!;
+ local $_;
+ while(<$F>) {
+ chomp;
+ s/\r//g;
+ s{[\t\s]*//.+$}{};
+ next if !/^([a-z0-9]+)[\t\s]+(.+)$/;
+ $o{$1} = $2;
+ }
+ close $F;
+ $self->{"s_$dir"} = \%o;
+ $self->{opened} = $dir;
+}
+
+
+sub get {
+ my($self, $dir, $var) = @_;
+ $self->open($dir) if defined $var;
+ $var = $dir if !defined $var;
+ $var ? $self->{"s_$self->{opened}"}{$var} : keys %{$self->{"s_$self->{opened}"}};
+}
+
+
+1;
+
+
+__END__
+
+=pod
+
+=head1 NAME
+
+SkinFile - Simple object oriented interface to parsing skin configuration files
+
+=head1 USAGE
+
+ use SkinFile;
+ my $s = SkinFile->new($dir);
+ my @skins = $s->list;
+
+ $s->open($skins[0]);
+ my $name = $s->get('name');
+
+ # same as above, but in one function
+ my $name = $s->get($skins[0], 'name');
+
+
diff --git a/lib/VNDB/DB/Discussions.pm b/lib/VNDB/DB/Discussions.pm
index 1c27c22e..9da25384 100644
--- a/lib/VNDB/DB/Discussions.pm
+++ b/lib/VNDB/DB/Discussions.pm
@@ -5,7 +5,7 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbThreadGet dbThreadEdit dbThreadAdd dbPostGet dbPostEdit dbPostAdd dbThreadCount dbPostRead|;
+our @EXPORT = qw|dbThreadGet dbThreadEdit dbThreadAdd dbPostGet dbPostEdit dbPostAdd dbThreadCount|;
# Options: id, type, iid, results, page, what, notusers, sort, reverse
@@ -252,15 +252,5 @@ sub dbPostAdd {
}
-sub dbPostRead { # thread id, user id, last post number
- my($s, $tid, $uid, $num) = @_;
- $s->dbExec(q|
- UPDATE threads_boards
- SET lastread = ?
- WHERE tid = ? AND type = 'u' AND iid = ?|,
- $num, $tid, $uid);
-}
-
-
1;
diff --git a/lib/VNDB/DB/Misc.pm b/lib/VNDB/DB/Misc.pm
index 02df0a2b..e3ef69b1 100644
--- a/lib/VNDB/DB/Misc.pm
+++ b/lib/VNDB/DB/Misc.pm
@@ -6,7 +6,7 @@ use warnings;
use Exporter 'import';
our @EXPORT = qw|
- dbStats dbItemEdit dbRevisionGet dbItemMod dbRandomQuote
+ dbStats dbItemEdit dbRevisionGet dbRandomQuote
|;
@@ -21,7 +21,7 @@ sub dbStats {
# Inserts a new revision into the database
-# Arguments: type [vrp], revision id, %options->{ editsum uid + db[item]RevisionInsert }
+# Arguments: type [vrp], revision id, %options->{ editsum uid ihid ilock + db[item]RevisionInsert }
# revision id = changes.id of the revision this edit is based on, undef to create a new DB item
# Returns: { iid, cid, rev }
sub dbItemEdit {
@@ -29,8 +29,13 @@ sub dbItemEdit {
my $fun = {qw|v vn r release p producer|}->{$type};
$self->dbExec('SELECT edit_!s_init(?)', $fun, $oid);
- $self->dbExec('UPDATE edit_revision SET requester = ?, ip = ?, comments = ?',
- $o{uid}||$self->authInfo->{id}, $self->reqIP, $o{editsum});
+ $self->dbExec('UPDATE edit_revision !H', {
+ 'requester = ?' => $o{uid}||$self->authInfo->{id},
+ 'ip = ?' => $self->reqIP,
+ 'comments = ?' => $o{editsum},
+ exists($o{ihid}) ? ('ihid = ?' => $o{ihid} ?1:0) : (),
+ exists($o{ilock}) ? ('ilock = ?' => $o{ilock}?1:0) : (),
+ });
$self->dbVNRevisionInsert( \%o) if $type eq 'v';
$self->dbProducerRevisionInsert(\%o) if $type eq 'p';
@@ -112,17 +117,6 @@ sub dbRevisionGet {
}
-# Lock or hide a DB item
-# arguments: v/r/p, id, %options ->( hidden, locked )
-sub dbItemMod {
- my($self, $type, $id, %o) = @_;
- $self->dbExec('UPDATE !s !H WHERE id = ?',
- {qw|v vn r releases p producers|}->{$type},
- { map { ($_.' = ?', int $o{$_}) } keys %o }, $id
- );
-}
-
-
# Returns a random quote (hashref with keys = vid, quote)
sub dbRandomQuote {
return $_[0]->dbRow(q|
diff --git a/lib/VNDB/DB/Producers.pm b/lib/VNDB/DB/Producers.pm
index f32f70db..5ec387cf 100644
--- a/lib/VNDB/DB/Producers.pm
+++ b/lib/VNDB/DB/Producers.pm
@@ -44,7 +44,7 @@ sub dbProducerGet {
my $select = 'p.id, pr.type, pr.name, pr.original, pr.lang, pr.id AS cid, p.rgraph';
$select .= ', pr.desc, pr.alias, pr.website, pr.l_wp, p.hidden, p.locked' if $o{what} =~ /extended/;
- $select .= q|, extract('epoch' from c.added) as added, c.requester, c.comments, p.latest, pr.id AS cid, u.username, c.rev| if $o{what} =~ /changes/;
+ $select .= q|, extract('epoch' from c.added) as added, c.requester, c.comments, p.latest, pr.id AS cid, u.username, c.rev, c.ihid, c.ilock| if $o{what} =~ /changes/;
$select .= ', pg.svg' if $o{what} =~ /relgraph/;
my($r, $np) = $self->dbPage(\%o, q|
diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm
index a2f62a63..cb8ad006 100644
--- a/lib/VNDB/DB/Releases.pm
+++ b/lib/VNDB/DB/Releases.pm
@@ -74,7 +74,7 @@ sub dbReleaseGet {
'rr.id AS cid',
$o{what} =~ /extended/ ? qw|rr.notes rr.catalog rr.gtin rr.resolution rr.voiced rr.freeware rr.doujin rr.ani_story rr.ani_ero r.hidden r.locked| : (),
$o{what} =~ /changes/ ?
- (qw|c.requester c.comments r.latest u.username c.rev|, q|extract('epoch' from c.added) as added|) : (),
+ (qw|c.requester c.comments r.latest u.username c.rev c.ihid c.ilock|, q|extract('epoch' from c.added) as added|) : (),
);
my $order = sprintf {
diff --git a/lib/VNDB/DB/ULists.pm b/lib/VNDB/DB/ULists.pm
index 28c4d572..19831593 100644
--- a/lib/VNDB/DB/ULists.pm
+++ b/lib/VNDB/DB/ULists.pm
@@ -65,7 +65,7 @@ sub dbVNListList {
# execute query
my($r, $np) = $self->dbPage(\%o, qq|
- SELECT vr.vid, vr.title, vr.original, v.c_released, v.c_languages, v.c_platforms, COALESCE(vo.vote, 0) AS vote
+ SELECT vr.vid, vr.title, vr.original, COALESCE(vo.vote, 0) AS vote
FROM vn v
JOIN vn_rev vr ON vr.id = v.latest
!s JOIN votes vo ON vo.vid = v.id AND vo.uid = ?
diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm
index f8fdfe3f..41f9f25a 100644
--- a/lib/VNDB/DB/Users.pm
+++ b/lib/VNDB/DB/Users.pm
@@ -5,11 +5,15 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel dbUserMessageCount dbSessionAdd dbSessionDel|;
+our @EXPORT = qw|
+ dbUserGet dbUserEdit dbUserAdd dbUserDel
+ dbSessionAdd dbSessionDel dbSessionUpdateLastUsed
+ dbNotifyGet dbNotifyMarkRead dbNotifyRemove
+|;
# %options->{ username passwd mail session uid ip registered search results page what sort reverse }
-# what: stats extended
+# what: notifycount stats extended
# sort: username registered votes changes tags
sub dbUserGet {
my $s = shift;
@@ -30,8 +34,10 @@ sub dbUserGet {
'ASCII(username) < 97 OR ASCII(username) > 122' => 1 ) : (),
$o{mail} ? (
'mail = ?' => $o{mail} ) : (),
- $o{uid} ? (
+ $o{uid} && !ref($o{uid}) ? (
'id = ?' => $o{uid} ) : (),
+ $o{uid} && ref($o{uid}) ? (
+ 'id IN(!l)' => [ $o{uid} ]) : (),
!$o{uid} && !$o{username} ? (
'id > 0' => 1 ) : (),
$o{ip} ? (
@@ -48,9 +54,11 @@ sub dbUserGet {
qw|id username c_votes c_changes show_list c_tags|,
q|extract('epoch' from registered) as registered|,
$o{what} =~ /extended/ ? (
- qw|mail rank salt skin customcss show_nsfw ign_votes|,
+ qw|mail rank salt skin customcss show_nsfw ign_votes notify_dbedit notify_announce|,
q|encode(passwd, 'hex') AS passwd|
) : (),
+ $o{what} =~ /notifycount/ ?
+ '(SELECT COUNT(*) FROM notifications WHERE uid = u.id AND read IS NULL) AS notifycount' : (),
$o{what} =~ /stats/ ? (
'(SELECT COUNT(*) FROM rlists WHERE uid = u.id) AS releasecount',
'(SELECT COUNT(DISTINCT rv.vid) FROM rlists rl JOIN releases r ON rl.rid = r.id JOIN releases_vn rv ON rv.rid = r.latest WHERE uid = u.id) AS vncount',
@@ -59,6 +67,7 @@ sub dbUserGet {
'(SELECT COUNT(DISTINCT tag) FROM tags_vn WHERE uid = u.id) AS tagcount',
'(SELECT COUNT(DISTINCT vid) FROM tags_vn WHERE uid = u.id) AS tagvncount',
) : (),
+ $o{session} ? q|extract('epoch' from s.lastused) as session_lastused| : (),
);
my @join = (
@@ -91,7 +100,7 @@ sub dbUserEdit {
my %h;
defined $o{$_} && ($h{$_.' = ?'} = $o{$_})
- for (qw| username mail rank show_nsfw show_list skin customcss salt ign_votes |);
+ for (qw| username mail rank show_nsfw show_list skin customcss salt ign_votes notify_dbedit notify_announce |);
$h{'passwd = decode(?, \'hex\')'} = $o{passwd}
if defined $o{passwd};
@@ -127,27 +136,10 @@ sub dbUserDel {
}
-# Returns number of unread messages
-sub dbUserMessageCount { # uid
- my($s, $uid) = @_;
- return $s->dbRow(q{
- SELECT SUM(tbi.count) AS cnt FROM (
- SELECT t.count-COALESCE(tb.lastread,0)
- FROM threads_boards tb
- JOIN threads t ON t.id = tb.tid AND NOT t.hidden
- WHERE tb.type = 'u' AND tb.iid = ?
- ) AS tbi (count)
- }, $uid)->{cnt}||0;
-}
-
-
# Adds a session to the database
-# If no expiration is supplied the database default is used
-# uid, 40 character session token, expiration time (timestamp)
+# uid, 40 character session token
sub dbSessionAdd {
- my($s, @o) = @_;
- $s->dbExec(q|INSERT INTO sessions (uid, token, expiration) VALUES(?, decode(?, 'hex'), to_timestamp(?))|,
- @o[0,1], $o[2]||(time+31536000));
+ $_[0]->dbExec(q|INSERT INTO sessions (uid, token) VALUES(?, decode(?, 'hex'))|, @_[1,2]);
}
@@ -162,5 +154,63 @@ sub dbSessionDel {
}
+# uid, token
+sub dbSessionUpdateLastUsed {
+ $_[0]->dbExec(q|UPDATE sessions SET lastused = NOW() WHERE uid = ? AND token = decode(?, 'hex')|, $_[1], $_[2]);
+}
+
+
+# %options->{ uid id what results page }
+# what: titles
+sub dbNotifyGet {
+ my($s, %o) = @_;
+ $o{what} ||= '';
+ $o{results} ||= 10;
+ $o{page} ||= 1;
+
+ my %where = (
+ 'n.uid = ?' => $o{uid},
+ $o{id} ? (
+ 'n.id = ?' => $o{id} ) : (),
+ defined($o{read}) ? (
+ 'n.read !s' => $o{read} ? 'IS NOT NULL' : 'IS NULL' ) : (),
+ );
+
+ my @join = (
+ $o{what} =~ /titles/ ? 'LEFT JOIN users u ON n.c_byuser = u.id' : (),
+ );
+
+ my @select = (
+ qw|n.id n.ntype n.ltype n.iid n.subid|,
+ q|extract('epoch' from n.date) as date|,
+ q|extract('epoch' from n.read) as read|,
+ $o{what} =~ /titles/ ? qw|u.username n.c_title| : (),
+ );
+
+ my($r, $np) = $s->dbPage(\%o, q|
+ SELECT !s
+ FROM notifications n
+ !s
+ !W
+ ORDER BY n.id
+ |, join(', ', @select), join(' ', @join), \%where);
+ return wantarray ? ($r, $np) : $r;
+}
+
+
+# ids
+sub dbNotifyMarkRead {
+ my $s = shift;
+ $s->dbExec('UPDATE notifications SET read = NOW() WHERE id IN(!l)', \@_);
+}
+
+
+# ids
+sub dbNotifyRemove {
+ my $s = shift;
+ $s->dbExec('DELETE FROM notifications WHERE id IN(!l)', \@_);
+}
+
+
1;
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index 6ff74d1f..cb1a878c 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -29,7 +29,7 @@ sub dbVNGet {
defined $o{char} && !$o{char} ? (
'(ASCII(vr.title) < 97 OR ASCII(vr.title) > 122) AND (ASCII(vr.title) < 65 OR ASCII(vr.title) > 90)' => 1 ) : (),
$o{lang} && @{$o{lang}} ? (
- '('.join(' OR ', map "v.c_languages ILIKE '%%$_%%'", @{$o{lang}}).')' => 1 ) : (),
+ 'v.c_languages && ARRAY[!l]::language[]' => [ $o{lang} ]) : (),
$o{platform} && @{$o{platform}} ? (
'('.join(' OR ', map "v.c_platforms ILIKE '%%$_%%'", @{$o{platform}}).')' => 1 ) : (),
$o{tags_include} && @{$o{tags_include}} ? (
@@ -81,12 +81,12 @@ sub dbVNGet {
);
my $tag_ids = $o{tags_include} && join ',', @{$o{tags_include}[1]};
- my @select = (
- qw|v.id v.locked v.hidden v.c_released v.c_languages v.c_platforms vr.title vr.original v.rgraph|, 'vr.id AS cid',
+ my @select = ( # see https://rt.cpan.org/Ticket/Display.html?id=54224 for the cast on c_languages
+ qw|v.id v.locked v.hidden v.c_released v.c_languages::text[] v.c_platforms vr.title vr.original v.rgraph|, 'vr.id AS cid',
$o{what} =~ /extended/ ? (
qw|vr.alias vr.image vr.img_nsfw vr.length vr.desc vr.l_wp vr.l_encubed vr.l_renai vr.l_vnn| ) : (),
$o{what} =~ /changes/ ? (
- qw|c.requester c.comments v.latest u.username c.rev|, q|extract('epoch' from c.added) as added|) : (),
+ qw|c.requester c.comments v.latest u.username c.rev c.ihid c.ilock|, q|extract('epoch' from c.added) as added|) : (),
$o{what} =~ /relgraph/ ? 'vg.svg' : (),
$o{what} =~ /rating/ ? (qw|v.c_popularity v.c_rating v.c_votecount|) : (),
$o{what} =~ /ranking/ ? (
diff --git a/lib/VNDB/Handler/Discussions.pm b/lib/VNDB/Handler/Discussions.pm
index fc477de2..c8431c2c 100644
--- a/lib/VNDB/Handler/Discussions.pm
+++ b/lib/VNDB/Handler/Discussions.pm
@@ -29,13 +29,7 @@ sub thread {
my $p = $self->dbPostGet(tid => $tid, results => 25, page => $page, what => 'user');
return 404 if !$p->[0];
- # mark as read when this thread is posted in the board of the currently logged in user
- my $uid = $self->authInfo->{id};
- $self->dbPostRead($t->{id}, $uid, $p->[$#$p]{num})
- if $uid && grep $_->{type} eq 'u' && $_->{iid} == $uid, @{$t->{boards}};
-
$self->htmlHeader(title => $t->{title});
-
div class => 'mainbox';
h1 $t->{title};
h2 mt '_thread_postedin';
diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm
index 625b0463..de4bdd67 100644
--- a/lib/VNDB/Handler/Misc.pm
+++ b/lib/VNDB/Handler/Misc.pm
@@ -13,8 +13,8 @@ YAWF::register(
qr{}, \&homepage,
qr{(?:([upvr])([1-9]\d*)/)?hist}, \&history,
qr{d([1-9]\d*)}, \&docpage,
+ qr{setlang}, \&setlang,
qr{nospam}, \&nospam,
- qr{([vrp])([1-9]\d*)/(lock|hide)}, \&itemmod,
qr{we-dont-like-ie6}, \&ie6message,
qr{opensearch\.xml}, \&opensearch,
@@ -63,7 +63,7 @@ sub homepage {
h1;
a href => '/hist', mt '_home_recentchanges';
end;
- my $changes = $self->dbRevisionGet(what => 'item user', results => 10, auto => 1, hidden => 1);
+ my $changes = $self->dbRevisionGet(what => 'item user', results => 10, auto => 1);
ul;
for (@$changes) {
li;
@@ -185,7 +185,7 @@ sub history {
my $f = $self->formValidate(
{ name => 'p', required => 0, default => 1, template => 'int' },
{ name => 'm', required => 0, default => !$type, enum => [ 0, 1 ] },
- { name => 'h', required => 0, default => 1, enum => [ -1..1 ] },
+ { name => 'h', required => 0, default => 0, enum => [ -1..1 ] },
{ name => 't', required => 0, default => '', enum => [ 'v', 'r', 'p' ] },
{ name => 'e', required => 0, default => 0, enum => [ -1..1 ] },
{ name => 'r', required => 0, default => 0, enum => [ 0, 1 ] },
@@ -289,24 +289,33 @@ sub docpage {
$sec++;
$subsec = 0;
qq|<h3><a href="#$sec" name="$sec">$sec. $1</a></h3>\n|
- }eg;
+ }e;
s{^:SUBSUB:(.+)\r?\n$}{
$subsec++;
qq|<h4><a href="#$sec.$subsec" name="$sec.$subsec">$sec.$subsec. $1</a></h4>\n|
- }eg;
+ }e;
s{^:INC:(.+)\r?\n$}{
$f = sprintf('%s/data/docs/%s', $VNDB::ROOT, $1);
open($F, '<:utf8', $f.$l) or open($F, '<:utf8', $f) or die $!;
my $ii = join('', <$F>);
close $F;
$ii;
- }eg;
+ }e;
s{^:TOP5CONTRIB:$}{
my $l = $self->dbUserGet(results => 6, sort => 'changes', reverse => 1);
'<dl>'.join('', map $_->{id} == 1 ? () :
sprintf('<dt><a href="/u%d">%s</a></dt><dd>%d</dd>', $_->{id}, $_->{username}, $_->{c_changes}),
@$l).'</dl>';
- }eg;
+ }e;
+ s{^:SKINCONTRIB:$}{
+ my %users;
+ push @{$users{ $self->{skins}{$_}[1] }}, [ $_, $self->{skins}{$_}[0] ]
+ for sort { $self->{skins}{$a}[0] cmp $self->{skins}{$b}[0] } keys %{$self->{skins}};
+ my $u = $self->dbUserGet(uid => [ keys %users ]);
+ '<dl>'.join('', map sprintf('<dt><a href="/u%d">%s</a></dt><dd>%s</dd>',
+ $_->{id}, $_->{username}, join(', ', map sprintf('<a href="?skin=%s">%s</a>', $_->[0], $_->[1]), @{$users{$_->{id}}})
+ ), @$u).'</dl>';
+ }e;
}
$self->htmlHeader(title => $title);
@@ -320,6 +329,20 @@ sub docpage {
}
+sub setlang {
+ my $self = shift;
+
+ my $lang = $self->formValidate({name => 'lang', required => 1, enum => [ VNDB::L10N::languages ]});
+ return 404 if $lang->{_err};
+ $lang = $lang->{lang};
+
+ (my $ref = $self->reqHeader('Referer')||'/') =~ s/^\Q$self->{url}//;
+ $self->resRedirect($ref, 'post');
+ $self->resHeader('Set-Cookie', "l10n=$lang; expires=Sat, 01-Jan-2030 00:00:00 GMT; path=/; domain=$self->{cookie_domain}")
+ if $lang ne $self->{l10n}->language_tag();
+}
+
+
sub nospam {
my $self = shift;
$self->htmlHeader(title => mt '_nospam_title', noindex => 1);
@@ -336,21 +359,6 @@ sub nospam {
}
-# /hide and /lock for v/r/p+ pages
-sub itemmod {
- my($self, $type, $iid, $act) = @_;
- return $self->htmlDenied if !$self->authCan($act eq 'hide' ? 'del' : 'lock');
-
- my $obj = $type eq 'v' ? $self->dbVNGet(id => $iid)->[0] :
- $type eq 'r' ? $self->dbReleaseGet(id => $iid, what => 'extended')->[0] :
- $self->dbProducerGet(id => $iid, what => 'extended')->[0];
- return 404 if !$obj->{id};
-
- $self->dbItemMod($type, $iid, $act eq 'hide' ? (hidden => !$obj->{hidden}) : (locked => !$obj->{locked}));
- $self->resRedirect("/$type$iid", 'temp');
-}
-
-
sub ie6message {
my $self = shift;
diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm
index 064a06bc..b1203dd7 100644
--- a/lib/VNDB/Handler/Producers.pm
+++ b/lib/VNDB/Handler/Producers.pm
@@ -155,7 +155,7 @@ sub edit {
|| $pid && ($p->{locked} && !$self->authCan('lock') || $p->{hidden} && !$self->authCan('del'));
my %b4 = !$pid ? () : (
- (map { $_ => $p->{$_} } qw|type name original lang website desc alias|),
+ (map { $_ => $p->{$_} } qw|type name original lang website desc alias ihid ilock|),
l_wp => $p->{l_wp} || '',
prodrelations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{name}, sort { $a->{id} <=> $b->{id} } @{$p->{relations}}),
);
@@ -173,12 +173,17 @@ sub edit {
{ name => 'desc', required => 0, maxlength => 5000, default => '' },
{ name => 'prodrelations', required => 0, maxlength => 5000, default => '' },
{ name => 'editsum', maxlength => 5000 },
+ { name => 'ihid', required => 0 },
+ { name => 'ilock', required => 0 },
);
if(!$frm->{_err}) {
# parse
my $relations = [ map { /^([a-z]+),([0-9]+),(.+)$/ && (!$pid || $2 != $pid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{prodrelations} ];
# normalize
+ $frm->{ihid} = $frm->{ihid}?1:0;
+ $frm->{ilock} = $frm->{ilock}?1:0;
+ $relations = [] if $frm->{ihid};
$frm->{prodrelations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations};
return $self->resRedirect("/p$pid", 'post')
diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm
index 498b9fa9..55bc9778 100644
--- a/lib/VNDB/Handler/Releases.pm
+++ b/lib/VNDB/Handler/Releases.pm
@@ -287,7 +287,7 @@ sub edit {
my $vn = $rid ? $r->{vn} : [{ vid => $vid, title => $v->{title} }];
my %b4 = !$rid ? () : (
(map { $_ => $r->{$_} } qw|type title original gtin catalog languages website released
- notes platforms patch resolution voiced freeware doujin ani_story ani_ero|),
+ notes platforms patch resolution voiced freeware doujin ani_story ani_ero ihid ilock|),
minage => defined($r->{minage}) ? $r->{minage} : -1,
media => join(',', sort map "$_->{medium} $_->{qty}", @{$r->{media}}),
producers => join('|||', map
@@ -323,8 +323,12 @@ sub edit {
{ name => 'producers', required => 0, default => '' },
{ name => 'vn', maxlength => 5000 },
{ name => 'editsum', maxlength => 5000 },
+ { name => 'ihid', required => 0 },
+ { name => 'ilock', required => 0 },
);
+ push @{$frm->{_err}}, [ 'released', 'required', 1 ] if !$frm->{released};
+
my($media, $producers, $new_vn);
if(!$frm->{_err}) {
# de-serialize
@@ -332,7 +336,7 @@ sub edit {
$producers = [ map { /^([0-9]+),([1-3])/ ? [ $1, $2&1?1:0, $2&2?1:0] : () } split /\|\|\|/, $frm->{producers} ];
$new_vn = [ map { /^([0-9]+)/ ? $1 : () } split /\|\|\|/, $frm->{vn} ];
$frm->{platforms} = [ grep $_, @{$frm->{platforms}} ];
- $frm->{$_} = $frm->{$_} ? 1 : 0 for (qw|patch freeware doujin|);
+ $frm->{$_} = $frm->{$_} ? 1 : 0 for (qw|patch freeware doujin ihid ilock|);
# reset some fields when the patch flag is set
$frm->{doujin} = $frm->{resolution} = $frm->{voiced} = $frm->{ani_story} = $frm->{ani_ero} = 0 if $frm->{patch};
@@ -350,7 +354,7 @@ sub edit {
if(!$frm->{_err}) {
my $nrev = $self->dbItemEdit(r => !$copy && $rid ? $r->{cid} : undef,
(map { $_ => $frm->{$_} } qw| type title original gtin catalog languages website released
- notes platforms resolution editsum patch voiced freeware doujin ani_story ani_ero|),
+ notes platforms resolution editsum patch voiced freeware doujin ani_story ani_ero ihid ilock|),
minage => $frm->{minage} < 0 ? undef : $frm->{minage},
vn => $new_vn,
producers => $producers,
diff --git a/lib/VNDB/Handler/Tags.pm b/lib/VNDB/Handler/Tags.pm
index 612b603d..aa46f5b9 100644
--- a/lib/VNDB/Handler/Tags.pm
+++ b/lib/VNDB/Handler/Tags.pm
@@ -191,7 +191,7 @@ sub tagedit {
{ name => 'meta', required => 0, default => 0 },
{ name => 'alias', required => 0, maxlength => 1024, default => '', regex => [ qr/^[^,]+$/s, 'No comma allowed in aliases' ] },
{ name => 'description', required => 0, maxlength => 1024, default => '' },
- { name => 'parents', required => 0, default => '' },
+ { name => 'parents', required => !$self->authCan('tagmod'), default => '' },
{ name => 'merge', required => 0, default => '' },
);
my @aliases = split /[\t\s]*\n[\t\s]*/, $frm->{alias};
diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm
index 2cec8b52..f68f9db9 100644
--- a/lib/VNDB/Handler/Users.pm
+++ b/lib/VNDB/Handler/Users.pm
@@ -3,7 +3,7 @@ package VNDB::Handler::Users;
use strict;
use warnings;
-use YAWF ':html';
+use YAWF ':html', 'xml_escape';
use VNDB::Func;
@@ -18,6 +18,8 @@ YAWF::register(
qr{u([1-9]\d*)/posts} => \&posts,
qr{u([1-9]\d*)/del(/[od])?} => \&delete,
qr{u/(all|[0a-z])} => \&list,
+ qr{u([1-9]\d*)/notifies} => \&notifies,
+ qr{u([1-9]\d*)/notify/([1-9]\d*)} => \&readnotify,
);
@@ -113,7 +115,7 @@ sub userpage {
}
if($u->{c_changes}) {
- my $list = $self->dbRevisionGet(what => 'item user', uid => $uid, results => 5, hidden => 1);
+ my $list = $self->dbRevisionGet(what => 'item user', uid => $uid, results => 5);
h1 class => 'boxtitle';
a href => "/u$uid/hist", mt '_userpage_changes';
end;
@@ -346,7 +348,7 @@ sub edit {
[ check => short => 'flags_list', name => mt '_usere_flist', "/u$uid/list", "/u$uid/wish" ],
[ check => short => 'flags_nsfw', name => mt '_usere_fnsfw' ],
[ select => short => 'skin', name => mt('_usere_skin'), width => 300, options => [
- map [ $_ eq $self->{skin_default} ? '' : $_, $self->{skins}{$_}.($self->debug?" [$_]":'') ], sort { $self->{skins}{$a} cmp $self->{skins}{$b} } keys %{$self->{skins}} ] ],
+ map [ $_ eq $self->{skin_default} ? '' : $_, $self->{skins}{$_}[0].($self->debug?" [$_]":'') ], sort { $self->{skins}{$a}[0] cmp $self->{skins}{$b}[0] } keys %{$self->{skins}} ] ],
[ textarea => short => 'customcss', name => mt '_usere_css' ],
]);
$self->htmlFooter;
@@ -519,5 +521,140 @@ sub list {
}
+sub notifies {
+ my($self, $uid) = @_;
+ return $self->htmlDenied if !$self->authInfo->{id} || $uid != $self->authInfo->{id};
+
+ my $u = $self->dbUserGet(uid => $uid)->[0];
+
+ my $f = $self->formValidate(
+ { name => 'p', required => 0, default => 1, template => 'int' },
+ { name => 'r', required => 0, default => 0, enum => [0,1] },
+ );
+ return 404 if $f->{_err};
+
+ # changing the notification settings
+ my $saved;
+ if($self->reqMethod() eq 'POST' && $self->reqParam('set')) {
+ my $frm = $self->formValidate(
+ { name => 'notify_dbedit', required => 0 },
+ { name => 'notify_announce', required => 0 }
+ );
+ return 404 if $frm->{_err};
+ for ('notify_dbedit', 'notify_announce') {
+ $frm->{$_} = $frm->{$_} ? 1 : 0;
+ $self->authInfo->{$_} = $frm->{$_};
+ }
+ $self->dbUserEdit($uid, %$frm);
+ $saved = 1;
+
+ # updating notifications
+ } elsif($self->reqMethod() eq 'POST') {
+ my $frm = $self->formValidate(
+ { name => 'notifysel', multi => 1, required => 0, template => 'int' },
+ { name => 'markread', required => 0 },
+ { name => 'remove', required => 0 }
+ );
+ return 404 if $frm->{_err};
+ my @ids = grep $_, @{$frm->{notifysel}};
+ $self->dbNotifyMarkRead(@ids) if @ids && $frm->{markread};
+ $self->dbNotifyRemove(@ids) if @ids && $frm->{remove};
+ }
+
+ my($list, $np) = $self->dbNotifyGet(
+ uid => $uid,
+ page => $f->{p},
+ results => 25,
+ what => 'titles',
+ read => $f->{r} == 1 ? undef : 0,
+ );
+
+ $self->htmlHeader(title => mt('_usern_title'), noindex => 1);
+ $self->htmlMainTabs(u => $u);
+ div class => 'mainbox';
+ h1 mt '_usern_title';
+ p class => 'browseopts';
+ a !$f->{r} ? (class => 'optselected') : (), href => "/u$uid/notifies?r=0", mt '_usern_o_unread';
+ a $f->{r} ? (class => 'optselected') : (), href => "/u$uid/notifies?r=1", mt '_usern_o_alsoread';
+ end;
+ p mt '_usern_nonotifies' if !@$list;
+ end;
+
+ if(@$list) {
+ form action => "/u$uid/notifies?r=$f->{r}", method => 'post';
+ $self->htmlBrowse(
+ items => $list,
+ options => $f,
+ nextpage => $np,
+ class => 'notifies',
+ pageurl => "/u$uid/notifies?r=$f->{r}",
+ header => [
+ [ '<input type="checkbox" class="checkall" name="notifysel" value="0" />' ],
+ [ mt '_usern_col_type' ],
+ [ mt '_usern_col_age' ],
+ [ mt '_usern_col_id' ],
+ [ mt '_usern_col_act' ],
+ ],
+ row => sub {
+ my($s, $n, $l) = @_;
+ Tr class => join ' ', $n%2?'odd':'', $l->{read}?'':'unread';
+ td class => 'tc1';
+ input type => 'checkbox', name => 'notifysel', value => "$l->{id}";
+ end;
+ td class => 'tc2', mt "_usern_type_$l->{ntype}";
+ td class => 'tc3', $self->{l10n}->age($l->{date});
+ td class => 'tc4';
+ a href => "/u$uid/notify/$l->{id}", "$l->{ltype}$l->{iid}".($l->{subid}?".$l->{subid}":'');
+ end;
+ td class => 'tc5', onclick => qq|javascript:location.href="/u$uid/notify/$l->{id}"|;
+ lit mt '_usern_n_'.(
+ $l->{ltype} eq 't' ? ($l->{subid} == 1 ? 't_new' : 't_reply')
+ : 'item_edit'),
+ sprintf('<i>%s</i>', xml_escape $l->{c_title}), sprintf('<i>%s</i>', xml_escape $l->{username});
+ end;
+ end;
+ },
+ footer => sub {
+ Tr;
+ td colspan => 5;
+ input type => 'submit', name => 'markread', value => mt '_usern_but_markread';
+ input type => 'submit', name => 'remove', value => mt '_usern_but_remove';
+ end;
+ end;
+ }
+ );
+ end;
+ }
+
+ form method => 'post', action => "/u$uid/notifies";
+ div class => 'mainbox';
+ h1 mt '_usern_set_title';
+ div class => 'notice', mt '_usern_set_saved' if $saved;
+ p;
+ for('dbedit', 'announce') {
+ input type => 'checkbox', name => "notify_$_", id => "notify_$_", value => 1,
+ $self->authInfo->{"notify_$_"} ? (checked => 'checked') : ();
+ label for => "notify_$_", ' '.mt("_usern_set_$_");
+ br;
+ }
+ input type => 'submit', name => 'set', value => mt '_usern_set_submit';
+ end;
+ end;
+ end;
+ $self->htmlFooter;
+}
+
+
+sub readnotify {
+ my($self, $uid, $nid) = @_;
+ return $self->htmlDenied if !$self->authInfo->{id} || $uid != $self->authInfo->{id};
+ my $n = $self->dbNotifyGet(uid => $uid, id => $nid)->[0];
+ return 404 if !$n->{iid};
+ $self->dbNotifyMarkRead($n->{id}) if !$n->{read};
+ # NOTE: for t+.+ IDs, this will create a double redirect, which is rather awkward...
+ $self->resRedirect("/$n->{ltype}$n->{iid}".($n->{subid}?".$n->{subid}":''), 'perm');
+}
+
+
1;
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index d5dfd878..60b24272 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -26,7 +26,7 @@ sub edit {
|| $vid && ($v->{locked} && !$self->authCan('lock') || $v->{hidden} && !$self->authCan('del'));
my %b4 = !$vid ? () : (
- (map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai l_vnn img_nsfw|),
+ (map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai l_vnn img_nsfw ihid ilock|),
anime => join(' ', sort { $a <=> $b } map $_->{id}, @{$v->{anime}}),
vnrelations => join('|||', map $_->{relation}.','.$_->{id}.','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}),
screenshots => join(' ', map sprintf('%d,%d,%d', $_->{id}, $_->{nsfw}?1:0, $_->{rid}), @{$v->{screenshots}}),
@@ -49,6 +49,8 @@ sub edit {
{ name => 'vnrelations', required => 0, default => '', maxlength => 5000 },
{ name => 'screenshots', required => 0, default => '', maxlength => 1000 },
{ name => 'editsum', maxlength => 5000 },
+ { name => 'ihid', required => 0 },
+ { name => 'ilock', required => 0 },
);
# handle image upload
@@ -60,6 +62,9 @@ sub edit {
my $relations = [ map { /^([a-z]+),([0-9]+),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3 ] : () } split /\|\|\|/, $frm->{vnrelations} ];
my $screenshots = [ map /^[0-9]+,[01],[0-9]+$/ ? [split /,/] : (), split / +/, $frm->{screenshots} ];
+ $frm->{ihid} = $frm->{ihid}?1:0;
+ $frm->{ilock} = $frm->{ilock}?1:0;
+ $relations = [] if $frm->{ihid};
$frm->{anime} = join ' ', sort { $a <=> $b } keys %$anime;
$frm->{vnrelations} = join '|||', map $_->[0].','.$_->[1].','.$_->[2], sort { $a->[1] <=> $b->[1]} @{$relations};
$frm->{img_nsfw} = $frm->{img_nsfw} ? 1 : 0;
@@ -71,7 +76,7 @@ sub edit {
# perform the edit/add
my $nrev = $self->dbItemEdit(v => $vid ? $v->{cid} : undef,
- (map { $_ => $frm->{$_} } qw|title original alias desc length l_wp l_encubed l_renai l_vnn editsum img_nsfw|),
+ (map { $_ => $frm->{$_} } qw|title original alias desc length l_wp l_encubed l_renai l_vnn editsum img_nsfw ihid ilock|),
anime => [ keys %$anime ],
relations => $relations,
image => $image,
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index 6c2e9b79..21655e37 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -455,30 +455,24 @@ sub _screenshots {
}
h1 mt '_vnpage_scr';
- table;
- for my $rel (@$r) {
- my @scr = grep $_->{rid} && $rel->{id} == $_->{rid}, @{$v->{screenshots}};
- next if !@scr;
- Tr class => 'rel';
- td colspan => 5;
- cssicon "lang $_", mt "_lang_$_" for (@{$rel->{languages}});
- txt $rel->{title};
- end;
- end;
- Tr;
- td class => 'scr';
- for (@scr) {
- div $_->{nsfw} ? (class => 'nsfw'.(!$self->authInfo->{show_nsfw} ? ' hidden' : '')) : ();
- a href => sprintf('%s/sf/%02d/%d.jpg', $self->{url_static}, $_->{id}%100, $_->{id}),
- rel => "iv:$_->{width}x$_->{height}:scr", $_->{nsfw} && !$self->authInfo->{show_nsfw} ? (class => 'hidden') : ();
- img src => sprintf('%s/st/%02d/%d.jpg', $self->{url_static}, $_->{id}%100, $_->{id}), alt => mt '_vnpage_scr_num', $_->{id};
- end;
- end;
- }
- end;
- end;
- }
- end;
+
+ for my $rel (@$r) {
+ my @scr = grep $_->{rid} && $rel->{id} == $_->{rid}, @{$v->{screenshots}};
+ next if !@scr;
+ p class => 'rel';
+ cssicon "lang $_", mt "_lang_$_" for (@{$rel->{languages}});
+ a href => "/r$rel->{id}", $rel->{title};
+ end;
+ div class => 'scr';
+ for (@scr) {
+ a href => sprintf('%s/sf/%02d/%d.jpg', $self->{url_static}, $_->{id}%100, $_->{id}),
+ class => sprintf('scrlnk%s%s', $_->{nsfw} ? ' nsfw':'', $_->{nsfw}&&!$self->authInfo->{show_nsfw}?' hidden':''),
+ rel => "iv:$_->{width}x$_->{height}:scr";
+ img src => sprintf('%s/st/%02d/%d.jpg', $self->{url_static}, $_->{id}%100, $_->{id}), alt => mt '_vnpage_scr_num', $_->{id};
+ end;
+ }
+ end;
+ }
end;
}
diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm
index 19a58a0f..2d133476 100644
--- a/lib/VNDB/Util/Auth.pm
+++ b/lib/VNDB/Util/Auth.pm
@@ -25,7 +25,9 @@ sub authInit {
return _rmcookie($self) if length($cookie) < 41;
my $token = substr($cookie, 0, 40);
my $uid = substr($cookie, 40);
- $self->{_auth} = $uid =~ /^\d+$/ && $self->dbUserGet(uid => $uid, session => $token, what => 'extended')->[0];
+ $self->{_auth} = $uid =~ /^\d+$/ && $self->dbUserGet(uid => $uid, session => $token, what => 'extended notifycount')->[0];
+ # update the sessions.lastused column if lastused < now()'6 hours'
+ $self->dbSessionUpdateLastUsed($uid, $token) if $self->{_auth} && $self->{_auth}{session_lastused} < time()-6*3600;
return _rmcookie($self) if !$self->{_auth};
}
@@ -40,11 +42,10 @@ sub authLogin {
if(_authCheck($self, $user, $pass)) {
my $token = sha1_hex(join('', Time::HiRes::gettimeofday()) . join('', map chr(rand(93)+33), 1..9));
- my $expiration = time + 31536000; # 1yr
my $cookie = $token . $self->{_auth}{id};
- $self->dbSessionAdd($self->{_auth}{id}, $token, $expiration);
+ $self->dbSessionAdd($self->{_auth}{id}, $token);
- my $expstr = strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($expiration));
+ my $expstr = strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime(time + 31536000)); # keep the cookie for 1 year
$self->resRedirect($to, 'post');
$self->resHeader('Set-Cookie', "vndb_auth=$cookie; expires=$expstr; path=/; domain=$self->{cookie_domain}");
return 1;
@@ -95,7 +96,7 @@ sub _authCheck {
return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass;
- my $d = $self->dbUserGet(username => $user, what => 'extended')->[0];
+ my $d = $self->dbUserGet(username => $user, what => 'extended notifycount')->[0];
return 0 if !defined $d->{id} || !$d->{rank};
if(_authEncryptPass($self, $pass, $d->{salt}) eq $d->{passwd}) {
diff --git a/lib/VNDB/Util/BrowseHTML.pm b/lib/VNDB/Util/BrowseHTML.pm
index e9812f36..cc296cc7 100644
--- a/lib/VNDB/Util/BrowseHTML.pm
+++ b/lib/VNDB/Util/BrowseHTML.pm
@@ -184,7 +184,7 @@ sub htmlBrowseVN {
end;
td class => 'tc3';
cssicon "lang $_", mt "_lang_$_"
- for (reverse sort split /\//, $l->{c_languages});
+ for (reverse sort @{$l->{c_languages}});
end;
td class => 'tc4';
lit $self->{l10n}->datestr($l->{c_released});
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index 79330272..be56e4a8 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -83,18 +83,6 @@ sub htmlMainTabs {
end;
}
- if($type =~ /[vrp]/ && $self->authCan('del')) {
- li;
- a href => "/$id/hide", mt $obj->{hidden} ? '_mtabs_unhide' : '_mtabs_hide';
- end;
- }
-
- if($type =~ /[vrp]/ && $self->authCan('lock')) {
- li;
- a href => "/$id/lock", mt $obj->{locked} ? '_mtabs_unlock' : '_mtabs_lock';
- end;
- }
-
if($type eq 'u' && $self->authCan('usermod')) {
li $sel eq 'del' ? (class => 'tabselected') : ();
a href => "/$id/del", mt '_mtabs_del';
@@ -141,12 +129,18 @@ sub htmlHiddenMessage {
my($self, $type, $obj) = @_;
return 0 if !$obj->{hidden};
my $board = $type eq 'r' ? 'v'.$obj->{vn}[0]{vid} : $type.$obj->{id};
+ # fetch edit summary (not present in $obj because the changes aren't fetched)
+ my $editsum = $type eq 'v' ? $self->dbVNGet(id => $obj->{id}, what => 'changes')->[0]{comments}
+ : $type eq 'r' ? $self->dbReleaseGet(id => $obj->{id}, what => 'changes')->[0]{comments}
+ : $self->dbProducerGet(id => $obj->{id}, what => 'changes')->[0]{comments};
div class => 'mainbox';
h1 $obj->{title}||$obj->{name};
div class => 'warning';
h2 mt '_hiddenmsg_title';
p;
lit mt '_hiddenmsg_msg', "/t/$board";
+ br; br;
+ lit bb2html $editsum;
end;
end;
end;
@@ -209,7 +203,11 @@ sub htmlRevision {
end;
end;
my $i = 1;
- revdiff(\$i, $type, $old, $new, @$_) for (@fields);
+ revdiff(\$i, $type, $old, $new, @$_) for (
+ [ ihid => serialize => sub { mt $_[0] ? '_revision_yes' : '_revision_no' } ],
+ [ ilock => serialize => sub { mt $_[0] ? '_revision_yes' : '_revision_no' } ],
+ @fields
+ );
end;
}
end;
@@ -262,7 +260,7 @@ sub revdiff {
$ser2 = mt '_revision_empty' if !$ser2 && $ser2 ne '0';
Tr $$i++ % 2 ? (class => 'odd') : ();
- td mt "_revfield_${type}_$short";
+ td mt $short eq 'ihid' || $short eq 'ilock' ? "_revfield_$short" : "_revfield_${type}_$short";
td class => 'tcval'; lit $ser1; end;
td class => 'tcval'; lit $ser2; end;
end;
@@ -399,20 +397,13 @@ sub htmlVoteStats {
sub htmlSearchBox {
my($self, $sel, $v) = @_;
- # escape search query for use as a query string value
- (my $q = $v||'') =~ s/&/%26/g;
- $q =~ s/\?/%3F/g;
- $q =~ s/;/%3B/g;
- $q =~ s/ /%20/g;
- $q = "?q=$q" if $q;
-
fieldset class => 'search';
- p class => 'searchtabs';
- a href => "/v/all$q", $sel eq 'v' ? (class => 'sel') : (), mt '_searchbox_vn';
- a href => "/r$q", $sel eq 'r' ? (class => 'sel') : (), mt '_searchbox_releases';
- a href => "/p/all$q", $sel eq 'p' ? (class => 'sel') : (), mt '_searchbox_producers';
- a href => '/g'.($q?"/list$q":''), $sel eq 'g' ? (class => 'sel') : (), mt '_searchbox_tags';
- a href => "/u/all$q", $sel eq 'u' ? (class => 'sel') : (), mt '_searchbox_users';
+ p id => 'searchtabs';
+ a href => '/v/all', $sel eq 'v' ? (class => 'sel') : (), mt '_searchbox_vn';
+ a href => '/r', $sel eq 'r' ? (class => 'sel') : (), mt '_searchbox_releases';
+ a href => '/p/all', $sel eq 'p' ? (class => 'sel') : (), mt '_searchbox_producers';
+ a href => '/g', $sel eq 'g' ? (class => 'sel') : (), mt '_searchbox_tags';
+ a href => '/u/all', $sel eq 'u' ? (class => 'sel') : (), mt '_searchbox_users';
end;
input type => 'text', name => 'q', id => 'q', class => 'text', value => $v;
input type => 'submit', class => 'submit', value => mt '_searchbox_submit';
diff --git a/lib/VNDB/Util/FormHTML.pm b/lib/VNDB/Util/FormHTML.pm
index 2882b4c4..cfac77b9 100644
--- a/lib/VNDB/Util/FormHTML.pm
+++ b/lib/VNDB/Util/FormHTML.pm
@@ -209,11 +209,23 @@ sub htmlForm {
end;
}
- # edit summary / submit button
+ # db mod / edit summary / submit button
if(!$options->{nosubmit}) {
div class => 'mainbox';
fieldset class => 'submit';
if($options->{editsum}) {
+ # hidden / locked checkbox
+ if($self->authCan('del')) {
+ input type => 'checkbox', name => 'ihid', id => 'ihid', value => 1, $options->{frm}{ihid} ? (checked => 'checked') : ();
+ label for => 'ihid', mt '_form_ihid';
+ }
+ if($self->authCan('lock')) {
+ input type => 'checkbox', name => 'ilock', id => 'ilock', value => 1, $options->{frm}{ilock} ? (checked => 'checked') : ();
+ label for => 'ilock', mt '_form_ilock';
+ }
+ txt "\n".mt('_form_hidlock_note')."\n" if $self->authCan('lock') || $self->authCan('del');
+
+ # edit summary
(my $txt = $options->{frm}{editsum}||'') =~ s/&/&amp;/;
$txt =~ s/</&lt;/;
$txt =~ s/>/&gt;/;
diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm
index ba330790..eb086bcd 100644
--- a/lib/VNDB/Util/LayoutHTML.pm
+++ b/lib/VNDB/Util/LayoutHTML.pm
@@ -77,18 +77,17 @@ sub _menu {
div class => 'menubox';
if($self->authInfo->{id}) {
- my $msg = $self->dbUserMessageCount($self->authInfo->{id});
my $uid = sprintf '/u%d', $self->authInfo->{id};
+ my $nc = $self->authInfo->{notifycount};
h2;
a href => $uid, ucfirst $self->authInfo->{username};
- # note: user ranks aren't TL'ed (but might be in the future, hmm)
txt ' ('.mt('_urank_'.$self->authInfo->{rank}).')';
end;
div;
a href => "$uid/edit", mt '_menu_myprofile'; br;
a href => "$uid/list", mt '_menu_myvnlist'; br;
a href => "$uid/wish", mt '_menu_mywishlist'; br;
- a href => "/t$uid", $msg ? (class => 'standout') : (), mt '_menu_mymessages', $msg; br;
+ a href => "$uid/notifies", $nc ? (class => 'notifyget') : (), mt('_menu_mynotifications').($nc?" ($nc)":''); br;
a href => "$uid/hist", mt '_menu_mychanges'; br;
a href => "$uid/tags", mt '_menu_mytags'; br;
br;