Initial Commit

This commit is contained in:
Werbeagentur AWEOS 2019-09-17 23:14:29 +02:00
commit 6c5d21ec44
12 changed files with 931 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/vendor

212
Plugin.php Normal file
View File

@ -0,0 +1,212 @@
<?php namespace Aweos\Resizer;
use Backend;
use System\Classes\PluginBase;
use System\Classes\MediaLibrary;
use Aweos\Resizer\Models\Setting;
use Aweos\Resizer\Console\ClearOld;
use Aweos\Resizer\Console\ResizeMake;
use Illuminate\Support\Facades\Cache;
use Aweos\Resizer\Console\ResizePurge;
/**
* resizer Plugin Information File
*/
class Plugin extends PluginBase
{
/**
* Returns information about this plugin.
*
* @return array
*/
public function pluginDetails()
{
return [
'name' => 'resizer',
'description' => 'No description provided yet...',
'author' => 'aweos',
'icon' => 'icon-leaf'
];
}
/**
* Register method, called when the plugin is first registered.
*
* @return void
*/
public function register()
{
$this->registerConsoleCommand('resizer.resizemake', ResizeMake::class);
$this->registerConsoleCommand('resizer.resizepurge', ResizePurge::class);
$this->registerConsoleCommand('resizer.clearold', ClearOld::class);
}
/**
* Boot method, called right before the request route.
*
* @return array
*/
public function boot()
{
}
/**
* Registers any front-end components implemented in this plugin.
*
* @return array
*/
public function registerComponents()
{
return []; // Remove this line to activate
return [
'Aweos\Resizer\Components\MyComponent' => 'myComponent',
];
}
/**
* Registers any back-end permissions used by this plugin.
*
* @return array
*/
public function registerPermissions()
{
return []; // Remove this line to activate
return [
'aweos.resizer.some_permission' => [
'tab' => 'resizer',
'label' => 'Some permission'
],
];
}
/**
* Registers back-end navigation items for this plugin.
*
* @return array
*/
public function registerNavigation()
{
return []; // Remove this line to activate
return [
'resizer' => [
'label' => 'resizer',
'url' => Backend::url('aweos/resizer/mycontroller'),
'icon' => 'icon-leaf',
'permissions' => ['aweos.resizer.*'],
'order' => 500,
],
];
}
public function registerSettings() {
return [
'resizer' => [
'label' => 'Resizer Settings',
'description' => 'Change how images are resized and compressed',
'category' => 'Base',
'icon' => 'icon-cog',
'class' => '\Aweos\Resizer\Models\Setting',
'order' => 500,
'keywords' => 'setting',
'permissions' => ['aweos.resizer.*']
]
];
}
public function registerMarkupTags() {
return [
'filters' => [
'resize' => function($media, $breakpoints = [], $name = '') {
return Cache::remember('resized_image.'.$name.$media, 3600, function() use ($media, $breakpoints) {
ksort($breakpoints);
$l = MediaLibrary::instance();
$folders = Setting::get('folders');
$sizes = Setting::get('srcx');
$media = '/'.ltrim($media, '/');
$filename = basename($media);
$dirname = dirname($media);
$extension = pathinfo($media, PATHINFO_EXTENSION);
if (! $l->folderExists($dirname.'/c')) {
return 'src="'.$l->getPathUrl($media).'"';
}
$sizes = collect($l->listFolderContents($dirname.'/c'))
->filter(function($f) use ($sizes, $filename) {
foreach ($sizes as $size) {
if (pathinfo($f->path, PATHINFO_FILENAME).'.'.pathinfo($filename, PATHINFO_EXTENSION)
== pathinfo($filename, PATHINFO_FILENAME).'-'.$size.'t.'.pathinfo($filename, PATHINFO_EXTENSION)) {
return true;
}
if (pathinfo($f->path, PATHINFO_FILENAME).'.'.pathinfo($filename, PATHINFO_EXTENSION)
== pathinfo($filename, PATHINFO_FILENAME).'-'.$size.'.'.pathinfo($filename, PATHINFO_EXTENSION)) {
return true;
}
}
return false;
})
->map(function($fileVersion) use ($l) {
$sizes = getimagesize($l->getPathUrl($fileVersion->path));
return [$l->getPathUrl($fileVersion->path), $sizes[0], $sizes[1]];
})->sortBy(function($size) {
return $size[1];
})
;
if ($sizes->isEmpty()) {
return 'src="'.$l->getPathUrl($media).'"';
}
$srcset = 'srcset="';
$s = '';
foreach($sizes as $size) {
$srcset .= $size[0].' '.$size[1].'w, ';
$s .= '(max-width: '.$size[1].'px) '.$size[1].'px, ';
}
$s = 'sizes="'.substr($s, 0, -2).', 1920px"';
if (!empty($breakpoints)) {
$s = $this->breakpointsToSizes($breakpoints);
}
$srcset = substr($srcset, 0, -2).'"';
$normalSize = 'width="'.$sizes->last()[1].'" height="'.$sizes->last()[2].'"';
return 'src="'.$l->getPathUrl($media).'" '.$srcset.' '.$s.' '.$normalSize;
});
}
]
];
}
public function breakpointsToSizes($breakpoints) {
$s = [];
foreach ($breakpoints as $size => $bp) {
if ($size === 'max') { continue; }
$s[] = '(max-width: '.$size.'px) '.$bp;
}
if (array_key_exists('max', $breakpoints)) {
$s[] = $breakpoints['max'];
}
return 'sizes="'.implode(', ', $s).'"';
}
public function registerSchedule($schedule) {
$schedule->command('resize:make')->dailyAt('01:00');
}
}

13
composer.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "aweos/oc-resizer-plugin",
"type": "october-plugin",
"require": {
"guzzlehttp/guzzle": "^6.3"
},
"authors": [
{
"name": "Werbeagentur AWEOS",
"email": "philipp@aweos.de"
}
]
}

295
composer.lock generated Normal file
View File

@ -0,0 +1,295 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "82331c61df9e7f95117f9b82b35b6ce2",
"packages": [
{
"name": "guzzlehttp/guzzle",
"version": "6.3.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
"reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.4",
"php": ">=5.5"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
"psr/log": "^1.0"
},
"suggest": {
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.3-dev"
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"time": "2018-04-22T15:46:56+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"time": "2016-12-20T10:07:11+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"time": "2019-07-01T23:21:34+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"time": "2019-03-08T08:55:37+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

63
console/ClearOld.php Normal file
View File

@ -0,0 +1,63 @@
<?php namespace Aweos\Resizer\Console;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ClearOld extends Command
{
/**
* @var string The console command name.
*/
protected $name = 'resizer:clearold';
/**
* @var string The console command description.
*/
protected $description = 'No description provided yet...';
/**
* Execute the console command.
* @return void
*/
public function handle()
{
foreach (\Aweos\Base\Models\OfferKitchen::get() as $k) {
$newImages = collect($k->images)->map(function($image) {
if (!strpos($image['image'], 'cropped-images')) {
return $image;
}
$n = preg_replace('/-(\d+-){4}\d+/', '', $image['image']);
$n = str_replace('cropped-images/', '', $n);
if (!\Storage::has('media'.$n)) {
return $image;
}
$image['image'] = $n;
return $image;
});
$k->update(['images' => $newImages]);
}
}
/**
* Get the console command arguments.
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* Get the console command options.
* @return array
*/
protected function getOptions()
{
return [];
}
}

177
console/ResizeMake.php Normal file
View File

@ -0,0 +1,177 @@
<?php namespace Aweos\Resizer\Console;
use Storage;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
use System\Classes\MediaLibrary;
use Aweos\Resizer\Models\Setting;
use GuzzleHttp\Exception\ClientException;
use October\Rain\Database\Attach\Resizer;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
class ResizeMake extends Command
{
public $media = null;
public $http = null;
/**
* @var string The console command name.
*/
protected $name = 'resize:make';
/**
* @var string The console command description.
*/
protected $description = 'Resizes configured images';
public function forAllResizables($callback) {
$folders = Setting::get('folders');
$sizes = Setting::get('srcx');
sort($sizes);
foreach($folders as $folder) {
$this->start($folder['folder'], $sizes, $callback);
}
}
/**
* Execute the console command.
* @return void
*/
public function handle()
{
$this->http = new Client(['base_uri' => 'https://api.tinify.com']);
$this->media = MediaLibrary::instance();
ProgressBar::setFormatDefinition('custom', '%current%/%max% %bar% -- %message% (%size%)');
$tinifyKey = Setting::get('tinify') ? Setting::get('tinypngkey') : null;
$i = 0;
$this->forAllResizables(function($file, $w) use (&$i) {
$i++;
});
$bar = $this->output->createProgressBar($i);
$bar->setFormat('custom');
/**
* @param File $file The current filename
* @param int $w Current width
* @param string $compressed compressed file
* @param string $uncompressed uncompressed file
*/
$this->forAllResizables(function($file, $w, $compressed, $uncompressed) use ($bar, $tinifyKey) {
$bar->setMessage($file->path);
$bar->setMessage($w, 'size');
$bar->advance();
if (Storage::exists($compressed)) {
return;
}
if (!Storage::exists($uncompressed)) {
$r = Resizer::open(Storage::disk('local')->path('media'.$file->path));
$r->resize($w, 0);
$r->save(Storage::disk('local')->path($uncompressed));
$this->info(Storage::url($uncompressed));
}
if (!is_null($tinifyKey)) {
$newUrl = url($this->media->getPathUrl(preg_replace('/^media/', '', $uncompressed)));
try {
$response = $this->http->post('/shrink', [
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Basic '.base64_encode('api:'.$tinifyKey)
],
'json' => [
'source' => ['url' => $newUrl]
]
]);
$output = json_decode((string) $response->getBody());
$url = $output->output->url;
$response = $this->http->get($output->output->url, [
'headers' => [
'Authorization' => 'Basic '.base64_encode('api:'.$tinifyKey)
]
]);
$image = (string) $response->getBody();
Storage::put($compressed, $image);
Storage::delete($uncompressed);
} catch(ClientException $e) {
return;
}
}
$bar->advance();
});
$bar->finish();
}
public function getMediaOfType($f, $type) {
return array_filter($this->media->listFolderContents($f, 'title', 'image'), function($item) use ($f, $type) {
return $item->type == $type && $item->path != $f.'/c';
});
}
public function start($f, $sizes, $callback) {
$f = '/'.ltrim(rtrim($f, '/'), '/');
$folders = $this->getMediaOfType($f, 'folder');
foreach ($folders as $folder) {
$this->start($folder->path, $sizes, $callback);
}
$files = $this->getMediaOfType($f, 'file');
// Create C-Folder as current subfolder if it doesnt exist yet
if(count($files) && !$this->media->folderExists($f.'/c')) {
$this->media->makeFolder($f.'/c');
}
foreach ($files as $file) {
foreach ($sizes as $w) {
$imagesize = getimagesize(Storage::path('media'.$file->path));
$uncompressedFilename = pathinfo($file->path, PATHINFO_FILENAME).'-'.$w.'.'.pathinfo($file->path, PATHINFO_EXTENSION);
$compressedFilename = pathinfo($file->path, PATHINFO_FILENAME).'-'.$w.'t.'.pathinfo($file->path, PATHINFO_EXTENSION);
$compressed = 'media'.dirname($file->path).'/c/'.$compressedFilename;
$uncompressed = 'media'.dirname($file->path).'/c/'.$uncompressedFilename;
if ($imagesize[0] < $w) {
continue;
}
call_user_func($callback, $file, $w, $compressed, $uncompressed);
}
}
}
/**
* Get the console command arguments.
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* Get the console command options.
* @return array
*/
protected function getOptions()
{
return [];
}
}

67
console/ResizePurge.php Normal file
View File

@ -0,0 +1,67 @@
<?php namespace Aweos\Resizer\Console;
use Storage;
use Illuminate\Console\Command;
use System\Classes\MediaLibrary;
use Aweos\Resizer\Models\Setting;
use October\Rain\Database\Attach\Resizer;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ResizePurge extends Command
{
/**
* @var string The console command name.
*/
protected $name = 'resize:purge';
/**
* @var string The console command description.
*/
protected $description = 'Drops all resized images';
/**
* Execute the console command.
* @return void
*/
public function handle()
{
$folders = Setting::get('folders');
$sizes = Setting::get('srcx');
foreach($folders as $folder) {
$f = '/'.rtrim(ltrim($folder['folder'], '/'), '/');
$this->start($f, $sizes);
}
}
public function start($f, $sizes) {
$l = MediaLibrary::instance();
$folders = array_filter(\Storage::allDirectories('media/'.$f), function($item) use ($f) {
return pathinfo($item, PATHINFO_FILENAME) == 'c';
});
foreach ($folders as $folder) {
\Storage::deleteDirectory($folder);
}
}
/**
* Get the console command arguments.
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* Get the console command options.
* @return array
*/
protected function getOptions()
{
return [];
}
}

41
models/Setting.php Normal file
View File

@ -0,0 +1,41 @@
<?php namespace Aweos\Resizer\Models;
use Model;
/**
* Setting Model
*/
class Setting extends Model
{
/**
* @var string The database table used by the model.
*/
public $table = 'aweos_resizer_settings';
public $implement = ['System.Behaviors.SettingsModel'];
public $settingsCode = 'resizer';
public $settingsFields = 'fields.yaml';
/**
* @var array Guarded fields
*/
protected $guarded = ['*'];
/**
* @var array Fillable fields
*/
protected $fillable = [];
/**
* @var array Relations
*/
public $hasOne = [];
public $hasMany = [];
public $belongsTo = [];
public $belongsToMany = [];
public $morphTo = [];
public $morphOne = [];
public $morphMany = [];
public $attachOne = [];
public $attachMany = [];
}

View File

@ -0,0 +1,8 @@
# ===================================
# List Column Definitions
# ===================================
columns:
id:
label: ID
searchable: true

View File

@ -0,0 +1,31 @@
# ===================================
# Form Field Definitions
# ===================================
fields:
folders:
type: repeater
form:
fields:
folder:
type: mediafinder
label: Folder
srcx:
type: taglist
mode: array
label: Source X
srcy:
type: taglist
mode: array
label: Source Y
tinify:
type: checkbox
label: Tinyfy with tinypng
tinypngkey:
type: text
label: Tinypng API Key
trigger:
action: show
field: tinify
condition: checked

View File

@ -0,0 +1,22 @@
<?php namespace Aweos\Resizer\Updates;
use Schema;
use October\Rain\Database\Schema\Blueprint;
use October\Rain\Database\Updates\Migration;
class CreateSettingsTable extends Migration
{
public function up()
{
Schema::create('aweos_resizer_settings', function(Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('aweos_resizer_settings');
}
}

1
updates/version.yaml Normal file
View File

@ -0,0 +1 @@
1.0.1: First version of resizer