From 9a4a5a5799b2d0bfb456e8d8a8f2aef625a46ffa Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Sat, 27 May 2017 15:45:40 +0200 Subject: [PATCH] Issue #42; abstraction layer finished This commit is dedicated to Schoumi, who is supporting me on Tipeee. Many thanks :-) --- lib/Lutim.pm | 31 ++- lib/Lutim/Command/cron/stats.pm | 64 +++--- lib/Lutim/Controller.pm | 340 +++++++++++++++----------------- lib/Lutim/DB/Image.pm | 120 ++++++++++- lib/Lutim/DB/Image/SQLite.pm | 158 ++++++++++++--- 5 files changed, 467 insertions(+), 246 deletions(-) diff --git a/lib/Lutim.pm b/lib/Lutim.pm index 841462a..854e272 100644 --- a/lib/Lutim.pm +++ b/lib/Lutim.pm @@ -144,24 +144,23 @@ sub startup { my $c = shift; # Create some short patterns for provisioning - if (LutimModel::Lutim->count('WHERE path IS NULL') < $c->config->{provisioning}) { + my $img = Lutim::DB::Image->new(app => $c->app); + if ($img->count_empty < $c->config->{provisioning}) { for (my $i = 0; $i < $c->config->{provis_step}; $i++) { - if (LutimModel->begin) { - my $short; - do { - $short= $c->shortener($c->config->{length}); - } while (LutimModel::Lutim->count('WHERE short = ?', $short) || $short eq 'about' || $short eq 'stats' || $short eq 'd' || $short eq 'm' || $short eq 'gallery' || $short eq 'zip' || $short eq 'infos'); + my $short; + do { + $short = $c->shortener($c->config->{length}); + } while ($img->count_short($short) || $short eq 'about' || $short eq 'stats' || $short eq 'd' || $short eq 'm' || $short eq 'gallery' || $short eq 'zip' || $short eq 'infos'); - LutimModel::Lutim->create( - short => $short, - counter => 0, - enabled => 1, - delete_at_first_view => 0, - delete_at_day => 0, - mod_token => $c->shortener($c->config->{token_length}) - ); - LutimModel->commit; - } + $img->short($short) + ->counter(0) + ->enabled(1) + ->delete_at_first_view(0) + ->delete_at_day(0) + ->mod_token($c->shortener($c->config->{token_length})) + ->write; + + $img = Lutim::DB::Image->new(app => $c->app); } } } diff --git a/lib/Lutim/Command/cron/stats.pm b/lib/Lutim/Command/cron/stats.pm index c82d6fa..08c79b5 100644 --- a/lib/Lutim/Command/cron/stats.pm +++ b/lib/Lutim/Command/cron/stats.pm @@ -1,8 +1,9 @@ package Lutim::Command::cron::stats; use Mojo::Base 'Mojolicious::Command'; -use LutimModel; +use Lutim::DB::Image; use Mojo::DOM; -use Mojo::Util qw(slurp spurt encode); +use Mojo::Util qw(encode); +use Mojo::File; use DateTime; use FindBin qw($Bin); use File::Spec qw(catfile); @@ -17,7 +18,8 @@ sub run { file => File::Spec->catfile($Bin, '..' ,'lutim.conf'), theme => 'default', default => { - stats_day_num => 365 + stats_day_num => 365, + dbtype => 'sqlite' } }); @@ -26,7 +28,7 @@ sub run { $config->{theme} = 'default'; $template = 'themes/'.$config->{theme}.'/templates/data.html.ep.template'; } - my $text = slurp($template); + my $text = Mojo::File->new($template)->slurp; my $dom = Mojo::DOM->new($text); my $thead_tr = $dom->at('table thead tr'); my $tbody_tr = $dom->at('table tbody tr'); @@ -35,18 +37,22 @@ sub run { my $separation = time() - $config->{stats_day_num} * 86400; my %data; - for my $img (LutimModel::Lutim->select('WHERE path IS NOT NULL AND created_at >= ?', $separation)) { - my $time = DateTime->from_epoch(epoch => $img->created_at); - my ($year, $month, $day) = ($time->year(), $time->month(), $time->day()); + my $img = Lutim::DB::Image->new(app => $c->app); + $img->select_created_after($separation)->each( + sub { + my ($e, $num) = @_; + my $time = DateTime->from_epoch(epoch => $e->created_at); + my ($year, $month, $day) = ($time->year(), $time->month(), $time->day()); - if (defined($data{$year}->{$month}->{$day})) { - $data{$year}->{$month}->{$day} += 1; - } else { - $data{$year}->{$month}->{$day} = 1; + if (defined($data{$year}->{$month}->{$day})) { + $data{$year}->{$month}->{$day} += 1; + } else { + $data{$year}->{$month}->{$day} = 1; + } } - } + ); - my $total = LutimModel::Lutim->count('WHERE path IS NOT NULL AND created_at < ?', $separation); + my $total = $img->count_created_before($separation); for my $year (sort {$a <=> $b} keys %data) { for my $month (sort {$a <=> $b} keys %{$data{$year}}) { for my $day (sort {$a <=> $b} keys %{$data{$year}->{$month}}) { @@ -64,27 +70,29 @@ sub run { $config->{theme} = 'default'; $template = 'themes/'.$config->{theme}.'/templates/raw.html.ep.template'; } - my $text2 = slurp($template2); + my $text2 = Mojo::File->new($template2)->slurp; my $dom2 = Mojo::DOM->new($text2); my $raw = $dom2->at('table tbody'); my $raw_foot = $dom2->at('table tfoot'); - my $unlimited_enabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 0 AND enabled = 1'); - my $unlimited_disabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 0 AND enabled = 0'); - my $day_enabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 1 AND enabled = 1'); - my $day_disabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 1 AND enabled = 0'); - my $week_enabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 7 AND enabled = 1'); - my $week_disabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 7 AND enabled = 0'); - my $month_enabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 30 AND enabled = 1'); - my $month_disabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 30 AND enabled = 0'); - my $year_enabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 365 AND enabled = 1'); - my $year_disabled = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 365 AND enabled = 0'); - my $year_disabled_in_month = LutimModel::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = 365 AND enabled = 0 AND created_at < ?', time - 30 * 86400); + my $unlimited_enabled = $img->count_delete_at_day_endis(0, 1); + my $unlimited_disabled = $img->count_delete_at_day_endis(0, 0); + my $day_enabled = $img->count_delete_at_day_endis(1, 1); + my $day_disabled = $img->count_delete_at_day_endis(1, 0); + my $week_enabled = $img->count_delete_at_day_endis(7, 1); + my $week_disabled = $img->count_delete_at_day_endis(7, 0); + my $month_enabled = $img->count_delete_at_day_endis(30, 1); + my $month_disabled = $img->count_delete_at_day_endis(30, 0); + my $year_enabled = $img->count_delete_at_day_endis(365, 1); + my $year_disabled = $img->count_delete_at_day_endis(365, 0); + my $year_disabled_in_month = $img->count_delete_at_day_endis(365, 1, time - 335 * 86400); + + my $year_disabled_in_month_pct = ($year_enabled != 0) ? " (".sprintf('%.2f', $year_disabled_in_month/$year_enabled)."%)" : ''; $raw->append_content("\n<%= \$raw[4] %>".$unlimited_enabled."".$unlimited_disabled."ΓΈ\n"); $raw->append_content("<%= \$raw[5] %>".$day_enabled."".$day_disabled."".$day_enabled." (100%)\n"); $raw->append_content("<%= \$raw[6] %>".$week_enabled."".$week_disabled."".$week_enabled." (100%)\n"); $raw->append_content("<%= \$raw[7] %>".$month_enabled."".$month_disabled."".$month_enabled." (100%)\n"); - $raw->append_content("<%= \$raw[8] %>".$year_enabled."".$year_disabled."".$year_disabled_in_month." (".sprintf('%.2f', $year_disabled_in_month/$year_enabled)."%)\n"); + $raw->append_content("<%= \$raw[8] %>".$year_enabled."".$year_disabled."".$year_disabled_in_month.$year_disabled_in_month_pct."\n"); $raw_foot->append_content("\n<%= \$raw[9] %>".($unlimited_enabled + $day_enabled + $week_enabled + $month_enabled + $year_enabled)."".($unlimited_disabled + $day_disabled + $week_disabled + $month_disabled + $year_disabled)."".($day_enabled + $week_enabled + $month_enabled + $year_disabled_in_month)."\n"); @@ -140,8 +148,8 @@ Morris.Donut({ $dom2 EOF - spurt $dom, 'themes/'.$config->{theme}.'/templates/data.html.ep'; - spurt encode('UTF-8', $dom2), 'themes/'.$config->{theme}.'/templates/raw.html.ep'; + Mojo::File->new('themes/'.$config->{theme}.'/templates/data.html.ep')->spurt($dom); + Mojo::File->new('themes/'.$config->{theme}.'/templates/raw.html.ep')->spurt(encode('UTF-8', $dom2)); } =encoding utf8 diff --git a/lib/Lutim/Controller.pm b/lib/Lutim/Controller.pm index d9229b8..1a94305 100644 --- a/lib/Lutim/Controller.pm +++ b/lib/Lutim/Controller.pm @@ -4,6 +4,7 @@ use Mojo::Base 'Mojolicious::Controller'; use Mojo::Util qw(url_unescape b64_encode); use Mojo::Asset::Memory; use Mojo::JSON qw(true false); +use Lutim::DB::Image; use DateTime; use Digest::file qw(digest_file_hex); use Text::Unidecode; @@ -45,9 +46,12 @@ sub about { } sub stats { - shift->render( + my $c = shift; + + my $img = Lutim::DB::Image->new(app => $c); + $c->render( template => 'stats', - total => LutimModel::Lutim->count('WHERE path IS NOT NULL') + total => $img->count_not_empty ); } @@ -85,13 +89,13 @@ sub get_counter { my $short = $c->param('short'); my $token = $c->param('token'); - my @images = LutimModel::Lutim->select('WHERE short = ? AND path IS NOT NULL AND mod_token = ?', ($short, $token)); - if (scalar(@images)) { + my $img = Lutim::DB::Image->new(app => $c->app, short => $short); + if (defined($img->mod_token) && $img->mod_token eq $token) { return $c->render( json => { success => true, - counter => $images[0]->counter, - enabled => ($images[0]->enabled) ? true : false + counter => $img->counter, + enabled => ($img->enabled) ? true : false } ); } @@ -109,11 +113,10 @@ sub modify { my $token = $c->param('token'); my $url = $c->param('url'); - my @images = LutimModel::Lutim->select('WHERE short = ? AND path IS NOT NULL', $short); - if (scalar(@images)) { - my $image = $images[0]; + my $image = Lutim::DB::Image->new(app => $c->app, short => $short); + if ($image->path) { my $msg; - if ($image->mod_token() ne $token || $token eq '') { + if ($image->mod_token ne $token || $token eq '') { $msg = $c->l('The delete token is invalid.'); } else { $c->app->log->info('[MODIFICATION] someone modify '.$image->filename.' with token method (path: '.$image->path.')'); @@ -178,11 +181,10 @@ sub delete { my $short = $c->param('short'); my $token = $c->param('token'); - my @images = LutimModel::Lutim->select('WHERE short = ? AND path IS NOT NULL', $short); - if (scalar(@images)) { - my $image = $images[0]; + my $image = Lutim::DB::Image->new(app => $c->app, short => $short); + if ($image->path) { my $msg; - if ($image->mod_token() ne $token || $token eq '') { + if ($image->mod_token ne $token || $token eq '') { $msg = $c->l('The delete token is invalid.'); } elsif ($image->enabled() == 0) { $msg = $c->l('The image %1 has already been deleted.', $image->filename); @@ -349,102 +351,96 @@ sub add { return $c->redirect_to('/'); } } - if(LutimModel->begin) { - my @records = LutimModel::Lutim->select('WHERE path IS NULL LIMIT 1'); - if (scalar(@records)) { - # Save file and create record - my $filename = unidecode($upload->filename); - my $ext = ($filename =~ m/([^.]+)$/)[0]; - my $path = 'files/'.$records[0]->short.'.'.$ext; + my $record = Lutim::DB::Image->new(app => $c->app)->select_empty; + if ($record->short) { + # Save file and create record + my $filename = unidecode($upload->filename); + my $ext = ($filename =~ m/([^.]+)$/)[0]; + my $path = 'files/'.$record->short.'.'.$ext; - my ($width, $height); - if ($im_loaded && $mediatype ne 'image/svg+xml' && $mediatype !~ m#image/(x-)?xcf# && $mediatype ne 'image/webp') { # ImageMagick don't work in Debian with svg (for now?) - my $im = Image::Magick->new; - $im->BlobToImage($upload->slurp); + my ($width, $height); + if ($im_loaded && $mediatype ne 'image/svg+xml' && $mediatype !~ m#image/(x-)?xcf# && $mediatype ne 'image/webp') { # ImageMagick don't work in Debian with svg (for now?) + my $im = Image::Magick->new; + $im->BlobToImage($upload->slurp); - # Automatic rotation from EXIF tag - $im->AutoOrient(); + # Automatic rotation from EXIF tag + $im->AutoOrient(); - # Update the uploaded file with it's auto-rotated clone - my $asset = Mojo::Asset::Memory->new->add_chunk($im->ImageToBlob()); - $upload->asset($asset); + # Update the uploaded file with it's auto-rotated clone + my $asset = Mojo::Asset::Memory->new->add_chunk($im->ImageToBlob()); + $upload->asset($asset); - # Create the thumbnail - $width = $im->Get('width'); - $height = $im->Get('height'); - $im->Resize(geometry=>'x85'); - - $thumb = 'data:'.$mediatype.';base64,'; - if ($mediatype eq 'image/gif') { - $thumb .= b64_encode $im->[0]->ImageToBlob(); - } else { - $thumb .= b64_encode $im->ImageToBlob(); - } + # Create the thumbnail + $width = $im->Get('width'); + $height = $im->Get('height'); + $im->Resize(geometry=>'x85'); + $thumb = 'data:'.$mediatype.';base64,'; + if ($mediatype eq 'image/gif') { + $thumb .= b64_encode $im->[0]->ImageToBlob(); + } else { + $thumb .= b64_encode $im->ImageToBlob(); } - unless ((defined($keep_exif) && $keep_exif) || $mediatype eq 'image/svg+xml' || $mediatype !~ m#image/(x-)?xcf# || $mediatype ne 'image/webp') { - # Remove the EXIF tags - my $data = new IO::Scalar \$upload->slurp(); - my $et = new Image::ExifTool; - - # Use $data in Image::ExifTool object - $et->ExtractInfo($data); - # Remove all metadata - $et->SetNewValue('*', undef); - - # Create a temporary IO::Scalar to write into - my $temp; - my $a = new IO::Scalar \$temp; - $et->WriteInfo($data, $a); - - # Update the uploaded file with it's no-tags clone - $data = Mojo::Asset::Memory->new->add_chunk($temp); - $upload->asset($data); - } - - my $key; - if ($c->param('crypt') || $c->config->{always_encrypt}) { - ($upload, $key) = $c->crypt($upload, $filename); - } - $upload->move_to($path); - - $records[0]->update( - path => $path, - filename => $filename, - mediatype => $mediatype, - footprint => digest_file_hex($path, 'SHA-512'), - enabled => 1, - delete_at_day => ($c->param('delete-day') && ($c->param('delete-day') <= $c->max_delay || $c->max_delay == 0)) ? $c->param('delete-day') : $c->max_delay, - delete_at_first_view => ($c->param('first-view')) ? 1 : 0, - created_at => time(), - created_by => $ip, - width => $width, - height => $height - ); - - # Log image creation - $c->app->log->info('[CREATION] '.$ip.' pushed '.$filename.' (path: '.$path.')'); - - # Give url to user - $short = $records[0]->short; - $real_short = $short; - if (!defined($records[0]->mod_token)) { - $records[0]->update( - mod_token => $c->shortener($c->config->{token_length}) - ); - } - $token = $records[0]->mod_token; - $short .= '/'.$key if (defined($key)); - - $limit = $records[0]->delete_at_day; - $created = $records[0]->created_at; - } else { - # Houston, we have a problem - $msg = $c->l('There is no more available URL. Retry or contact the administrator. %1', $c->config->{contact}); } + + unless ((defined($keep_exif) && $keep_exif) || $mediatype eq 'image/svg+xml' || $mediatype !~ m#image/(x-)?xcf# || $mediatype ne 'image/webp') { + # Remove the EXIF tags + my $data = new IO::Scalar \$upload->slurp(); + my $et = new Image::ExifTool; + + # Use $data in Image::ExifTool object + $et->ExtractInfo($data); + # Remove all metadata + $et->SetNewValue('*', undef); + + # Create a temporary IO::Scalar to write into + my $temp; + my $a = new IO::Scalar \$temp; + $et->WriteInfo($data, $a); + + # Update the uploaded file with it's no-tags clone + $data = Mojo::Asset::Memory->new->add_chunk($temp); + $upload->asset($data); + } + + my $key; + if ($c->param('crypt') || $c->config->{always_encrypt}) { + ($upload, $key) = $c->crypt($upload, $filename); + } + $upload->move_to($path); + + $record->path($path) + ->filename($filename) + ->mediatype($mediatype) + ->footprint(digest_file_hex($path, 'SHA-512')) + ->enabled(1) + ->delete_at_day(($c->param('delete-day') && ($c->param('delete-day') <= $c->max_delay || $c->max_delay == 0)) ? $c->param('delete-day') : $c->max_delay) + ->delete_at_first_view(($c->param('first-view'))? 1 : 0) + ->created_at(time()) + ->created_by($ip) + ->width($width) + ->height($height) + ->write; + + # Log image creation + $c->app->log->info('[CREATION] '.$ip.' pushed '.$filename.' (path: '.$path.')'); + + # Give url to user + $short = $record->short; + $real_short = $short; + if (!defined($record->mod_token)) { + $record->mod_token($c->shortener($c->config->{token_length}))->write; + } + $token = $record->mod_token; + $short .= '/'.$key if (defined($key)); + + $limit = $record->delete_at_day; + $created = $record->created_at; + } else { + # Houston, we have a problem + $msg = $c->l('There is no more available URL. Retry or contact the administrator. %1', $c->config->{contact}); } - LutimModel->commit; } else { $msg = $c->l('The file %1 is not an image.', $upload->filename); } @@ -519,15 +515,14 @@ sub short { my $thumb = $c->param('thumb'); my $dl = (defined($c->param('dl'))) ? 'attachment' : 'inline'; - my @images = LutimModel::Lutim->select('WHERE short = ? AND ENABLED = 1 AND path IS NOT NULL', $short); - - if (scalar(@images)) { - if($images[0]->delete_at_day && $images[0]->created_at + $images[0]->delete_at_day * 86400 <= time()) { + my $image = Lutim::DB::Image->new(app => $c->app, short => $short); + if ($image->enabled && $image->path) { + if($image->delete_at_day && $image->created_at + $image->delete_at_day * 86400 <= time()) { # Log deletion - $c->app->log->info('[DELETION] someone tried to view '.$images[0]->filename.' but it has been removed by expiration (path: '.$images[0]->path.')'); + $c->app->log->info('[DELETION] someone tried to view '.$image->filename.' but it has been removed by expiration (path: '.$image->path.')'); # Delete image - $c->delete_image($images[0]); + $c->delete_image($image); # Warn user $c->flash( @@ -539,54 +534,53 @@ sub short { my $test; if (defined($touit)) { $test = 1; - my $short = $images[0]->short; + my $short = $image->short; $short .= '/'.$key if (defined($key)); my ($width, $height) = (340,340); - if ($images[0]->mediatype eq 'image/gif') { - if (defined($images[0]->width) && defined($images[0]->height)) { - ($width, $height) = ($images[0]->width, $images[0]->height); + if ($image->mediatype eq 'image/gif') { + if (defined($image->width) && defined($image->height)) { + ($width, $height) = ($image->width, $image->height); } elsif ($im_loaded) { - my $upload = $c->decrypt($key, $images[0]->path); + my $upload = $c->decrypt($key, $image->path); my $im = Image::Magick->new; $im->BlobToImage($upload->slurp); $width = $im->Get('width'); $height = $im->Get('height'); - $images[0]->update( - width => $width, - height => $height - ); + $image->width($width) + ->height($height) + ->write; } } return $c->render( template => 'twitter', layout => undef, short => $short, - filename => $images[0]->filename, - mimetype => ($c->req->url->to_abs()->scheme eq 'https') ? $images[0]->mediatype : '', + filename => $image->filename, + mimetype => ($c->req->url->to_abs()->scheme eq 'https') ? $image->mediatype : '', width => $width, height => $height ); } else { # Delete image if needed - if ($images[0]->delete_at_first_view && $images[0]->counter >= 1) { + if ($image->delete_at_first_view && $image->counter >= 1) { # Log deletion - $c->app->log->info('[DELETION] someone made '.$images[0]->filename.' removed (path: '.$images[0]->path.')'); + $c->app->log->info('[DELETION] someone made '.$image->filename.' removed (path: '.$image->path.')'); # Delete image - $c->delete_image($images[0]); + $c->delete_image($image); $c->flash( msg => $c->l('Unable to find the image: it has been deleted.') ); return $c->redirect_to('/'); } else { - my $expires = ($images[0]->delete_at_day) ? $images[0]->delete_at_day : 360; - my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $images[0]->created_at); + my $expires = ($image->delete_at_day) ? $image->delete_at_day : 360; + my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $image->created_at); $dt->set_time_zone('GMT'); $expires = $dt->strftime("%a, %d %b %Y %H:%M:%S GMT"); - $test = $c->render_file($images[0]->filename, $images[0]->path, $images[0]->mediatype, $dl, $expires, $images[0]->delete_at_first_view, $key, $thumb); + $test = $c->render_file($image->filename, $image->path, $image->mediatype, $dl, $expires, $image->delete_at_first_view, $key, $thumb); } } @@ -594,40 +588,36 @@ sub short { # Update counter $c->on(finish => sub { # Log access - $c->app->log->info('[VIEW] someone viewed '.$images[0]->filename.' (path: '.$images[0]->path.')'); + $c->app->log->info('[VIEW] someone viewed '.$image->filename.' (path: '.$image->path.')'); # Update record - my $counter = $images[0]->counter + 1; - $images[0]->update(counter => $counter); - - $images[0]->update(last_access_at => time()); + my $counter = $image->counter + 1; + $image->counter($counter) + ->last_access_at(time) + ->write; # Delete image if needed - if ($images[0]->delete_at_first_view) { + if ($image->delete_at_first_view) { # Log deletion - $c->app->log->info('[DELETION] someone made '.$images[0]->filename.' removed (path: '.$images[0]->path.')'); + $c->app->log->info('[DELETION] someone made '.$image->filename.' removed (path: '.$image->path.')'); # Delete image - $c->delete_image($images[0]); + $c->delete_image($image); } }); } + } elsif ($image->path && !$image->enabled) { + # Log access try + $c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it does\'nt exist anymore.'); + + # Warn user + $c->flash( + msg => $c->l('Unable to find the image: it has been deleted.') + ); + return $c->redirect_to('/'); } else { - @images = LutimModel::Lutim->select('WHERE short = ? AND ENABLED = 0 AND path IS NOT NULL', $short); - - if (scalar(@images)) { - # Log access try - $c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it does\'nt exist anymore.'); - - # Warn user - $c->flash( - msg => $c->l('Unable to find the image: it has been deleted.') - ); - return $c->redirect_to('/'); - } else { - # Image never existed - $c->render_not_found; - } + # Image never existed + $c->render_not_found; } } @@ -649,16 +639,16 @@ sub zip { } else { $short =~ s/\.[^.]*//; } - my @images = LutimModel::Lutim->select('WHERE short = ? AND ENABLED = 1 AND path IS NOT NULL', $short); + my $image = Lutim::DB::Image->new(app => $c->app, short => $short); - if (scalar(@images)) { - my $filename = $images[0]->filename; - if($images[0]->delete_at_day && $images[0]->created_at + $images[0]->delete_at_day * 86400 <= time()) { + if ($image->enabled && $image->path) { + my $filename = $image->filename; + if($image->delete_at_day && $image->created_at + $image->delete_at_day * 86400 <= time()) { # Log deletion - $c->app->log->info('[DELETION] someone tried to view '.$images[0]->filename.' but it has been removed by expiration (path: '.$images[0]->path.')'); + $c->app->log->info('[DELETION] someone tried to view '.$image->filename.' but it has been removed by expiration (path: '.$image->path.')'); # Delete image - $c->delete_image($images[0]); + $c->delete_image($image); # Warn user $zip->addString($c->l('Unable to find the image: it has been deleted.'), 'images/'.$filename.'.txt'); @@ -666,22 +656,22 @@ sub zip { } # Delete image if needed - if ($images[0]->delete_at_first_view && $images[0]->counter >= 1) { + if ($image->delete_at_first_view && $image->counter >= 1) { # Log deletion - $c->app->log->info('[DELETION] someone made '.$images[0]->filename.' removed (path: '.$images[0]->path.')'); + $c->app->log->info('[DELETION] someone made '.$image->filename.' removed (path: '.$image->path.')'); # Delete image - $c->delete_image($images[0]); + $c->delete_image($image); $zip->addString($c->l('Unable to find the image: it has been deleted.'), 'images/'.$filename.'.txt'); next; } else { - my $expires = ($images[0]->delete_at_day) ? $images[0]->delete_at_day : 360; - my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $images[0]->created_at); + my $expires = ($image->delete_at_day) ? $image->delete_at_day : 360; + my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $image->created_at); $dt->set_time_zone('GMT'); $expires = $dt->strftime("%a, %d %b %Y %H:%M:%S GMT"); - my $path = $images[0]->path; + my $path = $image->path; unless ( -f $path && -r $path ) { $c->app->log->error("Cannot read file [$path]. error [$!]"); $zip->addString($c->l('Unable to find the image: it has been deleted.'), 'images/'.$filename.'.txt'); @@ -695,26 +685,22 @@ sub zip { } # Log access - $c->app->log->info('[VIEW] someone viewed '.$images[0]->filename.' (path: '.$images[0]->path.')'); - # Update counter - $images[0]->update(counter => $images[0]->counter + 1); - # Update record - $images[0]->update(last_access_at => time()); + $c->app->log->info('[VIEW] someone viewed '.$image->filename.' (path: '.$image->path.')'); + # Update counter and record + $image->counter($image->counter + 1) + ->last_access_at(time) + ->write; } + } elsif ($image->path && !$image->enabled) { + # Log access try + $c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it does\'nt exist anymore.'); + + # Warn user + $zip->addString($c->l('Unable to find the image: it has been deleted.'), 'images/'.$image->filename.'.txt'); + next; } else { - @images = LutimModel::Lutim->select('WHERE short = ? AND ENABLED = 0 AND path IS NOT NULL', $short); - - if (scalar(@images)) { - # Log access try - $c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it does\'nt exist anymore.'); - - # Warn user - $zip->addString($c->l('Unable to find the image: it has been deleted.'), 'images/'.$images[0]->filename.'.txt'); - next; - } else { - $zip->addString($c->l('Image not found.'), 'images/'.$short.'.txt'); - next; - } + $zip->addString($c->l('Image not found.'), 'images/'.$short.'.txt'); + next; } } my ($fh, $zipfile) = Archive::Zip::tempFile(); diff --git a/lib/Lutim/DB/Image.pm b/lib/Lutim/DB/Image.pm index a035dd4..0e936e0 100644 --- a/lib/Lutim/DB/Image.pm +++ b/lib/Lutim/DB/Image.pm @@ -98,9 +98,9 @@ sub new { if ($dbtype eq 'sqlite') { use Lutim::DB::Image::SQLite; $c = Lutim::DB::Image::SQLite->new(@_); - } elsif ($dbtype eq 'postgresql') { - use Lutim::DB::Image::Pg; - $c = Lutim::DB::Image::Pg->new(@_); + #} elsif ($dbtype eq 'postgresql') { + # use Lutim::DB::Image::Pg; + # $c = Lutim::DB::Image::Pg->new(@_); } } @@ -129,6 +129,120 @@ sub to_hash { }; } +=head2 count_delete_at_day_endis + +=over 1 + +=item B : C<$c-Ecount_delete_at_day_endis($delete_at_day, $enabled[, $time])> + +=item B : two mandatory parameters: one integer, the delete_at_day attribute, a boolean (0 or 1), the enabled attribute + an optional parameter: an unix timestamp + +=item B : count how many images there is with the given delete_at_day attribute, and enabled or disabled, depending on the given enabled attribute + if the optional parameter is given, count only images according to the given mandatory parameters that were created before the timestamp + +=item B : integer + +=back + +=head2 count_created_before + +=over 1 + +=item B : C<$c-Ecount_created_before($time)> + +=item B : an unix timestamp + +=item B : count how many images have been created before the given timestamp + +=item B : integer + +=back + +=head2 select_created_after + +=over 1 + +=item B : C<$c-Eselect_created_after($time)> + +=item B : an unix timestamp + +=item B : select images created after the given timestamp + +=item B : a Mojo::Collection object containing the images created after the given timestamp + +=back + +=head2 select_empty + +=over 1 + +=item B : C<$c-Eselect_empty> + +=item B : none + +=item B : select a ready-to-use empty record + +=item B : a db accessor object + +=back + +=head2 write + +=over 1 + +=item B : C<$c-Ewrite> + +=item B : none + +=item B : create or update a record in the database, with the values of the object's attributes + +=item B : the db accessor object + +=back + +=head2 count_short + +=over 1 + +=item B : C<$c-Ecount_short($short)> + +=item B : a random string, unique image identifier in the database + +=item B : checks that an identifier isn't already used + +=item B : integer, number of records having this identifier (should be 0 or 1) + +=back + +=head2 count_empty + +=over 1 + +=item B : C<$c-Ecount_empty> + +=item B : none + +=item B : counts the number of record which path is null + +=item B : integer + +=back + +=head2 count_not_empty + +=over 1 + +=item B : C<$c-Ecount_not_empty> + +=item B : none + +=item B : counts the number of record which path is not null + +=item B : integer + +=back + =head2 clean_ips_until =over 1 diff --git a/lib/Lutim/DB/Image/SQLite.pm b/lib/Lutim/DB/Image/SQLite.pm index a47f103..a165d37 100644 --- a/lib/Lutim/DB/Image/SQLite.pm +++ b/lib/Lutim/DB/Image/SQLite.pm @@ -13,6 +13,120 @@ sub new { return $c; } +sub count_delete_at_day_endis { + my $c = shift; + my $day = shift; + my $enabled = shift; + my $created = shift; + + if (defined $created) { + return Lutim::DB::SQLite::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = ? AND enabled = ? AND created_at < ?', $day, $enabled, $created); + } else { + return Lutim::DB::SQLite::Lutim->count('WHERE path IS NOT NULL AND delete_at_day = ? AND enabled = ?', $day, $enabled); + } +} + +sub count_created_before { + my $c = shift; + my $time = shift; + + return Lutim::DB::SQLite::Lutim->count('WHERE path IS NOT NULL AND created_at < ?', $time); +} + +sub select_created_after { + my $c = shift; + my $time = shift; + + my @images; + + my @records = Lutim::DB::SQLite::Lutim->select('WHERE path IS NOT NULL AND created_at >= ?', $time); + + for my $e (@records) { + my $i = Lutim::DB::Image->new(app => $c->app); + $i->record($e); + $i->_slurp; + + push @images, $i; + } + + return c(@images); +} + +sub select_empty { + my $c = shift; + + my @records = Lutim::DB::SQLite::Lutim->select('WHERE path IS NULL LIMIT 1'); + + $c->record($records[0]); + $c = $c->_slurp; + + return $c; +} + +sub write { + my $c = shift; + + if ($c->record) { + $c->record->update( + counter => $c->counter, + created_at => $c->created_at, + created_by => $c->created_by, + delete_at_day => $c->delete_at_day, + delete_at_first_view => $c->delete_at_first_view, + enabled => $c->enabled, + filename => $c->filename, + footprint => $c->footprint, + height => $c->height, + last_access_at => $c->last_access_at, + mediatype => $c->mediatype, + mod_token => $c->mod_token, + path => $c->path, + short => $c->short, + width => $c->width + ); + } else { + my $record = Lutim::DB::SQLite::Lutim->create( + counter => $c->counter, + created_at => $c->created_at, + created_by => $c->created_by, + delete_at_day => $c->delete_at_day, + delete_at_first_view => $c->delete_at_first_view, + enabled => $c->enabled, + filename => $c->filename, + footprint => $c->footprint, + height => $c->height, + last_access_at => $c->last_access_at, + mediatype => $c->mediatype, + mod_token => $c->mod_token, + path => $c->path, + short => $c->short, + width => $c->width + ); + $c->record($record); + } + + return $c; +} + +sub count_short { + my $c = shift; + my $short = shift; + + return Lutim::DB::SQLite::Lutim->count('WHERE short IS ?', $short); +} + +sub count_empty { + my $c = shift; + + return Lutim::DB::SQLite::Lutim->count('WHERE path IS NULL'); +} + +sub count_not_empty { + my $c = shift; + + return Lutim::DB::SQLite::Lutim->count('WHERE path IS NOT NULL'); +} + sub clean_ips_until { my $c = shift; my $time = shift; @@ -50,7 +164,7 @@ sub get_images_to_clean { my @images; - my @records = c(Lutim::DB::SQLite::Lutim->select('WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', time())); + my @records = Lutim::DB::SQLite::Lutim->select('WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', time()); for my $e (@records) { my $i = Lutim::DB::Image->new(app => $c->app); @@ -68,7 +182,7 @@ sub get_50_oldest { my @images; - my @records = c(Lutim::DB::SQLite::Lutim->select('WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50')); + my @records = Lutim::DB::SQLite::Lutim->select('WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50'); for my $e (@records) { my $i = Lutim::DB::Image->new(app => $c->app); @@ -93,31 +207,31 @@ sub disable { sub _slurp { my $c = shift; - my @urls; + my @images; if ($c->record) { - @urls = ($c->record); + @images = ($c->record); } elsif ($c->short) { - @urls = Lutim::DB::SQLite::Lutim->select('WHERE short = ?', $c->short); + @images = Lutim::DB::SQLite::Lutim->select('WHERE short = ?', $c->short); } - if (scalar @urls) { - $c->short($urls[0]->short); - $c->path($urls[0]->path); - $c->footprint($urls[0]->footprint); - $c->enabled($urls[0]->enabled); - $c->mediatype($urls[0]->mediatype); - $c->filename($urls[0]->filename); - $c->counter($urls[0]->counter); - $c->delete_at_first_view($urls[0]->delete_at_first_view); - $c->delete_at_day($urls[0]->delete_at_day); - $c->created_at($urls[0]->created_at); - $c->created_by($urls[0]->created_by); - $c->last_access_at($urls[0]->last_access_at); - $c->mod_token($urls[0]->mod_token); - $c->width($urls[0]->width); - $c->height($urls[0]->height); + if (scalar @images) { + $c->short($images[0]->short); + $c->path($images[0]->path); + $c->footprint($images[0]->footprint); + $c->enabled($images[0]->enabled); + $c->mediatype($images[0]->mediatype); + $c->filename($images[0]->filename); + $c->counter($images[0]->counter); + $c->delete_at_first_view($images[0]->delete_at_first_view); + $c->delete_at_day($images[0]->delete_at_day); + $c->created_at($images[0]->created_at); + $c->created_by($images[0]->created_by); + $c->last_access_at($images[0]->last_access_at); + $c->mod_token($images[0]->mod_token); + $c->width($images[0]->width); + $c->height($images[0]->height); - $c->record($urls[0]) unless $c->record; + $c->record($images[0]) unless $c->record; } return $c;