generic file handler support :)

git-svn-id: file:///home/shish/svn/shimmie2/trunk@623 7f39781d-f577-437e-ae19-be835c7a54ca
This commit is contained in:
shish 2007-12-06 02:26:34 +00:00
parent 0d126084ce
commit a5a6f4781c
12 changed files with 259 additions and 315 deletions

View File

@ -0,0 +1,23 @@
<?php
/*
* DataUploadEvent:
*
* Some data is being uploaded.
*/
class DataUploadEvent extends Event {
var $user, $tmpname, $metadata, $hash, $type;
public function DataUploadEvent($user, $tmpname, $metadata) {
$this->user = $user;
$this->tmpname = $tmpname;
$this->metadata = $metadata;
$this->metadata['hash'] = md5_file($tmpname);
$this->metadata['size'] = filesize($tmpname);
// useful for most file handlers, so pull directly into fields
$this->hash = $this->metadata['hash'];
$this->type = strtolower($metadata['extension']);
}
}
?>

View File

@ -0,0 +1,16 @@
<?php
/*
* ImageAdditionEvent:
*
* An image is being added to the database
*/
class ImageAdditionEvent extends Event {
var $image;
var $user;
public function ImageAdditionEvent($user, $image) {
$this->image = $image;
$this->user = $user;
}
}
?>

View File

@ -0,0 +1,15 @@
<?php
/*
* ThumbnailGenerationEvent:
* Request a thumb be made for an image
*/
class ThumbnailGenerationEvent extends Event {
var $hash;
var $type;
public function ThumbnailGenerationEvent($hash, $type) {
$this->hash = $hash;
$this->type = $type;
}
}
?>

View File

@ -1,17 +0,0 @@
<?php
/*
* UploadingImageEvent:
* $image_id
*
* An image is being uploaded.
*/
class UploadingImageEvent extends Event {
var $image;
var $user;
public function UploadingImageEvent($user, $image) {
$this->image = $image;
$this->user = $user;
}
}
?>

View File

@ -9,13 +9,10 @@ class Image {
var $posted; var $posted;
var $source; var $source;
public function Image($a=false, $b=false, $c=array(), $d="") { public function Image($a=null) {
if($b == false) { if(!is_null($a)) {
$this->create_from_row($a); $this->create_from_row($a);
} }
else {
$this->create_from_data($a, $b, $c, $d);
}
} }
private function create_from_row($row) { private function create_from_row($row) {
@ -24,43 +21,6 @@ class Image {
} }
} }
private function mime_to_ext($mime) {
switch($mime) {
default:
case 'image/jpeg': return "jpg"; break;
case 'image/png': return "png"; break;
case 'image/gif': return "gif"; break;
}
}
private function create_from_data($tmp, $filename, $tags, $source) {
global $config;
$this->ok = false;
$info = "";
if(!file_exists($tmp)) return;
if(filesize($tmp) > $config->get_int('upload_size')) return;
if(!($info = getimagesize($tmp))) return;
$this->width = $info[0];
$this->height = $info[1];
$this->mime_type = $info['mime'];
$this->filename = str_replace("/", "_", $filename); // is this even possible?
$this->filesize = filesize($tmp);
$this->ext = $this->mime_to_ext($info['mime']);
$this->hash = md5_file($tmp);
$this->temp_filename = $tmp;
$this->tag_array = tag_explode($tags);
$this->source = $source;
$this->ok = true;
}
public function is_ok() {
return $this->ok;
}
public function get_owner() { public function get_owner() {
global $database; global $database;
return $database->get_user_by_id($this->owner_id); return $database->get_user_by_id($this->owner_id);

150
ext/handle_pixel/main.php Normal file
View File

@ -0,0 +1,150 @@
<?php
/**
* Name: Pixel File Handler
* Author: Shish <webmaster@shishnet.org>
* Description: Handle JPG, PNG, GIF, etc files
*/
class PixelFileHandler extends Extension {
var $theme;
public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("handle_pixel", "PixelFileHandlerTheme");
if(is_a($event, 'DataUploadEvent') && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
$hash = $event->hash;
$ha = substr($hash, 0, 2);
if(!copy($event->tmpname, "images/$ha/$hash")) {
$event->veto("Pixel Handler failed to move file from uploads to archive");
return;
}
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
if(is_null($image)) {
$event->veto("Pixel Handler failed to create image object from data");
return;
}
send_event(new ImageAdditionEvent($event->user, $image));
}
if(is_a($event, 'ThumbnailGenerationEvent') && $this->supported_ext($event->type)) {
$this->create_thumb($event->hash);
}
if(is_a($event, 'DisplayingImageEvent') && $this->supported_ext($event->image->ext)) {
$this->theme->display_image($event->page, $event->image);
}
}
private function supported_ext($ext) {
$exts = array("jpg", "jpeg", "gif", "png");
foreach($exts as $supported) {
if($ext == $supported) return true;
}
return false;
}
private function create_image_from_data($filename, $metadata) {
global $config;
$image = new Image();
$info = "";
if(!($info = getimagesize($filename))) return null;
$image->width = $info[0];
$image->height = $info[1];
$image->filesize = $metadata['size'];
$image->hash = $metadata['hash'];
$image->filename = $metadata['filename'];
$image->ext = $metadata['extension'];
$image->tag_array = tag_explode($metadata['tags']);
$image->source = $metadata['source'];
return $image;
}
private function check_contents($file) {
return (file_exists($file) && !is_null(getimagesize($file)));
}
private function create_thumb($hash) {
$ha = substr($hash, 0, 2);
$inname = "images/$ha/$hash";
$outname = "thumbs/$ha/$hash";
global $config;
$ok = false;
switch($config->get_string("thumb_engine")) {
default:
case 'gd':
$ok = $this->make_thumb_gd($inname, $outname);
break;
case 'convert':
$ok = $this->make_thumb_convert($inname, $outname);
break;
}
return $ok;
}
// IM thumber {{{
private function make_thumb_convert($inname, $outname) {
global $config;
$w = $config->get_int("thumb_width");
$h = $config->get_int("thumb_height");
$q = $config->get_int("thumb_quality");
$mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
// "-limit memory $mem" broken?
exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname");
return true;
}
// }}}
// GD thumber {{{
private function make_thumb_gd($inname, $outname) {
global $config;
$thumb = $this->get_thumb($inname);
return imagejpeg($thumb, $outname, $config->get_int('thumb_quality'));
}
private function get_thumb($tmpname) {
global $config;
$info = getimagesize($tmpname);
$width = $info[0];
$height = $info[1];
$memory_use = (filesize($tmpname)*2) + ($width*$height*4) + (4*1024*1024);
$memory_limit = get_memory_limit();
if($memory_use > $memory_limit) {
$w = $config->get_int('thumb_width');
$h = $config->get_int('thumb_height');
$thumb = imagecreatetruecolor($w, min($h, 64));
$white = imagecolorallocate($thumb, 255, 255, 255);
$black = imagecolorallocate($thumb, 0, 0, 0);
imagefill($thumb, 0, 0, $white);
imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black);
return $thumb;
}
else {
$image = imagecreatefromstring($this->read_file($tmpname));
$tsize = get_thumbnail_size($width, $height);
$thumb = imagecreatetruecolor($tsize[0], $tsize[1]);
imagecopyresampled(
$thumb, $image, 0, 0, 0, 0,
$tsize[0], $tsize[1], $width, $height
);
return $thumb;
}
}
// }}}
}
add_event_listener(new PixelFileHandler());
?>

View File

@ -0,0 +1,10 @@
<?php
class PixelFileHandlerTheme extends Themelet {
public function display_image($page, $image) {
$ilink = $image->get_image_link();
$html = "<img id='main_image' src='$ilink'>";
$page->add_block(new Block("Image", $html, "main", 0));
}
}
?>

View File

@ -33,7 +33,7 @@ class ImageIO extends Extension {
} }
} }
if(is_a($event, 'UploadingImageEvent')) { if(is_a($event, 'ImageAdditionEvent')) {
$error = $this->add_image($event->image); $error = $this->add_image($event->image);
if(!empty($error)) $event->veto($error); if(!empty($error)) $event->veto($error);
} }
@ -87,80 +87,6 @@ class ImageIO extends Extension {
return $data; return $data;
} }
private function make_thumb($inname, $outname) {
global $config;
$ok = false;
switch($config->get_string("thumb_engine")) {
default:
case 'gd':
$ok = $this->make_thumb_gd($inname, $outname);
break;
case 'convert':
$ok = $this->make_thumb_convert($inname, $outname);
break;
}
return $ok;
}
// IM thumber {{{
private function make_thumb_convert($inname, $outname) {
global $config;
$w = $config->get_int("thumb_width");
$h = $config->get_int("thumb_height");
$q = $config->get_int("thumb_quality");
$mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
// "-limit memory $mem" broken?
exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname");
return true;
}
// }}}
// GD thumber {{{
private function make_thumb_gd($inname, $outname) {
global $config;
$thumb = $this->get_thumb($inname);
return imagejpeg($thumb, $outname, $config->get_int('thumb_quality'));
}
private function get_thumb($tmpname) {
global $config;
$info = getimagesize($tmpname);
$width = $info[0];
$height = $info[1];
$memory_use = (filesize($tmpname)*2) + ($width*$height*4) + (4*1024*1024);
$memory_limit = get_memory_limit();
if($memory_use > $memory_limit) {
$w = $config->get_int('thumb_width');
$h = $config->get_int('thumb_height');
$thumb = imagecreatetruecolor($w, min($h, 64));
$white = imagecolorallocate($thumb, 255, 255, 255);
$black = imagecolorallocate($thumb, 0, 0, 0);
imagefill($thumb, 0, 0, $white);
imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black);
return $thumb;
}
else {
$image = imagecreatefromstring($this->read_file($tmpname));
$tsize = get_thumbnail_size($width, $height);
$thumb = imagecreatetruecolor($tsize[0], $tsize[1]);
imagecopyresampled(
$thumb, $image, 0, 0, 0, 0,
$tsize[0], $tsize[1], $width, $height
);
return $thumb;
}
}
// }}}
private function add_image($image) { private function add_image($image) {
global $page; global $page;
global $user; global $user;
@ -200,28 +126,6 @@ class ImageIO extends Extension {
$image->hash, $image->ext, $image->width, $image->height, $image->source)); $image->hash, $image->ext, $image->width, $image->height, $image->source));
$image->id = $database->db->Insert_ID(); $image->id = $database->db->Insert_ID();
/*
* If no errors: move the file from the temporary upload
* area to the main file store, create a thumbnail, and
* insert the image info into the database
*/
if(!copy($image->temp_filename, $image->get_image_filename())) {
send_event(new ImageDeletionEvent($image->id));
$error = "The image couldn't be moved from the temporary area to the
main data store -- is the web server allowed to write to '".
($image->get_image_filename())."'?";
return $error;
}
chmod($image->get_image_filename(), 0644);
if(!$this->make_thumb($image->get_image_filename(), $image->get_thumb_filename())) {
send_event(new ImageDeletionEvent($image->id));
$error="The image thumbnail couldn't be generated -- is the web
server allowed to write to '".($image->get_thumb_filename())."'?";
return $error;
}
chmod($image->get_thumb_filename(), 0644);
send_event(new TagSetEvent($image->id, $image->get_tag_array())); send_event(new TagSetEvent($image->id, $image->get_tag_array()));
return null; return null;

View File

@ -2,14 +2,17 @@
class RegenThumb extends Extension { class RegenThumb extends Extension {
var $theme; var $theme;
// event handler {{{
public function receive_event($event) { public function receive_event($event) {
if(is_null($this->theme)) $this->theme = get_theme_object("regen_thumb", "RegenThumbTheme"); if(is_null($this->theme)) $this->theme = get_theme_object("regen_thumb", "RegenThumbTheme");
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "regen_thumb")) { if(is_a($event, 'PageRequestEvent') && ($event->page_name == "regen_thumb")) {
global $user; global $user;
if($user->is_admin() && isset($_POST['program']) && isset($_POST['image_id'])) { if($user->is_admin() && isset($_POST['image_id'])) {
$this->make_thumb($_POST['program'], $_POST['image_id']); global $database;
$image = $database->get_image(int_escape($_POST['image_id']));
send_event(new ThumbnailGenerationEvent($image->hash, $image->ext));
$this->theme->display_results($event->page, $image);
} }
} }
@ -20,96 +23,6 @@ class RegenThumb extends Extension {
} }
} }
} }
// }}}
// do things {{{
// FIXME: make locations of convert / epeg config variables
private function make_thumb($program, $image_id) {
global $database;
global $config;
$i_image_id = int_escape($image_id);
$image = $database->get_image($i_image_id);
$f_image = $this->check_filename($image->get_image_filename());
$f_thumb = $this->check_filename($image->get_thumb_filename());
$w = $config->get_int('thumb_width');
$h = $config->get_int('thumb_height');
$q = $config->get_int('thumb_quality');
switch($program) {
case 'convert':
if(file_exists($f_thumb)) unlink($f_thumb);
$mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
// "-limit memory $mem" broken?
exec("convert {$f_image}[0] -geometry {$w}x{$h} -quality {$q} jpg:$f_thumb");
break;
case 'gd':
$this->make_thumb_gd($f_image, $f_thumb);
break;
default:
break;
}
global $page;
$this->theme->display_results($page, $image);
}
private function check_filename($filename) {
$filename = preg_replace("#[^a-zA-Z0-9/\._]#", "", $filename);
return $filename;
}
// }}}
// GD thumber {{{
private function read_file($fname) {
$fp = fopen($fname, "r");
if(!$fp) return false;
$data = fread($fp, filesize($fname));
fclose($fp);
return $data;
}
private function make_thumb_gd($inname, $outname) {
global $config;
$thumb = $this->get_thumb($inname);
return imagejpeg($thumb, $outname, $config->get_int('thumb_quality'));
}
private function get_thumb($tmpname) {
global $config;
$info = getimagesize($tmpname);
$width = $info[0];
$height = $info[1];
$memory_use = (filesize($tmpname)*2) + ($width*$height*4) + (4*1024*1024);
$memory_limit = get_memory_limit();
if($memory_use > $memory_limit) {
$w = $config->get_int('thumb_width');
$h = $config->get_int('thumb_height');
$thumb = imagecreatetruecolor($w, min($h, 64));
$white = imagecolorallocate($thumb, 255, 255, 255);
$black = imagecolorallocate($thumb, 0, 0, 0);
imagefill($thumb, 0, 0, $white);
imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black);
return $thumb;
}
else {
$image = imagecreatefromstring($this->read_file($tmpname));
$tsize = get_thumbnail_size($width, $height);
$thumb = imagecreatetruecolor($tsize[0], $tsize[1]);
imagecopyresampled(
$thumb, $image, 0, 0, 0, 0,
$tsize[0], $tsize[1], $width, $height
);
return $thumb;
}
}
// }}}
} }
add_event_listener(new RegenThumb()); add_event_listener(new RegenThumb());
?> ?>

View File

@ -8,11 +8,6 @@ class RegenThumbTheme extends Themelet {
$html = " $html = "
<form action='".make_link("regen_thumb")."' method='POST'> <form action='".make_link("regen_thumb")."' method='POST'>
<input type='hidden' name='image_id' value='$image_id'> <input type='hidden' name='image_id' value='$image_id'>
<select name='program'>
<option value='convert'>ImageMagick</option>
<option value='gd'>GD</option>
<!-- <option value='epeg'>EPEG (for JPEG only)</option> -->
</select>
<input type='submit' value='Regenerate'> <input type='submit' value='Regenerate'>
</form> </form>
"; ";

View File

@ -60,6 +60,13 @@ class Upload extends Extension {
), "<br>Transload: "); ), "<br>Transload: ");
$event->panel->add_block($sb); $event->panel->add_block($sb);
} }
if(is_a($event, "DataUploadEvent")) {
global $config;
if(filesize($tmp_filename) > $config->get_int('upload_size')) {
$event->veto("File too large (".filesize($tmp_filename)." &gt; ".($config->get_int('upload_size')).")");
}
}
} }
// }}} // }}}
// do things {{{ // do things {{{
@ -74,37 +81,22 @@ class Upload extends Extension {
if(empty($source)) $source = null; if(empty($source)) $source = null;
$ok = false; $ok = true;
if(!file_exists($file['tmp_name'])) { // blank file boxes cause empty uploads, no need for error message
// this happens normally with blank file boxes if(file_exists($file['tmp_name'])) {
$ok = true; global $user;
} $pathinfo = pathinfo($file['name']);
else if(filesize($file['tmp_name']) > $config->get_int('upload_size')) { $metadata['filename'] = $pathinfo['basename'];
$this->theme->display_upload_error($page, "Error with ".html_escape($file['name']), $metadata['extension'] = $pathinfo['extension'];
"File too large (".to_shorthand_int(filesize($file['tmp_name']))." &gt; ". $metadata['tags'] = $tags;
(to_shorthand_int($config->get_int('upload_size'))).")"); $metadata['source'] = $source;
} $event = new DataUploadEvent($user, $file['tmp_name'], $metadata);
else if(!($info = getimagesize($file['tmp_name']))) { send_event($event);
$this->theme->display_upload_error($page, "Error with ".html_escape($file['name']), if($event->vetoed) {
"PHP doesn't recognise this as an image file");
}
else {
$image = new Image($file['tmp_name'], $file['name'], $tags, $source);
if($image->is_ok()) {
global $user;
$event = new UploadingImageEvent($user, $image);
send_event($event);
$ok = !$event->vetoed;
if(!$ok) {
$this->theme->display_upload_error($page, "Error with ".html_escape($file['name']),
$event->veto_reason);
}
}
else {
$this->theme->display_upload_error($page, "Error with ".html_escape($file['name']), $this->theme->display_upload_error($page, "Error with ".html_escape($file['name']),
"Something is not right!"); $event->veto_reason);
$ok = false;
} }
} }
@ -115,7 +107,7 @@ class Upload extends Extension {
global $page; global $page;
global $config; global $config;
$ok = false; $ok = true;
if(empty($source)) $source = $url; if(empty($source)) $source = $url;
@ -161,32 +153,21 @@ class Upload extends Extension {
if(filesize($tmp_filename) == 0) { if(filesize($tmp_filename) == 0) {
$this->theme->display_upload_error($page, "Error with ".html_escape($filename), $this->theme->display_upload_error($page, "Error with ".html_escape($filename),
"No data found -- perhaps the site has hotlink protection?"); "No data found -- perhaps the site has hotlink protection?");
} $ok = false;
else if(filesize($tmp_filename) > $config->get_int('upload_size')) {
$this->theme->display_upload_error($page, "Error with ".html_escape($filename),
"File too large (".filesize($tmp_filename)." &gt; ".
($config->get_int('upload_size')).")");
}
else if(!($info = @getimagesize($tmp_filename))) {
$this->theme->display_upload_error($page, "Error with ".html_escape($filename),
"PHP doesn't recognise this as an image file -- perhaps the site has hotlink protection?");
} }
else { else {
$image = new Image($tmp_filename, basename($url), $tags, $source); global $user;
$pathinfo = pathinfo($file);
if($image->is_ok()) { $metadata['filename'] = $pathinfo['basename'];
global $user; $metadata['extension'] = $pathinfo['extension'];
$event = new UploadingImageEvent($user, $image); $metadata['tags'] = $tags;
send_event($event); $metadata['source'] = $source;
$ok = !$event->vetoed; $event = new DataUploadEvent($user, $tmp_filename, $metadata);
if(!$ok) { send_event($event);
$this->theme->display_upload_error($page, "Error with ".html_escape($filename), if($event->vetoed) {
$event->veto_reason); $this->theme->display_upload_error($page, "Error with ".html_escape($file['name']),
} $event->veto_reason);
} $ok = false;
else {
$this->theme->display_upload_error($page, "Error with ".html_escape($filename),
"Something is not right!");
} }
} }
@ -196,5 +177,5 @@ class Upload extends Extension {
} }
// }}} // }}}
} }
add_event_listener(new Upload()); add_event_listener(new Upload(), 40); // early, so it can veto the DataUploadEvent before any data handlers see it
?> ?>

View File

@ -8,7 +8,6 @@ class ViewTheme extends Themelet {
$page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list()));
$page->set_heading(html_escape($image->get_tag_list())); $page->set_heading(html_escape($image->get_tag_list()));
$page->add_block(new Block("Navigation", $this->build_navigation($image->id), "left", 0)); $page->add_block(new Block("Navigation", $this->build_navigation($image->id), "left", 0));
$page->add_block(new Block("Image", $this->build_image_view($image), "main", 0));
$page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10)); $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10));
$page->add_block(new Block(null, $this->build_pin($image->id), "main", 11)); $page->add_block(new Block(null, $this->build_pin($image->id), "main", 11));
} }
@ -56,11 +55,6 @@ class ViewTheme extends Themelet {
return "$h_pin<br>$h_search"; return "$h_pin<br>$h_search";
} }
protected function build_image_view($image) {
$ilink = $image->get_image_link();
return "<img id='main_image' src='$ilink'>";
}
protected function build_info($image, $editor_parts) { protected function build_info($image, $editor_parts) {
global $user; global $user;
$owner = $image->get_owner(); $owner = $image->get_owner();