Merge pull request #32 from green-ponies/master

Image Replace
This commit is contained in:
Shish Moom 2011-08-31 16:34:29 -07:00
commit f5d08f1585
20 changed files with 333 additions and 50 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ thumbs
data data
sql.log sql.log
shimmie.log shimmie.log
!lib/images
ext/admin ext/admin
ext/artists ext/artists
ext/autocomplete ext/autocomplete

View File

@ -98,6 +98,7 @@ abstract class SimpleExtension implements Extension {
public function receive_event(Event $event) { public function receive_event(Event $event) {
$name = get_class($event); $name = get_class($event);
// this is rather clever..
$name = "on".str_replace("Event", "", $name); $name = "on".str_replace("Event", "", $name);
if(method_exists($this->_child, $name)) { if(method_exists($this->_child, $name)) {
$this->_child->$name($event); $this->_child->$name($event);
@ -133,15 +134,50 @@ abstract class DataHandlerExtension implements Extension {
if(is_null($this->theme)) $this->theme = get_theme_object($this); if(is_null($this->theme)) $this->theme = get_theme_object($this);
if(($event instanceof DataUploadEvent) && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { if(($event instanceof DataUploadEvent) && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
if(!move_upload_to_archive($event)) return; if(!move_upload_to_archive($event)) return;
send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
$image = $this->create_image_from_data(warehouse_path("images", $event->hash), $event->metadata);
if(is_null($image)) { /* Check if we are replacing an image */
throw new UploadException("Data handler failed to create image object from data"); if (array_key_exists('replace',$event->metadata) && isset($event->metadata['replace']))
{
/* hax: This seems like such a dirty way to do this.. */
/* Validate things */
$image_id = int_escape($event->metadata['replace']);
/* Check to make sure the image exists. */
$existing = Image::by_id($image_id);
if(is_null($existing)) {
throw new UploadException("Image to replace does not exist!");
}
if ($existing->hash === $event->metadata['hash']) {
throw new UploadException("The uploaded image is the same as the one to replace.");
}
// even more hax..
$event->metadata['tags'] = $existing->get_tag_list();
$image = $this->create_image_from_data(warehouse_path("images", $event->metadata['hash']), $event->metadata);
if(is_null($image)) {
throw new UploadException("Data handler failed to create image object from data");
}
$ire = new ImageReplaceEvent($image_id, $image);
send_event($ire);
$event->image_id = $image_id;
}
else
{
$image = $this->create_image_from_data(warehouse_path("images", $event->hash), $event->metadata);
if(is_null($image)) {
throw new UploadException("Data handler failed to create image object from data");
}
$iae = new ImageAdditionEvent($event->user, $image);
send_event($iae);
$event->image_id = $iae->image->id;
} }
$iae = new ImageAdditionEvent($event->user, $image);
send_event($iae);
$event->image_id = $iae->image->id;
} }
if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) { if(($event instanceof ThumbnailGenerationEvent) && $this->supported_ext($event->type)) {

View File

@ -457,6 +457,16 @@ class Image {
unlink($this->get_thumb_filename()); unlink($this->get_thumb_filename());
} }
/**
* This function removes an image (and thumbnail) from the DISK ONLY.
* It DOES NOT remove anything from the database.
*/
public function remove_image_only() {
log_info("core-image", "Removed Image File ({$this->hash})");
unlink($this->get_image_filename());
unlink($this->get_thumb_filename());
}
/** /**
* Someone please explain this * Someone please explain this
* *

View File

@ -46,6 +46,32 @@ class ImageDeletionEvent extends Event {
} }
} }
/*
* ImageReplaceEvent:
* $id -- the ID of the image to replace
* $image -- the image object of the new image to use
*
* This function replaces an image. Effectively it only
* replaces the image file contents and leaves the tags
* and such the same.
*/
class ImageReplaceEvent extends Event {
var $id, $image;
public function ImageReplaceEvent($id, Image $image) {
$this->id = $id;
$this->image = $image;
}
}
class ImageReplaceException extends SCoreException {
var $error;
public function __construct($error) {
$this->error = $error;
}
}
/* /*
* ThumbnailGenerationEvent: * ThumbnailGenerationEvent:
@ -129,6 +155,19 @@ class ImageIO extends SimpleExtension {
} }
} }
} }
if($event->page_matches("image_admin/replace")) {
global $page, $user;
if($user->is_admin() && isset($_POST['image_id']) && $user->check_auth_token()) {
$image = Image::by_id($_POST['image_id']);
if($image) {
$page->set_mode("redirect");
$page->set_redirect(make_link('upload/replace/'.$image->id));
} else {
/* Invalid image ID */
throw new ImageReplaceException("Image to replace does not exist.");
}
}
}
} }
public function onImageAdminBlockBuilding($event) { public function onImageAdminBlockBuilding($event) {
@ -151,6 +190,15 @@ class ImageIO extends SimpleExtension {
$event->image->delete(); $event->image->delete();
} }
public function onImageReplace($event) {
try {
$this->replace_image($event->id, $event->image);
}
catch(ImageReplaceException $e) {
throw new UploadException($e->error);
}
}
public function onUserPageBuilding($event) { public function onUserPageBuilding($event) {
$u_id = url_escape($event->display_user->id); $u_id = url_escape($event->display_user->id);
$i_image_count = Image::count_images(array("user_id={$event->display_user->id}")); $i_image_count = Image::count_images(array("user_id={$event->display_user->id}"));
@ -266,7 +314,8 @@ class ImageIO extends SimpleExtension {
$image->tag_array = array(); $image->tag_array = array();
send_event(new TagSetEvent($image, $tags_to_set)); send_event(new TagSetEvent($image, $tags_to_set));
} }
// }}} // }}} end add
// fetch image {{{ // fetch image {{{
private function send_file($image_id, $type) { private function send_file($image_id, $type) {
global $config; global $config;
@ -312,6 +361,58 @@ class ImageIO extends SimpleExtension {
"The requested image was not found in the database")); "The requested image was not found in the database"));
} }
} }
// }}} // }}} end fetch
}
// replace image {{{
private function replace_image($id, $image) {
global $page;
global $user;
global $database;
global $config;
/* Check to make sure the image exists. */
$existing = Image::by_id($id);
if(is_null($existing)) {
throw new ImageReplaceException("Image to replace does not exist!");
}
if(strlen(trim($image->source)) == 0) {
$image->source = $existing->get_source();
}
if(!empty($image->source)) {
if(!preg_match("#^(https?|ftp)://#", $image->source)) {
throw new ImageReplaceException("Image's source isn't a valid URL");
}
}
/*
This step could be optional, ie: perhaps move the image somewhere
and have it stored in a 'replaced images' list that could be
inspected later by an admin?
*/
log_debug("image", "Removing image with hash ".$existing->hash);
$existing->remove_image_only(); // Actually delete the old image file from disk
// Update the data in the database.
$database->Execute(
"UPDATE images SET
filename = :filename, filesize = :filesize, hash = :hash,
ext = :ext, width = :width, height = :height, source = :source
WHERE
id = :id
",
array(
"filename"=>$image_new->filename, "filesize"=>$image->filesize, "hash"=>$image->hash,
"ext"=>$image->ext, "width"=>$image->width, "height"=>$image->height, "source"=>$image->source,
"id"=>$id
)
);
log_info("image", "Replaced Image #{$id} with ({$image->hash})");
}
// }}} end replace
} // end of class ImageIO
?> ?>

View File

@ -27,6 +27,16 @@ class ImageIOTheme {
</form> </form>
"; ";
} }
if($config->get_bool("upload_replace") && $user->is_admin()) {
$html .= "
".make_form(make_link("image_admin/replace"))."
<input type='hidden' name='image_id' value='$i_image_id' />
<input type='submit' value='Replace' />
</form>
";
}
return $html; return $html;
} }
} }

View File

@ -54,6 +54,7 @@ class Upload implements Extension {
$config->set_default_int('upload_count', 3); $config->set_default_int('upload_count', 3);
$config->set_default_int('upload_size', '1MB'); $config->set_default_int('upload_size', '1MB');
$config->set_default_bool('upload_anon', false); $config->set_default_bool('upload_anon', false);
$config->set_default_bool('upload_replace', true);
} }
if($event instanceof PostListBuildingEvent) { if($event instanceof PostListBuildingEvent) {
@ -67,47 +68,113 @@ class Upload implements Extension {
} }
} }
if(($event instanceof PageRequestEvent) && $event->page_matches("upload")) { if($event instanceof PageRequestEvent) {
if(count($_FILES) + count($_POST) > 0) {
$tags = Tag::explode($_POST['tags']); if ($event->page_matches("upload/replace"))
$source = isset($_POST['source']) ? $_POST['source'] : null; {
if($this->can_upload($user)) { /* Upload & Replace Image Request */
$ok = true;
foreach($_FILES as $file) { if (!$config->get_bool("upload_replace")) {
$ok = $ok & $this->try_upload($file, $tags, $source); throw new UploadException("Upload Replacing Images is not enabled.");
}
// check if the user is an administrator and can upload files.
if (!$user->is_admin() && !$this->can_upload($user)) {
$this->theme->display_permission_denied($page);
}
else
{
if($is_full) {
throw new UploadException("Can not replace Image: disk nearly full");
} }
foreach($_POST as $name => $value) { // Try to get the image ID
if(substr($name, 0, 3) == "url" && strlen($value) > 0) { $image_id = int_escape($event->get_arg(0));
$ok = $ok & $this->try_transload($value, $tags, $source); if (empty($image_id)) {
$image_id = isset($_POST['image_id']) ? $_POST['image_id'] : null;
}
if (empty($image_id)) {
throw new UploadException("Can not replace Image: No valid Image ID given.");
}
$image_old = Image::by_id($image_id);
if(is_null($image_old)) {
$this->theme->display_error($page, "Image not found", "No image in the database has the ID #$image_id");
}
if(count($_FILES) + count($_POST) > 0)
{
if (count($_FILES) > 1) {
throw new UploadException("Can not upload more than one image for replacing.");
}
if (count($_FILES)) {
foreach($_FILES as $file) {
$ok = $this->try_upload($file, $tags, $source, $image_id);
break; // leave the foreach loop.
}
} else {
foreach($_POST as $name => $value) {
if(substr($name, 0, 3) == "url" && strlen($value) > 0) {
$ok = $this->try_transload($value, $tags, $source, $image_id);
break; // leave the foreach loop.
}
}
}
$this->theme->display_upload_status($page, $ok);
}
else if(!empty($_GET['url']))
{
$url = $_GET['url'];
$ok = $this->try_transload($url, $tags, $url, $image_id);
$this->theme->display_upload_status($page, $ok);
}
else
{
$this->theme->display_replace_page($page, $image_id);
}
} // END of if admin / can_upload
}
else if ($event->page_matches("upload"))
{
if(!$this->can_upload($user)) {
$this->theme->display_permission_denied($page);
} else {
/* Regular Upload Image */
if(count($_FILES) + count($_POST) > 0)
{
$tags = Tag::explode($_POST['tags']);
$source = isset($_POST['source']) ? $_POST['source'] : null;
$ok = true;
foreach($_FILES as $file) {
$ok = $ok & $this->try_upload($file, $tags, $source);
}
foreach($_POST as $name => $value) {
if(substr($name, 0, 3) == "url" && strlen($value) > 0) {
$ok = $ok & $this->try_transload($value, $tags, $source);
}
}
$this->theme->display_upload_status($page, $ok);
}
else if(!empty($_GET['url']))
{
$url = $_GET['url'];
$tags = array('tagme');
if(!empty($_GET['tags']) && $_GET['tags'] != "null") {
$tags = Tag::explode($_GET['tags']);
}
$ok = $this->try_transload($url, $tags, $url);
$this->theme->display_upload_status($page, $ok);
}
else
{
if(!$is_full) {
$this->theme->display_page($page);
} }
} }
} // END of if can_upload
$this->theme->display_upload_status($page, $ok);
}
else {
$this->theme->display_permission_denied($page);
}
} }
else if(!empty($_GET['url'])) { } // END of if PageRequestEvent
if($this->can_upload($user)) {
$url = $_GET['url'];
$tags = array('tagme');
if(!empty($_GET['tags']) && $_GET['tags'] != "null") {
$tags = Tag::explode($_GET['tags']);
}
$ok = $this->try_transload($url, $tags, $url);
$this->theme->display_upload_status($page, $ok);
}
else {
$this->theme->display_permission_denied($page);
}
}
else {
if(!$is_full) {
$this->theme->display_page($page);
}
}
}
if($event instanceof SetupBuildingEvent) { if($event instanceof SetupBuildingEvent) {
$tes = array(); $tes = array();
@ -126,6 +193,7 @@ class Upload implements Extension {
$sb->add_label("<br/><i>PHP's Max Size Upload = ".ini_get('upload_max_filesize')."</i><br/>"); $sb->add_label("<br/><i>PHP's Max Size Upload = ".ini_get('upload_max_filesize')."</i><br/>");
$sb->add_shorthand_int_option("upload_size", "<br/>Max size per file: "); $sb->add_shorthand_int_option("upload_size", "<br/>Max size per file: ");
$sb->add_bool_option("upload_anon", "<br/>Allow anonymous uploads: "); $sb->add_bool_option("upload_anon", "<br/>Allow anonymous uploads: ");
$sb->add_bool_option("upload_replace", "<br/>Allow replacing images: ");
$sb->add_choice_option("transload_engine", $tes, "<br/>Transload: "); $sb->add_choice_option("transload_engine", $tes, "<br/>Transload: ");
$event->panel->add_block($sb); $event->panel->add_block($sb);
} }
@ -172,7 +240,7 @@ class Upload implements Extension {
} }
} }
private function try_upload($file, $tags, $source) { private function try_upload($file, $tags, $source, $replace='') {
global $page; global $page;
global $config; global $config;
global $user; global $user;
@ -188,15 +256,19 @@ class Upload implements Extension {
if ($file['error'] !== UPLOAD_ERR_OK) { if ($file['error'] !== UPLOAD_ERR_OK) {
throw new UploadException($this->upload_error_message($file['error'])); throw new UploadException($this->upload_error_message($file['error']));
} }
$pathinfo = pathinfo($file['name']); $pathinfo = pathinfo($file['name']);
$metadata['filename'] = $pathinfo['basename']; $metadata['filename'] = $pathinfo['basename'];
$metadata['extension'] = $pathinfo['extension']; $metadata['extension'] = $pathinfo['extension'];
$metadata['tags'] = $tags; $metadata['tags'] = $tags;
$metadata['source'] = $source; $metadata['source'] = $source;
/* check if we have been given an image ID to replace */
if (!empty($replace)) {
$metadata['replace'] = $replace;
}
$event = new DataUploadEvent($user, $file['tmp_name'], $metadata); $event = new DataUploadEvent($user, $file['tmp_name'], $metadata);
send_event($event); send_event($event);
if($event->image_id == -1) { if($event->image_id == -1) {
throw new UploadException("File type not recognised"); throw new UploadException("File type not recognised");
@ -213,7 +285,7 @@ class Upload implements Extension {
return $ok; return $ok;
} }
private function try_transload($url, $tags, $source) { private function try_transload($url, $tags, $source, $replace='') {
global $page; global $page;
global $config; global $config;
@ -279,6 +351,12 @@ class Upload implements Extension {
$metadata['extension'] = $pathinfo['extension']; $metadata['extension'] = $pathinfo['extension'];
$metadata['tags'] = $tags; $metadata['tags'] = $tags;
$metadata['source'] = $source; $metadata['source'] = $source;
/* check if we have been given an image ID to replace */
if (!empty($replace)) {
$metadata['replace'] = $replace;
}
$event = new DataUploadEvent($user, $tmp_filename, $metadata); $event = new DataUploadEvent($user, $tmp_filename, $metadata);
try { try {
send_event($event); send_event($event);

View File

@ -75,6 +75,53 @@ class UploadTheme extends Themelet {
$page->add_block(new Block("Upload", $html, "main", 20)); $page->add_block(new Block("Upload", $html, "main", 20));
} }
/* only allows 1 file to be uploaded - for replacing another image file */
public function display_replace_page(Page $page, $image_id) {
global $config;
$tl_enabled = ($config->get_string("transload_engine", "none") != "none");
$upload_list = '';
$width = $tl_enabled ? "35%" : "80%";
$upload_list .= "
<tr>
<td width='50'>File</td>
<td width='250'><input id='data0' name='data0' type='file'></td>
";
if($tl_enabled) {
$upload_list .= "
<td width='50'>URL</td>
<td width='250'><input id='url0' name='url0' type='text'></td>
";
}
$upload_list .= "
</tr>
";
$max_size = $config->get_int('upload_size');
$max_kb = to_shorthand_int($max_size);
$image = Image::by_id($image_id);
$thumbnail = $this->build_thumb_html($image, null);
$html = "<p>Replacing Image ID ".$image_id."<br>Please note: You will have to refresh the image page, or empty your browser cache.</p>"
.$thumbnail."<br>"
.make_form(make_link("upload/replace/".$image_id), "POST", $multipart=True)."
<input type='hidden' name='image_id' value='$image_id'>
<table id='large_upload_form'>
$upload_list
<tr><td>Source</td><td colspan='3'><input name='source' type='text'></td></tr>
<tr><td colspan='4'><input id='uploadbutton' type='submit' value='Post'></td></tr>
</table>
</form>
<small>(Max file size is $max_kb)</small>
";
$page->set_title("Replace Image");
$page->set_heading("Replace Image");
$page->add_block(new NavBlock());
$page->add_block(new Block("Upload Replacement Image", $html, "main", 20));
}
public function display_upload_status(Page $page, $ok) { public function display_upload_status(Page $page, $ok) {
if($ok) { if($ok) {
$page->set_mode("redirect"); $page->set_mode("redirect");

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB