Update media resizer

This commit is contained in:
Philipp Lang 2021-09-09 19:18:41 +02:00
parent c8ee7f1514
commit acf891f47a
45 changed files with 330 additions and 11568 deletions

View File

@ -4,16 +4,11 @@ use Aweos\Resizer\Classes\CacheManager;
use Aweos\Resizer\Classes\FileObserver;
use Aweos\Resizer\Classes\ImageResizer;
use Aweos\Resizer\Classes\ResizeJob;
use Aweos\Resizer\Classes\TagGenerator;
use Aweos\Resizer\Console\ClearOld;
use Aweos\Resizer\Console\ResizeMake;
use Aweos\Resizer\Console\ResizePurge;
use Aweos\Resizer\FormWidgets\Responsiveimage;
use Aweos\Resizer\Models\Setting;
use Aweos\Resizer\Lib\MediaPath;
use Event;
use Illuminate\Support\Facades\Cache;
use Queue;
use Storage;
use System\Classes\MediaLibrary;
use System\Classes\PluginBase;
use System\Models\File;
@ -47,7 +42,6 @@ class Plugin extends PluginBase
{
$this->registerConsoleCommand('resizer.resizemake', ResizeMake::class);
$this->registerConsoleCommand('resizer.resizepurge', ResizePurge::class);
$this->registerConsoleCommand('resizer.clearold', ClearOld::class);
}
/**
@ -66,22 +60,22 @@ class Plugin extends PluginBase
});
Event::listen('media.file.upload', function($widget, $filePath, $uploadedFile) {
if (app(FileObserver::class)->shouldProcessFile($filePath)) {
if ((new MediaPath($filePath))->shouldProcess()) {
Queue::push(ResizeJob::class, [$filePath]);
}
});
Event::listen('media.file.delete', function($widget, $filePath) {
app(FileObserver::class)->delete($filePath);
app(CacheManager::class)->delete($filePath);
app(FileObserver::class)->delete(new MediaPath($filePath));
app(CacheManager::class)->delete(new MediaPath($filePath));
});
Event::listen('media.file.move', function($widget, $old, $new) {
app(FileObserver::class)->rename($old, $new);
app(CacheManager::class)->delete($old);
app(FileObserver::class)->rename(new MediaPath($old), new MediaPath($new));
app(CacheManager::class)->delete(new MediaPath($old));
});
Event::listen('media.file.rename', function($widget, $old, $new) {
app(FileObserver::class)->rename($old, $new);
app(CacheManager::class)->delete($old);
app(FileObserver::class)->rename(new MediaPath($old), new MediaPath($new));
app(CacheManager::class)->delete(new MediaPath($old));
});
}
@ -103,34 +97,13 @@ class Plugin extends PluginBase
public function registerMarkupTags() {
return [
'filters' => [
'resize' => fn ($media, $size = 'original', $sizes = null) => app(CacheManager::class)->get($media, $size, $sizes)
'resize' => fn ($media, $size = 'original', $sizes = null) => app(CacheManager::class)->get(
new MediaPath($media),
$size,
$sizes,
)
]
];
}
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');
}
public function registerFormWidgets() {
return [
Responsiveimage::class => 'responsiveimage'
];
}
}

View File

@ -2,47 +2,51 @@
namespace Aweos\Resizer\Classes;
use Aweos\Resizer\Lib\MediaPath;
use Cache;
class CacheManager
{
public FileObserver $fileObserver;
public TagGenerator $tag;
public TagGenerator $tagGenerator;
private string $tagAll = 'resizer';
public function __construct(FileObserver $fileObserver, TagGenerator $tag)
public function __construct(TagGenerator $tagGenerator)
{
$this->tag = $tag;
$this->fileObserver = $fileObserver;
$this->tagGenerator = $tagGenerator;
}
public function get(string $path, string $size, ?string $sizes): string
public function get(MediaPath $path, string $size, ?string $sizes): string
{
return Cache::tags($this->getTag($path, $sizes))
->rememberForever($this->cacheKey($path, $size), fn () => $this->tag->generate($path, $size, $sizes));
return Cache::tags($this->pathTag($path, $size))->rememberForever(
$this->cacheKey($path, $size),
fn () => $this->tagGenerator->generate($path, $size, $sizes)
);
}
public function delete(string $path): void
public function delete(MediaPath $path): void
{
$path = $this->fileObserver->normalizePath($path);
Cache::tags("resizer.$path")->flush();
}
private function getTag(string $path): array
{
return ['resizer', "resizer.{$this->fileObserver->normalizePath($path)}"];
}
private function cacheKey(string $path, string $size): string
{
$normalPath = $this->fileObserver->normalizePath($path);
return "resizer.{$size}.{$normalPath}";
Cache::tags([$this->singlePathTag($path)])->flush();
}
public function flush(): void
{
Cache::tags('resizer')->flush();
Cache::tags([$this->tagAll])->flush();
}
private function cacheKey(MediaPath $path, string $size): string
{
return "resizer.{$size}.{$path->normal()}";
}
private function pathTag(MediaPath $path, string $size): array
{
return [$this->tagAll, $this->singlePathTag($path)];
}
public function singlePathTag(MediaPath $path): string
{
return "resizer.{$path->normal()}";
}
}

View File

@ -1,85 +0,0 @@
<?php
namespace Aweos\Resizer\Classes;
use Storage;
use Aweos\Resizer\Models\Setting;
use October\Rain\Database\Attach\Resizer;
use Aweos\Resizer\Models\Attachment;
class CompressJob {
public $attachment_id;
public $maxFilesize = 1920;
public $disk = 'uploads';
public $fullPath;
public $crop;
public $sizes;
public function fire($job, $data) {
$this->attachment_id = $data['attachment_id'];
$this->attachment = Attachment::find($this->attachment_id);
if (!$this->shouldResize()) {
return;
}
$this->crop = $data['crop'];
$this->sizes = Setting::get('srcx');
Storage::disk($this->disk)->makeDirectory('cropped');
$this->fullPath = Storage::disk($this->disk)->path($this->attachment->source->path);
$this->crop();
$this->createVersions();
}
public function crop() {
if ($this->crop === null) {
return;
}
$r = Resizer::open($this->fullPath);
$r->crop(floor($this->crop['x']), floor($this->crop['y']), floor($this->crop['w']), floor($this->crop['h']));
$r->save($this->fullPath);
}
public function createVersions()
{
[ $width, $height ] = getimagesize($this->fullPath);
Storage::disk($this->disk)->makeDirectory($this->attachment->path);
if ($width > $this->maxFilesize) {
$r = Resizer::open($this->fullPath);
$r->resize($this->maxFilesize, 0);
$r->save($this->fullPath);
}
$r = Resizer::open($this->fullPath);
$r->save(Storage::disk($this->disk)->path($this->attachment->getVersionPath('full')));
[ $width, $height ] = getimagesize($this->fullPath);
foreach ($this->sizes as $w) {
if ($width < $w) {
continue;
}
$r = Resizer::open($this->fullPath);
$r->resize($w, 0);
$r->save(Storage::disk($this->disk)->path($this->attachment->getVersionPath($w)));
}
}
private function getStrategy() {
return app($this->strategy);
}
private function shouldResize() {
return $this->attachment->source->isCroppable();
}
}

View File

@ -2,68 +2,30 @@
namespace Aweos\Resizer\Classes;
use Aweos\Resizer\Models\Setting;
use Aweos\Resizer\Lib\MediaPath;
use Storage;
class FileObserver
{
public function versionsPath(string $mediaPath): string
public function delete(MediaPath $path): void
{
return "uploads/public/c/{$this->normalizePath($mediaPath)}";
}
foreach ($path->versions() as $version) {
Storage::delete($version->get('path'));
}
public function normalizePath(string $path): string
{
return preg_replace('|^/*|', '', $path);
}
public function fullMediaPath(string $path): string
{
return "media/{$this->normalizePath($path)}";
}
public function shouldProcessFile($file): bool
{
return collect(Setting::get('folders'))->pluck('folder')->first(
fn ($folder) => starts_with($file, $folder.'/')
) !== null;
}
public function delete($path): void
{
$dir = pathinfo($path, PATHINFO_DIRNAME);
$base = $dir.'/'.pathinfo($path, PATHINFO_FILENAME);
Storage::delete("uploads/public/c{$path}");
collect(Storage::files("uploads/public/c{$dir}"))->filter(
fn ($file) => preg_match('|'.preg_quote("uploads/public/c{$base}", '|').'-[0-9]+x[0-9]+\.[a-zA-Z]+$|', $file)
)->each(function ($path) {
Storage::delete($path);
});
if (empty(Storage::allFiles("uploads/public/c{$dir}"))) {
Storage::deleteDirectory("uploads/public/c{$dir}");
if (empty(Storage::files($path->versionsDirPath()))) {
Storage::deleteDirectory($path->versionsDirPath());
}
}
public function rename(string $old, string $new): void
public function rename(MediaPath $old, MediaPath $new): void
{
$dir = pathinfo($old, PATHINFO_DIRNAME);
$base = pathinfo($old, PATHINFO_FILENAME);
$newBase = pathinfo($new, PATHINFO_FILENAME);
$newDir = pathinfo($new, PATHINFO_DIRNAME);
foreach (Storage::files("uploads/public/c{$dir}") as $file) {
if (!preg_match_all('|'.preg_quote("uploads/public/c{$dir}/{$base}", '|').'(-[0-9]+x[0-9]+)?(\.[a-zA-Z]+)$|', $file, $matches)) {
continue;
}
$size = $matches[1][0];
$ext = $matches[2][0];
Storage::move($file, "uploads/public/c{$newDir}/{$newBase}{$size}{$ext}");
foreach ($old->versions() as $version) {
Storage::move(
$version->get('path'),
$new->versionsDirPath().'/'.$new->filename().$version->get('size').$version->get('ext')
);
}
}

View File

@ -2,6 +2,7 @@
namespace Aweos\Resizer\Classes;
use Aweos\Resizer\Lib\MediaPath;
use Aweos\Resizer\Models\Setting;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Collection;
@ -16,6 +17,7 @@ class ImageResizer
private FilesystemAdapter $disk;
private string $uploadDir;
private MediaLibrary $media;
private MediaPath $file;
public function __construct(FilesystemAdapter $disk, string $uploadDir, MediaLibrary $media)
{
@ -24,29 +26,19 @@ class ImageResizer
$this->media = $media;
}
public function generate(string $file): void
public function generate(MediaPath $file): void
{
$this->source = app(FileObserver::class)->normalizePath($file);
if (Storage::mimeType(app(FileObserver::class)->fullMediaPath($file)) !== 'image/jpeg') {
return;
$this->file = $file;
$this->disk->put($this->file->versionsPath(), $this->file->get());
if ($this->file->compressor()->shouldGenerateVersions()) {
$this->generateVersions();
}
$this->copyOriginalImage();
$this->generateVersions();
}
public function copyOriginalImage()
{
$this->disk->put($this->destinationFilename(), $this->media->get($this->source));
}
private function destinationFilename(): string
{
return $this->uploadDir.$this->source;
}
private function dimensions(): Collection
{
[$width, $height] = getimagesize(Storage::path($this->destinationFilename()));
[$width, $height] = getimagesize($this->file->root());
return collect(compact('width', 'height'));
}
@ -85,24 +77,21 @@ class ImageResizer
{
foreach ($this->possibleSizes() as $size) {
$temp = microtime().'.jpg';
$this->disk->copy($this->destinationFilename(), $temp);
$fullOriginalPath = $this->disk->path($this->destinationFilename());
$pathinfo = collect(pathinfo($this->destinationFilename()));
$r = app(ImageManager::class)->make($this->disk->path($temp))
$r = app(ImageManager::class)->make($this->file->root())
->fit($size->get('width'), $size->get('height'), fn ($constraint) => $constraint->upsize())
->save($this->disk->path($temp));
list($destWidth, $destHeight) = getimagesize($this->disk->path($temp));
$versionFilename = $pathinfo->get('dirname').
$versionFilename = $this->file->versionsDirPath().
'/'.
$pathinfo->get('filename').
$this->file->filename().
'-'.
$destWidth.
'x'.
$destHeight.
'.'.
$pathinfo->get('extension');
$this->file->extension();
if ($this->disk->exists($versionFilename)) {
$this->disk->delete($versionFilename);
@ -110,6 +99,10 @@ class ImageResizer
$this->disk->move($temp, $versionFilename);
}
foreach ($this->file->versions() as $version) {
$this->file->compressor()->make($this->disk->path($version->get('path')));
}
}
}

View File

@ -2,12 +2,14 @@
namespace Aweos\Resizer\Classes;
use Aweos\Resizer\Lib\MediaPath;
class ResizeJob
{
public function fire($job, $params)
{
list($file) = $params;
app(ImageResizer::class)->generate($file);
app(ImageResizer::class)->generate(new MediaPath($file));
}
}

View File

@ -2,7 +2,9 @@
namespace Aweos\Resizer\Classes;
use Aweos\Resizer\Models\Attachment;
use Aweos\Resizer\Compressors\CompressorNotFoundException;
use Aweos\Resizer\Compressors\Factory as CompressorFactory;
use Aweos\Resizer\Lib\MediaPath;
use Aweos\Resizer\Models\Setting;
use Cache;
use Illuminate\Support\Collection;
@ -14,9 +16,11 @@ class TagGenerator {
public MediaLibrary $media;
public array $breakpoints;
public $path;
private CompressorFactory $compressorFactory;
public function __construct()
public function __construct(CompressorFactory $compressorFactory)
{
$this->compressorFactory = $compressorFactory;
$this->media = MediaLibrary::instance();
$this->breakpoints = Setting::get('breakpoints');
ksort($this->breakpoints);
@ -32,34 +36,24 @@ class TagGenerator {
public function possibleFiles(string $ratio): ?Collection
{
if (Storage::mimeType(app(FileObserver::class)->fullMediaPath($this->path)) !== 'image/jpeg') {
return null;
}
$filename = pathinfo($this->path, PATHINFO_FILENAME);
$basePath = app(FileObserver::class)->versionsPath(pathinfo($this->path, PATHINFO_DIRNAME));
[$originalWidth, $originalHeight] = getimagesize(Storage::path(app(FileObserver::class)->fullMediaPath($this->path)));
$filename = $this->path->filename();
$basePath = $this->path->versionsDirPath();
[$originalWidth, $originalHeight] = getimagesize($this->path->root());
$aspectRatio = $ratio === 'original'
? $originalWidth / $originalHeight
: $this->size($ratio)[0] / $this->size($ratio)[1];
$result = collect([]);
foreach (Storage::files($basePath) as $file) {
if (!preg_match('|'.preg_quote($basePath.'/'.$filename, '|').'-([0-9]+x[0-9]+)\.([a-zA-Z]+)$|', $file, $matches)) {
continue;
}
$width = explode('x', $matches[1])[0];
$height = explode('x', $matches[1])[1];
if ($width / ($height+1) > $aspectRatio || $width / ($height-1) < $aspectRatio) {
foreach ($this->path->versions() as $version) {
if ($version->get('width') / ($version->get('height')+1) > $aspectRatio || $version->get('width') / ($version->get('height')-1) < $aspectRatio) {
continue;
}
$result->push(collect([
'url' => Storage::url($file),
'width' => $width,
'height' => $height,
'url' => $version->get('url'),
'width' => $version->get('width'),
'height' => $version->get('height'),
]));
}
@ -68,13 +62,22 @@ class TagGenerator {
: null;
}
public function generate(string $path, ?string $ratio = 'original', $sizes = null): string
public function generate(MediaPath $path, ?string $ratio = 'original', $sizes = null): string
{
$this->path = $path;
try {
if (!$path->compressor()->shouldGenerateVersions()) {
return $this->fallback();
}
} catch (CompressorNotFoundException $e) {
return $this->fallback();
}
$files = $this->possibleFiles($ratio);
if ($files === null) {
return 'src="'.$this->media->findFiles($path)[0]->publicUrl.'"';
return $this->fallback();
}
$sizes = $this->parseSizes($files, $sizes);
@ -115,4 +118,9 @@ class TagGenerator {
return "(min-width: {$components[0]}px) {$components[1]}";
})->push($minSize);
}
private function fallback(): string
{
return 'src="'.$this->path->publicUrl().'"';
}
}

View File

@ -1,90 +0,0 @@
<?php
namespace Aweos\Resizer\Classes;
use Queue;
use Storage;
use Aweos\Resizer\Models\SourceFile;
use Aweos\Resizer\Models\Attachment;
class UploadStorage {
public $disk = 'uploads';
public $file;
public $data;
public $strategy = FirstLetterStrategy::class;
public function storeFileFromUpload($uploadedFile, $data) {
$this->file = $uploadedFile;
$this->data = $data;
$sourceFile = new SourceFile([
'basename' => pathinfo($this->file->getClientOriginalName(), PATHINFO_FILENAME),
'extension' => $this->file->getClientOriginalExtension(),
'tags' => [],
'aspect_ratio' => $this->data['aspectRatio'],
'min_width' => $this->data['minWidth']
]);
$sourceFile->slugAttributes();
$uploadedFile->storeAs('', $sourceFile->path, $this->disk);
$sourceFile->save();
return $this->storeAttachment($sourceFile, [
'title' => $this->data['title'],
'description' => $this->data['description'] ?? '',
'crop' => $this->data['crop']
]);
}
public function storeAttachment($sourceFile, $data) {
$attachment = $sourceFile->attachments()->create([
'title' => $data['title'],
'description' => $data['description']
]);
Queue::push(CompressJob::class, [
'attachment_id' => $attachment->id,
'crop' => $data['crop']
]);
return $attachment;
}
public function getFileData($id) {
$attachment = Attachment::find($id);
return $attachment ? [
'url' => $this->storage()->url($attachment->source->path),
'type' => $this->storage()->mimeType($attachment->source->path),
'size' => $this->storage()->size($attachment->source->path),
'name' => $attachment->slug,
'id' => $id
] : null;
}
public function removeAttachment($id) {
$attachment = Attachment::find($id);
collect(Storage::disk($this->disk)->files($attachment->path))->filter(function($file) use ($attachment) {
return preg_match($attachment->versionRegex, $file);
})->each(function($file) {
Storage::disk($this->disk)->delete($file);
});
$attachment->delete();
}
private function storage() {
return Storage::disk($this->disk);
}
private function originalExtension() {
return $this->file->getClientOriginalExtension();
}
private function getStrategy() {
return app($this->strategy);
}
}

View File

@ -10,5 +10,10 @@
"name": "Werbeagentur AWEOS",
"email": "philipp@aweos.de"
}
]
],
"autoload": {
"psr-4": {
"Aweos\\Resizer\\Compilers\\": "./compilers"
}
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Aweos\Resizer\Compressors;
abstract class Compressor
{
abstract function make(string $path): array;
public function tmpPath(): string
{
return "/tmp/".str_slug(microtime());
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Aweos\Resizer\Compressors;
use Exception;
class CompressorNotFoundException extends Exception
{
}

View File

@ -0,0 +1,17 @@
<?php
namespace Aweos\Resizer\Compressors;
class DefaultCompressor extends Compressor {
public function make(string $path): array
{
return [];
}
public function shouldGenerateVersions(): bool
{
return false;
}
}

38
compressors/Factory.php Normal file
View File

@ -0,0 +1,38 @@
<?php
namespace Aweos\Resizer\Compressors;
use Aweos\Resizer\Lib\MediaPath;
class Factory
{
private string $default = DefaultCompressor::class;
public array $types = [
'image/jpeg' => JpgCompressor::class,
];
public function fromMedia(string $mediaPath): Compressor
{
return $this->resolve(new MediaPath($mediaPath));
}
public function resolve(MediaPath $path): Compressor
{
$mime = mime_content_type($path->root());
$compiler = $this->resolveType($mime);
if (is_null($compiler)) {
return new $this->default($path);
}
return new $compiler($path);
}
private function resolveType(string $type): ?string
{
return collect($this->types)->get($type);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Aweos\Resizer\Compressors;
class JpgCompressor extends Compressor {
public function make(string $path): array
{
$output = "/tmp/".str_slug(microtime());
$mimetype = mime_content_type($path);
system('imagemin '.escapeshellarg($path).' --plugin=jpegtran --plugin=mozjpeg --plugin.mozjpeg.quality=70 > '.escapeshellarg($output));
system("mv ".escapeshellarg($output)." ".escapeshellarg($path));
return [
$path => [$path],
];
}
public function shouldGenerateVersions(): bool
{
return true;
}
}

View File

@ -1,63 +0,0 @@
<?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 [];
}
}

View File

@ -1,12 +1,7 @@
<?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;
use Storage;
class ResizePurge extends Command
{
@ -26,25 +21,7 @@ class ResizePurge extends Command
*/
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);
}
Storage::deleteDirectory('uploads/public/c');
}
/**

View File

@ -1,183 +0,0 @@
<?php namespace Aweos\Resizer\FormWidgets;
use Db;
use Input;
use Request;
use Response;
use Validator;
use Backend\Widgets\Form;
use Backend\Classes\FormField;
use Backend\Classes\FormWidgetBase;
use October\Rain\Filesystem\Definitions as FileDefinitions;
use ApplicationException;
use ValidationException;
use Exception;
use Aweos\Resizer\Traits\ResponsiveSaver;
use Aweos\Resizer\Traits\ResponsiveWidget;
use Aweos\Resizer\Classes\UploadStorage;
use Aweos\Resizer\Models\SourceFile;
/**
* File upload field
* Renders a form file uploader field.
*
* Supported options:
* - mode: image-single, image-multi, file-single, file-multi
* - upload-label: Add file
* - empty-label: No file uploaded
*
* @package october\backend
* @author Alexey Bobkov, Samuel Georges
*/
class Responsiveimage extends FormWidgetBase
{
public $minWidth;
public $aspectRatio;
protected $defaultAlias = 'responsiveimage';
protected $configFormWidget;
/**
* @inheritDoc
*/
public function init()
{
$this->fillFromConfig([
'minWidth',
'aspectRatio'
]);
/*
if ($this->formField->disabled) {
$this->previewMode = true;
}
//$this->getConfigFormWidget();
*/
}
/**
* @inheritDoc
*/
public function render()
{
$this->prepareVars();
return $this->makePartial('responsiveimage');
}
/**
* Prepares the view data
*/
protected function prepareVars()
{
$this->vars['aspectRatio'] = $this->aspectRatio
? $this->vueParam(explode(' / ', $this->aspectRatio))
: 'null';
$this->vars['minWidth'] = $this->minWidth ?: '0';
$this->vars['name'] = $this->formField->getName();
$this->vars['value'] = is_numeric($this->getLoadValue()) ? $this->getLoadValue() : 'null';
$this->vars['meta'] = is_numeric($this->getLoadValue())
? $this->vueParam(app(UploadStorage::class)->getFileData($this->getLoadValue()))
: 'null';
$this->vars['model'] = $this->model;
$this->vars['croppableTypes'] = $this->vueParam(SourceFile::$croppableTypes);
}
private function vueParam($config) {
return str_replace('"', "'", json_encode($config));
}
/**
* @inheritDoc
*/
protected function loadAssets()
{
$this->addCss('css/responsiveimage.build.css', 'core');
$this->addJs('js/responsiveimage.build.js', 'core');
}
/**
* @inheritDoc
*/
public function getSaveValue($value)
{
return is_numeric($value) ? $value : null;
}
public function getLoadValue() {
return is_numeric(parent::getLoadValue()) ? parent::getLoadValue() : '';
}
/**
* Upload handler for the server-side processing of uploaded files
*/
public function onUpload()
{
if (!Input::hasFile('file_data')) {
throw new ApplicationException('File missing from request');
}
$data = json_decode(Input::get('extraData'), true);
$uploadedFile = Input::file('file_data');
if (!$uploadedFile->isValid()) {
throw new ApplicationException('File is not valid');
}
$attachment = app(UploadStorage::class)->storeFileFromUpload($uploadedFile, $data);
return Response::make($attachment->id);
}
public function onDelete() {
app(UploadStorage::class)->removeAttachment(Input::get('file_id'));
}
public function onOpenDatabase() {
$files = (new SourceFile)->newQuery();
if ($aspectRatio = Input::get('databasePopup.crop.aspectRatio', null)) {
$files->where('aspect_ratio', json_encode($aspectRatio));
}
if ($minWidth = Input::get('databasePopup.crop.minWidth', null)) {
$files->where('min_width', '>=', $minWidth);
}
return $this->makePartial('database', [
'images' => $files->get()
]);
}
public function onSelectDatabase() {
$v = Validator::make(Input::get(), [
'image' => 'required|exists:responsive_source_files,id',
'title' => 'required'
], [
'image.required' => 'Bitte ein Bild auswählen',
'title.required' => 'Bitte einen Titel angeben'
]);
if ($v->fails()) {
throw new ValidationException($v);
}
$attachment = app(UploadStorage::class)->storeAttachment(SourceFile::find(Input::get('image')), [
'title' => Input::get('title', ''),
'description' => Input::get('description', ''),
'crop' => null
]);
return Response::json([
'id' => $attachment->id,
'url' => $attachment->url,
'name' => $attachment->slug,
'type' => $attachment->source->type,
'size' => $attachment->source->size
]);
}
}

View File

@ -1,253 +0,0 @@
.field-responsiveimage .upload-object {-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;position:relative;outline:none;overflow:hidden;display:inline-block;vertical-align:top}
.field-responsiveimage .upload-object img {width:100%;height:100%}
.field-responsiveimage .upload-object .icon-container {display:table;opacity:.6}
.field-responsiveimage .upload-object .icon-container i {color:#95a5a6;display:inline-block}
.field-responsiveimage .upload-object .icon-container div {display:table-cell;text-align:center;vertical-align:middle}
.field-responsiveimage .upload-object .icon-container.image >div.icon-wrapper {display:none}
.field-responsiveimage .upload-object h4 {font-size:13px;color:#2A3E51;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:150%;margin:15px 0 5px 0;padding-right:0;-webkit-transition:padding 0.1s;transition:padding 0.1s;position:relative}
.field-responsiveimage .upload-object h4 a {position:absolute;right:0;top:0;display:none;font-weight:400}
.field-responsiveimage .upload-object p.size {font-size:12px;color:#95a5a6}
.field-responsiveimage .upload-object p.size strong {font-weight:400}
.field-responsiveimage .upload-object .meta .drag-handle {position:absolute;bottom:0;right:0;cursor:move;display:block}
.field-responsiveimage .upload-object .info h4 a,
.field-responsiveimage .upload-object .meta a.upload-remove-button,
.field-responsiveimage .upload-object .meta a.drag-handle {color:#2b3e50;display:none;font-size:13px;text-decoration:none}
.field-responsiveimage .upload-object .icon-container {position:relative}
.field-responsiveimage .upload-object .icon-container:after {background-image:url('../../../../../../../modules/system/assets/ui/images/loader-transparent.svg');position:absolute;content:' ';width:40px;height:40px;left:50%;top:50%;margin-top:-20px;margin-left:-20px;display:block;background-size:40px 40px;background-position:50% 50%;-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}
.field-responsiveimage .upload-object.is-success .icon-container {opacity:1}
.field-responsiveimage .upload-object.is-success .icon-container:after {opacity:0;-webkit-transition:opacity 0.3s ease;transition:opacity 0.3s ease}
.field-responsiveimage .upload-object.is-error .icon-container:after {content:"";background:none;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f071";-webkit-animation:none;animation:none;font-size:40px;color:#ab2a1c;margin-top:-20px;margin-left:-20px;text-shadow:2px 2px 0 #fff}
.field-responsiveimage .upload-object.is-loading .icon-container {opacity:.6}
.field-responsiveimage .upload-object.is-loading .icon-container:after {opacity:1;-webkit-transition:opacity 0.3s ease;transition:opacity 0.3s ease}
.field-responsiveimage .upload-object.is-success {cursor:pointer}
.field-responsiveimage .upload-object.is-success .progress-bar {opacity:0;-webkit-transition:opacity 0.3s ease;transition:opacity 0.3s ease}
.field-responsiveimage .upload-object.is-success:hover h4 a,
.field-responsiveimage .upload-object.is-success:hover .meta .upload-remove-button,
.field-responsiveimage .upload-object.is-success:hover .meta .drag-handle {display:block}
.field-responsiveimage .upload-object.is-error {cursor:pointer}
.field-responsiveimage .upload-object.is-error .icon-container {opacity:1}
.field-responsiveimage .upload-object.is-error .icon-container >img,
.field-responsiveimage .upload-object.is-error .icon-container >i {opacity:.5}
.field-responsiveimage .upload-object.is-error .info h4 {color:#ab2a1c}
.field-responsiveimage .upload-object.is-error .info h4 a {display:none}
.field-responsiveimage .upload-object.is-error .meta {display:none}
.field-responsiveimage.is-sortable {position:relative}
.field-responsiveimage.is-sortable .upload-placeholder {position:relative;border:1px dotted #e0e0e0 !important}
.field-responsiveimage.is-sortable .upload-object.dragged {position:absolute;opacity:0.5;filter:alpha(opacity=50);z-index:2000}
.field-responsiveimage.is-sortable .upload-object.dragged .uploader-toolbar {display:none}
.field-responsiveimage.is-preview .upload-button,
.field-responsiveimage.is-preview .upload-remove-button,
.field-responsiveimage.is-preview .meta a.drag-handle {display:none !important}
@media (max-width:1024px) {.field-responsiveimage .upload-object.is-success h4 a,.field-responsiveimage .upload-object.is-success .meta .upload-remove-button,.field-responsiveimage .upload-object.is-success .meta .drag-handle {display:block !important }}
.fileupload-config-form .fileupload-url-button {padding-left:0}
.fileupload-config-form .fileupload-url-button >i {color:#666}
.fileupload-config-form .file-upload-modal-image-header {background-color:#FEFEFE;background-image:-webkit-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb),-webkit-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb);background-image:-moz-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb),-moz-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb);background-image:-o-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb),-o-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb);background-image:-ms-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb),-ms-linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb);background-image:linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb),linear-gradient(45deg,#cbcbcb 25%,transparent 25%,transparent 75%,#cbcbcb 75%,#cbcbcb);-webkit-background-size:20px 20px;-moz-background-size:20px 20px;background-size:20px 20px;background-position:0 0,10px 10px}
.fileupload-config-form .file-upload-modal-image-header,
.fileupload-config-form .file-upload-modal-image-header img {border-top-right-radius:2px;border-top-left-radius:2px}
.fileupload-config-form .file-upload-modal-image-header .close {position:absolute;top:20px;right:20px;background:#BDC3C7;opacity:.7;height:24px;width:22px}
.fileupload-config-form .file-upload-modal-image-header .close:hover,
.fileupload-config-form .file-upload-modal-image-header .close:focus {opacity:.9}
.fileupload-config-form .file-upload-modal-image-header + .modal-body {padding-top:20px}
.field-responsiveimage.style-image-multi .upload-button,
.field-responsiveimage.style-image-multi .upload-object {margin:0 10px 10px 0}
.field-responsiveimage.style-image-multi .upload-button {display:block;border:2px dashed #BDC3C7;background-clip:content-box;background-color:#F9F9F9;position:relative;outline:none;float:left;width:76px;height:76px}
.field-responsiveimage.style-image-multi .upload-button .upload-button-icon {position:absolute;width:22px;height:22px;top:50%;left:50%;margin-top:-11px;margin-left:-11px}
.field-responsiveimage.style-image-multi .upload-button .upload-button-icon:before {text-align:center;display:block;font-size:22px;height:22px;width:22px;line-height:22px;color:#BDC3C7}
.field-responsiveimage.style-image-multi .upload-button .upload-button-icon.large-icon {width:34px;height:34px;top:50%;left:50%;margin-top:-17px;margin-left:-17px}
.field-responsiveimage.style-image-multi .upload-button .upload-button-icon.large-icon:before {font-size:34px;height:24px;width:24px;line-height:24px}
.field-responsiveimage.style-image-multi .upload-button:hover {border:2px dashed #1F99DC}
.field-responsiveimage.style-image-multi .upload-button:hover .upload-button-icon:before {color:#1F99DC}
.field-responsiveimage.style-image-multi .upload-button:focus {border:2px dashed #1F99DC}
.field-responsiveimage.style-image-multi .upload-button:focus .upload-button-icon:before {color:#1F99DC}
.field-responsiveimage.style-image-multi .upload-files-container {margin-left:90px}
.field-responsiveimage.style-image-multi .upload-object {background:#fff;border:1px solid #ecf0f1;width:260px}
.field-responsiveimage.style-image-multi .upload-object .progress-bar {display:block;width:100%;overflow:hidden;height:5px;background-color:#f5f5f5;border-radius:3px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);position:absolute;bottom:10px;left:0}
.field-responsiveimage.style-image-multi .upload-object .progress-bar .upload-progress {float:left;width:0%;height:100%;line-height:5px;color:#fff;background-color:#5fb6f5;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}
.field-responsiveimage.style-image-multi .upload-object .icon-container {border-right:1px solid #f6f8f9;float:left;display:inline-block;overflow:hidden;width:75px;height:75px}
.field-responsiveimage.style-image-multi .upload-object .icon-container i {font-size:35px}
.field-responsiveimage.style-image-multi .upload-object .icon-container.image img {border-bottom-left-radius:3px;border-top-left-radius:3px;width:auto}
.field-responsiveimage.style-image-multi .upload-object .info {margin-left:90px}
.field-responsiveimage.style-image-multi .upload-object .info h4 {padding-right:15px}
.field-responsiveimage.style-image-multi .upload-object .info h4 a {right:15px}
.field-responsiveimage.style-image-multi .upload-object .meta {position:absolute;bottom:0;left:0;right:0;margin:0 15px 0 90px}
.field-responsiveimage.style-image-multi .upload-object .meta a.drag-handle {bottom:15px}
.field-responsiveimage.style-image-multi .upload-object.upload-placeholder {height:75px;background-color:transparent}
.field-responsiveimage.style-image-multi .upload-object.upload-placeholder:after {opacity:0}
.field-responsiveimage.style-image-multi .upload-object:hover {background:#4da7e8 !important}
.field-responsiveimage.style-image-multi .upload-object:hover i,
.field-responsiveimage.style-image-multi .upload-object:hover p.size {color:#ecf0f1}
.field-responsiveimage.style-image-multi .upload-object:hover h4 {color:white}
.field-responsiveimage.style-image-multi .upload-object:hover .icon-container {border-right-color:#4da7e8 !important}
.field-responsiveimage.style-image-multi .upload-object:hover h4 {padding-right:35px}
.field-responsiveimage.style-image-multi.is-preview .upload-files-container {margin-left:0}
.form-sidebar .field-responsiveimage.style-image-multi .upload-files-container {margin-left:0}
.form-sidebar .field-responsiveimage.style-image-multi .upload-button {width:100%}
@media (max-width:1280px) {.field-responsiveimage.style-image-multi .upload-object {width:230px }}
@media (max-width:1024px) {.field-responsiveimage.style-image-multi .upload-button {width:100% }.field-responsiveimage.style-image-multi .upload-files-container {margin-left:0 }.field-responsiveimage.style-image-multi .upload-object {margin-right:0;display:block;width:auto }}
.field-responsiveimage.style-image-single.is-populated .upload-button {display:none}
.field-responsiveimage.style-image-single .upload-button {display:block;border:2px dashed #BDC3C7;background-clip:content-box;background-color:#F9F9F9;position:relative;outline:none;min-height:100px;min-width:100px}
.field-responsiveimage.style-image-single .upload-button .upload-button-icon {position:absolute;width:22px;height:22px;top:50%;left:50%;margin-top:-11px;margin-left:-11px}
.field-responsiveimage.style-image-single .upload-button .upload-button-icon:before {text-align:center;display:block;font-size:22px;height:22px;width:22px;line-height:22px;color:#BDC3C7}
.field-responsiveimage.style-image-single .upload-button .upload-button-icon.large-icon {width:34px;height:34px;top:50%;left:50%;margin-top:-17px;margin-left:-17px}
.field-responsiveimage.style-image-single .upload-button .upload-button-icon.large-icon:before {font-size:34px;height:24px;width:24px;line-height:24px}
.field-responsiveimage.style-image-single .upload-button:hover {border:2px dashed #1F99DC}
.field-responsiveimage.style-image-single .upload-button:hover .upload-button-icon:before {color:#1F99DC}
.field-responsiveimage.style-image-single .upload-button:focus {border:2px dashed #1F99DC}
.field-responsiveimage.style-image-single .upload-button:focus .upload-button-icon:before {color:#1F99DC}
.field-responsiveimage.style-image-single .upload-object {padding-bottom:66px}
.field-responsiveimage.style-image-single .upload-object .icon-container {border:1px solid #f6f8f9;background:rgba(255,255,255,0.5)}
.field-responsiveimage.style-image-single .upload-object .icon-container.image img {-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;display:block;max-width:100%;height:auto;min-height:100px;min-width:100px}
.field-responsiveimage.style-image-single .upload-object .progress-bar {display:block;width:100%;overflow:hidden;height:5px;background-color:#f5f5f5;border-radius:3px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);position:absolute;bottom:10px;left:0}
.field-responsiveimage.style-image-single .upload-object .progress-bar .upload-progress {float:left;width:0%;height:100%;line-height:5px;color:#fff;background-color:#5fb6f5;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}
.field-responsiveimage.style-image-single .upload-object .info {position:absolute;left:0;right:0;bottom:0;height:66px}
.field-responsiveimage.style-image-single .upload-object .meta {position:absolute;bottom:65px;left:0;right:0;margin:0 15px}
.field-responsiveimage.style-image-single .upload-object:hover h4 {padding-right:20px}
@media (max-width:1024px) {.field-responsiveimage.style-image-single .upload-object h4 {padding-right:20px !important }}
.field-responsiveimage.style-file-multi .upload-button {margin-bottom:10px}
.field-responsiveimage.style-file-multi .upload-files-container {border:1px solid #eee;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;border-bottom:none;display:none}
.field-responsiveimage.style-file-multi.is-populated .upload-files-container {display:block}
.field-responsiveimage.style-file-multi .upload-object {display:block;width:100%;border-bottom:1px solid #eee;padding-left:10px}
.field-responsiveimage.style-file-multi .upload-object:nth-child(even) {background-color:#f5f5f5}
.field-responsiveimage.style-file-multi .upload-object .icon-container {position:absolute;top:0;left:10px;width:15px;padding:11px 7px}
.field-responsiveimage.style-file-multi .upload-object .icon-container i {line-height:150%;font-size:15px}
.field-responsiveimage.style-file-multi .upload-object .icon-container img {display:none}
.field-responsiveimage.style-file-multi .upload-object .info {margin-left:35px;margin-right:15%}
.field-responsiveimage.style-file-multi .upload-object .info h4,
.field-responsiveimage.style-file-multi .upload-object .info p {margin:0;padding:11px 0;font-size:12px;font-weight:normal;line-height:150%;color:#666}
.field-responsiveimage.style-file-multi .upload-object .info h4 {padding-right:15px}
.field-responsiveimage.style-file-multi .upload-object .info h4 a {padding:10px 0;right:15px}
.field-responsiveimage.style-file-multi .upload-object .info p.size {position:absolute;top:0;right:0;width:15%;display:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.field-responsiveimage.style-file-multi .upload-object .progress-bar {display:block;width:100%;overflow:hidden;height:5px;background-color:#f5f5f5;border-radius:3px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);position:absolute;top:18px;left:0}
.field-responsiveimage.style-file-multi .upload-object .progress-bar .upload-progress {float:left;width:0%;height:100%;line-height:5px;color:#fff;background-color:#5fb6f5;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}
.field-responsiveimage.style-file-multi .upload-object .meta {position:absolute;top:0;right:0;margin-right:15px;width:15%}
.field-responsiveimage.style-file-multi .upload-object .meta a.drag-handle {top:-2px;bottom:auto;line-height:150%;padding:10px 0}
.field-responsiveimage.style-file-multi .upload-object .icon-container:after {width:20px;height:20px;margin-top:-10px;margin-left:-10px;background-size:20px 20px}
.field-responsiveimage.style-file-multi .upload-object.is-error .icon-container:after {font-size:20px}
.field-responsiveimage.style-file-multi .upload-object.is-success .info p.size {display:block}
.field-responsiveimage.style-file-multi .upload-object.upload-placeholder {height:35px;background-color:transparent}
.field-responsiveimage.style-file-multi .upload-object.upload-placeholder:after {opacity:0}
.field-responsiveimage.style-file-multi .upload-object:hover {background:#4da7e8 !important}
.field-responsiveimage.style-file-multi .upload-object:hover i,
.field-responsiveimage.style-file-multi .upload-object:hover p.size {color:#ecf0f1}
.field-responsiveimage.style-file-multi .upload-object:hover h4 {color:white}
.field-responsiveimage.style-file-multi .upload-object:hover .icon-container {border-right-color:#4da7e8 !important}
.field-responsiveimage.style-file-multi .upload-object:hover h4 {padding-right:35px}
@media (max-width:1199px) {.field-responsiveimage.style-file-multi .info {margin-right:20% !important }.field-responsiveimage.style-file-multi .info p.size {width:20% !important }.field-responsiveimage.style-file-multi .meta {width:20% !important }}
@media (max-width:991px) {.field-responsiveimage.style-file-multi .upload-object h4 {padding-right:35px !important }.field-responsiveimage.style-file-multi .info {margin-right:25% !important }.field-responsiveimage.style-file-multi .info p.size {width:25% !important;padding-right:35px !important }.field-responsiveimage.style-file-multi .meta {width:25% !important }}
.field-responsiveimage.style-file-single {background-color:#fff;border:1px solid #d1d6d9;overflow:hidden;position:relative;padding-right:30px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5)}
.field-responsiveimage.style-file-single .upload-button {position:absolute;top:50%;margin-top:-44px;height:88px;background:transparent;right:-2px;color:#595959}
.field-responsiveimage.style-file-single .upload-button i {font-size:14px}
.field-responsiveimage.style-file-single .upload-button:hover {color:#333}
.field-responsiveimage.style-file-single .upload-empty-message {padding:8px 0 8px 11px;font-size:14px}
.field-responsiveimage.style-file-single.is-populated .upload-empty-message {display:none}
.field-responsiveimage.style-file-single .upload-object {display:block;width:100%;padding:7px 0 9px 0}
.field-responsiveimage.style-file-single .upload-object .icon-container {position:absolute;top:0;left:0;width:15px;padding:0 5px;margin:8px 0 0 7px;text-align:center}
.field-responsiveimage.style-file-single .upload-object .icon-container i {line-height:150%;font-size:15px}
.field-responsiveimage.style-file-single .upload-object .icon-container img {display:none}
.field-responsiveimage.style-file-single .upload-object .info {margin-left:34px;margin-right:15%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.field-responsiveimage.style-file-single .upload-object .info h4,
.field-responsiveimage.style-file-single .upload-object .info p {display:inline;margin:0;padding:0;font-size:13px;line-height:150%;color:#666}
.field-responsiveimage.style-file-single .upload-object .info p.size {font-weight:normal}
.field-responsiveimage.style-file-single .upload-object .info p.size:before {content:" - "}
.field-responsiveimage.style-file-single .upload-object .progress-bar {display:block;width:100%;overflow:hidden;height:5px;background-color:#f5f5f5;border-radius:3px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);position:absolute;top:50%;margin-top:-2px;right:5px}
.field-responsiveimage.style-file-single .upload-object .progress-bar .upload-progress {float:left;width:0%;height:100%;line-height:5px;color:#fff;background-color:#5fb6f5;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}
.field-responsiveimage.style-file-single .upload-object .meta {position:absolute;top:50%;margin-top:-44px;height:88px;right:0;width:15%}
.field-responsiveimage.style-file-single .upload-object .meta .upload-remove-button {position:absolute;top:50%;right:0;height:20px;margin-top:-10px;margin-right:10px;z-index:100}
.field-responsiveimage.style-file-single .upload-object .icon-container:after {width:20px;height:20px;margin-top:-10px;margin-left:-10px;background-size:20px 20px}
.field-responsiveimage.style-file-single .upload-object.is-error .icon-container:after {font-size:20px}
.responsiveimage-jcrop-body {
div[data-jcrop].no-jcrop img {
max-width: 100%;
}
}
.field-responsiveimage {
.vue-dropzone {
display: none;
}
.upload-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1.5rem;
}
.preview-container {
display: grid;
grid-gap: 0.5rem;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
.cdz-preview {
display: flex;
position: relative;
background: white;
border: 1px solid rgba(0,0,0,0.2);
[data-dz-remove] {
position: absolute;
right: 0;
top: 0;
width: 2rem;
height: 2rem;
color: #333;
fill: currentColor;
margin-top: 0.5rem;
margin-right: 0.5rem;
}
div.details {
padding: 1rem;
flex: 1 0 0;
min-width: 0;
display: flex;
justify-content: space-evenly;
flex-direction: column;
& > div {
font-size: 1.2rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
img {
width: 15rem !important;
height: 10rem !important;
object-fit: cover !important;
}
div.cdz-progress {
position: absolute;
height: 5px;
bottom: 0;
width: 100%;
display: flex;
.dz-upload {
transition: width 0.2s;
background: hsl(200.9, 78.6%, 45.9%);
}
}
}
}
}
.responsive-database-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 1.5rem;
margin-bottom: 1.5rem;
label {
position: relative;
display: block;
cursor: pointer;
input {
visibility: hidden;
z-index: -999;
position: absolute;
& + img {
max-width: 100%;
height: auto;
}
}
input:checked + img {
box-shadow: 3px 3px 1px hsl(200.9, 78.6%, 45.9%),
-3px 3px 1px hsl(200.9, 78.6%, 45.9%),
3px -3px 1px hsl(200.9, 78.6%, 45.9%),
-3px -3px 1px hsl(200.9, 78.6%, 45.9%);
}
}
}

View File

@ -1,413 +0,0 @@
.uploader-object-active() {
background: @fileupload-object-active-bg !important;
i, p.size {
color: #ecf0f1;
}
h4 {
color: white;
}
.icon-container {
border-right-color: @fileupload-object-active-bg !important;
}
}
.uploader-progress-bar() {
display: block;
width: 100%;
overflow: hidden;
height: @fileupload-progress-bar-height;
background-color: @fileupload-progress-bar-bg;
border-radius: @border-radius-base;
.box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
.upload-progress {
float: left;
width: 0%;
height: 100%;
line-height: @fileupload-progress-bar-height;
color: @fileupload-progress-bar-color;
background-color: #5fb6f5;
.box-shadow(none);
.transition(width .6s ease);
}
}
.uploader-block-button() {
display: block;
border: 2px dashed #BDC3C7;
background-clip: content-box;
background-color: #F9F9F9;
position: relative;
outline: none;
.upload-button-icon {
position: absolute;
width: 22px;
height: 22px;
top: 50%;
left: 50%;
margin-top: -11px;
margin-left: -11px;
&:before {
text-align: center;
display: block;
font-size: 22px;
height: 22px;
width: 22px;
line-height: 22px;
color: #BDC3C7;
}
&.large-icon {
width: 34px;
height: 34px;
top: 50%;
left: 50%;
margin-top: -17px;
margin-left: -17px;
&:before {
font-size: 34px;
height: 24px;
width: 24px;
line-height: 24px;
}
}
}
&:hover {
border: 2px dashed #1F99DC;
.upload-button-icon:before {
color: #1F99DC;
}
}
&:focus {
border: 2px dashed #1F99DC;
.upload-button-icon:before {
color: #1F99DC;
}
}
}
.uploader-small-loader() {
width: 20px;
height: 20px;
margin-top: -10px;
margin-left: -10px;
background-size: 20px 20px;
}
.uploader-vertical-align() {
position: absolute;
top: 50%;
margin-top: -44px;
height: 88px;
}
//
// Shared
//
.field-fileupload {
//
// Uploaded item
//
.upload-object {
.border-radius(3px);
position: relative;
outline: none;
overflow: hidden;
display: inline-block;
vertical-align: top;
img {
width: 100%;
height: 100%;
}
.icon-container {
display: table;
opacity: .6;
i {
color: #95a5a6;
display: inline-block;
}
div {
display: table-cell;
text-align: center;
vertical-align: middle;
}
}
.icon-container.image {
> div.icon-wrapper {
display: none;
}
}
h4 {
font-size: 13px;
color: #2A3E51;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 150%;
margin: 15px 0 5px 0;
padding-right: 0;
.transition(padding 0.1s);
position: relative;
a {
position: absolute;
right: 0;
top: 0;
display: none;
font-weight: 400;
}
}
p.size {
font-size: 12px;
color: #95a5a6;
strong { font-weight: 400; }
}
.meta {
.drag-handle {
position: absolute;
bottom: 0;
right: 0;
cursor: move;
display: block;
}
}
.info h4 a,
.meta a.upload-remove-button,
.meta a.drag-handle {
color: #2b3e50;
display: none;
font-size: 13px;
text-decoration: none;
}
}
//
// Loading State
//
.upload-object {
.icon-container {
position: relative;
}
.icon-container:after {
background-image: url('../../../../../system/assets/ui/images/loader-transparent.svg');
position: absolute;
content: ' ';
width: 40px;
height: 40px;
left: 50%;
top: 50%;
margin-top: -20px;
margin-left: -20px;
display: block;
background-size: 40px 40px;
background-position: 50% 50%;
.animation(spin 1s linear infinite);
}
&.is-success {
.icon-container {
opacity: 1;
}
.icon-container:after {
opacity: 0;
.transition(opacity .3s ease);
}
}
// Replaces the loader with an error symbol
&.is-error {
.icon-container:after {
content: "";
background: none;
.icon(@exclamation-triangle);
.animation(none);
font-size: 40px;
color: #ab2a1c;
margin-top: -20px;
margin-left: -20px;
text-shadow: 2px 2px 0 #fff;
}
}
&.is-loading {
.icon-container {
opacity: .6;
}
.icon-container:after {
opacity: 1;
.transition(opacity .3s ease);
}
}
}
//
// Success state
//
.upload-object.is-success {
cursor: pointer;
.progress-bar {
opacity: 0;
.transition(opacity .3s ease);
}
&:hover {
h4 a,
.meta .upload-remove-button,
.meta .drag-handle { display: block; }
}
}
//
// Error State
//
.upload-object.is-error {
cursor: pointer;
.icon-container {
opacity: 1;
> img, > i {
opacity: .5;
}
}
.info h4 {
color: #ab2a1c;
a {
display: none;
}
}
.meta {
display: none;
}
}
//
// Sortable
//
&.is-sortable {
position: relative;
.upload-placeholder {
position: relative;
border: 1px dotted #e0e0e0 !important;
}
.upload-object.dragged {
position: absolute;
.opacity(.5);
z-index: 2000;
.uploader-toolbar {
display: none;
}
}
}
//
// Preview mode
//
&.is-preview {
.upload-button,
.upload-remove-button,
.meta a.drag-handle {
display: none !important;
}
}
}
//
// Media
//
@media (max-width: 1024px) {
.field-fileupload {
.upload-object.is-success {
h4 a,
.meta .upload-remove-button,
.meta .drag-handle { display: block !important; }
}
}
}
//
// Config form
//
.fileupload-config-form {
.fileupload-url-button{
padding-left: 0;
> i {
color: #666;
}
}
.file-upload-modal-image-header {
// Photoshop transparent background
// Based on: http://lea.verou.me/css3patterns/#checkerboard
background-color: #FEFEFE;
background-image: -webkit-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB), -webkit-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB);
background-image: -moz-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB), -moz-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB);
background-image: -o-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB), -o-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB);
background-image: -ms-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB), -ms-linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB);
background-image: linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB), linear-gradient(45deg, #CBCBCB 25%, transparent 25%, transparent 75%, #CBCBCB 75%, #CBCBCB);
-webkit-background-size: 20px 20px;
-moz-background-size: 20px 20px;
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
&, img {
.border-top-radius(2px);
}
.close {
position: absolute;
top: 20px;
right: 20px;
background: #BDC3C7;
opacity: .7;
height: 24px;
width: 22px;
&:hover, &:focus {
opacity: .9;
}
}
}
.file-upload-modal-image-header + .modal-body {
padding-top: @padding-standard;
}
}

View File

@ -1,176 +0,0 @@
//
// Multi File
//
.field-fileupload.style-file-multi {
.upload-button {
margin-bottom: 10px;
}
.upload-files-container {
border: 1px solid @fileupload-list-border-color;
.border-radius(3px);
border-bottom: none;
display: none;
}
&.is-populated .upload-files-container {
display: block;
}
.upload-object {
display: block;
width: 100%;
border-bottom: 1px solid @fileupload-list-border-color;
padding-left: 10px;
&:nth-child(even) {
background-color: @fileupload-list-accent-bg;
}
.icon-container {
position: absolute;
top: 0;
left: 10px;
width: 15px;
padding: 11px 7px;
i {
line-height: 150%;
font-size: 15px;
}
img { display: none; }
}
.info {
margin-left: 35px;
margin-right: 15%;
h4, p {
margin: 0;
padding: 11px 0;
font-size: 12px;
font-weight: normal;
line-height: 150%;
color: #666666;
}
h4 {
padding-right: 15px;
a {
padding: 10px 0;
right: 15px;
}
}
p.size {
position: absolute;
top: 0;
right: 0;
width: 15%;
display: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.progress-bar {
.uploader-progress-bar();
position: absolute;
top: 18px;
left: 0;
}
.meta {
position: absolute;
top: 0;
right: 0;
margin-right: 15px;
width: 15%;
a.drag-handle {
top: -2px;
bottom: auto;
line-height: 150%;
padding: 10px 0;
}
}
.icon-container:after {
.uploader-small-loader();
}
&.is-error .icon-container:after {
font-size: 20px;
}
//
// Success
//
&.is-success {
.info p.size { display: block; }
}
//
// Sorting
//
&.upload-placeholder {
height: 35px;
background-color: transparent;
&:after { opacity: 0; }
}
//
// Hover
//
&:hover {
.uploader-object-active();
h4 { padding-right: 35px; }
}
}
}
//
// Media
//
@media (max-width: @screen-md-max) {
.field-fileupload.style-file-multi {
.info {
margin-right: 20% !important;
p.size {
width: 20% !important;
}
}
.meta {
width: 20% !important;
}
}
}
@media (max-width: @screen-sm-max) {
.field-fileupload.style-file-multi {
.upload-object {
h4 { padding-right: 35px !important; }
}
.info {
margin-right: 25% !important;
p.size {
width: 25% !important;
padding-right: 35px !important;
}
}
.meta {
width: 25% !important;
}
}
}

View File

@ -1,119 +0,0 @@
//
// Single File
//
.field-fileupload.style-file-single {
background-color: @color-form-field-bg;
border: 1px solid @color-form-field-border;
overflow: hidden;
position: relative;
padding-right: 30px;
border-radius: 3px;
.box-shadow(@input-box-shadow);
.upload-button {
.uploader-vertical-align();
background: transparent;
right: -2px;
color: lighten(@color-form-field-recordfinder-btn, 15%);
i {
font-size: 14px;
}
&:hover {
color: @color-form-field-recordfinder-btn;
}
}
.upload-empty-message {
padding: 8px 0 8px 11px;
font-size: 14px;
}
&.is-populated {
.upload-empty-message {
display: none;
}
}
.upload-object {
display: block;
width: 100%;
padding: 7px 0 9px 0;
.icon-container {
position: absolute;
top: 0;
left: 0;
width: 15px;
padding: 0 5px;
margin: 8px 0 0 7px;
text-align: center;
i {
line-height: 150%;
font-size: 15px;
}
img { display: none; }
}
.info {
margin-left: 34px;
margin-right: 15%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
h4, p {
display: inline;
margin: 0;
padding: 0;
font-size: 13px;
line-height: 150%;
color: #666666;
}
p.size {
font-weight: normal;
&:before {
content: " - ";
}
}
}
.progress-bar {
.uploader-progress-bar();
position: absolute;
top: 50%;
margin-top: -2px;
right: 5px;
}
.meta {
.uploader-vertical-align();
right: 0;
width: 15%;
.upload-remove-button {
position: absolute;
top: 50%;
right: 0;
height: 20px;
margin-top: -10px;
margin-right: 10px;
z-index: 100;
}
}
.icon-container:after {
.uploader-small-loader();
}
&.is-error .icon-container:after {
font-size: 20px;
}
}
}

View File

@ -1,134 +0,0 @@
//
// Multi Image
//
.field-fileupload.style-image-multi {
.upload-button,
.upload-object {
margin: 0 10px 10px 0;
}
.upload-button {
.uploader-block-button();
float: left;
width: 76px;
height: 76px;
}
.upload-files-container {
margin-left: 90px;
}
.upload-object {
background: #fff;
border: 1px solid #ecf0f1;
width: 260px;
.progress-bar {
.uploader-progress-bar();
position: absolute;
bottom: 10px;
left: 0;
}
.icon-container {
border-right: 1px solid #f6f8f9;
float: left;
display: inline-block;
overflow: hidden;
width: 75px;
height: 75px;
i {
font-size: 35px;
}
&.image img {
.border-left-radius(3px);
width: auto;
}
}
.info {
margin-left: 90px;
h4 {
padding-right: 15px;
a {
right: 15px;
}
}
}
.meta {
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: 0 15px 0 90px;
a.drag-handle {
bottom: 15px;
}
}
&.upload-placeholder {
height: 75px;
background-color: transparent;
&:after { opacity: 0; }
}
&:hover {
.uploader-object-active();
h4 { padding-right: 35px; }
}
}
&.is-preview {
.upload-files-container {
margin-left: 0;
}
}
}
//
// On Sidebar
//
.form-sidebar .field-fileupload.style-image-multi .upload-files-container {
margin-left: 0px;
}
.form-sidebar .field-fileupload.style-image-multi .upload-button {
width: 100%;
}
//
// Media
//
@media (max-width: 1280px) {
.field-fileupload.style-image-multi {
.upload-object {
width: 230px;
}
}
}
@media (max-width: 1024px) {
.field-fileupload.style-image-multi {
.upload-button {
width: 100%;
}
.upload-files-container {
margin-left: 0;
}
.upload-object {
margin-right: 0;
display: block;
width: auto;
}
}
}

View File

@ -1,78 +0,0 @@
//
// Single Image
//
.field-fileupload.style-image-single {
&.is-populated {
.upload-button {
display: none;
}
}
.upload-button {
.uploader-block-button();
min-height: 100px;
min-width: 100px;
}
.upload-object {
padding-bottom: 66px;
.icon-container {
border: 1px solid #f6f8f9;
background: rgba(255,255,255,.5);
&.image img {
.border-radius(3px);
.img-responsive();
// This is needed when the image is very large and
// being processed by dropzone on the client-side
// the image has no height or width.
min-height: 100px;
min-width: 100px;
}
}
.progress-bar {
.uploader-progress-bar();
position: absolute;
bottom: 10px;
left: 0;
}
.info {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 66px;
}
.meta {
position: absolute;
bottom: 65px;
left: 0;
right: 0;
margin: 0 15px;
}
&:hover {
h4 { padding-right: 20px; }
}
}
}
//
// Media
//
@media (max-width: 1024px) {
.field-fileupload.style-image-single {
.upload-object {
h4 { padding-right: 20px !important; }
}
}
}

View File

@ -1,15 +0,0 @@
@import "../../../../assets/less/core/boot.less";
@fileupload-progress-bar-height: 5px;
@fileupload-progress-bar-color: #fff;
@fileupload-progress-bar-bg: #f5f5f5;
@fileupload-inactive-icon: #808b93;
@fileupload-object-active-bg: #4da7e8;
@fileupload-list-accent-bg: #f5f5f5;
@fileupload-list-border-color: #eeeeee;
@import "fileupload.base.less";
@import "fileupload.imagemulti.less";
@import "fileupload.imagesingle.less";
@import "fileupload.filemulti.less";
@import "fileupload.filesingle.less";

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
{
"name": "assets",
"version": "1.0.0",
"description": "",
"main": "webpack.mix.js",
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cross-env": "^7.0.2",
"laravel-mix": "^5.0.7",
"postcss-nested": "^4.0.0",
"vue": "^2.6.12",
"vue2-dropzone": "^3.6.0"
},
"devDependencies": {
"vue-template-compiler": "^2.6.12"
}
}

View File

@ -1,186 +0,0 @@
<template>
<div
class="field-responsiveimage style-image-single"
data-control="responsiveimage"
>
<!-- Add New Image -->
<div class="upload-wrapper" v-show="addVisible">
<a href="javascript:;" class="upload-button" ref="addQueueTrigger">
<span class="upload-button-icon oc-icon-upload"></span>
</a>
<a href="javascript:;" @click="openDatabase" class="upload-button">
<span class="upload-button-icon oc-icon-database"></span>
</a>
</div>
<vue-dropzone @vdropzone-mounted="onMount" @vdropzone-removed-file="onRemove" @vdropzone-success="onUploadSuccess" @vdropzone-sending="onSend" @vdropzone-thumbnail="onAdd" v-if="loaded" ref="dropzone" :id="id" :options="dropzoneOptions"></vue-dropzone>
<div class="preview-container" ref="previewsContainer"></div>
<input type="hidden" :name="name" :value="asString">
</div>
</template>
<script>
import vueDropzone from 'vue2-dropzone';
import popup from './popup.mixin.js';
export default {
mixins: [ popup ],
data: function() {
return {
extraData: {},
addVisible: true,
content: {
sections: [],
placeholders: {
header: {}
}
},
loaded: false,
content: []
};
},
components: { vueDropzone },
props: {
meta: {},
cropOptions: {},
name: {
required: true
},
value: {
default: function() { return null; }
},
formid: {},
handlers: {}
},
computed: {
id() {
return Math.random().toString(36).substring(7);
},
asString() {
return this.content;
},
formData() {
return $(this.$el).closest('form').serializeArray();
},
dropzoneOptions() {
return {
paramName: 'file_data',
autoProcessQueue: false,
url: window.location,
clickable: this.$refs.addQueueTrigger,
previewsContainer: this.$refs.previewsContainer,
headers: {
'X-OCTOBER-REQUEST-HANDLER': this.handlers.onUpload,
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
previewTemplate: `
<div class="cdz-preview">
<a href="#" data-dz-remove>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/></svg>
</a>
<img data-dz-thumbnail />
<div class="details">
<div><span data-dz-name></span></div>
<div data-dz-size></div>
</div>
<div class="cdz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
</div>`
};
}
},
methods: {
onUploadSuccess(file, response) {
this.content = response;
},
onAdd(file) {
if(file.manuallyAdded === true) {
return;
}
if (file.width < this.cropOptions.minWidth) {
$.oc.flashMsg({
text: `Dieses Bild ist zu klein. Bitte wähle ein Bild mit mindestens ${this.cropOptions.minWidth} Pixel Breite.`,
class: 'error'
});
this.$refs.dropzone.removeFile(file);
return;
}
this.addVisible = false;
this.showPopup(file).then((ret) => {
this.extraData[file.name] = ret;
this.$refs.dropzone.processQueue();
}).catch((err) => {
this.$refs.dropzone.removeFile(file);
this.addVisible = true;
});
},
openDatabase(e) {
var _cls = this;
var data = {
file_id: this.value,
crop: this.cropOptions
};
$(e.target).popup({ handler: this.handlers.onOpenDatabase, size: 'giant', extraData: { databasePopup: data } }).on('complete.oc.popup', function(e) {
$(e.relatedTarget).on('ajaxSuccess', 'form', function(event, context, data) {
_cls.content = data.id;
_cls.addVisible = false;
var file = { size: data.size, name: data.name, type: data.type };
_cls.$refs.dropzone.manuallyAddFile(file, data.url);
$(e.relatedTarget).popup('hide');
});
});
},
onSend(file, xhr, formData) {
formData.append('extraData', JSON.stringify({
...this.extraData[file.name],
...this.cropOptions
}));
this.formData.forEach((v) => {
formData.append(v.name, v.value);
});
Object.keys(this.cropOptions).forEach((v) => {
formData.append(v, this.cropOptions[v]);
});
},
onMount() {
if (!this.value) { return; }
this.addVisible = false;
var file = { size: this.meta.size, name: this.meta.name, type: this.meta.type };
this.$refs.dropzone.manuallyAddFile(file, this.meta.url);
},
onRemove(file, error, xhr) {
var _cls = this;
$(this.$el).request(this.handlers.onDelete, {
data: { file_id: this.content },
success: function(data) {
_cls.addVisible = true;
_cls.content = '';
}
});
}
},
mounted() {
this.loaded = true;
this.content = this.value;
var self = this;
}
};
</script>

View File

@ -1,57 +0,0 @@
import Vue from 'vue';
import App from './App';
+function ($) { "use strict";
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
var Responsiveimage = function (element, options) {
this.$el = $(element)
this.$form = $(element).closest('form');
this.options = options || {}
$.oc.foundation.controlUtils.markDisposable(element)
Base.call(this)
this.init()
}
Responsiveimage.prototype = Object.create(BaseProto)
Responsiveimage.prototype.constructor = Responsiveimage
Responsiveimage.prototype.init = function() {
this.$app = new Vue({
el: this.$el.children('div').get(0),
components: { App }
});
}
Responsiveimage.DEFAULTS = {
}
// PLUGIN DEFINITION
// ============================
var old = $.fn.responsiveimage
$.fn.responsiveimage = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.responsiveimage')
var options = $.extend({}, Responsiveimage.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.responsiveimage', (data = new Responsiveimage(this, options)))
})
}
$.fn.responsiveimage.Constructor = Responsiveimage
$.fn.responsiveimage.noConflict = function () {
$.fn.responsiveimage = old
return this
}
$(document).render(function (){
$('[data-responsiveimage]').responsiveimage()
})
}(window.jQuery);

View File

@ -1,125 +0,0 @@
export default {
data: function() {
return {
popup: null,
jcrop: null
};
},
methods: {
onChangeCrop() {
this.popup.values.crop = this.jcrop.tellSelect();
},
showPopup(file, cro) {
var _cls = this;
this.popup = {
file: file,
values: { crop: null }
};
$('body').append(this.popupContent(file));
return new Promise((resolve, reject) => {
var $popup = $(`#${this.popupId}`);
$popup.modal({ backdrop: false });
$popup.one('shown.bs.modal', function() {
var $cropContainer = $popup.find('[data-jcrop]');
var imageElement = new Image();
imageElement.src = file.dataURL;
$cropContainer.append(imageElement);
if (_cls.useJcrop) {
var jcropOptions = {
shade: true,
boxWidth: $cropContainer.width(),
onChange: _cls.onChangeCrop,
aspectRatio: _cls.realAspectRatio,
minSize: [_cls.cropOptions.minWidth, 0]
};
_cls.jcrop = $.Jcrop($popup.find('[data-jcrop] img').get(0), jcropOptions);
}
$popup.on('change', '[name=title]', (e) => { _cls.popup.values.title = e.target.value });
$popup.on('change', '[name=description]', (e) => { _cls.popup.values.description = e.target.value });
});
$popup.off('click', '[data-resolve]').on('click', '[data-resolve]', function(e) {
e.preventDefault();
if (!_cls.popup.values.title) {
$.oc.flashMsg({ text: 'Bitte einen Titel angeben.', class: 'error' });
return;
}
if (_cls.aspectRatio && !_cls.popup.values.crop) {
$.oc.flashMsg({ text: 'Bitte das Bild beschneiden', class: 'error' });
return;
}
$popup.modal('hide');
resolve(_cls.popup.values);
});
$popup.off('hidden.bs.modal').one('hidden.bs.modal', () => {
$popup.remove();
reject(null);
});
});
},
popupContent(file) {
var decoration = this.jcropContainerDecoration(file);
return `<div class="control-popup modal fade" id="${this.popupId}">
<div class="modal-dialog">
<div class="modal-content">
<form>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Bild konfigurieren</h4>
</div>
<div class="modal-body responsiveimage-jcrop-body">
<div data-jcrop ${decoration}></div>
<p style="margin-top: 2rem;">Füge dem Anhang einen Titel und eine Beschreibung hinzu.</p>
<div class="form-group text-field span-full">
<input type="text" name="title" value="" placeholder="Titel" class="form-control" autocomplete="off" maxlength="255">
</div>
<div class="form-group textarea-field span-full">
<textarea name="description" class="form-control field-textarea size-tiny" placeholder="Beschreibung"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" data-reject>Abbrechen</button>
<button type="submit" class="btn btn-primary" data-resolve>Änderungen übernehmen</button>
</div>
</form>
</div>
</div>
</div>`;
},
useJcrop(file) {
return this.cropOptions.types.some(type => type == file.type);
},
jcropContainerDecoration(file) {
return this.useJcrop(file) ? '' : 'class="no-jcrop"';
},
},
computed: {
realAspectRatio() {
return this.cropOptions.aspectRatio
? this.cropOptions.aspectRatio[0] / this.cropOptions.aspectRatio[1]
: null;
},
popupId() {
return `subform-popup-${this.formid}`;
},
},
mounted() {
}
};

View File

@ -1,27 +0,0 @@
let mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for your application, as well as bundling up your JS files.
|
*/
mix.config.resourceRoot = '..';
mix.options({
hmrOptions: {
host: 'dpsgkoeln.test',
port: 8080
}
})
mix.js('src/main.js', 'js/responsiveimage.build.js')
.postCss('css/main.css', 'css/responsiveimage.build.css', [
require('postcss-nested')
])
.sourceMaps();

View File

@ -1,47 +0,0 @@
<div class="fileupload-config-form">
<?= Form::open() ?>
<input type="hidden" name="file_id" value="<?= $file->id ?>" />
<input type="hidden" name="manage_id" value="<?= $relationManageId ?>" />
<input type="hidden" name="_relation_field" value="<?= $relationField ?>" />
<?php if (starts_with($displayMode, 'image')): ?>
<div class="file-upload-modal-image-header">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<img
src="<?= $file->thumbUrl ?>"
class="img-responsive center-block"
alt=""
title="<?= e(trans('backend::lang.fileupload.attachment')) ?>: <?= e($file->file_name) ?>"
style="<?= $cssDimensions ?>" />
</div>
<?php else: ?>
<div class="modal-header">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('backend::lang.fileupload.attachment')) ?>: <?= $file->file_name ?></h4>
</div>
<?php endif ?>
<div class="modal-body">
<p><?= e(trans('backend::lang.fileupload.help')) ?></p>
<?= $this->getConfigFormWidget()->render(); ?>
</div>
<div class="modal-footer">
<a href="<?= $file->pathUrl ?>" class="pull-left btn btn-link fileupload-url-button" target="_blank">
<i class="oc-icon-link"></i><?= e(trans('backend::lang.fileupload.attachment_url')) ?>
</a>
<button
type="submit"
class="btn btn-primary"
data-request="<?= $this->getEventHandler('onSaveAttachmentConfig') ?>"
data-popup-load-indicator>
<?= e(trans('backend::lang.form.save')) ?>
</button>
<button
type="button"
class="btn btn-default"
data-dismiss="popup">
<?= e(trans('backend::lang.form.cancel')) ?>
</button>
</div>
<?= Form::close() ?>
</div>

View File

@ -1,30 +0,0 @@
<form data-request="<?= $this->getEventHandler('onSelectDatabase') ?>">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Bild aus Datenbank einfügen</h4>
</div>
<div class="modal-body">
<p>Wähle hier ein Bild aus</p>
<div class="responsive-database-grid">
<?php foreach ($images as $file): ?>
<label for="responsive-image-<?= $file->id ?>">
<input type="radio" name="image" id="responsive-image-<?= $file->id ?>" value="<?= $file->id ?>" data-select>
<img src="<?= $file->url ?>">
</label>
<?php endforeach; ?>
</div>
<div class="form-group textarea-field span-full">
<textarea name="title" class="form-control field-textarea size-tiny" placeholder="Titel"></textarea>
</div>
<div class="form-group textarea-field span-full">
<textarea name="description" class="form-control field-textarea size-tiny" placeholder="Beschreibung"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Abbrechen</button>
<button type="submit" class="btn btn-primary">Bild wählen</button>
</div>
</form>

View File

@ -1,73 +0,0 @@
<div
id="<?= $this->getId() ?>"
class="field-fileupload style-file-multi is-sortable is-multi <?= count($fileList) ? 'is-populated' : '' ?> <?= $this->previewMode ? 'is-preview' : '' ?>"
data-control="fileupload"
data-upload-handler="<?= $this->getEventHandler('onUpload') ?>"
data-template="#<?= $this->getId('template') ?>"
data-error-template="#<?= $this->getId('errorTemplate') ?>"
data-sort-handler="<?= $this->getEventHandler('onSortAttachments') ?>"
data-unique-id="<?= $this->getId() ?>"
data-max-filesize="<?= $maxFilesize ?>"
<?php if ($useCaption): ?>data-config-handler="<?= $this->getEventHandler('onLoadAttachmentConfig') ?>"<?php endif ?>
<?php if ($acceptedFileTypes): ?>data-file-types="<?= $acceptedFileTypes ?>"<?php endif ?>
>
<!-- Upload Button -->
<button type="button" class="btn btn-sm btn-secondary oc-icon-upload upload-button">
<?= $prompt ?>
</button>
<!-- Existing files -->
<div class="upload-files-container">
<?php foreach ($fileList as $file): ?>
<div class="upload-object is-success" data-id="<?= $file->id ?>" data-path="<?= $file->pathUrl ?>">
<div class="icon-container">
<i class="icon-file"></i>
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name><?= e($file->title ?: $file->file_name) ?></span>
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
data-request-data="file_id: <?= $file->id ?>"
><i class="icon-times"></i></a>
</h4>
<p class="size"><?= e($file->sizeToString()) ?></p>
</div>
<div class="meta">
<a href="javascript:;" class="drag-handle"><i class="icon-bars"></i></a>
</div>
</div>
<?php endforeach ?>
</div>
</div>
<!-- Template for new files -->
<script type="text/template" id="<?= $this->getId('template') ?>">
<div class="upload-object dz-preview dz-file-preview">
<div class="icon-container">
<i class="icon-file"></i>
<img data-dz-thumbnail alt="" />
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name></span>
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
><i class="icon-times"></i></a>
</h4>
<p class="size" data-dz-size></p>
</div>
<div class="meta">
<a href="javascript:;" class="drag-handle"><i class="icon-bars"></i></a>
<div class="progress-bar"><span class="upload-progress" data-dz-uploadprogress></span></div>
<div class="error-message"><span data-dz-errormessage></span></div>
</div>
</div>
</script>

View File

@ -1,76 +0,0 @@
<div
id="<?= $this->getId() ?>"
class="field-fileupload style-file-single <?= $singleFile ? 'is-populated' : '' ?> <?= $this->previewMode ? 'is-preview' : '' ?>"
data-control="fileupload"
data-upload-handler="<?= $this->getEventHandler('onUpload') ?>"
data-template="#<?= $this->getId('template') ?>"
data-error-template="#<?= $this->getId('errorTemplate') ?>"
data-unique-id="<?= $this->getId() ?>"
data-max-filesize="<?= $maxFilesize ?>"
<?php if ($useCaption): ?>data-config-handler="<?= $this->getEventHandler('onLoadAttachmentConfig') ?>"<?php endif ?>
<?php if ($acceptedFileTypes): ?>data-file-types="<?= $acceptedFileTypes ?>"<?php endif ?>
>
<!-- Upload Button -->
<button type="button" class="btn btn-default upload-button">
<i class="icon-upload"></i>
</button>
<!-- Existing file -->
<div class="upload-files-container">
<?php if ($singleFile): ?>
<div class="upload-object is-success" data-id="<?= $singleFile->id ?>" data-path="<?= $singleFile->pathUrl ?>">
<div class="icon-container">
<i class="icon-file"></i>
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name><?= e($singleFile->title ?: $singleFile->file_name) ?></span>
</h4>
<p class="size"><?= e($singleFile->sizeToString()) ?></p>
</div>
<div class="meta">
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
data-request-data="file_id: <?= $singleFile->id ?>"
><i class="icon-times"></i></a>
</div>
</div>
<?php endif ?>
</div>
<!-- Empty message -->
<div class="upload-empty-message">
<span class="text-muted"><?= $prompt ?></span>
</div>
</div>
<!-- Template for new file -->
<script type="text/template" id="<?= $this->getId('template') ?>">
<div class="upload-object dz-preview dz-file-preview">
<div class="icon-container">
<i class="icon-file"></i>
<img data-dz-thumbnail alt="" />
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name></span>
</h4>
<p class="size" data-dz-size></p>
</div>
<div class="meta">
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
><i class="icon-times"></i></a>
<div class="progress-bar"><span class="upload-progress" data-dz-uploadprogress></span></div>
<div class="error-message"><span data-dz-errormessage></span></div>
</div>
</div>
</script>

View File

@ -1,72 +0,0 @@
<div
id="<?= $this->getId() ?>"
class="field-fileupload style-image-multi is-sortable is-multi <?= count($fileList) ? 'is-populated' : '' ?> <?= $this->previewMode ? 'is-preview' : '' ?>"
data-control="fileupload"
data-upload-handler="<?= $this->getEventHandler('onUpload') ?>"
data-template="#<?= $this->getId('template') ?>"
data-error-template="#<?= $this->getId('errorTemplate') ?>"
data-sort-handler="<?= $this->getEventHandler('onSortAttachments') ?>"
data-unique-id="<?= $this->getId() ?>"
data-max-filesize="<?= $maxFilesize ?>"
<?php if ($useCaption): ?>data-config-handler="<?= $this->getEventHandler('onLoadAttachmentConfig') ?>"<?php endif ?>
<?php if ($acceptedFileTypes): ?>data-file-types="<?= $acceptedFileTypes ?>"<?php endif ?>
>
<!-- Upload Button -->
<a href="javascript:;" class="upload-button">
<span class="upload-button-icon oc-<?= $emptyIcon ?>"></span>
</a>
<!-- Existing files -->
<div class="upload-files-container">
<?php foreach ($fileList as $file): ?>
<div class="upload-object is-success" data-id="<?= $file->id ?>" data-path="<?= $file->pathUrl ?>">
<div class="icon-container image">
<img src="<?= $file->thumbUrl ?>" alt="" />
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name><?= e($file->title ?: $file->file_name) ?></span>
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
data-request-data="file_id: <?= $file->id ?>"
><i class="icon-times"></i></a>
</h4>
<p class="size"><?= e($file->sizeToString()) ?></p>
</div>
<div class="meta">
<a href="javascript:;" class="drag-handle"><i class="icon-bars"></i></a>
</div>
</div>
<?php endforeach ?>
</div>
</div>
<!-- Template for new files -->
<script type="text/template" id="<?= $this->getId('template') ?>">
<div class="upload-object dz-preview dz-file-preview">
<div class="icon-container image">
<img data-dz-thumbnail alt="" />
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name></span>
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
><i class="icon-times"></i></a>
</h4>
<p class="size" data-dz-size></p>
</div>
<div class="meta">
<a href="javascript:;" class="drag-handle"><i class="icon-bars"></i></a>
<div class="progress-bar"><span class="upload-progress" data-dz-uploadprogress></span></div>
<div class="error-message"><span data-dz-errormessage></span></div>
</div>
</div>
</script>

View File

@ -1,74 +0,0 @@
<div
id="<?= $this->getId() ?>"
class="field-fileupload style-image-single <?= $singleFile ? 'is-populated' : '' ?> <?= $this->previewMode ? 'is-preview' : '' ?>"
data-control="fileupload"
data-upload-handler="<?= $this->getEventHandler('onUpload') ?>"
data-template="#<?= $this->getId('template') ?>"
data-error-template="#<?= $this->getId('errorTemplate') ?>"
data-unique-id="<?= $this->getId() ?>"
data-thumbnail-width="<?= $imageWidth ?: '0' ?>"
data-thumbnail-height="<?= $imageHeight ?: '0' ?>"
data-max-filesize="<?= $maxFilesize ?>"
<?php if ($useCaption): ?>data-config-handler="<?= $this->getEventHandler('onLoadAttachmentConfig') ?>"<?php endif ?>
<?php if ($acceptedFileTypes): ?>data-file-types="<?= $acceptedFileTypes ?>"<?php endif ?>
>
<!-- Add New Image -->
<a
href="javascript:;"
style="<?= $cssBlockDimensions ?>"
class="upload-button">
<span class="upload-button-icon oc-<?= $emptyIcon ?> <?= $imageWidth > 100 ? 'large-icon' : '' ?>"></span>
</a>
<!-- Existing file -->
<div class="upload-files-container">
<?php if ($singleFile): ?>
<div class="upload-object is-success" data-id="<?= $singleFile->id ?>" data-path="<?= $singleFile->pathUrl ?>">
<div class="icon-container image">
<img src="<?= $singleFile->thumbUrl ?>" alt="" />
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name><?= e($singleFile->title ?: $singleFile->file_name) ?></span>
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
data-request-data="file_id: <?= $singleFile->id ?>"
><i class="icon-times"></i></a>
</h4>
<p class="size"><?= e($singleFile->sizeToString()) ?></p>
</div>
<div class="meta"></div>
</div>
<?php endif ?>
</div>
</div>
<!-- Template for new file -->
<script type="text/template" id="<?= $this->getId('template') ?>">
<div class="upload-object dz-preview dz-file-preview">
<div class="icon-container image">
<img data-dz-thumbnail style="<?= $cssDimensions ?>" alt="" />
</div>
<div class="info">
<h4 class="filename">
<span data-dz-name></span>
<a
href="javascript:;"
class="upload-remove-button"
data-request="<?= $this->getEventHandler('onRemoveAttachment') ?>"
data-request-confirm="<?= e(trans('backend::lang.fileupload.remove_confirm')) ?>"
><i class="icon-times"></i></a>
</h4>
<p class="size" data-dz-size></p>
</div>
<div class="meta">
<div class="progress-bar"><span class="upload-progress" data-dz-uploadprogress></span></div>
<div class="error-message"><span data-dz-errormessage></span></div>
</div>
</div>
</script>

View File

@ -1,24 +0,0 @@
<?php if ($this->previewMode && !$fileList->count()): ?>
<div class="form-control">
<?= $value ?>
</div>
<?php else: ?>
<div data-responsiveimage>
<div>
<app formid="<?php echo $this->getId(); ?>" name="<?= $name ?>" :handlers="{
onUpload: '<?= $this->getEventHandler('onUpload') ?>',
onOpenDatabase: '<?= $this->getEventHandler('onOpenDatabase') ?>',
onDelete: '<?= $this->getEventHandler('onDelete') ?>'
}"
:crop-options="{
aspectRatio: <?= $aspectRatio ?>,
minWidth: <?= $minWidth ?>,
types: <?= $croppableTypes; ?>
}"
:value="<?= $value; ?>"
:meta="<?= $meta; ?>"
></app>
</div>
</div>
<?php endif ?>

109
lib/MediaPath.php Normal file
View File

@ -0,0 +1,109 @@
<?php
namespace Aweos\Resizer\Lib;
use Aweos\Resizer\Compressors\Compressor;
use Aweos\Resizer\Compressors\Factory as CompressorFactory;
use Aweos\Resizer\Models\Setting;
use Illuminate\Support\Collection;
use Media\Classes\MediaLibrary;
use Storage;
class MediaPath
{
private string $path;
public function __construct(string $path)
{
$this->path = $path;
}
public function root(): string
{
return Storage::path($this->storagePath());
}
public function storagePath(): string
{
return "media/{$this->normal()}";
}
public function normal(): string
{
return preg_replace('|^/*|', '', $this->path);
}
public function compressor(): Compressor
{
return app(CompressorFactory::class)->resolve($this);
}
public function filename(): string
{
return pathinfo($this->path, PATHINFO_FILENAME);
}
public function extension(): string
{
return pathinfo($this->path, PATHINFO_EXTENSION);
}
public function versionsPath(): string
{
return "uploads/public/c/{$this->normal()}";
}
public function versionsDirPath(): string
{
return pathinfo("uploads/public/c/{$this->normal()}", PATHINFO_DIRNAME);
}
public function publicUrl(): string
{
return MediaLibrary::instance()->findFiles($this->path)[0]->publicUrl;
}
public function get(): string
{
return MediaLibrary::instance()->get($this->path);
}
public function shouldProcess(): bool
{
return collect(Setting::get('folders'))->pluck('folder')->first(
fn ($folder) => starts_with('/'.$this->normal(), $folder.'/')
) !== null;
}
public function versions(): Collection
{
$return = collect([]);
foreach (Storage::files($this->versionsDirPath()) as $file) {
if (!preg_match_all('|('.preg_quote($this->filename(), '|').')(-[0-9]+x[0-9]+)?(\.[a-zA-Z]+)$|', $file, $matches)) {
continue;
}
if ($matches[2][0]) {
[$width, $height] = explode('x', substr($matches[2][0], 1));
}
$path = $this->versionsDirPath().'/'.$matches[0][0];
$return->push(collect([
'path' => $path,
'url' => Storage::url($path),
'filename' => $matches[0][0],
'base' => $matches[1][0],
'size' => $matches[2][0],
'ext' => $matches[3][0],
'width' => $width ?? null,
'height' => $height ?? null,
]));
}
return $return;
}
}

View File

@ -1,73 +0,0 @@
<?php namespace Aweos\Resizer\Models;
use Model;
use Storage;
use Cache;
/**
* Setting Model
*/
class Attachment extends Model
{
use \October\Rain\Database\Traits\Sluggable;
protected $slugs = ['slug' => 'title'];
/**
* @var string The database table used by the model.
*/
public $table = 'responsive_attachments';
/**
* @var array Guarded fields
*/
protected $guarded = ['*'];
/**
* @var array Fillable fields
*/
protected $fillable = ['id', 'file_id', 'title', 'slug', 'description'];
/**
* @var array Relations
*/
public $hasOne = [];
public $hasMany = [];
public $belongsTo = [
'source' => [SourceFile::class, 'key' => 'file_id']
];
public $belongsToMany = [];
public $morphTo = [];
public $morphOne = [];
public $morphMany = [];
public $attachOne = [];
public $attachMany = [];
public function getFilenameAttribute() {
return $this->slug.'.'.$this->source->extension;
}
public function getPathAttribute() {
return strtolower($this->slug[0]);
}
public function getVersionPath($w) {
return $this->path.'/'.$this->slug.'-'.$w.'.'.$this->source->extension;
}
public function getVersionRegexAttribute() {
$path = preg_quote($this->path, '/');
$ext = preg_quote($this->source->extension, '/');
$name = preg_quote($this->slug, '/');
return "/^{$path}\/{$name}(-[0-9]+)?(-full)?\.{$ext}$/";
}
public function getUrlAttribute() {
return Storage::disk('uploads')->url($this->source->path);
}
public function beforeDelete() {
Cache::forget('responsive-image-'.$this->id);
}
}

View File

@ -1,68 +0,0 @@
<?php namespace Aweos\Resizer\Models;
use Model;
use Storage;
/**
* Setting Model
*/
class SourceFile extends Model
{
use \October\Rain\Database\Traits\Sluggable;
protected $slugs = ['slug' => 'basename'];
public static $croppableTypes = [ 'image/jpeg', 'image/gif', 'image/png', 'image/webp' ];
/**
* @var string The database table used by the model.
*/
public $table = 'responsive_source_files';
/**
* @var array Guarded fields
*/
protected $guarded = ['*'];
/**
* @var array Fillable fields
*/
protected $fillable = ['id', 'basename', 'extension', 'tags', 'slug', 'aspect_ratio', 'min_width'];
public $jsonable = ['tags', 'aspect_ratio'];
/**
* @var array Relations
*/
public $hasOne = [];
public $hasMany = [
'attachments' => [Attachment::class, 'key' => 'file_id']
];
public $belongsTo = [];
public $belongsToMany = [];
public $morphTo = [];
public $morphOne = [];
public $morphMany = [];
public $attachOne = [];
public $attachMany = [];
public function getPathAttribute() {
return 'source/'.$this->slug.'.'.$this->extension;
}
public function getUrlAttribute() {
return Storage::disk('uploads')->url($this->path);
}
public function getTypeAttribute() {
return Storage::disk('uploads')->mimeType($this->path);
}
public function getSizeAttribute() {
return Storage::disk('uploads')->size($this->path);
}
public function isCroppable(): bool {
return in_array($this->type, static::$croppableTypes);
}
}

View File

@ -2,7 +2,9 @@
namespace Aweos\Resizer\Tests\MediaTest;
use Aweos\Resizer\Classes\CacheManager;
use Aweos\Resizer\Classes\TagGenerator;
use Aweos\Resizer\Lib\MediaPath;
use Aweos\Resizer\Models\Setting;
use Aweos\Resizer\Tests\TestCase;
use Cache;
@ -32,7 +34,7 @@ class ImageTagTest extends TestCase
$this->assertEquals(
'width="500" height="500" sizes="(max-width: 100px) 100px, (max-width: 200px) 200px, (max-width: 500px) 500px" srcset="/storage/uploads/public/c/pages/test-100x100.jpg 100w, /storage/uploads/public/c/pages/test-200x200.jpg 200w, /storage/uploads/public/c/pages/test-500x500.jpg 500w" src="/storage/uploads/public/c/pages/test-500x500.jpg"',
app(Twig::class)->parse('{{"pages/test.jpg" | resize}}'),
app(CacheManager::class)->get(new MediaPath('pages/test.jpg'), 'original', null),
);
}

View File

@ -1,120 +0,0 @@
<?php namespace Aweos\Resizer\Traits;
use Str;
use Backend\Classes\FormField;
use October\Rain\Halcyon\Model as HalcyonModel;
/**
* Implements special logic for processing form data, typically from from postback, and
* filling the model attributes and attributes of any related models. This is a
* customized, safer and simplified version of `$model->push()`.
*
* @package october\backend
* @author Alexey Bobkov, Samuel Georges
*/
trait ResponsiveSaver
{
/**
* @var array List of prepared models that require saving.
*/
protected $modelsToSave = [];
/**
* Takes a model and fills it with data from a multidimensional array.
* If an attribute is found to be a relationship, that relationship
* is also filled.
*
* $modelsToSave = $this->prepareModelsToSave($model, [...]);
*
* foreach ($modelsToSave as $modelToSave) {
* $modelToSave->save();
* }
*
* @param \October\Rain\Database\Model $model Model to fill.
* @param array $saveData Attribute values to fill model.
* @return array The collection of models to save.
*/
protected function prepareModelsToSave($model, $saveData)
{
$this->modelsToSave = [];
$this->setModelAttributes($model, $saveData);
$this->modelsToSave = array_reverse($this->modelsToSave);
return $this->modelsToSave;
}
/**
* Sets a data collection to a model attributes, relations are also set.
*
* @param \October\Rain\Database\Model $model Model to fill.
* @param array $saveData Attribute values to fill model.
* @return void
*/
protected function setModelAttributes($model, $saveData)
{
$this->modelsToSave[] = $model;
if (!is_array($saveData)) {
return;
}
if ($model instanceof HalcyonModel) {
$model->fill($saveData);
return;
}
$attributesToPurge = [];
$singularTypes = ['belongsTo', 'hasOne', 'morphTo', 'morphOne'];
foreach ($saveData as $attribute => $value) {
$isNested = $attribute == 'pivot' || (
$model->hasRelation($attribute) &&
in_array($model->getRelationType($attribute), $singularTypes)
);
if ($isNested && is_array($value)) {
$this->setModelAttributes($model->{$attribute}, $value);
}
elseif ($value !== FormField::NO_SAVE_DATA) {
if (Str::startsWith($attribute, '_')) {
$attributesToPurge[] = $attribute;
}
$model->{$attribute} = $value;
}
}
if ($attributesToPurge) {
$this->deferPurgedSaveAttributes($model, $attributesToPurge);
}
}
/**
* Removes an array of attributes from the model. If the model implements
* the Purgeable trait, this is preferred over the internal logic.
*
* @param \October\Rain\Database\Model $model Model to adjust.
* @param array $attributesToPurge Attribute values to remove from the model.
* @return void
*/
protected function deferPurgedSaveAttributes($model, $attributesToPurge)
{
if (!is_array($attributesToPurge)) {
return;
}
/*
* Compatibility with Purgeable trait:
* This will give the ability to restore purged attributes
* and make them available again if necessary.
*/
if (method_exists($model, 'getPurgeableAttributes')) {
$model->addPurgeable($attributesToPurge);
}
else {
$model->bindEventOnce('model.saveInternal', function () use ($model, $attributesToPurge) {
foreach ($attributesToPurge as $attribute) {
unset($model->attributes[$attribute]);
}
});
}
}
}

View File

@ -1,101 +0,0 @@
<?php namespace Aweos\Resizer\Traits;
use Lang;
use ApplicationException;
use Exception;
/**
* Form Model Widget Trait
*
* Special logic for for form widgets that use a database stored model.
*
* @package october\backend
* @author Alexey Bobkov, Samuel Georges
*/
trait ResponsiveWidget
{
/**
* Returns the final model and attribute name of
* a nested HTML array attribute.
* Eg: list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
* @param string $attribute.
* @return array
*/
public function resolveModelAttribute($attribute)
{
try {
return $this->formField->resolveModelAttribute($this->model, $attribute);
}
catch (Exception $ex) {
throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', [
'class' => get_class($this->model),
'relation' => $attribute
]));
}
}
/**
* Returns the model of a relation type,
* supports nesting via HTML array.
* @return Relation
*/
protected function getRelationModel()
{
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
if (!$model) {
throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', [
'class' => get_class($this->model),
'relation' => $this->valueFrom
]));
}
if (!$model->hasRelation($attribute)) {
throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', [
'class' => get_class($model),
'relation' => $attribute
]));
}
return $model->makeRelation($attribute);
}
/**
* Returns the value as a relation object from the model,
* supports nesting via HTML array.
* @return Relation
*/
protected function getRelationObject()
{
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
if (!$model) {
throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', [
'class' => get_class($this->model),
'relation' => $this->valueFrom
]));
}
if (!$model->hasRelation($attribute)) {
throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', [
'class' => get_class($model),
'relation' => $attribute
]));
}
return $model->{$attribute}();
}
/**
* Returns the value as a relation type from the model,
* supports nesting via HTML array.
* @return Relation
*/
protected function getRelationType()
{
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
return $model->getRelationType($attribute);
}
}

View File

@ -1,38 +0,0 @@
<?php namespace Aweos\Resizer\Updates;
use Schema;
use October\Rain\Database\Schema\Blueprint;
use October\Rain\Database\Updates\Migration;
class CreateResponsiveFilesTable extends Migration
{
public function up()
{
Schema::create('responsive_source_files', function(Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('basename');
$table->string('slug');
$table->string('extension');
$table->json('tags');
$table->string('aspect_ratio')->nullable();
$table->integer('min_width')->nullable();
$table->timestamps();
});
Schema::create('responsive_attachments', function(Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('file_id')->unsigned();
$table->string('title');
$table->string('slug');
$table->string('description')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('aweos_resizer_settings');
}
}

View File

@ -1,4 +1,2 @@
1.0.1: First version of resizer
1.0.2:
- Create attachments
- create_file_attachments_table.php
- create_settings_table.php