package VNDB::Handler::Tags;
use strict;
use warnings;
use TUWF ':html', ':xml', 'xml_escape';
use VNDB::Func;
TUWF::register(
qr{g([1-9]\d*)}, \&tagpage,
qr{g([1-9]\d*)/(edit)}, \&tagedit,
qr{g([1-9]\d*)/(add)}, \&tagedit,
qr{g/new}, \&tagedit,
qr{g/list}, \&taglist,
qr{g/links}, \&taglinks,
qr{v([1-9]\d*)/tagmod}, \&vntagmod,
qr{u([1-9]\d*)/tags}, \&usertags,
qr{g}, \&tagindex,
qr{g/debug}, \&fulltree,
qr{xml/tags\.xml}, \&tagxml,
);
sub tagpage {
my($self, $tag) = @_;
my $t = $self->dbTagGet(id => $tag, what => 'parents(0) childs(2) aliases')->[0];
return $self->resNotFound if !$t;
my $f = $self->formValidate(
{ get => 's', required => 0, default => 'tagscore', enum => [ qw|title rel pop tagscore rating| ] },
{ get => 'o', required => 0, default => 'd', enum => [ 'a','d' ] },
{ get => 'p', required => 0, default => 1, template => 'int' },
{ get => 'm', required => 0, default => -1, enum => [qw|0 1 2|] },
{ get => 'fil', required => 0 },
);
return $self->resNotFound if $f->{_err};
my $tagspoil = $self->reqCookie('tagspoil')||'';
$f->{m} = $tagspoil =~ /^[0-2]$/ ? $tagspoil : 0 if $f->{m} == -1;
$f->{fil} //= $self->authPref('filter_vn');
my($list, $np) = $t->{meta} || $t->{state} != 2 ? ([],0) : $self->filFetchDB(vn => $f->{fil}, undef, {
what => 'rating',
results => 50,
page => $f->{p},
sort => $f->{s}, reverse => $f->{o} eq 'd',
tagspoil => $f->{m},
tag_inc => $tag,
tag_exc => undef,
});
my $title = mt '_tagp_title', $t->{meta}?0:1, $t->{name};
$self->htmlHeader(title => $title, noindex => $t->{state} != 2);
$self->htmlMainTabs('g', $t);
if($t->{state} != 2) {
div class => 'mainbox';
h1 $title;
if($t->{state} == 1) {
div class => 'warning';
h2 mt '_tagp_del_title';
p;
lit mt '_tagp_del_msg';
end;
end;
} else {
div class => 'notice';
h2 mt '_tagp_pending_title';
p mt '_tagp_pending_msg';
end;
}
end 'div';
}
div class => 'mainbox';
a class => 'addnew', href => "/g$tag/add", mt '_tagp_addchild' if $self->authCan('tag') && $t->{state} != 1;
h1 $title;
parenttags($t, mt('_tagp_indexlink'), 'g');
if($t->{description}) {
p class => 'description';
lit bb2html $t->{description};
end;
}
p class => 'center';
b mt('_tagp_cat');
br;
txt mt("_tagcat_$t->{cat}");
end;
if(@{$t->{aliases}}) {
p class => 'center';
b mt('_tagp_aliases');
br;
lit xml_escape($_).'
' for (@{$t->{aliases}});
end;
}
end 'div';
childtags($self, mt('_tagp_childs'), 'g', $t) if @{$t->{childs}};
if(!$t->{meta} && $t->{state} == 2) {
form action => "/g$t->{id}", 'accept-charset' => 'UTF-8', method => 'get';
div class => 'mainbox';
a class => 'addnew', href => "/g/links?t=$tag", mt '_tagp_rawvotes';
h1 mt '_tagp_vnlist';
p class => 'browseopts';
a href => "/g$t->{id}?fil=$f->{fil};m=0", $f->{m} == 0 ? (class => 'optselected') : (), onclick => "setCookie('tagspoil', 0);return true;", mt '_tagp_spoil0';
a href => "/g$t->{id}?fil=$f->{fil};m=1", $f->{m} == 1 ? (class => 'optselected') : (), onclick => "setCookie('tagspoil', 1);return true;", mt '_tagp_spoil1';
a href => "/g$t->{id}?fil=$f->{fil};m=2", $f->{m} == 2 ? (class => 'optselected') : (), onclick => "setCookie('tagspoil', 2);return true;", mt '_tagp_spoil2';
end;
a id => 'filselect', href => '#v';
lit '▸ '.mt('_js_fil_filters').'';
end;
input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil};
if(!@$list) {
p; br; br; txt mt '_tagp_novn'; end;
}
p; br; txt mt '_tagp_cached'; end;
end 'div';
end 'form';
$self->htmlBrowseVN($list, $f, $np, "/g$t->{id}?fil=$f->{fil};m=$f->{m}", 1) if @$list;
}
$self->htmlFooter(prefs => ['filter_vn']);
}
sub tagedit {
my($self, $tag, $act) = @_;
my($frm, $par);
if($act && $act eq 'add') {
$par = $self->dbTagGet(id => $tag)->[0];
return $self->resNotFound if !$par;
$frm->{parents} = $par->{name};
$tag = undef;
}
return $self->htmlDenied if !$self->authCan('tag') || $tag && !$self->authCan('tagmod');
my $t = $tag && $self->dbTagGet(id => $tag, what => 'parents(1) aliases addedby')->[0];
return $self->resNotFound if $tag && !$t;
if($self->reqMethod eq 'POST') {
return if !$self->authCheckCode;
$frm = $self->formValidate(
{ post => 'name', required => 1, maxlength => 250, regex => [ qr/^[^,]+$/, 'A comma is not allowed in tag names' ] },
{ post => 'state', required => 0, default => 0, enum => [ 0..2 ] },
{ post => 'cat', required => 1, enum => $self->{tag_categories} },
{ post => 'catrec', required => 0 },
{ post => 'meta', required => 0, default => 0 },
{ post => 'alias', required => 0, maxlength => 1024, default => '', regex => [ qr/^[^,]+$/s, 'No comma allowed in aliases' ] },
{ post => 'description', required => 0, maxlength => 10240, default => '' },
{ post => 'parents', required => !$self->authCan('tagmod'), default => '' },
{ post => 'merge', required => 0, default => '' },
);
my @aliases = split /[\t\s]*\n[\t\s]*/, $frm->{alias};
my @parents = split /[\t\s]*,[\t\s]*/, $frm->{parents};
my @merge = split /[\t\s]*,[\t\s]*/, $frm->{merge};
if(!$frm->{_err}) {
my $c = $self->dbTagGet(name => $frm->{name}, noid => $tag);
push @{$frm->{_err}}, [ 'name', 'tagexists', $c->[0] ] if @$c;
for (@aliases) {
$c = $self->dbTagGet(name => $_, noid => $tag);
push @{$frm->{_err}}, [ 'alias', 'tagexists', $c->[0] ] if @$c;
}
for(@parents, @merge) {
my $c = $self->dbTagGet(name => $_, noid => $tag);
push @{$frm->{_err}}, [ 'parents', 'func', [ 0, mt '_tagedit_err_notfound', $_ ]] if !@$c;
$_ = $c->[0]{id};
}
}
if(!$frm->{_err}) {
$frm->{state} = $frm->{meta} = 0 if !$self->authCan('tagmod');
my %opts = (
name => $frm->{name},
state => $frm->{state},
cat => $frm->{cat},
description => $frm->{description},
meta => $frm->{meta}?1:0,
aliases => \@aliases,
parents => \@parents,
);
if(!$tag) {
$tag = $self->dbTagAdd(%opts);
} else {
$self->dbTagEdit($tag, %opts, upddate => $frm->{state} == 2 && $t->{state} != 2);
_set_childs_cat($self, $tag, $frm->{cat}) if $frm->{catrec};
}
$self->dbTagMerge($tag, @merge) if $self->authCan('tagmod') && @merge;
$self->resRedirect("/g$tag", 'post');
return;
}
}
if($tag) {
$frm->{$_} ||= $t->{$_} for (qw|name meta description state cat|);
$frm->{alias} ||= join "\n", @{$t->{aliases}};
$frm->{parents} ||= join ', ', map $_->{name}, @{$t->{parents}};
}
my $title = $par ? mt('_tagedit_title_add', $par->{name}) : $tag ? mt('_tagedit_title_edit', $t->{name}) : mt '_tagedit_title_new';
$self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('g', $par || $t, 'edit') if $t || $par;
if(!$self->authCan('tagmod')) {
div class => 'mainbox';
h1 mt '_tagedit_req_title';
div class => 'notice';
h2 mt '_tagedit_req_subtitle';
p;
lit mt '_tagedit_req_msg';
end;
end;
end;
}
$self->htmlForm({ frm => $frm, action => $par ? "/g$par->{id}/add" : $tag ? "/g$tag/edit" : '/g/new' }, 'tagedit' => [ $title,
[ input => short => 'name', name => mt '_tagedit_frm_name' ],
$self->authCan('tagmod') ? (
$tag ?
[ static => label => mt('_tagedit_frm_by'), content => $self->{l10n}->userstr($t->{addedby}, $t->{username}) ] : (),
[ select => short => 'state', name => mt('_tagedit_frm_state'), options => [
map [$_, mt '_tagedit_frm_state'.$_], 0..2 ] ],
[ checkbox => short => 'meta', name => mt '_tagedit_frm_meta' ],
$tag ?
[ static => content => mt '_tagedit_frm_meta_warn' ] : (),
) : (),
[ select => short => 'cat', name => mt('_tagedit_frm_cat'), options => [
map [$_, mt "_tagcat_$_"], @{$self->{tag_categories}} ] ],
$self->authCan('tagmod') && $tag ? (
[ checkbox => short => 'catrec', name => mt '_tagedit_frm_catrec' ],
[ static => content => mt '_tagedit_frm_catrec_warn' ],
) : (),
[ textarea => short => 'alias', name => mt('_tagedit_frm_alias'), cols => 30, rows => 4 ],
[ textarea => short => 'description', name => mt '_tagedit_frm_desc' ],
[ static => content => mt '_tagedit_frm_desc_msg' ],
[ input => short => 'parents', name => mt '_tagedit_frm_parents' ],
[ static => content => mt '_tagedit_frm_parents_msg' ],
$self->authCan('tagmod') ? (
[ part => title => mt '_tagedit_frm_merge' ],
[ input => short => 'merge', name => mt '_tagedit_frm_merge_tags' ],
[ static => content => mt '_tagedit_frm_merge_msg' ],
) : (),
]);
$self->htmlFooter;
}
# recursively edit all child tags and set the category field
# Note: this can be done more efficiently by doing everything in one UPDATE
# query, but that takes more code and this feature isn't used very often
# anyway.
sub _set_childs_cat {
my($self, $tag, $cat) = @_;
my %done;
my $e;
$e = sub {
my $l = shift;
for (@$l) {
$self->dbTagEdit($_->{id}, cat => $cat) if !$done{$_->{id}}++;
$e->($_->{sub}) if $_->{sub};
}
};
my $childs = $self->dbTagTree($tag, 25);
$e->($childs);
}
sub taglist {
my $self = shift;
my $f = $self->formValidate(
{ get => 's', required => 0, default => 'name', enum => ['added', 'name'] },
{ get => 'o', required => 0, default => 'a', enum => ['a', 'd'] },
{ get => 'p', required => 0, default => 1, template => 'int' },
{ get => 't', required => 0, default => -1, enum => [ -1..2 ] },
{ get => 'q', required => 0, default => '' },
);
return $self->resNotFound if $f->{_err};
my($t, $np) = $self->dbTagGet(
sort => $f->{s}, reverse => $f->{o} eq 'd',
page => $f->{p},
results => 50,
state => $f->{t},
search => $f->{q}
);
$self->htmlHeader(title => mt '_tagb_title');
div class => 'mainbox';
h1 mt '_tagb_title';
form action => '/g/list', 'accept-charset' => 'UTF-8', method => 'get';
input type => 'hidden', name => 't', value => $f->{t};
$self->htmlSearchBox('g', $f->{q});
end;
p class => 'browseopts';
a href => "/g/list?q=$f->{q};t=-1", $f->{t} == -1 ? (class => 'optselected') : (), mt '_tagb_state-1';
a href => "/g/list?q=$f->{q};t=0", $f->{t} == 0 ? (class => 'optselected') : (), mt '_tagb_state0';
a href => "/g/list?q=$f->{q};t=1", $f->{t} == 1 ? (class => 'optselected') : (), mt '_tagb_state1';
a href => "/g/list?q=$f->{q};t=2", $f->{t} == 2 ? (class => 'optselected') : (), mt '_tagb_state2';
end;
if(!@$t) {
p mt '_tagb_noresults';
}
end 'div';
if(@$t) {
$self->htmlBrowse(
class => 'taglist',
options => $f,
nextpage => $np,
items => $t,
pageurl => "/g/list?t=$f->{t};q=$f->{q};s=$f->{s};o=$f->{o}",
sorturl => "/g/list?t=$f->{t};q=$f->{q}",
header => [
[ mt('_tagb_col_added'), 'added' ],
[ mt('_tagb_col_name'), 'name' ],
],
row => sub {
my($s, $n, $l) = @_;
Tr $n % 2 ? (class => 'odd') : ();
td class => 'tc1', $self->{l10n}->age($l->{added});
td class => 'tc3';
a href => "/g$l->{id}", $l->{name};
if($f->{t} == -1) {
b class => 'grayedout', ' '.mt '_tagb_note_awaiting' if $l->{state} == 0;
b class => 'grayedout', ' '.mt '_tagb_note_del' if $l->{state} == 1;
}
end;
end 'tr';
}
);
}
$self->htmlFooter;
}
sub taglinks {
my $self = shift;
my $f = $self->formValidate(
{ get => 'p', required => 0, default => 1, template => 'int' },
{ get => 'o', required => 0, default => 'd', enum => ['a', 'd'] },
{ get => 's', required => 0, default => 'date', enum => [qw|date tag|] },
{ get => 'v', required => 0, default => 0, template => 'int' },
{ get => 'u', required => 0, default => 0, template => 'int' },
{ get => 't', required => 0, default => 0, template => 'int' },
);
return $self->resNotFound if $f->{_err} || $f->{p} > 100;
my($list, $np) = $self->dbTagLinks(
what => 'details',
results => 50,
page => $f->{p},
sort => $f->{s},
reverse => $f->{o} eq 'd',
$f->{v} ? (vid => $f->{v}) : (),
$f->{u} ? (uid => $f->{u}) : (),
$f->{t} ? (tag => $f->{t}) : (),
);
my $url = sub {
my %f = ((map +($_,$f->{$_}), qw|s o v u t|), @_);
my $qs = join ';', map $f{$_}?"$_=$f{$_}":(), keys %f;
return '/g/links'.($qs?"?$qs":'')
};
$self->htmlHeader(noindex => 1, title => mt '_taglink_title');
div class => 'mainbox';
h1 mt '_taglink_title';
div class => 'warning';
h2 mt '_taglink_spoil_title';
p mt '_taglink_spoil_msg';
end;
br;
if($f->{u} || $f->{t} || $f->{v}) {
p mt '_taglink_fil_active';
ul;
if($f->{u}) {
my $o = $self->dbUserGet(uid => $f->{u})->[0];
li;
txt '['; a href => $url->(u=>0), mt '_taglink_fil_remove'; txt '] ';
txt mt '_taglink_fil_user'; txt ' ';
a href => "/u$o->{id}", $o->{username};
end;
}
if($f->{t}) {
my $o = $self->dbTagGet(id => $f->{t})->[0];
li;
txt '['; a href => $url->(t=>0), mt '_taglink_fil_remove'; txt '] ';
txt mt '_taglink_fil_tag'; txt ' ';
a href => "/g$o->{id}", $o->{name};
end;
}
if($f->{v}) {
my $o = $self->dbVNGet(id => $f->{v})->[0];
li;
txt '['; a href => $url->(v=>0), mt '_taglink_fil_remove'; txt '] ';
txt mt '_taglink_fil_vn'; txt ' ';
a href => "/v$o->{id}", $o->{title};
end;
}
end 'ul';
}
p mt '_taglink_fil_add' unless $f->{v} && $f->{u} && $f->{t};
end 'div';
$self->htmlBrowse(
class => 'taglinks',
options => $f,
nextpage => $np,
items => $list,
pageurl => $url->(),
sorturl => $url->(s=>0,o=>0),
header => [
[ mt('_taglink_col_date'), 'date' ],
[ mt('_taglink_col_user') ],
[ mt('_taglink_col_rating') ],
[ mt('_taglink_col_tag'), 'tag' ],
[ mt('_taglink_col_spoiler') ],
[ mt('_taglink_col_vn'), ],
],
row => sub {
my($s, $n, $l) = @_;
Tr $n % 2 ? (class => 'odd') : ();
td class => 'tc1';
lit $self->{l10n}->date($l->{date});
end;
td class => 'tc2';
a href => $url->(u=>$l->{uid}), class => 'setfil', '> ' if !$f->{u};
a href => "/u$l->{uid}", $l->{username};
end;
td class => 'tc3'.($l->{ignore}?' ignored':'');
tagscore $l->{vote};
end;
td class => 'tc4';
a href => $url->(t=>$l->{tag}), class => 'setfil', '> ' if !$f->{t};
a href => "/g$l->{tag}", $l->{name};
end;
td class => 'tc5', !defined $l->{spoiler} ? ' ' : mt "_taglink_spoil$l->{spoiler}";
td class => 'tc6';
a href => $url->(v=>$l->{vid}), class => 'setfil', '> ' if !$f->{v};
a href => "/v$l->{vid}", shorten $l->{title}, 50;
end;
end;
},
);
$self->htmlFooter;
}
sub vntagmod {
my($self, $vid) = @_;
my $v = $self->dbVNGet(id => $vid)->[0];
return $self->resNotFound if !$v || $v->{hidden};
return $self->htmlDenied if !$self->authCan('tag');
my $tags = $self->dbTagStats(vid => $vid, results => 9999);
my $my = $self->dbTagLinks(vid => $vid, uid => $self->authInfo->{id});
if($self->reqMethod eq 'POST') {
return if !$self->authCheckCode;
my $frm = $self->formValidate(
{ post => 'taglinks', required => 0, default => '', maxlength => 10240, regex => [ qr/^[1-9][0-9]*,-?[1-3],-?[0-2]( [1-9][0-9]*,-?[1-3],-?[0-2])*$/, 'meh' ] },
{ post => 'overrule', required => 0, multi => 1, template => 'int' },
);
return $self->resNotFound if $frm->{_err};
# convert some data in a more convenient structure for faster lookup
my %tags = map +($_->{id} => $_), @$tags;
my %old = map +($_->{tag} => $_), @$my;
my %new = map { my($tag, $vote, $spoiler) = split /,/; ($tag => [ $vote, $spoiler ]) } split / /, $frm->{taglinks};
my %over = !$self->authCan('tagmod') || !$frm->{overrule}[0] ? () : (map $new{$_} ? ($_ => 1) : (), @{$frm->{overrule}});
# hashes which need to be filled, indicating what should be changed to the DB
my %delete; # tag => 1
my %update; # tag => [ vote, spoiler ] (ignore flag is untouched)
my %insert; # tag => [ vote, spoiler, ignore ]
my %overrule; # tag => 0/1
for my $t (keys %old, keys %new) {
my $prev_over = $old{$t} && !$old{$t}{ignore} && $tags{$t}{overruled};
# overrule checkbox has changed? make sure to (de-)overrule the tag votes
$overrule{$t} = $over{$t}?1:0 if (!$prev_over && $over{$t}) || ($prev_over && !$over{$t});
# tag deleted?
if($old{$t} && !$new{$t}) {
$delete{$t} = 1;
next;
}
# and insert or update the vote
if(!$old{$t} && $new{$t}) {
# determine whether this vote is going to be ignored or not
my $ign = $tags{$t}{overruled} && !$prev_over && !$over{$t};
$insert{$t} = [ $new{$t}[0], $new{$t}[1], $ign ];
} elsif($old{$t}{vote} != $new{$t}[0] || (defined $old{$t}{spoiler} ? $old{$t}{spoiler} : -1) != $new{$t}[1]) {
$update{$t} = [ $new{$t}[0], $new{$t}[1] ];
}
}
$self->dbTagLinkEdit($self->authInfo->{id}, $vid, \%insert, \%update, \%delete, \%overrule);
# need to re-fetch the tags and tag links, as these have been modified
$tags = $self->dbTagStats(vid => $vid, results => 9999);
$my = $self->dbTagLinks(vid => $vid, uid => $self->authInfo->{id});
}
my $title = mt '_tagv_title', $v->{title};
$self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('v', $v, 'tagmod');
div class => 'mainbox';
h1 $title;
div class => 'notice';
h2 mt '_tagv_msg_title';
ul;
li; lit mt '_tagv_msg_guidelines'; end;
li mt '_tagv_msg_submit';
li mt '_tagv_msg_cache';
end;
end;
end 'div';
$self->htmlForm({ action => "/v$vid/tagmod", nosubmit => 1 }, tagmod => [ mt('_tagv_frm_title'),
[ hidden => short => 'taglinks', value => '' ],
[ static => nolabel => 1, content => sub {
table class => 'tgl';
thead;
Tr;
td '';
td colspan => $self->authCan('tagmod') ? 3 : 2, class => 'tc_you', mt '_tagv_col_you';
td colspan => 3, class => 'tc_others', mt '_tagv_col_others';
end;
Tr;
td class => 'tc_tagname', mt '_tagv_col_tag';
td class => 'tc_myvote', mt '_tagv_col_rating';
td class => 'tc_myover', 'O' if $self->authCan('tagmod');
td class => 'tc_myspoil', mt '_tagv_col_spoiler';
td class => 'tc_allvote', mt '_tagv_col_rating';
td class => 'tc_allspoil', mt '_tagv_col_spoiler';
td class => 'tc_allwho', '';
end;
end 'thead';
tfoot; Tr;
td colspan => 6;
input type => 'submit', class => 'submit', value => mt('_tagv_save'), style => 'float: right';
input id => 'tagmod_tag', type => 'text', class => 'text', value => '';
input id => 'tagmod_add', type => 'button', class => 'submit', value => mt '_tagv_add';
br;
p;
lit mt '_tagv_addmsg';
end;
end;
end; end 'tfoot';
tbody id => 'tagtable';
_tagmod_list($self, $vid, $tags, $my);
end 'tbody';
end 'table';
} ],
]);
$self->htmlFooter;
}
sub _tagmod_list {
my($self, $vid, $tags, $my) = @_;
my %my = map +($_->{tag} => $_), @$my;
for my $cat (@{$self->{tag_categories}}) {
my @tags = grep $_->{cat} eq $cat, @$tags;
next if !@tags;
Tr class => 'tagmod_cat';
td colspan => 7, mt "_tagcat_$cat";
end;
for my $t (@tags) {
my $m = $my{$t->{id}};
Tr id => "tgl_$t->{id}";
td class => 'tc_tagname'; a href => "/g$t->{id}", $t->{name}; end;
td class => 'tc_myvote', $m->{vote}||0;
if($self->authCan('tagmod')) {
td class => 'tc_myover';
input type => 'checkbox', name => 'overrule', value => $t->{id},
$m->{vote} && !$m->{ignore} && $t->{overruled} ? (checked => 'checked') : ()
if $t->{cnt} > 1;
end;
}
td class => 'tc_myspoil', defined $m->{spoiler} ? $m->{spoiler} : -1;
td class => 'tc_allvote';
tagscore $t->{rating};
i $t->{overruled} ? (class => 'grayedout') : (), " ($t->{cnt})";
b class => 'standout', style => 'font-weight: bold', title => mt('_tagv_overruletip'), ' !' if $t->{overruled};
end;
td class => 'tc_allspoil', sprintf '%.2f', $t->{spoiler};
td class => 'tc_allwho';
a href => "/g/links?v=$vid;t=$t->{id}", mt '_tagv_who';
end;
end;
}
}
}
sub tagindex {
my $self = shift;
$self->htmlHeader(title => mt '_tagidx_title');
div class => 'mainbox';
a class => 'addnew', href => "/g/new", mt '_tagidx_create' if $self->authCan('tag');
h1 mt '_tagidx_search';
form action => '/g/list', 'accept-charset' => 'UTF-8', method => 'get';
$self->htmlSearchBox('g', '');
end;
end;
my $t = $self->dbTagTree(0, 2);
childtags($self, mt('_tagp_tree'), 'g', {childs => $t});
table class => 'mainbox threelayout';
Tr;
# Recently added
td;
a class => 'right', href => '/g/list', mt '_tagidx_browseall';
my $r = $self->dbTagGet(sort => 'added', reverse => 1, results => 10, state => 2);
h1 mt '_tagidx_recent';
ul;
for (@$r) {
li;
txt $self->{l10n}->age($_->{added});
txt ' ';
a href => "/g$_->{id}", $_->{name};
end;
}
end;
end;
# Popular
td;
a class => 'addnew', href => "/g/links", mt '_tagidx_rawtags';
$r = $self->dbTagGet(sort => 'vns', reverse => 1, meta => 0, results => 10);
h1 mt '_tagidx_popular';
ul;
for (@$r) {
li;
a href => "/g$_->{id}", $_->{name};
txt " ($_->{c_vns})";
end;
}
end;
end;
# Moderation queue
td;
h1 mt '_tagidx_queue';
$r = $self->dbTagGet(state => 0, sort => 'added', reverse => 1, results => 10);
ul;
li mt '_tagidx_queue_empty' if !@$r;
for (@$r) {
li;
txt $self->{l10n}->age($_->{added});
txt ' ';
a href => "/g$_->{id}", $_->{name};
end;
}
li;
br;
a href => '/g/list?t=0;o=d;s=added', mt '_tagidx_queue_link';
txt ' - ';
a href => '/g/list?t=1;o=d;s=added', mt '_tagidx_denied';
end;
end;
end;
end 'tr';
end 'table';
$self->htmlFooter;
}
# non-translatable debug page
sub fulltree {
my $self = shift;
return $self->htmlDenied if !$self->authCan('tagmod');
my $e;
$e = sub {
my $lst = shift;
ul style => 'list-style-type: none; margin-left: 15px';
for (@$lst) {
li;
txt '> ';
a href => "/g$_->{id}", $_->{name};
b class => 'grayedout', " ($_->{c_vns})" if $_->{c_vns};
end;
$e->($_->{sub}) if $_->{sub};
}
end;
};
my $tags = $self->dbTagTree(0, 25);
$self->htmlHeader(title => '[DEBUG] Tag tree', noindex => 1);
div class => 'mainbox';
h1 '[DEBUG] Tag tree';
$e->($tags);
end;
$self->htmlFooter;
}
sub tagxml {
my $self = shift;
my $f = $self->formValidate(
{ get => 'q', required => 0, maxlength => 500 },
{ get => 'id', required => 0, multi => 1, template => 'int' },
);
return $self->resNotFound if $f->{_err} || (!$f->{q} && !$f->{id} && !$f->{id}[0]);
my($list, $np) = $self->dbTagGet(
!$f->{q} ? () : $f->{q} =~ /^g([1-9]\d*)/ ? (id => $1) : $f->{q} =~ /^name:(.+)$/ ? (name => $1) : (search => $f->{q}),
$f->{id} && $f->{id}[0] ? (id => $f->{id}) : (),
results => 15,
page => 1,
);
$self->resHeader('Content-type' => 'text/xml; charset=UTF-8');
xml;
tag 'tags', more => $np ? 'yes' : 'no', $f->{q} ? (query => $f->{q}) : ();
for(@$list) {
tag 'item', id => $_->{id}, meta => $_->{meta} ? 'yes' : 'no', state => $_->{state}, $_->{name};
}
end;
}
1;