Compare commits
31 Commits
Author | SHA1 | Date |
---|---|---|
|
4a87b83b5a | |
|
09919a1c29 | |
|
820a725517 | |
|
2d39816954 | |
|
7e8f762885 | |
|
9c33c8f128 | |
|
cfb38ed792 | |
|
2473c7c55b | |
|
fb7f70fb12 | |
|
8a4dc77cfa | |
|
fb15678d4e | |
|
f2218d83ad | |
|
ff3516ca4f | |
|
c24c32afd3 | |
|
f269864194 | |
|
61ed6e0a3d | |
|
0c10472e2e | |
|
d429b8959b | |
|
00592aca0e | |
|
c385212f75 | |
|
bcf708ecc3 | |
|
63fb409951 | |
|
47c734dfd4 | |
|
9d8ae685f6 | |
|
f5e9aff0be | |
|
278bf58551 | |
|
41dce6c2a7 | |
|
0f58e0f1eb | |
|
87265191f2 | |
|
58867e87e4 | |
|
502bb41e97 |
|
@ -1 +1,3 @@
|
|||
/vendor/
|
||||
/.php-cs-fixer.cache
|
||||
/.phpunit.result.cache
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# Laravel Medialibrary Helper
|
||||
|
||||
This package creates routes for the popular Medialibrary Package from Spatie ().
|
||||
|
||||
## Available methods
|
||||
|
||||
In RegisterMediaCollections, you have the following methods available:
|
||||
|
||||
You can set a filename by default for the file. This accepts the associated Model, as well as the original filename. You should return the new name of the file with the extension (e.g. disc.jpg).
|
||||
|
||||
```
|
||||
forceFileName(fn ($model, $path) => Str::slug($path))
|
||||
```
|
||||
|
||||
You can set a max width (in Pixels) for images. This will resize the image BEFORE any normal Medialibrary conversions take place.
|
||||
|
||||
```
|
||||
maxWidth(fn () => 2500)
|
||||
```
|
||||
|
||||
You can call whatever you want after an image has been added, modified or deleted.
|
||||
|
||||
```
|
||||
->after(function ($model) {
|
||||
....
|
||||
})
|
||||
```
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Zoomyboy\\MedialibraryHelper\\Tests\\": "tests/",
|
||||
"Zoomyboy\\MedialibraryHelper\\": "src/"
|
||||
}
|
||||
},
|
||||
|
@ -15,6 +16,32 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"spatie/laravel-medialibrary": "^10.7"
|
||||
"ext-imagick": ">=3.6.0",
|
||||
"spatie/laravel-medialibrary": "^10.7",
|
||||
"laravel/framework": "^9.50",
|
||||
"spatie/laravel-data": "^3.1",
|
||||
"pestphp/pest": "^1.22"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"orchestra/testbench": "^7.0",
|
||||
"illuminate/console": "^9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"@php vendor/bin/testbench package:discover --ansi"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Zoomyboy\\MedialibraryHelper\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<php>
|
||||
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_URL" value="http://localhost:8000"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper;
|
||||
|
||||
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||
|
||||
class CollectionExtension
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
MediaCollection::mixin(new class()
|
||||
{
|
||||
public function forceFileName()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('forceFileName', $callback);
|
||||
}
|
||||
|
||||
public function storing()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('storing', $callback);
|
||||
}
|
||||
|
||||
public function destroyed()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('destroyed', $callback);
|
||||
}
|
||||
|
||||
public function after()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('after', $callback);
|
||||
}
|
||||
|
||||
public function withDefaultProperties()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('withDefaultProperties', $callback);
|
||||
}
|
||||
|
||||
public function stored()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('stored', $callback);
|
||||
}
|
||||
|
||||
public function withPropertyValidation()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('withPropertyValidation', $callback);
|
||||
}
|
||||
|
||||
public function withFallback()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('withFallback', $callback);
|
||||
}
|
||||
|
||||
public function maxWidth()
|
||||
{
|
||||
return fn ($callback) => $this->registerCustomCallback('maxWidth', $callback);
|
||||
}
|
||||
|
||||
public function runCallback()
|
||||
{
|
||||
return function (string $callback, ...$parameters) {
|
||||
$this->setDefaultCustomCallbacks();
|
||||
|
||||
return call_user_func($this->customCallbacks->get($callback), ...$parameters);
|
||||
};
|
||||
}
|
||||
|
||||
public function registerCustomCallback()
|
||||
{
|
||||
return function (string $name, callable $callback) {
|
||||
$this->setDefaultCustomCallbacks();
|
||||
$this->customCallbacks->put($name, $callback);
|
||||
|
||||
return $this;
|
||||
};
|
||||
}
|
||||
|
||||
public function setDefaultCustomCallbacks()
|
||||
{
|
||||
return function () {
|
||||
if (property_exists($this, 'customCallbacks')) {
|
||||
return;
|
||||
}
|
||||
$this->customCallbacks = collect([
|
||||
'forceFileName' => fn ($model, $name) => $name,
|
||||
'maxWidth' => fn ($size) => null,
|
||||
'stored' => fn ($event) => true,
|
||||
'after' => fn ($event) => true,
|
||||
'destroyed' => fn ($event) => true,
|
||||
'storing' => fn ($adder, $name) => $adder,
|
||||
'withDefaultProperties' => fn ($path, $pathinfo) => [],
|
||||
'withPropertyValidation' => fn ($path) => [],
|
||||
'withFallback' => fn ($parent) => null,
|
||||
]);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -2,79 +2,177 @@
|
|||
|
||||
namespace Zoomyboy\MedialibraryHelper;
|
||||
|
||||
use Imagick;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Spatie\Image\Image;
|
||||
use Spatie\LaravelData\DataCollection;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Exceptions\InvalidBase64Data;
|
||||
use Spatie\MediaLibrary\MediaCollections\FileAdder;
|
||||
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
class MediaController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'nullable|string',
|
||||
'content' => 'required|string',
|
||||
'name' => 'string',
|
||||
'model' => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
|
||||
]);
|
||||
|
||||
$model = $this->validateModel($request);
|
||||
$this->authorize('storeMedia', $model);
|
||||
|
||||
$collection = $model->getMediaCollection($request->input('collection'));
|
||||
$isSingle = 1 === $collection->collectionSizeLimit;
|
||||
$this->authorize('storeMedia', [$model, $collection->name]);
|
||||
|
||||
if ($collection->forceFileRenamer) {
|
||||
$fileRenamer = $collection->forceFileRenamer;
|
||||
$path = $fileRenamer($model);
|
||||
} else {
|
||||
$path = $request->input('name', Str::random(32));
|
||||
}
|
||||
$request->validate($isSingle ? [
|
||||
'payload' => 'array',
|
||||
'payload.*' => '',
|
||||
'payload.name' => 'required|string|max:255',
|
||||
'payload.content' => 'required|string',
|
||||
] : [
|
||||
'payload' => 'required|array|min:1',
|
||||
'payload.*' => 'array',
|
||||
'payload.*.name' => 'string',
|
||||
'payload.*.content' => 'string',
|
||||
]);
|
||||
|
||||
$content = $isSingle ? [$request->input('content')] : $request->input('content');
|
||||
$content = $isSingle ? [$request->input('payload')] : $request->input('payload');
|
||||
|
||||
$medias = collect([]);
|
||||
foreach ($content as $c) {
|
||||
Storage::disk('public')->put($path, base64_decode($c));
|
||||
$medias = collect($content)->map(function ($c) use ($collection, $model) {
|
||||
$pathinfo = pathinfo($c['name']);
|
||||
$basename = $collection->runCallback('forceFileName', $model, $pathinfo['filename']);
|
||||
$path = $basename . '.' . $pathinfo['extension'];
|
||||
|
||||
$medias->push($model->addMedia(Storage::disk('public')->path($path))->toMediaCollection($collection->name));
|
||||
}
|
||||
$adder = $this->fileAdderFromData($model, $c['content'], $collection)
|
||||
->usingName($basename)
|
||||
->usingFileName($path)
|
||||
->withCustomProperties($collection->runCallback('withDefaultProperties', $path, $pathinfo));
|
||||
|
||||
return response()->json($isSingle ? $medias->first()->toArray() : $medias->map(fn ($media) => $media->toArray()));
|
||||
return tap(
|
||||
$collection->runCallback('storing', $adder, $path)->toMediaCollection($collection->name),
|
||||
fn ($media) => $collection->runCallback('stored', $media)
|
||||
);
|
||||
});
|
||||
|
||||
$collection->runCallback('after', $model->fresh());
|
||||
|
||||
return $isSingle ? MediaData::from($medias->first()) : MediaData::collection($medias);
|
||||
}
|
||||
|
||||
public function index(Request $request, $parentModel, int $parentId, string $collection): JsonResponse
|
||||
public function update(Request $request, Media $media): MediaData
|
||||
{
|
||||
$this->authorize('updateMedia', [$media->model, $media->collection_name]);
|
||||
|
||||
$rules = collect($media->model->getMediaCollection($media->collection_name)->runCallback('withPropertyValidation', $media->file_name))
|
||||
->mapWithKeys(fn ($rule, $key) => ["properties.{$key}" => $rule])->toArray();
|
||||
|
||||
$validated = $request->validate($rules);
|
||||
|
||||
$media->update(['custom_properties' => data_get($validated, 'properties', [])]);
|
||||
$media->model->getMediaCollection($media->collection_name)->runCallback('after', $media->model->fresh());
|
||||
|
||||
return MediaData::from($media);
|
||||
}
|
||||
|
||||
public function index(Request $request, $parentModel, int $parentId, string $collectionName): MediaData|DataCollection
|
||||
{
|
||||
$model = app('media-library-helpers')->get($parentModel);
|
||||
$model = $model::findOrFail($parentId);
|
||||
$isSingle = $model->getMediaCollection($collection)->collectionSizeLimit;
|
||||
$model = $model::find($parentId);
|
||||
$this->authorize('listMedia', [$model, $collectionName]);
|
||||
$collection = $model->getMediaCollection($collectionName);
|
||||
$isSingle = 1 === $collection->collectionSizeLimit;
|
||||
|
||||
return response()->json([
|
||||
'data' => $isSingle ? $model->getFirstMedia($collection) : $model->getMedia($collection)->map(fn ($c) => $c->toArray()),
|
||||
]);
|
||||
abort_if($isSingle && !$model->getFirstMedia($collectionName) && !MediaData::defaultFromCollection($model, $collection), 404);
|
||||
|
||||
if ($isSingle && !$model->getFirstMedia($collectionName)) {
|
||||
return MediaData::defaultFromCollection($model, $collection);
|
||||
}
|
||||
|
||||
return $isSingle
|
||||
? MediaData::from($model->getFirstMedia($collectionName))
|
||||
: MediaData::collection($model->getMedia($collectionName));
|
||||
}
|
||||
|
||||
public function destroy(Media $media, Request $request): JsonResponse
|
||||
{
|
||||
$this->authorize('destroyMedia', $media->model);
|
||||
$this->authorize('destroyMedia', [$media->model, $media->collection_name]);
|
||||
$model = $media->model->fresh();
|
||||
$collection = $model->getMediaCollection($media->collection_name);
|
||||
$media->delete();
|
||||
$collection->runCallback('destroyed', $media->model->fresh());
|
||||
$collection->runCallback('after', $media->model->fresh());
|
||||
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
private function validateModel(Request $request): HasMedia
|
||||
protected function hasCallback(MediaCollection $collection, string $callback): bool
|
||||
{
|
||||
return property_exists($collection, $callback);
|
||||
}
|
||||
|
||||
protected function validateModel(Request $request): HasMedia
|
||||
{
|
||||
$model = app('media-library-helpers')->get($request->input('model'));
|
||||
|
||||
$request->validate([
|
||||
'collection' => ['required', 'string', Rule::in((new $model())->getRegisteredMediaCollections()->map(fn ($collection) => $collection->name)->toArray())],
|
||||
'collection' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name')),
|
||||
],
|
||||
]);
|
||||
|
||||
return $model::findOrFail($request->input('id'));
|
||||
$model = $model::find($request->input('id'));
|
||||
if (!$model) {
|
||||
throw ValidationException::withMessages(['model' => 'nicht gefunden']);
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
protected function fileAdderFromData($model, $data, $collection): FileAdder
|
||||
{
|
||||
$maxWidth = $collection->runCallback('maxWidth', 9);
|
||||
if (str_contains($data, ';base64')) {
|
||||
[$_, $data] = explode(';', $data);
|
||||
[$_, $data] = explode(',', $data);
|
||||
}
|
||||
|
||||
// strict mode filters for non-base64 alphabet characters
|
||||
$binaryData = base64_decode($data, true);
|
||||
|
||||
if (false === $binaryData) {
|
||||
throw InvalidBase64Data::create();
|
||||
}
|
||||
|
||||
// decoding and then reencoding should not change the data
|
||||
if (base64_encode($binaryData) !== $data) {
|
||||
throw InvalidBase64Data::create();
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'media-library');
|
||||
file_put_contents($tmpFile, $binaryData);
|
||||
|
||||
$i = (new Imagick());
|
||||
$i->readImage($tmpFile);
|
||||
|
||||
if ($i->getImageFormat() === 'HEIC') {
|
||||
$i->setFormat('jpg');
|
||||
$i->writeImage($tmpFile);
|
||||
}
|
||||
|
||||
if (null !== $maxWidth && 'image/jpeg' === mime_content_type($tmpFile)) {
|
||||
Image::load($tmpFile)->width($maxWidth)->save();
|
||||
}
|
||||
|
||||
return $model->addMedia($tmpFile);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
#[MapOutputName(SnakeCaseMapper::class)]
|
||||
class MediaData extends Data
|
||||
{
|
||||
public ?int $id;
|
||||
|
||||
public string $originalUrl;
|
||||
|
||||
public int $size;
|
||||
|
||||
public string $name;
|
||||
|
||||
public string $collectionName;
|
||||
|
||||
public string $fileName;
|
||||
|
||||
public string $mimeType;
|
||||
|
||||
#[MapInputName('custom_properties')]
|
||||
public array $properties;
|
||||
|
||||
public array $conversions = [];
|
||||
|
||||
public bool $fallback = false;
|
||||
|
||||
public static function fromMedia(Media $media): self
|
||||
{
|
||||
$conversions = collect($media->getMediaConversionNames())->flip()->map(fn ($integer, $conversion) => $media->hasGeneratedConversion($conversion)
|
||||
? ['original_url' => $media->getFullUrl($conversion)]
|
||||
: null,
|
||||
);
|
||||
|
||||
return self::withoutMagicalCreationFrom([
|
||||
...$media->toArray(),
|
||||
'conversions' => $conversions->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
$mime = Str::slug($this->mimeType);
|
||||
|
||||
return [
|
||||
'icon' => Storage::disk('public')->url("filetypes/{$mime}.svg"),
|
||||
];
|
||||
}
|
||||
|
||||
public static function defaultFromCollection(HasMedia $parent, MediaCollection $collection): ?self
|
||||
{
|
||||
$default = $collection->runCallback('withFallback', $parent);
|
||||
|
||||
if (is_null($default)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::from([
|
||||
'id' => null,
|
||||
'originalUrl' => Storage::disk($default[1])->url($default[0]),
|
||||
'size' => -1,
|
||||
'collection_name' => $collection->name,
|
||||
'name' => pathinfo($default[0], PATHINFO_FILENAME),
|
||||
'file_name' => pathinfo($default[0], PATHINFO_BASENAME),
|
||||
'properties' => [],
|
||||
'fallback' => true,
|
||||
'mime_type' => Storage::disk($default[1])->mimeType($default[0]),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
class OrderController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function __invoke(Request $request, $parentModel, int $parentId, string $collectionName)
|
||||
{
|
||||
$mediaCount = collect($request->order)->map(function ($media) {
|
||||
$media = Media::findOrFail($media);
|
||||
|
||||
return $media->model_id.'_'.$media->model_type;
|
||||
})->unique()->count();
|
||||
|
||||
if (1 !== $mediaCount) {
|
||||
throw ValidationException::withMessages(['order' => 'Sortierung von verschiedenen Medien nicht möglich.']);
|
||||
}
|
||||
|
||||
$model = app('media-library-helpers')->get($parentModel);
|
||||
$model = $model::find($parentId);
|
||||
$this->authorize('listMedia', [$model, $collectionName]);
|
||||
|
||||
Media::setNewOrder($request->order);
|
||||
|
||||
$model->getMediaCollection($collectionName)->runCallback('after', $model->fresh());
|
||||
|
||||
return MediaData::collection($model->getMedia($collectionName));
|
||||
}
|
||||
}
|
|
@ -4,27 +4,35 @@ namespace Zoomyboy\MedialibraryHelper;
|
|||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
|
||||
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||
|
||||
class ServiceProvider extends BaseServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
app()->bind('media-library-helpers', fn () => collect([]));
|
||||
app()->singleton(CollectionExtension::class, fn () => new CollectionExtension());
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
app(Router::class)->group(['middleware' => ['web', 'auth:web']], function ($router) {
|
||||
app(Router::class)->group($this->routeGroup(), function ($router) {
|
||||
$router->post('mediaupload', [MediaController::class, 'store'])->name('media.store');
|
||||
$router->delete('mediaupload/{media}', [MediaController::class, 'destroy'])->name('media.destroy');
|
||||
$router->get('mediaupload/{parent_model}/{parent_id}/{collection}', [MediaController::class, 'index'])->name('media.index');
|
||||
$router->get('mediaupload/{parent_model}/{parent_id}/{collection_name}', [MediaController::class, 'index'])->name('media.index');
|
||||
$router->patch('mediaupload/{parent_model}/{parent_id}/{collection_name}', OrderController::class)->name('media.order');
|
||||
$router->patch('mediaupload/{media}', [MediaController::class, 'update'])->name('media.update');
|
||||
});
|
||||
|
||||
MediaCollection::macro('forceFileName', function ($callback) {
|
||||
$this->forceFileRenamer = $callback;
|
||||
app(CollectionExtension::class)->boot();
|
||||
}
|
||||
|
||||
return $this;
|
||||
});
|
||||
/**
|
||||
* @return array{middleware: array<int, string>}
|
||||
*/
|
||||
protected function routeGroup(): array
|
||||
{
|
||||
return [
|
||||
'middleware' => config('media-library.middleware'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
|
||||
class MediaChange
|
||||
{
|
||||
use Dispatchable;
|
||||
use SerializesModels;
|
||||
|
||||
public function __construct(public HasMedia $model)
|
||||
{
|
||||
}
|
||||
|
||||
public function broadcastOn()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
|
||||
class MediaDestroyed
|
||||
{
|
||||
use Dispatchable;
|
||||
use SerializesModels;
|
||||
|
||||
public function __construct(public HasMedia $model)
|
||||
{
|
||||
}
|
||||
|
||||
public function broadcastOn()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
class MediaStored
|
||||
{
|
||||
use Dispatchable;
|
||||
use SerializesModels;
|
||||
|
||||
public function __construct(public Media $media)
|
||||
{
|
||||
}
|
||||
|
||||
public function broadcastOn()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaDestroyed;
|
||||
|
||||
test('it deletes multiple media', function () {
|
||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('multipleForced');
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('multipleForced');
|
||||
$media = $post->getFirstMedia('multipleForced');
|
||||
|
||||
$this->deleteJson("/mediaupload/{$media->id}")->assertStatus(200);
|
||||
|
||||
$this->assertCount(1, $post->fresh()->getMedia('multipleForced'));
|
||||
});
|
||||
|
||||
test('it deletes single media', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('defaultSingleFile');
|
||||
$media = $post->getFirstMedia('defaultSingleFile');
|
||||
|
||||
$this->deleteJson("/mediaupload/{$media->id}")->assertStatus(200);
|
||||
|
||||
$this->assertCount(0, $post->fresh()->getMedia('defaultSingleFile'));
|
||||
});
|
||||
|
||||
test('it needs authorization', function () {
|
||||
$this->auth(['destroyMedia' => false])->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('defaultSingleFile');
|
||||
$media = $post->getFirstMedia('defaultSingleFile');
|
||||
|
||||
$this->deleteJson("/mediaupload/{$media->id}")->assertStatus(403);
|
||||
});
|
||||
|
||||
test('it fires event', function () {
|
||||
Event::fake();
|
||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('singleWithEvent');
|
||||
$media = $post->getFirstMedia('singleWithEvent');
|
||||
|
||||
$this->deleteJson("/mediaupload/{$media->id}")->assertStatus(200);
|
||||
|
||||
Event::assertDispatched(MediaDestroyed::class, fn ($event) => $event->model->is($post));
|
||||
Event::assertDispatched(MediaChange::class, fn ($event) => $event->model->is($post));
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
test('it gets all medias', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$firstMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||
$secondMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/images");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('0.id', $firstMedia->id);
|
||||
$response->assertJsonPath('1.id', $secondMedia->id);
|
||||
$response->assertJsonPath('1.properties.test', 'old');
|
||||
$response->assertJsonPath('1.icon', url('storage/filetypes/applicationpdf.svg'));
|
||||
});
|
||||
|
||||
test('it gets media in order', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$firstMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||
$secondMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||
$thirdMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||
$order = $post->getMedia('images')->pluck('id');
|
||||
$order->prepend($order->pop());
|
||||
Media::setNewOrder($order->toArray());
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/images");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('0.id', $thirdMedia->id);
|
||||
$response->assertJsonPath('1.id', $firstMedia->id);
|
||||
$response->assertJsonPath('2.id', $secondMedia->id);
|
||||
});
|
||||
|
||||
test('it gets media for single', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$media = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('defaultSingleFile');
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/defaultSingleFile");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('id', $media->id);
|
||||
$response->assertJsonPath('properties.test', 'old');
|
||||
$response->assertJsonPath('fallback', false);
|
||||
$response->assertJsonPath('mime_type', 'application/pdf');
|
||||
});
|
||||
|
||||
test('it checks for authorization', function () {
|
||||
$this->auth(['listMedia' => false])->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/images");
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
|
||||
test('it returns 404 when media not found', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/defaultSingleFile");
|
||||
|
||||
$response->assertStatus(404);
|
||||
});
|
||||
|
||||
test('test it gets conversions for single media', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$media = $post->addMedia($this->jpgFile()->getPathname())->preservingOriginal()->toMediaCollection('conversionsWithDefault');
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/conversionsWithDefault");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('conversions.tiny.original_url', $media->getFullUrl('tiny'));
|
||||
});
|
||||
|
||||
test('test it gets default single media', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
Storage::disk('public')->put('default.jpg', $this->jpgFile()->getContent());
|
||||
|
||||
$response = $this->getJson("/mediaupload/post/{$post->id}/conversionsWithDefault");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('id', null);
|
||||
$response->assertJsonPath('original_url', Storage::disk('public')->url('default.jpg'));
|
||||
$response->assertJsonPath('name', 'default');
|
||||
$response->assertJsonPath('file_name', 'default.jpg');
|
||||
$response->assertJsonPath('fallback', true);
|
||||
$response->assertJsonPath('mime_type', 'image/jpeg');
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
||||
|
||||
test('it can reorder media', function () {
|
||||
Event::fake();
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
$post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
$post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
$order = $post->getMedia('images')->pluck('id');
|
||||
$order->prepend($order->pop());
|
||||
|
||||
$response = $this->patchJson("/mediaupload/post/{$post->id}/images", [
|
||||
'order' => $order,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('0.id', $order->get(0));
|
||||
$response->assertJsonPath('1.id', $order->get(1));
|
||||
$response->assertJsonPath('2.id', $order->get(2));
|
||||
$this->assertEquals($order, $post->fresh()->getMedia('images')->pluck('id'));
|
||||
Event::assertDispatched(MediaChange::class, fn ($event) => $event->model->is($post));
|
||||
});
|
||||
|
||||
test('images should belong to same model', function () {
|
||||
$this->auth()->registerModel();
|
||||
|
||||
$post = $this->newPost();
|
||||
$firstMedia = $post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
|
||||
$post = $this->newPost();
|
||||
$secondMedia = $post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
$thirdMedia = $post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
|
||||
$response = $this->patchJson("/mediaupload/post/{$post->id}/images", [
|
||||
'order' => [$firstMedia->id, $secondMedia->id, $thirdMedia->id],
|
||||
]);
|
||||
|
||||
$response->assertJsonValidationErrors('order');
|
||||
});
|
||||
|
||||
test('it should authorize', function () {
|
||||
$this->auth(['listMedia' => false])->registerModel();
|
||||
|
||||
$post = $this->newPost();
|
||||
$media = $post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
|
||||
$response = $this->patchJson("/mediaupload/post/{$post->id}/images", [
|
||||
'order' => [$media->id],
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
||||
|
||||
test('it updates a single files properties', function () {
|
||||
Event::fake();
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('multipleProperties');
|
||||
$media = $post->getFirstMedia('multipleProperties');
|
||||
|
||||
$response = $this->patchJson("/mediaupload/{$media->id}", [
|
||||
'properties' => [
|
||||
'test' => 'new',
|
||||
'missing' => 'value',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertEquals('new', $media->fresh()->getCustomProperty('test'));
|
||||
$this->assertEquals(null, $media->fresh()->getCustomProperty('missing'));
|
||||
$response->assertJsonPath('properties.test', 'new');
|
||||
$response->assertJsonMissingPath('properties.missing');
|
||||
Event::assertDispatched(MediaChange::class, fn ($event) => $event->model->is($post));
|
||||
});
|
||||
|
||||
test('it validates a single files properties', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('multipleProperties');
|
||||
$media = $post->getFirstMedia('multipleProperties');
|
||||
|
||||
$response = $this->patchJson("/mediaupload/{$media->id}", [
|
||||
'properties' => [
|
||||
'test' => 'new feswfewfwewefew wewe ew ewewf wefwfwefwefwefwewefewwedw sad fd',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors('properties.test');
|
||||
});
|
||||
|
||||
test('it checks for authorization', function () {
|
||||
$this->auth(['updateMedia' => false])->registerModel();
|
||||
$post = $this->newPost();
|
||||
$post->addMedia($this->pdfFile()->getPathname())->preservingOriginal()->toMediaCollection('multipleProperties');
|
||||
$media = $post->getFirstMedia('multipleProperties');
|
||||
|
||||
$response = $this->patchJson("/mediaupload/{$media->id}", [
|
||||
'properties' => [
|
||||
'test' => 'new',
|
||||
'missing' => 'value',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
|
@ -0,0 +1,335 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaStored;
|
||||
|
||||
test('it uploads a single file to a single file collection', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'defaultSingleFile',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$this->assertCount(1, $post->getMedia('defaultSingleFile'));
|
||||
$media = $post->getFirstMedia('defaultSingleFile');
|
||||
$response->assertJsonPath('id', $media->id);
|
||||
$response->assertJsonPath('original_url', $media->getFullUrl());
|
||||
$response->assertJsonPath('size', 3028);
|
||||
$response->assertJsonPath('name', 'beispiel bild');
|
||||
$response->assertJsonPath('collection_name', 'defaultSingleFile');
|
||||
$response->assertJsonPath('file_name', 'beispiel-bild.jpg');
|
||||
$response->assertJsonMissingPath('model_type');
|
||||
$response->assertJsonMissingPath('model_id');
|
||||
});
|
||||
|
||||
test('it uploads heig image', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->getFile('heic.jpg', 'heic.jpg')->getContent());
|
||||
|
||||
$this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'conversionsWithDefault',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
])->assertStatus(201);
|
||||
});
|
||||
|
||||
test('it uploads a single image to a single file collection', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->jpgFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'defaultSingleFile',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$this->assertCount(1, $post->getMedia('defaultSingleFile'));
|
||||
});
|
||||
|
||||
test('it forces a filename for a single collection', function () {
|
||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'singleForced',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$this->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('singleForced')->file_name);
|
||||
$response->assertJsonPath('name', 'beispiel bild 2023-04-04');
|
||||
$response->assertJsonPath('file_name', 'beispiel-bild-2023-04-04.jpg');
|
||||
});
|
||||
|
||||
test('it sets custom title when storing', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'singleStoringHook',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$media = $post->getFirstMedia('singleStoringHook');
|
||||
|
||||
$this->assertEquals('AAA', $media->getCustomProperty('use'));
|
||||
$this->assertEquals('beispiel bild', $media->getCustomProperty('ttt'));
|
||||
});
|
||||
|
||||
test('it sets custom properties from properties method', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'multipleProperties',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$media = $post->getFirstMedia('multipleProperties');
|
||||
|
||||
$this->assertEquals('beispielBild.jpg', $media->getCustomProperty('test'));
|
||||
});
|
||||
|
||||
test('it forces a filename for multiple collections', function () {
|
||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'multipleForced',
|
||||
'payload' => [
|
||||
[
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$this->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('multipleForced')->file_name);
|
||||
});
|
||||
|
||||
test('it throws event when file has been uploaded', function () {
|
||||
Event::fake();
|
||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'singleWithEvent',
|
||||
'payload' => [
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
Event::assertDispatched(MediaStored::class, fn ($event) => $event->media->id === $response->json('id'));
|
||||
Event::assertDispatched(MediaChange::class, fn ($event) => $event->model->is($post));
|
||||
});
|
||||
|
||||
test('it throws event when multiple files uploaded', function () {
|
||||
Event::fake();
|
||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
||||
$post = $this->newPost();
|
||||
$content = base64_encode($this->pdfFile()->getContent());
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'multipleFilesWithEvent',
|
||||
'payload' => [
|
||||
[
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
[
|
||||
'content' => $content,
|
||||
'name' => 'beispiel bild 1.jpg',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
Event::assertDispatched(MediaStored::class, fn ($event) => $event->media->id === $response->json('0.id'));
|
||||
Event::assertDispatched(MediaStored::class, fn ($event) => $event->media->id === $response->json('1.id'));
|
||||
});
|
||||
|
||||
test('it uploads multiple files', function () {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
$file = $this->pdfFile();
|
||||
$post->addMedia($file->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'images',
|
||||
'payload' => [
|
||||
[
|
||||
'content' => base64_encode($file->getContent()),
|
||||
'name' => 'aaaa.jpg',
|
||||
],
|
||||
[
|
||||
'content' => base64_encode($file->getContent()),
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$this->assertCount(3, $post->getMedia('images'));
|
||||
$media = $post->getMedia('images')->skip(1)->values();
|
||||
$this->assertCount(2, $response->json());
|
||||
$response->assertJsonPath('0.id', $media->get(0)->id);
|
||||
$response->assertJsonPath('1.id', $media->get(1)->id);
|
||||
$response->assertJsonPath('0.original_url', $media->first()->getFullUrl());
|
||||
$response->assertJsonPath('0.size', 3028);
|
||||
$response->assertJsonPath('0.name', 'aaaa');
|
||||
$response->assertJsonPath('0.collection_name', 'images');
|
||||
$response->assertJsonPath('0.file_name', 'aaaa.jpg');
|
||||
$response->assertJsonMissingPath('0.model_type');
|
||||
$response->assertJsonMissingPath('0.model_id');
|
||||
});
|
||||
|
||||
test('it returns 403 when not authorized', function () {
|
||||
$this->auth(['storeMedia' => false])->registerModel();
|
||||
$post = $this->newPost();
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'defaultSingleFile',
|
||||
'payload' => [
|
||||
'content' => base64_encode($this->pdfFile()->getContent()),
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
|
||||
test('it needs validation for single files', function (array $payload, string $invalidFieldName) {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'defaultSingleFile',
|
||||
'payload' => [
|
||||
'content' => base64_encode($this->pdfFile()->getContent()),
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
...$payload,
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors($invalidFieldName);
|
||||
})->with(function () {
|
||||
yield [['model' => 'missingmodel'], 'model'];
|
||||
yield [['id' => -1], 'model'];
|
||||
yield [['collection' => 'missingcollection'], 'collection'];
|
||||
yield [['payload' => ['name' => 'AAA', 'content' => []]], 'payload.content'];
|
||||
yield [['payload' => ['name' => 'AAA', 'content' => ['UU']]], 'payload.content'];
|
||||
yield [['payload' => ['name' => 'AAA', 'content' => null]], 'payload.content'];
|
||||
yield [['payload' => ['name' => 'AAA', 'content' => '']], 'payload.content'];
|
||||
yield [['payload' => ['name' => 'AAA', 'content' => 1]], 'payload.content'];
|
||||
yield [['payload' => ['name' => '', 'content' => 'aaadfdf']], 'payload.name'];
|
||||
yield [['payload' => ['name' => ['U'], 'content' => 'aaadfdf']], 'payload.name'];
|
||||
yield [['payload' => ['name' => 1, 'content' => 'aaadfdf']], 'payload.name'];
|
||||
yield [['payload' => ['name' => null, 'content' => 'aaadfdf']], 'payload.name'];
|
||||
yield [['payload' => 'lalal'], 'payload'];
|
||||
yield [['payload' => 55], 'payload'];
|
||||
});
|
||||
|
||||
test('it needs validation for multiple files', function (array $payload, string $invalidFieldName) {
|
||||
$this->auth()->registerModel();
|
||||
$post = $this->newPost();
|
||||
|
||||
$response = $this->postJson('/mediaupload', [
|
||||
'model' => 'post',
|
||||
'id' => $post->id,
|
||||
'collection' => 'images',
|
||||
'payload' => [
|
||||
[
|
||||
'content' => base64_encode($this->pdfFile()->getContent()),
|
||||
'name' => 'beispiel bild.jpg',
|
||||
],
|
||||
],
|
||||
...$payload,
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors($invalidFieldName);
|
||||
})->with(function () {
|
||||
yield [['model' => 'missingmodel'], 'model'];
|
||||
yield [['id' => -1], 'model'];
|
||||
yield [['collection' => 'missingcollection'], 'collection'];
|
||||
yield [['payload' => 'lalal'], 'payload'];
|
||||
yield [['payload' => []], 'payload'];
|
||||
yield [['payload' => 1], 'payload'];
|
||||
|
||||
yield [['payload' => [['name' => 'AAA', 'content' => []]]], 'payload.0.content'];
|
||||
yield [['payload' => [['name' => 'AAA', 'content' => ['UU']]]], 'payload.0.content'];
|
||||
yield [['payload' => [['name' => 'AAA', 'content' => null]]], 'payload.0.content'];
|
||||
yield [['payload' => [['name' => 'AAA', 'content' => '']]], 'payload.0.content'];
|
||||
yield [['payload' => [['name' => 'AAA', 'content' => 1]]], 'payload.0.content'];
|
||||
yield [['payload' => [['name' => '', 'content' => 'aaadfdf']]], 'payload.0.name'];
|
||||
yield [['payload' => [['name' => ['U'], 'content' => 'aaadfdf']]], 'payload.0.name'];
|
||||
yield [['payload' => [['name' => 1, 'content' => 'aaadfdf']]], 'payload.0.name'];
|
||||
yield [['payload' => [['name' => null, 'content' => 'aaadfdf']]], 'payload.0.name'];
|
||||
yield [['payload' => ['RRR']], 'payload.0'];
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaDestroyed;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaStored;
|
||||
|
||||
class Post extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('defaultSingleFile')->maxWidth(fn () => 250)->singleFile();
|
||||
|
||||
$this->addMediaCollection('conversionsWithDefault')
|
||||
->singleFile()
|
||||
->withFallback(fn ($parent) => ['default.jpg', 'public'])
|
||||
->registerMediaConversions(function () {
|
||||
$this->addMediaConversion('tiny')->width(200)->height(200);
|
||||
});
|
||||
|
||||
$this->addMediaCollection('images')->after(fn ($model) => Event::dispatch(new MediaChange($model)));
|
||||
|
||||
$this->addMediaCollection('singleForced')->singleFile()->forceFileName(function ($model, $name) {
|
||||
return $name . ' ' . now()->format('Y-m-d');
|
||||
});
|
||||
|
||||
$this->addMediaCollection('multipleForced')->forceFileName(function ($model, $name) {
|
||||
return $name . ' ' . now()->format('Y-m-d');
|
||||
});
|
||||
|
||||
$this->addMediaCollection('singleStoringHook')->singleFile()->storing(function ($adder, $fileName) {
|
||||
return $adder->withCustomProperties([
|
||||
'use' => 'AAA',
|
||||
'ttt' => pathinfo($fileName, PATHINFO_FILENAME),
|
||||
]);
|
||||
});
|
||||
|
||||
$this->addMediaCollection('singleWithEvent')->singleFile()->stored(function (Media $media) {
|
||||
Event::dispatch(new MediaStored($media));
|
||||
})
|
||||
->destroyed(fn ($model) => Event::dispatch(new MediaDestroyed($model)))
|
||||
->after(fn ($model) => Event::dispatch(new MediaChange($model)));
|
||||
|
||||
$this->addMediaCollection('multipleFilesWithEvent')->stored(function (Media $media) {
|
||||
Event::dispatch(new MediaStored($media));
|
||||
});
|
||||
|
||||
$this->addMediaCollection('multipleProperties')->singleFile()->withDefaultProperties(fn ($path, $pathinfo) => [
|
||||
'test' => Str::camel($path),
|
||||
])->withPropertyValidation(fn ($path) => [
|
||||
'test' => 'string|max:10',
|
||||
])
|
||||
->after(fn ($model) => Event::dispatch(new MediaChange($model)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
uses(Zoomyboy\MedialibraryHelper\Tests\TestCase::class)->in('Feature');
|
||||
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
|
||||
uses()->beforeEach(fn () => Storage::fake('media'))->in('Feature');
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Zoomyboy\MedialibraryHelper\Tests;
|
||||
|
||||
use Illuminate\Http\File;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Orchestra\Testbench\TestCase as BaseTestCase;
|
||||
use Spatie\LaravelData\LaravelDataServiceProvider;
|
||||
use Spatie\MediaLibrary\MediaLibraryServiceProvider;
|
||||
use Zoomyboy\MedialibraryHelper\ServiceProvider;
|
||||
use Zoomyboy\MedialibraryHelper\Tests\Models\Post;
|
||||
|
||||
class TestCase extends BaseTestCase
|
||||
{
|
||||
/**
|
||||
* Define database migrations.
|
||||
*/
|
||||
protected function defineDatabaseMigrations(): void
|
||||
{
|
||||
$this->loadMigrationsFrom(__DIR__ . '/migrations');
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app): array
|
||||
{
|
||||
return [
|
||||
ServiceProvider::class,
|
||||
MediaLibraryServiceProvider::class,
|
||||
LaravelDataServiceProvider::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a pdf file with a filename and get path.
|
||||
*/
|
||||
protected function pdfFile(?string $filename = null): File
|
||||
{
|
||||
return $this->getFile('pdf.pdf', $filename ?: 'pdf.pdf');
|
||||
}
|
||||
|
||||
protected function jpgFile(?string $filename = null): File
|
||||
{
|
||||
return $this->getFile('jpg.jpg', $filename ?: 'jpg.jpg');
|
||||
}
|
||||
|
||||
protected function getFile(string $location, string $as): File
|
||||
{
|
||||
$path = __DIR__ . '/stubs/' . $location;
|
||||
$to = sys_get_temp_dir() . '/' . $as;
|
||||
copy($path, $to);
|
||||
|
||||
return new File($to);
|
||||
}
|
||||
|
||||
protected function registerModel(): static
|
||||
{
|
||||
app()->extend('media-library-helpers', fn ($p) => $p->put('post', Post::class));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function newPost(): Post
|
||||
{
|
||||
return Post::create(['title' => 'Lorem', 'content' => 'aafff']);
|
||||
}
|
||||
|
||||
protected function auth(array $policies = []): self
|
||||
{
|
||||
$policies = [
|
||||
'storeMedia' => true,
|
||||
'updateMedia' => true,
|
||||
'destroyMedia' => true,
|
||||
'listMedia' => true,
|
||||
...$policies,
|
||||
];
|
||||
|
||||
foreach ($policies as $ability => $result) {
|
||||
Gate::define($ability, fn (?string $user, string $collectionName) => $result);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function defineEnvironment($app)
|
||||
{
|
||||
$app['config']->set('media-library.middleware', ['web']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('media', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->morphs('model');
|
||||
$table->uuid('uuid')->nullable()->unique();
|
||||
$table->string('collection_name');
|
||||
$table->string('name');
|
||||
$table->string('file_name');
|
||||
$table->string('mime_type')->nullable();
|
||||
$table->string('disk');
|
||||
$table->string('conversions_disk')->nullable();
|
||||
$table->unsignedBigInteger('size');
|
||||
$table->json('manipulations');
|
||||
$table->json('custom_properties');
|
||||
$table->json('generated_conversions');
|
||||
$table->json('responsive_images');
|
||||
$table->unsignedInteger('order_column')->nullable()->index();
|
||||
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class InitialMigration extends Migration {
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('posts', function($table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->string('content');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 177 KiB |
|
@ -0,0 +1,198 @@
|
|||
%PDF-1.3
|
||||
%âãÏÓ
|
||||
|
||||
1 0 obj
|
||||
<<
|
||||
/Type /Catalog
|
||||
/Outlines 2 0 R
|
||||
/Pages 3 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<<
|
||||
/Type /Outlines
|
||||
/Count 0
|
||||
>>
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<<
|
||||
/Type /Pages
|
||||
/Count 2
|
||||
/Kids [ 4 0 R 6 0 R ]
|
||||
>>
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<<
|
||||
/Type /Page
|
||||
/Parent 3 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 9 0 R
|
||||
>>
|
||||
/ProcSet 8 0 R
|
||||
>>
|
||||
/MediaBox [0 0 612.0000 792.0000]
|
||||
/Contents 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Length 1074 >>
|
||||
stream
|
||||
2 J
|
||||
BT
|
||||
0 0 0 rg
|
||||
/F1 0027 Tf
|
||||
57.3750 722.2800 Td
|
||||
( A Simple PDF File ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 688.6080 Td
|
||||
( This is a small demonstration .pdf file - ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 664.7040 Td
|
||||
( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 652.7520 Td
|
||||
( text. And more text. And more text. And more text. ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 628.8480 Td
|
||||
( And more text. And more text. And more text. And more text. And more ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 616.8960 Td
|
||||
( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 604.9440 Td
|
||||
( more text. And more text. And more text. And more text. And more text. ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 592.9920 Td
|
||||
( And more text. And more text. ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 569.0880 Td
|
||||
( And more text. And more text. And more text. And more text. And more ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 557.1360 Td
|
||||
( text. And more text. And more text. Even more. Continued on page 2 ...) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<<
|
||||
/Type /Page
|
||||
/Parent 3 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 9 0 R
|
||||
>>
|
||||
/ProcSet 8 0 R
|
||||
>>
|
||||
/MediaBox [0 0 612.0000 792.0000]
|
||||
/Contents 7 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
<< /Length 676 >>
|
||||
stream
|
||||
2 J
|
||||
BT
|
||||
0 0 0 rg
|
||||
/F1 0027 Tf
|
||||
57.3750 722.2800 Td
|
||||
( Simple PDF File 2 ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 688.6080 Td
|
||||
( ...continued from page 1. Yet more text. And more text. And more text. ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 676.6560 Td
|
||||
( And more text. And more text. And more text. And more text. And more ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 664.7040 Td
|
||||
( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 652.7520 Td
|
||||
( paint dry. And more text. And more text. And more text. And more text. ) Tj
|
||||
ET
|
||||
BT
|
||||
/F1 0010 Tf
|
||||
69.2500 640.8000 Td
|
||||
( Boring. More, a little more text. The end, and just as well. ) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
8 0 obj
|
||||
[/PDF /Text]
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type1
|
||||
/Name /F1
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
>>
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
<<
|
||||
/Creator (Rave \(http://www.nevrona.com/rave\))
|
||||
/Producer (Nevrona Designs)
|
||||
/CreationDate (D:20060301072826)
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000019 00000 n
|
||||
0000000093 00000 n
|
||||
0000000147 00000 n
|
||||
0000000222 00000 n
|
||||
0000000390 00000 n
|
||||
0000001522 00000 n
|
||||
0000001690 00000 n
|
||||
0000002423 00000 n
|
||||
0000002456 00000 n
|
||||
0000002574 00000 n
|
||||
|
||||
trailer
|
||||
<<
|
||||
/Size 11
|
||||
/Root 1 0 R
|
||||
/Info 10 0 R
|
||||
>>
|
||||
|
||||
startxref
|
||||
2714
|
||||
%%EOF
|
Loading…
Reference in New Issue