summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2011-05-01 11:13:42 +0200
committerYorhel <git@yorhel.nl>2011-05-01 11:13:42 +0200
commit2e73fe7c7a174f3349786a69684f6a70101a57c0 (patch)
tree6701c7ca4429a7f59d5d2cd08bf98d8a143aced5
parentee977448037feeb53736f35809722dd16d3d3d91 (diff)
parent2bf4818105a11abeca313b36d820c3cc397dc5b3 (diff)
Merge branch 'beta'2.20
Conflicts: ChangeLog
-rw-r--r--ChangeLog17
-rw-r--r--Makefile8
-rw-r--r--data/global.pl12
-rw-r--r--data/lang.txt141
-rw-r--r--data/notes/sponsored-links24
-rw-r--r--data/script.js24
-rw-r--r--data/style.css1029
-rw-r--r--lib/Multi/Core.pm2
-rw-r--r--lib/Multi/Maintenance.pm18
-rw-r--r--lib/VNDB/DB/Affiliates.pm69
-rw-r--r--lib/VNDB/DB/Misc.pm3
-rw-r--r--lib/VNDB/DB/Releases.pm2
-rw-r--r--lib/VNDB/DB/Users.pm4
-rw-r--r--lib/VNDB/Handler/Affiliates.pm146
-rw-r--r--lib/VNDB/Handler/Chars.pm12
-rw-r--r--lib/VNDB/Handler/Misc.pm7
-rw-r--r--lib/VNDB/Handler/Producers.pm2
-rw-r--r--lib/VNDB/Handler/Releases.pm2
-rw-r--r--lib/VNDB/Handler/Traits.pm6
-rw-r--r--lib/VNDB/Handler/ULists.pm3
-rw-r--r--lib/VNDB/Handler/Users.pm20
-rw-r--r--lib/VNDB/Handler/VNEdit.pm64
-rw-r--r--lib/VNDB/Handler/VNPage.pm49
-rw-r--r--lib/VNDB/Util/Auth.pm5
-rw-r--r--lib/VNDB/Util/CommonHTML.pm17
-rw-r--r--lib/VNDB/Util/FormHTML.pm6
-rw-r--r--lib/VNDB/Util/LayoutHTML.pm13
-rw-r--r--lib/VNDBUtil.pm8
-rw-r--r--util/sql/all.sql13
-rw-r--r--util/sql/func.sql56
-rw-r--r--util/sql/schema.sql18
-rw-r--r--util/updates/update_2.20.sql54
-rwxr-xr-xutil/vndb.pl2
33 files changed, 953 insertions, 903 deletions
diff --git a/ChangeLog b/ChangeLog
index 44cb643e..727b4e71 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,21 @@
-2.20 - ?
+2.20 - 2011-05-01
+ - Added support for sponsored links on VN pages
- Order the VNs listed on char browser by release date
- Order the traits groups on /i by their 'order' column
- Use same browsing-table on trait pages and char browser
+ - Added spoiler warning to character revision pages
+ - Generate dbedit/dbdel notifications on character edits
- CSS: Hide links in [spoiler] tags
+ - Added 'select' all to wishlist and moved 'select all' down on notifies
+ - Added char/tag/trait stats to database statistics box
+ - Update traits_chars cache daily using Multi::Maintenance
+ - Toggle [spoiler] tag visibility with global setting rather than mouse-over
+ - Added "Add character" link to VN pages
+ - Added "Image ID" field to VN image uploader
+ - Added "All except characters" filter to history browser
+ - Cleaned up CSS code
+ - Cleaned up permissions
+ - Replaced user ranks with a permission system
- Bugfix: don't accidentally remove char traits when editing
- Bugfix: fixed possible SQL table name clash on history browser
- Bugfix: properly announce chars and traits in Multi::IRC
@@ -17,6 +30,8 @@
- Bugfix: Properly format future dates on my vn list
- Bugfix: Properly position the sub-tabs on VN page without tags
- Bugfix: Allow unhiding of posts by mods
+ - Bugfix: Forgot to make two JS strings translatable
+ - Bugfix: Don't allow unicode numbers as 'int' in formValidate
2.19 - 2011-03-30
- Character database:
diff --git a/Makefile b/Makefile
index d1ed33fe..568eb1b0 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@
.PHONY: all dirs js skins robots chmod chmod-tladmin multi-stop multi-start multi-restart sql-import\
update-2.10 update-2.11 update-2.12 update-2.13 update-2.14 update-2.15 update-2.16 update-2.17\
- update-2.18 update-2.19
+ update-2.18 update-2.19 update-2.20
all: dirs js skins robots data/config.pl
@@ -179,3 +179,9 @@ update-2.19: all
$(multi-stop)
${runpsql} < util/updates/update_2.19.sql
$(multi-start)
+
+update-2.20: all
+ $(multi-stop)
+ ${runpsql} < util/updates/update_2.20.sql
+ $(multi-start)
+
diff --git a/data/global.pl b/data/global.pl
index 5fd2bf85..2453ed6b 100644
--- a/data/global.pl
+++ b/data/global.pl
@@ -31,15 +31,8 @@ our %S = (%S,
scr_size => [ 136, 102 ], # w*h of screenshot thumbnails
ch_size => [ 256, 300 ], # max. w*h of char images
cv_size => [ 256, 400 ], # max. w*h of cover images
- user_ranks => [
- # allowed actions # DB number
- [qw| hist |], # 0
- [qw| hist |], # 1
- [qw| hist board |], # 2
- [qw| hist board edit tag |], # 3
- [qw| hist board boardmod edit charedit tag mod lock del tagmod |], # 4
- [qw| hist board boardmod edit charedit tag mod lock del tagmod usermod |], # 5
- ],
+ # bit flags
+ permissions => {qw| board 1 boardmod 2 edit 4 charedit 8 tag 16 dbmod 32 tagmod 64 usermod 128 affiliate 256 |},
languages => [qw|cs da de en es fi fr hu it ja ko nl no pl pt-br pt-pt ru sk sv tr vi zh|],
producer_types => [qw|co in ng|],
discussion_boards => [qw|an db ge v p u|], # <- note that some properties of these boards are hard-coded
@@ -137,4 +130,3 @@ require $ROOT.'/data/config.pl' if -f $ROOT.'/data/config.pl';
1;
-
diff --git a/data/lang.txt b/data/lang.txt
index f037b61e..f3171116 100644
--- a/data/lang.txt
+++ b/data/lang.txt
@@ -104,51 +104,6 @@ ends with ']'. The following options are supported:
# data/global.pl - used in many places
-# user ranks
-
-:_urank_0
-en : visitor
-ru : посетитель
-cs : návštěvník
-hu : vendég
-nl : bezoeker
-
-:_urank_1
-en : banned
-ru : забанен
-cs : zabanovaný
-hu : kicsapva
-nl : gebanned
-
-:_urank_2
-en : loser
-ru : лузер
-cs : loser
-hu : vesztes
-nl : sukkel
-
-:_urank_3
-en : user
-ru : пользователь
-cs : uživatel
-hu : felhasználó
-nl : gebruiker
-
-:_urank_4
-en : mod
-ru : модератор
-cs : moderátor
-hu : mod
-nl :
-
-:_urank_5
-en : admin
-ru : администратор
-cs : administrátor
-hu : admin
-nl :
-
-
# languages
:_lang_cs
@@ -1985,6 +1940,27 @@ cs : Producenti
hu : Készítők
nl : Producenten
+:_menu_stat_chars
+en : Characters
+ru*:
+cs*:
+hu*:
+nl : Karakters
+
+:_menu_stat_tags
+en : VN Tags
+ru*:
+cs*:
+hu*:
+nl :
+
+:_menu_stat_traits
+en : Character traits
+ru*:
+cs*:
+hu*:
+nl : Karakter kenmerken
+
:_menu_stat_users
en : Users
ru : Пользователей
@@ -2143,6 +2119,20 @@ nl : laatste
# Revision pages
+:_revision_spoil_title
+en : SPOILER WARNING!
+ru*:
+cs*:
+hu*:
+nl : SPOILERWAARSCHUWING!
+
+:_revision_spoil_msg
+en : This revision page may contain major spoilers. You may want to view the [url,_1,final page] instead.
+ru*:
+cs*:
+hu*:
+nl : Deze revisiepagina kan spoilers bevatten. Je kan misschien beter de [url,_1,uiteindelijke pagina] openen.
+
:_revision_previous
en : earlier revision
ru : более ранняя редакция
@@ -2485,6 +2475,13 @@ cs : Pouze postavy
hu : Csak szereplők
nl : Alleen karakters
+:_hist_filter_nochars
+en : All except characters
+ru*:
+cs*:
+hu*:
+nl : Alles behalve karakters
+
:_hist_filter_allactions
en : Show all changes
ru : Показать все изменения
@@ -5431,6 +5428,13 @@ cs : Přidat vlastnost
hu : Sajátosság hozzáadása
nl : Voeg kenmerk toe
+:_chare_traits_del
+en : del
+ru*:
+cs*:
+hu*:
+nl :
+
:_chare_traits_present
en : Selected trait is already present.
ru*:
@@ -5515,6 +5519,13 @@ cs : VN je již přítomna.
hu : A VN már jelen van.
nl : VN is al geselecteerd.
+:_chare_vns_relexists
+en : Release already present.
+ru*:
+cs*:
+hu*:
+nl : Uitgave is al geselecteerd.
+
# Character browser (/c/*)
@@ -5647,6 +5658,13 @@ cs : Tato vlastnost ještě nebyla provázána s žádnými postavami nebo tyto
hu : Ez a sajátosság még nincs hozzárendelve egy szereplőhöz se, vagy a spoiler beállításaid miatt, el vannak rejtve.
nl : Dit kenmerk is nog niet gekoppeld aan een karakter, of deze worden niet weergegeven in verband met je spoilerinstelling.
+:_traitp_cached
+en : The list below also includes all characters linked to child traits. This list is cached, it can take up to 24 hours after a character has been edited for it to show up on this page.
+ru*:
+cs*:
+hu*:
+nl : De volgende lijst bevat ook alle karakters die gelinkt zijn aan subkenmerken. Deze lijst is gecached, het kan 24 uur duren voordat een karakter op deze pagina te zien is.
+
# Trait add/edit form (/i+/edit, /i+/add, /i/new)
@@ -6686,12 +6704,12 @@ cs : Uživatelské jméno
hu : Felhasználónév
nl : Gebruikersnaam
-:_usere_rank
-en : Rank
-ru : Ранг
-cs : Postavení
-hu : Rang
-nl :
+:_usere_perm
+en : Permissions
+ru*:
+cs*:
+hu*:
+nl : Permissies
:_usere_ignvotes
en : Ignore votes in VN statistics
@@ -7441,6 +7459,20 @@ cs : ~[obrázek se zpracovává, vraťte se prosím za několik minut~]
hu : ~[a kép feldolgozás alatt van, gyere vissza pár perc múlva~]
nl : ~[bezig met het verwerken van het plaatje, kom a.u.b. terug in een paar minuten~]
+:_vnedit_image_id
+en : Image ID
+ru*:
+cs*: ID obrázku
+hu*: Kép ID (azonosító)
+nl : Plaatje ID
+
+:_vnedit_image_id_msg
+en : Use a character image that is already on the server. Set to '0' to remove the current image.
+ru*:
+cs*:
+hu*: Használj egy olyan képet ami már megtalálható a szerveren. Ha '0'-ra állítod törölheted a jelenlegi képet.
+nl : Gebruik een karakterplaatje dat al op de server staat. Gebruik '0' om een huidig plaatje te verwijderen.
+
:_vnedit_image_upload
en : Upload new image
ru : Загрузить новое изображение
@@ -8315,6 +8347,13 @@ cs : K této vizuální novele zatím nemáme informace o žádném vydání...
hu : Még nincs információnk ennek a visual novelnek a kiadásairól...
nl : We hebben op dit moment nog geen informatie over uitgaven van deze visual novel...
+:_vnpage_char_add
+en : add character
+ru*:
+cs*:
+hu*:
+nl : voeg karakter toe
+
:_vnpage_rel_add
en : add release
ru : добавить выпуск
diff --git a/data/notes/sponsored-links b/data/notes/sponsored-links
index c31001d2..222553aa 100644
--- a/data/notes/sponsored-links
+++ b/data/notes/sponsored-links
@@ -1,7 +1,7 @@
Advertisements
-Last modified: 2011-01
-Status: Long-term plans / nothing implemented yet
+Last modified: 2011-04-10
+Status: Implemented / Implementation may differ from these notes
Idea: (semi-)large "Buy now" / "Download now" button on VN pages, linking
@@ -26,24 +26,18 @@ Possible parties interested in advertising:
- Play-asia
Has an affiliate system that includes direct links
stores JAN, UPC, and catalog numbers
-- Himeyashop / Erogeshop
- Has no affiliate system but has shown interest in link exchanges in the past
- Does not store JAN/UPC/catalog numbers
- "Temporarily" closed, so probably not a good time to ask for ads?
- DLSite English
- Seems to have an affiliate system, haven't really looked at it yet
+ Has an affiliate system that includes direct links
Most releases don't even have a JAN code or catalog number
- MangaGamer
Rather specific "shop", but could count as one.
Has no affiliate system, but is planning to add one, as announced in
http://mangagamer.wordpress.com/2010/12/31/holidays-passing/
Releases don't have catalog numbers or EAN codes
-- Eroge-Europe.com
- Seems to have an affiliate system, haven't really looked at it yet
- Does not store JAN/UPC/catalog numbers
- PaletWeb
Has no affiliate system
Does have JAN codes for a few titles, but inconsistent
+ Rather messy website... finding/updating links will be a chore
- CDJapan
Doesn't have that many VNs from what I've browsed, but still several
Has an affiliate system (seems to include direct links)
@@ -52,6 +46,13 @@ Possible parties interested in advertising:
Does not seem to have many VNs (3 or 4?)
Has no affiliate system
Does not have JAN or catalog numbers
+- Himeyashop / Erogeshop (out of business?)
+ Has no affiliate system but has shown interest in link exchanges in the past
+ Does not store JAN/UPC/catalog numbers
+ "Temporarily" closed, so probably not a good time to ask for ads?
+- Eroge-Europe.com (out of business?)
+ Seems to have an affiliate system, haven't really looked at it yet
+ Does not store JAN/UPC/catalog numbers
So who is going to update all those links?
@@ -103,6 +104,3 @@ Three possibilities:
special-case advertisers and give them special treatment or fetch
additional information.
-I would greatly prefer option #1, but since that's not very practical option
-#3 (the VGMdb-like solution) is probably the best.
-
diff --git a/data/script.js b/data/script.js
index 97f5fa30..41998405 100644
--- a/data/script.js
+++ b/data/script.js
@@ -1814,7 +1814,7 @@ function ctrAdd(item, spoil) {
tag('b', {'class':'grayedout'}, group?group+' / ':''),
tag('a', {'href':'/i'+id}, name)),
sp,
- tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:ctrDel}, 'del'))
+ tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:ctrDel}, mt('_chare_traits_del')))
));
ctrEmpty();
ctrSerialize();
@@ -1994,7 +1994,7 @@ function cvnRelChange() {
while(tr.nodeName.toLowerCase() != 'tr')
tr = tr.parentNode;
if(byId('cvn_v'+tr.cvn_vid+'r'+val)) {
- alert('Release already selected.');
+ alert(mt('_chare_vns_relexists'));
for(var i=0; i<this.options.length; i++)
this.options[i].selected = this.options[i].value == tr.cvn_rid;
return;
@@ -2738,7 +2738,7 @@ if(byId('expandall')) {
}
-// charspoil handling (ugly)
+// charspoil handling
if(byId('charspoil_sel')) {
var k = byClass('charspoil');
var h = byName(byId('charspoil_sel'), 'a');
@@ -2746,6 +2746,7 @@ if(byId('charspoil_sel')) {
for(var i=0; i<k.length; i++)
setClass(k[i], 'hidden',
hasClass(k[i], 'charspoil_0') ? false :
+ hasClass(k[i], 'charspoil_-1') ? spoil > 1 :
hasClass(k[i], 'charspoil_1') ? spoil < 1 : spoil < 2);
for(var i=0; i<h.length; i++)
setClass(h[i], 'sel', spoil == i);
@@ -2762,6 +2763,23 @@ if(byId('charspoil_sel')) {
}
+// mouse-over price information / disclaimer
+if(byId('buynow')) {
+ var l = byClass(byId('buynow'), 'acronym', 'pricenote');
+ for(var i=0; i<l.length; i++) {
+ l[i].buynow_last = l[i].title;
+ l[i].title = null;
+ ddInit(l[i], 'bottom', function(acr) {
+ return tag('p', {onmouseover:ddHide, style:'padding: 3px'},
+ acr.buynow_last, tag('br', null),
+ '* The displayed price only serves as an indication and',
+ tag('br', null), 'usually excludes shipping. Actual price may differ.'
+ );
+ });
+ }
+}
+
+
// set note input box (/u+/list)
if(byId('not') && byId('vns'))
byId('vns').onchange = function () {
diff --git a/data/style.css b/data/style.css
index 5b35e7af..9ed059c3 100644
--- a/data/style.css
+++ b/data/style.css
@@ -1,50 +1,15 @@
-
-
-* {
- margin: 0;
- padding: 0;
-}
-
-body, td {
- font: 11px "Tahoma";
-}
-body {
- $_bodybg$;
- color: $maintext$
-}
-a {
- color: $maintext$;
- text-decoration: none;
-}
-a:hover {
- border-bottom: 1px dotted $maintext$;
-}
-table {
- border-collapse: collapse;
-}
-table td {
- vertical-align: top;
- padding: 3px;
-}
-table tr.odd {
- background: url($_boxbg$) repeat;
-}
-img {
- border: none;
-}
-
-#bgright {
- position: absolute;
- top: 0px;
- right: 0px;
- $_bgright$
-}
-
-#header {
- position: absolute;
- top: 80px;
- left: 400px;
-}
+* { margin: 0; padding: 0; }
+body, td { font: 11px "Tahoma"; }
+body { $_bodybg$; color: $maintext$ }
+a { color: $link$; text-decoration: none; }
+a:hover { border-bottom: 1px dotted $maintext$; }
+table { border-collapse: collapse; }
+table td { vertical-align: top; padding: 3px; }
+table tr.odd { background: url($_boxbg$) repeat; }
+img { border: none; }
+
+#bgright { position: absolute; top: 0px; right: 0px; $_bgright$ }
+#header { position: absolute; top: 80px; left: 400px; }
#header h1, #header h1 a {
font-family: "Futura", "Century New Gothic", "Arial", Serif;
font-size: 24px;
@@ -52,16 +17,8 @@ img {
border: none!important;
$_maintitle$
}
-
-#footer {
- margin: 15px auto 0 auto;
- text-align: center;
- color: $footer$;
-}
-#footer a {
- color: $footer$;
- text-decoration: underline;
-}
+#footer { margin: 15px auto 0 auto; text-align: center; color: $footer$; }
+#footer a { color: $footer$; text-decoration: underline; }
#debug {
position: fixed;
@@ -77,42 +34,59 @@ img {
#debug h2 { color: #f00!important; font-size: 20px; }
#debug, #debug a { color: #fff!important; }
-ul, ol {
- margin-left: 35px;
-}
-p.locked {
- float: right;
- color: $standout$;
- font-style: italic;
- margin: 0!important;
-}
-b.grayedout { font-weight: normal; color: $grayedout$ }
-i.grayedout { font-style: normal; color: $grayedout$ }
-#maincontent h2 b {
- font: 11px "Tahoma";
- font-weight: normal;
-}
-p.description {
- margin: 10px 100px!important;
-}
-b.done { font-weight: normal; color: $statok$ }
-b.todo { font-weight: normal; color: $statnok$ }
-.clearfloat {
- clear: both;
- height: 0;
-}
-.hidden {
- display: none!important
-}
+/* Warning/Notice Box */
+div.warning, div.notice { margin: 5px 10%; padding: 15px; background-color: $warnbg$; border: 1px solid $warnborder$; }
+div.notice { background-color: $noticebg$; border: 1px solid $noticeborder$; }
+div.warning ul, div.notice ul { margin-left: 0; }
+div.warning li, div.notice li { margin-left: 20px; }
+div.warning h2, div.notice h2 { font-size: 11px; font-weight: bold; margin: 0; }
-b.spoiler, b.spoiler a {
- color: #000!important;
- background-color: #000;
- font-weight: normal;
+/* dropdown box */
+#dd_box { position: absolute; left: 0px; border: 1px solid $border$; background-color: $secbg$; z-index: 2 }
+#dd_box ul { list-style-type: none; margin: 0; padding: 0 }
+#dd_box li b { display: block; font-weight: normal; padding-left: 5px; }
+#dd_box li i { display: block; font-style: normal; padding-left: 10px; padding-right: 5px }
+#dd_box li a { display: block; padding-left: 10px; border: 0; padding-right: 5px }
+#dd_box li a:hover { background: url($_boxbg$) repeat }
+
+/* dropdown search */
+#ds_box {
+ position: absolute;
+ top: 0;
+ border: 1px solid $border$;
+ border-top: none;
+ background-color: $secbg$;
+ cursor: pointer;
+ z-index: 2
}
+#ds_box b { padding: 2px 0 0 10px; }
+#ds_box tr.selected { background: url($_boxbg$) repeat; }
+#ds_box table { width: 100%; }
+
+
+
+/* general text formatting */
+
+ul, ol { margin-left: 35px; }
+p.locked { float: right; color: $standout$; font-style: italic; margin: 0!important; }
+b.grayedout { font-weight: normal; color: $grayedout$ }
+i.grayedout { font-style: normal; color: $grayedout$ }
+#maincontent h2 b { font: 11px "Tahoma"; font-weight: normal; }
+p.description { margin: 10px 100px!important; }
+b.done { font-weight: normal; color: $statok$ }
+b.todo { font-weight: normal; color: $statnok$ }
+p.center { text-align: center; }
+b.future,
+b.standout,
+a.standout { font-weight: normal; color: $standout$; }
+.clearfloat { clear: both; height: 0; }
+.hidden { display: none!important }
+.linethrough { text-decoration: line-through }
+b.spoiler, b.spoiler a { color: #000!important; background-color: #000; font-weight: normal; }
b.spoiler_shown { font-weight: normal }
b.spoiler_shown a { color: $link$!important }
+
#maincontent div.quote {
padding: 1px 5px;
margin: 0px 10px;
@@ -121,7 +95,6 @@ b.spoiler_shown a { color: $link$!important }
border-left: 1px dotted $border$;
text-align: left;
}
-.linethrough { text-decoration: line-through }
pre {
padding:1px 5px;
margin: 5px 15px;
@@ -136,13 +109,6 @@ pre {
/***** general form markup *****/
-form, fieldset {
- border: 0;
- display: block;
-}
-legend {
- display: none;
-}
input.text, input.submit, select, textarea {
background-color: $secbg$;
color: $maintext$;
@@ -150,53 +116,46 @@ input.text, input.submit, select, textarea {
font: 12px "Tahoma";
margin: 1px;
}
-optgroup option {
- padding-left: 10px;
- font-style: normal;
-}
-input.submit {
- background: url($_boxbg$) repeat;
- padding: 1px;
-}
-input.text, select {
- width: 200px;
-}
-fieldset.submit {
- width: 100%;
- text-align: center;
- margin: 5px;
-}
-fieldset.submit input {
- width: 150px;
-}
-fieldset.submit h2 {
- font-size: 11px!important;
-}
-fieldset.submit textarea {
- margin: 0 20px 5px 20px;
-}
-table.formtable {
- margin: 0 20px 20px 20px;
-}
-table.formtable td {
- padding: 0;
-}
-table.formtable tr.newfield td {
- padding-top: 5px;
-}
-table.formtable tr.newpart td {
- padding-top: 20px;
- font-weight: bold;
-}
-td.label, td.label label {
- width: 90px;
-}
-td.label label {
- display: block;
-}
-td.field label {
- margin: 0 5px 0 5px;
-}
+form, fieldset { border: 0; display: block; }
+legend { display: none; }
+optgroup option { padding-left: 10px; font-style: normal; }
+input.submit { background: url($_boxbg$) repeat; padding: 1px; }
+input.text, select { width: 200px; }
+fieldset.submit { width: 100%; text-align: center; margin: 5px; }
+fieldset.submit input { width: 150px; }
+fieldset.submit h2 { font-size: 11px!important; }
+fieldset.submit textarea { margin: 0 20px 5px 20px; }
+td.label, td.label label { width: 90px; }
+td.label label { display: block; }
+td.field label { margin: 0 5px 0 5px; }
+table.formtable { margin: 0 20px 20px 20px; }
+table.formtable td { padding: 0; }
+table.formtable tr.newfield td { padding-top: 5px; }
+table.formtable tr.newpart td { padding-top: 20px; font-weight: bold; }
+
+
+
+
+/***** menu *****/
+
+
+#menulist a { color: $maintext$; text-decoration: none; }
+#menulist a:hover { border-bottom: 1px dotted $maintext$; }
+#menulist { position: absolute; left: 30px; top: 190px; width: 150px; }
+#menulist div.menubox { margin: 0 0 10px 0; border: 1px solid $border$; background: url($_boxbg$) repeat; }
+#menulist div.menubox div { padding: 2px 7px; }
+#menulist h2 { border-bottom: 1px solid $border$; background: url($_boxbg$) repeat; padding: 1px 3px; }
+#menulist h2, #menulist h2 a { font-size: 11px; color: $maintext$; }
+#menulist h2 #lang_select { float: right; padding-top: 1px; }
+#menulist dt { display: block; float: left; width: 93px; font-style: italic; }
+#menulist dd { width: 40px; float: left; text-align: right; }
+#menulist p { text-align: center; }
+#menulist input.text { width: 100px; margin-left: 15px; }
+#menulist input.submit { width: 90px; margin-left: 20px; }
+#menulist #search input.text { width: 133px; margin: 0 0 3px 7px; font-style: italic; }
+#menulist #search input.submit { display: none; }
+#dd_box acronym { margin: 2px 5px 2px 0!important; }
+#menulist .notifyget { display: inline-block; width: 125px; padding: 4px; background: $warnbg$; border: 1px solid $warnborder$; }
@@ -211,114 +170,32 @@ td.field label {
margin: 0;
padding-bottom: 50px!important;
}
-#maincontent h1, #maincontent h2 {
+.mainbox h1, .mainbox h2 {
font-family: "Futura", "Century New Gothic", "Arial", Serif;
font-weight: normal;
font-size: 13px;
}
-#maincontent h1 {
- color: $boxtitle$;
- font-size: 19px;
- margin: -5px 0 15px 0;
-}
-#maincontent h2.alttitle {
- color: $alttitle$;
- margin: -17px 0 15px 15px;
- font-weight: normal;
-}
-#maincontent div.mainbox, #maincontent table.mainbox td {
+div.mainbox, table.mainbox td {
border: 1px solid $border$;
margin: 21px 0 -10px 0;
padding: 5px;
background: url($_boxbg$) repeat;
}
-#maincontent .mainbox a {
- color: $link$;
-}
-#maincontent p {
- margin: 3px 20px;
-}
-#maincontent div div p, #maincontent div table p {
- margin: 0;
-}
-#maincontent h2 {
- font-weight: bold;
- font-size: 14px;
- margin: 10px 0 0 5px;
-}
-p.center {
- text-align: center;
-}
-b.future, b.standout, a.standout {
- font-weight: normal;
- color: $standout$;
-}
-
-
-
-
-/***** menu *****/
-
-#menulist {
- position: absolute;
- left: 30px;
- top: 190px;
- width: 150px;
-}
-#menulist div.menubox {
- margin: 0 0 10px 0;
- border: 1px solid $border$;
- background: url($_boxbg$) repeat;
-}
-#menulist div.menubox div {
- padding: 2px 7px;
-}
-#menulist h2 {
- border-bottom: 1px solid $border$;
- background: url($_boxbg$) repeat;
- padding: 1px 3px;
-}
-#menulist h2, #menulist h2 a {
- font-size: 11px;
- color: $maintext$;
-}
-#menulist h2 #lang_select {
- float: right;
- padding-top: 1px;
-}
-#menulist dt {
- display: block;
- float: left;
- width: 85px;
- font-style: italic;
-}
-#menulist dd {
- width: 40px;
- float: left;
- text-align: right;
-}
-#menulist p {
- text-align: center;
-}
-#menulist input.text {
- width: 100px;
- margin-left: 15px;
-}
-#menulist input.submit {
- width: 90px;
- margin-left: 20px;
-}
-#menulist #search input.text {
- width: 133px;
- margin: 0 0 3px 7px;
- font-style: italic;
-}
-#menulist #search input.submit {
- display: none;
-}
-#dd_box acronym { margin: 2px 5px 2px 0!important; }
-#menulist .notifyget { display: inline-block; width: 125px; padding: 4px; background: $warnbg$; border: 1px solid $warnborder$; }
+.mainbox h1 { color: $boxtitle$; font-size: 19px; margin: -5px 0 15px 0; }
+.mainbox h2.alttitle { color: $alttitle$; margin: -17px 0 15px 15px; font-weight: normal; }
+.mainbox p { margin: 3px 20px; }
+.mainbox div p,
+.mainbox table p { margin: 0; }
+.mainbox h2 { font-weight: bold; font-size: 14px; margin: 10px 0 0 5px; }
+a.addnew, p.addnew { float: right; margin: 0 }
+.mainbox.threelayout { border-collapse: separate; border-spacing: 10px; margin: 10px -10px -20px -10px; min-width: 100%; }
+.mainbox.threelayout td { width: 32%; padding: 0 2px 10px 2px; }
+.mainbox.threelayout h1 { margin: 0; font-size: 14px; font-weight: bold; }
+.mainbox.threelayout h2 { font-size: 12px; margin-top: 3px; }
+.mainbox.threelayout a.right { float: right; }
+.mainbox.threelayout ul { list-style-type: none; margin-left: 10px; }
+.mainbox.threelayout h1 a { color: $boxtitle$; }
@@ -362,99 +239,33 @@ ul.maintabs.browsetabs li.left a { margin-left: 0; margin-right: 5px }
/***** Homepage ******/
-#maincontent .mainbox.threelayout {
- border-collapse: separate;
- border-spacing: 10px;
- margin: 10px -10px -20px -10px;
- min-width: 100%;
-}
-#maincontent .mainbox.threelayout td {
- width: 32%;
- padding: 0 2px 10px 2px;
-}
-#maincontent .mainbox.threelayout h1 {
- margin: 0;
- font-size: 14px;
- font-weight: bold;
-}
-#maincontent .mainbox.threelayout h2 {
- font-size: 12px;
- margin-top: 3px;
-}
-#maincontent .mainbox.threelayout a.right {
- float: right;
-}
-#maincontent .mainbox.threelayout ul {
- list-style-type: none;
- margin-left: 10px;
-}
-p.screenshots {
- text-align: center;
- margin-top: 10px;
- padding: 0;
- height: 105px;
-}
-p.screenshots img {
- margin: 2px;
-}
-#maincontent .mainbox.threelayout h1 a {
- color: $boxtitle$;
-}
-a.feed { float: right }
-
+p.screenshots { text-align: center; margin-top: 10px; padding: 0; height: 105px; }
+p.screenshots img { margin: 2px; }
+a.feed { float: right }
/***** Browsing ******/
-p.browseopts {
- text-align: center;
- padding: 2px;
-}
p.browseopts a {
padding: 1px 3px;
- color: $maintext$!important;
+ color: $maintext$;
border: 1px solid $border$;
margin: 0 2px;
white-space: nowrap;
}
+p.browseopts { text-align: center; padding: 2px; }
p.browseopts a.optselected,
-p.browseopts a:hover {
- border: 0;
- padding: 2px 4px;
-}
-#maincontent div.mainbox.browse {
- padding: 0;
-}
-div.mainbox.browse table {
- width: 100%;
-}
-div.mainbox.browse table td.tc1 {
- padding-left: 25px;
-}
-table thead td {
- font-weight: bold;
- background-color: $secbg$;
-}
-fieldset.search {
- display: block;
- width: 100%;
- text-align: center;
- margin: 0 0 10px 0;
-}
-fieldset.search .submit {
- padding: 0 1px;
-}
-p#searchtabs {
- height: 12px;
- padding-right: 70px;
-}
-p#searchtabs a {
- padding: 2px 6px 2px 6px;
- margin: 0 2px;
- color: $maintext$!important;
-}
+p.browseopts a:hover { border: 0; padding: 2px 4px; }
+div.mainbox.browse { padding: 0; }
+div.mainbox.browse table { width: 100%; }
+div.mainbox.browse table td.tc1 { padding-left: 25px; }
+table thead td { font-weight: bold; background-color: $secbg$; }
+fieldset.search { display: block; width: 100%; text-align: center; margin: 0 0 10px 0; }
+fieldset.search .submit { padding: 0 1px; }
+p#searchtabs { height: 12px; padding-right: 70px; }
+p#searchtabs a { padding: 2px 6px 2px 6px; margin: 0 2px; color: $maintext$; }
p#searchtabs a:hover, p#searchtabs a.sel {
border: 1px solid $secborder$;
border-bottom: none;
@@ -463,7 +274,10 @@ p#searchtabs a:hover, p#searchtabs a.sel {
}
#q { width: 450px }
+
+
/* history browser */
+
div.history table { table-layout: fixed }
div.history td { white-space: nowrap }
div.history td.tc1_1 { width: 60px; padding-left: 0; padding-right: 0; text-align: right }
@@ -476,66 +290,28 @@ div.history td.tc4 b { margin-left: 10px }
-
-
/***** Discussions ******/
/* threads page */
-#maincontent div.mainbox.thread {
- padding: 0;
-}
-div.thread table {
- width: 100%;
-}
-div.thread td {
- border-bottom: 1px solid $border$;
-}
-div.thread td.tc1 {
- width: 150px;
- padding: 5px 10px;
- border-right: 1px solid $border$;
-}
-div.thread td.tc2 {
- padding: 10px 20px 10px 10px;
-}
-div.thread tr.deleted td {
- padding: 1px 10px;
-}
-div.thread i.deleted {
- font-style: normal;
- color: $grayedout$;
-}
-div.thread i.lastmod {
- float: right;
- font-size: 10px;
- color: $grayedout$;
- margin: 0 -10px -5px 0;
-}
-div.thread i.edit {
- float: right;
- color: $grayedout$;
- font-style: normal;
- margin: -10px -10px 0 0;
-}
+#maincontent div.thread { padding: 0; }
+div.thread table { width: 100%; }
+div.thread td { border-bottom: 1px solid $border$; }
+div.thread td.tc1 { width: 150px; padding: 5px 10px; border-right: 1px solid $border$; }
+div.thread td.tc2 { padding: 10px 20px 10px 10px; }
+div.thread tr.deleted td { padding: 1px 10px; }
+div.thread i.deleted { font-style: normal; color: $grayedout$; }
+div.thread i.lastmod { float: right; font-size: 10px; color: $grayedout$; margin: 0 -10px -5px 0; }
+div.thread i.edit { float: right; color: $grayedout$; font-style: normal; margin: -10px -10px 0 0; }
/* threads browser */
-div.mainbox.discussions td.tc4 {
- text-align: right;
-}
-div.mainbox.discussions a.locked {
- text-decoration: line-through;
-}
-div.mainbox.discussions b.boards {
- padding-left: 10px;
- font-weight: normal;
-}
-div.mainbox.discussions b.boards a {
- color: $grayedout$!important;
-}
-div.discussions td.tc2 { width: 50px; }
-div.discussions td.tc3 { width: 90px; }
-div.discussions td.tc4 { width: 170px; }
-#maincontent h1.boxtitle, #maincontent h1.boxtitle a {
+div.mainbox.discussions td.tc4 { text-align: right; }
+div.mainbox.discussions a.locked { text-decoration: line-through; }
+div.mainbox.discussions b.boards { padding-left: 10px; font-weight: normal; }
+div.mainbox.discussions b.boards a { color: $grayedout$; }
+div.discussions td.tc2 { width: 50px; }
+div.discussions td.tc3 { width: 90px; }
+div.discussions td.tc4 { width: 170px; }
+h1.boxtitle, h1.boxtitle a {
font-family: "Futura", "Century New Gothic", "Arial", Serif;
font-weight: bold;
font-style: italic;
@@ -547,119 +323,49 @@ div.discussions td.tc4 { width: 170px; }
-
/***** VN page *******/
-div.vndetails {
- margin: 0 auto;
- width: 800px;
-}
-div.vnimg {
- float: left;
- width: 250px;
- margin: 0 10px;
-}
-div.vnimg i {
- display: block;
- width: 100%;
- text-align: center;
- font-size: 10px;
-}
-div.vnimg p {
- text-align: center;
- padding: 0px;
- margin: 0;
-}
-.vndesc h2 {
- margin: 5px 0 0 0!important;
-}
-.vndesc p {
- padding: 0 0 0 5px;
-}
-p#nsfw_hid {
- display: none;
- cursor: pointer;
-}
-div.vndetails table {
- float: left;
- width: 530px;
-}
-div.vndetails table td.key {
- width: 80px;
-}
-div.vndetails table dt {
- float: left;
- font-style: italic;
-}
-div.vndetails table dd {
- margin-left: 90px;
-}
-
-div.vndetails td.relations dt {
- float: none;
- font-style: normal;
-}
-div.vndetails td.relations dd {
- margin-left: 15px;
-}
-div.vndetails td.anime b {
- font-size: 8px;
- font-weight: normal;
- padding-right: 4px;
-}
-div#vntags {
- margin: 15px 30px 0 30px;
- border-top: 1px solid $border$;
- padding: 1px 5% 0 5%;
- text-align: center;
-}
-#vntags span { white-space: nowrap; margin-left: 15px; }
-#vntags b { color: $grayedout$; font-weight: normal; font-size: 8px }
-#tagops {
- float: right;
- text-align: right;
- width: auto;
- margin: 0 30px;
-}
-#tagops a { margin: 0 0 0 10px; border: 0; outline: none }
-#tagops a.sec { border-left: 1px solid $border$; padding-left: 10px }
-#maincontent #tagops a.tsel { color: $maintext$; }
-
-.releases table, #screenshots table {
- width: 100%;
-}
-.releases tr.lang td, #screenshots tr.rel td {
- background: url($_boxbg$) repeat;
- font-weight: bold;
-}
-.releases td.tc1 {
- padding-left: 30px;
- width: 80px;
-}
-.releases td.tc2 {
- text-align: center;
- width: 50px;
-}
-.releases td.tc3 {
- text-align: right;
- padding: 0;
- width: 90px;
-}
-.releases td.tc5 {
- width: 70px;
-}
-.releases td.tc5 a {
- color: $maintext$!important;
- border: 0;
-}
-.releases td.tc6 {
- text-align: right;
- width: 25px;
- padding: 0;
-}
-a.addnew {
- float: right;
-}
+div.vndetails { margin: 0 auto; width: 800px; }
+div.vnimg { float: left; width: 250px; margin: 0 10px; }
+div.vnimg i { display: block; width: 100%; text-align: center; font-size: 10px; }
+div.vnimg p { text-align: center; padding: 0px; margin: 0; }
+.vndesc h2 { margin: 5px 0 0 0; }
+.vndesc p { padding: 0 0 0 5px; }
+p#nsfw_hid { display: none; cursor: pointer; }
+div.vndetails table { float: left; width: 530px; }
+div.vndetails table td.key { width: 80px; }
+div.vndetails table dt { float: left; font-style: italic; }
+div.vndetails table dd { margin-left: 90px; }
+div.vndetails td.relations dt { float: none; font-style: normal; }
+div.vndetails td.relations dd { margin-left: 15px; }
+div.vndetails td.anime b { font-size: 8px; font-weight: normal; padding-right: 4px; }
+
+td#buynow h1 { display: block; width: 100px; background: #bf9228; font-size: 13px; text-align: center; margin: -3px 0 0 0 }
+td#buynow h1 a { display: block; width: 100%; color: #fff; border: none; padding: 2px; font-weight: bold }
+td#buynow ul { background: #ffe095; width: 100%; margin: 0; border: 1px solid #bf9228 }
+td#buynow li { list-style-type: none; margin: 0 }
+td#buynow li a { display: block; width: 100%; color: #000; border: none }
+td#buynow li a:hover { background: #ffebba }
+td#buynow .pricenote { border: 0 }
+
+div#vntags { margin: 15px 30px 0 30px; border-top: 1px solid $border$; padding: 1px 5% 0 5%; text-align: center; }
+#vntags span { white-space: nowrap; margin-left: 15px; }
+#vntags b { color: $grayedout$; font-weight: normal; font-size: 8px }
+#tagops { float: right; text-align: right; width: auto; margin: 0 30px; }
+#tagops a { margin: 0 0 0 10px; border: 0; outline: none }
+#tagops a.sec { border-left: 1px solid $border$; padding-left: 10px }
+#tagops a.tsel { color: $maintext$; }
+
+.releases table,
+#screenshots table { width: 100%; }
+.releases tr.lang td,
+#screenshots tr.rel td { background: url($_boxbg$) repeat; font-weight: bold; }
+.releases td.tc1 { padding-left: 30px; width: 80px; }
+.releases td.tc2 { text-align: center; width: 50px; }
+.releases td.tc3 { text-align: right; padding: 0; width: 90px; }
+.releases td.tc5 { width: 70px; }
+.releases td.tc5 a { color: $maintext$; border: 0; }
+.releases td.tc6 { text-align: right; width: 25px; padding: 0; }
#screenshots p.rel {
background: url($_boxbg$) repeat;
@@ -677,28 +383,19 @@ a.addnew {
#screenshots p.nsfwtoggle { float: right; margin: 0; }
-#dd_box { position: absolute; left: 0px; border: 1px solid $border$; background-color: $secbg$; z-index: 2 }
-#dd_box ul { list-style-type: none; margin: 0; padding: 0 }
-#dd_box li b { display: block; font-weight: normal; padding-left: 5px; }
-#dd_box li i { display: block; font-style: normal; padding-left: 10px; padding-right: 5px }
-#dd_box li a { display: block; padding-left: 10px; color: $link$; border: 0; padding-right: 5px }
-#dd_box li a:hover { background: url($_boxbg$) repeat }
-
-
-
/***** Vote stats ****/
-.votestats { width: 610px; margin: 0 auto; }
-.votegraph { float: left; margin-right: 20px }
+.votestats { width: 610px; margin: 0 auto; }
+.votegraph { float: left; margin-right: 20px }
.votegraph td { padding: 0 2px; }
.votegraph td.number { text-align: right }
.votegraph td div { float: left; height: 14px; background-color: $border$; margin-right: 2px; padding: 0; }
-.votestats thead td { background: transparent; text-align: center; padding: 2px; }
+.votestats thead td { background: transparent; text-align: center; padding: 2px; }
.votestats tfoot td { text-align: right }
-.votestats div { text-align: center; padding-top: 5px; }
+.votestats div { text-align: center; padding-top: 5px; }
-.recentvotes { width: 300px }
+.recentvotes { width: 300px }
.recentvotes thead tr td b { font-weight: normal; padding-left: 5px }
@@ -715,52 +412,25 @@ a.addnew {
#jt_box_vn_rel td.tc_vn input { width: 280px; }
#jt_box_vn_rel td.tc_rel select { width: 130px; }
-#ds_box {
- position: absolute;
- top: 0;
- border: 1px solid $border$;
- border-top: none;
- background-color: $secbg$;
- cursor: pointer;
- z-index: 2
-}
-#ds_box b {
- padding: 2px 0 0 10px;
-}
-#ds_box tr.selected {
- background: url($_boxbg$) repeat;
-}
-#ds_box table {
- width: 100%;
-}
-
-#jt_box_vn_img div.img {
- float: left;
- height: 400px;
- padding-right: 20px;
-}
-#jt_box_vn_img h2 {
- margin: 0;
-}
+#jt_box_vn_img div.img { float: left; height: 400px; padding-right: 20px; }
+#jt_box_vn_img h2 { margin: 0; }
#jt_box_vn_scr table { width: 95% }
-#scr_table td { height: 108px; border-top: 1px solid #258; padding: 0; padding-right: 5px }
-#scr_table td.thumb { width: 136px; vertical-align: middle }
-#scr_table select { width: 400px; }
-div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px; position: absolute; display: none; left: 0; top: 0; }
-
-
+#scr_table td { height: 108px; border-top: 1px solid #258; padding: 0; padding-right: 5px }
+#scr_table td.thumb { width: 136px; vertical-align: middle }
+#scr_table select { width: 400px; }
+div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px; position: absolute; display: none; left: 0; top: 0; }
/****** VN browse ********/
.vnbrowse thead .tc_s { padding-left: 30px }
-.vnbrowse .tc_s { width: 65px }
-.vnbrowse .tc2 { text-align: right; padding: 0; }
-.vnbrowse .tc3 { padding: 0; }
-.vnbrowse .tc5 { text-align: right; padding-right: 10px }
-.vnbrowse .tc6 { width: 80px }
+.vnbrowse .tc_s { width: 65px }
+.vnbrowse .tc2 { text-align: right; padding: 0; }
+.vnbrowse .tc3 { padding: 0; }
+.vnbrowse .tc5 { text-align: right; padding-right: 10px }
+.vnbrowse .tc6 { width: 80px }
#filselect {
text-align: center;
display: block;
@@ -788,22 +458,11 @@ div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px
#prodrel td.tc6 { width: 25px; text-align: right; padding: 0; }
#expandprodrel { float: right; font-weight: bold; padding-bottom: 2px; border: none }
-.producerbrowse ul {
- float: left;
- margin-top: -5px;
- margin-left: 3%;
- width: 28%;
-}
-.producerbrowse ul li {
- list-style-type: none;
-}
-.producerbrowse ul li acronym {
- margin-right: 5px;
- margin-top: 1px;
-}
-.producerbrowse {
- padding-bottom: 10px!important
-}
+div.producerbrowse { padding-bottom: 10px }
+.producerbrowse ul { float: left; margin-top: -5px; margin-left: 3%; width: 28%; }
+.producerbrowse ul li { list-style-type: none; }
+.producerbrowse ul li acronym { margin-right: 5px; margin-top: 1px; }
+
/***** Producer edit *****/
@@ -820,30 +479,27 @@ div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px
/***** Release page *****/
-.release table {
- width: 400px;
- margin: 0 auto;
-}
-.release .key {
- width: 70px;
-}
+.release table { width: 400px; margin: 0 auto; }
+.release .key { width: 70px; }
+
+
+/* Release edit */
-/* edit */
-.platforms { padding-left: 20px; }
+.platforms { padding-left: 20px; }
.platforms span { display: block; float: left; width: 150px; }
-#jt_box_rel_format h2 { clear: left; padding-top: 10px; }
-#media_div select.qty { width: 90px; }
+#jt_box_rel_format h2 { clear: left; padding-top: 10px; }
+#media_div select.qty { width: 90px; }
#media_div select.medium { width: 150px }
-#media_div { padding-left: 20px; }
-#media_div span { display: block }
+#media_div { padding-left: 20px; }
+#media_div span { display: block }
#jt_box_rel_vn h2, #jt_box_rel_prod h2 { clear: left; padding-top: 10px; }
#jt_box_rel_vn div, #jt_box_rel_vn table,
#jt_box_rel_prod div, #jt_box_rel_prod table { margin-left: 20px }
#jt_box_rel_vn input, #jt_box_rel_prod input { margin-right: 10px; width: 295px }
#jt_box_rel_vn .tc_title, #jt_box_rel_prod .tc_name { width: 310px; padding: 2px }
-#jt_box_rel_prod .tc_role select { width: 100px; margin-right: 10px; }
+#jt_box_rel_prod .tc_role select { width: 100px; margin-right: 10px; }
@@ -860,38 +516,40 @@ div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px
div.chardetails { margin: 0 auto; width: 800px; }
div.charimg { float: left; width: 250px; margin: 0 10px; text-align: center }
div.charimg p { text-align: center; padding: 0px; margin: 0; }
-.chardesc h2 { margin: 0!important; }
+.chardesc h2 { margin: 0; }
.chardesc p { padding: 0 0 0 5px; }
-div.chardetails table { float: left; width: 530px; }
+div.chardetails table { float: left; width: 530px; }
div.chardetails table td.key { width: 80px; }
-div.chardetails.charsep { padding-top: 5px; margin-top: 5px; border-top: 1px solid $border$ }
-#charspoil_sel { clear: right; float: right; }
-#charspoil_sel a { margin: 0 0 0 10px; border: 0; outline: none }
-#maincontent #charspoil_sel a.sel { color: $maintext$; }
+div.chardetails.charsep { padding-top: 5px; margin-top: 5px; border-top: 1px solid $border$ }
+#charspoil_sel { clear: right; float: right; }
+#charspoil_sel a { margin: 0 0 0 10px; border: 0; outline: none }
+#charspoil_sel a.sel { color: $maintext$; }
/***** Char edit *****/
+
#jt_box_chare_img div.img { float: left; height: 300px; padding-right: 20px; }
-#jt_box_chare_img h2 { margin: 0; }
+#jt_box_chare_img h2 { margin: 0; }
#jt_box_chare_traits table { margin-bottom: 10px; margin-left: 10px; }
#jt_box_chare_traits h2 { margin: 0 0 3px 0px; }
-#jt_box_chare_traits td.tc_name { width: 200px }
+#jt_box_chare_traits td.tc_name { width: 200px }
#jt_box_chare_traits td.tc_name input { width: 280px; }
-#jt_box_chare_traits td.tc_spoil { width: 80px; }
+#jt_box_chare_traits td.tc_spoil { width: 80px; }
#jt_box_chare_vns table { margin-bottom: 10px; margin-left: 10px; }
#jt_box_chare_vns h2 { margin: 0 0 3px 0px; }
-#jt_box_chare_vns td.tc_vn { font-weight: bold; padding: 5px 0 3px 0 }
-#jt_box_chare_vns td.tc_vn i{ font-weight: normal; padding-left: 5px; font-style: normal }
-#jt_box_chare_vns td.tc_rel { width: 340px; padding-left: 15px }
-#jt_box_chare_vns td.tc_rel select { width: 340px; }
+#jt_box_chare_vns td.tc_vn { font-weight: bold; padding: 5px 0 3px 0 }
+#jt_box_chare_vns td.tc_vn i { font-weight: normal; padding-left: 5px; font-style: normal }
+#jt_box_chare_vns td.tc_rel { width: 340px; padding-left: 15px }
+#jt_box_chare_vns td.tc_rel select { width: 340px; }
#jt_box_chare_vns td.tc_rol,
-#jt_box_chare_vns td.tc_rol select { width: 150px }
+#jt_box_chare_vns td.tc_rol select { width: 150px }
#jt_box_chare_vns td.tc_spl,
-#jt_box_chare_vns td.tc_spl select { width: 100px }
-#jt_box_chare_vns td.tc_del { padding-left: 5px }
+#jt_box_chare_vns td.tc_spl select { width: 100px }
+#jt_box_chare_vns td.tc_del { padding-left: 5px }
#jt_box_chare_vns td.tc_vnadd input { width: 280px }
+
/***** Char browse *****/
div.charb table { table-layout: fixed }
@@ -902,19 +560,19 @@ div.charb td.tc2 b { margin-left: 10px }
div.charb td.tc2 b a { color: $grayedout$!important }
+
/***** Documentation pages *****/
-.docs { padding: 0 15% 20px 15%; }
-.docs h3 { margin-top: 30px; font-size: 14px }
-.docs h4 { margin-top: 15px; font-size: 12px }
-.docs dd { padding-bottom: 5px; margin-left: 120px; }
-.docs dt { float: left }
-.docs ul.index { display: block; float: right; width: 150px; padding: 2px; margin: 0 0 10px 5px; background: url($_boxbg$) repeat; border: 1px solid $border$; }
-.docs ul.index li { list-style-type: none; }
+.docs { padding: 0 15% 20px 15%; }
+.docs h3 { margin-top: 30px; font-size: 14px }
+.docs h4 { margin-top: 15px; font-size: 12px }
+.docs dd { padding-bottom: 5px; margin-left: 120px; }
+.docs dt { float: left }
+.docs ul.index { display: block; float: right; width: 150px; padding: 2px; margin: 0 0 10px 5px; background: url($_boxbg$) repeat; border: 1px solid $border$; }
+.docs ul.index li { list-style-type: none; }
.docs ul.index li a { margin: 0 0 0 10px; }
-.docs .retired { text-decoration: line-through; }
-.docs dt b { color: $grayedout$; font-weight: normal; font-style: normal; font-size: 12px; }
-
+.docs .retired { text-decoration: line-through; }
+.docs dt b { color: $grayedout$; font-weight: normal; font-style: normal; font-size: 12px; }
@@ -926,7 +584,7 @@ div.votelist td.tc2 { width: 50px; text-align: right; padding-right: 10px }
/***** Wishlist browser ******/
-.wishlist .tc1 { padding-top: 0; padding-bottom: 0; }
+.wishlist .tc1 { padding-top: 0; padding-bottom: 0; }
.wishlist tfoot td { padding: 0 0 0 25px }
@@ -940,33 +598,28 @@ div.votelist td.tc2 { width: 50px; text-align: right; padding-right: 10px }
.browse.rlist .tc3_5 b { margin-left: 10px }
.browse.rlist .tc4 { width: 60px; text-align: right; padding-top: 0; padding-bottom: 0 }
.browse.rlist .tc6 { width: 100px }
-.browse.rlist .relhid .tc6 { padding-left: 15px; width: auto }
.browse.rlist .tc7 { width: 90px }
.browse.rlist .tc8 { width: 70px }
.browse.rlist tfoot select { width: 200px }
+.browse.rlist .relhid .tc6 { padding-left: 15px; width: auto }
/***** User notifications *****/
-.browse.notifies td.tc1 { width: 14px }
-.browse.notifies td.tc3 { width: 90px }
-.browse.notifies td.tc4 { width: 60px }
+.browse.notifies td.tc1 { width: 14px }
+.browse.notifies td.tc3 { width: 90px }
+.browse.notifies td.tc4 { width: 60px }
.browse.notifies tbody td.tc5 { color: $grayedout$; cursor: pointer }
-.browse.notifies td.tc5 i { font-style: normal; color: $maintext$ }
-.browse.notifies .unread td { font-weight: bold }
-.browse.notifies tfoot td { padding: 0 0 0 25px }
+.browse.notifies td.tc5 i { font-style: normal; color: $maintext$ }
+.browse.notifies .unread td { font-weight: bold }
+.browse.notifies tfoot td { padding: 0 0 0 25px }
/***** Userpage *****/
-.userpage table {
- width: 400px;
- margin: 0 auto;
-}
-.userpage .key {
- width: 70px;
-}
+.userpage table { width: 400px; margin: 0 auto; }
+.userpage .key { width: 70px; }
/***** User posts browser ****/
@@ -983,20 +636,22 @@ div.uposts td.tc4 b { margin-left: 10px }
/***** Tag page *****/
-.tagtree { margin-left: 20px; margin-top: -20px; list-style-type: none; }
-.tagtree li { float: left; width: 200px; margin-top: 10px; }
-.tagtree li li { float: none; width: auto; margin-top: 0; }
-.tagtree ul { margin-left: 10px; list-style-type: none; }
+.tagtree { margin-left: 20px; margin-top: -20px; list-style-type: none; }
+.tagtree li { float: left; width: 200px; margin-top: 10px; }
+.tagtree li li { float: none; width: auto; margin-top: 0; }
+.tagtree ul { margin-left: 10px; list-style-type: none; }
.tagvnlist .tc1 { width: 105px; }
.tagvnlist .tc1 i { font-style: normal; font-size: 8px }
.tagvnlist .tc3 { text-align: right; padding: 0; }
.tagvnlist .tc4 { padding: 0; }
.tagvnlist .tc6 { text-align: right; padding-right: 10px; }
+
/***** Tag/trait list (/g/list, /i/list) *****/
.browse.taglist .tc1 { width: 80px }
+
/***** Tag links *****/
.browse.taglinks .tc1 { width: 70px }
@@ -1006,110 +661,56 @@ div.uposts td.tc4 b { margin-left: 10px }
.browse.taglinks .ignored .taglvl.taglvl0 { color: $grayedout$!important }
.browse.taglinks .setfil { font-size: 8px; padding-right: 3px }
+
/***** VN tagmod *****/
#jt_box_tagmod .formtable table td { padding: 1px 5px }
-table.tgl tfoot td { padding-top: 20px!important; }
-table.tgl .tc_you { border-right: 1px solid $border$; border-left: 1px solid $border$; width: 150px; text-align: center }
-table.tgl .tc_others { border-left: 1px solid $border$; width: 150px; text-align: center }
-table.tgl .tc_tagname { min-width: 200px; border-right: 1px solid $border$ }
+table.tgl tfoot td { padding-top: 20px!important; }
+table.tgl .tc_you { border-right: 1px solid $border$; border-left: 1px solid $border$; width: 150px; text-align: center }
+table.tgl .tc_others { border-left: 1px solid $border$; width: 150px; text-align: center }
+table.tgl .tc_tagname { min-width: 200px; border-right: 1px solid $border$ }
table.tgl tbody .tc_tagname { padding-left: 15px!important }
-table.tgl .tc_myvote { padding-left: 30px!important }
-table.tgl .tc_myover { padding: 0!important }
-table.tgl .tc_myspoil { border-right: 1px solid $border$; padding-right: 30px!important; text-align: right; padding-left: 10px!important; cursor: pointer }
-table.tgl .tc_allvote { padding-left: 30px!important; }
-table.tgl .tc_allvote i { font-style: normal; font-size: 8px }
-table.tgl .tc_allspoil { text-align: right; padding-right: 15px!important; }
-table.tgl .tagmod_cat td { font-weight: bold }
-.taglvl { display: block; float: left; width: 8px; height: 12px; border: 1px solid $border$; font-size: 1px; color: $maintext$!important }
-.taglvl0 { width: 15px; border: none!important; font-size: 10px; text-align: center; }
-div.taglvl0 { font-size: 8px; width: 20px!important }
-div.taglvl { border: none!important; width: 10px; height: 14px }
-a.taglvl:hover { border-bottom: 1px solid transparent!important }
+table.tgl .tc_myvote { padding-left: 30px!important }
+table.tgl .tc_myover { padding: 0!important }
+table.tgl .tc_myspoil { border-right: 1px solid $border$; padding-right: 30px!important; text-align: right; padding-left: 10px!important; cursor: pointer }
+table.tgl .tc_allvote { padding-left: 30px!important; }
+table.tgl .tc_allvote i { font-style: normal; font-size: 8px }
+table.tgl .tc_allspoil { text-align: right; padding-right: 15px!important; }
+table.tgl .tagmod_cat td { font-weight: bold }
+.taglvl { display: block; float: left; width: 8px; height: 12px; border: 1px solid $border$; font-size: 1px; color: $maintext$!important }
+.taglvl0 { width: 15px; border: none; font-size: 10px; text-align: center; }
+div.taglvl0 { font-size: 8px; width: 20px!important }
+div.taglvl { border: none; width: 10px; height: 14px }
+a.taglvl:hover { border-bottom: 1px solid transparent }
.taglvlsel.taglvl-3 { background-color: #f00; border-color: #f00 }
.taglvlsel.taglvl-2 { background-color: #f40; border-color: #f40 }
.taglvlsel.taglvl-1 { background-color: #f80; border-color: #f80 }
-.taglvlsel.taglvl1 { background-color: #cf0; border-color: #cf0 }
-.taglvlsel.taglvl2 { background-color: #8f0; border-color: #8f0 }
-.taglvlsel.taglvl3 { background-color: #0f0; border-color: #0f0 }
-
-
-
-
-/***** Warning/Notice Box *****/
-
-div.warning, div.notice {
- margin: 5px 10%;
- padding: 15px;
- background-color: $warnbg$;
- border: 1px solid $warnborder$;
-}
-div.notice {
- background-color: $noticebg$;
- border: 1px solid $noticeborder$;
-}
-div.warning ul, div.notice ul {
- margin-left: 0;
-}
-div.warning li, div.notice li {
- margin-left: 20px;
-}
-#maincontent div.warning h2, #maincontent div.notice h2 {
- font-size: 11px;
- font-weight: bold;
- margin: 0;
-}
+.taglvlsel.taglvl1 { background-color: #cf0; border-color: #cf0 }
+.taglvlsel.taglvl2 { background-color: #8f0; border-color: #8f0 }
+.taglvlsel.taglvl3 { background-color: #0f0; border-color: #0f0 }
/****** Revision information ******/
-div.revision {
- padding-bottom: 10px!important;
-}
-div.revision div, div.revision table {
+div.revision div.rev, div.revision table {
border: 1px solid $border$;
margin: 0 auto;
width: 90%;
background-color: $secbg$;
clear: both;
}
-div.revision table thead tr td {
- background-color: transparent!important;
- text-align: center;
- font-weight: normal;
-}
-div.revision table td {
- border-right: 1px solid $border$;
- padding: 5px;
-}
-div.revision td.tcval {
- width: 44%;
-}
-div.revision div {
- padding: 5px;
- text-align: center;
-}
-.diff_add {
- font-weight: normal;
- background-color: $diffadd$;
-}
-.diff_del {
- font-weight: normal;
- background-color: $diffdel$;
-}
-div.revision .next {
- float: right;
- margin-right: 5%;
-}
-div.revision .prev {
- float: left;
- margin-left: 5%;
-}
-div.revision .item {
- text-align: center;
-}
+div.revision { padding-bottom: 10px; }
+div.revision table thead tr td { background-color: transparent!important; text-align: center; font-weight: normal; }
+div.revision table td { border-right: 1px solid $border$; padding: 5px; }
+div.revision td.tcval { width: 44%; }
+div.revision div.rev { padding: 5px; text-align: center; }
+.diff_add { font-weight: normal; background-color: $diffadd$; }
+.diff_del { font-weight: normal; background-color: $diffdel$; }
+div.revision .next { float: right; margin-right: 5%; }
+div.revision .prev { float: left; margin-left: 5%; }
+div.revision .item { text-align: center; }
@@ -1124,12 +725,12 @@ div#iv_view {
padding: 5px;
text-align: center;
}
-#iv_view a { border: 0; font-weight: bold; font-size: 12px }
+#iv_view a { border: 0; font-weight: bold; font-size: 12px }
#iv_view img { cursor: pointer }
-#ivclose { float: right; padding-left: 10px }
-#ivnext { padding-left: 5px; }
-#ivprev { padding-right: 5px; }
-#ivfull { float: left; padding-right: 10px; }
+#ivclose { float: right; padding-left: 10px }
+#ivnext { padding-left: 5px; }
+#ivprev { padding-right: 5px; }
+#ivfull { float: left; padding-right: 10px; }
#ivimgload {
position: absolute;
display: block;
@@ -1145,7 +746,6 @@ div#iv_view {
-
/****** filter selector *****/
div#fil_div {
@@ -1158,23 +758,22 @@ div#fil_div {
width: 600px;
text-align: center;
}
-#fil_div a.close { float: right; color: $link$; border: 0; font-weight: bold }
-#fil_div p.browseopts { padding: 2px 50px; line-height: 23px }
-#fil_div .browseopts a { outline: none }
+#fil_div a.close { float: right; border: 0; font-weight: bold }
+#fil_div p.browseopts { padding: 2px 50px; line-height: 23px }
+#fil_div .browseopts a { outline: none; color: $maintext$ }
#fil_div .browseopts a.active { font-weight: bold }
-#fil_div b.ruler { display: block; margin: auto; width: 93%; height: 1px; border-bottom: 1px solid $border$; margin-bottom: 5px }
-#fil_div h3 { width: 100%; text-align: center; font-size: 11px }
-#fil_div table { width: 93%; text-align: left; margin: 0 auto 5px auto }
+#fil_div b.ruler { display: block; margin: auto; width: 93%; height: 1px; border-bottom: 1px solid $border$; margin-bottom: 5px }
+#fil_div h3 { width: 100%; text-align: center; font-size: 11px }
+#fil_div table { width: 93%; text-align: left; margin: 0 auto 5px auto }
#fil_div table td.label label { width: 120px }
#fil_div table td.label b { display: block; font-weight: normal; padding: 10px 5px 0 0 }
#fil_div table td.check { width: 15px }
#fil_div label.active { font-weight: bold }
-#fil_div .opts a { border: 0; outline: none; color: $link$ }
+#fil_div .opts a { border: 0; outline: none }
#fil_div .opts b { margin: 0 7px; font-weight: normal }
#fil_div .opts a.tsel { color: $maintext$; }
-#filselect i { font-style: normal }
-#fil_div table ul { margin: 0 0 0 15px }
-#fil_div table ul a { color: $link$ }
+#filselect i { font-style: normal }
+#fil_div table ul { margin: 0 0 0 15px }
diff --git a/lib/Multi/Core.pm b/lib/Multi/Core.pm
index 9eaa6269..3c9d8bae 100644
--- a/lib/Multi/Core.pm
+++ b/lib/Multi/Core.pm
@@ -98,7 +98,7 @@ sub log_msg { # msg
sub log { # level, msg
(my $p = eval { $_[SENDER][2]{$_[CALLER_STATE]}[0] } || '') =~ s/^Multi:://;
log_msg sprintf '%s::%s: %s', $p, $_[CALLER_STATE],
- $_[ARG1] ? sprintf($_[ARG0], @_[ARG1..$#_]) : $_[ARG0];
+ $#_>ARG0 ? sprintf($_[ARG0], @_[ARG1..$#_]) : $_[ARG0];
}
diff --git a/lib/Multi/Maintenance.pm b/lib/Multi/Maintenance.pm
index 5b092d0a..33154f9b 100644
--- a/lib/Multi/Maintenance.pm
+++ b/lib/Multi/Maintenance.pm
@@ -18,13 +18,13 @@ sub spawn {
package_states => [
$p => [qw|
_start shutdown set_daily daily set_monthly monthly log_stats
- vncache_inc tagcache vnpopularity vnrating cleangraphs cleansessions cleannotifications
+ vncache_inc tagcache traitcache vnpopularity vnrating cleangraphs cleansessions cleannotifications
vncache_full usercache statscache logrotate
vnsearch_check vnsearch_gettitles vnsearch_update
|],
],
heap => {
- daily => [qw|vncache_inc tagcache vnpopularity vnrating cleangraphs cleansessions cleannotifications|],
+ daily => [qw|vncache_inc tagcache traitcache vnpopularity vnrating cleangraphs cleansessions cleannotifications|],
monthly => [qw|vncache_full usercache statscache logrotate|],
vnsearch_checkdelay => 3600,
@_,
@@ -46,6 +46,7 @@ sub _start {
sub shutdown {
$_[KERNEL]->delay('daily');
$_[KERNEL]->delay('monthly');
+ $_[KERNEL]->delay('vnsearch_check');
$_[KERNEL]->alias_remove('maintenance');
}
@@ -121,13 +122,19 @@ sub vncache_inc {
sub tagcache {
- # takes about 2 seconds max, still OK
+ # takes about 5 seconds max, still OK
$_[KERNEL]->post(pg => do => 'SELECT tag_vn_calc()', undef, 'log_stats', 'tagcache');
}
+sub traitcache {
+ # still takes less than a second
+ $_[KERNEL]->post(pg => do => 'SELECT traits_chars_calc()', undef, 'log_stats', 'traitcache');
+}
+
+
sub vnpopularity {
- # still takes at most 3 seconds. let's hope that doesn't increase...
+ # takes a bit more than 8 seconds, meh...
$_[KERNEL]->post(pg => do => 'SELECT update_vnpopularity()', undef, 'log_stats', 'vnpopularity');
}
@@ -216,6 +223,9 @@ sub statscache {
q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM vn WHERE hidden = FALSE) WHERE section = 'vn'|,
q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM releases WHERE hidden = FALSE) WHERE section = 'releases'|,
q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM producers WHERE hidden = FALSE) WHERE section = 'producers'|,
+ q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM chars WHERE hidden = FALSE) WHERE section = 'chars'|,
+ q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM tags WHERE state = 2) WHERE section = 'tags'|,
+ q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM traits WHERE state = 2) WHERE section = 'traits'|,
q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM threads WHERE hidden = FALSE) WHERE section = 'threads'|,
q|UPDATE stats_cache SET count = (SELECT COUNT(*) FROM threads_posts WHERE hidden = FALSE
AND EXISTS(SELECT 1 FROM threads WHERE threads.id = tid AND threads.hidden = FALSE)) WHERE section = 'threads_posts'|
diff --git a/lib/VNDB/DB/Affiliates.pm b/lib/VNDB/DB/Affiliates.pm
new file mode 100644
index 00000000..51320b47
--- /dev/null
+++ b/lib/VNDB/DB/Affiliates.pm
@@ -0,0 +1,69 @@
+
+package VNDB::DB::Affiliates;
+
+use strict;
+use warnings;
+use POSIX 'strftime';
+use Exporter 'import';
+
+our @EXPORT = qw|dbAffiliateGet dbAffiliateEdit dbAffiliateDel dbAffiliateAdd|;
+
+
+# options: id rids affiliate hidden sort reverse
+# what: release
+sub dbAffiliateGet {
+ my($self, %o) = @_;
+ $o{sort} ||= 'id';
+ $o{reverse} //= 0;
+
+ my %where = (
+ $o{id} ? ('id = ?' => $o{id}) : (),
+ $o{rids} ? ('rid IN(!l)' => [$o{rids}]) : (),
+ defined($o{affiliate}) ? ('affiliate = ?' => $o{affiliate}) : (),
+ defined($o{hidden}) ? ('!s af.hidden' => $o{hidden} ? '' : 'NOT') : (),
+ );
+
+ my $join = $o{what} ? 'JOIN releases r ON r.id = af.rid JOIN releases_rev rr ON rr.id = r.latest' : '';
+ my $select = $o{what} ? ', rr.title' : '';
+
+ my $order = sprintf {
+ id => 'af.id %s',
+ rel => 'rr.title %s',
+ prio => 'af.priority %s',
+ url => 'af.url %s',
+ lastfetch => 'af.lastfetch %s',
+ }->{$o{sort}}, $o{reverse} ? 'DESC' : 'ASC';
+
+ return $self->dbAll(qq|
+ SELECT af.id, af.rid, af.hidden, af.priority, af.affiliate, af.url, af.version,
+ extract('epoch' from af.lastfetch) as lastfetch, af.price$select
+ FROM affiliate_links af
+ $join
+ !W
+ ORDER BY !s|, \%where, $order);
+}
+
+
+sub dbAffiliateDel {
+ my($self, $id) = @_;
+ $self->dbExec('DELETE FROM affiliate_links WHERE id = ?', $id);
+}
+
+
+sub dbAffiliateEdit {
+ my($self, $id, %ops) = @_;
+ my %set;
+ exists($ops{$_}) && ($set{"$_ = ?"} = $ops{$_}) for(qw|rid priority hidden affiliate url version|);
+ return if !keys %set;
+ $self->dbExec('UPDATE affiliate_links !H WHERE id = ?', \%set, $id);
+}
+
+sub dbAffiliateAdd {
+ my($self, %ops) = @_;
+ $self->dbExec('INSERT INTO affiliate_links (rid, priority, hidden, affiliate, url, version) VALUES(!l)',
+ [@ops{qw| rid priority hidden affiliate url version|}]);
+}
+
+
+1;
+
diff --git a/lib/VNDB/DB/Misc.pm b/lib/VNDB/DB/Misc.pm
index 2f98abe2..3155ca56 100644
--- a/lib/VNDB/DB/Misc.pm
+++ b/lib/VNDB/DB/Misc.pm
@@ -64,6 +64,7 @@ sub dbRevisionGet {
# what types should we join?
my @types = (
!$o{type} ? ('v', 'r', 'p', 'c') :
+ ref($o{type}) ? @{$o{type}} :
$o{type} ne 'v' ? $o{type} :
$o{releases} ? ('v', 'r') : 'v'
);
@@ -73,7 +74,7 @@ sub dbRevisionGet {
q{((h.type = 'v' AND vr.vid = ?) OR (h.type = 'r' AND h.id = ANY(ARRAY(SELECT rv.rid FROM releases_vn rv WHERE rv.vid = ?))))} => [$o{iid}, $o{iid}],
) : (
$o{type} ? (
- 'h.type = ?' => $o{type} ) : (),
+ 'h.type IN(!l)' => [ ref($o{type})?$o{type}:[$o{type}] ] ) : (),
$o{iid} ? (
'!sr.!sid = ?' => [ $o{type}, $o{type}, $o{iid} ] ) : (),
),
diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm
index bd2d5bd3..6f9465f8 100644
--- a/lib/VNDB/DB/Releases.pm
+++ b/lib/VNDB/DB/Releases.pm
@@ -12,7 +12,7 @@ our @EXPORT = qw|dbReleaseGet dbReleaseRevisionInsert|;
# Options: id vid pid rev released page results what med sort reverse date_before date_after
# plat lang olang type minage search resolution freeware doujin voiced ani_story ani_ero
-# What: extended changes vn producers platforms media
+# What: extended changes vn producers platforms media affiliates
# Sort: title released minage
sub dbReleaseGet {
my($self, %o) = @_;
diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm
index 17f360cc..abea9bec 100644
--- a/lib/VNDB/DB/Users.pm
+++ b/lib/VNDB/DB/Users.pm
@@ -55,7 +55,7 @@ sub dbUserGet {
qw|id username c_votes c_changes c_tags|,
q|extract('epoch' from registered) as registered|,
$o{what} =~ /extended/ ? (
- qw|mail rank salt ign_votes|,
+ qw|mail perm salt ign_votes|,
q|encode(passwd, 'hex') AS passwd|
) : (),
$o{what} =~ /hide_list/ ? 'up.value AS hide_list' : (),
@@ -118,7 +118,7 @@ sub dbUserEdit {
my %h;
defined $o{$_} && ($h{$_.' = ?'} = $o{$_})
- for (qw| username mail rank salt ign_votes |);
+ for (qw| username mail perm salt ign_votes |);
$h{'passwd = decode(?, \'hex\')'} = $o{passwd}
if defined $o{passwd};
diff --git a/lib/VNDB/Handler/Affiliates.pm b/lib/VNDB/Handler/Affiliates.pm
new file mode 100644
index 00000000..679d6ee2
--- /dev/null
+++ b/lib/VNDB/Handler/Affiliates.pm
@@ -0,0 +1,146 @@
+
+package VNDB::Handler::Affiliates;
+
+use strict;
+use warnings;
+use TUWF ':html';
+use VNDB::Func;
+
+
+TUWF::register(
+ qr{affiliates} => \&list,
+ qr{affiliates/del/([1-9]\d*)} => \&linkdel,
+ qr{affiliates/edit/([1-9]\d*)} => \&edit,
+ qr{affiliates/new} => \&edit,
+);
+
+
+sub list {
+ my $self = shift;
+
+ return $self->htmlDenied if !$self->authCan('affiliate');
+ my $f = $self->formValidate(
+ { get => 'a', required => 0, enum => [ 0..$#{$self->{affiliates}} ] },
+ { get => 'h', required => 0, default => 0, enum => [ -1..1 ] },
+ { get => 'o', required => 0, default => 'a', enum => ['a', 'd'] },
+ { get => 's', required => 0, default => 'rel', enum => [qw|rel prio url lastfetch|] },
+ );
+ return $self->resNotFound if $f->{_err};
+
+ $self->htmlHeader(title => 'Affiliate administration interface');
+ div class => 'mainbox';
+ h1 'Affiliate administration interface';
+ p class => 'browseopts';
+ a defined($f->{a}) && $f->{a} == $_ ? (class => 'optselected') : (), href => "/affiliates?a=$_", $self->{affiliates}[$_]{name}
+ for (grep $self->{affiliates}[$_], 0..$#{$self->{affiliates}});
+ end;
+ if(defined $f->{a}) {
+ p class => 'browseopts';
+ a $f->{h} == -1 ? (class => 'optselected') : (), href => "/affiliates?a=$f->{a};h=-1",'all';
+ a $f->{h} == 1 ? (class => 'optselected') : (), href => "/affiliates?a=$f->{a};h=1", 'hidden';
+ a $f->{h} == 0 ? (class => 'optselected') : (), href => "/affiliates?a=$f->{a};h=0", 'non-hidden';
+ end;
+ }
+ end;
+
+ if(defined $f->{a}) {
+ my $list = $self->dbAffiliateGet(
+ affiliate => $f->{a}, hidden => $f->{h}==-1?undef:$f->{h},
+ what => 'release',
+ sort => $f->{s}, reverse => $f->{o} eq 'd'
+ );
+ $self->htmlBrowse(
+ items => $list,
+ nextpage => 0,
+ options => {p=>0, %$f},
+ pageurl => '',
+ sorturl => "/affiliates?a=$f->{a};h=$f->{h}",
+ header => [
+ ['Release', 'rel'],
+ ['Version'],
+ ['Hid'],
+ ['Prio', 'prio'],
+ ['Price / Lastfetch', 'lastfetch'],
+ ['', 'url' ]
+ ],
+ row => sub {
+ my($s, $n, $l) = @_;
+ Tr $n % 2 ? (class => 'odd') : ();
+ td class => 'tc1'; a href => "/r$l->{rid}", shorten $l->{title}, 50; end;
+ td class => 'tc2', $l->{version} || '<default>';
+ td class => 'tc3', $l->{hidden} ? 'YES' : 'no';
+ td class => 'tc4', $l->{priority};
+ td class => 'tc5', sprintf '%s / %s', $l->{price}, $l->{lastfetch} ? $self->{l10n}->age($l->{lastfetch}) : '-';
+ td class => 'tc6';
+ a href => $l->{url}, 'link';
+ txt ' | ';
+ a href => "/affiliates/edit/$l->{id}", 'edit';
+ txt ' | ';
+ a href => "/affiliates/del/$l->{id}?formcode=".$self->authGetCode("/affiliates/del/$l->{id}"), 'del';
+ end;
+ end;
+ },
+ );
+ }
+ $self->htmlFooter;
+}
+
+
+sub linkdel {
+ my($self, $id) = @_;
+ return $self->htmlDenied if !$self->authCan('affiliate');
+ return if !$self->authCheckCode;
+ my $l = $self->dbAffiliateGet(id => $id)->[0];
+ return $self->resNotFound if !$l;
+ $self->dbAffiliateDel($id);
+ $self->resRedirect("/affiliates?a=$l->{affiliate}");
+}
+
+
+sub edit {
+ my($self, $id) = @_;
+ return $self->htmlDenied if !$self->authCan('affiliate');
+
+ my $r = $id && $self->dbAffiliateGet(id => $id)->[0];
+ return $self->resNotFound if $id && !$r;
+
+ my $frm;
+ if($self->reqMethod eq 'POST') {
+ return if !$self->authCheckCode;
+ $frm = $self->formValidate(
+ { post => 'rid', required => 1, template => 'int' },
+ { post => 'priority', required => 0, default => 0, template => 'int' },
+ { post => 'hidden', required => 0, default => 0, enum => [0,1] },
+ { post => 'affiliate',required => 1, enum => [0..$#{$self->{affiliates}}] },
+ { post => 'url', required => 1 },
+ { post => 'version', required => 0, default => '' },
+ );
+ if(!$frm->{_err}) {
+ $self->dbAffiliateEdit($id, %$frm) if $id;
+ $self->dbAffiliateAdd(%$frm) if !$id;
+ return $self->resRedirect("/affiliates?a=$frm->{affiliate}", 'post');
+ }
+ }
+
+ if($id) {
+ $frm->{$_} = $r->{$_} for(qw|rid priority hidden affiliate url version|);
+ } else {
+ $frm->{rid} = $self->reqGet('rid');
+ }
+
+ $self->htmlHeader(title => 'Edit affiliate link');
+ $self->htmlForm({ frm => $frm, action => $id ? "/affiliates/edit/$id" : '/affiliates/new' }, 'blah' => [ 'Edit affiliate link',
+ [ input => short => 'rid', name => 'Release ID', width => 100 ],
+ [ input => short => 'priority', name => 'Priority', width => 50 ],
+ [ check => short => 'hidden', name => 'Hidden' ],
+ [ select => short => 'affiliate', name => 'Affiliate', options => [ map
+ [ $_, $self->{affiliates}[$_]{name} ], grep $self->{affiliates}[$_], 0..$#{$self->{affiliates}} ] ],
+ [ input => short => 'url', name => 'URL', width => 400 ],
+ [ input => short => 'version', name => 'Version', width => 400 ],
+ ]);
+ $self->htmlFooter;
+}
+
+
+1;
+
diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm
index 7968b66c..8edb6236 100644
--- a/lib/VNDB/Handler/Chars.pm
+++ b/lib/VNDB/Handler/Chars.pm
@@ -233,7 +233,7 @@ sub charTable {
td class => 'chardesc', colspan => 2;
h2 mt '_charp_description';
p;
- lit bb2html $r->{desc};
+ lit bb2html $r->{desc}, 0, 1;
end;
end;
end;
@@ -257,7 +257,7 @@ sub edit {
$rev = undef if !$r || $r->{cid} == $r->{latest};
return $self->htmlDenied if !$self->authCan('charedit')
- || $id && ($r->{locked} && !$self->authCan('lock') || $r->{hidden} && !$self->authCan('del'));
+ || $id && (($r->{locked} || $r->{hidden}) && !$self->authCan('dbmod'));
my %b4 = !$id ? () : (
(map +($_ => $r->{$_}), qw|name original alias desc image ihid ilock s_bust s_waist s_hip height weight bloodt gender main_spoil|),
@@ -331,14 +331,14 @@ sub edit {
$frm->{vns} = \@vns;
my $nrev = $self->dbItemEdit(c => !$copy && $id ? $r->{cid} : undef, %$frm);
-
- # TEMPORARY SOLUTION! I'll investigate more efficient solutions and incremental updates whenever I have more data
- $self->dbExec('SELECT traits_chars_calc()');
-
return $self->resRedirect("/c$nrev->{iid}.$nrev->{rev}", 'post');
}
}
+ if(!$id) {
+ my $vid = $self->formValidate({ get => 'vid', required => 1, template => 'int'});
+ $frm->{vns} //= "$vid->{vid}-0-0-primary" if !$vid->{_err};
+ }
$frm->{$_} //= $b4{$_} for keys %b4;
$frm->{editsum} //= sprintf 'Reverted to revision c%d.%d', $id, $rev if !$copy && $rev;
$frm->{editsum} = sprintf 'New character based on c%d.%d', $id, $r->{rev} if $copy;
diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm
index 8f5b4949..643af1cf 100644
--- a/lib/VNDB/Handler/Misc.pm
+++ b/lib/VNDB/Handler/Misc.pm
@@ -196,7 +196,7 @@ sub history {
{ get => 'p', required => 0, default => 1, template => 'int' },
{ get => 'm', required => 0, default => !$type, enum => [ 0, 1 ] },
{ get => 'h', required => 0, default => 0, enum => [ -1..1 ] },
- { get => 't', required => 0, default => '', enum => [qw|v r p c|] },
+ { get => 't', required => 0, default => '', enum => [qw|v r p c a|] },
{ get => 'e', required => 0, default => 0, enum => [ -1..1 ] },
{ get => 'r', required => 0, default => 0, enum => [ 0, 1 ] },
);
@@ -216,7 +216,7 @@ sub history {
what => 'item user',
$type && $type ne 'u' ? ( type => $type, iid => $id ) : (),
$type eq 'u' ? ( uid => $id ) : (),
- $f->{t} ? ( type => $f->{t} ) : (),
+ $f->{t} ? ( type => $f->{t} eq 'a' ? [qw|v r p|] : $f->{t} ) : (),
page => $f->{p},
results => 50,
auto => $f->{m},
@@ -250,7 +250,7 @@ sub history {
end;
}
if(!$type || $type eq 'u') {
- if($self->authCan('del')) {
+ if($self->authCan('dbmod')) {
p class => 'browseopts';
a $f->{h} == 1 ? (class => 'optselected') : (), href => $u->(h => 1), mt '_hist_filter_hidedel';
a $f->{h} == -1 ? (class => 'optselected') : (), href => $u->(h => -1), mt '_hist_filter_showdel';
@@ -262,6 +262,7 @@ sub history {
a $f->{t} eq 'r' ? (class => 'optselected') : (), href => $u->(t => 'r'), mt '_hist_filter_onlyreleases';
a $f->{t} eq 'p' ? (class => 'optselected') : (), href => $u->(t => 'p'), mt '_hist_filter_onlyproducers';
a $f->{t} eq 'c' ? (class => 'optselected') : (), href => $u->(t => 'c'), mt '_hist_filter_onlychars';
+ a $f->{t} eq 'a' ? (class => 'optselected') : (), href => $u->(t => 'a'), mt '_hist_filter_nochars';
end;
p class => 'browseopts';
a !$f->{e} ? (class => 'optselected') : (), href => $u->(e => 0), mt '_hist_filter_allactions';
diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm
index 5030c1a3..52db6edf 100644
--- a/lib/VNDB/Handler/Producers.pm
+++ b/lib/VNDB/Handler/Producers.pm
@@ -205,7 +205,7 @@ sub edit {
$rev = undef if !$p || $p->{cid} == $p->{latest};
return $self->htmlDenied if !$self->authCan('edit')
- || $pid && ($p->{locked} && !$self->authCan('lock') || $p->{hidden} && !$self->authCan('del'));
+ || $pid && (($p->{locked} || $p->{hidden}) && !$self->authCan('dbmod'));
my %b4 = !$pid ? () : (
(map { $_ => $p->{$_} } qw|type name original lang website desc alias ihid ilock|),
diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm
index 159b7c3f..dd2b278e 100644
--- a/lib/VNDB/Handler/Releases.pm
+++ b/lib/VNDB/Handler/Releases.pm
@@ -280,7 +280,7 @@ sub edit {
return $self->resNotFound if $vid && !$v->{id};
return $self->htmlDenied if !$self->authCan('edit')
- || $rid && ($r->{locked} && !$self->authCan('lock') || $r->{hidden} && !$self->authCan('del'));
+ || $rid && (($r->{locked} || $r->{hidden}) && !$self->authCan('dbmod'));
my $vn = $rid ? $r->{vn} : [{ vid => $vid, title => $v->{title} }];
my %b4 = !$rid ? () : (
diff --git a/lib/VNDB/Handler/Traits.pm b/lib/VNDB/Handler/Traits.pm
index 1f3c2872..ea599ed1 100644
--- a/lib/VNDB/Handler/Traits.pm
+++ b/lib/VNDB/Handler/Traits.pm
@@ -104,8 +104,7 @@ sub traitpage {
if(!@$chars) {
p; br; br; txt mt '_traitp_nochars'; end;
}
- # not really cached at the moment
- # p; br; txt mt '_traitp_cached'; end;
+ p; br; txt mt '_traitp_cached'; end;
end 'div';
@$chars && $self->charBrowseTable($chars, $np, $f, "/i$trait?m=$f->{m}");
@@ -171,9 +170,6 @@ sub traitedit {
} else {
$self->dbTraitEdit($trait, %opts, upddate => $frm->{state} == 2 && $t->{state} != 2) if $trait;
_set_childs_group($self, $trait, $group||$trait) if ($group||0) != ($t->{group}||0);
-
- # TEMPORARY SOLUTION! I'll investigate more efficient solutions and incremental updates whenever I have more data
- $self->dbExec('SELECT traits_chars_calc()');
}
$self->resRedirect("/i$trait", 'post');
return;
diff --git a/lib/VNDB/Handler/ULists.pm b/lib/VNDB/Handler/ULists.pm
index 363c2262..1c82d982 100644
--- a/lib/VNDB/Handler/ULists.pm
+++ b/lib/VNDB/Handler/ULists.pm
@@ -239,6 +239,7 @@ sub wishlist {
{ post => 'sel', required => 0, default => 0, multi => 1, template => 'int' },
{ post => 'batchedit', required => 1, enum => [ -1, @{$self->{wishlist_status}} ] },
);
+ $frm->{sel} = [ grep $_, @{$frm->{sel}} ]; # weed out "select all" checkbox
if(!$frm->{_err} && @{$frm->{sel}} && $frm->{sel}[0]) {
$self->dbWishListDel($uid, $frm->{sel}) if $frm->{batchedit} == -1;
$self->dbWishListAdd($frm->{sel}, $uid, $frm->{batchedit}) if $frm->{batchedit} >= 0;
@@ -303,6 +304,8 @@ sub wishlist {
$own ? (footer => sub {
Tr;
td colspan => 3;
+ input type => 'checkbox', class => 'checkall', name => 'sel', value => 0;
+ txt ' ';
Select name => 'batchedit', id => 'batchedit';
option mt '_wishlist_select';
optgroup label => mt '_wishlist_changeprio';
diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm
index 75ddb547..dc748f75 100644
--- a/lib/VNDB/Handler/Users.pm
+++ b/lib/VNDB/Handler/Users.pm
@@ -301,7 +301,7 @@ sub edit {
$frm = $self->formValidate(
$self->authCan('usermod') ? (
{ post => 'usrname', template => 'pname', minlength => 2, maxlength => 15 },
- { post => 'rank', enum => [ 1..$#{$self->{user_ranks}} ] },
+ { post => 'perms', required => 0, multi => 1, enum => [ keys %{$self->{permissions}} ] },
{ post => 'ign_votes', required => 0, default => 0 },
) : (),
{ post => 'mail', template => 'mail' },
@@ -318,7 +318,10 @@ sub edit {
$self->dbUserPrefSet($uid, $_ => $frm->{$_}) for (qw|skin customcss show_nsfw hide_list |);
my %o;
$o{username} = $frm->{usrname} if $frm->{usrname};
- $o{rank} = $frm->{rank} if $frm->{rank};
+ if($self->authCan('usermod')) {
+ $o{perm} = 0;
+ $o{perm} += $self->{permissions}{$_} for(@{ delete $frm->{perms} });
+ }
$o{mail} = $frm->{mail};
($o{passwd}, $o{salt}) = $self->authPreparePass($frm->{usrpass}) if $frm->{usrpass};
$o{ign_votes} = $frm->{ign_votes} ? 1 : 0 if $self->authCan('usermod');
@@ -330,8 +333,9 @@ sub edit {
}
# fill out default values
- $frm->{usrname} ||= $u->{username};
- $frm->{$_} ||= $u->{$_} for(qw|rank mail|);
+ $frm->{usrname} ||= $u->{username};
+ $frm->{mail} ||= $u->{mail};
+ $frm->{perms} ||= [ grep $u->{perm} & $self->{permissions}{$_}, keys %{$self->{permissions}} ];
$frm->{$_} //= $u->{prefs}{$_} for(qw|skin customcss show_nsfw hide_list|);
$frm->{ign_votes} = $u->{ign_votes} if !defined $frm->{ign_votes};
@@ -350,8 +354,8 @@ sub edit {
[ part => title => mt '_usere_geninfo' ],
$self->authCan('usermod') ? (
[ input => short => 'usrname', name => mt('_usere_username') ],
- [ select => short => 'rank', name => mt('_usere_rank'), options => [
- map [ $_, mt '_urank_'.$_ ], 1..$#{$self->{user_ranks}} ] ],
+ [ select => short => 'perms', name => mt('_usere_perm'), multi => 1, size => (scalar keys %{$self->{permissions}}), options => [
+ map [ $_, $_ ], sort keys %{$self->{permissions}} ] ],
[ check => short => 'ign_votes', name => mt '_usere_ignvotes' ],
) : (
[ static => label => mt('_usere_username'), content => $frm->{usrname} ],
@@ -611,7 +615,7 @@ sub notifies {
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' ],
@@ -639,6 +643,8 @@ sub notifies {
footer => sub {
Tr;
td colspan => 5;
+ input type => 'checkbox', class => 'checkall', name => 'notifysel', value => 0;
+ txt ' ';
input type => 'submit', name => 'markread', value => mt '_usern_but_markread';
input type => 'submit', name => 'remove', value => mt '_usern_but_remove';
b class => 'grayedout', ' '.mt '_usern_autodel';
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index ce564fc0..d0848c09 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -23,12 +23,12 @@ sub edit {
$rev = undef if !$vid || $v->{cid} == $v->{latest};
return $self->htmlDenied if !$self->authCan('edit')
- || $vid && ($v->{locked} && !$self->authCan('lock') || $v->{hidden} && !$self->authCan('del'));
+ || $vid && (($v->{locked} || $v->{hidden}) && !$self->authCan('dbmod'));
my $r = $v ? $self->dbReleaseGet(vid => $v->{id}) : [];
my %b4 = !$vid ? () : (
- (map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai l_vnn img_nsfw ihid ilock|),
+ (map { $_ => $v->{$_} } qw|title original desc alias length l_wp l_encubed l_renai l_vnn image img_nsfw ihid ilock|),
anime => join(' ', sort { $a <=> $b } map $_->{id}, @{$v->{anime}}),
vnrelations => join('|||', map $_->{relation}.','.$_->{id}.','.($_->{official}?1:0).','.$_->{title}, sort { $a->{id} <=> $b->{id} } @{$v->{relations}}),
screenshots => join(' ', map sprintf('%d,%d,%d', $_->{id}, $_->{nsfw}?1:0, $_->{rid}), @{$v->{screenshots}}),
@@ -48,7 +48,7 @@ sub edit {
{ post => 'l_renai', required => 0, default => '', maxlength => 100 },
{ post => 'l_vnn', required => 0, default => $b4{l_vnn}||0, template => 'int' },
{ post => 'anime', required => 0, default => '' },
- { post => 'previmage', required => 0, default => 0, template => 'int' },
+ { post => 'image', required => 0, default => 0, template => 'int' },
{ post => 'img_nsfw', required => 0, default => 0 },
{ post => 'vnrelations', required => 0, default => '', maxlength => 5000 },
{ post => 'screenshots', required => 0, default => '', maxlength => 1000 },
@@ -60,7 +60,7 @@ sub edit {
push @{$frm->{_err}}, 'badeditsum' if !$frm->{editsum} || lc($frm->{editsum}) eq lc($frm->{desc});
# handle image upload
- my $image = _uploadimage($self, $v, $frm);
+ $frm->{image} = _uploadimage($self, $v, $frm);
if(!$frm->{_err}) {
# parse and re-sort fields that have multiple representations of the same information
@@ -86,15 +86,13 @@ sub edit {
# nothing changed? just redirect
return $self->resRedirect("/v$vid", 'post')
- if $vid && !$self->reqPost('img') && $image == $v->{image}
- && !grep $frm->{$_} ne $b4{$_}, keys %b4;
+ if $vid && !grep $frm->{$_} ne $b4{$_}, keys %b4;
# 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 ihid ilock|),
+ (map { $_ => $frm->{$_} } qw|title original image alias desc length l_wp l_encubed l_renai l_vnn editsum img_nsfw ihid ilock|),
anime => [ keys %$anime ],
relations => $relations,
- image => $image,
screenshots => $screenshots,
);
@@ -123,7 +121,7 @@ sub edit {
sub _uploadimage {
my($self, $v, $frm) = @_;
- return $v ? $frm->{previmage} : 0 if $frm->{_err} || !$self->reqPost('img');
+ return $v ? $frm->{image} : 0 if $frm->{_err} || !$self->reqPost('img');
# perform some elementary checks
my $imgdata = $self->reqUploadRaw('img');
@@ -163,29 +161,31 @@ sub _form {
[ static => content => mt '_vnedit_anime_msg' ],
],
- vn_img => [ mt('_vnedit_image'),
- [ hidden => short => 'previmage', value => $v ? $v->{image} : 0 ],
- [ static => nolabel => 1, content => sub {
- div class => 'img';
- p mt '_vnedit_image_none' if !$v || !$v->{image};
- p mt '_vnedit_image_processing' if $v && $v->{image} < 0;
- img src => sprintf("%s/cv/%02d/%d.jpg", $self->{url_static}, $v->{image}%100, $v->{image}), alt => $v->{title} if $v && $v->{image} > 0;
- end;
- div;
-
- h2 mt '_vnedit_image_upload';
- input type => 'file', class => 'text', name => 'img', id => 'img';
- p mt('_vnedit_image_upload_msg');
- br; br; br;
-
- h2 mt '_vnedit_image_nsfw';
- input type => 'checkbox', class => 'checkbox', id => 'img_nsfw', name => 'img_nsfw',
- $frm->{img_nsfw} ? (checked => 'checked') : ();
- label class => 'checkbox', for => 'img_nsfw', mt '_vnedit_image_nsfw_check';
- p mt '_vnedit_image_nsfw_msg';
- end 'div';
- }],
- ],
+ vn_img => [ mt('_vnedit_image'), [ static => nolabel => 1, content => sub {
+ div class => 'img';
+ p mt '_vnedit_image_none' if !$v || !$v->{image};
+ p mt '_vnedit_image_processing' if $v && $v->{image} < 0;
+ img src => sprintf("%s/cv/%02d/%d.jpg", $self->{url_static}, $v->{image}%100, $v->{image}), alt => $v->{title} if $v && $v->{image} > 0;
+ end;
+
+ div;
+ h2 mt '_vnedit_image_id';
+ input type => 'text', class => 'text', name => 'image', id => 'image', value => $frm->{image}||'';
+ p mt '_vnedit_image_id_msg';
+ br; br;
+
+ h2 mt '_vnedit_image_upload';
+ input type => 'file', class => 'text', name => 'img', id => 'img';
+ p mt('_vnedit_image_upload_msg');
+ br; br; br;
+
+ h2 mt '_vnedit_image_nsfw';
+ input type => 'checkbox', class => 'checkbox', id => 'img_nsfw', name => 'img_nsfw',
+ $frm->{img_nsfw} ? (checked => 'checked') : ();
+ label class => 'checkbox', for => 'img_nsfw', mt '_vnedit_image_nsfw_check';
+ p mt '_vnedit_image_nsfw_msg';
+ end 'div';
+ }]],
vn_rel => [ mt('_vnedit_rel'),
[ hidden => short => 'vnrelations' ],
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index 37efb04a..e10b4414 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -141,6 +141,7 @@ sub page {
_relations($self, \$i, $v) if @{$v->{relations}};
_anime($self, \$i, $v) if @{$v->{anime}};
_useroptions($self, \$i, $v) if $self->authInfo->{id};
+ _affiliate_links($self, $r);
Tr;
td class => 'vndesc', colspan => 2;
@@ -404,11 +405,56 @@ sub _useroptions {
}
+sub _affiliate_links {
+ my($self, $r) = @_;
+ return if !keys @$r;
+ my %r = map +($_->{id}, $_), @$r;
+ my $links = $self->dbAffiliateGet(rids => [ keys %r ], hidden => 0);
+ return if !@$links;
+
+ $links = [ sort { $b->{priority}||$self->{affiliates}[$b->{affiliate}]{default_prio} <=> $a->{priority}||$self->{affiliates}[$a->{affiliate}]{default_prio} } @$links ];
+ my $en = VNDB::L10N->get_handle('en');
+
+ Tr; td colspan => 2, id => 'buynow'; # don't call it "affiliate", most adblock filters have that included >_>
+ h1; a rel => 'nofollow', href => $self->{affiliates}[$links->[0]{affiliate}]{link_format} ? $self->{affiliates}[$links->[0]{affiliate}]{link_format}->($links->[0]{url}) : $links->[0]{url}, 'Buy now!'; end;
+ ul;
+ for my $link (@$links) {
+ my $f = $self->{affiliates}[$link->{affiliate}];
+
+ my $rel = $r{$link->{rid}};
+ my $plat = grep($_ eq 'win', @{$rel->{platforms}}) ? '' : ' '.join(' and ', map $en->maketext("_plat_$_"), @{$rel->{platforms}});
+ my $version = join(', ', map $en->maketext("_lang_$_"), @{$rel->{languages}}).$plat.' version';
+
+ li; a rel => 'nofollow', href => $f->{link_format} ? $f->{link_format}->($link->{url}) : $link->{url};
+ use utf8;
+ txt '→ ';
+ txt $link->{version}
+ || ($f->{default_version} && $f->{default_version}->($self, $link, $rel))
+ || $version;
+ txt ' ';
+ acronym class => 'pricenote', title => sprintf('Last updated: %s.', $en->age($link->{lastfetch})), "for $link->{price}*"
+ if $link->{price};
+ txt " at $f->{name}.";
+ end; end;
+ }
+ end;
+ end; end;
+}
+
+
sub _releases {
my($self, $v, $r) = @_;
div class => 'mainbox releases';
- a class => 'addnew', href => "/v$v->{id}/add", mt '_vnpage_rel_add';
+ if($self->authCan('edit')) {
+ p class => 'addnew';
+ if($self->authCan('charedit')) {
+ a href => "/c/new?vid=$v->{id}", mt '_vnpage_char_add';
+ txt ' | ';
+ }
+ a href => "/v$v->{id}/add", mt '_vnpage_rel_add';
+ end;
+ }
h1 mt '_vnpage_rel';
if(!@$r) {
p mt '_vnpage_rel_none';
@@ -459,6 +505,7 @@ sub _releases {
}
end;
td class => 'tc6';
+ a href => "/affiliates/new?rid=$rel->{id}", 'a' if $self->authCan('affiliate');
if($rel->{website}) {
a href => $rel->{website}, rel => 'nofollow';
cssicon 'ext', mt '_vnpage_rel_extlink';
diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm
index 88e68edc..89807bef 100644
--- a/lib/VNDB/Util/Auth.pm
+++ b/lib/VNDB/Util/Auth.pm
@@ -88,8 +88,7 @@ sub authInfo {
# a certain action. Argument is the action name as defined in global.pl
sub authCan {
my($self, $act) = @_;
- my $r = $self->{_auth} ? $self->{_auth}{rank} : 0;
- return scalar grep $_ eq $act, @{$self->{user_ranks}[$r]}[0..$#{$self->{user_ranks}[$r]}];
+ return $self->{_auth} ? $self->{_auth}{perm} & $self->{permissions}{$act} : 0;
}
@@ -102,7 +101,7 @@ sub _authCheck {
return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass;
my $d = $self->dbUserGet(username => $user, what => 'extended notifycount')->[0];
- return 0 if !defined $d->{id} || !$d->{rank};
+ return 0 if !$d->{id};
if(_authEncryptPass($self, $pass, $d->{salt}) eq $d->{passwd}) {
$self->{_auth} = $d;
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index 41370b4b..5f2fb330 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -72,8 +72,8 @@ sub htmlMainTabs {
}
if( $type eq 'u' && ($self->authInfo->{id} && $obj->{id} == $self->authInfo->{id} || $self->authCan('usermod'))
- || $type =~ /[vrp]/ && $self->authCan('edit') && (!$obj->{locked} || $self->authCan('lock')) && (!$obj->{hidden} || $self->authCan('del'))
- || $type eq 'c' && $self->authCan('charedit') && (!$obj->{locked} || $self->authCan('lock')) && (!$obj->{hidden} || $self->authCan('del'))
+ || $type =~ /[vrp]/ && $self->authCan('edit') && ((!$obj->{locked} && !$obj->{hidden}) || $self->authCan('dbmod'))
+ || $type eq 'c' && $self->authCan('charedit') && ((!$obj->{locked} && !$obj->{hidden}) || $self->authCan('dbmod'))
|| $type =~ /[gi]/ && $self->authCan('tagmod')
) {
li $sel eq 'edit' ? (class => 'tabselected') : ();
@@ -143,7 +143,7 @@ sub htmlHiddenMessage {
end;
end;
end 'div';
- return $self->htmlFooter() || 1 if !$self->authCan('del');
+ return $self->htmlFooter() || 1 if !$self->authCan('dbmod');
return 0;
}
@@ -163,6 +163,15 @@ sub htmlRevision {
div class => 'mainbox revision';
h1 mt '_revision_title', $new->{rev};
+ # character information may be rather spoilerous
+ if($type eq 'c') {
+ div class => 'warning';
+ h2 mt '_revision_spoil_title';
+ lit mt '_revision_spoil_msg', "/c$new->{id}";
+ end;
+ br;br;
+ }
+
# previous/next revision links
a class => 'prev', href => sprintf('/%s%d.%d', $type, $new->{id}, $new->{rev}-1), '<- '.mt '_revision_previous'
if $new->{rev} > 1;
@@ -174,7 +183,7 @@ sub htmlRevision {
# no previous revision, just show info about the revision itself
if(!$old) {
- div;
+ div class => 'rev';
revheader($self, $type, $new);
br;
b mt '_revision_new_summary';
diff --git a/lib/VNDB/Util/FormHTML.pm b/lib/VNDB/Util/FormHTML.pm
index 68b6a101..805467f6 100644
--- a/lib/VNDB/Util/FormHTML.pm
+++ b/lib/VNDB/Util/FormHTML.pm
@@ -214,15 +214,11 @@ sub htmlForm {
fieldset class => 'submit';
if($options->{editsum}) {
# hidden / locked checkbox
- if($self->authCan('del')) {
+ if($self->authCan('dbmod')) {
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';
- }
- if($self->authCan('lock') || $self->authCan('del')) {
br; txt mt('_form_hidlock_note'); br;
}
diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm
index 22052a75..752f6728 100644
--- a/lib/VNDB/Util/LayoutHTML.pm
+++ b/lib/VNDB/Util/LayoutHTML.pm
@@ -89,7 +89,6 @@ sub _menu {
my $nc = $self->authInfo->{notifycount};
h2;
a href => $uid, ucfirst $self->authInfo->{username};
- txt ' ('.mt('_urank_'.$self->authInfo->{rank}).')';
end;
div;
a href => "$uid/edit", mt '_menu_myprofile'; br;
@@ -100,9 +99,13 @@ sub _menu {
a href => "$uid/hist", mt '_menu_mychanges'; br;
a href => '/g/links?u='.$self->authInfo->{id}, mt '_menu_mytags'; br;
br;
- a href => '/v/new', mt '_menu_addvn'; br;
- a href => '/p/new', mt '_menu_addproducer'; br;
- a href => '/c/new', mt '_menu_addcharacter'; br;
+ if($self->authCan('edit')) {
+ a href => '/v/new', mt '_menu_addvn'; br;
+ a href => '/p/new', mt '_menu_addproducer'; br;
+ }
+ if($self->authCan('charedit')) {
+ a href => '/c/new', mt '_menu_addcharacter'; br;
+ }
br;
a href => "$uid/logout", mt '_menu_logout';
end;
@@ -130,7 +133,7 @@ sub _menu {
h2 mt '_menu_dbstats';
div;
dl;
- for (qw|vn releases producers users threads posts|) {
+ for (qw|vn releases producers chars tags traits users threads posts|) {
dt mt "_menu_stat_$_";
dd $self->{stats}{$_};
}
diff --git a/lib/VNDBUtil.pm b/lib/VNDBUtil.pm
index a0469a1c..76290013 100644
--- a/lib/VNDBUtil.pm
+++ b/lib/VNDBUtil.pm
@@ -27,8 +27,7 @@ sub shorten {
# v+, v+.+
# http://../
sub bb2html {
- my $raw = shift;
- my $maxlength = shift;
+ my($raw, $maxlength, $charspoil) = @_;
$raw =~ s/\r//g;
return '' if !$raw && $raw ne "0";
@@ -76,7 +75,8 @@ sub bb2html {
next;
} elsif($tag eq '[spoiler]') {
push @open, 'spoiler';
- $result .= '<b class="spoiler">';
+ $result .= !$charspoil ? '<b class="spoiler">'
+ : '<b class="grayedout charspoil charspoil_-1">&lt;hidden by spoiler settings&gt;</b><span class="charspoil charspoil_2 hidden">';
next;
} elsif($tag eq '[quote]') {
push @open, 'quote';
@@ -89,7 +89,7 @@ sub bb2html {
$rmnewline = 1;
next;
} elsif($tag eq '[/spoiler]' && $open[$#open] eq 'spoiler') {
- $result .= '</b>';
+ $result .= !$charspoil ? '</b>' : '</span>';
pop @open;
next;
} elsif($tag eq '[/quote]' && $open[$#open] eq 'quote') {
diff --git a/util/sql/all.sql b/util/sql/all.sql
index d05297da..eac8d58a 100644
--- a/util/sql/all.sql
+++ b/util/sql/all.sql
@@ -12,7 +12,7 @@ CREATE TYPE gender AS ENUM ('unknown', 'm', 'f', 'b');
CREATE TYPE language AS ENUM ('cs', 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja', 'ko', 'nl', 'no', 'pl', 'pt-pt', 'pt-br', 'ru', 'sk', 'sv', 'tr', 'vi', 'zh');
CREATE TYPE medium AS ENUM ('cd', 'dvd', 'gdr', 'blr', 'flp', 'mrt', 'mem', 'umd', 'nod', 'in', 'otc');
CREATE TYPE notification_ntype AS ENUM ('pm', 'dbdel', 'listdel', 'dbedit', 'announce');
-CREATE TYPE notification_ltype AS ENUM ('v', 'r', 'p', 't');
+CREATE TYPE notification_ltype AS ENUM ('v', 'r', 'p', 'c', 't');
CREATE TYPE prefs_key AS ENUM ('l10n', 'skin', 'customcss', 'filter_vn', 'filter_release', 'show_nsfw', 'hide_list', 'notify_nodbedit', 'notify_announce');
CREATE TYPE producer_relation AS ENUM ('old', 'new', 'sub', 'par', 'imp', 'ipa', 'spa', 'ori');
CREATE TYPE release_type AS ENUM ('complete', 'partial', 'trial');
@@ -47,6 +47,12 @@ CREATE TRIGGER stats_cache_new AFTER INSERT ON producers
CREATE TRIGGER stats_cache_edit AFTER UPDATE ON producers FOR EACH ROW WHEN (OLD.hidden IS DISTINCT FROM NEW.hidden) EXECUTE PROCEDURE update_stats_cache();
CREATE TRIGGER stats_cache_new AFTER INSERT ON releases FOR EACH ROW WHEN (NEW.hidden = FALSE) EXECUTE PROCEDURE update_stats_cache();
CREATE TRIGGER stats_cache_edit AFTER UPDATE ON releases FOR EACH ROW WHEN (OLD.hidden IS DISTINCT FROM NEW.hidden) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_new AFTER INSERT ON chars FOR EACH ROW WHEN (NEW.hidden = FALSE) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_edit AFTER UPDATE ON chars FOR EACH ROW WHEN (OLD.hidden IS DISTINCT FROM NEW.hidden) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_new AFTER INSERT ON tags FOR EACH ROW WHEN (NEW.state = 2) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_edit AFTER UPDATE ON tags FOR EACH ROW WHEN (OLD.state IS DISTINCT FROM NEW.state) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_new AFTER INSERT ON traits FOR EACH ROW WHEN (NEW.state = 2) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_edit AFTER UPDATE ON traits FOR EACH ROW WHEN (OLD.state IS DISTINCT FROM NEW.state) EXECUTE PROCEDURE update_stats_cache();
CREATE TRIGGER stats_cache_new AFTER INSERT ON threads FOR EACH ROW WHEN (NEW.hidden = FALSE) EXECUTE PROCEDURE update_stats_cache();
CREATE TRIGGER stats_cache_edit AFTER UPDATE ON threads FOR EACH ROW WHEN (OLD.hidden IS DISTINCT FROM NEW.hidden) EXECUTE PROCEDURE update_stats_cache();
CREATE TRIGGER stats_cache_new AFTER INSERT ON threads_posts FOR EACH ROW WHEN (NEW.hidden = FALSE) EXECUTE PROCEDURE update_stats_cache();
@@ -88,11 +94,13 @@ CREATE TRIGGER notify_pm AFTER INSERT ON threads_pos
CREATE TRIGGER notify_dbdel AFTER UPDATE ON vn FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
CREATE TRIGGER notify_dbdel AFTER UPDATE ON producers FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
CREATE TRIGGER notify_dbdel AFTER UPDATE ON releases FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
+CREATE TRIGGER notify_dbdel AFTER UPDATE ON chars FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
CREATE TRIGGER notify_listdel AFTER UPDATE ON vn FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_listdel();
CREATE TRIGGER notify_listdel AFTER UPDATE ON releases FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_listdel();
CREATE TRIGGER notify_dbedit AFTER UPDATE ON vn FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
CREATE TRIGGER notify_dbedit AFTER UPDATE ON producers FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
CREATE TRIGGER notify_dbedit AFTER UPDATE ON releases FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
+CREATE TRIGGER notify_dbedit AFTER UPDATE ON chars FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
CREATE TRIGGER notify_announce AFTER INSERT ON threads_posts FOR EACH ROW WHEN (NEW.num = 1) EXECUTE PROCEDURE notify_announce();
CREATE TRIGGER vn_vnsearch_notify AFTER UPDATE ON vn FOR EACH ROW
@@ -124,6 +132,9 @@ INSERT INTO stats_cache (section, count) VALUES
('vn', 0),
('producers', 0),
('releases', 0),
+ ('chars', 0),
+ ('tags', 0),
+ ('traits', 0),
('threads', 0),
('threads_posts', 0);
diff --git a/util/sql/func.sql b/util/sql/func.sql
index 75e5ecad..3af04562 100644
--- a/util/sql/func.sql
+++ b/util/sql/func.sql
@@ -490,13 +490,16 @@ $$ LANGUAGE 'plpgsql';
-- the stats_cache table
CREATE OR REPLACE FUNCTION update_stats_cache() RETURNS TRIGGER AS $$
+DECLARE
+ unhidden boolean;
+ hidden boolean;
BEGIN
IF TG_OP = 'INSERT' THEN
IF TG_TABLE_NAME = 'users' THEN
UPDATE stats_cache SET count = count+1 WHERE section = TG_TABLE_NAME;
ELSE
IF TG_TABLE_NAME = 'threads_posts' THEN
- IF EXISTS(SELECT 1 FROM threads WHERE id = NEW.tid AND hidden = FALSE) THEN
+ IF EXISTS(SELECT 1 FROM threads WHERE id = NEW.tid AND threads.hidden = FALSE) THEN
UPDATE stats_cache SET count = count+1 WHERE section = TG_TABLE_NAME;
END IF;
ELSE
@@ -504,13 +507,20 @@ BEGIN
END IF;
END IF;
- ELSIF TG_OP = 'UPDATE' AND TG_TABLE_NAME <> 'users' THEN
- IF OLD.hidden = TRUE THEN
+ ELSIF TG_OP = 'UPDATE' THEN
+ IF TG_TABLE_NAME IN('tags', 'traits') THEN
+ unhidden := OLD.state <> 2 AND NEW.state = 2;
+ hidden := OLD.state = 2 AND NEW.state <> 2;
+ ELSE
+ unhidden := OLD.hidden AND NOT NEW.hidden;
+ hidden := NOT unhidden;
+ END IF;
+ IF unhidden THEN
IF TG_TABLE_NAME = 'threads' THEN
UPDATE stats_cache SET count = count+NEW.count WHERE section = 'threads_posts';
END IF;
UPDATE stats_cache SET count = count+1 WHERE section = TG_TABLE_NAME;
- ELSIF OLD.hidden = FALSE THEN
+ ELSIF hidden THEN
IF TG_TABLE_NAME = 'threads' THEN
UPDATE stats_cache SET count = count-NEW.count WHERE section = 'threads_posts';
END IF;
@@ -802,16 +812,16 @@ END;
$$ LANGUAGE plpgsql;
--- called on UPDATE vn / producers / releases when (NOT OLD.hidden AND NEW.hidden)
+-- called on UPDATE vn / producers / releases / chars when (NOT OLD.hidden AND NEW.hidden)
CREATE OR REPLACE FUNCTION notify_dbdel() RETURNS trigger AS $$
BEGIN
INSERT INTO notifications (ntype, ltype, uid, iid, subid, c_title, c_byuser)
SELECT DISTINCT 'dbdel'::notification_ntype,
- (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' WHEN 'releases' THEN 'r' ELSE 'p' END)::notification_ltype,
- c.requester, NEW.id, c2.rev, x.title, c2.requester
+ (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' WHEN 'releases' THEN 'r' WHEN 'producers' THEN 'p' ELSE 'c' END)::notification_ltype,
+ h.requester, NEW.id, h2.rev, x.title, h2.requester
-- look for changes of the deleted entry
-- this method may look a bit unintuitive, but it's way faster than doing LEFT JOINs
- FROM changes c
+ FROM changes h
JOIN ( SELECT vr.id, vr2.title FROM vn_rev vr
JOIN vn v ON v.id = vr.vid JOIN vn_rev vr2 ON vr2.id = v.latest
WHERE TG_TABLE_NAME = 'vn' AND vr.vid = NEW.id
@@ -821,12 +831,15 @@ BEGIN
UNION SELECT pr.id, pr2.name FROM producers_rev pr
JOIN producers p ON p.id = pr.pid JOIN producers_rev pr2 ON pr2.id = p.latest
WHERE TG_TABLE_NAME = 'producers' AND pr.pid = NEW.id
- ) x(id, title) ON c.id = x.id
+ UNION SELECT cr.id, cr2.name FROM chars_rev cr
+ JOIN chars c ON c.id = cr.cid JOIN chars_rev cr2 ON cr2.id = c.latest
+ WHERE TG_TABLE_NAME = 'chars' AND cr.cid = NEW.id
+ ) x(id, title) ON h.id = x.id
-- join info about the deletion itself
- JOIN changes c2 ON c2.id = NEW.latest
- WHERE c.requester <> 1 -- exclude Multi
+ JOIN changes h2 ON h2.id = NEW.latest
+ WHERE h.requester <> 1 -- exclude Multi
-- exclude the user who deleted the entry
- AND c.requester <> c2.requester;
+ AND h.requester <> h2.requester;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
@@ -862,16 +875,16 @@ END;
$$ LANGUAGE plpgsql;
--- called on UPDATE vn / producers / releases when (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden)
+-- called on UPDATE vn / producers / releases / chars when (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden)
-- this trigger is very similar to notify_dbdel()
CREATE OR REPLACE FUNCTION notify_dbedit() RETURNS trigger AS $$
BEGIN
INSERT INTO notifications (ntype, ltype, uid, iid, subid, c_title, c_byuser)
SELECT DISTINCT 'dbedit'::notification_ntype,
- (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' WHEN 'releases' THEN 'r' ELSE 'p' END)::notification_ltype,
- c.requester, NEW.id, c2.rev, x.title, c2.requester
+ (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' WHEN 'releases' THEN 'r' WHEN 'producers' THEN 'p' ELSE 'c' END)::notification_ltype,
+ h.requester, NEW.id, h2.rev, x.title, h2.requester
-- look for changes of the edited entry
- FROM changes c
+ FROM changes h
JOIN ( SELECT vr.id, vr2.title FROM vn_rev vr
JOIN vn v ON v.id = vr.vid JOIN vn_rev vr2 ON vr2.id = v.latest
WHERE TG_TABLE_NAME = 'vn' AND vr.vid = NEW.id
@@ -881,13 +894,16 @@ BEGIN
UNION SELECT pr.id, pr2.name FROM producers_rev pr
JOIN producers p ON p.id = pr.pid JOIN producers_rev pr2 ON pr2.id = p.latest
WHERE TG_TABLE_NAME = 'producers' AND pr.pid = NEW.id
- ) x(id, title) ON c.id = x.id
+ UNION SELECT cr.id, cr2.name FROM chars_rev cr
+ JOIN chars c ON c.id = cr.cid JOIN chars_rev cr2 ON cr2.id = c.latest
+ WHERE TG_TABLE_NAME = 'chars' AND cr.cid = NEW.id
+ ) x(id, title) ON h.id = x.id
-- join info about the deletion itself
- JOIN changes c2 ON c2.id = NEW.latest
+ JOIN changes h2 ON h2.id = NEW.latest
-- exclude the user who edited the entry
- WHERE c.requester <> c2.requester
+ WHERE h.requester <> h2.requester
-- exclude users who don't want this notify
- AND NOT EXISTS(SELECT 1 FROM users_prefs up WHERE uid = c.requester AND key = 'notify_nodbedit');
+ AND NOT EXISTS(SELECT 1 FROM users_prefs up WHERE uid = h.requester AND key = 'notify_nodbedit');
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
diff --git a/util/sql/schema.sql b/util/sql/schema.sql
index 39e9351a..6cbd2b14 100644
--- a/util/sql/schema.sql
+++ b/util/sql/schema.sql
@@ -1,5 +1,19 @@
+-- affiliate_links
+CREATE TABLE affiliate_links (
+ id SERIAL PRIMARY KEY,
+ rid integer NOT NULL,
+ hidden boolean NOT NULL DEFAULT false,
+ priority smallint NOT NULL DEFAULT 0,
+ affiliate smallint NOT NULL DEFAULT 0,
+ url varchar NOT NULL,
+ version varchar NOT NULL DEFAULT '',
+ lastfetch timestamptz,
+ price varchar NOT NULL DEFAULT '',
+ data varchar NOT NULL DEFAULT ''
+);
+
-- anime
CREATE TABLE anime (
id integer NOT NULL PRIMARY KEY,
@@ -343,7 +357,7 @@ CREATE TABLE users (
id SERIAL NOT NULL PRIMARY KEY,
username varchar(20) NOT NULL UNIQUE,
mail varchar(100) NOT NULL,
- rank smallint NOT NULL DEFAULT 3,
+ perm smallint NOT NULL DEFAULT 1+4+16,
passwd bytea NOT NULL DEFAULT '',
registered timestamptz NOT NULL DEFAULT NOW(),
c_votes integer NOT NULL DEFAULT 0,
@@ -452,6 +466,7 @@ CREATE TABLE wlists (
+ALTER TABLE affiliate_links ADD FOREIGN KEY (rid) REFERENCES releases (id);
ALTER TABLE changes ADD FOREIGN KEY (requester) REFERENCES users (id) ON DELETE SET DEFAULT;
ALTER TABLE chars ADD FOREIGN KEY (latest) REFERENCES chars_rev (id) DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE chars_rev ADD FOREIGN KEY (id) REFERENCES changes (id);
@@ -521,6 +536,7 @@ ALTER TABLE wlists ADD FOREIGN KEY (uid) REFERENCES users
ALTER TABLE wlists ADD FOREIGN KEY (vid) REFERENCES vn (id);
+CREATE INDEX affiliate_links_rid ON affiliate_links (rid) WHERE NOT hidden;
CREATE INDEX releases_vn_vid ON releases_vn (vid);
CREATE INDEX tags_vn_date ON tags_vn (date);
CREATE UNIQUE INDEX chars_vns_pkey ON chars_vns (cid, vid, COALESCE(rid, 0));
diff --git a/util/updates/update_2.20.sql b/util/updates/update_2.20.sql
new file mode 100644
index 00000000..390e9a41
--- /dev/null
+++ b/util/updates/update_2.20.sql
@@ -0,0 +1,54 @@
+
+ALTER TYPE notification_ltype RENAME TO tmp;
+CREATE TYPE notification_ltype AS ENUM ('v', 'r', 'p', 'c', 't');
+ALTER TABLE notifications ALTER COLUMN ltype TYPE notification_ltype USING ltype::text::notification_ltype;
+DROP TYPE tmp;
+
+\i util/sql/func.sql
+
+CREATE TRIGGER notify_dbdel AFTER UPDATE ON chars FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
+CREATE TRIGGER notify_dbedit AFTER UPDATE ON chars FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
+
+
+INSERT INTO stats_cache VALUES
+ ('chars', (SELECT COUNT(*) FROM chars WHERE NOT hidden)),
+ ('tags', (SELECT COUNT(*) FROM tags WHERE state = 2)),
+ ('traits', (SELECT COUNT(*) FROM traits WHERE state = 2));
+
+CREATE TRIGGER stats_cache_new AFTER INSERT ON chars FOR EACH ROW WHEN (NEW.hidden = FALSE) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_edit AFTER UPDATE ON chars FOR EACH ROW WHEN (OLD.hidden IS DISTINCT FROM NEW.hidden) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_new AFTER INSERT ON tags FOR EACH ROW WHEN (NEW.state = 2) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_edit AFTER UPDATE ON tags FOR EACH ROW WHEN (OLD.state IS DISTINCT FROM NEW.state) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_new AFTER INSERT ON traits FOR EACH ROW WHEN (NEW.state = 2) EXECUTE PROCEDURE update_stats_cache();
+CREATE TRIGGER stats_cache_edit AFTER UPDATE ON traits FOR EACH ROW WHEN (OLD.state IS DISTINCT FROM NEW.state) EXECUTE PROCEDURE update_stats_cache();
+
+
+
+CREATE TABLE affiliate_links (
+ id SERIAL PRIMARY KEY,
+ rid integer NOT NULL REFERENCES releases (id),
+ hidden boolean NOT NULL DEFAULT false, -- to hide a link for some reason
+ priority smallint NOT NULL DEFAULT 0, -- manual ordering when competing on a VN page, usually not necessary
+ affiliate smallint NOT NULL DEFAULT 0, -- index to a semi-static array in data/config.pl
+ url varchar NOT NULL,
+ version varchar NOT NULL DEFAULT '', -- "x edition" or "x version", default used is "<language> version"
+ lastfetch timestamptz, -- last update of price
+ price varchar NOT NULL DEFAULT '', -- formatted, including currency, e.g. "$50" or "€34.95 / $50.46"
+ data varchar NOT NULL DEFAULT '' -- to be used by a fetch bot, if any
+);
+
+CREATE INDEX affiliate_links_rid ON affiliate_links (rid) WHERE NOT hidden;
+
+
+
+-- rank -> permissions
+
+ALTER TABLE users RENAME rank TO perm;
+ALTER TABLE users ALTER COLUMN perm SET DEFAULT 1+4+16;
+UPDATE users SET perm = CASE
+ WHEN perm = 2 THEN 1
+ WHEN perm = 3 THEN 1+4+16
+ WHEN perm = 4 THEN 1+2+4+8+16+32+64
+ WHEN perm = 5 THEN 1+2+4+8+16+32+64+128+256
+ ELSE 0 END;
+
diff --git a/util/vndb.pl b/util/vndb.pl
index b6056dd4..765138fe 100755
--- a/util/vndb.pl
+++ b/util/vndb.pl
@@ -52,7 +52,7 @@ TUWF::set(
mail => { regex => qr/^[^@<>]+@[^@.<>]+(?:\.[^@.<>]+)+$/ },
url => { regex => qr/^(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?$/ },
asciiprint => { regex => qr/^[\x20-\x7E]*$/ },
- int => { regex => qr/^-?\d+$/ },
+ int => { regex => qr/^-?[0-9]+$/ },
pname => { regex => qr/^[a-z0-9-]*$/ },
},
);