Initial commit
This commit is contained in:
commit
65e0da7e11
1397 changed files with 596542 additions and 0 deletions
225
site/OFF_plugins/imagekit/lib/complainingthumb.php
Normal file
225
site/OFF_plugins/imagekit/lib/complainingthumb.php
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit;
|
||||
|
||||
use Response;
|
||||
use Thumb;
|
||||
use Str;
|
||||
|
||||
|
||||
/**
|
||||
* An extended version of Kirby’s thumb class which is able
|
||||
* to throw an error, if thumbs are resized with the
|
||||
* GD Library and PHP’s memory limit is exceeded or if
|
||||
* thumbnail creation failed for another reason.
|
||||
*/
|
||||
class ComplainingThumb extends Thumb {
|
||||
|
||||
private static $_errorData = [];
|
||||
private static $_errorFormat = 'image';
|
||||
|
||||
private static $_errorListening = false;
|
||||
private static $_creating = false;
|
||||
private static $_errorReporting;
|
||||
private static $_displayErrors;
|
||||
private static $_targetDimensions;
|
||||
private static $_sendError = false;
|
||||
|
||||
private static $_reservedMemory;
|
||||
|
||||
public static function setErrorFormat($format = null) {
|
||||
if (!is_null($format)) static::$_errorFormat = $format;
|
||||
static::$_errorFormat;
|
||||
}
|
||||
|
||||
public static function enableSendError() {
|
||||
static::$_sendError = true;
|
||||
}
|
||||
|
||||
public function create() {
|
||||
|
||||
if(!static::$_sendError) {
|
||||
// Don’t setup a error handlers, if complaining is
|
||||
// not enabled and just return the result of create().
|
||||
return parent::create();
|
||||
}
|
||||
|
||||
$this->prepareErrorHandler();
|
||||
$result = parent::create();
|
||||
$this->restoreErrorHandler();
|
||||
|
||||
if(!file_exists($this->destination->root)) {
|
||||
|
||||
$message = str::template('Thumbnail creation for "{file}" failed. Please ensure, that the thumbs directory is writable and your driver configuration is correct.', [
|
||||
'file' => static::$_errorData['file'],
|
||||
]);
|
||||
|
||||
static::sendErrorResponse($message);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function prepareErrorHandler() {
|
||||
|
||||
// Reserve one additional megabyte of memory to make
|
||||
// catching of out-of-memory errors more reliable.
|
||||
// The variable’s content is deleted in the shutdown
|
||||
// function to have more available memory to prepare an
|
||||
// error.
|
||||
static::$_reservedMemory = str_repeat('#', 1024 * 1024);
|
||||
|
||||
$dimensions = $this->source->dimensions();
|
||||
$filename = str_replace(kirby()->roots()->index() . DS, '', $this->source->root());
|
||||
|
||||
$asset = new ProxyAsset($this->destination->root);
|
||||
$asset->options($this->options);
|
||||
$asset->original($this->source);
|
||||
$targetDimensions = $asset->dimensions();
|
||||
|
||||
static::$_errorData = [
|
||||
'file' => $filename,
|
||||
'width' => $dimensions->width,
|
||||
'height' => $dimensions->height,
|
||||
'size' => $this->source->size(),
|
||||
'targetWidth' => $targetDimensions->width,
|
||||
'targetHeight' => $targetDimensions->height,
|
||||
];
|
||||
|
||||
static::$_displayErrors = ini_get('display_errors');
|
||||
static::$_errorReporting = error_reporting();
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 0);
|
||||
|
||||
if (!static::$_errorListening) {
|
||||
// As of PHP 7.0 there is not way of de-registering a
|
||||
// shutdown callback. So we only register it once at
|
||||
// the first time, this method is called.
|
||||
static::$_errorListening = true;
|
||||
register_shutdown_function([__CLASS__, 'shutdownCallback']);
|
||||
}
|
||||
|
||||
static::$_creating = true;
|
||||
}
|
||||
|
||||
private function restoreErrorHandler() {
|
||||
|
||||
// Delete the reserved memory, because if thumbnail
|
||||
// creation succeeded, it is not needed any more.
|
||||
static::$_reservedMemory = null;
|
||||
|
||||
ini_set('display_errors', static::$_displayErrors);
|
||||
error_reporting(static::$_errorReporting);
|
||||
|
||||
static::$_creating = false;
|
||||
}
|
||||
|
||||
public static function shutdownCallback() {
|
||||
|
||||
// Delete the reserved memory to have more memory for
|
||||
// preparing an error message.
|
||||
static::$_reservedMemory = null;
|
||||
|
||||
$error = error_get_last();
|
||||
if(!$error) return;
|
||||
|
||||
if($error['type'] == E_ERROR || $error['type'] === E_WARNING) {
|
||||
|
||||
if(str::contains($error['message'], 'Allowed Memory Size')) {
|
||||
|
||||
$message = str::template('Thumbnail creation for "{file}" failed, because source image is probably too large ({width} × {height} pixels / {size}) To fix this issue, increase the memory limit of PHP or upload a smaller version of this image.', [
|
||||
'file' => static::$_errorData['file'],
|
||||
'width' => number_format(static::$_errorData['width']),
|
||||
'height' => number_format(static::$_errorData['height']),
|
||||
'size' => number_format(static::$_errorData['size'] / (1024 * 1024), 2) . " MB",
|
||||
]);
|
||||
|
||||
static::sendErrorResponse($message);
|
||||
|
||||
} else {
|
||||
static::sendErrorResponse($error['message']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function sendErrorResponse($message = '') {
|
||||
|
||||
// Make sure, that the error message is non-cachable
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache");
|
||||
|
||||
if(static::$_errorFormat === 'image') {
|
||||
|
||||
// The returned error image is an SVG file, because it
|
||||
// can be created just by joined a couple of XML tags
|
||||
// together and is supported by every modern browser,
|
||||
// starting with IE 9 (which is of course not
|
||||
// modern any more …).
|
||||
header('Content-Type: image/svg+xml');
|
||||
|
||||
// Although this is technically not correct, we have
|
||||
// to send status code 200, otherwise the image does
|
||||
// not show up in Firefox and Safari, although it
|
||||
// works with code 500 in Chrome and Edge. Also tested
|
||||
// in IE 10, IE 11
|
||||
http_response_code(200);
|
||||
|
||||
$width = static::$_errorData['targetWidth'];
|
||||
$height = static::$_errorData['targetHeight'];
|
||||
|
||||
// Return an SVG File with the thumb’s dimensions
|
||||
// Icon Credit: fontawesome.io
|
||||
?><svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="<?= $width ?>" height="<?= $height ?>"
|
||||
viewBox="0 0 <?= $width ?> <?= $height ?>"
|
||||
preserveAspectRatio="none">
|
||||
|
||||
<symbol id="icon">
|
||||
<svg viewBox="0 0 48 44.52" width="48" height="44.52" preserveAspectRatio="xMinYMax meet">
|
||||
<path d="M27.425,36.789V31.705a0.854,0.854,0,0,0-.254-0.629,0.823,0.823,0,0,0-.6-0.254H21.431a0.823,0.823,0,0,0-.6.254,0.854,0.854,0,0,0-.254.629v5.084a0.854,0.854,0,0,0,.254.629,0.823,0.823,0,0,0,.6.254h5.137a0.823,0.823,0,0,0,.6-0.254A0.854,0.854,0,0,0,27.425,36.789ZM27.371,26.782L27.853,14.5a0.589,0.589,0,0,0-.268-0.508,1.034,1.034,0,0,0-.642-0.294H21.057a1.034,1.034,0,0,0-.642.294,0.64,0.64,0,0,0-.268.562L20.6,26.782a0.514,0.514,0,0,0,.268.441,1.152,1.152,0,0,0,.642.174h4.95a1.088,1.088,0,0,0,.629-0.174A0.6,0.6,0,0,0,27.371,26.782ZM27,1.793L47.545,39.464a3.192,3.192,0,0,1-.054,3.371,3.422,3.422,0,0,1-2.943,1.686H3.452A3.422,3.422,0,0,1,.509,42.835a3.192,3.192,0,0,1-.054-3.371L21,1.793A3.417,3.417,0,0,1,22.261.482a3.381,3.381,0,0,1,3.478,0A3.417,3.417,0,0,1,27,1.793Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
</symbol>
|
||||
|
||||
<rect width="100%" height="100%" fill="#F4999D" />
|
||||
|
||||
<switch>
|
||||
<foreignObject width="100%" height="100%" requiredExtensions="http://www.w3.org/1999/xhtml">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml" style="background:#F4999D;text-align:center;color:#fff;box-sizing:border-box;margin:0;padding:0;font-size:12px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif">
|
||||
<p style="margin:0;word-wrap:break-word;position:absolute;top:50%;transform:translateY(-50%);width: 100%;box-sizing:border-box;padding: 0 1em;">
|
||||
<svg viewBox="0 0 48 44.52" width="24" height="22.125" xmlns="http://www.w3.org/2000/svg" style="display: block; margin: 0 auto 12px;"><path d="M27.425,36.789V31.705a0.854,0.854,0,0,0-.254-0.629,0.823,0.823,0,0,0-.6-0.254H21.431a0.823,0.823,0,0,0-.6.254,0.854,0.854,0,0,0-.254.629v5.084a0.854,0.854,0,0,0,.254.629,0.823,0.823,0,0,0,.6.254h5.137a0.823,0.823,0,0,0,.6-0.254A0.854,0.854,0,0,0,27.425,36.789ZM27.371,26.782L27.853,14.5a0.589,0.589,0,0,0-.268-0.508,1.034,1.034,0,0,0-.642-0.294H21.057a1.034,1.034,0,0,0-.642.294,0.64,0.64,0,0,0-.268.562L20.6,26.782a0.514,0.514,0,0,0,.268.441,1.152,1.152,0,0,0,.642.174h4.95a1.088,1.088,0,0,0,.629-0.174A0.6,0.6,0,0,0,27.371,26.782ZM27,1.793L47.545,39.464a3.192,3.192,0,0,1-.054,3.371,3.422,3.422,0,0,1-2.943,1.686H3.452A3.422,3.422,0,0,1,.509,42.835a3.192,3.192,0,0,1-.054-3.371L21,1.793A3.417,3.417,0,0,1,22.261.482a3.381,3.381,0,0,1,3.478,0A3.417,3.417,0,0,1,27,1.793Z" fill="#ffffff"/></svg>
|
||||
<?= $message ?>
|
||||
</p>
|
||||
</body>
|
||||
</foreignObject>
|
||||
<!-- Fallback -->
|
||||
<use xlink:href="#icon" transform="translate(<?= number_format($width / 2 - 48 / 2,1,'.','') ?>, <?= number_format($height / 2 - 44.52 / 2,1,'.','') ?>)" />
|
||||
</switch>
|
||||
</svg><?php
|
||||
|
||||
} else {
|
||||
|
||||
// If error format has been set to JSON, return JSON ;-)
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'status' => '',
|
||||
'code' => 500,
|
||||
'message' => $message,
|
||||
'data' => [
|
||||
'file' => static::$_errorData,
|
||||
],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
128
site/OFF_plugins/imagekit/lib/component/thumb.php
Normal file
128
site/OFF_plugins/imagekit/lib/component/thumb.php
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Component;
|
||||
|
||||
use Asset;
|
||||
use F;
|
||||
use Header;
|
||||
use Kirby\Component\Thumb as ThumbComponent;
|
||||
use Kirby\Plugins\ImageKit\LazyThumb;
|
||||
use Kirby\Plugins\ImageKit\ComplainingThumb;
|
||||
use Kirby\Plugins\ImageKit\ProxyAsset;
|
||||
use Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
|
||||
/**
|
||||
* Replacement for Kirby’s built-in `thumb` component with
|
||||
* asynchronous thumb creation and image optimization
|
||||
* capabilities.
|
||||
*/
|
||||
class Thumb extends ThumbComponent {
|
||||
|
||||
public function defaults() {
|
||||
|
||||
return array_merge(parent::defaults(), [
|
||||
'imagekit.lazy' => true,
|
||||
'imagekit.complain' => true,
|
||||
'imagekit.widget' => true,
|
||||
'imagekit.widget.step' => 5,
|
||||
'imagekit.widget.discover' => true,
|
||||
|
||||
'imagekit.optimize' => false,
|
||||
'imagekit.engine' => null,
|
||||
|
||||
'imagekit.license' => '',
|
||||
|
||||
// Used for the development of the plugin, currently
|
||||
// not officialy documented.
|
||||
'imagekit.debug' => false,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function configure() {
|
||||
parent::configure();
|
||||
|
||||
// Register route to catch non-existing files within the
|
||||
// thumbs directory.
|
||||
$base = ltrim(substr($this->kirby->roots->thumbs(), strlen($this->kirby->roots->index())), DS);
|
||||
|
||||
// Setup optimizer if enabled.
|
||||
if($this->kirby->option('imagekit.optimize')) {
|
||||
optimizer::register();
|
||||
}
|
||||
|
||||
$this->kirby->set('route', [
|
||||
'pattern' => "{$base}/(:all)", // $base = 'thumbs' by default
|
||||
'action' => function ($path) {
|
||||
|
||||
if($this->kirby->option('imagekit.complain')) {
|
||||
complainingthumb::enableSendError();
|
||||
complainingthumb::setErrorFormat('image');
|
||||
}
|
||||
|
||||
// Try to load a jobfile for given thumb url and
|
||||
// execute if exists
|
||||
$thumb = lazythumb::process($path);
|
||||
|
||||
if($thumb) {
|
||||
// Serve the image, if everything went fine :-D
|
||||
|
||||
$root = $thumb->result->root();
|
||||
|
||||
// Make sure, we’re sending a 200 status, telling
|
||||
// the browser that everything’s okay.
|
||||
header::status(200);
|
||||
|
||||
// Don’t tell anyone that this image was just
|
||||
// created by PHP ;-)
|
||||
header_remove('X-Powered-By');
|
||||
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', f::modified($root)) . ' GMT');
|
||||
header('Content-Type: ' . f::mime($root));
|
||||
header('Content-Length: ' . f::size($root));
|
||||
|
||||
// Send file and stop script execution
|
||||
readfile($root);
|
||||
|
||||
exit;
|
||||
|
||||
} else {
|
||||
// Show a 404 error, if the job could not be
|
||||
// found or executed
|
||||
return site()->errorPage();
|
||||
}
|
||||
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public function create($file, $params) {
|
||||
|
||||
if (!$this->kirby->option('imagekit.lazy') || (isset($params['imagekit.lazy']) && !$params['imagekit.lazy'])) {
|
||||
return parent::create($file, $params);
|
||||
}
|
||||
|
||||
if(!$file->isWebsafe()) return $file;
|
||||
|
||||
// Instead of a Thumb, a Job will be created for later
|
||||
// execution
|
||||
$thumb = new LazyThumb($file, $params);
|
||||
|
||||
if($thumb->result instanceof ProxyAsset) {
|
||||
// If the thumb is yet to be generated, use the
|
||||
// virtual asset, created by the LazyThumb class
|
||||
$asset = $thumb->result;
|
||||
} else {
|
||||
// Otherwise, create a new asset from the returned
|
||||
// media object.
|
||||
$asset = new Asset($thumb->result);
|
||||
}
|
||||
|
||||
// store a reference to the original file
|
||||
$asset->original($file);
|
||||
|
||||
return $asset;
|
||||
}
|
||||
|
||||
}
|
||||
73
site/OFF_plugins/imagekit/lib/imagekit.php
Normal file
73
site/OFF_plugins/imagekit/lib/imagekit.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit;
|
||||
|
||||
use F;
|
||||
use Obj;
|
||||
use Str;
|
||||
|
||||
|
||||
/**
|
||||
* Utility class for retrieving information about the plugin
|
||||
* version and it’s license.
|
||||
*/
|
||||
class ImageKit {
|
||||
|
||||
protected $version;
|
||||
|
||||
protected function __construct() {
|
||||
// Just declared to prevent direct instantiation of this
|
||||
// class (singleton pattern).
|
||||
}
|
||||
|
||||
public static function instance() {
|
||||
static $instance;
|
||||
return ($instance ?: $instance = new static());
|
||||
}
|
||||
|
||||
public function version() {
|
||||
if(is_null($this->version)) {
|
||||
$package = json_decode(f::read(dirname(__DIR__) . DS . 'package.json'));
|
||||
$this->version = $package->version;
|
||||
}
|
||||
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function root() {
|
||||
return dirname(__DIR__);
|
||||
}
|
||||
|
||||
public function license() {
|
||||
$key = kirby()->option('imagekit.license');
|
||||
$type = 'trial';
|
||||
|
||||
/**
|
||||
* Hey there,
|
||||
*
|
||||
* if you have digged deep into Kirby’s source code,
|
||||
* than you’ve probably stumbled across a similiar
|
||||
* message, asking you to be honest when using the
|
||||
* software. I ask you the same, if your intention is to
|
||||
* use ImageKit. Writing this plugin took a lot of time
|
||||
* and it hopefully saves you a lot of headaches. If you
|
||||
* would use a cloud-provider instead of rolling your own
|
||||
* thumb engine, then your would also have to pay them.
|
||||
*
|
||||
* Anyway, have a nice day!
|
||||
*
|
||||
* Fabian
|
||||
*/
|
||||
if (str::startsWith($key, 'IMGKT1') && str::length($key) === 39) {
|
||||
$type = 'ImageKit 1';
|
||||
} else {
|
||||
$key = null;
|
||||
}
|
||||
|
||||
return new Obj(array(
|
||||
'key' => $key,
|
||||
'type' => $type,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
293
site/OFF_plugins/imagekit/lib/lazythumb.php
Normal file
293
site/OFF_plugins/imagekit/lib/lazythumb.php
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
<?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 Kirby’s 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 it’s 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 we’re 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 thumbnail’s 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, it’s 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;
|
||||
}
|
||||
|
||||
}
|
||||
169
site/OFF_plugins/imagekit/lib/optimizer.php
Normal file
169
site/OFF_plugins/imagekit/lib/optimizer.php
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit;
|
||||
use A;
|
||||
use Dir;
|
||||
use F;
|
||||
use Thumb;
|
||||
|
||||
|
||||
class Optimizer {
|
||||
|
||||
protected static $kirby;
|
||||
protected static $optimizers = [];
|
||||
|
||||
// These variables store all loaded optimizers of an
|
||||
// actual instance of the Optimizer object, sorted by
|
||||
// their priority.
|
||||
protected $pre;
|
||||
protected $post;
|
||||
|
||||
/**
|
||||
* Creates an optimmizer for given Thumb
|
||||
*
|
||||
* @param Thumb $thumb
|
||||
* @param array $pre Optimizers to apply prior to
|
||||
* thumbnail creation.
|
||||
* @param array $post Optimizers to apply after
|
||||
* thumbnail creation.
|
||||
*/
|
||||
protected function __construct($thumb, $pre, $post) {
|
||||
static::init();
|
||||
$this->thumb = $thumb;
|
||||
$this->pre = $pre;
|
||||
$this->post = $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class for given thumb.
|
||||
*
|
||||
* @param Thumb $thumb
|
||||
* @return Optimizer
|
||||
*/
|
||||
public static function create(Thumb $thumb) {
|
||||
static::init();
|
||||
|
||||
$pre = [];
|
||||
$post = [];
|
||||
|
||||
// Get optimizers parameter
|
||||
$optimizers = a::get($thumb->options, 'imagekit.optimize', kirby()->option('imagekit.optimize'), true);
|
||||
|
||||
foreach(static::$optimizers as $optimizerClass) {
|
||||
if($optimizers === true || (is_array($optimizers) && in_array($optimizerClass::name(), $optimizers))) {
|
||||
if($optimizer = $optimizerClass::create($thumb)) {
|
||||
if($optimizer->priority('pre') !== false) {
|
||||
$pre[] = $optimizer;
|
||||
}
|
||||
if($optimizer->priority('post') !== false) {
|
||||
$post[] = $optimizer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all applicable optimization operations.
|
||||
usort($pre, function($a, $b) {
|
||||
if($a === $b) return 0;
|
||||
return ($a->priority('pre') < $b->priority('pre')) ? -1 : 1;
|
||||
});
|
||||
|
||||
usort($post, function($a, $b) {
|
||||
if($a === $b) return 0;
|
||||
return ($a->priority('post') < $b->priority('post')) ? -1 : 1;
|
||||
});
|
||||
|
||||
return new static($thumb, $pre, $post);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all operations that should happen before thumbnail
|
||||
* creation.
|
||||
*/
|
||||
public function pre() {
|
||||
foreach($this->pre as $optimizer) {
|
||||
$optimizer->pre();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all operations that should happen after thumbnail
|
||||
* creation.
|
||||
*/
|
||||
public function post() {
|
||||
foreach($this->post as $optimizer) {
|
||||
$optimizer->post();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the optimizer by extending all thumbnail
|
||||
* drivers in Kirby’s toolkit.
|
||||
*/
|
||||
public static function register() {
|
||||
static $registred;
|
||||
if($registred) return;
|
||||
|
||||
foreach(thumb::$drivers as $name => $driver) {
|
||||
thumb::$drivers[$name] = function($thumb) use ($driver) {
|
||||
|
||||
if(a::get($thumb->options, 'imagekit.optimize', kirby()->option('imagekit.optimize')) !== false) {
|
||||
$optimizer = static::create($thumb);
|
||||
|
||||
$optimizer->pre();
|
||||
$driver($thumb);
|
||||
$optimizer->post();
|
||||
|
||||
} else {
|
||||
$driver($thumb);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$registred = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans `optimizer` subdir for available optimizers and
|
||||
* loads them, if they’re available.
|
||||
*
|
||||
* @param Kirby $kirby
|
||||
*/
|
||||
public static function init($kirby = null) {
|
||||
static $initialized;
|
||||
if ($initialized) return;
|
||||
|
||||
static::$kirby = $kirby ?: kirby();
|
||||
|
||||
$lib = imagekit()->root() . DS . 'lib' . DS . 'optimizer';
|
||||
|
||||
// Load and initialize all optimizers
|
||||
foreach(dir::read($lib, ['base.php']) as $basename) {
|
||||
require_once($lib . DS . $basename);
|
||||
$optimizerClass = __NAMESPACE__ . '\\Optimizer\\' . f::name($basename);
|
||||
|
||||
// Setup defaults
|
||||
static::$kirby->options = array_merge($optimizerClass::defaults(), static::$kirby->options);
|
||||
|
||||
$optimizerClass::configure(static::$kirby);
|
||||
|
||||
if ($optimizerClass::available()) {
|
||||
static::$optimizers[] = $optimizerClass;
|
||||
}
|
||||
}
|
||||
|
||||
$initialized = true;
|
||||
}
|
||||
|
||||
public static function available($name) {
|
||||
static::init();
|
||||
$name = strtolower($name);
|
||||
|
||||
foreach(static::$optimizers as $optimizer) {
|
||||
if($optimizer::name() === $name) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
256
site/OFF_plugins/imagekit/lib/optimizer/base.php
Normal file
256
site/OFF_plugins/imagekit/lib/optimizer/base.php
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
use Exception;
|
||||
use F;
|
||||
use ReflectionClass;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for the optimizer class. An optimizer is
|
||||
* created for a specific thumbnail by calling the
|
||||
* `create($thumb)` method.
|
||||
*/
|
||||
interface BaseInterface {
|
||||
/**
|
||||
* Should implement at least some basic checks to make
|
||||
* sure, that the optimizer is working. This method should
|
||||
* check for executables being in place,
|
||||
* for PHP extensions, operating system etc.
|
||||
*
|
||||
* @return boolean Return `true`, if the optimizer is
|
||||
* available, otherwise `false`.
|
||||
*/
|
||||
public static function available();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The base class for ImageKit’s optimizers.
|
||||
*/
|
||||
abstract class Base implements BaseInterface {
|
||||
|
||||
/**
|
||||
* Defines the file types, this optimizer can handle.
|
||||
*
|
||||
* @var string|array Either string with a value of '*' or
|
||||
* an array containing a list of mime types.
|
||||
*/
|
||||
public static $selector = '*';
|
||||
|
||||
/**
|
||||
* Defines the priority of this optimizer’s operations.
|
||||
* @var array An array of two elements, where each can be
|
||||
* either a positive integer, zero or false.
|
||||
* The first value is the priority of
|
||||
* pre-operations, the second one of
|
||||
* post-operations. Priority is not set static
|
||||
* to make synamic changed possible.
|
||||
*/
|
||||
public $priority = [10, 10];
|
||||
|
||||
/**
|
||||
* Kirby’s instance.
|
||||
*
|
||||
* @var Kirby
|
||||
*/
|
||||
public static $kirby;
|
||||
|
||||
/**
|
||||
* Thumbnail will be set by create method.
|
||||
*
|
||||
* @var Thumb
|
||||
*/
|
||||
protected $thumb;
|
||||
|
||||
/**
|
||||
* Constructor of this optimizer
|
||||
*
|
||||
* @param Thumb An instance of the Thumb class.
|
||||
*/
|
||||
protected function __construct($thumb) {
|
||||
$this->thumb = $thumb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all available setting variables of
|
||||
* this optimizer.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function defaults() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after defaults have been added to the global
|
||||
* Kirby instance. Can be used for further operations
|
||||
* that need all options to be in place.
|
||||
*/
|
||||
public static function configure($kirby) {
|
||||
static::$kirby = $kirby;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to create an instance of given
|
||||
* optimizer.
|
||||
*/
|
||||
public static function create($thumb) {
|
||||
if(!static::matches($thumb)) {
|
||||
// Don’t create optimizer for given thumb, if it
|
||||
// cannot handle it’s file type.
|
||||
return null;
|
||||
} else {
|
||||
return new static($thumb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations to performed before thumbnail creation. This
|
||||
* can be used to modify parameters on the passed $thumb
|
||||
* object.
|
||||
*
|
||||
* @param Thumb $thumb
|
||||
*/
|
||||
public function pre() {
|
||||
// Do some crazy stuff here in a subclass …
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations to be performed after the thumbnai has been
|
||||
* created by the thumbs driver.
|
||||
*
|
||||
* @param Thumb $thumb
|
||||
*/
|
||||
public function post() {
|
||||
// Do some crazy stuff here in a subclass …
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of this optimizer.
|
||||
*
|
||||
* @param string $which Must be either 'pre' or 'post'.
|
||||
* @return int|boolean The priority of either 'pre' or
|
||||
* 'post' operations or false, if this
|
||||
* optimizer does not have a pre/post
|
||||
* operation defined.
|
||||
*/
|
||||
public function priority($which) {
|
||||
switch($which) {
|
||||
case 'pre':
|
||||
return $this->priority[0];
|
||||
|
||||
case 'post':
|
||||
return $this->priority[1];
|
||||
|
||||
default:
|
||||
throw new Exception('`$which` parameter must have a value of either `"pre"` or `"post"`.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, checks the extension of a thumb
|
||||
* destination file against the mime types, this optimizer
|
||||
* can handle. Additional checks are not performed.
|
||||
*
|
||||
* @param Thumb $thumb
|
||||
* @return bool `true`, if Optimizer can handle given
|
||||
* thumb, otherwise `false`.
|
||||
*/
|
||||
public static function matches($thumb) {
|
||||
$mime = f::extensionToMime(f::extension($thumb->destination->root));
|
||||
return in_array($mime, static::$selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the optimizer class in lowercase
|
||||
* without namespace.
|
||||
*
|
||||
* @return string The optimizer’s class name without
|
||||
* namespace.
|
||||
*/
|
||||
public static function name() {
|
||||
return strtolower(str_replace(__NAMESPACE__ . '\\', '', get_called_class()));
|
||||
}
|
||||
|
||||
|
||||
/* ===== Utility Functions ============================================== */
|
||||
|
||||
/**
|
||||
* Returns a temporary filename, based on the original
|
||||
* filename of the thumbnail destination.
|
||||
*
|
||||
* @param string $extension Optionally change the
|
||||
* extension of the temporary
|
||||
* file by providing this
|
||||
* parameter.
|
||||
* @return string The full path to the
|
||||
* temporary file.
|
||||
*/
|
||||
protected function getTemporaryFilename($extension = null) {
|
||||
$parts = pathinfo($this->thumb->destination->root);
|
||||
|
||||
if (!$extension) {
|
||||
$extension = $parts['extension'];
|
||||
}
|
||||
|
||||
// Add a unique suffix
|
||||
$suffix = '-' . uniqid();
|
||||
|
||||
return $parts['dirname'] . DS . $parts['filename'] . $suffix . '.' . $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the filesize of $target and $alternative and
|
||||
* only keeps the smallest of both files. If $alternative
|
||||
* is smaller than $target, $target will be replaced by
|
||||
* $alternative.
|
||||
*
|
||||
* @param string $target Full path to target file.
|
||||
* @param string $alternative Full path to alternative file.
|
||||
*
|
||||
*/
|
||||
protected function keepSmallestFile($target, $alternative) {
|
||||
if(f::size($alternative) <= f::size($target)) {
|
||||
f::remove($target);
|
||||
f::move($alternative, $target);
|
||||
} else {
|
||||
f::remove($alternative);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the driver of a given thumb is given driver id.
|
||||
*
|
||||
* @param string $driver Name of the thumbnail engine,
|
||||
* (i.e. 'im' or 'gd').
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isDriver($driver) {
|
||||
return (
|
||||
(isset($this->thumb->options['driver']) && $this->thumb->options['driver'] === $driver) ||
|
||||
(static::$kirby->option('imagekit.driver') === $driver)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get the value of an option from given Thumb
|
||||
* object. If not set, returns the global value of this
|
||||
* option.
|
||||
*
|
||||
* @param Thumb $thumb An instance of the Thumb class
|
||||
* from Kirby’s toolkit.
|
||||
* @param string $key The option key.
|
||||
* @return mixed Either local or global value of
|
||||
* the option.
|
||||
*/
|
||||
protected function option($key, $default = null) {
|
||||
if(isset($this->thumb->options[$key])) {
|
||||
return $this->thumb->options[$key];
|
||||
} else {
|
||||
return static::$kirby->option($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
74
site/OFF_plugins/imagekit/lib/optimizer/gifsicle.php
Normal file
74
site/OFF_plugins/imagekit/lib/optimizer/gifsicle.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
use F;
|
||||
|
||||
|
||||
/**
|
||||
* Lossless optimization of GIF files by using `gifsicle`.
|
||||
* Supports animated GIFs.
|
||||
*
|
||||
* See: https://www.lcdf.org/gifsicle/
|
||||
*/
|
||||
class Gifsicle extends Base {
|
||||
|
||||
public static $selector = ['image/gif'];
|
||||
public $priority = [false, 50];
|
||||
|
||||
protected $targetFile;
|
||||
protected $tempFile;
|
||||
|
||||
|
||||
public static function defaults() {
|
||||
return [
|
||||
'imagekit.gifsicle.bin' => null,
|
||||
'imagekit.gifsicle.level' => 3,
|
||||
'imagekit.gifsicle.colors' => false,
|
||||
'imagekit.gifsicle.flags' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function available() {
|
||||
return !empty(static::$kirby->option('imagekit.gifsicle.bin'));
|
||||
}
|
||||
|
||||
public function post() {
|
||||
|
||||
$tmpFile = $this->getTemporaryFilename();
|
||||
|
||||
$command = [];
|
||||
|
||||
$command[] = static::$kirby->option('imagekit.gifsicle.bin');
|
||||
|
||||
if($this->thumb->options['interlace']) {
|
||||
$command[] = '--interlace';
|
||||
}
|
||||
|
||||
// Set colors
|
||||
$colors = $this->option('imagekit.gifsicle.colors');
|
||||
if($colors !== false) {
|
||||
$command[] = "--colors $colors";
|
||||
}
|
||||
|
||||
// Set optimization level.
|
||||
$command[] = '--optimize=' . $this->option('imagekit.gifsicle.level');
|
||||
|
||||
$flags = $this->option('imagekit.gifsicle.flags');
|
||||
if(!empty($flags)) {
|
||||
// Add exra flags, if defined by user.
|
||||
$command[] = $flags;
|
||||
}
|
||||
|
||||
// Set output file
|
||||
$command[] = '--output "' . $tmpFile . '"';
|
||||
|
||||
// Set input file
|
||||
$command[] = '"' . $this->thumb->destination->root . '"';
|
||||
|
||||
exec(implode(' ', $command));
|
||||
|
||||
$this->keepSmallestFile($this->thumb->destination->root, $tmpFile);
|
||||
}
|
||||
|
||||
}
|
||||
79
site/OFF_plugins/imagekit/lib/optimizer/jpegtran.php
Normal file
79
site/OFF_plugins/imagekit/lib/optimizer/jpegtran.php
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
use F;
|
||||
|
||||
|
||||
/**
|
||||
* Lossless optimization of JPEG files by using `jpegtran`.
|
||||
*
|
||||
* See: http://jpegclub.org/jpegtran/
|
||||
* and: http://linux.die.net/man/1/jpegtran
|
||||
*/
|
||||
class JPEGTran extends Base {
|
||||
|
||||
public static $selector = ['image/jpeg'];
|
||||
public $priority = [false, 50];
|
||||
|
||||
protected $targetFile;
|
||||
protected $tempFile;
|
||||
|
||||
|
||||
public static function defaults() {
|
||||
return [
|
||||
'imagekit.jpegtran.bin' => null,
|
||||
'imagekit.jpegtran.optimize' => true,
|
||||
'imagekit.jpegtran.copy' => 'none',
|
||||
'imagekit.jpegtran.flags' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function available() {
|
||||
return !empty(static::$kirby->option('imagekit.jpegtran.bin'));
|
||||
}
|
||||
|
||||
public function post() {
|
||||
|
||||
$tmpFile = $this->getTemporaryFilename();
|
||||
|
||||
$command = [];
|
||||
|
||||
$command[] = static::$kirby->option('imagekit.jpegtran.bin');
|
||||
|
||||
if($this->thumb->options['interlace']) {
|
||||
$command[] = '-progressive';
|
||||
}
|
||||
|
||||
if($this->thumb->options['grayscale']) {
|
||||
$command[] = '-grayscale';
|
||||
}
|
||||
|
||||
// Copy metadata (or not)?
|
||||
if($copy = $this->option('imagekit.jpegtran.copy')) {
|
||||
$command[] = "-copy $copy";
|
||||
}
|
||||
|
||||
if($this->option('imagekit.jpegtran.optimize')) {
|
||||
$command[] = '-optimize';
|
||||
}
|
||||
|
||||
// Write to a temporary file, so we can compare filesizes
|
||||
// after optimization and keep only the smaller file as
|
||||
// jpegtran does not always create smaller files.
|
||||
$command[] = '-outfile "' . $tmpFile . '"';
|
||||
|
||||
$flags = $this->option('imagekit.jpegtran.flags');
|
||||
if(!empty($flags)) {
|
||||
// Add exra flags, if defined by user.
|
||||
$command[] = $flags;
|
||||
}
|
||||
|
||||
$command[] = '"' . $this->thumb->destination->root . '"';
|
||||
|
||||
exec(implode(' ', $command));
|
||||
|
||||
$this->keepSmallestFile($this->thumb->destination->root, $tmpFile);
|
||||
}
|
||||
|
||||
}
|
||||
100
site/OFF_plugins/imagekit/lib/optimizer/mozjpeg.php
Normal file
100
site/OFF_plugins/imagekit/lib/optimizer/mozjpeg.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
|
||||
/**
|
||||
* Uses `mozjpeg` for encoding JPEG files. This often creates
|
||||
* much smaller JPEG files than most other encoders at a
|
||||
* comparable quality.
|
||||
*
|
||||
* See: https://github.com/mozilla/mozjpeg
|
||||
*/
|
||||
class MozJPEG extends Base {
|
||||
|
||||
public static $selector = ['image/jpeg'];
|
||||
public $priority = [100, 5];
|
||||
|
||||
protected $targetFile;
|
||||
protected $tmpFile;
|
||||
|
||||
|
||||
public static function defaults() {
|
||||
return [
|
||||
'imagekit.mozjpeg.bin' => null,
|
||||
'imagekit.mozjpeg.quality' => 85,
|
||||
'imagekit.mozjpeg.flags' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function available() {
|
||||
return !empty(static::$kirby->option('imagekit.mozjpeg.bin'));
|
||||
}
|
||||
|
||||
public function pre() {
|
||||
$this->targetFile = $this->thumb->destination->root;
|
||||
|
||||
if($this->isDriver('im')) {
|
||||
// Instruct imagemagick driver to write out a temporary,
|
||||
// uncrompressed TGA file, so our JPEG will not be
|
||||
// compressed twice. I played around with PNM too,
|
||||
// because it’s much faster as an intermediate format,
|
||||
// but some images got corrupted by mozjpeg.
|
||||
$this->tmpFile = $this->getTemporaryFilename('tga');
|
||||
$this->thumb->destination->root = $this->tmpFile;
|
||||
} else {
|
||||
// If GD driver (or an unknown driver) is active, we
|
||||
// need to encode twice, because SimpleImage can only
|
||||
// save to JPEG, PNG or GIF. As saving a 24-bit
|
||||
// lossless PNG as an intermediate step is too
|
||||
// expensive for large images, we need to encode
|
||||
// as JPEG :-(
|
||||
// This also needs a temporary file, because it seems
|
||||
// that mozjpeg cannot overwrite the it’s file.
|
||||
$this->tmpFile = $this->getTemporaryFilename();
|
||||
$this->thumb->destination->root = $this->tmpFile;
|
||||
$this->thumb->options['quality'] = 99;
|
||||
}
|
||||
}
|
||||
|
||||
public function post() {
|
||||
|
||||
$command = [];
|
||||
|
||||
$command[] = static::$kirby->option('imagekit.mozjpeg.bin');
|
||||
|
||||
// Quality
|
||||
$command[] = '-quality ' . $this->option('imagekit.mozjpeg.quality');
|
||||
|
||||
// Interlace
|
||||
if($this->thumb->options['interlace']) {
|
||||
$command[] = '-progressive';
|
||||
}
|
||||
|
||||
// Grayscale
|
||||
if($this->thumb->options['grayscale']) {
|
||||
$command[] = '-grayscale';
|
||||
}
|
||||
|
||||
// Set output file.
|
||||
$command[] = '-outfile "' . $this->targetFile . '"';
|
||||
|
||||
$flags = $this->option('imagekit.mozjpeg.flags');
|
||||
if(!empty($flags)) {
|
||||
// Add exra flags, if defined by user.
|
||||
$command[] = $flags;
|
||||
}
|
||||
|
||||
// Use tmp file as input.
|
||||
$command[] = '"' . $this->tmpFile . '"';
|
||||
|
||||
exec(implode(' ', $command));
|
||||
// Delete temporary file and restore destination path
|
||||
// on thumb object. This only needs to be done, if
|
||||
// ImageMagick driver is used and the input file was
|
||||
// in PNM format.
|
||||
@unlink($this->tmpFile);
|
||||
$this->thumb->destination->root = $this->targetFile;
|
||||
}
|
||||
|
||||
}
|
||||
64
site/OFF_plugins/imagekit/lib/optimizer/optipng.php
Normal file
64
site/OFF_plugins/imagekit/lib/optimizer/optipng.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
|
||||
/**
|
||||
* Lossless optimization of PNG images using `optipng`.
|
||||
*
|
||||
* See: http://optipng.sourceforge.net/
|
||||
* and: http://optipng.sourceforge.net/pngtech/optipng.html
|
||||
*/
|
||||
class OptiPNG extends Base {
|
||||
|
||||
public static $selector = ['image/png'];
|
||||
public $priority = [false, 50];
|
||||
|
||||
protected $targetFile;
|
||||
protected $tmpFile;
|
||||
|
||||
|
||||
public static function defaults() {
|
||||
return [
|
||||
'imagekit.optipng.bin' => null,
|
||||
'imagekit.optipng.level' => 2,
|
||||
'imagekit.optipng.strip' => 'all',
|
||||
'imagekit.optipng.flags' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function available() {
|
||||
return !empty(static::$kirby->option('imagekit.optipng.bin'));
|
||||
}
|
||||
|
||||
public function post() {
|
||||
|
||||
$command = [];
|
||||
|
||||
$command[] = static::$kirby->option('imagekit.optipng.bin');
|
||||
|
||||
// Optimization Level
|
||||
$level = $this->option('imagekit.optipng.level');
|
||||
if($level !== false) { // Level can be 0, so strict comparison is neccessary
|
||||
$command[] = "-o$level";
|
||||
}
|
||||
|
||||
// Should we strip Metadata?
|
||||
if($strip = $this->option('imagekit.optipng.strip')) {
|
||||
$command[] = "-strip $strip";
|
||||
}
|
||||
|
||||
$flags = $this->option('imagekit.optipng.flags');
|
||||
if(!empty($flags)) {
|
||||
// Add exra flags, if defined by user.
|
||||
$command[] = $flags;
|
||||
}
|
||||
|
||||
// Set file to optimize
|
||||
$command[] = '"' . $this->thumb->destination->root . '"';
|
||||
|
||||
exec(implode(' ', $command));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
127
site/OFF_plugins/imagekit/lib/optimizer/pngquant.php
Normal file
127
site/OFF_plugins/imagekit/lib/optimizer/pngquant.php
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit\Optimizer;
|
||||
|
||||
|
||||
/**
|
||||
* Lossy optimization by using `pngquant` for converting
|
||||
* 24-bit PNGs to an 8-bit palette while preserving the
|
||||
* alpha-channel.
|
||||
*
|
||||
* See: https://pngquant.org/
|
||||
*/
|
||||
class PNGQuant extends Base {
|
||||
|
||||
public static $selector = ['image/png'];
|
||||
public $priority = [100, 5];
|
||||
|
||||
protected $targetFile;
|
||||
protected $tmpFile;
|
||||
|
||||
public static function defaults() {
|
||||
return [
|
||||
'imagekit.pngquant.bin' => null,
|
||||
'imagekit.pngquant.quality' => null,
|
||||
'imagekit.pngquant.speed' => 3,
|
||||
'imagekit.pngquant.posterize' => false,
|
||||
'imagekit.pngquant.colors' => false,
|
||||
'imagekit.pngquant.flags' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function available() {
|
||||
return !empty(static::$kirby->option('imagekit.pngquant.bin'));
|
||||
}
|
||||
|
||||
public function pre() {
|
||||
$this->targetFile = $this->thumb->destination->root;
|
||||
|
||||
if($this->isDriver('im')) {
|
||||
// Instruct imagemagick driver to write out a
|
||||
// temporary, uncrompressed PNM file, so our PNG will
|
||||
// not need to be encoded twice.
|
||||
$this->tmpFile = $this->getTemporaryFilename('pnm');
|
||||
$this->thumb->destination->root = $this->tmpFile;
|
||||
} else {
|
||||
// If driver is anything else but IM, we do not create
|
||||
// a temporary file.
|
||||
$this->tmpFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function post() {
|
||||
|
||||
$command = [];
|
||||
|
||||
$command[] = static::$kirby->option('imagekit.pngquant.bin');
|
||||
|
||||
// Quality
|
||||
$quality = $this->option('imagekit.pngquant.quality');
|
||||
if($quality !== null) {
|
||||
$command[] = "--quality $quality";
|
||||
}
|
||||
|
||||
// Speed
|
||||
if($speed = $this->option('imagekit.pngquant.speed')) {
|
||||
$command[] = "--speed $speed";
|
||||
}
|
||||
|
||||
// Posterize
|
||||
$posterize = $this->option('imagekit.pngquant.posterize');
|
||||
if($posterize !== false) { // need verbose check,
|
||||
// because posterize can have
|
||||
// value of 0
|
||||
$command[] = "--posterize $posterize";
|
||||
}
|
||||
|
||||
$copy = $this->option('imagekit.pngquant.copy');
|
||||
if($copy !== null) {
|
||||
$command[] = "--copy $copy";
|
||||
}
|
||||
|
||||
if(is_null($this->tmpFile)) {
|
||||
// Only save optimized file, if it is smaller than the
|
||||
// original. This only makes sense, if input file a
|
||||
// PNG image. If input is PNM, the result should
|
||||
// always be saved.
|
||||
$command[] = '--skip-if-larger';
|
||||
}
|
||||
|
||||
$flags = $this->option('imagekit.pngquant.flags');
|
||||
if(!empty($flags)) {
|
||||
$command[] = $flags;
|
||||
}
|
||||
|
||||
// Force pngquant to override original file, if it was used
|
||||
// as input (i.e. when no temporary file was created).
|
||||
$command[] = '--force --output "' . $this->targetFile . '"';
|
||||
|
||||
// Colors
|
||||
$colors = $this->option('imagekit.pngquant.colors');
|
||||
if($colors != false) {
|
||||
$command[] = $colors;
|
||||
}
|
||||
|
||||
// Separator between options and input file as
|
||||
// recommended according by the pngquant docs.
|
||||
$command[] = '--';
|
||||
|
||||
if(!is_null($this->tmpFile)) {
|
||||
// Use tmp file as input.
|
||||
$command[] = '"' . $this->tmpFile . '"';
|
||||
} else {
|
||||
// Use target file as input
|
||||
$command[] = '"' . $this->targetFile . '"';
|
||||
}
|
||||
|
||||
exec(implode(' ', $command));
|
||||
|
||||
if(!is_null($this->tmpFile)) {
|
||||
// Delete temporary file and restore destination path on thumb object.
|
||||
@unlink($this->tmpFile);
|
||||
$this->thumb->destination->root = $this->targetFile;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
268
site/OFF_plugins/imagekit/lib/proxyasset.php
Normal file
268
site/OFF_plugins/imagekit/lib/proxyasset.php
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Plugins\ImageKit;
|
||||
|
||||
use A;
|
||||
use Asset;
|
||||
use Dimensions;
|
||||
use Exception;
|
||||
use F;
|
||||
use Kirby;
|
||||
use Media;
|
||||
use Str;
|
||||
use Thumb;
|
||||
use Url;
|
||||
|
||||
|
||||
/**
|
||||
* An extended version of Kirby’s Asset class, which also
|
||||
* works with files that do not exist yet.
|
||||
*/
|
||||
class ProxyAsset extends Asset {
|
||||
|
||||
// Thumb parameters like you would path to the `Thumb`
|
||||
// class’ constructor
|
||||
protected $options = [];
|
||||
|
||||
// Will be changed to true if thumbnail has been generated
|
||||
protected $generated = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Media|string $path
|
||||
*/
|
||||
public function __construct($path) {
|
||||
// This constructor function mimics the behavior of both
|
||||
// Asset’s and Media’s constructors. Because `realpath()`
|
||||
// (used in Media’s constructor) does not work with
|
||||
// non-existing files, the complete constructors of both
|
||||
// classes are redeclared here, using a different function
|
||||
// for resolving paths of
|
||||
// non-existing files. This constructor should always try
|
||||
// to match the behavior of Kirby’s original Asset class.
|
||||
|
||||
// Asset’s constructor
|
||||
$this->kirby = kirby::instance();
|
||||
|
||||
if($path instanceof Media) {
|
||||
// Because “root” of non-existing files does not work,
|
||||
// when they’re initialized as Media objects, we’ll
|
||||
// reconstruct the file’s path from it’s URL.
|
||||
$root = $this->kirby->roots()->index() . str_replace(kirby()->urls->index, '', $path->url());
|
||||
$url = $path->url();
|
||||
} else {
|
||||
$root = url::isAbsolute($path) ? null : $this->kirby->roots()->index() . DS . ltrim($path, DS);
|
||||
$url = url::makeAbsolute($path);
|
||||
}
|
||||
|
||||
// Media’s constructor
|
||||
$this->url = $url;
|
||||
$this->root = $root === null ? $root : static::normalizePath($root);
|
||||
$this->filename = basename($root);
|
||||
$this->name = pathinfo($root, PATHINFO_FILENAME);
|
||||
$this->extension = strtolower(pathinfo($root, PATHINFO_EXTENSION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to normalize a given path by resolving `..`
|
||||
* and `.`. Tries to mimick the behavoir of `realpath()`
|
||||
* which does not work on non-existing files.
|
||||
* Source: http://php.net/manual/de/function.realpath.php#84012
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected static function normalizePath($path) {
|
||||
$path = str_replace(['/', '\\'], DS, $path);
|
||||
$parts = explode(DS, $path);
|
||||
$absolutes = [];
|
||||
foreach($parts as $part) {
|
||||
if('.' === $part) continue;
|
||||
if('..' === $part)
|
||||
array_pop($absolutes);
|
||||
else
|
||||
$absolutes[] = $part;
|
||||
}
|
||||
return implode(DS, $absolutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for transformation parameters
|
||||
*
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
public function options(array $options = null) {
|
||||
if($options === null) {
|
||||
return $this->options;
|
||||
} else {
|
||||
$this->options = $options;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the file if possible. If the
|
||||
* proxy asset has not been generated yet, dimensions are
|
||||
* read from source file and then calculated according to
|
||||
* given thumb options.
|
||||
*
|
||||
* @return Dimensions
|
||||
*/
|
||||
public function dimensions() {
|
||||
|
||||
if($this->generated) {
|
||||
// If the thumbnail has been generated, get dimensions
|
||||
// from thumb file.
|
||||
return parent::dimensions();
|
||||
}
|
||||
|
||||
if(isset($this->cache['dimensions'])) {
|
||||
return $this->cache['dimensions'];
|
||||
}
|
||||
|
||||
if(!$this->original) {
|
||||
throw new Exception('Cannot calculate dimensions of ProxyAsset without a valid original.');
|
||||
}
|
||||
|
||||
if(!is_array($this->options)) {
|
||||
throw new Exception('You have to set transformation options on ProxyAsset before calling `dimensions()`');
|
||||
}
|
||||
|
||||
if(in_array($this->original->mime(), ['image/jpeg', 'image/png', 'image/gif'])) {
|
||||
$size = (array)getimagesize($this->original->root());
|
||||
$width = a::get($size, 0, 0);
|
||||
$height = a::get($size, 1, 0);
|
||||
} else {
|
||||
$width = 0;
|
||||
$height = 0;
|
||||
}
|
||||
|
||||
// Create dimensions object and resize according to thumb options.
|
||||
$dimensions = new Dimensions($width, $height);
|
||||
|
||||
if($this->options['crop']) {
|
||||
$dimensions->crop($this->options['width'], $this->options['height']);
|
||||
} else {
|
||||
$dimensions->fitWidthAndHeight($this->options['width'], $this->options['height'], $this->options['upscale']);
|
||||
}
|
||||
|
||||
return $this->cache['dimensions'] = $dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the actual thumbnail. This function is triggered
|
||||
* by certain methods, which need the final thumbnail to be
|
||||
* there for returning reasonable results.
|
||||
*/
|
||||
public function generate() {
|
||||
|
||||
if($this->generated) return;
|
||||
|
||||
$thumb = new Thumb($this->original, $this->options);
|
||||
|
||||
// Just to be sure to have all corrent data of the
|
||||
// resulting object in place, we override this object’s
|
||||
// properties with those of thumb’s result.
|
||||
$this->reset();
|
||||
foreach(['url', 'root', 'filename', 'name', 'extension', 'content'] as $prop) {
|
||||
$this->$prop = $thumb->result->$prop;
|
||||
}
|
||||
|
||||
$this->generated = true;
|
||||
}
|
||||
|
||||
public function mime() {
|
||||
return f::mime($this->generated ? $this->root : $this->original->root());
|
||||
}
|
||||
|
||||
public function type() {
|
||||
return f::type($this->generated ? $this->root : $this->original->root());
|
||||
}
|
||||
|
||||
public function is($value) {
|
||||
return f::is($this->generated ? $this->root : $this->original->root(), $value);
|
||||
}
|
||||
|
||||
public function read($format = null) {
|
||||
$this->generate();
|
||||
return parent::read($format);
|
||||
}
|
||||
|
||||
public function content($content = null, $format = null) {
|
||||
if(is_null($content)) $this->generate();
|
||||
return parent::content($content, $format);
|
||||
}
|
||||
|
||||
public function move($to) {
|
||||
$this->generate();
|
||||
return parent::move($to);
|
||||
}
|
||||
|
||||
public function copy($to) {
|
||||
$this->generate();
|
||||
return parent::copy($to);
|
||||
}
|
||||
|
||||
public function size() {
|
||||
$this->generate();
|
||||
return parent::size();
|
||||
}
|
||||
|
||||
public function modified($format = null, $handler = 'date') {
|
||||
$this->generate();
|
||||
return parent::modified($format, $handler);
|
||||
}
|
||||
|
||||
public function base64() {
|
||||
$this->generate();
|
||||
return parent::base64();
|
||||
}
|
||||
|
||||
public function exists() {
|
||||
$this->generate();
|
||||
return parent::exists();
|
||||
}
|
||||
|
||||
public function isWritable() {
|
||||
$this->generate();
|
||||
return parent::isWritable();
|
||||
}
|
||||
|
||||
public function isReadable() {
|
||||
$this->generate();
|
||||
return parent::isReadable();
|
||||
}
|
||||
|
||||
public function load($data = array()) {
|
||||
$this->generate();
|
||||
return parent::load($data);
|
||||
}
|
||||
|
||||
public function show() {
|
||||
$this->generate();
|
||||
return parent::show();
|
||||
}
|
||||
|
||||
public function download($filename = null) {
|
||||
$this->generate();
|
||||
return parent::download($filename);
|
||||
}
|
||||
|
||||
public function exif() {
|
||||
$this->generate();
|
||||
return parent::exif();
|
||||
}
|
||||
|
||||
public function imagesize() {
|
||||
$this->generate();
|
||||
return parent::imagesize();
|
||||
}
|
||||
|
||||
// Methods that don’t need to be overloaded:
|
||||
// thumb, resize, crop, width, height, ratio, scale, bw, blur
|
||||
// => thumb() fails automatically, if called on a (Proxy)Asset with original set.
|
||||
// isThumb, isWebsafe
|
||||
// => Don’t need the result file to be in place and work without any further help.
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue