diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | data/docs/7 | 3 | ||||
-rw-r--r-- | data/global.pl | 4 | ||||
-rw-r--r-- | data/lang.txt | 377 | ||||
-rw-r--r-- | data/script.js | 173 | ||||
-rw-r--r-- | data/style.css | 4 | ||||
-rw-r--r-- | lib/Multi/Feed.pm | 6 | ||||
-rw-r--r-- | lib/Multi/Maintenance.pm | 4 | ||||
-rw-r--r-- | lib/VNDB/DB/Chars.pm | 22 | ||||
-rw-r--r-- | lib/VNDB/DB/VN.pm | 6 | ||||
-rw-r--r-- | lib/VNDB/Handler/Chars.pm | 20 | ||||
-rw-r--r-- | lib/VNDB/Handler/Producers.pm | 2 | ||||
-rw-r--r-- | lib/VNDB/Handler/Releases.pm | 4 | ||||
-rw-r--r-- | lib/VNDB/Handler/Tags.pm | 6 | ||||
-rw-r--r-- | lib/VNDB/Handler/Traits.pm | 15 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNEdit.pm | 78 | ||||
-rw-r--r-- | lib/VNDB/Util/CommonHTML.pm | 2 | ||||
-rw-r--r-- | lib/VNDB/Util/FormHTML.pm | 17 | ||||
-rw-r--r-- | lib/VNDB/Util/LayoutHTML.pm | 2 | ||||
-rw-r--r-- | lib/VNDB/Util/Misc.pm | 5 | ||||
-rwxr-xr-x | util/jsgen.pl | 3 | ||||
-rw-r--r-- | util/sql/all.sql | 3 | ||||
-rw-r--r-- | util/sql/func.sql | 14 | ||||
-rw-r--r-- | util/updates/update_2.22.sql | 11 |
25 files changed, 580 insertions, 215 deletions
@@ -1,4 +1,9 @@ -2.22 - ? +2.22 - 2011-12-31 + - Added character filters + - Added duplicate checking form before creating a new VN entry + - Combined "remove" and "add" labels in a single lang.txt entry + - Added secondary order to VN browser when sorting on release date + - doc updates for the characters and traits 2.21 - 2011-08-23 - New resolution: 1280x960 @@ -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.20 update-2.21 + update-2.18 update-2.19 update-2.20 update-2.21 update-2.22 all: dirs js skins robots data/config.pl @@ -190,3 +190,8 @@ update-2.21: all ${runpsql} < util/updates/update_2.21.sql $(multi-start) +update-2.22: all + $(multi-stop) + ${runpsql} < util/updates/update_2.22.sql + $(multi-start) + diff --git a/data/docs/7 b/data/docs/7 index 7a8e2d83..058075fd 100644 --- a/data/docs/7 +++ b/data/docs/7 @@ -50,7 +50,8 @@ <b>Code development</b><br /> <dl> <dt>Yorhel</dt><dd>Main developer.</dd> - <dt>3dB</dt><dd>Wrote the current user authentication code and implemented post throttling on the discussion board.</dd> + <dt>QCyph</dt><dd>Contributed the character filters.</dd> + <dt>3dB</dt><dd>Contributed the current user authentication code and post throttling on the discussion board.</dd> </dl> <br /> diff --git a/data/global.pl b/data/global.pl index 31d45631..fd9c3e31 100644 --- a/data/global.pl +++ b/data/global.pl @@ -31,8 +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 - # bit flags - permissions => {qw| board 1 boardmod 2 edit 4 charedit 8 tag 16 dbmod 32 tagmod 64 usermod 128 affiliate 256 |}, + # bit flags (TODO: completely merge charedit into edit) + permissions => {qw| board 1 boardmod 2 edit 4 charedit 4 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 diff --git a/data/lang.txt b/data/lang.txt index 03c7b132..a453fc48 100644 --- a/data/lang.txt +++ b/data/lang.txt @@ -1641,6 +1641,22 @@ hu : Beküldés nl : Verstuur de : Bestätigen +:_form_continue +en : Continue +ru*: +cs*: +hu*: +nl : Verder +de*: + +:_form_continue_ign +en : Continue and ignore duplicates +ru*: +cs*: +hu*: +nl : Ga verder en negeer dubbele items +de*: + :_form_ihid en : Deleted ru : Удалено @@ -1724,6 +1740,22 @@ hu : -nap- nl : -dag- de : -Tag- +:_js_remove +en : remove +ru : убрать +cs : odstranit +hu : eltávolítás +nl : verwijder +de : entfernen + +:_js_add +en : add +ru : добавить +cs : přidat +hu : bejegyez +nl : toevoegen +de : hinzufügen + :_js_ds_noresults en : No results... ru : Совпадений не найдено... @@ -1772,6 +1804,30 @@ hu : Itt nem használhatsz meta címkéket! nl : Meta tags kunnen hier niet gebruikt worden! de : Meta-Tags können hier nicht genutzt werden! +:_js_ds_trait_meta +en : meta +ru*: +cs*: +hu*: +nl : +de*: + +:_js_ds_trait_mod +en : awaiting moderation +ru*: +cs*: +hu*: +nl : ongemodereerd +de*: + +:_js_ds_trait_nometa +en : Can't use meta traits here! +ru*: +cs*: +hu*: +nl : Meta kenmerken kunnen hier niet gebruikt worden! +de*: + # Filter selector @@ -2354,15 +2410,6 @@ hu : szerkesztés nl : wijzig de : bearbeiten -# delete -:_mtabs_del -en : del -ru : удалить -cs : smazat -hu : törlés -nl : verwijder -de : löschen - # VN relations :_mtabs_relations en : relations @@ -3494,22 +3541,6 @@ hu : Készítő bejegyzése nl : Nieuwe producent toevoegen de : Hersteller hinzufügen -:_pedit_rel_addbut -en : add -ru : добавить -cs : přidat -hu : hozzáad -nl : toevoegen -de : hinzufügen - -:_pedit_rel_del -en : remove -ru : убрать -cs : odebrat -hu : eltávolít -nl : verwijderen -de : entfernen - :_pedit_rel_none en : Nothing selected. ru : Ничего не выбрано. @@ -4256,14 +4287,6 @@ hu : -medium- nl : de : -Medium- -:_redit_form_med_remove -en : remove -ru : убрать -cs : odebrat -hu : eltávolítás -nl : verwijder -de : entfernen - :_redit_form_prod en : Producers ru : Компании @@ -4312,22 +4335,6 @@ hu : Mindkettő nl : Beide de : Beides -:_redit_form_prod_addbut -en : add -ru : добавить -cs : přidat -hu : bejegyez -nl : voeg toe -de : hinzufügen - -:_redit_form_prod_remove -en : remove -ru : убрать -cs : odebrat -hu : eltávolít -nl : verwijder -de : entfernen - :_redit_form_prod_none en : Nothing selected. ru : Ничего не выбрано. @@ -4384,22 +4391,6 @@ hu : Visual novel bejegyzése nl : Nieuwe visual novel de : Visual Novel hinzufügen -:_redit_form_vn_addbut -en : add -ru : добавить -cs : přidat -hu : bejegyez -nl : voeg toe -de : hinzufügen - -:_redit_form_vn_remove -en : remove -ru : убрать -cs : odebrat -hu : eltávolít -nl : verwijder -de : entfernen - :_redit_form_vn_none en : Nothing selected. ru : Ничего не выбрано. @@ -5268,14 +5259,6 @@ hu : Aktív szűrők: nl : Actieve filters: de : Aktive Filter: -:_taglink_fil_remove -en : remove -ru : убрать -cs : odstranit -hu : eltávolít -nl : verwijder -de : entfernen - :_taglink_fil_user en : User: ru : Пользователь: @@ -6171,14 +6154,6 @@ hu : Sajátosság hozzáadása nl : Voeg kenmerk toe de : Füge Eigenschaft hinzu -:_chare_traits_del -en : del -ru*: -cs*: -hu : töröl -nl : -de : entfernen - :_chare_traits_present en : Selected trait is already present. ru*: @@ -6251,14 +6226,6 @@ hu : Mind / mások nl : Alle / overige de : Alle / andere -:_chare_vns_del -en : remove -ru*: -cs : odstranit -hu : eltávolít -nl : verwijder -de : entfernen - :_chare_vns_allrel en : All releases already selected. ru*: @@ -6310,8 +6277,181 @@ hu : A kritériumaid alapján nem található szereplő. nl : Geen karakters gevonden die aan je kriteria voldoen. de : Keine Charaktere, die deinen Kriterien entsprechen, gefunden. +:_charb_fil_title +en : Character filters +ru*: +cs*: +hu*: +nl : Karakterfilters +de*: + +:_charb_general +en : General +ru*: +cs*: +hu*: +nl : Algemeen +de*: + +:_charb_gender +en : Gender +ru*: +cs*: +hu*: +nl : Geslacht +de*: + +:_charb_bust_min +en : Bust min +ru*: +cs*: +hu*: +nl : Borst min +de*: + +:_charb_bust_max +en : Bust max +ru*: +cs*: +hu*: +nl : Borst max +de*: + +:_charb_waist_min +en : Waist min +ru*: +cs*: +hu*: +nl : Taille min +de*: + +:_charb_waist_max +en : Waist max +ru*: +cs*: +hu*: +nl : Taille max +de*: + +:_charb_hip_min +en : Hips min +ru*: +cs*: +hu*: +nl : Heupen min +de*: + +:_charb_hip_max +en : Hips max +ru*: +cs*: +hu*: +nl : Heupen max +de*: + +:_charb_height_min +en : Height min +ru*: +cs*: +hu*: +nl : Lengte min +de*: + +:_charb_height_max +en : Height max +ru*: +cs*: +hu*: +nl : Lengte max +de*: + +:_charb_weight_min +en : Weight min +ru*: +cs*: +hu*: +nl : Gewicht min +de*: + +:_charb_weight_max +en : Weight max +ru*: +cs*: +hu*: +nl : Gewicht max +de*: + +:_charb_bloodt +en : Blood type +ru*: +cs*: +hu*: +nl : Bloedgroep +de*: + +:_charb_traits +en : Traits +ru*: +cs*: +hu*: +nl : Kenmerken +de*: + +:_charb_traitinc +en : Traits to include +ru*: +cs*: +hu*: +nl : Kenmerken meenemen +de*: + +:_charb_traitexc +en : Traits to exclude +ru*: +cs*: +hu*: +nl : Kenmerken uitsluiten +de*: +:_charb_traitnothere +en : Additional trait filters are not available on this page. Use the character browser instead (available from the main menu -> characters). +ru*: +cs*: +hu*: +nl : Extra kenmerkfilters zijn niet aanwezig op deze pagina. Gebruik de karakterbrowser voor deze functionaliteit (beschikbaar via het hoofdmenu -> karakters). +de*: +:_charb_spoil0 +en : Hide spoilers +ru*: +cs*: +hu*: +nl : Verberg spoilers +de*: + +:_charb_spoil1 +en : Show minor spoilers +ru*: +cs*: +hu*: +nl : Toon geringe spoilers +de*: + +:_charb_spoil2 +en : Show major spoilers +ru*: +cs*: +hu*: +nl : Toon alle spoilers +de*: + +:_charb_roles +en : Roles +ru*: +cs*: +hu*: +nl : Rol +de*: @@ -8112,14 +8252,6 @@ hu : Ezzek a szűrők figyelmen kívűl maradnak a címke oldalakon (ha alapért nl : Deze filters worden genegeerd op tagpaginas (als ze gebruikt worden als standaardfilters). de : Diese Filter werden auf Tag-Seiten ignoriert (wenn als Standard eingestellt). -:_vnbrowse_tagrem -en : remove -ru : убрать -cs : odstranit -hu : eltávolítás -nl : verwijder -de : entfernen - :_vnbrowse_taginc en : Tags to include ru : Включить теги @@ -8299,6 +8431,33 @@ hu : Új visual novel bejegyzése nl : Voeg nieuwe visual novel toe de : Neue Visual Novel hinzufügen +:_vnedit_dup_title +en : Possible duplicates found +ru*: +cs*: +hu*: +nl : Mogelijke dubbele items gevonden +de*: + +:_vnedit_dup_msg +en : The following is a list of visual novels that match the title(s) you gave. + Please check this list to avoid creating a duplicate visual novel entry. + Be especially wary of items that have been deleted! To see why an entry + has been deleted, click on its title.[br] + [br] + To add the visual novel either way, hit the "Continue and ignore + duplicates" button below. +ru*: +cs*: +hu*: +nl : De onderstaande lijst bevat visual novels met een vergelijkbare titel. + Controleer deze lijst a.u.b. om te voorkomen dat je een visual novel dubbel toevoegt. + Wees helemaal op de hoede van items die verwijderd zijn! Om te zien waarom + iets verwijderd is, klik op de titel.[br] + [br] + Klik op de "Ga verder en negeer dubbele items" knop om alsnog verder te gaan. +de*: + :_vnedit_geninfo en : General info ru : Основная информация @@ -8577,22 +8736,6 @@ hu : - nl : van de : von -:_vnedit_rel_addbut -en : add -ru : добавить -cs : přidat -hu : bejegyez -nl : toevoegen -de : hinzufügen - -:_vnedit_rel_del -en : del -ru : убрать -cs : smazat -hu : töröl -nl : verwijder -de : entfernen - :_vnedit_rel_none en : No relations selected. ru : Отношений не выбрано. @@ -8789,14 +8932,6 @@ hu : #[_1]-dik pillanatkép nl : de : Screenshot #[_1] -:_vnedit_scr_remove -en : remove -ru : убрать -cs : odebrat -hu : eltávolítás -nl : verwijder -de : entfernen - :_vnedit_scr_fullsize en : Full size: [_1] ru : Полный размер: [_1] diff --git a/data/script.js b/data/script.js index e85bce3d..4065def4 100644 --- a/data/script.js +++ b/data/script.js @@ -819,7 +819,7 @@ function vnrAdd(rel, vid, official, title) { tag('label', {'for':'official_'+vid}, mt('_vnedit_rel_official')), sel, ' '+mt('_vnedit_rel_of')), tag('td', {'class':'tc_title'}, shorten(byId('title').value, 40)), - tag('td', {'class':'tc_add' }, tag('a', {href:'#', onclick:vnrDel}, mt('_vnedit_rel_del'))) + tag('td', {'class':'tc_add' }, tag('a', {href:'#', onclick:vnrDel}, mt('_js_remove'))) )); vnrEmpty(); @@ -880,7 +880,7 @@ function vnrFormAdd() { ajax('/xml/vn.xml?q='+encodeURIComponent(input), function(hr) { txt.disabled = sel.disabled = off.disabled = false; txt.value = ''; - setText(lnk, mt('_vnedit_rel_addbut')); + setText(lnk, mt('_js_add')); var items = hr.responseXML.getElementsByTagName('item'); if(items.length < 1) @@ -933,7 +933,7 @@ function medAdd(med, qty) { msel.appendChild(tag('option', {value:medTypes[i][0], selected: med==medTypes[i][0]}, medTypes[i][1])); byId('media_div').appendChild(tag('span', qsel, msel, - med != '' ? tag('input', {type: 'button', 'class':'submit', onclick:medDel, value:mt('_redit_form_med_remove')}) : null + med != '' ? tag('input', {type: 'button', 'class':'submit', onclick:medDel, value:mt('_js_remove')}) : null )); } @@ -1148,7 +1148,7 @@ function scrCheckStatus() { var nsfwid = 'scr_sfw_'+tr.scr_id; setContent(byName(tr, 'td')[1], tag('b', mt('_vnedit_scr_id', tr.scr_id)), - ' (', tag('a', {href: '#', onclick:scrDel}, mt('_vnedit_scr_remove')), ')', + ' (', tag('a', {href: '#', onclick:scrDel}, mt('_js_remove')), ')', tag('br', null), mt('_vnedit_scr_fullsize', dim), odd ? tag('b', {'class':'standout', 'style':'font-weight: bold'}, ' '+mt('_vnedit_scr_nonstandard')) : null, @@ -1454,7 +1454,7 @@ function rvnLoad() { function rvnAdd(id, title) { byId('vn_tbl').appendChild(tag('tr', {id:'rvn_'+id, rvn_id:id}, tag('td', {'class':'tc_title'}, 'v'+id+':', tag('a', {href:'/v'+id}, shorten(title, 40))), - tag('td', {'class':'tc_rm'}, tag('a', {href:'#', onclick:rvnDel}, mt('_redit_form_vn_remove'))) + tag('td', {'class':'tc_rm'}, tag('a', {href:'#', onclick:rvnDel}, mt('_js_remove'))) )); rvnStripe(); rvnEmpty(); @@ -1502,7 +1502,7 @@ function rvnFormAdd() { ajax('/xml/vn.xml?q='+encodeURIComponent(val), function(hr) { txt.disabled = false; txt.value = ''; - setText(lnk, mt('_redit_form_vn_addbut')); + setText(lnk, mt('_js_add')); var items = hr.responseXML.getElementsByTagName('item'); if(items.length < 1) @@ -1564,7 +1564,7 @@ function rprAdd(id, role, name) { byId('producer_tbl').appendChild(tag('tr', {id:'rpr_'+id, rpr_id:id}, tag('td', {'class':'tc_name'}, 'p'+id+':', tag('a', {href:'/p'+id}, shorten(name, 40))), tag('td', {'class':'tc_role'}, rl), - tag('td', {'class':'tc_rm'}, tag('a', {href:'#', onclick:rprDel}, mt('_redit_form_prod_remove'))) + tag('td', {'class':'tc_rm'}, tag('a', {href:'#', onclick:rprDel}, mt('_js_remove'))) )); rprEmpty(); } @@ -1604,7 +1604,7 @@ function rprFormAdd() { ajax('/xml/producers.xml?q='+encodeURIComponent(val), function(hr) { txt.disabled = false; txt.value = ''; - setText(lnk, mt('_redit_form_prod_addbut')); + setText(lnk, mt('_js_add')); var items = hr.responseXML.getElementsByTagName('item'); if(items.length < 1) @@ -1676,7 +1676,7 @@ function prrAdd(rel, pid, title) { byId('relation_tbl').appendChild(tag('tr', {id:'relation_tr_'+pid}, tag('td', {'class':'tc_prod' }, 'p'+pid+':', tag('a', {href:'/p'+pid}, shorten(title, 40))), tag('td', {'class':'tc_rel' }, sel), - tag('td', {'class':'tc_add' }, tag('a', {href:'#', onclick:prrDel}, mt('_pedit_rel_del'))) + tag('td', {'class':'tc_add' }, tag('a', {href:'#', onclick:prrDel}, mt('_js_remove'))) )); prrEmpty(); @@ -1735,7 +1735,7 @@ function prrFormAdd() { ajax('/xml/producers.xml?q='+encodeURIComponent(input), function(hr) { txt.disabled = sel.disabled = false; txt.value = ''; - setText(lnk, mt('_pedit_rel_addbut')); + setText(lnk, mt('_js_add')); var items = hr.responseXML.getElementsByTagName('item'); if(items.length < 1) @@ -1818,7 +1818,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}, mt('_chare_traits_del'))) + tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:ctrDel}, mt('_js_remove'))) )); ctrEmpty(); ctrSerialize(); @@ -1988,7 +1988,7 @@ function cvnRelAdd(vid, rid, role, spoil) { tag('td', {'class':'tc_rel'}, rsel), tag('td', {'class':'tc_rol'}, lsel), tag('td', {'class':'tc_spl'}, ssel), - tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:cvnRelDel}, mt('_chare_vns_del'))) + tag('td', {'class':'tc_del'}, tag('a', {href:'#', onclick:cvnRelDel}, mt('_js_remove'))) ), last); } @@ -2124,7 +2124,9 @@ if(byId('jt_box_chare_vns')) var fil_cats; // [ <object with field->tr mapping>, <category-link1>, .. ] var fil_escape = "_ !\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~".split(''); function filLoad() { - var l = byId('filselect').href.match(/#r$/) ? filReleases() : filVN(); + var l = byId('filselect').href.match(/#r$/) ? filReleases() + : byId('filselect').href.match(/#c$/) ? filChars() + : filVN(); fil_cats = [ new Object ]; var p = tag('p', {'class':'browseopts'}); @@ -2179,7 +2181,8 @@ function filLoad() { f.submit(); }}), tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_reset'), onclick:function () { byId('fil').value = ''; filDeSerialize()} }), - PREF_CODE != '' ? tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_save'), onclick:filSaveDefault }) : null, + typeof PREFS != 'undefined' && ('filter_vn' in PREFS | 'filter_release' in PREFS) && PREF_CODE != '' ? + tag('input', {type:'button', 'class':'submit', value: mt('_js_fil_save'), onclick:filSaveDefault }) : null, tag('p', {id:'fil_savenote', 'class':'hidden'}, '') )); filSelectCat(1); @@ -2265,7 +2268,7 @@ function filSerialize() { if(r.length > 0 && r[0] != '') values[fil_cats[0][f].fil_code] = r.join('~'); } - if(!values['tag_inc']) + if(!values['tag_inc'] && !values['trait_inc']) delete values['tagspoil']; var l = []; for(var f in values) @@ -2323,6 +2326,63 @@ function filShow() { return false; } +var curSlider = null; +function filFSlider(c, n, min, max, def, unit) { + var bw = 200; var pw = 1; // slidebar width and pointer width + var s = tag('p', {fil_val:def, 'class':'slider'}); + var b = tag('div', {style:'width:'+(bw-2)+'px;', s:s}); + var p = tag('div', {style:'width:'+pw+'px;', s:s}); + var v = tag('span', def+' '+unit); + s.appendChild(b); + b.appendChild(p); + s.appendChild(v); + + var set = function (e, v) { + var w = bw-pw-6; + var s,x; + + if(v) { + s = e; + x = v[0] == '' ? def : parseInt(v[0]); + x = (x-min)*w/(max-min); + } else { + s = curSlider; + if(!e) e = window.event; + x = (!e) ? (def-min)*w/(max-min) + : (e.pageX || e.clientX + document.body.scrollLeft - document.body.clientLeft)-5; + var o = s.childNodes[0]; + while(o.offsetParent) { + x -= o.offsetLeft; + o = o.offsetParent; + } + } + + if(x<0) x = 0; if(x>w) x = w; + s.fil_val = min + Math.floor(x*(max-min)/w); + s.childNodes[1].innerHTML = s.fil_val+' '+unit; + s.childNodes[0].childNodes[0].style.left = x+'px'; + return false; + } + + b.onmousedown = p.onmousedown = function (e) { + curSlider = this.s; + if(!curSlider.oldmousemove) curSlider.oldmousemove = document.onmousemove; + if(!curSlider.oldmouseup) curSlider.oldmouseup = document.onmouseup; + document.onmouseup = function () { + document.onmousemove = curSlider.oldmousemove; + curSlider.oldmousemove = null; + document.onmouseup = curSlider.oldmouseup; + curSlider.oldmouseup = null; + filSelectField(curSlider); + return false; + } + document.onmousemove = set; + return set(e); + } + + return [c, n, s, function (c) { return [ c.fil_val ]; }, set ]; +} + function filFSelect(c, n, lines, opts) { var s = tag('select', {onfocus: filSelectField, onchange: filSerialize, multiple: lines > 1, size: lines}); for(var i=0; i<opts.length; i++) { @@ -2379,14 +2439,18 @@ function filFOptions(c, n, opts, setfunc) { ]; } -function filFTagInput(name, label) { +function filFTagInput(name, label, type) { + var src = type=='tag' ? '/xml/tags.xml' : '/xml/traits.xml'; + var visible = false; var remove = function() { ; }; - var addtag = function(ul, id, name) { - ul.appendChild(tag('li', { fil_id: id }, - tag('a', {href:'/g'+id}, name||'g'+id), + var addtag = function(ul, id, name, group) { + ul.appendChild( + tag('li', { fil_id: id }, + type=='trait' && group ? tag('b', {'class':'grayedout'}, group+' / ') : null, + type=='tag' ? tag('a', {href:'/g'+id}, name||'g'+id) : tag('a', {href:'/i'+id}, name||'i'+id), ' (', tag('a', {href:'#', onclick:function () { // a -> li -> ul -> div @@ -2395,7 +2459,7 @@ function filFTagInput(name, label) { filSelectField(ul.parentNode); return false } - }, mt('_vnbrowse_tagrem')), ')' + }, mt('_js_remove')), ')' )); } var fetch = function(c) { @@ -2421,12 +2485,12 @@ function filFTagInput(name, label) { txt.value = mt('_js_loading'); txt.disabled = true; if(visible) - ajax('/xml/tags.xml?'+q.join(';'), function (hr) { + ajax(src+'?'+q.join(';'), function (hr) { var l = []; var items = hr.responseXML.getElementsByTagName('item'); setText(ul, ''); for(var i=0; i<items.length; i++) - addtag(ul, items[i].getAttribute('id'), items[i].firstChild.nodeValue); + addtag(ul, items[i].getAttribute('id'), items[i].firstChild.nodeValue, items[i].getAttribute('groupname')); txt.value = ''; txt.disabled = false; c.fil_val = null; @@ -2434,18 +2498,21 @@ function filFTagInput(name, label) { }; var input = tag('input', {type:'text', 'class':'text', style:'width:300px', onfocus:filSelectField}); var list = tag('ul', null); - dsInit(input, '/xml/tags.xml?q=', + dsInit(input, src+'?q=', function(item, tr) { - tr.appendChild(tag('td', shorten(item.firstChild.nodeValue, 40), - item.getAttribute('meta') == 'yes' ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_tag_meta')) : null, - item.getAttribute('state') == 0 ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_tag_mod')) : null + var g = item.getAttribute('groupname'); + tr.appendChild(tag('td', + type=='trait' && g ? tag('b', {'class':'grayedout'}, g+' / ') : null, + shorten(item.firstChild.nodeValue, 40), // l10n /_js_ds_(tag|trait)_(meta|mod)/ + item.getAttribute('meta') == 'yes' ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_'+type+'_meta')) : null, + item.getAttribute('state') == 0 ? tag('b', {'class': 'grayedout'}, ' '+mt('_js_ds_'+type+'_mod')) : null )); }, function(item, obj) { - if(item.getAttribute('meta') == 'yes') - alert(mt('_js_ds_tag_nometa')); + if(item.getAttribute('meta') == 'yes') // l10n /_js_ds_(tag|trait)_nometa/ + alert(mt('_js_ds_'+type+'_nometa')); else { - addtag(byName(obj.parentNode, 'ul')[0], item.getAttribute('id'), item.firstChild.nodeValue); + addtag(byName(obj.parentNode, 'ul')[0], item.getAttribute('id'), item.firstChild.nodeValue, item.getAttribute('groupname')); filSelectField(obj); } return ''; @@ -2466,6 +2533,50 @@ function filFTagInput(name, label) { ]; } +function filChars() { + var gend = genders; + for(var i=0; i<gend.length; i++) // l10n /_gender_.+/ + gend[i] = [ gend[i], mt('_gender_'+gend[i]) ]; + var bloodt = blood_types; + for(var i=0; i<bloodt.length; i++) // l10n /_bloodt_.+/ + bloodt[i] = [ bloodt[i], mt('_bloodt_'+bloodt[i]) ]; + var roles = char_roles; + for(var i=0; i<roles.length; i++) // l10n /_charrole_.+/ + roles[i] = [ roles[i], mt('_charrole_'+roles[i]) ]; + + var ontraitpage = location.pathname.indexOf('/c/') < 0; + + return [ + mt('_charb_fil_title'), + [ mt('_charb_general'), + filFSelect('gender', mt('_charb_gender'), 4, gend), + filFSelect('bloodt', mt('_charb_bloodt'), 5, bloodt), + '', + filFSlider('bust_min', mt('_charb_bust_min'), 20, 120, 40, 'cm'), + filFSlider('bust_max', mt('_charb_bust_max'), 20, 120, 100, 'cm'), + filFSlider('waist_min', mt('_charb_waist_min'), 20, 120, 40, 'cm'), + filFSlider('waist_max', mt('_charb_waist_max'), 20, 120, 100, 'cm'), + filFSlider('hip_min', mt('_charb_hip_min'), 20, 120, 40, 'cm'), + filFSlider('hip_max', mt('_charb_hip_max'), 20, 120, 100, 'cm'), + '', + filFSlider('height_min', mt('_charb_height_min'), 0, 300, 60, 'cm'), + filFSlider('height_max', mt('_charb_height_max'), 0, 300, 240, 'cm'), + filFSlider('weight_min', mt('_charb_weight_min'), 0, 400, 80, 'kg'), + filFSlider('weight_max', mt('_charb_weight_max'), 0, 400, 320, 'kg'), + ], + ontraitpage ? [ mt('_charb_traits'), + [ '', ' ', tag(mt('_charb_traitnothere')) ], + ] : [ mt('_charb_traits'), + [ '', ' ', tag(mt('_js_fil_booland')) ], + filFTagInput('trait_inc', mt('_charb_traitinc'), 'trait'), + filFTagInput('trait_exc', mt('_charb_traitexc'), 'trait'), + filFOptions('tagspoil', ' ', [[0, mt('_charb_spoil0')],[1, mt('_charb_spoil1')],[2, mt('_charb_spoil2')]], + function (o) { var s = getCookie('tagspoil'); if(o+'' == '') return s == null ? 0 : s; setCookie('tagspoil', o); return o}) + ], + [ mt('_charb_roles'), filFSelect('role', mt('_charb_roles'), 4, roles) ] + ]; +} + function filReleases() { var types = release_types; for(var i=0; i<types.length; i++) // l10n /_rtype_.+/ @@ -2537,8 +2648,8 @@ function filVN() { ] : [ mt('_vnbrowse_tags'), [ '', ' ', tag(mt('_js_fil_booland')) ], [ '', ' ', PREF_CODE != '' ? tag(mt('_vnbrowse_tagactive')) : null ], - filFTagInput('tag_inc', mt('_vnbrowse_taginc')), - filFTagInput('tag_exc', mt('_vnbrowse_tagexc')), + filFTagInput('tag_inc', mt('_vnbrowse_taginc'), 'tag'), + filFTagInput('tag_exc', mt('_vnbrowse_tagexc'), 'tag'), filFOptions('tagspoil', ' ', [[0, mt('_vnbrowse_spoil0')],[1, mt('_vnbrowse_spoil1')],[2, mt('_vnbrowse_spoil2')]], function (o) { var s = getCookie('tagspoil'); if(o+'' == '') return s == null ? 0 : s; setCookie('tagspoil', o); return o}) ], diff --git a/data/style.css b/data/style.css index 78f3736f..ecb8bb42 100644 --- a/data/style.css +++ b/data/style.css @@ -768,6 +768,10 @@ div#fil_div { #fil_div .opts a.tsel { color: $maintext$; } #filselect i { font-style: normal } #fil_div table ul { margin: 0 0 0 15px } +#fil_div .slider p { margin: 1px; } +#fil_div .slider div { margin: 1px; border: 1px solid $secborder$; float: left; height: 12px; } +#fil_div .slider div div { border-top: none; border-bottom: none; cursor: default; position: relative; height: 10px; margin: 1px; } +#fil_div .slider span { margin-left: 5px } diff --git a/lib/Multi/Feed.pm b/lib/Multi/Feed.pm index 1a955e05..ea9cbcac 100644 --- a/lib/Multi/Feed.pm +++ b/lib/Multi/Feed.pm @@ -112,7 +112,7 @@ sub write_atom { # num, res, feed, time $x->tag(updated => datetime($updated)); $x->tag(id => $VNDB::S{url}.$VNDB::S{atom_feeds}{$feed}[2]); $x->tag(link => rel => 'self', type => 'application/atom+xml', href => "$VNDB::S{url}/feeds/$feed.atom", undef); - $x->tag(link => rel => 'alternate', type => 'text/html', href => $VNDB::S{atom_feeds}{$feed}[2], undef); + $x->tag(link => rel => 'alternate', type => 'text/html', href => $VNDB::S{url}.$VNDB::S{atom_feeds}{$feed}[2], undef); for(@$r) { $x->tag('entry'); @@ -123,10 +123,10 @@ sub write_atom { # num, res, feed, time if($_->{username}) { $x->tag('author'); $x->tag(name => $_->{username}); - $x->tag(uri => '/u'.$_->{uid}) if $_->{uid}; + $x->tag(uri => $VNDB::S{url}.'/u'.$_->{uid}) if $_->{uid}; $x->end; } - $x->tag(link => rel => 'alternate', type => 'text/html', href => $_->{id}, undef); + $x->tag(link => rel => 'alternate', type => 'text/html', href => $VNDB::S{url}.$_->{id}, undef); $x->tag('summary', type => 'html', bb2html $_->{summary}) if $_->{summary}; $x->end('entry'); } diff --git a/lib/Multi/Maintenance.pm b/lib/Multi/Maintenance.pm index a754e618..e7c66a04 100644 --- a/lib/Multi/Maintenance.pm +++ b/lib/Multi/Maintenance.pm @@ -272,7 +272,7 @@ sub logrotate { sub vnsearch_check { $_[KERNEL]->call(pg => query => - 'SELECT id FROM vn WHERE NOT hidden AND c_search IS NULL LIMIT 1', + 'SELECT id FROM vn WHERE c_search IS NULL LIMIT 1', undef, 'vnsearch_gettitles'); } @@ -303,7 +303,7 @@ sub vnsearch_update { # num, res, vid, time my @t = map +($_->{title}, $_->{original}), @$res; # alias fields are a bit special for (@$res) { - push @t, split /,/, $_->{alias} if $_->{alias}; + push @t, split /[\n,]/, $_->{alias} if $_->{alias}; } my $t = normalize_titles(@t); $_[KERNEL]->call(core => log => 'Updated search cache for v%d', $id); diff --git a/lib/VNDB/DB/Chars.pm b/lib/VNDB/DB/Chars.pm index be620378..86b3a859 100644 --- a/lib/VNDB/DB/Chars.pm +++ b/lib/VNDB/DB/Chars.pm @@ -8,7 +8,8 @@ use Exporter 'import'; our @EXPORT = qw|dbCharGet dbCharRevisionInsert dbCharImageId|; -# options: id rev instance traitspoil trait_inc trait_exc char what results page +# options: id rev instance tagspoil trait_inc trait_exc char what results page gender bloodt +# bust_min bust_max waist_min waist_max hip_min hip_max height_min height_max weight_min weight_max role # what: extended traits vns changes sub dbCharGet { my $self = shift; @@ -16,7 +17,7 @@ sub dbCharGet { page => 1, results => 10, what => '', - traitspoil => 0, + tagspoil => 0, @_ ); @@ -29,15 +30,30 @@ sub dbCharGet { $o{notid} ? ( 'c.id <> ?' => $o{notid} ) : (), $o{instance} ? ( 'cr.main = ?' => $o{instance} ) : (), $o{vid} ? ( 'cr.id IN(SELECT cid FROM chars_vns WHERE vid = ?)' => $o{vid} ) : (), + defined $o{gender} ? ( 'cr.gender IN(!l)' => [ ref $o{gender} ? $o{gender} : [$o{gender}] ]) : (), + defined $o{bloodt} ? ( 'cr.bloodt IN(!l)' => [ ref $o{bloodt} ? $o{bloodt} : [$o{bloodt}] ]) : (), + defined $o{bust_min} ? ( 'cr.s_bust >= ?' => $o{bust_min} ) : (), + defined $o{bust_max} ? ( 'cr.s_bust <= ? AND cr.s_bust > 0' => $o{bust_max} ) : (), + defined $o{waist_min} ? ( 'cr.s_waist >= ?' => $o{waist_min} ) : (), + defined $o{waist_max} ? ( 'cr.s_waist <= ? AND cr.s_waist > 0' => $o{waist_max} ) : (), + defined $o{hip_min} ? ( 'cr.s_hip >= ?' => $o{hip_min} ) : (), + defined $o{hip_max} ? ( 'cr.s_hip <= ? AND cr.s_hip > 0' => $o{hip_max} ) : (), + defined $o{height_min} ? ( 'cr.height >= ?' => $o{height_min} ) : (), + defined $o{height_max} ? ( 'cr.height <= ? AND cr.height > 0' => $o{height_max} ) : (), + defined $o{weight_min} ? ( 'cr.weight >= ?' => $o{weight_min} ) : (), + defined $o{weight_max} ? ( 'cr.weight <= ? AND cr.weight > 0' => $o{weight_max} ) : (), $o{search} ? ( '(cr.name ILIKE ? OR cr.original ILIKE ? OR cr.alias ILIKE ?)', [ map '%%'.$o{search}.'%%', 1..3 ] ) : (), $o{char} ? ( 'LOWER(SUBSTR(cr.name, 1, 1)) = ?' => $o{char} ) : (), defined $o{char} && !$o{char} ? ( '(ASCII(cr.name) < 97 OR ASCII(cr.name) > 122) AND (ASCII(cr.name) < 65 OR ASCII(cr.name) > 90)' => 1 ) : (), + $o{role} ? ( + 'EXISTS(SELECT 1 FROM chars_vns cvi WHERE cvi.cid = cr.id AND cvi.role IN(!l))', + [ ref $o{role} ? $o{role} : [$o{role}] ] ) : (), $o{trait_inc} ? ( 'c.id IN(SELECT cid FROM traits_chars WHERE tid IN(!l) AND spoil <= ? GROUP BY cid HAVING COUNT(tid) = ?)', - [ ref $o{trait_inc} ? $o{trait_inc} : [$o{trait_inc}], $o{traitspoil}, ref $o{trait_inc} ? $#{$o{trait_inc}}+1 : 1 ]) : (), + [ ref $o{trait_inc} ? $o{trait_inc} : [$o{trait_inc}], $o{tagspoil}, ref $o{trait_inc} ? $#{$o{trait_inc}}+1 : 1 ]) : (), $o{trait_exc} ? ( 'c.id NOT IN(SELECT cid FROM traits_chars WHERE tid IN(!l))' => [ ref $o{trait_exc} ? $o{trait_exc} : [$o{trait_exc}] ] ) : (), ); diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm index 474066ce..473fa92c 100644 --- a/lib/VNDB/DB/VN.pm +++ b/lib/VNDB/DB/VN.pm @@ -11,7 +11,7 @@ our @EXPORT = qw|dbVNGet dbVNRevisionInsert dbVNImageId dbScreenshotAdd dbScreen # Options: id, rev, char, search, length, lang, olang, plat, tag_inc, tag_exc, tagspoil, -# hasani, hasshot, ul_notblack, ul_onwish, results, page, what, sort, reverse +# hasani, hasshot, ul_notblack, ul_onwish, results, page, what, sort, reverse, inc_hidden # What: extended anime relations screenshots relgraph rating ranking changes # Sort: id rel pop rating title tagscore rand sub dbVNGet { @@ -66,7 +66,7 @@ sub dbVNGet { $uid && defined $o{ul_onlist} ? ( 'v.id !s IN(SELECT vid FROM vnlists WHERE uid = ?)' => [ $o{ul_onlist} ? '' : 'NOT', $uid ] ) : (), # don't fetch hidden items unless we ask for an ID - !$o{id} && !$o{rev} ? ( + !$o{id} && !$o{rev} && !$o{inc_hidden} ? ( 'v.hidden = FALSE' => 0 ) : (), # optimize fetching random entries (only when there are no other filters present, otherwise this won't work well) $o{sort} eq 'rand' && $o{results} <= 10 && !grep(!/^(?:results|page|what|sort|tagspoil)$/, keys %o) ? ( @@ -107,7 +107,7 @@ sub dbVNGet { my $order = sprintf { id => 'id %s', - rel => 'c_released %s', + rel => 'c_released %s, title ASC', pop => 'c_popularity %s NULLS LAST', rating => 'c_rating %s NULLS LAST', title => 'title %s', diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm index 9c8a1b5e..c707a453 100644 --- a/lib/VNDB/Handler/Chars.pm +++ b/lib/VNDB/Handler/Chars.pm @@ -451,33 +451,39 @@ sub list { my($self, $fch) = @_; my $f = $self->formValidate( - { get => 'p', required => 0, default => 1, template => 'int' }, - { get => 'q', required => 0, default => '' }, + { get => 'p', required => 0, default => 1, template => 'int' }, + { get => 'q', required => 0, default => '' }, + { get => 'fil', required => 0, default => '' }, ); return $self->resNotFound if $f->{_err}; - my($list, $np) = $self->dbCharGet( + my($list, $np) = $self->filFetchDB(char => $f->{fil}, {}, { $fch ne 'all' ? ( char => $fch ) : (), $f->{q} ? ( search => $f->{q} ) : (), results => 50, page => $f->{p}, what => 'vns', - ); + }); $self->htmlHeader(title => mt '_charb_title'); my $quri = uri_escape($f->{q}); + form action => '/c/all', 'accept-charset' => 'UTF-8', method => 'get'; div class => 'mainbox'; h1 mt '_charb_title'; - form action => '/c/all', 'accept-charset' => 'UTF-8', method => 'get'; - $self->htmlSearchBox('c', $f->{q}); - end; + $self->htmlSearchBox('c', $f->{q}); p class => 'browseopts'; for ('all', 'a'..'z', 0) { a href => "/c/$_?q=$quri", $_ eq $fch ? (class => 'optselected') : (), $_ eq 'all' ? mt('_char_all') : $_ ? uc $_ : '#'; } end; + + a id => 'filselect', href => '#c'; + lit '<i>▸</i> '.mt('_js_fil_filters').'<i></i>'; + end; + input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil}; end; + end 'form'; if(!@$list) { div class => 'mainbox'; diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm index 52db6edf..1369c1e2 100644 --- a/lib/VNDB/Handler/Producers.pm +++ b/lib/VNDB/Handler/Producers.pm @@ -304,7 +304,7 @@ sub edit { end; end; td class => 'tc_add'; - a href => '#', mt '_pedit_rel_addbut'; + a href => '#', mt '_js_add'; end; end; end 'table'; diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm index f0fdac3f..0291ba0a 100644 --- a/lib/VNDB/Handler/Releases.pm +++ b/lib/VNDB/Handler/Releases.pm @@ -456,7 +456,7 @@ sub _form { option value => 2, selected => 'selected', mt '_redit_form_prod_pub'; option value => 3, mt '_redit_form_prod_both'; end; end; - td class => 'tc_add'; a id => 'producer_add', href => '#', mt '_redit_form_prod_addbut'; end; + td class => 'tc_add'; a id => 'producer_add', href => '#', mt '_js_add'; end; end; end 'table'; }], ], @@ -469,7 +469,7 @@ sub _form { h2 mt('_redit_form_vn_add'); div; input id => 'vn_input', type => 'text', class => 'text'; - a href => '#', id => 'vn_add', mt '_redit_form_vn_addbut'; + a href => '#', id => 'vn_add', mt '_js_add'; end; }], ], diff --git a/lib/VNDB/Handler/Tags.pm b/lib/VNDB/Handler/Tags.pm index 3d698fc3..39388a30 100644 --- a/lib/VNDB/Handler/Tags.pm +++ b/lib/VNDB/Handler/Tags.pm @@ -390,7 +390,7 @@ sub taglinks { if($f->{u}) { my $o = $self->dbUserGet(uid => $f->{u})->[0]; li; - txt '['; a href => $url->(u=>0), mt '_taglink_fil_remove'; txt '] '; + txt '['; a href => $url->(u=>0), mt '_js_remove'; txt '] '; txt mt '_taglink_fil_user'; txt ' '; a href => "/u$o->{id}", $o->{username}; end; @@ -398,7 +398,7 @@ sub taglinks { if($f->{t}) { my $o = $self->dbTagGet(id => $f->{t})->[0]; li; - txt '['; a href => $url->(t=>0), mt '_taglink_fil_remove'; txt '] '; + txt '['; a href => $url->(t=>0), mt '_js_remove'; txt '] '; txt mt '_taglink_fil_tag'; txt ' '; a href => "/g$o->{id}", $o->{name}; end; @@ -406,7 +406,7 @@ sub taglinks { if($f->{v}) { my $o = $self->dbVNGet(id => $f->{v})->[0]; li; - txt '['; a href => $url->(v=>0), mt '_taglink_fil_remove'; txt '] '; + txt '['; a href => $url->(v=>0), mt '_js_remove'; txt '] '; txt mt '_taglink_fil_vn'; txt ' '; a href => "/v$o->{id}", $o->{title}; end; diff --git a/lib/VNDB/Handler/Traits.pm b/lib/VNDB/Handler/Traits.pm index f3abdfeb..0a21011c 100644 --- a/lib/VNDB/Handler/Traits.pm +++ b/lib/VNDB/Handler/Traits.pm @@ -27,6 +27,7 @@ sub traitpage { my $f = $self->formValidate( { get => 'p', required => 0, default => 1, template => 'int' }, { get => 'm', required => 0, default => undef, enum => [qw|0 1 2|] }, + { get => 'fil', required => 0, default => '' }, ); return $self->resNotFound if $f->{_err}; my $tagspoil = $self->reqCookie('tagspoil')||''; @@ -83,14 +84,15 @@ sub traitpage { childtags($self, mt('_traitp_childs'), 'i', $t) if @{$t->{childs}}; if(!$t->{meta} && $t->{state} == 2) { - my($chars, $np) = $self->dbCharGet( + my($chars, $np) = $self->filFetchDB(char => $f->{fil}, {}, { trait_inc => $trait, - traitspoil => $f->{m}, + tagspoil => $f->{m}, results => 50, page => $f->{p}, what => 'vns', - ); + }); + form action => "/i$t->{id}", 'accept-charset' => 'UTF-8', method => 'get'; div class => 'mainbox'; h1 mt '_traitp_charlist'; @@ -101,12 +103,17 @@ sub traitpage { a href => "/i$trait?m=2", $f->{m} == 2 ? (class => 'optselected') : (), onclick => "setCookie('tagspoil', 2);return true;", mt '_tagp_spoil2'; end; + a id => 'filselect', href => '#c'; + lit '<i>▸</i> '.mt('_js_fil_filters').'<i></i>'; + end; + input type => 'hidden', class => 'hidden', name => 'fil', id => 'fil', value => $f->{fil}; + if(!@$chars) { p; br; br; txt mt '_traitp_nochars'; end; } p; br; txt mt '_traitp_cached'; end; end 'div'; - + end 'form'; @$chars && $self->charBrowseTable($chars, $np, $f, "/i$trait?m=$f->{m}"); } diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm index 0309a01c..56031472 100644 --- a/lib/VNDB/Handler/VNEdit.pm +++ b/lib/VNDB/Handler/VNEdit.pm @@ -10,13 +10,71 @@ use VNDB::Func; TUWF::register( qr{v(?:([1-9]\d*)(?:\.([1-9]\d*))?/edit|/new)} => \&edit, + qr{v/add} => \&addform, qr{xml/vn\.xml} => \&vnxml, qr{xml/screenshots\.xml} => \&scrxml, ); +sub addform { + my $self = shift; + return $self->htmlDenied if !$self->authCan('edit'); + + my $frm; + my $l = []; + if($self->reqMethod eq 'POST') { + return if !$self->authCheckCode; + $frm = $self->formValidate( + { post => 'title', maxlength => 250 }, + { post => 'original', required => 0, maxlength => 250, default => '' }, + { post => 'alias', required => 0, maxlength => 500, default => '' }, + { post => 'continue_ign',required => 0 }, + ); + + # look for duplicates + if(!$frm->{_err} && !$frm->{continue_ign}) { + $l = $self->dbVNGet(search => $frm->{title}, what => 'changes', results => 50, inc_hidden => 1); + push @$l, @{$self->dbVNGet(search => $frm->{original}, what => 'changes', results => 50, inc_hidden => 1)} if $frm->{original}; + $_ && push @$l, $self->dbVNGet(search => $_, what => 'changes', results => 50, inc_hidden => 1) for(split /\n/, $frm->{alias}); + my %ids = map +($_->{id}, $_), @$l; + $l = [ map $ids{$_}, sort { $ids{$a}{title} cmp $ids{$b}{title} } keys %ids ]; + } + + return edit($self, undef, undef, 1) if !@$l && !$frm->{_err}; + } + + $self->htmlHeader(title => mt('_vnedit_title_add'), noindex => 1); + if(@$l) { + div class => 'mainbox'; + h1 mt '_vnedit_dup_title'; + div class => 'warning'; + p; lit mt '_vnedit_dup_msg'; end; + end; + ul; + for(@$l) { + li; + a href => "/v$_->{id}", title => $_->{original}||$_->{title}, "v$_->{id}: ".shorten($_->{title}, 50); + b class => 'standout', ' deleted' if $_->{hidden}; + end; + } + end; + end 'div'; + } + + $self->htmlForm({ frm => $frm, action => '/v/add', continue => @$l ? 2 : 1 }, + vn_add => [ mt('_vnedit_title_add'), + [ input => short => 'title', name => mt '_vnedit_frm_title' ], + [ input => short => 'original', name => mt '_vnedit_original' ], + [ static => content => mt '_vnedit_original_msg' ], + [ textarea => short => 'alias', name => mt('_vnedit_alias'), rows => 4 ], + [ static => content => mt '_vnedit_alias_msg' ], + ]); + $self->htmlFooter; +} + + sub edit { - my($self, $vid, $rev) = @_; + my($self, $vid, $rev, $nosubmit) = @_; my $v = $vid && $self->dbVNGet(id => $vid, what => 'extended screenshots relations anime changes', $rev ? (rev => $rev) : ())->[0]; return $self->resNotFound if $vid && !$v->{id}; @@ -36,7 +94,7 @@ sub edit { my $frm; if($self->reqMethod eq 'POST') { - return if !$self->authCheckCode; + return if !$nosubmit && !$self->authCheckCode; $frm = $self->formValidate( { post => 'title', maxlength => 250 }, { post => 'original', required => 0, maxlength => 250, default => '' }, @@ -53,16 +111,16 @@ sub edit { { post => 'vnrelations', required => 0, default => '', maxlength => 5000 }, { post => 'screenshots', required => 0, default => '', maxlength => 1000 }, { post => 'editsum', required => 0, maxlength => 5000 }, - { post => 'ihid', required => 0 }, - { post => 'ilock', required => 0 }, + { post => 'ihid', required => 0 }, + { post => 'ilock', required => 0 }, ); - push @{$frm->{_err}}, 'badeditsum' if !$frm->{editsum} || lc($frm->{editsum}) eq lc($frm->{desc}); + push @{$frm->{_err}}, 'badeditsum' if !$nosubmit && (!$frm->{editsum} || lc($frm->{editsum}) eq lc($frm->{desc})); # handle image upload - $frm->{image} = _uploadimage($self, $frm); + $frm->{image} = _uploadimage($self, $frm) if !$nosubmit; - if(!$frm->{_err}) { + if(!$nosubmit && !$frm->{_err}) { # parse and re-sort fields that have multiple representations of the same information my $anime = { map +($_=>1), grep /^[0-9]+$/, split /[ ,]+/, $frm->{anime} }; my $relations = [ map { /^([a-z]+),([0-9]+),([01]),(.+)$/ && (!$vid || $2 != $vid) ? [ $1, $2, $3, $4 ] : () } split /\|\|\|/, $frm->{vnrelations} ]; @@ -169,8 +227,8 @@ sub _form { vn_img => [ mt('_vnedit_image'), [ static => nolabel => 1, content => sub { div class => 'img'; p mt '_vnedit_image_none' if !$frm->{image}; - p mt '_vnedit_image_processing' if $frm->{image} < 0; - img src => imgurl(cv => $frm->{image}) if $frm->{image} > 0; + p mt '_vnedit_image_processing' if $frm->{image} && $frm->{image} < 0; + img src => imgurl(cv => $frm->{image}) if $frm->{image} && $frm->{image} > 0; end; div; @@ -220,7 +278,7 @@ sub _form { end; td class => 'tc_title', $v ? $v->{title} : ''; td class => 'tc_add'; - a href => '#', mt '_vnedit_rel_addbut'; + a href => '#', mt '_js_add'; end; end; end 'table'; diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm index 60e7b237..73e2c5ba 100644 --- a/lib/VNDB/Util/CommonHTML.pm +++ b/lib/VNDB/Util/CommonHTML.pm @@ -84,7 +84,7 @@ sub htmlMainTabs { if($type eq 'u' && $self->authCan('usermod')) { li $sel eq 'del' ? (class => 'tabselected') : (); - a href => "/$id/del", mt '_mtabs_del'; + a href => "/$id/del", mt '_js_remove'; end; } diff --git a/lib/VNDB/Util/FormHTML.pm b/lib/VNDB/Util/FormHTML.pm index 21eeb980..13ec56f9 100644 --- a/lib/VNDB/Util/FormHTML.pm +++ b/lib/VNDB/Util/FormHTML.pm @@ -165,10 +165,12 @@ sub htmlFormPart { # Generates a form, first argument is a hashref with global options, keys: -# frm => the $frm as returned by formValidate, -# action => The location the form should POST to (also used as form id) -# upload => 1/0, adds an enctype. -# editsum => 1/0, adds an edit summary field before the submit button +# frm => the $frm as returned by formValidate, +# action => The location the form should POST to (also used as form id) +# upload => 1/0, adds an enctype. +# nosubmit => 1/0, hides the submit button +# editsum => 1/0, adds an edit summary field before the submit button +# continue => 2/1/0, replace submit button with continue buttons # The other arguments are a list of subforms in the form # of (subform-name => [form parts]). Each subform is shown as a # (JavaScript-powered) tab, and has it's own 'mainbox'. This function @@ -235,7 +237,12 @@ sub htmlForm { textarea name => 'editsum', id => 'editsum', rows => 4, cols => 50, $options->{frm}{editsum}||''; br; } - input type => 'submit', value => mt('_form_submit'), class => 'submit'; + if(!$options->{continue}) { + input type => 'submit', value => mt('_form_submit'), class => 'submit'; + } else { + input type => 'submit', value => mt('_form_continue'), class => 'submit'; + input type => 'submit', name => 'continue_ign', value => mt('_form_continue_ign'), class => 'submit', style => 'width: auto' if $options->{continue} == 2; + } end; end 'div'; } diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm index 752f6728..7507d682 100644 --- a/lib/VNDB/Util/LayoutHTML.pm +++ b/lib/VNDB/Util/LayoutHTML.pm @@ -100,7 +100,7 @@ sub _menu { a href => '/g/links?u='.$self->authInfo->{id}, mt '_menu_mytags'; br; br; if($self->authCan('edit')) { - a href => '/v/new', mt '_menu_addvn'; br; + a href => '/v/add', mt '_menu_addvn'; br; a href => '/p/new', mt '_menu_addproducer'; br; } if($self->authCan('charedit')) { diff --git a/lib/VNDB/Util/Misc.pm b/lib/VNDB/Util/Misc.pm index 5406fe06..04114483 100644 --- a/lib/VNDB/Util/Misc.pm +++ b/lib/VNDB/Util/Misc.pm @@ -13,11 +13,12 @@ our @EXPORT = qw|filFetchDB ieCheck|; my %filfields = ( vn => [qw|length hasani tag_inc tag_exc taginc tagexc tagspoil lang olang plat ul_notblack ul_onwish ul_voted ul_onlist|], release => [qw|type patch freeware doujin date_before date_after released minage lang olang resolution plat med voiced ani_story ani_ero|], + char => [qw|gender bloodt bust_min bust_max waist_min waist_max hip_min hip_max height_min height_max weight_min weight_max trait_inc trait_exc tagspoil role|], ); # Arguments: -# type ('vn' or 'release'), +# type ('vn', 'release' or 'char'), # filter overwrite (string or undef), # when defined, these filters will be used instead of the preferences, # must point to a variable, will be modified in-place with the actually used filters @@ -30,7 +31,7 @@ sub filFetchDB { my($self, $type, $overwrite, $pre, $post) = @_; $pre = {} if !$pre; $post = {} if !$post; - my $dbfunc = $self->can($type eq 'vn' ? 'dbVNGet' : 'dbReleaseGet'); + my $dbfunc = $self->can($type eq 'vn' ? 'dbVNGet' : $type eq 'release' ? 'dbReleaseGet' : 'dbCharGet'); my $prefname = 'filter_'.$type; my $pref = $self->authPref($prefname); diff --git a/util/jsgen.pl b/util/jsgen.pl index f046fe1a..26815423 100755 --- a/util/jsgen.pl +++ b/util/jsgen.pl @@ -111,6 +111,9 @@ sub jsgen { $common .= sprintf "animated = [ %s ];\n", join ', ', @{$S{animated}}; $common .= sprintf "voiced = [ %s ];\n", join ', ', @{$S{voiced}}; $common .= sprintf "vn_lengths = [ %s ];\n", join ', ', @{$S{vn_lengths}}; + $common .= sprintf "blood_types = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{blood_types}}; + $common .= sprintf "genders = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{genders}}; + $common .= sprintf "char_roles = [ %s ];\n", join ', ', map qq{"$_"}, @{$S{char_roles}}; $common .= sprintf "L10N_LANG = [ %s ];\n", join(', ', map sprintf('["%s","%s"]', $_, $lang{$_}{"_lang_$_"}||$lang{en}{"_lang_$_"}), VNDB::L10N::languages()); diff --git a/util/sql/all.sql b/util/sql/all.sql index eac8d58a..bb5829f7 100644 --- a/util/sql/all.sql +++ b/util/sql/all.sql @@ -104,8 +104,7 @@ CREATE TRIGGER notify_dbedit AFTER UPDATE ON chars 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 - WHEN (OLD.c_search IS NOT NULL AND NEW.c_search IS NULL AND NOT NEW.hidden - OR NEW.hidden IS DISTINCT FROM OLD.hidden + WHEN (OLD.c_search IS NOT NULL AND NEW.c_search IS NULL OR NEW.latest IS DISTINCT FROM OLD.latest ) EXECUTE PROCEDURE vn_vnsearch_notify(); CREATE TRIGGER vn_vnsearch_notify AFTER UPDATE ON releases FOR EACH ROW diff --git a/util/sql/func.sql b/util/sql/func.sql index 3af04562..e5be2fde 100644 --- a/util/sql/func.sql +++ b/util/sql/func.sql @@ -738,20 +738,16 @@ $$ LANGUAGE plpgsql; -- Check for updates to vn.c_search -- 1. NOTIFY is sent when vn.c_search goes from non-NULL to NULL -- vn.c_search is set to NULL when: --- 2. UPDATE on VN with the hidden field going from TRUE to FALSE --- 3. VN add/edit of which the title/original/alias fields differ from previous revision --- 4. Release gets hidden or unhidden --- 5. Release add/edit of which the title/original/vn fields differ from the previous revision +-- 2. VN add/edit of which the title/original/alias fields differ from previous revision +-- 3. Release gets hidden or unhidden +-- 4. Release add/edit of which the title/original/vn fields differ from the previous revision CREATE OR REPLACE FUNCTION vn_vnsearch_notify() RETURNS trigger AS $$ BEGIN IF TG_TABLE_NAME = 'vn' THEN -- 1. - IF NEW.c_search IS NULL AND NOT NEW.hidden THEN + IF NEW.c_search IS NULL THEN NOTIFY vnsearch; -- 2. - ELSIF NEW.hidden IS DISTINCT FROM OLD.hidden THEN - UPDATE vn SET c_search = NULL WHERE id = NEW.id; - -- 3. ELSIF NEW.latest IS DISTINCT FROM OLD.latest THEN IF EXISTS(SELECT 1 FROM vn_rev v1, vn_rev v2 WHERE v1.id = OLD.latest AND v2.id = NEW.latest @@ -761,7 +757,7 @@ BEGIN END IF; END IF; ELSIF TG_TABLE_NAME = 'releases' THEN - -- 4. & 5. + -- 3. & 4. IF NEW.hidden IS DISTINCT FROM OLD.hidden OR ( NEW.latest IS DISTINCT FROM OLD.latest AND ( EXISTS( diff --git a/util/updates/update_2.22.sql b/util/updates/update_2.22.sql new file mode 100644 index 00000000..3268481e --- /dev/null +++ b/util/updates/update_2.22.sql @@ -0,0 +1,11 @@ + + +DROP TRIGGER vn_vnsearch_notify ON vn; + +CREATE TRIGGER vn_vnsearch_notify AFTER UPDATE ON vn FOR EACH ROW + WHEN (OLD.c_search IS NOT NULL AND NEW.c_search IS NULL + OR NEW.latest IS DISTINCT FROM OLD.latest + ) EXECUTE PROCEDURE vn_vnsearch_notify(); + +\i util/sql/func.sql + |