popnoire/site/OFF_plugins/imagekit/lib/lazythumb.php
2026-02-12 15:22:46 +01:00

293 lines
8.8 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Kirby\Plugins\ImageKit;
use Asset;
use Dir;
use Error;
use Exception;
use F;
use File;
use Media;
use Str;
use Thumb;
// Honestly, who in the PHP team is responsible for naming things?!?
use RecursiveIteratorIterator as Walker;
use RecursiveDirectoryIterator as DirWalker;
/**
* Extended version of Kirbys thumb class for
* creating “lazy” thumbnails.
*/
class LazyThumb extends Thumb {
const JOBFILE_SUFFIX = '-imagekitjob.php';
public function __construct($source, $params = []) {
$this->source = $this->result = is_a($source, 'Media') ? $source : new Media($source);
$this->options = array_merge(static::$defaults, $this->params($params));
$this->destination = $this->destination();
// don't create the thumbnail if it's not necessary
if($this->isObsolete()) return;
// don't create the thumbnail if it exists
if(!$this->isThere()) {
// try to create the thumb folder if it is not there yet
dir::make(dirname($this->destination->root));
// check for a valid image
if(!$this->source->exists() || $this->source->type() != 'image') {
throw new Error('The given image is invalid', static::ERROR_INVALID_IMAGE);
}
// check for a valid driver
if(!array_key_exists($this->options['driver'], static::$drivers)) {
throw new Error('Invalid thumbnail driver', static::ERROR_INVALID_DRIVER);
}
// create a jobfile for the thumbnail
$this->create();
// create a virtual asset, on which methods like width()
// and height() can be called on.
$this->result = new ProxyAsset(new Media($this->destination->root, $this->destination->url));
$this->result->original($this->source);
$this->result->options($this->options);
} else {
// create the result object
$this->result = new Media($this->destination->root, $this->destination->url);
}
return $this;
}
protected function create() {
$root = static::jobfile($this->destination->root);
if(f::exists($root)) return;
if(is_a($this->source, 'File')) {
// Source file belongs to a page
$pageid = $this->source->page()->id();
$dir = null;
} else {
// Source file is an outlaw, hiding somewhere else in
// the file tree
$pageid = null;
$dir = substr($this->source->root(), str::length(kirby()->roots->index));
$dir = pathinfo(ltrim($dir , DS), PATHINFO_DIRNAME);
}
$options = [
'imagekit.version' => imagekit()->version(),
'source' => [
'filename' => $this->source->filename(),
'dir' => $dir,
'page' => $pageid,
],
'options' => $this->options,
];
// Remove `destination` option before export, because
// closures cannot be exported and this option is a
// closure by default.
unset($options['options']['destination']);
$export = "<?php\nreturn " . var_export($options, true) . ';';
f::write($root, $export);
}
// ===== API Methods ========================================================
public static function process($path) {
$thumbs = kirby()->roots->thumbs();
if(!str::startsWith($path, $thumbs)) {
$path = $thumbs . DS . $path;
}
$jobfile = static::jobfile($path);
if(!$thumbinfo = @include($jobfile)) {
// Abort, if there is no matching jobfile for the
// requested thumb.
return false;
}
// This option is a closure by default, which cannot be
// restored. Currently, we can only restore it by
// overriding it the the default option of the thumb
// class. So this does not work, when a custom
// 'destination' option was set for this particular
// thumbnail or the option has been changed between
// jobfile creation and execution of this method.
$thumbinfo['options']['destination'] = thumb::$defaults['destination'];
if (!is_null($thumbinfo['source']['page'])) {
// Try to relocate the image and get its association
// with the original parent page or site
if ($thumbinfo['source']['page'] === '') {
// Image was uploaded to the "content" directory
$image = site()->image($thumbinfo['source']['filename']);
} else {
// Image belongs to a specific page
$image = page($thumbinfo['source']['page'])->image($thumbinfo['source']['filename']);
}
if(!$image) {
// If source image does not exist any more, remove
// the jobfile.
f::remove($jobfile);
return false;
}
} else {
// If the image does not belong to a specific page,
// just use an `Asset` as source.
$image = new Asset($thumbinfo['source']['dir'] . DS . $thumbinfo['source']['filename']);
if(!$image->exists()) {
f::remove($jobfile);
return false;
}
}
// override url and root of the thumbs directory to the
// current values. This prevents ImageKit from failing
// after your Kirby installation has been moved.
$thumbinfo['options']['ul'] = kirby()->urls->thumbs();
$thumbinfo['options']['root'] = kirby()->roots->thumbs();
// Finally execute job file by creating a thumb
$thumb = new ComplainingThumb($image, $thumbinfo['options']);
if(!kirby()->option('imagekit.debug') && f::exists($thumb->destination()->root)) {
// Delete job file if thumbnail has been generated
// successfully and were not in debug mode.
f::remove($jobfile);
}
return $thumb;
}
/**
* Returns the path of a thumbnails jobfile. The jobfile
* contains instructions about how to create the actual
* thumbnail.
*
* @return string A thumbnails jobfile.
*/
public static function jobfile($path) {
return !str::endsWith($path, self::JOBFILE_SUFFIX) ? $path . self::JOBFILE_SUFFIX : $path;
}
/**
* Returns all pending thumbnails, i.e. thumbnails that
* have not been created yet. This works by looking for
* jobfiles in the thumbs directory.
*
* @return array A list of all pending thumbnails
*/
public static function pending() {
$pending = [];
$iterator = new Walker(new DirWalker(kirby()->roots()->thumbs()), Walker::SELF_FIRST);
foreach($iterator as $file) {
$pathname = $file->getPathname();
if(str::endsWith($pathname, self::JOBFILE_SUFFIX)) {
$thumb = str::substr($pathname, 0, -str::length(self::JOBFILE_SUFFIX));
if(!file_exists($thumb)) {
$pending[] = $pathname;
}
}
}
return $pending;
}
/**
* Walks the thumbs directory, searches for image files
* and returns a list of those.
*
* @return array A list of all websafe image files within
* the thumbs directory.
*/
public static function created() {
$created = [];
$iterator = new Walker(new DirWalker(kirby()->roots()->thumbs()), Walker::SELF_FIRST);
foreach($iterator as $file) {
$pathname = $file->getPathname();
if(in_array(pathinfo($pathname, PATHINFO_EXTENSION), ['jpg', 'jpeg', 'png', 'gif'])) {
$created[] = $pathname;
}
}
return $created;
}
/**
* Get the actual status of generated and pending thumbs
* on your site.
*
* @return array An associative array containing stats
* about your thumbs folder.
*/
public static function status() {
return [
'pending' => sizeof(static::pending()),
'created' => sizeof(static::created()),
];
}
/**
* Clears the entire thumbs directory.
*
* @return boolen `true` if cleaning was successful,
* otherwise `false`.
*/
public static function clear() {
$root = kirby()->roots()->thumbs();
// Look for placeholder files used by many projects
// when working with git. these files are used to add
// empty directories to repositories. Files will be
// re-created after zhe cache has been flushed. Although
// these files are usually empty, its more secure to
// read their contents before deleting them, just in case …
$indexFile = $root . DS . 'index.html';
$index = f::exists($indexFile) ? f::read($indexFile) : false;
$gitkeepFile = $root . DS . '.gitkeep';
$gitkeep = f::exists($gitkeepFile) ? f::read($gitkeepFile) : false;
$result = dir::clean($root);
if($result) {
// Only re-create if thumbs dir cleanup was successful
if($index !== false) {
// Re-create index.html if it existed before
f::write($indexFile, $index);
}
if($gitkeep !== false) {
// Re-create .gitkeep file, if it existed before
f::write($gitkeepFile, $gitkeep);
}
}
return $result;
}
}