Add double-density support to photo thumbnails #28
@ -488,7 +488,7 @@ class Database
 | 
				
			|||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * This function can be used to insert data into the database in a secure way.
 | 
						 * This function can be used to insert data into the database in a secure way.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public function insert($method = 'replace', $table, $columns, $data)
 | 
						public function insert($method, $table, $columns, $data)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		// With nothing to insert, simply return.
 | 
							// With nothing to insert, simply return.
 | 
				
			||||||
		if (empty($data))
 | 
							if (empty($data))
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,7 @@ class GenericTable
 | 
				
			|||||||
		$this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start'];
 | 
							$this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Figure out where we are on the whole, too.
 | 
							// Figure out where we are on the whole, too.
 | 
				
			||||||
		$numPages = ceil($this->recordCount / $this->items_per_page);
 | 
							$numPages = max(1, ceil($this->recordCount / $this->items_per_page));
 | 
				
			||||||
		$this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages);
 | 
							$this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Let's bear a few things in mind...
 | 
							// Let's bear a few things in mind...
 | 
				
			||||||
@ -234,7 +234,9 @@ class GenericTable
 | 
				
			|||||||
				else
 | 
									else
 | 
				
			||||||
					$pattern = $options['data']['pattern'];
 | 
										$pattern = $options['data']['pattern'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!is_numeric($rowData[$options['data']['timestamp']]))
 | 
									if (!isset($rowData[$options['data']['timestamp']]))
 | 
				
			||||||
 | 
										$timestamp = 0;
 | 
				
			||||||
 | 
									elseif (!is_numeric($rowData[$options['data']['timestamp']]))
 | 
				
			||||||
					$timestamp = strtotime($rowData[$options['data']['timestamp']]);
 | 
										$timestamp = strtotime($rowData[$options['data']['timestamp']]);
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
					$timestamp = (int) $rowData[$options['data']['timestamp']];
 | 
										$timestamp = (int) $rowData[$options['data']['timestamp']];
 | 
				
			||||||
 | 
				
			|||||||
@ -67,14 +67,33 @@ class Image extends Asset
 | 
				
			|||||||
		return EXIF::fromFile($this->getPath());
 | 
							return EXIF::fromFile($this->getPath());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function getPath()
 | 
						public function getImageUrls($width = null, $height = null)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return ASSETSDIR . '/' . $this->subdir . '/' . $this->filename;
 | 
							$image_urls = [];
 | 
				
			||||||
 | 
							if (isset($width) || isset($height))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								$thumbnail = new Thumbnail($this);
 | 
				
			||||||
 | 
								$image_urls[1] = $this->getThumbnailUrl($width, $height, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Can we afford to generate double-density thumbnails as well?
 | 
				
			||||||
 | 
								if ((!isset($width) || $this->image_width >= $width * 2) &&
 | 
				
			||||||
 | 
									(!isset($height) || $this->image_height >= $height * 2))
 | 
				
			||||||
 | 
									$image_urls[2] = $this->getThumbnailUrl($width * 2, $height * 2, false);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									$image_urls[2] = $this->getThumbnailUrl($this->image_width, $this->image_height, true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								$image_urls[1] = $this->getUrl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return $image_urls;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function getUrl()
 | 
						public function getInlineImage($width = null, $height = null, $className = 'inline-image')
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return ASSETSURL . '/' . $this->subdir . '/' . $this->filename;
 | 
							$image_urls = $this->getImageUrls($width, $height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return '<img class="' . $className . '" src="' . $image_urls[1] . '" alt=""' .
 | 
				
			||||||
 | 
								(isset($image_urls[2]) ? ' srcset="' . $image_urls[2] . ' 2x"' : '') . '>';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@ -141,7 +160,8 @@ class Image extends Asset
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return Registry::get('db')->query('
 | 
							return Registry::get('db')->query('
 | 
				
			||||||
			DELETE FROM assets_thumbs
 | 
								UPDATE assets_thumbs
 | 
				
			||||||
 | 
								SET filename = NULL
 | 
				
			||||||
			WHERE id_asset = {int:id_asset}',
 | 
								WHERE id_asset = {int:id_asset}',
 | 
				
			||||||
			['id_asset' => $this->id_asset]);
 | 
								['id_asset' => $this->id_asset]);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -63,9 +63,9 @@ class PageIndex
 | 
				
			|||||||
			   lower     current/cont. pgs.       center            upper
 | 
								   lower     current/cont. pgs.       center            upper
 | 
				
			||||||
		*/
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		$this->num_pages = ceil($this->recordCount / $this->items_per_page);
 | 
							$this->num_pages = max(1, ceil($this->recordCount / $this->items_per_page));
 | 
				
			||||||
		$this->current_page = min(ceil($this->start / $this->items_per_page) + 1, $this->num_pages);
 | 
							$this->current_page = min(ceil($this->start / $this->items_per_page) + 1, $this->num_pages);
 | 
				
			||||||
		if ($this->num_pages == 0)
 | 
							if ($this->num_pages <= 1)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			$this->needsPageIndex = false;
 | 
								$this->needsPageIndex = false;
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@
 | 
				
			|||||||
class Thumbnail
 | 
					class Thumbnail
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	private $image;
 | 
						private $image;
 | 
				
			||||||
 | 
						private $image_meta;
 | 
				
			||||||
	private $thumbnails;
 | 
						private $thumbnails;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private $properly_initialised;
 | 
						private $properly_initialised;
 | 
				
			||||||
@ -23,7 +24,7 @@ class Thumbnail
 | 
				
			|||||||
	const CROP_MODE_SLICE_CENTRE = 4;
 | 
						const CROP_MODE_SLICE_CENTRE = 4;
 | 
				
			||||||
	const CROP_MODE_SLICE_BOTTOM = 5;
 | 
						const CROP_MODE_SLICE_BOTTOM = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function __construct($image)
 | 
						public function __construct(Image $image)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		$this->image      = $image;
 | 
							$this->image      = $image;
 | 
				
			||||||
		$this->image_meta = $image->getMeta();
 | 
							$this->image_meta = $image->getMeta();
 | 
				
			||||||
@ -45,51 +46,45 @@ class Thumbnail
 | 
				
			|||||||
		$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
 | 
							$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
 | 
				
			||||||
		if (!empty($this->thumbnails[$thumb_selector]))
 | 
							if (!empty($this->thumbnails[$thumb_selector]))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			$thumb_path = '/' . $this->image->getSubdir() . '/' . $this->thumbnails[$thumb_selector];
 | 
								$thumb_filename = $this->image->getSubdir() . '/' . $this->thumbnails[$thumb_selector];
 | 
				
			||||||
			if (file_exists(THUMBSDIR . $thumb_path))
 | 
								if (file_exists(THUMBSDIR . '/' . $thumb_filename))
 | 
				
			||||||
				return THUMBSURL . $thumb_path;
 | 
									return THUMBSURL . '/' . $thumb_filename;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Do we have a custom thumbnail on file?
 | 
							// Do we have a custom thumbnail on file?
 | 
				
			||||||
		$custom_selector = 'custom_' . $this->width . 'x' . $this->height;
 | 
							$custom_selector = 'custom_' . $this->width . 'x' . $this->height;
 | 
				
			||||||
		if (isset($this->image_meta[$custom_selector]))
 | 
							if (isset($this->image_meta[$custom_selector]))
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			$custom_thumb_path = '/' . $this->image->getSubdir() . '/' . $this->image_meta[$custom_selector];
 | 
								$custom_filename = $this->image->getSubdir() . '/' . $this->image_meta[$custom_selector];
 | 
				
			||||||
			if (file_exists(ASSETSDIR . $custom_thumb_path))
 | 
								if (file_exists(ASSETSDIR . '/' . $custom_filename))
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				// Ensure destination thumbnail directory exists.
 | 
					 | 
				
			||||||
				if (!file_exists($this->image->getSubdir()))
 | 
					 | 
				
			||||||
					@mkdir(THUMBSDIR . '/' . $this->image->getSubdir(), 0755, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// Copy the custom thumbail to the general thumbnail directory.
 | 
									// Copy the custom thumbail to the general thumbnail directory.
 | 
				
			||||||
				copy(ASSETSDIR . $custom_thumb_path, THUMBSDIR . $custom_thumb_path);
 | 
									copy(ASSETSDIR . '/' . $custom_filename, THUMBSDIR . '/' . $custom_filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Let's remember this for future reference.
 | 
									// Let's remember this for future reference.
 | 
				
			||||||
				$this->markAsGenerated($this->image_meta[$custom_selector]);
 | 
									$this->markAsGenerated($this->image_meta[$custom_selector]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				return THUMBSURL . $custom_thumb_path;
 | 
									return THUMBSURL . '/' . $custom_filename;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				throw new UnexpectedValueException('Custom thumbnail expected, but missing in file system!');
 | 
									throw new UnexpectedValueException('Custom thumbnail expected, but missing in file system!');
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Is this the right moment to generate a thumbnail, then?
 | 
							// Is this the right moment to generate a thumbnail, then?
 | 
				
			||||||
		if ($generate && array_key_exists($thumb_selector, $this->thumbnails))
 | 
							if ($generate)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return $this->generate();
 | 
								if (array_key_exists($thumb_selector, $this->thumbnails))
 | 
				
			||||||
 | 
									return $this->generate();
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									throw new Exception("Trying to generate a thumbnail not previously queued by the system\n" .
 | 
				
			||||||
 | 
										print_r(func_get_args(), true));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// If not, queue it for generation at another time, and return a URL to generate it with.
 | 
							// If not, queue it for generation at another time, and return a URL to generate it with.
 | 
				
			||||||
		elseif (!$generate)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			$this->markAsQueued();
 | 
					 | 
				
			||||||
			return BASEURL . '/thumbnail/' . $this->image->getId() . '/' . $this->width . 'x' . $this->height . $this->filename_suffix . '/';
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Still here..? What are you up to? ..Sneaking?
 | 
					 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			throw new Exception("Trying to generate a thumbnail for selector " . $thumb_selector . ", which does not appear to have been requested by the system.\n" . print_r(func_get_args(), true));
 | 
								$this->markAsQueued();
 | 
				
			||||||
 | 
								return BASEURL . '/thumbnail/' . $this->image->getId() . '/' . $thumb_selector . '/';
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -260,14 +255,18 @@ class Thumbnail
 | 
				
			|||||||
				'_' . $this->width . 'x' . $this->height . $this->filename_suffix . '.' . $ext;
 | 
									'_' . $this->width . 'x' . $this->height . $this->filename_suffix . '.' . $ext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Ensure the thumbnail subdirectory exists.
 | 
								// Ensure the thumbnail subdirectory exists.
 | 
				
			||||||
			if (!is_dir(THUMBSDIR . '/' . $this->image->getSubdir()))
 | 
								$target_dir = THUMBSDIR . '/' . $this->image->getSubdir();
 | 
				
			||||||
				mkdir(THUMBSDIR . '/' . $this->image->getSubdir(), 0755, true);
 | 
								if (!is_dir($target_dir))
 | 
				
			||||||
 | 
									mkdir($target_dir, 0755, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!is_writable($target_dir))
 | 
				
			||||||
 | 
									throw new Exception('Thumbnail directory is not writable!');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// No need to preserve every detail.
 | 
								// No need to preserve every detail.
 | 
				
			||||||
			$thumb->setImageCompressionQuality(80);
 | 
								$thumb->setImageCompressionQuality(80);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Save it in a public spot.
 | 
								// Save it in a public spot.
 | 
				
			||||||
			$thumb->writeImage(THUMBSDIR . '/' . $this->image->getSubdir() . '/' . $thumb_filename);
 | 
								$thumb->writeImage($target_dir . '/' . $thumb_filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Let's remember this for future reference...
 | 
								// Let's remember this for future reference...
 | 
				
			||||||
			$this->markAsGenerated($thumb_filename);
 | 
								$this->markAsGenerated($thumb_filename);
 | 
				
			||||||
@ -278,7 +277,6 @@ class Thumbnail
 | 
				
			|||||||
			// Finally, return the URL for the generated thumbnail image.
 | 
								// Finally, return the URL for the generated thumbnail image.
 | 
				
			||||||
			return THUMBSURL . '/' . $this->image->getSubdir() . '/' . $thumb_filename;
 | 
								return THUMBSURL . '/' . $this->image->getSubdir() . '/' . $thumb_filename;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// Blast! Curse your sudden but inevitable betrayal!
 | 
					 | 
				
			||||||
		catch (ImagickException $e)
 | 
							catch (ImagickException $e)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			throw new Exception('ImageMagick error occurred while generating thumbnail. Output: ' . $e->getMessage());
 | 
								throw new Exception('ImageMagick error occurred while generating thumbnail. Output: ' . $e->getMessage());
 | 
				
			||||||
@ -336,10 +334,16 @@ class Thumbnail
 | 
				
			|||||||
		if ($success)
 | 
							if ($success)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
 | 
								$thumb_selector = $this->width . 'x' . $this->height . $this->filename_suffix;
 | 
				
			||||||
			$this->thumbnails[$thumb_selector] = $filename !== 'NULL' ? $filename : '';
 | 
								$this->thumbnails[$thumb_selector] = $filename !== 'NULL' ? $filename : null;
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return $success;
 | 
								// For consistency, write new thumbnail filename to parent Image object.
 | 
				
			||||||
 | 
								// TODO: there could still be an inconsistency if multiple objects exists for the same image asset.
 | 
				
			||||||
 | 
								$this->image->getThumbnails()[$thumb_selector] = $this->thumbnails[$thumb_selector];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return $success;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								throw new UnexpectedValueException('Thumbnail queuing query failed');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private function markAsQueued()
 | 
						private function markAsQueued()
 | 
				
			||||||
 | 
				
			|||||||
@ -65,11 +65,9 @@ class PhotoPage extends SubTemplate
 | 
				
			|||||||
					<a href="', $this->photo->getUrl(), '">';
 | 
										<a href="', $this->photo->getUrl(), '">';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ($this->photo->isPortrait())
 | 
							if ($this->photo->isPortrait())
 | 
				
			||||||
			echo '
 | 
								echo $this->photo->getInlineImage(null, 960);
 | 
				
			||||||
						<img src="', $this->photo->getThumbnailUrl(null, 960), '" alt="">';
 | 
					 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			echo '
 | 
								echo $this->photo->getInlineImage(1280, null);
 | 
				
			||||||
						<img src="', $this->photo->getThumbnailUrl(1280, null), '" alt="">';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		echo '
 | 
							echo '
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
 | 
				
			|||||||
@ -90,8 +90,14 @@ class PhotosIndex extends SubTemplate
 | 
				
			|||||||
					<a class="edit" href="', BASEURL, '/editasset/?id=', $image->getId(), '">Edit</a>';
 | 
										<a class="edit" href="', BASEURL, '/editasset/?id=', $image->getId(), '">Edit</a>';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		echo '
 | 
							echo '
 | 
				
			||||||
					<a href="', $image->getPageUrl(), $this->url_suffix, '">
 | 
										<a href="', $image->getPageUrl(), $this->url_suffix, '#photo_frame">
 | 
				
			||||||
						<img src="', $image->getThumbnailUrl($width, $height, $crop, $fit), '" alt="" title="', $image->getTitle(), '">';
 | 
											<img src="', $image->getThumbnailUrl($width, $height, $crop, $fit), '"';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Can we offer double-density thumbs?
 | 
				
			||||||
 | 
							if ($image->width() >= $width * 2 && $image->height() >= $height * 2)
 | 
				
			||||||
 | 
								echo ' srcset="', $image->getThumbnailUrl($width * 2, $height * 2, $crop, $fit), ' 2x"';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							echo ' alt="" title="', $image->getTitle(), '">';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ($this->show_labels)
 | 
							if ($this->show_labels)
 | 
				
			||||||
			echo '
 | 
								echo '
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user