summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile6
-rw-r--r--data/global.pl1
-rw-r--r--data/lang.txt77
-rw-r--r--data/style.css5
-rw-r--r--lib/Multi/Feed.pm5
-rw-r--r--lib/Multi/Image.pm40
-rw-r--r--lib/VNDB/DB/Chars.pm12
-rw-r--r--lib/VNDB/Handler/Chars.pm69
-rw-r--r--util/sql/func.sql7
-rw-r--r--util/updates/update_2.19.sql3
11 files changed, 205 insertions, 21 deletions
diff --git a/.gitignore b/.gitignore
index c4732a40..0a0a346b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
/static/feeds/
/static/s/*/style.css
/static/s/*/boxbg.png
+/static/ch/
/static/cv/
/static/sf/
/static/st/
diff --git a/Makefile b/Makefile
index a03efb2f..d1ed33fe 100644
--- a/Makefile
+++ b/Makefile
@@ -43,7 +43,7 @@
all: dirs js skins robots data/config.pl
-dirs: static/f/js static/cv static/sf static/st data/log www www/feeds
+dirs: static/f/js static/ch static/cv static/sf static/st data/log www www/feeds
js: static/f/js/en.js
@@ -51,7 +51,7 @@ skins: static/s/*/style.css
robots: dirs www/robots.txt static/robots.txt
-static/cv static/sf static/st:
+static/ch static/cv static/sf static/st:
mkdir $@;
for i in $$(seq -w 0 1 99); do mkdir "$@/$$i"; done
@@ -70,7 +70,7 @@ static/s/%/style.css: static/s/%/conf util/skingen.pl data/style.css
chmod: all
chmod a+xrw static/f/js
- chmod -R a-x+rwX static/{cv,sf,st}
+ chmod -R a-x+rwX static/{ch,cv,sf,st}
chmod a-x+rw static/s/*/{style.css,boxbg.png}
chmod-tladmin:
diff --git a/data/global.pl b/data/global.pl
index 0e4b02e8..e6f935ec 100644
--- a/data/global.pl
+++ b/data/global.pl
@@ -29,6 +29,7 @@ our %S = (%S,
source_url => 'http://git.blicky.net/vndb.git/?h=master',
admin_email => 'contact@vndb.org',
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
diff --git a/data/lang.txt b/data/lang.txt
index ae605d3a..b0f899d1 100644
--- a/data/lang.txt
+++ b/data/lang.txt
@@ -4784,6 +4784,27 @@ cs*:
hu*:
nl : Omschrijving
+:_revfield_c_image
+en : Image
+ru*:
+cs*:
+hu*:
+nl : Plaatje
+
+:_chdiff_image_proc
+en : ~[processing~]
+ru : ~[обработка~]
+cs : ~[zpracovává se~]
+hu : ~[feldolgozás~]
+nl : ~[bewerken...~]
+
+:_chdiff_image_none
+en : No image
+ru : Нет изображения
+cs : Obrázek není
+hu : Nincs kép
+nl : Geen plaatje
+
# Add/edit character
@@ -4843,6 +4864,55 @@ cs*:
hu*:
nl : (In)officiele aliassen, elke alias op een aparte regel.
+:_chare_image
+en : Image
+ru : Изображение
+cs : Obrázek
+hu : Kép
+nl : Plaatje
+
+:_chare_image_none
+en : No image uploaded yet
+ru : Изображения пока нет
+cs : Obrázek ještě nebyl nahrán
+hu : Még nincs kép feltöltve
+nl : Nog geen plaatje geupload
+
+:_chare_image_processing
+en : ~[processing image, please return in a few minutes~]
+ru : ~[обработка изображения, пожалуйста подождите несколько минут~]
+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~]
+
+:_chare_image_id
+en : Image ID
+ru*:
+cs*:
+hu*:
+nl : Plaatje ID
+
+:_chare_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 :
+nl : Gebruik een karakterplaatje dat al op de server staat. Gebruik '0' om een huidig plaatje te verwijderen.
+
+:_chare_image_upload
+en : Upload new image
+ru : Загрузить новое изображение
+cs : Nahrát nový obrázek
+hu : Tölts fel egy új képet
+nl : Upload nieuw plaatje
+
+:_chare_image_upload_msg
+en : Image must be in JPEG or PNG format and at most 1MiB. Images larger than 256x300 will automatically be resized. Image must be safe for work!
+ru*:
+cs*:
+hu*:
+nl : Plaatje moet in het JPEG of PNG formaat zijn, en moet kleiner zijn dan 1MiB. Plaatjes groter dan 256x300 worden automatisch verkleind. Het plaatje moet veilig zijn in een werkomgeving!
+
@@ -6671,6 +6741,13 @@ nl : Spatiegescheiden lijst van [url,http://anidb.net/,AniDB] anime IDs.
en [url,http://anidb.net/a3348,Fate/stay night] toe als gerelateerde anime.[br]
Houd er rekening mee dat het een aantal minuten kan duren voordat de animetitels zichtbaar worden op de VN pagina.
+:_chare_form_desc
+en : Description
+ru : Описание
+cs : Popis
+hu : Leírás
+nl : Omschrijving
+
:_vnedit_image
en : Image
ru : Изображение
diff --git a/data/style.css b/data/style.css
index 99b0f9e6..d4437491 100644
--- a/data/style.css
+++ b/data/style.css
@@ -852,6 +852,11 @@ div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height: 1px
.relbrowse .tc3 { width: 85px; text-align: right; padding: 0; }
+/***** Char edit *****/
+#jt_box_chare_img div.img { float: left; height: 400px; padding-right: 20px; }
+#jt_box_chare_img h2 { margin: 0; }
+
+
/***** Documentation pages *****/
diff --git a/lib/Multi/Feed.pm b/lib/Multi/Feed.pm
index 12631497..483089bd 100644
--- a/lib/Multi/Feed.pm
+++ b/lib/Multi/Feed.pm
@@ -64,13 +64,14 @@ sub generate {
# changes
$_[KERNEL]->post(pg => query => q{
- SELECT '/'||c.type||COALESCE(vr.vid, rr.rid, pr.pid)||'.'||c.rev AS id,
- COALESCE(vr.title, rr.title, pr.name) AS title, extract('epoch' from c.added) AS updated,
+ SELECT '/'||c.type||COALESCE(vr.vid, rr.rid, pr.pid, cr.cid)||'.'||c.rev AS id,
+ COALESCE(vr.title, rr.title, pr.name, cr.name) AS title, extract('epoch' from c.added) AS updated,
u.username, u.id AS uid, c.comments AS summary
FROM changes c
LEFT JOIN vn_rev vr ON c.type = 'v' AND c.id = vr.id
LEFT JOIN releases_rev rr ON c.type = 'r' AND c.id = rr.id
LEFT JOIN producers_rev pr ON c.type = 'p' AND c.id = pr.id
+ LEFT JOIN chars_rev cr ON c.type = 'c' AND c.id = cr.id
JOIN users u ON u.id = c.requester
WHERE c.requester <> 1
ORDER BY c.id DESC
diff --git a/lib/Multi/Image.pm b/lib/Multi/Image.pm
index ad1d436c..3f8bb1a9 100644
--- a/lib/Multi/Image.pm
+++ b/lib/Multi/Image.pm
@@ -18,10 +18,11 @@ sub spawn {
POE::Session->create(
package_states => [
$p => [qw| _start
- _start shutdown cv_check cv_process scr_check scr_process
+ _start shutdown ch_check ch_process cv_check cv_process scr_check scr_process
|],
],
heap => {
+ chpath => $VNDB::ROOT.'/static/ch',
cvpath => $VNDB::ROOT.'/static/cv',
sfpath => $VNDB::ROOT.'/static/sf',
stpath => $VNDB::ROOT.'/static/st',
@@ -35,20 +36,53 @@ sub spawn {
sub _start {
$_[KERNEL]->alias_set('image');
$_[KERNEL]->sig(shutdown => 'shutdown');
- $_[KERNEL]->post(pg => listen => coverimage => 'cv_check', screenshot => 'scr_check');
+ $_[KERNEL]->post(pg => listen => charimage => 'ch_check', coverimage => 'cv_check', screenshot => 'scr_check');
+ $_[KERNEL]->yield('ch_check');
$_[KERNEL]->yield('cv_check');
$_[KERNEL]->yield('scr_check');
}
sub shutdown {
- $_[KERNEL]->post(pg => unlisten => 'coverimage', 'screenshot');
+ $_[KERNEL]->post(pg => unlisten => 'charimage', 'coverimage', 'screenshot');
+ $_[KERNEL]->delay('ch_check');
$_[KERNEL]->delay('cv_check');
$_[KERNEL]->delay('scr_check');
$_[KERNEL]->alias_remove('image');
}
+sub ch_check {
+ $_[KERNEL]->delay('ch_check');
+ $_[KERNEL]->post(pg => query => 'SELECT image FROM chars_rev WHERE image < 0 LIMIT 1', undef, 'ch_process');
+}
+
+
+sub ch_process { # num, res
+ return $_[KERNEL]->delay(ch_check => $_[HEAP]{check_delay}) if $_[ARG0] == 0;
+
+ my $id = -1*$_[ARG1][0]{image};
+ my $start = time;
+ my $img = sprintf '%s/%02d/%d.jpg', $_[HEAP]{chpath}, $id%100, $id;
+ my $os = -s $img;
+
+ my $im = Image::Magick->new;
+ $im->Read($img);
+ $im->Set(magick => 'JPEG');
+ my($ow, $oh) = ($im->Get('width'), $im->Get('height'));
+ my($nw, $nh) = imgsize($ow, $oh, @{$VNDB::S{ch_size}});
+ $im->Thumbnail(width => $nw, height => $nh);
+ $im->Set(quality => 80);
+ $im->Write($img);
+
+ $_[KERNEL]->post(pg => do => 'UPDATE chars_rev SET image = image*-1 WHERE image = ?', [ -1*$id ]);
+ $_[KERNEL]->call(core => log => 'Processed character image %d in %.2fs: %.2fkB (%dx%d) -> %.2fkB (%dx%d)',
+ $id, time-$start, $os/1024, $ow, $oh, (-s $img)/1024, $nw, $nh);
+
+ $_[KERNEL]->yield('ch_check');
+}
+
+
sub cv_check {
$_[KERNEL]->delay('cv_check');
$_[KERNEL]->post(pg => query => 'SELECT image FROM vn_rev WHERE image < 0 LIMIT 1', undef, 'cv_process');
diff --git a/lib/VNDB/DB/Chars.pm b/lib/VNDB/DB/Chars.pm
index b12c4425..3f744239 100644
--- a/lib/VNDB/DB/Chars.pm
+++ b/lib/VNDB/DB/Chars.pm
@@ -5,7 +5,7 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbCharGet dbCharRevisionInsert|;
+our @EXPORT = qw|dbCharGet dbCharRevisionInsert dbCharImageId|;
# options: id rev what results page
@@ -26,7 +26,7 @@ sub dbCharGet {
);
my @select = qw|c.id cr.name cr.original|;
- push @select, qw|c.hidden c.locked cr.alias cr.desc| if $o{what} =~ /extended/;
+ push @select, qw|c.hidden c.locked cr.alias cr.desc cr.image| if $o{what} =~ /extended/;
push @select, qw|h.requester h.comments c.latest u.username h.rev h.ihid h.ilock|, "extract('epoch' from h.added) as added", 'cr.id AS cid' if $o{what} =~ /changes/;
my @join;
@@ -51,10 +51,16 @@ sub dbCharRevisionInsert {
my($self, $o) = @_;
my %set = map exists($o->{$_}) ? (qq|"$_" = ?|, $o->{$_}) : (),
- qw|name original alias desc|;
+ qw|name original alias desc image|;
$self->dbExec('UPDATE edit_char !H', \%set) if keys %set;
}
+# fetches an ID for a new image
+sub dbCharImageId {
+ return shift->dbRow("SELECT nextval('charimg_seq') AS ni")->{ni};
+}
+
+
1;
diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm
index f593077f..1c0643fd 100644
--- a/lib/VNDB/Handler/Chars.pm
+++ b/lib/VNDB/Handler/Chars.pm
@@ -35,6 +35,10 @@ sub page {
[ original => diff => 1 ],
[ alias => diff => qr/[ ,\n\.]/ ],
[ desc => diff => qr/[ ,\n\.]/ ],
+ [ image => htmlize => sub {
+ return $_[0] > 0 ? sprintf '<img src="%s/ch/%02d/%d.jpg" />', $self->{url_static}, $_[0]%100, $_[0]
+ : mt $_[0] < 0 ? '_chdiff_image_proc' : '_chdiff_image_none';
+ }],
);
}
@@ -42,6 +46,7 @@ sub page {
$self->htmlItemMessage('c', $r);
h1 $r->{name};
h2 class => 'alttitle', $r->{original} if $r->{original};
+ img src => sprintf('%s/ch/%02d/%d.jpg', $self->{url_static}, $r->{image}%100, $r->{image}), alt => $r->{name} if $r->{image};
if($r->{desc}) {
p class => 'description';
lit bb2html($r->{desc});
@@ -64,7 +69,7 @@ sub edit {
|| $id && ($r->{locked} && !$self->authCan('lock') || $r->{hidden} && !$self->authCan('del'));
my %b4 = !$id ? () : (
- (map { $_ => $r->{$_} } qw|name original alias desc ihid ilock|),
+ (map { $_ => $r->{$_} } qw|name original alias desc image ihid ilock|),
);
my $frm;
@@ -75,11 +80,16 @@ sub edit {
{ post => 'original', required => 0, maxlength => 200, default => '' },
{ post => 'alias', required => 0, maxlength => 500, default => '' },
{ post => 'desc', required => 0, maxlength => 5000, default => '' },
+ { post => 'image', required => 0, default => 0, template => 'int' },
{ post => 'editsum', required => 0, maxlength => 5000 },
{ post => 'ihid', required => 0 },
{ post => 'ilock', required => 0 },
);
push @{$frm->{_err}}, 'badeditsum' if !$frm->{editsum} || lc($frm->{editsum}) eq lc($frm->{desc});
+
+ # handle image upload
+ $frm->{image} = _uploadimage($self, $r, $frm);
+
if(!$frm->{_err}) {
$frm->{ihid} = $frm->{ihid} ?1:0;
$frm->{ilock} = $frm->{ilock}?1:0;
@@ -99,18 +109,57 @@ sub edit {
$self->htmlHeader(title => $title, noindex => 1);
$self->htmlMainTabs('c', $r, 'edit') if $r;
$self->htmlEditMessage('c', $r, $title);
- $self->htmlForm({ frm => $frm, action => $r ? "/c$id/edit" : '/c/new', editsum => 1 },
- 'chare_geninfo' => [ mt('_chare_form_generalinfo'),
- [ input => name => mt('_pedit_form_name'), short => 'name' ],
- [ input => name => mt('_pedit_form_original'), short => 'original' ],
- [ static => content => mt('_pedit_form_original_note') ],
- [ text => name => mt('_pedit_form_alias'), short => 'alias', rows => 3 ],
- [ static => content => mt('_pedit_form_alias_note') ],
- [ text => name => mt('_pedit_form_desc').'<br /><b class="standout">'.mt('_inenglish').'</b>', short => 'desc', rows => 6 ],
- ]);
+ $self->htmlForm({ frm => $frm, action => $r ? "/c$id/edit" : '/c/new', editsum => 1, upload => 1 },
+ chare_geninfo => [ mt('_chare_form_generalinfo'),
+ [ input => name => mt('_chare_form_name'), short => 'name' ],
+ [ input => name => mt('_chare_form_original'), short => 'original' ],
+ [ static => content => mt('_chare_form_original_note') ],
+ [ text => name => mt('_chare_form_alias'), short => 'alias', rows => 3 ],
+ [ static => content => mt('_chare_form_alias_note') ],
+ [ text => name => mt('_chare_form_desc').'<br /><b class="standout">'.mt('_inenglish').'</b>', short => 'desc', rows => 6 ],
+ ],
+
+ chare_img => [ mt('_chare_image'), [ static => nolabel => 1, content => sub {
+ div class => 'img';
+ p mt '_chare_image_none' if !$frm->{image};
+ p mt '_chare_image_processing' if $frm->{image} && $frm->{image} < 0;
+ img src => sprintf("%s/ch/%02d/%d.jpg", $self->{url_static}, $frm->{image}%100, $frm->{image}) if $frm->{image} && $frm->{image} > 0;
+ end;
+
+ div;
+ h2 mt '_chare_image_id';
+ input type => 'text', class => 'text', name => 'image', id => 'image', value => $frm->{image};
+ p mt '_chare_image_id_msg';
+ br; br;
+
+ h2 mt '_chare_image_upload';
+ input type => 'file', class => 'text', name => 'img', id => 'img';
+ p mt('_chare_image_upload_msg');
+ end;
+ }]]);
$self->htmlFooter;
}
+sub _uploadimage {
+ my($self, $c, $frm) = @_;
+ return $c ? $frm->{image} : 0 if $frm->{_err} || !$self->reqPost('img');
+
+ # perform some elementary checks
+ my $imgdata = $self->reqUploadRaw('img');
+ $frm->{_err} = [ 'noimage' ] if $imgdata !~ /^(\xff\xd8|\x89\x50)/; # JPG or PNG headers
+ $frm->{_err} = [ 'toolarge' ] if length($imgdata) > 1024*1024;
+ return undef if $frm->{_err};
+
+ # get image ID and save it, to be processed by Multi
+ my $imgid = $self->dbCharImageId;
+ my $fn = sprintf '%s/static/ch/%02d/%d.jpg', $VNDB::ROOT, $imgid%100, $imgid;
+ $self->reqSaveUpload('img', $fn);
+ chmod 0666, $fn;
+
+ return -1*$imgid;
+}
+
+
1;
diff --git a/util/sql/func.sql b/util/sql/func.sql
index 23ae0fb2..2b204902 100644
--- a/util/sql/func.sql
+++ b/util/sql/func.sql
@@ -560,6 +560,13 @@ $$ LANGUAGE plpgsql;
+-- Send a notify when a new character image is uploaded
+CREATE OR REPLACE FUNCTION chars_rev_image_notify() RETURNS trigger AS $$
+ BEGIN NOTIFY charimage; RETURN NULL; END;
+$$ LANGUAGE plpgsql;
+
+
+
-- Send a notify when a screenshot needs to be processed
CREATE OR REPLACE FUNCTION screenshot_process_notify() RETURNS trigger AS $$
BEGIN NOTIFY screenshot; RETURN NULL; END;
diff --git a/util/updates/update_2.19.sql b/util/updates/update_2.19.sql
index 0360feda..462e4eed 100644
--- a/util/updates/update_2.19.sql
+++ b/util/updates/update_2.19.sql
@@ -75,6 +75,8 @@ CREATE TABLE chars_vns (
PRIMARY KEY(cid, vid, rid)
);
+CREATE SEQUENCE charimg_seq;
+
-- allow characters to be versioned using the changes table
@@ -86,6 +88,7 @@ DROP TYPE dbentry_type;
ALTER TYPE dbentry_type_tmp RENAME TO dbentry_type;
CREATE TRIGGER hidlock_update BEFORE UPDATE ON chars FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();
+CREATE TRIGGER chars_rev_image_notify AFTER INSERT OR UPDATE ON chars_rev FOR EACH ROW WHEN (NEW.image < 0) EXECUTE PROCEDURE chars_rev_image_notify();
-- load the updated functions