Merge pull request #33 from green-ponies/master

Incoming Code!
This commit is contained in:
Shish Moom 2011-09-09 13:56:37 -07:00
commit d61b7b7712
31 changed files with 520 additions and 136 deletions

View file

@ -28,7 +28,7 @@ class BrowserSearch implements Extension {
global $config; global $config;
$search_title = $config->get_string('title'); $search_title = $config->get_string('title');
$search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml'); $search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml');
$page->add_header("<link rel='search' type='application/opensearchdescription+xml' title='$search_title' href='$search_file_url'>"); $page->add_html_header("<link rel='search' type='application/opensearchdescription+xml' title='$search_title' href='$search_file_url'>");
} }
// The search.xml file that is generated on the fly // The search.xml file that is generated on the fly

View file

@ -159,8 +159,8 @@ class DanbooruApi implements Extension
{ {
$fp = fopen($url, "r"); $fp = fopen($url, "r");
if(!$fp) { if(!$fp) {
header("HTTP/1.0 409 Conflict"); $page->add_http_header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: fopen read error"); $page->add_http_header("X-Danbooru-Errors: fopen read error");
} }
$data = ""; $data = "";
@ -193,8 +193,8 @@ class DanbooruApi implements Extension
$filename = basename($url); $filename = basename($url);
} else } else
{ // Nothing was specified at all { // Nothing was specified at all
header("HTTP/1.0 409 Conflict"); $page->add_http_header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: no input files"); $page->add_http_header("X-Danbooru-Errors: no input files");
return; return;
} }
@ -206,8 +206,8 @@ class DanbooruApi implements Extension
{ {
if(strtolower($_REQUEST['md5']) != $hash) if(strtolower($_REQUEST['md5']) != $hash)
{ {
header("HTTP/1.0 409 Conflict"); $page->add_http_header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: md5 mismatch"); $page->add_http_header("X-Danbooru-Errors: md5 mismatch");
return; return;
} }
} }
@ -217,11 +217,11 @@ class DanbooruApi implements Extension
// Does it exist already? // Does it exist already?
$existing = Image::by_hash($hash); $existing = Image::by_hash($hash);
if(!is_null($existing)) { if(!is_null($existing)) {
header("HTTP/1.0 409 Conflict"); $page->add_http_header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: duplicate"); $page->add_http_header("X-Danbooru-Errors: duplicate");
$existinglink = make_link("post/view/" . $existing->id); $existinglink = make_link("post/view/" . $existing->id);
if($danboorup_kludge) $existinglink=make_http($existinglink); if($danboorup_kludge) $existinglink=make_http($existinglink);
header("X-Danbooru-Location: $existinglink"); $page->add_http_header("X-Danbooru-Location: $existinglink");
return; // wut! return; // wut!
} }
@ -246,21 +246,21 @@ class DanbooruApi implements Extension
// Did we POST or GET this call? // Did we POST or GET this call?
if($_SERVER['REQUEST_METHOD'] == 'POST') if($_SERVER['REQUEST_METHOD'] == 'POST')
{ {
header("X-Danbooru-Location: $newid"); $page->add_http_header("X-Danbooru-Location: $newid");
} }
else else
header("Location: $newid"); $page->add_http_header("Location: $newid");
} }
catch(UploadException $ex) { catch(UploadException $ex) {
// Did something screw up? // Did something screw up?
header("HTTP/1.0 409 Conflict"); $page->add_http_header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: exception - " . $ex->getMessage()); $page->add_http_header("X-Danbooru-Errors: exception - " . $ex->getMessage());
return; return;
} }
} else } else
{ {
header("HTTP/1.0 409 Conflict"); $page->add_http_header("HTTP/1.0 409 Conflict");
header("X-Danbooru-Errors: authentication error"); $page->add_http_header("X-Danbooru-Errors: authentication error");
return; return;
} }
} }
@ -387,7 +387,7 @@ class DanbooruApi implements Extension
if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show'))
{ {
$fixedlocation = make_link("post/view/" . $event->get_arg(3)); $fixedlocation = make_link("post/view/" . $event->get_arg(3));
header("Location: $fixedlocation"); $page->add_http_header("Location: $fixedlocation");
} }
} }

View file

@ -19,7 +19,7 @@ class RegenThumbTheme extends Themelet {
public function display_results(Page $page, Image $image) { public function display_results(Page $page, Image $image) {
$page->set_title("Thumbnail Regenerated"); $page->set_title("Thumbnail Regenerated");
$page->set_heading("Thumbnail Regenerated"); $page->set_heading("Thumbnail Regenerated");
$page->add_header("<meta http-equiv=\"cache-control\" content=\"no-cache\">"); $page->add_html_header("<meta http-equiv=\"cache-control\" content=\"no-cache\">");
$page->add_block(new NavBlock()); $page->add_block(new NavBlock());
$page->add_block(new Block("Thumbnail", $this->build_thumb_html($image))); $page->add_block(new Block("Thumbnail", $this->build_thumb_html($image)));
} }

247
contrib/resize/main.php Normal file
View file

@ -0,0 +1,247 @@
<?php
/*
* Name: Resize Image
* Author: jgen <jgen.tech@gmail.com>
* Description: Allows admins to resize images.
* License: GPLv2
* Version: 0.1
* Notice:
* The image resize and resample code is based off of the "smart_resize_image"
* function copyright 2008 Maxim Chernyak, released under a MIT-style license.
* Documentation:
* This extension allows admins to resize images.
*/
/**
* This class is just a wrapper around SCoreException.
*/
class ImageResizeException extends SCoreException {
var $error;
public function __construct($error) {
$this->error = $error;
}
}
/**
* This class handles image resize requests.
*/
class ResizeImage extends SimpleExtension {
public function onInitExt($event) {
global $config;
$config->set_default_bool('resize_enabled', true);
$config->set_default_int('resize_default_width', 0);
$config->set_default_int('resize_default_height', 0);
}
public function onImageAdminBlockBuilding($event) {
global $user, $config;
if($user->is_admin() && $config->get_bool("resize_enabled")) {
/* Add a link to resize the image */
$event->add_part($this->theme->get_resize_html($event->image->id));
}
}
public function onSetupBuilding($event) {
$sb = new SetupBlock("Image Resize");
$sb->add_bool_option("resize_enabled", "Allow resizing images: ");
$sb->add_label("<br>Preset/Default Width: ");
$sb->add_int_option("resize_default_width");
$sb->add_label(" px");
$sb->add_label("<br>Preset/Default Height: ");
$sb->add_int_option("resize_default_height");
$sb->add_label(" px");
$sb->add_label("<br>(enter 0 for no default)");
$event->panel->add_block($sb);
}
public function onPageRequest($event) {
global $page, $user;
if ( $event->page_matches("resize") && $user->is_admin() ) {
// Try to get the image ID
$image_id = int_escape($event->get_arg(0));
if (empty($image_id)) {
$image_id = isset($_POST['image_id']) ? $_POST['image_id'] : null;
}
if (empty($image_id)) {
throw new ImageResizeException("Can not resize Image: No valid Image ID given.");
}
$image = Image::by_id($image_id);
if(is_null($image)) {
$this->theme->display_error($page, "Image not found", "No image in the database has the ID #$image_id");
} else {
/* Check if options were given to resize an image. */
if (isset($_POST['resize_width']) || isset($_POST['resize_height'])) {
/* get options */
$width = $height = 0;
if (isset($_POST['resize_width'])) {
$width = int_escape($_POST['resize_width']);
}
if (isset($_POST['resize_height'])) {
$height = int_escape($_POST['resize_height']);
}
/* Attempt to resize the image */
try {
$this->resize_image($image_id, $width, $height);
//$this->theme->display_resize_page($page, $image_id);
$page->set_mode("redirect");
$page->set_redirect(make_link("post/view/".$image_id));
} catch (ImageResizeException $e) {
$this->theme->display_resize_error($page, "Error Resizing", $e->error);
}
} else {
/* Display options for resizing */
$this->theme->display_resize_page($page, $image_id);
}
}
}
}
// Private functions
/*
This function could be made much smaller by using the ImageReplaceEvent
ie: Pretend that we are replacing the image with a resized copy.
*/
private function resize_image($image_id, $width, $height) {
global $config;
global $user;
global $page;
global $database;
if ( ($height <= 0) && ($width <= 0) ) {
throw new ImageResizeException("Invalid options for height and width. ($width x $height)");
}
$image_obj = Image::by_id($image_id);
$hash = $image_obj->hash;
if (is_null($hash)) {
throw new ImageResizeException("Image does not have a hash associated with it.");
}
$image_filename = warehouse_path("images", $hash);
$info = getimagesize($image_filename);
/* Get the image file type */
$pathinfo = pathinfo($image_obj->filename);
$filetype = strtolower($pathinfo['extension']);
if (($image_obj->width != $info[0] ) || ($image_obj->height != $info[1])) {
throw new ImageResizeException("The image size does not match what is in the database! - Aborting Resize.");
}
/* Check memory usage limits */
$memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024);
$memory_limit = get_memory_limit();
if ($memory_use > $memory_limit) {
throw new ImageResizeException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)");
}
/* Calculate the new size of the image */
if ( $height > 0 && $width > 0 ) {
$new_height = $height;
$new_width = $width;
} else {
// Scale the new image
if ($width == 0) $factor = $height/$image_obj->height;
elseif ($height == 0) $factor = $width/$image_obj->width;
else $factor = min( $width / $image_obj->width, $height / $image_obj->height );
$new_width = round( $image_obj->width * $factor );
$new_height = round( $image_obj->height * $factor );
}
/* Attempt to load the image */
switch ( $info[2] ) {
case IMAGETYPE_GIF: $image = imagecreatefromgif($image_filename); break;
case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($image_filename); break;
case IMAGETYPE_PNG: $image = imagecreatefrompng($image_filename); break;
default:
throw new ImageResizeException("Unsupported image type.");
}
/* Resize and resample the image */
$image_resized = imagecreatetruecolor( $new_width, $new_height );
if ( ($info[2] == IMAGETYPE_GIF) || ($info[2] == IMAGETYPE_PNG) ) {
$transparency = imagecolortransparent($image);
if ($transparency >= 0) {
$transparent_color = imagecolorsforindex($image, $trnprt_indx);
$transparency = imagecolorallocate($image_resized, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);
imagefill($image_resized, 0, 0, $transparency);
imagecolortransparent($image_resized, $transparency);
}
elseif ($info[2] == IMAGETYPE_PNG) {
imagealphablending($image_resized, false);
$color = imagecolorallocatealpha($image_resized, 0, 0, 0, 127);
imagefill($image_resized, 0, 0, $color);
imagesavealpha($image_resized, true);
}
}
imagecopyresampled($image_resized, $image, 0, 0, 0, 0, $new_width, $new_height, $image_obj->width, $image_obj->height);
/* Temp storage while we resize */
$tmp_filename = tempnam("/tmp", 'shimmie_resize');
if (empty($tmp_filename)) {
throw new ImageResizeException("Unable to save temporary image file.");
}
/* Output to the same format as the original image */
switch ( $info[2] ) {
case IMAGETYPE_GIF: imagegif($image_resized, $tmp_filename); break;
case IMAGETYPE_JPEG: imagejpeg($image_resized, $tmp_filename); break;
case IMAGETYPE_PNG: imagepng($image_resized, $tmp_filename); break;
default:
throw new ImageResizeException("Unsupported image type.");
}
/* Move the new image into the main storage location */
$new_hash = md5_file($tmp_filename);
$new_size = filesize($tmp_filename);
$target = warehouse_path("images", $new_hash);
if(!file_exists(dirname($target))) mkdir(dirname($target), 0755, true);
if(!@copy($tmp_filename, $target)) {
throw new ImageResizeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)");
}
$new_filename = 'resized-'.$image_obj->filename;
/* Remove temporary file */
@unlink($tmp_filename);
/* Delete original image and thumbnail */
log_debug("image", "Removing image with hash ".$hash);
$image_obj->remove_image_only();
/* Generate new thumbnail */
send_event(new ThumbnailGenerationEvent($new_hash, $filetype));
/* Update the database */
$database->Execute(
"UPDATE images SET
filename = :filename, filesize = :filesize, hash = :hash, width = :width, height = :height
WHERE
id = :id
",
array(
"filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash,
"width"=>$new_width, "height"=>$new_height, "id"=>$image_id
)
);
log_info("resize", "Resized Image #{$image_id} - New hash: {$new_hash}");
}
}
?>

5
contrib/resize/style.css Normal file
View file

@ -0,0 +1,5 @@
form#form_resize {
float: left;
margin: 0;
width: 60%;
}

60
contrib/resize/theme.php Normal file
View file

@ -0,0 +1,60 @@
<?php
class ResizeImageTheme extends Themelet {
/*
* Display a link to resize an image
*/
public function get_resize_html($image_id) {
global $user;
global $config;
$i_image_id = int_escape($image_id);
$html = "
".make_form(make_link("resize"),'POST',false,'resize_image')."
<input type='hidden' name='image_id' value='$i_image_id' />
<input type='submit' value='Resize' id='resize_image_submit' />
</form>
";
return $html;
}
public function display_resize_error(Page $page, $title, $message) {
$page->set_title("Resize Image");
$page->set_heading("Resize Image");
$page->add_block(new NavBlock());
$page->add_block(new Block($title, $message));
}
public function display_resize_page(Page $page, $image_id) {
global $config;
$default_width = $config->get_int('resize_default_width');
$default_height = $config->get_int('resize_default_height');
$image = Image::by_id($image_id);
$thumbnail = $this->build_thumb_html($image, null);
$html = "<div style='clear:both;'></div>
<p>Resize Image ID ".$image_id."<br>".$thumbnail."</p>
<p>Please note: You will have to refresh the image page, or empty your browser cache.</p>
<p>Enter the new size for the image, or leave blank to scale the image automatically.</p><br>"
.make_form(make_link('resize/'.$image_id), 'POST', $multipart=True,'form_resize')."
<input type='hidden' name='image_id' value='$image_id'>
<table id='large_upload_form'>
<tr><td>New Width</td><td colspan='3'><input id='resize_width' name='resize_width' type='text' value='".$default_width."'></td></tr>
<tr><td>New Height</td><td colspan='3'><input id='resize_height' name='resize_height' type='text' value='".$default_height."'></td></tr>
<tr><td colspan='4'><input id='resizebutton' type='submit' value='Resize'></td></tr>
</table>
</form>
";
$page->set_title("Resize Image");
$page->set_heading("Resize Image");
$page->add_block(new NavBlock());
$page->add_block(new Block("Resize Image", $html, "main", 20));
}
}
?>

View file

@ -11,7 +11,7 @@ class RSS_Comments extends SimpleExtension {
global $config, $page; global $config, $page;
$title = $config->get_string('title'); $title = $config->get_string('title');
$page->add_header("<link rel=\"alternate\" type=\"application/rss+xml\" ". $page->add_html_header("<link rel=\"alternate\" type=\"application/rss+xml\" ".
"title=\"$title - Comments\" href=\"".make_link("rss/comments")."\" />"); "title=\"$title - Comments\" href=\"".make_link("rss/comments")."\" />");
} }

View file

@ -13,11 +13,11 @@ class RSS_Images extends SimpleExtension {
if(count($event->search_terms) > 0) { if(count($event->search_terms) > 0) {
$search = html_escape(implode(' ', $event->search_terms)); $search = html_escape(implode(' ', $event->search_terms));
$page->add_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ". $page->add_html_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ".
"title=\"$title - Images with tags: $search\" href=\"".make_link("rss/images/$search/1")."\" />"); "title=\"$title - Images with tags: $search\" href=\"".make_link("rss/images/$search/1")."\" />");
} }
else { else {
$page->add_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ". $page->add_html_header("<link id=\"images\" rel=\"alternate\" type=\"application/rss+xml\" ".
"title=\"$title - Images\" href=\"".make_link("rss/images/1")."\" />"); "title=\"$title - Images\" href=\"".make_link("rss/images/1")."\" />");
} }
} }

View file

@ -14,11 +14,11 @@ class SiteDescription extends SimpleExtension {
global $config, $page; global $config, $page;
if(strlen($config->get_string("site_description")) > 0) { if(strlen($config->get_string("site_description")) > 0) {
$description = $config->get_string("site_description"); $description = $config->get_string("site_description");
$page->add_header("<meta name=\"description\" content=\"$description\">"); $page->add_html_header("<meta name=\"description\" content=\"$description\">");
} }
if(strlen($config->get_string("site_keywords")) > 0) { if(strlen($config->get_string("site_keywords")) > 0) {
$keywords = $config->get_string("site_keywords"); $keywords = $config->get_string("site_keywords");
$page->add_header("<meta name=\"keywords\" content=\"$keywords\">"); $page->add_html_header("<meta name=\"keywords\" content=\"$keywords\">");
} }
} }

View file

@ -11,7 +11,7 @@ class taggerTheme extends Themelet {
// Initialization code // Initialization code
$base_href = $config->get_string('base_href'); $base_href = $config->get_string('base_href');
// TODO: AJAX test and fallback. // TODO: AJAX test and fallback.
$page->add_header("<script src='$base_href/ext/tagger/webtoolkit.drag.js' type='text/javascript'></script>"); $page->add_html_header("<script src='$base_href/ext/tagger/webtoolkit.drag.js' type='text/javascript'></script>");
$page->add_block(new Block(null, $page->add_block(new Block(null,
"<script type='text/javascript'>Tagger.initialize(" "<script type='text/javascript'>Tagger.initialize("
.$event->get_image()->id.");</script>","main",1000)); .$event->get_image()->id.");</script>","main",1000));

View file

@ -463,8 +463,8 @@ class Image {
*/ */
public function remove_image_only() { public function remove_image_only() {
log_info("core-image", "Removed Image File ({$this->hash})"); log_info("core-image", "Removed Image File ({$this->hash})");
unlink($this->get_image_filename()); @unlink($this->get_image_filename());
unlink($this->get_thumb_filename()); @unlink($this->get_thumb_filename());
} }
/** /**

View file

@ -108,7 +108,8 @@ class Page {
var $heading = ""; var $heading = "";
var $subheading = ""; var $subheading = "";
var $quicknav = ""; var $quicknav = "";
var $headers = array(); var $html_headers = array();
var $http_headers = array();
var $blocks = array(); var $blocks = array();
/** @publicsection */ /** @publicsection */
@ -136,9 +137,17 @@ class Page {
/** /**
* Add a line to the HTML head section * Add a line to the HTML head section
*/ */
public function add_header($line, $position=50) { public function add_html_header($line, $position=50) {
while(isset($this->headers[$position])) $position++; while(isset($this->html_headers[$position])) $position++;
$this->headers[$position] = $line; $this->html_headers[$position] = $line;
}
/**
* Add a http header to be sent to the client.
*/
public function add_http_header($line, $position=50) {
while(isset($this->http_headers[$position])) $position++;
$this->http_headers[$position] = $line;
} }
/** /**
@ -158,14 +167,20 @@ class Page {
public function display() { public function display() {
global $page; global $page;
header("Content-type: {$this->type}"); $this->add_http_header("Content-type: {$this->type}", 1);
header("X-Powered-By: SCore-".SCORE_VERSION); $this->add_http_header("X-Powered-By: SCore-".SCORE_VERSION, 2);
if (!headers_sent()) {
foreach($this->http_headers as $head){ header($head); }
} else {
print "Error: Headers have already been sent to the client.";
}
switch($this->mode) { switch($this->mode) {
case "page": case "page":
header("Cache-control: no-cache"); header("Cache-control: no-cache");
usort($this->blocks, "blockcmp"); usort($this->blocks, "blockcmp");
$this->add_auto_headers(); $this->add_auto_html_headers();
$layout = new Layout(); $layout = new Layout();
$layout->display_page($page); $layout->display_page($page);
break; break;
@ -186,26 +201,26 @@ class Page {
} }
} }
protected function add_auto_headers() { protected function add_auto_html_headers() {
$data_href = get_base_href(); $data_href = get_base_href();
foreach(glob("lib/*.css") as $css) { foreach(glob("lib/*.css") as $css) {
$this->add_header("<link rel='stylesheet' href='$data_href/$css' type='text/css'>"); $this->add_html_header("<link rel='stylesheet' href='$data_href/$css' type='text/css'>");
} }
$css_files = glob("ext/*/style.css"); $css_files = glob("ext/*/style.css");
if($css_files) { if($css_files) {
foreach($css_files as $css_file) { foreach($css_files as $css_file) {
$this->add_header("<link rel='stylesheet' href='$data_href/$css_file' type='text/css'>"); $this->add_html_header("<link rel='stylesheet' href='$data_href/$css_file' type='text/css'>");
} }
} }
foreach(glob("lib/*.js") as $js) { foreach(glob("lib/*.js") as $js) {
$this->add_header("<script src='$data_href/$js' type='text/javascript'></script>"); $this->add_html_header("<script src='$data_href/$js' type='text/javascript'></script>");
} }
$js_files = glob("ext/*/script.js"); $js_files = glob("ext/*/script.js");
if($js_files) { if($js_files) {
foreach($js_files as $js_file) { foreach($js_files as $js_file) {
$this->add_header("<script src='$data_href/$js_file' type='text/javascript'></script>"); $this->add_html_header("<script src='$data_href/$js_file' type='text/javascript'></script>");
} }
} }
} }

View file

@ -14,8 +14,9 @@ class Handle404 extends SimpleExtension {
// hax. // hax.
if($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { if($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) {
$h_pagename = html_escape(implode('/', $event->args)); $h_pagename = html_escape(implode('/', $event->args));
header("HTTP/1.0 404 Page Not Found");
log_debug("handle_404", "Hit 404: $h_pagename"); log_debug("handle_404", "Hit 404: $h_pagename");
$page->add_http_header("HTTP/1.0 404 Page Not Found",5);
$page->set_title("404"); $page->set_title("404");
$page->set_heading("404 - No Handler Found"); $page->set_heading("404 - No Handler Found");
$page->add_block(new NavBlock()); $page->add_block(new NavBlock());

View file

@ -1,21 +1,27 @@
<?php <?php
/* /*
* Name: Image Manager * Name: Image Manager
* Author: Shish * Author: Shish <webmaster@shishnet.org>
* Modified by: jgen <jgen.tech@gmail.com>
* Description: Handle the image database * Description: Handle the image database
* Visibility: admin * Visibility: admin
*/ */
/* /**
* ImageAdditionEvent: * An image is being added to the database.
* $user -- the user adding the image
* $image -- the image being added
*
* An image is being added to the database
*/ */
class ImageAdditionEvent extends Event { class ImageAdditionEvent extends Event {
var $user, $image; var $user, $image;
/**
* Inserts a new image into the database with its associated
* information. Also calls TagSetEvent to set the tags for
* this new image.
*
* @sa TagSetEvent
* @param $user The user adding the image
* @param $image The new image to add.
*/
public function ImageAdditionEvent(User $user, Image $image) { public function ImageAdditionEvent(User $user, Image $image) {
$this->image = $image; $this->image = $image;
$this->user = $user; $this->user = $user;
@ -30,34 +36,41 @@ class ImageAdditionException extends SCoreException {
} }
} }
/* /**
* ImageDeletionEvent: * An image is being deleted.
* $image -- the image being deleted
*
* An image is being deleted. Used by things like tags
* and comments handlers to clean out related rows in
* their tables
*/ */
class ImageDeletionEvent extends Event { class ImageDeletionEvent extends Event {
var $image; var $image;
/**
* Deletes an image.
* Used by things like tags and comments handlers to
* clean out related rows in their tables.
*
* @param $image The image being deleted
*/
public function ImageDeletionEvent(Image $image) { public function ImageDeletionEvent(Image $image) {
$this->image = $image; $this->image = $image;
} }
} }
/* /**
* ImageReplaceEvent: * An image is being replaced.
* $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 { class ImageReplaceEvent extends Event {
var $id, $image; var $id, $image;
/**
* Replaces an image.
* Updates an existing ID in the database to use a new image
* file, leaving the tags and such unchanged. Also removes
* the old image file and thumbnail from the disk.
*
* @param $id
* The ID of the image to replace
* @param $image
* The image object of the new image to use
*/
public function ImageReplaceEvent($id, Image $image) { public function ImageReplaceEvent($id, Image $image) {
$this->id = $id; $this->id = $id;
$this->image = $image; $this->image = $image;
@ -72,15 +85,18 @@ class ImageReplaceException extends SCoreException {
} }
} }
/**
/* * Request a thumbnail be made for an image object.
* ThumbnailGenerationEvent:
* Request a thumb be made for an image
*/ */
class ThumbnailGenerationEvent extends Event { class ThumbnailGenerationEvent extends Event {
var $hash; var $hash, $type;
var $type;
/**
* Request a thumbnail be made for an image object
*
* @param $hash The unique hash of the image
* @param $type The type of the image
*/
public function ThumbnailGenerationEvent($hash, $type) { public function ThumbnailGenerationEvent($hash, $type) {
$this->hash = $hash; $this->hash = $hash;
$this->type = $type; $this->type = $type;
@ -95,8 +111,7 @@ class ThumbnailGenerationEvent extends Event {
* $image -- the image who's link is being parsed * $image -- the image who's link is being parsed
*/ */
class ParseLinkTemplateEvent extends Event { class ParseLinkTemplateEvent extends Event {
var $link, $original; var $link, $original, $image;
var $image;
public function ParseLinkTemplateEvent($link, Image $image) { public function ParseLinkTemplateEvent($link, Image $image) {
$this->link = $link; $this->link = $link;
@ -110,9 +125,8 @@ class ParseLinkTemplateEvent extends Event {
} }
/* /**
* A class to handle adding / getting / removing image * A class to handle adding / getting / removing image files from the disk.
* files from the disk
*/ */
class ImageIO extends SimpleExtension { class ImageIO extends SimpleExtension {
public function onInitExt($event) { public function onInitExt($event) {
@ -124,11 +138,12 @@ class ImageIO extends SimpleExtension {
$config->set_default_string('thumb_convert_path', 'convert.exe'); $config->set_default_string('thumb_convert_path', 'convert.exe');
$config->set_default_bool('image_show_meta', false); $config->set_default_bool('image_show_meta', false);
$config->set_default_bool('jquery_confirm', true); $config->set_default_bool('image_jquery_confirm', true);
$config->set_default_string('image_ilink', ''); $config->set_default_string('image_ilink', '');
$config->set_default_string('image_tlink', ''); $config->set_default_string('image_tlink', '');
$config->set_default_string('image_tip', '$tags // $size // $filesize'); $config->set_default_string('image_tip', '$tags // $size // $filesize');
$config->set_default_string('upload_collision_handler', 'error'); $config->set_default_string('upload_collision_handler', 'error');
$config->set_default_int('image_expires', (60*60*24*365) ); // defaults to one year
} }
public function onPageRequest($event) { public function onPageRequest($event) {
@ -172,9 +187,15 @@ class ImageIO extends SimpleExtension {
public function onImageAdminBlockBuilding($event) { public function onImageAdminBlockBuilding($event) {
global $user; global $user;
global $config;
if($user->is_admin()) { if($user->is_admin()) {
$event->add_part($this->theme->get_deleter_html($event->image->id)); $event->add_part($this->theme->get_deleter_html($event->image->id));
} }
/* In the future, could perhaps allow users to replace images that they own as well... */
if ($user->is_admin() && $config->get_bool("upload_replace")) {
$event->add_part($this->theme->get_replace_html($event->image->id));
}
} }
public function onImageAddition($event) { public function onImageAddition($event) {
@ -200,6 +221,9 @@ class ImageIO extends SimpleExtension {
} }
public function onUserPageBuilding($event) { public function onUserPageBuilding($event) {
global $user;
global $config;
$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}"));
$i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1;
@ -219,7 +243,17 @@ class ImageIO extends SimpleExtension {
if(!in_array("OS", $_SERVER) || $_SERVER["OS"] != 'Windows_NT') { if(!in_array("OS", $_SERVER) || $_SERVER["OS"] != 'Windows_NT') {
$sb->add_bool_option("image_show_meta", "<br>Show metadata: "); $sb->add_bool_option("image_show_meta", "<br>Show metadata: ");
} }
$sb->add_bool_option("jquery_confirm", "<br>Confirm Delete with jQuery: "); $sb->add_bool_option("image_jquery_confirm", "<br>Confirm Delete with jQuery: ");
$expires = array();
$expires['1 Minute'] = 60;
$expires['1 Hour'] = 3600;
$expires['1 Day'] = 86400;
$expires['1 Month (31 days)'] = 2678400; //(60*60*24*31)
$expires['1 Year'] = 31536000; // 365 days (60*60*24*365)
$expires['Never'] = 3153600000; // 100 years..
$sb->add_choice_option("image_expires", $expires, "<br>Image Expiration: ");
$event->panel->add_block($sb); $event->panel->add_block($sb);
$thumbers = array(); $thumbers = array();
@ -344,13 +378,18 @@ class ImageIO extends SimpleExtension {
} }
$gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT'; $gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT';
// FIXME: should be $page->blah
if($if_modified_since == $gmdate_mod) { if($if_modified_since == $gmdate_mod) {
header("HTTP/1.0 304 Not Modified"); $page->add_http_header("HTTP/1.0 304 Not Modified",3);
} }
else { else {
header("Last-Modified: $gmdate_mod"); $page->add_http_header("Last-Modified: $gmdate_mod");
header("Expires: Fri, 2 Sep 2101 12:42:42 GMT"); // War was beginning
if ( $config->get_int("image_expires") ) {
$expires = date(DATE_RFC1123, time() + $config->get_int("image_expires"));
} else {
$expires = 'Fri, 2 Sep 2101 12:42:42 GMT'; // War was beginning
}
$page->add_http_header('Expires: '.$expires);
} }
} }
else { else {
@ -403,7 +442,7 @@ class ImageIO extends SimpleExtension {
id = :id id = :id
", ",
array( array(
"filename"=>$image_new->filename, "filesize"=>$image->filesize, "hash"=>$image->hash, "filename"=>$image->filename, "filesize"=>$image->filesize, "hash"=>$image->hash,
"ext"=>$image->ext, "width"=>$image->width, "height"=>$image->height, "source"=>$image->source, "ext"=>$image->ext, "width"=>$image->width, "height"=>$image->height, "source"=>$image->source,
"id"=>$id "id"=>$id
) )

View file

@ -1,42 +1,46 @@
<?php <?php
class ImageIOTheme { class ImageIOTheme {
/* /**
* Display a link to delete an image * Display a link to delete an image
* (Added inline Javascript to confirm the deletion) * (Added inline Javascript to confirm the deletion)
* *
* $image_id = the image to delete * @param $image_id The image to delete
*/ */
public function get_deleter_html($image_id) { public function get_deleter_html($image_id) {
global $user;
global $config; global $config;
$i_image_id = int_escape($image_id); if($config->get_bool("image_jquery_confirm")) {
if($config->get_bool("jquery_confirm")) {
$html = " $html = "
".make_form(make_link("image_admin/delete"),'POST',false,'delete_image')." ".make_form(make_link("image_admin/delete"),'POST',false,'delete_image')."
<input type='hidden' name='image_id' value='$i_image_id' /> <input type='hidden' name='image_id' value='$image_id' />
<input type='submit' value='Delete' id='delete_image_submit' /> <input type='submit' value='Delete' id='delete_image_submit' />
</form> </form>
"; ";
} else { } else {
$html = " $html = "
".make_form(make_link("image_admin/delete"))." ".make_form(make_link("image_admin/delete"))."
<input type='hidden' name='image_id' value='$i_image_id' /> <input type='hidden' name='image_id' value='$image_id' />
<input type='submit' value='Delete' onclick='return confirm(\"Delete the image?\");' /> <input type='submit' value='Delete' onclick='return confirm(\"Delete the image?\");' />
</form> </form>
"; ";
} }
if($config->get_bool("upload_replace") && $user->is_admin()) { return $html;
$html .= " }
".make_form(make_link("image_admin/replace"))."
<input type='hidden' name='image_id' value='$i_image_id' /> /**
<input type='submit' value='Replace' /> * Display link to replace the image
</form> *
"; * @param $image_id The image to replace
} */
public function get_replace_html($image_id) {
$html = "
".make_form(make_link("image_admin/replace"))."
<input type='hidden' name='image_id' value='$image_id' />
<input type='submit' value='Replace' />
</form>";
return $html; return $html;
} }
} }

View file

@ -5,17 +5,19 @@
* Description: Allows people to upload files to the website * Description: Allows people to upload files to the website
*/ */
/* /**
* DataUploadEvent: * Occurs when some data is being uploaded.
* $user -- the user uploading the data
* $tmpname -- the temporary file used for upload
* $metadata -- info about the file, should contain at least "filename", "extension", "tags" and "source"
*
* Some data is being uploaded. Should be caught by a file handler.
*/ */
class DataUploadEvent extends Event { class DataUploadEvent extends Event {
var $user, $tmpname, $metadata, $hash, $type, $image_id = -1; var $user, $tmpname, $metadata, $hash, $type, $image_id = -1;
/**
* Some data is being uploaded.
* This should be caught by a file handler.
* @param $user The user uploading the data.
* @param $tmpname The temporary file used for upload.
* @param $metadata Info about the file, should contain at least "filename", "extension", "tags" and "source".
*/
public function DataUploadEvent(User $user, $tmpname, $metadata) { public function DataUploadEvent(User $user, $tmpname, $metadata) {
assert(file_exists($tmpname)); assert(file_exists($tmpname));
@ -34,6 +36,11 @@ class DataUploadEvent extends Event {
class UploadException extends SCoreException {} class UploadException extends SCoreException {}
/**
* Main upload class.
* All files that are uploaded to the site are handled through this class.
* This also includes transloaded files as well.
*/
class Upload implements Extension { class Upload implements Extension {
var $theme; var $theme;
// event handling {{{ // event handling {{{
@ -107,6 +114,9 @@ class Upload implements Extension {
throw new UploadException("Can not upload more than one image for replacing."); throw new UploadException("Can not upload more than one image for replacing.");
} }
$source = isset($_POST['source']) ? $_POST['source'] : null;
$tags = ''; // Tags aren't changed when uploading. Set to null to stop PHP warnings.
if (count($_FILES)) { if (count($_FILES)) {
foreach($_FILES as $file) { foreach($_FILES as $file) {
$ok = $this->try_upload($file, $tags, $source, $image_id); $ok = $this->try_upload($file, $tags, $source, $image_id);
@ -273,7 +283,8 @@ class Upload implements Extension {
if($event->image_id == -1) { if($event->image_id == -1) {
throw new UploadException("File type not recognised"); throw new UploadException("File type not recognised");
} }
header("X-Shimmie-Image-ID: ".int_escape($event->image_id)); //header("X-Shimmie-Image-ID: ".int_escape($event->image_id));
$page->add_http_header("X-Shimmie-Image-ID: ".int_escape($event->image_id));
} }
catch(UploadException $ex) { catch(UploadException $ex) {
$this->theme->display_upload_error($page, "Error with ".html_escape($file['name']), $this->theme->display_upload_error($page, "Error with ".html_escape($file['name']),

View file

@ -86,16 +86,16 @@ class UploadTheme extends Themelet {
<tr> <tr>
<td width='50'>File</td> <td width='50'>File</td>
<td width='250'><input id='data0' name='data0' type='file'></td> <td width='250'><input id='data0' name='data0' type='file'></td>
</tr>
"; ";
if($tl_enabled) { if($tl_enabled) {
$upload_list .= " $upload_list .= "
<tr>
<td width='50'>URL</td> <td width='50'>URL</td>
<td width='250'><input id='url0' name='url0' type='text'></td> <td width='250'><input id='url0' name='url0' type='text'></td>
</tr>
"; ";
} }
$upload_list .= "
</tr>
";
$max_size = $config->get_int('upload_size'); $max_size = $config->get_int('upload_size');
$max_kb = to_shorthand_int($max_size); $max_kb = to_shorthand_int($max_size);
@ -103,7 +103,9 @@ class UploadTheme extends Themelet {
$image = Image::by_id($image_id); $image = Image::by_id($image_id);
$thumbnail = $this->build_thumb_html($image, null); $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>" $html = "
<div style='clear:both;'></div>
<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>" .$thumbnail."<br>"
.make_form(make_link("upload/replace/".$image_id), "POST", $multipart=True)." .make_form(make_link("upload/replace/".$image_id), "POST", $multipart=True)."
<input type='hidden' name='image_id' value='$image_id'> <input type='hidden' name='image_id' value='$image_id'>

View file

@ -10,11 +10,11 @@ class ViewImageTheme extends Themelet {
$metatags = str_replace(" ", ", ", html_escape($image->get_tag_list())); $metatags = str_replace(" ", ", ", html_escape($image->get_tag_list()));
$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->add_header("<meta name=\"keywords\" content=\"$metatags\">"); $page->add_html_header("<meta name=\"keywords\" content=\"$metatags\">");
$page->add_header("<meta property=\"og:title\" content=\"$metatags\">"); $page->add_html_header("<meta property=\"og:title\" content=\"$metatags\">");
$page->add_header("<meta property=\"og:type\" content=\"article\">"); $page->add_html_header("<meta property=\"og:type\" content=\"article\">");
$page->add_header("<meta property=\"og:image\" content=\"".make_http($image->get_thumb_link())."\">"); $page->add_html_header("<meta property=\"og:image\" content=\"".make_http($image->get_thumb_link())."\">");
$page->add_header("<meta property=\"og:url\" content=\"".make_http(make_link("post/view/{$image->id}"))."\">"); $page->add_html_header("<meta property=\"og:url\" content=\"".make_http(make_link("post/view/{$image->id}"))."\">");
$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), "left", 0)); $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 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));

View file

@ -53,8 +53,8 @@ class Layout {
$header_html = ""; $header_html = "";
ksort($page->headers); ksort($page->html_headers);
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -10,7 +10,7 @@ class Themelet {
public function display_permission_denied(Page $page) { public function display_permission_denied(Page $page) {
header("HTTP/1.0 403 Permission Denied"); $page->add_http_header("HTTP/1.0 403 Permission Denied");
$this->display_error($page, "Permission Denied", "You do not have permission to access this page"); $this->display_error($page, "Permission Denied", "You do not have permission to access this page");
} }

View file

@ -14,8 +14,8 @@ class Layout {
$contact_link = $config->get_string('contact_link'); $contact_link = $config->get_string('contact_link');
$header_html = ""; $header_html = "";
ksort($page->headers); ksort($page->html_headers);
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -18,7 +18,7 @@ class Themelet {
* A specific, common error message * A specific, common error message
*/ */
public function display_permission_denied(Page $page) { public function display_permission_denied(Page $page) {
header("HTTP/1.0 403 Permission Denied"); $page->add_http_header("HTTP/1.0 403 Permission Denied");
$this->display_error($page, "Permission Denied", "You do not have permission to access this page"); $this->display_error($page, "Permission Denied", "You do not have permission to access this page");
} }

View file

@ -14,8 +14,8 @@ class Layout {
$contact_link = $config->get_string('contact_link'); $contact_link = $config->get_string('contact_link');
$header_html = ""; $header_html = "";
ksort($page->headers); ksort($page->html_headers);
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -18,7 +18,7 @@ class Themelet {
* A specific, common error message * A specific, common error message
*/ */
public function display_permission_denied(Page $page) { public function display_permission_denied(Page $page) {
header("HTTP/1.0 403 Permission Denied"); $page->add_http_header("HTTP/1.0 403 Permission Denied");
$this->display_error($page, "Permission Denied", "You do not have permission to access this page"); $this->display_error($page, "Permission Denied", "You do not have permission to access this page");
} }

View file

@ -9,8 +9,8 @@ class Layout {
$contact_link = $config->get_string('contact_link'); $contact_link = $config->get_string('contact_link');
$header_html = ""; $header_html = "";
ksort($page->headers); ksort($page->html_headers);
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -20,7 +20,7 @@ class Layout {
$contact_link = $config->get_string('contact_link'); $contact_link = $config->get_string('contact_link');
$header_html = ""; $header_html = "";
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -18,7 +18,7 @@ class Themelet {
* A specific, common error message * A specific, common error message
*/ */
public function display_permission_denied(Page $page) { public function display_permission_denied(Page $page) {
header("HTTP/1.0 403 Permission Denied"); $page->add_http_header("HTTP/1.0 403 Permission Denied");
$this->display_error($page, "Permission Denied", "You do not have permission to access this page"); $this->display_error($page, "Permission Denied", "You do not have permission to access this page");
} }

View file

@ -9,8 +9,8 @@ class Layout {
$contact_link = $config->get_string('contact_link'); $contact_link = $config->get_string('contact_link');
$header_html = ""; $header_html = "";
ksort($page->headers); ksort($page->html_headers);
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -16,7 +16,7 @@ class Themelet {
* A specific, common error message * A specific, common error message
*/ */
public function display_permission_denied(Page $page) { public function display_permission_denied(Page $page) {
header("HTTP/1.0 403 Permission Denied"); $page->add_http_header("HTTP/1.0 403 Permission Denied");
$this->display_error($page, "Permission Denied", "You do not have permission to access this page"); $this->display_error($page, "Permission Denied", "You do not have permission to access this page");
} }

View file

@ -14,7 +14,7 @@ class Layout {
$contact_link = $config->get_string('contact_link'); $contact_link = $config->get_string('contact_link');
$header_html = ""; $header_html = "";
foreach($page->headers as $line) { foreach($page->html_headers as $line) {
$header_html .= "\t\t$line\n"; $header_html .= "\t\t$line\n";
} }

View file

@ -18,7 +18,7 @@ class Themelet {
* A specific, common error message * A specific, common error message
*/ */
public function display_permission_denied(Page $page) { public function display_permission_denied(Page $page) {
header("HTTP/1.0 403 Permission Denied"); $page->add_http_header("HTTP/1.0 403 Permission Denied");
$this->display_error($page, "Permission Denied", "You do not have permission to access this page"); $this->display_error($page, "Permission Denied", "You do not have permission to access this page");
} }