diff --git a/.gitignore b/.gitignore index 2a52798..2983f55 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /public/hot /public/storage /storage/*.key +/storage/stub/*.webp /vendor .env .env.backup diff --git a/Dockerfile b/Dockerfile index 0ebc104..dd9f171 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,12 @@ RUN apt-get -y update \ && apt-get -y install --no-install-recommends libvips42 \ && apt-get -y install libvips-tools \ && apt-get -y install libvips-dev \ - && pecl install vips \ - && echo 'extension="vips.so"' > /usr/local/etc/php/conf.d/20-vips.ini -RUN echo 'ffi.enable = "true"' >> /usr/local/etc/php/conf.d/20-vips.ini - + && pecl install vips +#<<<<<<< HEAD +# && echo 'extension="vips.so"' > /usr/local/etc/php/conf.d/20-vips.ini +#RUN echo 'ffi.enable = "true"' >> /usr/local/etc/php/conf.d/20-vips.ini +# +#======= +# && echo 'extension="vips.so"' > /usr/local/etc/php/conf.d/20-vips.ini \ +# && echo "ffi.enable=true" >> /usr/local/etc/php/php.ini +#>>>>>>> 68568fb8827dca49383e3294693017472af49313 diff --git a/app/Console/Commands/TestGenerator.php b/app/Console/Commands/TestGenerator.php index e2a940b..a678eb8 100644 --- a/app/Console/Commands/TestGenerator.php +++ b/app/Console/Commands/TestGenerator.php @@ -46,6 +46,7 @@ class TestGenerator extends Command $models = $this->getSelectedModels(); + // if (!is_dir('tests/Feature/' . ucfirst($model))) { // mkdir('tests/Feature/' . ucfirst($model), 0777, true); // } else { diff --git a/app/Http/Controllers/CollectionController.php b/app/Http/Controllers/CollectionController.php index f389947..2fe3b5f 100644 --- a/app/Http/Controllers/CollectionController.php +++ b/app/Http/Controllers/CollectionController.php @@ -2,8 +2,10 @@ namespace App\Http\Controllers; +use App\Documents\PolicyDocument; use App\Http\Resources\CollectionResource; use App\Models\Collection; +use App\Utilities\Polices\BasePolicy; use Illuminate\Http\Request; class CollectionController extends Controller @@ -11,23 +13,30 @@ class CollectionController extends Controller public function show(Collection $collection) { + + BasePolicy::allow(app('modelDocument')?->getPolicy,$collection); + return new CollectionResource($collection); } public function store(Request $request) { + BasePolicy::allow(app('modelDocument')?->storePolicy); $collection = Collection::create($request->all()); return new CollectionResource($collection); } public function update(Request $request, Collection $collection) { + BasePolicy::allow(app('modelDocument')?->updatePolicy, $collection); + $collection->update($request->all()); return new CollectionResource($collection); } public function destroy(Collection $collection) { + BasePolicy::allow(app('modelDocument')?->{$collection->trashed() ? 'restorePolicy' : 'deletePolicy'},$collection); if ($collection->trashed()) { return $collection->restore(); } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 1af040a..6e92763 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Http\Controllers\Traits\FileTrait; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; @@ -9,7 +10,7 @@ use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController { - use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + use AuthorizesRequests, DispatchesJobs, ValidatesRequests, FileTrait; protected function resourceAbilityMap() { diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php index 136f81b..f159a36 100644 --- a/app/Http/Controllers/FileController.php +++ b/app/Http/Controllers/FileController.php @@ -3,53 +3,45 @@ namespace App\Http\Controllers; use App\Http\Requests\FileStoreRequest; +use App\Http\Requests\FileUpdateRequest; use App\Http\Resources\FileResource; -use App\Jobs\FileConversionQueue; +use App\Image\ImageProcessor; use App\Models\File; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Storage; + class FileController extends Controller { - public function show($collection, $uuid, $ext) + public function show($collection, $uuid, $ext, Request $request, ImageProcessor $imageProcessor) { - dump($collection, $uuid, $ext); - } - - public function private($collection, $path) - { - Storage::disk(app()->collection->disk)->download($path); + if ($request->castParams == 'resource') { + return new FileResource(app()->file); + } + if (!is_null(array_intersect(array_keys($request->all()), $this->availableParams))) { + $data = $imageProcessor->processToBuffer(app()->file->getPath(), $request->all()); + $response = Response::make($data, 200); + $response->header('Content-Type','image/webp'); + return $response; + } + return response()->file(app()->file->getPath()); } - public function store(Request $request) + public function store(FileStoreRequest $request, ImageProcessor $imageProcessor) { - // skip policy ---and first level---------- - - $validated = $request->validate([ - "file" => [ - "mimes:" . app()->collection->getExts(), - "mimetypes:" . app()->collection->getMimeTypes(), - "dimensions:min_width=" . app()->collection->min_width . ",min_height=" . app()->collection->min_height . ',max_width=' . app()->collection->max_width . ',max_height=' . app()->collection->max_height, - "max:" . app()->collection->max_size, - "min:" . app()->collection->min_size, - ], - "alts" => [app()->collection->alt_required ? "required" : "null", 'array'], - "alts.*" => [app()->collection->alt_required ? "required" : "null"], - "description" => [app()->collection->description_required ? "required" : "null"], - ]); - - - - - DB::beginTransaction(); - - $urlStorage = '/' . date('y') . '/' . date('m') . app()->getFileUuid; + $request->file = $imageProcessor->convertImage($request->file->path(), '/tmp/' . app()->uuid . '.' . app()->collection->ext); + if (!is_null(app()->collection->process)) { + $request->file = $imageProcessor->process($request->file->path(), '/tmp/' . app()->uuid . '.' . app()->collection->ext, app()->collection->process); + } - try { - $file = File::create([ - 'uuid' => app()->getFileUuid, + $fileResource = null; + DB::transaction(function () use ($request, &$fileResource) { + $uuid = app()->uuid; + $fileResource = File::create([ + 'uuid' => $uuid, 'original_name' => $request->name, 'public' => $request->public, 'ext' => $request->file->extension(), @@ -64,35 +56,34 @@ class FileController extends Controller 'ip' => $request->ip(), 'collection_id' => app()->collection->id, 'published_at' => $request->published_at, - 'server_path' => $urlStorage + 'server_path' => '/' . date('y') . '/' . date('m') . '/', ]); - if (!app()->collection->tmp_support && app()->collection->count == 1) { File::where('user_id', auth()->id())->delete(); } - $file = $request->file->storeAs($urlStorage, app()->getFileUuid, app()->collection->disk); + + $storedFile = Storage::disk(app()->collection->disk)->putFileAs($fileResource->server_path, $request->file, $fileResource->uuid . '.' . app()->collection->ext); + if (app()->collection->public) { - Storage::setVisibility($file, 'public'); + Storage::disk(app()->collection->disk)->setVisibility($storedFile, 'public'); } + }); - DB::commit(); - } catch (\Exception $e) { - DB::rollback(); - } - - FileConversionQueue::dispatch($file, app()->collection); - return new FileResource($file); + return new FileResource($fileResource); } - - public function update(Request $request, $id) + public function update(FileUpdateRequest $request) { - // + app()->file->update($request->all()); + return new FileResource(app()->file); } - public function destroy($id) + public function destroy() { - // + if (app()->file->trashed()) { + return app()->file->restore(); + } + return app()->file->delete(); } } diff --git a/app/Http/Controllers/Traits/FileTrait.php b/app/Http/Controllers/Traits/FileTrait.php new file mode 100644 index 0000000..3b5bddd --- /dev/null +++ b/app/Http/Controllers/Traits/FileTrait.php @@ -0,0 +1,8 @@ +route()->action['as'] == 'api.file.store') { - app()->bind('collection', function () use ($request) { - return Collection::where('name', $request->route('collections_name'))->get(); - }); - - app()->singleton('getFileUuid', function () { - return Str::uuid(); + if (($request->route()->action['as'] == 'api.files.store') || ($request->route()->action['as'] == "api.files.show") || ($request->route()->action['as'] == 'api.files.update')) { + app()->singleton('collection', function () use ($request) { + return Collection::where('name', $request->route('collection_name'))->firstOrFail(); }); } if ($request->route()->action['as'] == 'api.file.private') { - app()->bind('collection', function () use ($request) { - return Collection::where('name', $request->route('collections_name'))->get(); + app()->singleton('collection', function () use ($request) { + return Collection::where('name', $request->route('collection_name'))->firstOrFail(); }); } diff --git a/app/Http/Middleware/BindFileModelMiddleware.php b/app/Http/Middleware/BindFileModelMiddleware.php new file mode 100644 index 0000000..a77158d --- /dev/null +++ b/app/Http/Middleware/BindFileModelMiddleware.php @@ -0,0 +1,75 @@ +route()->action['as'] == 'api.files.store') && is_null($request->file('file'))) { + $file = File::find($request->file); + + if (!is_null($file)) { + $Collection = Collection::where('name', $request->route('collection_name'))->firstOrFail(); + if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { + app()->bind('file', function () use ($file) { + return $file; + }); + } + } + } + + if ($request->route()->action['as'] == 'api.files.show') { + $file = File::findOrFail($request->route('uuid')); + $Collection = Collection::where('name', $request->route('collection_name'))->firstOrFail(); + if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { + app()->bind('file', function () use ($file) { + return $file; + }); + }else{ + abort(404); + } + } + + if ($request->route()->action['as'] == 'api.files.update') { + $file = File::findOrFail($request->route('uuid')); + $Collection = Collection::where('name', $request->route('collection_name'))->firstOrFail(); + if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { + app()->bind('file', function () use ($file) { + return $file; + }); + }else{ + abort(404); + } + } + + if ($request->route()->action['as'] == 'api.files.destroy') { + $file = File::withTrashed()->findOrFail($request->route('uuid')); + $Collection = Collection::withTrashed()->where('name', $request->route('collection_name'))->firstOrFail(); + if (Storage::disk($Collection->disk)->exists($file->server_path . $file->uuid . '.' . $Collection->ext)) { + app()->bind('file', function () use ($file) { + return $file; + }); + }else{ + abort(404); + } + } + + return $next($request); + } +} diff --git a/app/Http/Requests/FileStoreRequest.php b/app/Http/Requests/FileStoreRequest.php new file mode 100644 index 0000000..ac10ba3 --- /dev/null +++ b/app/Http/Requests/FileStoreRequest.php @@ -0,0 +1,72 @@ +collection->tmp_support && !$this->model_id) { + return false; + } + if (app()->collection->count !== 1 && (app()->collection->count <= File::where('user_id', auth()->id())->where('collection_id',app()->collection->id)->count()) && !app()->collection->tmp_support) { + return false; + } + if (!app()->bound('file') && is_null($this->file('file'))) { + return false; + } + + if (!$this->hasFile('file')) { + $this->replace([ + 'file' => new \Illuminate\Http\File(app()->file->getPath(), app()->file->uuid . '.' . app()->collection->ext) + ]); + } + + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + + return [ + "file" => [ + "mimes:" . app()->collection->getExts(), + "mimetypes:" . app()->collection->getMimeTypes(), + !$this->isImage(request()->file->path()) ?: "dimensions:min_width=" . app()->collection->min_width . ",min_height=" . app()->collection->min_height . ',max_width=' . app()->collection->max_width . ',max_height=' . app()->collection->max_height, + "max:" . app()->collection->max_file_size, + "min:" . app()->collection->min_file_size, + ], + "alts" => [app()->collection->alt_required ? "required" : "nullable", 'array'], + "alts.*" => [app()->collection->alt_required ? "required" : "nullable", 'max:1000'], + "description" => [app()->collection->description_required ? "required" : "nullable", 'max:300'], + 'original_name' => ["string", "nullable", 'max:300'], + 'published_at' => ['date_format:Y-m-d H:i:s', 'nullable'], + ]; + } +} diff --git a/app/Http/Requests/FileUpdateRequest.php b/app/Http/Requests/FileUpdateRequest.php new file mode 100644 index 0000000..d4ac52c --- /dev/null +++ b/app/Http/Requests/FileUpdateRequest.php @@ -0,0 +1,34 @@ + + */ + public function rules() + { + return [ + "alts" => [app()->collection->alt_required ? "required" : "nullable", 'array'], + "alts.*" => [app()->collection->alt_required ? "required" : "nullable", 'max:1000'], + "description" => [app()->collection->description_required ? "required" : "nullable", 'max:300'], + 'original_name' => ["string", "nullable", 'max:300'], + 'published_at' => ['date_format:Y-m-d H:i:s', 'nullable'], + ]; + } +} diff --git a/app/Http/Resources/CollectionResource.php b/app/Http/Resources/CollectionResource.php index 24773b9..d98f5de 100644 --- a/app/Http/Resources/CollectionResource.php +++ b/app/Http/Resources/CollectionResource.php @@ -32,7 +32,7 @@ class CollectionResource extends JsonResource "description_required" => $this->description_required, "count" => $this->count, "exts" => $this->exts, - "avalible_exts" => $this->avalible_exts, + "ext" => $this->ext, "mimetypes" => $this->mimetypes, "model" => $this->model, "expire_date" => $this->expire_date, diff --git a/app/Http/Resources/FileResource.php b/app/Http/Resources/FileResource.php index 6eaf9fd..8b8205b 100644 --- a/app/Http/Resources/FileResource.php +++ b/app/Http/Resources/FileResource.php @@ -21,6 +21,7 @@ class FileResource extends JsonResource "mimetype" => $this->mimetype, "width" => $this->width, "height" => $this->height, + "server_path" => $this->server_path, "file_size" => $this->file_size, "sort" => $this->sort, "alts" => $this->alts, diff --git a/app/Image/ImageProcessor.php b/app/Image/ImageProcessor.php new file mode 100644 index 0000000..7aef495 --- /dev/null +++ b/app/Image/ImageProcessor.php @@ -0,0 +1,271 @@ + 100]) + { + if (array_key_exists('w', $options) && array_key_exists('h', $options) && array_key_exists('r', $options)) { + unset($options['r']); + } + + if (array_key_exists('r', $options)) { + $options['r'] = explode(':', $options['r']); + if (count($options['r']) != 2) { + unset($options['r']); + } + + $validator = Validator::make($options, [ + 'r' => ['nullable', 'array'], + 'r.*' => ['nullable', 'numeric'], + ]); + if ($validator->fails()) { + foreach ($validator->messages()->getMessages() as $field_name => $messages) { + unset($options[explode('.', $field_name)[0]]); + } + } + } + + if (array_key_exists('r', $options)) { + + if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $options['w'] = $options['w'] * $options['r'][0]; + $options['h'] = $options['w'] * $options['r'][1]; + } + + if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { + $options['w'] = $options['h'] * $options['r'][0]; + $options['h'] = $options['h'] * $options['r'][1]; + } + if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $options['w'] = getimagesize($filename)[0] * $options['r'][0]; + $options['h'] = getimagesize($filename)[0] * $options['r'][1]; + } + + unset($options['r']); + } + + $validator = Validator::make($options, [ + 'w' => ['numeric', 'between:1,2000'], + 'h' => ['numeric', 'between:1,2000'], + 'canv' => ['boolean'], + 'brightness' => ['numeric', 'between:0,100'], + 'saturation' => ['numeric', 'between:0,100'], + 'hue' => ['numeric', 'between:0,100'], + 'rotation' => ['numeric'], + 'flip' => ['string', ValidationRule::in(['h', 'v', 'hv'])] + ]); + + if ($validator->fails()) { + foreach ($validator->messages()->getMessages() as $field_name => $messages) { + unset($options[$field_name]); + } + } + + + if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $image = Image::newFromFile($filename); + } + + if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $image = Image::thumbnail($filename, $options['w'], ['height' => $this->defaultH]); + } + + if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { + $image = Image::thumbnail($filename, $this->defaultW, ['height' => $options['h']]); + } + + if (array_key_exists('w', $options) && array_key_exists('h', $options)) { + if (array_key_exists('canv', $options) && ($options['canv'] == true)) { + $image = Image::thumbnail($filename, $options['w'], ['height' => $options['h']]); + $widthH = ($options['h'] - $image->height) / 2; + $widthW = ($options['w'] - $image->width) / 2; + $image = $image->embed( + $widthW, + $widthH, + $options['w'], + $options['h'], + ['extend' => 'background', 'background' => [255,255,255]] + + ); + } else { + $image = Image::thumbnail($filename, $options['w'], ['height' => $options['h'], 'crop' => 'centre']); + } + } + + if (array_key_exists('brightness', $options) || array_key_exists('saturation', $options) || array_key_exists('hue', $options)) { + $image = $this->brightness($image, array_key_exists('brightness', $options) ? $options['brightness'] : 1.0, array_key_exists('saturation', $options) ? $options['saturation'] : 1.0, array_key_exists('hue', $options) ? $options['hue'] : 0.0); + } + + if (array_key_exists('rotation', $options)) { + $image = $image->rotate($options['rotation']); + } + + if (array_key_exists('flip', $options)) { + if ($options['flip'] == "h") { + $image = $image->fliphor(); + } + + if ($options['flip'] == "v") { + $image = $image->flipver(); + } + + if ($options['flip'] == "hv") { + $image = $image->fliphor(); + $image = $image->flipver(); + } + } + if (array_key_exists('q', $options)) { + $saveOptions['Q'] = $options['q']; + } + if (app()->file->ext == 'png') { + $saveOptions = array_merge(['palette'=>true],$saveOptions); + } + + return $image->writeToBuffer("." . app()->file->ext, $saveOptions); + } + + public function process(string $filename, string $target, array $options = [], $saveOptions = ['Q' => 100]) + { + + + if (array_key_exists('w', $options) && array_key_exists('h', $options) && array_key_exists('r', $options)) { + unset($options['r']); + } + + if (array_key_exists('r', $options)) { + $options['r'] = explode(':', $options['r']); + if (count($options['r']) != 2) { + unset($options['r']); + } + + $validator = Validator::make($options, [ + 'r' => ['nullable', 'array'], + 'r.*' => ['nullable', 'numeric'], + ]); + if ($validator->fails()) { + foreach ($validator->messages()->getMessages() as $field_name => $messages) { + unset($options[explode('.', $field_name)[0]]); + } + } + } + + if (array_key_exists('r', $options)) { + + if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $options['w'] = $options['w'] * $options['r'][0]; + $options['h'] = $options['w'] * $options['r'][1]; + } + + if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { + $options['w'] = $options['h'] * $options['r'][0]; + $options['h'] = $options['h'] * $options['r'][1]; + } + if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $options['w'] = getimagesize($filename)[0] * $options['r'][0]; + $options['h'] = getimagesize($filename)[0] * $options['r'][1]; + } + + unset($options['r']); + } + + $validator = Validator::make($options, [ + 'w' => ['numeric', 'between:1,2000'], + 'h' => ['numeric', 'between:1,2000'], + 'canv' => ['boolean'], + 'brightness' => ['numeric', 'between:0,100'], + 'saturation' => ['numeric', 'between:0,100'], + 'hue' => ['numeric', 'between:0,100'], + 'rotation' => ['numeric'], + 'flip' => ['string', ValidationRule::in(['h', 'v', 'hv'])] + ]); + + if ($validator->fails()) { + foreach ($validator->messages()->getMessages() as $field_name => $messages) { + unset($options[$field_name]); + } + } + + + if (!array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $image = Image::newFromFile($filename); + } + + if (array_key_exists('w', $options) && !array_key_exists('h', $options)) { + $image = Image::thumbnail($filename, $options['w'], ['height' => $this->defaultH]); + } + + if (!array_key_exists('w', $options) && array_key_exists('h', $options)) { + $image = Image::thumbnail($filename, $this->defaultW, ['height' => $options['h']]); + } + + if (array_key_exists('w', $options) && array_key_exists('h', $options)) { + if (array_key_exists('canv', $options) && ($options['canv'] == true)) { + $image = Image::thumbnail($filename, $options['w'], ['height' => $options['h']]); + $widthH = ($options['h'] - $image->height) / 2; + $widthW = ($options['w'] - $image->width) / 2; + $image = $image->embed( + $widthW, + $widthH, + $options['w'], + $options['h'], + ['extend' => 'background', 'background' => 0] + ); + } else { + $image = Image::thumbnail($filename, $options['w'], ['height' => $options['h'], 'crop' => 'centre']); + } + } + + if (array_key_exists('brightness', $options) || array_key_exists('saturation', $options) || array_key_exists('hue', $options)) { + $image = $this->brightness($image, array_key_exists('brightness', $options) ? $options['brightness'] : 1.0, array_key_exists('saturation', $options) ? $options['saturation'] : 1.0, array_key_exists('hue', $options) ? $options['hue'] : 0.0); + } + + if (array_key_exists('rotation', $options)) { + $image = $image->rotate($options['rotation']); + } + + if (array_key_exists('flip', $options)) { + if ($options['flip'] == "h") { + $image = $image->fliphor(); + } + + if ($options['flip'] == "v") { + $image = $image->flipver(); + } + + if ($options['flip'] == "hv") { + $image = $image->fliphor(); + $image = $image->flipver(); + } + } + $image->writeToFile($target, $saveOptions); + + return new \Illuminate\Http\File($target); + } + + public function createFakeImage($stub, $path, $saveOptions = ['Q' => 100], $options = ["w" => null, "h" => null, "r" => null, "flip" => null, "canv" => null, "rotation" => null]) + { + return $this->process($stub, $path, $options, $saveOptions); + } + + public function convertImage(string $filePath, string $target, array $options = ['Q' => 100]): \Illuminate\Http\File + { + $tmpFile = \Jcupitt\Vips\Image::newFromFile($filePath); + $tmpFile->writeToFile($target, $options); + return new \Illuminate\Http\File($target); + } +} diff --git a/app/Image/Processor.php b/app/Image/Traits/ModulateTrait.php similarity index 83% rename from app/Image/Processor.php rename to app/Image/Traits/ModulateTrait.php index 969dc8d..ae5dfdc 100644 --- a/app/Image/Processor.php +++ b/app/Image/Traits/ModulateTrait.php @@ -1,11 +1,12 @@ interpretation; $hue %= 360; if ($hue < 0) { @@ -25,4 +26,5 @@ class Processor { ->linear([$brightness, $saturation, 1.0], [0.0, 0.0, $hue]) ->colourspace($oldInterpretation); } + } diff --git a/app/Models/Collection.php b/app/Models/Collection.php index 7fd6714..f35145c 100644 --- a/app/Models/Collection.php +++ b/app/Models/Collection.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Models\Traits\Validatable; +use App\Models\Traits\ValidationMaker; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -9,7 +11,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; class Collection extends Model { - use HasFactory, SoftDeletes; + use HasFactory, SoftDeletes, Validatable, ValidationMaker; protected $fillable = [ "name", @@ -28,7 +30,7 @@ class Collection extends Model "alt_required", "description_required", "exts", - "avalible_exts", + "ext", "mimetypes", "model", "expire_date", @@ -36,22 +38,46 @@ class Collection extends Model protected $casts = [ 'exts' => 'array', - 'avalible_exts' => 'array', 'mimetypes' => 'array', ]; + public function files() + { + return $this->hasMany(File::class); + } + protected function exts(): Attribute { return Attribute::make( set: fn ($value) => json_encode($value), ); } - protected function avalible_exts(): Attribute + + public function rules(): array { - return Attribute::make( - set: fn ($value) => json_encode($value), - ); + return [ + 'name' => ['max:100', 'Required', 'string', 'unique:collections,name'], + "path" => ['max:255', 'nullable', 'string'], + "public" => ['nullable', 'boolean'], + "disk" => ['required', 'string', 'max:255'], + "count" => ['required', 'numeric', 'max:255'], + "tmp_support" => ['required', 'boolean'], + "remove_tmp_time" => ['date_format:Y-m-d H:i:s', 'nullable'], + "max_file_size" => ['nullable', 'numeric'], + "min_file_size" => ['nullable', 'numeric'], + "max_width" => ['nullable', 'numeric'], + "min_width" => ['nullable', 'numeric'], + "max_height" => ['nullable', 'numeric'], + "min_height" => ['nullable', 'numeric'], + "alt_required" => ['required', 'boolean'], + "description_required"=> ['required','boolean'], + "exts" => ['nullable'], + "ext" => ['string','max:100','nullable'], + "mimetypes"=> ['nullable'], + "expire_date" => ['date_format:Y-m-d H:i:s','nullable'], + ]; } + protected function mimetypes(): Attribute { return Attribute::make( @@ -68,5 +94,4 @@ class Collection extends Model { return implode(",", app()->collection->mimetypes); } - } diff --git a/app/Models/File.php b/app/Models/File.php index 3df7b3b..830054d 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -3,10 +3,12 @@ namespace App\Models; use App\Models\Traits\Validatable; +use App\Models\Traits\ValidationMaker; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; class File extends Model @@ -15,6 +17,8 @@ class File extends Model protected $primaryKey = 'uuid'; + public $incrementing = false; + protected $fillable = [ "uuid", "original_name", @@ -29,6 +33,7 @@ class File extends Model "description", "user_id", "ip", + "model_id", "collection_id", "published_at", ]; @@ -45,16 +50,18 @@ class File extends Model ); } - // public function rules(): array - // { - // return [ - // "image" => [ - // "mimes:" . app()->collection->getExts(), - // "mimetypes:" . app()->collection->getMimeTypes(), - // "dimensions:min_width=" . app()->collection->min_width . ",min_height=" . app()->collection->min_height . ',max_width=' . app()->collection->max_width . ',max_height=' . app()->collection->max_height, - // "max:" . app()->collection->max_size, - // "min:" . app()->collection->min_size, - // ], - // ]; - // } + public function collection() + { + return $this->belongsTo(Collection::class); + } + + public function getPath() + { + return Storage::disk($this->collection->disk)->path($this->server_path . $this->uuid . '.' . $this->collection->ext); + } + public function getModifiedPath() + { + return Storage::disk(app()->collection->disk)->path(app()->file->server_path . app()->file->uuid . '-modified.' . app()->collection->ext); + } + } diff --git a/app/Models/Traits/Validatable.php b/app/Models/Traits/Validatable.php index ddf6915..ca2e336 100644 --- a/app/Models/Traits/Validatable.php +++ b/app/Models/Traits/Validatable.php @@ -59,7 +59,9 @@ trait Validatable $rules = array_merge($rules, $this->createValidations()); $rules = array_merge($rules, $this->createValidations('category_data')); + $validator = Validator::make($this->toArray(), $rules); + if ($abort && $validator->fails()) { throw new ValidationException($validator, new JsonResponse([ @@ -83,7 +85,6 @@ trait Validatable return $abort ? $this : true; } - public function getLoads() { return []; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 34c5d64..6f766d5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -27,7 +27,7 @@ class AppServiceProvider extends ServiceProvider { Storage::disk('local')->buildTemporaryUrlsUsing(function ($path, $expiration, $options) { return URL::temporarySignedRoute( - 'api.file.private', + 'api.files.private', $expiration, array_merge($options, ['path' => $path]) ); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index ab8b2cf..97d03ea 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,10 +2,13 @@ namespace App\Providers; +use App\Models\File; +use App\Observers\FileObserver; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; +use PHPUnit\TextUI\XmlConfiguration\FileCollection; class EventServiceProvider extends ServiceProvider { diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 28dec01..6689fb3 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Models\File; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Http\Request; diff --git a/app/Providers/UuidServiceProvider.php b/app/Providers/UuidServiceProvider.php new file mode 100644 index 0000000..235b289 --- /dev/null +++ b/app/Providers/UuidServiceProvider.php @@ -0,0 +1,31 @@ +app->bind('uuid', function () { + return Str::uuid(); + }); + } +} diff --git a/app/Utilities/Helpers/Image.php b/app/Utilities/Helpers/Image.php new file mode 100644 index 0000000..8be9b74 --- /dev/null +++ b/app/Utilities/Helpers/Image.php @@ -0,0 +1,193 @@ + 0 ? '?text=' . urlencode(implode(' ', $imageParts)) : '' + ); + } + + /** + * Download a remote random image to disk and return its location + * + * Requires curl, or allow_url_fopen to be on in php.ini. + * + * @example '/path/to/dir/13b73edae8443990be1aa8f1a483bc27.png' + * + * @return bool|string + */ + public static function image( + $dir = null, + $width = 640, + $height = 480, + $category = null, + $fullPath = true, + $randomize = true, + $word = null, + $gray = false, + $format = 'png' + ) { + trigger_deprecation( + 'fakerphp/faker', + '1.20', + 'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' + ); + + $dir = null === $dir ? sys_get_temp_dir() : $dir; // GNU/Linux / OS X / Windows compatible + // Validate directory path + if (!is_dir($dir) || !is_writable($dir)) { + throw new \InvalidArgumentException(sprintf('Cannot write to directory "%s"', $dir)); + } + + // Generate a random filename. Use the server address so that a file + // generated at the same time on a different server won't have a collision. + $name = md5(uniqid(empty($_SERVER['SERVER_ADDR']) ? '' : $_SERVER['SERVER_ADDR'], true)); + $filename = sprintf('%s.%s', $name, $format); + $filepath = $dir . DIRECTORY_SEPARATOR . $filename; + + $url = static::imageUrl($width, $height, $category, $randomize, $word, $gray, $format); + + // save file + if (function_exists('curl_exec')) { + // use cURL + $fp = fopen($filepath, 'w'); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_FILE, $fp); + $success = curl_exec($ch) && curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200; + fclose($fp); + curl_close($ch); + + if (!$success) { + unlink($filepath); + + // could not contact the distant URL or HTTP error - fail silently. + return false; + } + } elseif (ini_get('allow_url_fopen')) { + // use remote fopen() via copy() + $success = copy($url, $filepath); + + if (!$success) { + // could not contact the distant URL or HTTP error - fail silently. + return false; + } + } else { + return new \RuntimeException('The image formatter downloads an image from a remote HTTP server. Therefore, it requires that PHP can request remote hosts, either via cURL or fopen()'); + } + + return $fullPath ? $filepath : $filename; + } + + public static function getFormats(): array + { + trigger_deprecation( + 'fakerphp/faker', + '1.20', + 'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' + ); + + return array_keys(static::getFormatConstants()); + } + + public static function getFormatConstants(): array + { + trigger_deprecation( + 'fakerphp/faker', + '1.20', + 'Provider is deprecated and will no longer be available in Faker 2. Please use a custom provider instead' + ); + + return [ + static::FORMAT_JPG => constant('IMAGETYPE_JPEG'), + static::FORMAT_JPEG => constant('IMAGETYPE_JPEG'), + static::FORMAT_PNG => constant('IMAGETYPE_PNG'), + ]; + } +} diff --git a/composer.json b/composer.json index ccefe54..e3126b1 100644 --- a/composer.json +++ b/composer.json @@ -7,12 +7,13 @@ "require": { "php": "^8.0.2", "guzzlehttp/guzzle": "^7.2", + "jcupitt/vips": "2.0.0", "jenssegers/agent": "^2.6", "jenssegers/mongodb": "^3.9", + "kosinix/grafika": "^2.0", "laravel/framework": "^9.19", "laravel/sanctum": "^2.14.1", - "laravel/tinker": "^2.7", - "jcupitt/vips" : "2.0.0" + "laravel/tinker": "^2.7" }, "require-dev": { "brianium/paratest": "^6.5", diff --git a/composer.lock b/composer.lock index 729296a..4104022 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aa091ad8935a98954cd65fdb252e3ab7", + "content-hash": "5ccb92d0caa97ee80e3508d0b7941f1c", "packages": [ { "name": "brick/math", @@ -1230,6 +1230,51 @@ ], "time": "2022-06-29T19:04:13+00:00" }, + { + "name": "kosinix/grafika", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/kosinix/grafika.git", + "reference": "211f61fc334b8b36616b23e8af7c5727971d96ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kosinix/grafika/zipball/211f61fc334b8b36616b23e8af7c5727971d96ee", + "reference": "211f61fc334b8b36616b23e8af7c5727971d96ee", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Grafika\\": "src/Grafika" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT", + "GPL-2.0+" + ], + "authors": [ + { + "name": "Nico Amarilla", + "homepage": "https://www.kosinix.com" + } + ], + "description": "An image manipulation library for PHP.", + "homepage": "http://kosinix.github.io/grafika", + "keywords": [ + "grafika" + ], + "support": { + "issues": "https://github.com/kosinix/grafika/issues", + "source": "https://github.com/kosinix/grafika/tree/develop" + }, + "time": "2017-06-20T03:13:49+00:00" + }, { "name": "laravel/framework", "version": "v9.21.6", diff --git a/config/app.php b/config/app.php index e8b5010..260a8a0 100644 --- a/config/app.php +++ b/config/app.php @@ -195,7 +195,8 @@ return [ App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\SettingServiceProvider::class, - // App\Providers\CollectionServiceProvider::class + App\Providers\UuidServiceProvider::class + ], /* diff --git a/config/filesystems.php b/config/filesystems.php index 1b24a72..374f1ca 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -48,6 +48,14 @@ return [ ], ], + 'tmp' => [ + 'driver' => 'local', + 'root' => '/tmp', + 'url' => env('APP_URL') . '/storage', + 'visibility' => 'public', + 'throw' => false, + ], + // 'public' => [ // 'driver' => 'local', // 'root' => storage_path('app/public'), diff --git a/database/factories/BaseFactory.php b/database/factories/BaseFactory.php index 319c837..686b684 100644 --- a/database/factories/BaseFactory.php +++ b/database/factories/BaseFactory.php @@ -3,7 +3,7 @@ namespace Database\Factories; - +use App\Image\ImageProcessor; use Illuminate\Support\Str; trait BaseFactory @@ -62,12 +62,11 @@ trait BaseFactory return rand($min + 1, $min + 100); } - public function withDependency() + public function withDependency($dependencyAttributes = []) { - return $this->state(function (array $attributes) { - return $this->dependencyProvider(); + return $this->state(function (array $attributes) use ($dependencyAttributes) { + return $this->dependencyProvider($dependencyAttributes); }); } - } diff --git a/database/factories/CollectionFactory.php b/database/factories/CollectionFactory.php index 1ca7151..3afd0ad 100644 --- a/database/factories/CollectionFactory.php +++ b/database/factories/CollectionFactory.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Factories\Factory; */ class CollectionFactory extends Factory { + use BaseFactory; /** * Define the model's default state. * @@ -19,39 +20,55 @@ class CollectionFactory extends Factory { return [ "name" => fake()->name(), - "public" => "public", + "public" => rand(0, 1), "disk" => "local", - "count" => rand(3,18), - "tmp_support" => rand(0,1), - "remove_tmp_time" => 132, - "max_file_size" => rand(300,2000), - "min_file_size" => rand(300,2000), - "max_width" => rand(300,2000), - "min_width" => rand(300,2000), - "max_height" => rand(300,2000), - "min_height" => rand(300,2000), - "alt_required" => rand(0,1), - "description_required" => rand(0,1), + "count" => rand(3, 18), + "remove_tmp_time" => "2022-07-27 09:17:59", + "max_file_size" => rand(300, 2000), + 'alt_required' => false, + 'description_required' => false, + 'tmp_support' => true, + 'max_width' => 2000, + 'max_height' => 2000, + 'min_width' => 1, + 'min_height' => 1, + 'min_file_size' => 0, "exts" => [ "jpg", "jpeg", "png", "webp" ], - "avalible_exts" => [ - "jpg", - "jpeg", - "png", - "webp" - ], + "ext" => "webp", "mimetypes" => [ - "jpg", - "jpeg", - "png", - "webp" + "image/webp", + "image/png", + "image/jpeg", + "image/jpg" ], "model" => fake()->name(), "expire_date" => "2022-07-27 09:17:59" ]; } + + public function testGeneratorConfig() + { + return [ + 'store' => '', + 'update' => '', + 'show' => '', + 'delete' => '', + ]; + } + + public function dependencyProvider($dependencyAttributes = []) + { + return []; + } + public function list() + { + return [ + 'name:gt' + ]; + } } diff --git a/database/factories/Documents/PolicyDocumentFactory.php b/database/factories/Documents/PolicyDocumentFactory.php index 2446684..378e167 100755 --- a/database/factories/Documents/PolicyDocumentFactory.php +++ b/database/factories/Documents/PolicyDocumentFactory.php @@ -4,10 +4,14 @@ namespace Database\Factories\Documents; use App\Documents\ModelDocument; use App\Documents\PolicyDocument; +use Database\Factories\BaseFactory; use Illuminate\Database\Eloquent\Factories\Factory; class PolicyDocumentFactory extends Factory + { + use BaseFactory; + protected $model = PolicyDocument::class; public function definition() @@ -19,4 +23,9 @@ class PolicyDocumentFactory extends Factory 'needs' => [], ]; } + + public function dependencyProvider($dependencyAttributes= []) + { + return []; + } } diff --git a/database/factories/FileFactory.php b/database/factories/FileFactory.php index 5ebfb9d..d661b69 100644 --- a/database/factories/FileFactory.php +++ b/database/factories/FileFactory.php @@ -2,14 +2,22 @@ namespace Database\Factories; +use App\Image\ImageProcessor; use App\Models\Collection; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\File> */ class FileFactory extends Factory { + use BaseFactory; + + private $uuid = null; + /** * Define the model's default state. * @@ -17,25 +25,48 @@ class FileFactory extends Factory */ public function definition() { + $this->uuid = app()->uuid; return [ - "uuid" => 1, + "uuid" => $this->uuid, "original_name" => fake()->name(), "ext" => ['jpg', 'jpeg', 'png', 'webp'][rand(0, 3)], "mimetype" => 'image', "width" => rand(300, 2000), "height" => rand(300, 2000), "file_size" => rand(300, 2000), - "server_path" => date('y') . '/' . date('m'), "sort" => rand(0, 23), + "server_path" => '/' . date('y') . '/' . date('m') . '/', "alts" => [ 'hello wroldswdfouiwref iuwrhgf ow rgfaw ghfawej', 'jhsf asduyfsadf sadf safsuf isfjsdfsudifsduiyf sdiuf sd' ], "description" => 'ajsfoisahjfoaspf asduf safsafjsh lh', - "user_id" => rand(43724, 382348), + "user_id" => 1, "ip" => "127.0. 0.1", - "collection_id" => Collection::factory()->create()->id, + // "collection_id" => $collection->id, "published_at" => "2022-07-27 09:17:59", ]; } + + + public function dependencyProvider($dependencyAttributes = []) + { + + + if (array_key_exists('withImage', $dependencyAttributes)) { + if ($dependencyAttributes['withImage'] == true) { + unset($dependencyAttributes['withImage']); + $collection = Collection::factory()->createQuietly($dependencyAttributes); + $imageProcessor = new ImageProcessor; + $imageProcessor->createFakeImage(storage_path('stub') . '/image.png', Storage::disk($collection->disk)->path('/' . date('y') . '/' . date('m') . '/' . $this->uuid . '.' . $collection->ext)); + } + + } else { + $collection = Collection::factory()->createQuietly($dependencyAttributes); + } + + return [ + 'collection_id' => $collection + ]; + } } diff --git a/database/factories/ILaravelFactory.php b/database/factories/ILaravelFactory.php index ff1ee35..f64ff20 100644 --- a/database/factories/ILaravelFactory.php +++ b/database/factories/ILaravelFactory.php @@ -57,7 +57,7 @@ class ILaravelFactory extends Factory ]; } - public function dependencyProvider() + public function dependencyProvider($dependencyAttributes= []) { return [ 'user_id' => UserDocument::factory()->create()->id diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index ff9c948..02c4bf7 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -34,7 +34,7 @@ class UserFactory extends Factory return []; } - public function dependencyProvider() + public function dependencyProvider($dependencyAttributes= []) { return []; } diff --git a/database/migrations/2022_07_27_073858_create_files_table.php b/database/migrations/2022_07_27_073858_create_files_table.php index 570fb5a..74b41a8 100644 --- a/database/migrations/2022_07_27_073858_create_files_table.php +++ b/database/migrations/2022_07_27_073858_create_files_table.php @@ -24,6 +24,7 @@ return new class extends Migration $table->bigInteger("file_size")->nullable(); $table->bigInteger("sort")->nullable(); $table->json("alts")->nullable(); + $table->unsignedBigInteger("model_id")->nullable(); $table->string("description")->nullable(); $table->unsignedBigInteger("user_id")->nullable(); $table->string("ip")->nullable(); diff --git a/database/migrations/2022_07_27_073906_create_collections_table.php b/database/migrations/2022_07_27_073906_create_collections_table.php index 09a587a..a30ebf0 100644 --- a/database/migrations/2022_07_27_073906_create_collections_table.php +++ b/database/migrations/2022_07_27_073906_create_collections_table.php @@ -17,7 +17,7 @@ return new class extends Migration $table->id(); $table->string("name")->nullable()->unique(); $table->string("path")->nullable(); - $table->string("public")->nullable(); + $table->boolean("public")->nullable(); $table->string("disk")->nullable(); $table->integer("count")->nullable(); $table->boolean("tmp_support")->nullable(); @@ -31,7 +31,8 @@ return new class extends Migration $table->boolean("alt_required")->nullable(); $table->boolean("description_required")->nullable(); $table->json("exts")->nullable(); - $table->json("avalible_exts")->nullable(); + $table->string("ext")->nullable(); + $table->json('image_processor')->nullable(); $table->json("mimetypes")->nullable(); $table->string("model")->unique()->nullable(); $table->time("expire_date")->nullable(); diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 22e5279..6e89110 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,8 +2,12 @@ namespace Database\Seeders; +use App\Image\ImageProcessor; +use App\Models\Collection; +use App\Models\File; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\Storage; class DatabaseSeeder extends Seeder { @@ -21,7 +25,49 @@ class DatabaseSeeder extends Seeder // 'email' => 'test@example.com', // ]); - \App\Models\Collection::factory(10)->create(); - \App\Models\File::factory(1)->create(); + // \App\Models\Collection::factory()->create([ + // "name" => "fuck", + // "count" => 1, + // "tmp_support" => 0, + // "remove_tmp_time" => 132, + // "max_file_size" => 100000000, + // "min_file_size" => 10, + // "max_width" =>2000, + // "min_width" =>10, + // "max_height" => 2000, + // "min_height" =>10, + // "alt_required" => 0, + // "description_required" =>0, + // "exts" => [ + // "jpg", + // "jpeg", + // "png", + // "webp" + // ], + // "ext" => "webp", + // ]); + // \App\Models\Collection::factory(10)->create(); + // \App\Models\File::factory()->create(); + $collection = Collection::factory()->createQuietly([ + 'alt_required' => false, + 'description_required' => false, + 'tmp_support' => true, + 'max_width' => 2000, + 'max_height' => 2000, + 'min_width' => 1, + 'min_height' => 1, + 'min_file_size' => 0, + 'public' => false + ]); + $uuid = app()->uuid; + $file = File::factory()->createQuietly([ + 'uuid' => $uuid, + 'server_path' => '/' . date('y') . '/' . date('m') . '/', + 'user_id' => auth()->id(), + 'collection_id' => $collection->id + ]); + $imageProcessor = new ImageProcessor; + $imageProcessor->createFakeImage(storage_path('stub') . '/image.png', Storage::disk($collection->disk)->path($file->server_path . $uuid . '.' . $collection->ext)); + } } diff --git a/docker-compose.yml b/docker-compose.yml index cb06596..000513d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: - '8000:80' volumes: - '.:/var/www/html' + mysql: image: 'mysql' tmpfs: /var/lib/mysql diff --git a/public/image-modified.webp b/public/image-modified.webp deleted file mode 100644 index fa30048..0000000 Binary files a/public/image-modified.webp and /dev/null differ diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index f3ee5b1..643535d 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -16,13 +16,9 @@ -
+ @csrf -
- - -


@@ -36,6 +32,18 @@
+ + @dump($errors->any()) + @if($errors->any()) +
+

Opps Something went wrong

+ +
+@endif diff --git a/routes/api.php b/routes/api.php index f6d9471..afb66af 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,7 +2,10 @@ use App\Http\Controllers\CollectionController; use App\Http\Controllers\FileController; +use App\Models\File; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Storage; /* |-------------------------------------------------------------------------- @@ -19,10 +22,18 @@ use Illuminate\Support\Facades\Route; // return $request->user(); // }); +Route::get('newTmp/{uuid}', function ($uuid) { + $file = File::find($uuid); + return Storage::disk('local')->temporaryUrl($file->server_path . $file->uuid . '.webp', now()->addMinutes(5), ['collection_name' => 'Paris Renner II']); +})->name('newTmp'); + + Route::group(['as' => 'api.'], function () { Route::apiResource('collections', CollectionController::class); - Route::delete('collections/{collection}', [CollectionController::class, "destroy"])->withTrashed(); - Route::get('{collection_name}/{uuid}.{extention}', [FileController::class, 'show'])->name('file.show'); - Route::get('{collection_name}/{path}', [FileController::class, 'private'])->name('file.private'); - Route::post('{collection_name}', [FileController::class, 'store'])->name('file.store'); + Route::delete('collections/{collection}', [CollectionController::class, "destroy"])->withTrashed()->name('collections.destroy'); + Route::get('{collection_name}/{uuid}.{extention}', [FileController::class, 'show'])->name('files.show'); + // Route::get('{collection_name}/{path}', [FileController::class, 'private'])->name('files.private'); + Route::post('{collection_name}/{model_id?}', [FileController::class, 'store'])->name('files.store'); + Route::put('{collection_name}/{uuid}.{extention}', [FileController::class, 'update'])->name('files.update'); + Route::delete('{collection_name}/{uuid}.{extention}', [FileController::class, 'destroy'])->withTrashed()->name('files.destroy'); }); diff --git a/routes/web.php b/routes/web.php index 655f21f..94febc7 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,13 +1,6 @@ dddd = 'tesst'; - // dump($request->); - - if (!isset($request->w) && !isset($request->h)) { - $image = Image::thumbnail('../public/image.png', getimagesize('../public/image.png')[0]); - } - - if ($request->r) { - $rArray = explode(':', $request->r); - - if (isset($request->w) && !isset($request->h)) { - $request->h = $request->w * $rArray[1]; - $request->w = $request->w * $rArray[0]; - } - - - if (!isset($request->w) && isset($request->h)) { - $request->h = $request->h * $rArray[1]; - $request->w = $request->h * $rArray[0]; - } - if (!isset($request->w) && !isset($request->h)) { - $request->h = getimagesize('../public/image.png')[0] * $rArray[1]; - $request->w = getimagesize('../public/image.png')[0] * $rArray[0]; - } - } - - if (isset($request->w) && !isset($request->h)) { - $image = Image::thumbnail('../public/image.png', $request->w, ['height' => getimagesize('../public/image.png')[1]]); - } - - if (!isset($request->w) && isset($request->h)) { - $image = Image::thumbnail('../public/image.png', getimagesize('../public/image.png')[0], ['height' => $request->h]); - } - - if (isset($request->w) && isset($request->h) && !($request->canv == true)) { - $image = Image::thumbnail('../public/image.png', $request->w, ['height' => $request->h, 'crop' => 'centre']); - } - - if (isset($request->w) && isset($request->h) && $request->canv == true) { - $image = Image::thumbnail('../public/image.png', $request->w, ['height' => $request->h]); - $widthH = ($request->h - $image->height) / 2; - $widthW = ($request->w - $image->width) / 2; - $image = $image->embed( - $widthW, - $widthH, - $request->w, - $request->h, - ['extend' => 'background','background'=>1024] - ); - } - - if (isset($request->brightness) || isset($request->saturation) || isset($request->hue)) { - $image = Processor::brightness($image, isset($request->brightness) ? $request->brightness : 1.0, isset($request->saturation) ? $request->saturation : 1.0, isset($request->hue) ? $request->hue : 0.0); - } - - if ($request->rotation) { - $image = $image->rotate($request->rotation); - } - - if ($request->flip == "h") { - $image = $image->fliphor(); - } - - if ($request->flip == "v") { - $image = $image->flipver(); - } - - if ($request->flip == "hv") { - $image = $image->fliphor(); - $image = $image->flipver(); - } - - - $image->writeToFile('image-modified.webp', [ - 'Q' => isset($request->q) ? $request->q : 100 - ]); - - return response()->file(public_path("image-modified.webp" . $request->ext)); -}); - - -Route::get('/upload',function(){ -return view('welcome'); -}); - -Route::post('/upload',[FileController::class,'store']); diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore index 05c4471..8ef3ff7 100644 --- a/storage/framework/.gitignore +++ b/storage/framework/.gitignore @@ -1,3 +1,4 @@ + compiled.php config.php down diff --git a/public/image.png b/storage/stub/image.png similarity index 100% rename from public/image.png rename to storage/stub/image.png diff --git a/storage/stub/image2.jpeg b/storage/stub/image2.jpeg new file mode 100644 index 0000000..bf4e5e2 Binary files /dev/null and b/storage/stub/image2.jpeg differ diff --git a/storage/stub/image3.png b/storage/stub/image3.png new file mode 100644 index 0000000..989ceeb Binary files /dev/null and b/storage/stub/image3.png differ diff --git a/tests/Base/FactoryMethodsTrait.php b/tests/Base/FactoryMethodsTrait.php index 1f939cf..16c3155 100644 --- a/tests/Base/FactoryMethodsTrait.php +++ b/tests/Base/FactoryMethodsTrait.php @@ -19,9 +19,9 @@ trait FactoryMethodsTrait return Arr::except($model->toArray(), $model->getAppends()); } - public function one($class, $attributes = []) + public function one($class, $attributes = [], $dependencyAttributes = []) { - return $class::factory()->createQuietly($attributes); + return $class::factory()->withDependency($dependencyAttributes)->createQuietly($attributes); } public function randomOne($class) @@ -29,9 +29,9 @@ trait FactoryMethodsTrait return $class::inRandomOrder()->first(); } - public function trashed($class) + public function trashed($class, $dependencyAttributes = []) { - $model = $this->one($class); + $model = $this->one($class,dependencyAttributes:$dependencyAttributes); $model->delete(); $this->assertSoftDeleted($model); diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index 65a8ac0..7a5a5b8 100644 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -2,7 +2,13 @@ namespace Tests; +use Tests\Base\AuthMethodsTrait; +use Tests\Base\DynamicPolicyAndModelTrait; +use Tests\Base\FactoryMethodsTrait; +use Tests\Base\FreshMongodbDatabase; +use Tests\Base\MockMethodsTrait; + class Bootstrap extends TestCase { - + use AuthMethodsTrait,DynamicPolicyAndModelTrait,FactoryMethodsTrait,FreshMongodbDatabase,MockMethodsTrait; } diff --git a/tests/Feature/Collection/CollectionDeleteTest.php b/tests/Feature/Collection/CollectionDeleteTest.php new file mode 100644 index 0000000..f48347b --- /dev/null +++ b/tests/Feature/Collection/CollectionDeleteTest.php @@ -0,0 +1,56 @@ +modelWithPolicy('collections', ['permission:collections.delete']) + ->loginAs(['collections.delete']) + ->deleteJson(route("api.collections.destroy", $collection = $this->one(Collection::class))) + ->assertOk(); + + $this->loginAsAdmin() + ->getJson(route("api.collections.show", $collection)) + ->assertNotFound(); + } + + public function test_collection_restore_success() + { + $this->modelWithPolicy('collections', ['permission:collections.restore']) + ->loginAsUser(['collections.restore']) + ->deleteJson(route("api.collections.destroy", $collection = $this->trashed(Collection::class))) + ->assertOk(); + + $this->loginAsAdmin() + ->getJson(route("api.collections.show", $collection)) + ->assertOk(); + } + + public function test_collection_delete_forbidden() + { + $this->modelWithPolicy('collections', ['permission:collections.delete']) + ->loginAs(['wrong.permission']) + ->deleteJson(route("api.collections.destroy", $collection = $this->one(Collection::class))) + ->assertForbidden(); + } + + public function test_collection_restore_forbidden() + { + $this->modelWithPolicy('collections', ['permission:collections.restore']) + ->loginAs(['wrong.permission']) + ->deleteJson(route("api.collections.destroy", $collection = $this->trashed(Collection::class))) + ->assertForbidden(); + } + + public function test_collection_delete_notFound() + { + $this->loginAsAdmin() + ->deleteJson(route("api.collections.destroy", 0)) + ->assertNotFound(); + } +} diff --git a/tests/Feature/Collection/CollectionShowTest.php b/tests/Feature/Collection/CollectionShowTest.php new file mode 100644 index 0000000..64d7525 --- /dev/null +++ b/tests/Feature/Collection/CollectionShowTest.php @@ -0,0 +1,34 @@ +modelWithPolicy('collections', ['permission:collections.show']) + ->loginAs(['collections.show']) + ->getJson(route("api.collections.show", $collection = $this->one(Collection::class))) + ->assertOk(); + } + + public function test_collection_show_not_found() + { + $this->modelWithPolicy('collections', ['permission:collections.show']) + ->loginAs(['collections.show']) + ->getJson(route("api.collections.show", 0)) + ->assertNotFound(); + } + + public function test_collection_show_forbidden() + { + $this->modelWithPolicy('collections', ['permission:collections.show']) + ->loginAs(['wrong.permission']) + ->getJson(route("api.collections.show", $collection = $this->one(Collection::class)), []) + ->assertForbidden(); + } +} diff --git a/tests/Feature/Collection/CollectionStoreTest.php b/tests/Feature/Collection/CollectionStoreTest.php new file mode 100644 index 0000000..5f6e5f7 --- /dev/null +++ b/tests/Feature/Collection/CollectionStoreTest.php @@ -0,0 +1,51 @@ +modelWithPolicy('collections', ['permission:collections.store']) + ->loginAs(['collections.store']) + ->postJson(route('api.collections.store'), $collection = $this->make(Collection::class)) + ->assertCreated(); + } + + /** + * @testWith + * ["name:gtString"] + * ["name:numeric"] + * ["name:null"] + * ["path:gtString"] + * ["path:numeric"] + * ["public:string"] + * ["disk:numeric"] + * ["disk:null"] + * ["disk:gtString"] + * ["count:null"] + * ["count:string"] + * ["count:gt:100000"] + * + */ + public function test_collection_store_unprocessable($field) + { + $this->modelWithPolicy('collections', ['permission:collections.store']) + ->loginAsAdmin() + ->postJson(route("api.collections.store"), $collection = $this->make(collection::class, smash: $field, withDependency: true)) + ->assertUnprocessable(); + } + + public function test_collection_store_forbidden() + { + $this->modelWithPolicy('collections', ['permission:collections.store']) + ->loginAs(['wrong.permission']) + ->postJson(route("api.collections.store"), []) + ->assertForbidden(); + } +} diff --git a/tests/Feature/Collection/CollectionUpdateTest.php b/tests/Feature/Collection/CollectionUpdateTest.php new file mode 100644 index 0000000..bd70f27 --- /dev/null +++ b/tests/Feature/Collection/CollectionUpdateTest.php @@ -0,0 +1,67 @@ +modelWithPolicy('collections', ['permission:collections.update']) + ->loginAs(['collections.update']) + ->putJson( + route("api.collections.update", $collection = $this->one(Collection::class)), + $update = $this->make(Collection::class, withDependency: true) + ) + ->assertOk(); + } + + /** + * @testWith + * ["name:gtString"] + * ["name:numeric"] + * ["name:null"] + * ["path:gtString"] + * ["path:numeric"] + * ["public:string"] + * ["disk:numeric"] + * ["disk:null"] + * ["disk:gtString"] + * ["count:null"] + * ["count:string"] + * ["count:gt:100000"] + * + */ + public function test_collection_update_unprocessable($field) + { + $this->modelWithPolicy('collections', ['permission:collections.update']) + ->loginAs(['collections.update'])->putJson( + route("api.collections.update", $collection = $this->one(Collection::class)), + $update = $this->make(collection::class, smash: $field, withDependency: true) + ) + ->assertUnprocessable(); + } + + public function test_collection_update_forbidden() + { + $this->modelWithPolicy('collections', ['permission:collections.update']) + ->loginAs(['wrong.permission']) + ->putJson( + route("api.collections.update", $collection = $this->one(Collection::class)), + [] + ) + ->assertForbidden(); + } + + public function test_collection_update_not_found() + { + $this->loginAsUser(['collections.update']) + ->putJson(route("api.collections.update", 0), []) + ->assertNotFound(); + } +} diff --git a/tests/Feature/FileDeleteTest.php b/tests/Feature/FileDeleteTest.php new file mode 100644 index 0000000..553a28e --- /dev/null +++ b/tests/Feature/FileDeleteTest.php @@ -0,0 +1,47 @@ +assertFalse("it's not mohammad's fault, I'm waiting for dynamic policy"); + // } + + public function test_user_with_permission_can_delete_file() + { + $file = $this->one(File::class, dependencyAttributes: ['withImage' => true]); + $collection = Collection::find($file->collection_id); + $response = $this->loginAs()->deleteJson(route('api.files.destroy', ['collection_name' => $collection->name, 'uuid' => $file->uuid, 'extention' => $collection->ext])); + $response->assertok(); + } + + public function test_file_restore_success() + { + $this->modelWithPolicy('collections', ['permission:collections.restore']) + ->loginAsUser(['collections.restore']) + ->deleteJson(route("api.collections.destroy", $collection = $this->trashed(Collection::class, dependencyAttributes: ['withImage' => true]))) + ->assertOk(); + + $this->loginAsAdmin() + ->getJson(route("api.collections.show", $collection)) + ->assertOk(); + } + + public function test_file_delete_notFound() + { + $this->loginAsAdmin() + ->deleteJson(route("api.files.destroy", ['collection_name' => 'not found', 'uuid' => 'wrong uuid', 'extention' => 'wrong ext'])) + ->assertNotFound(); + } +} diff --git a/tests/Feature/FileShowTest.php b/tests/Feature/FileShowTest.php new file mode 100644 index 0000000..48a5130 --- /dev/null +++ b/tests/Feature/FileShowTest.php @@ -0,0 +1,28 @@ +one(File::class, dependencyAttributes: ['withImage' => true]); + $collection = Collection::find($file->collection_id); + $w = rand(10, 2000); + $h = rand(10, 2000); + $canv = rand(0, 1); + $response = $this->loginAs()->getJson(route('api.files.show', ['collection_name' => $collection->name, 'uuid' => $file->uuid, 'extention' => $collection->ext, 'w' => $w, 'h' => $h, 'canv' => $canv])); + $response->assertOk()->assertHeader('content-type', $response->headers->get('content-type')); + } +} diff --git a/tests/Feature/FileStoreTest.php b/tests/Feature/FileStoreTest.php new file mode 100644 index 0000000..2412a42 --- /dev/null +++ b/tests/Feature/FileStoreTest.php @@ -0,0 +1,206 @@ + UploadedFile::fake()->image('test.png'), + "alts" => ['1', '2', '3'], + "description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', + "public" => 1 + ]; + $collection = Collection::factory()->createQuietly([ + 'tmp_support' => false + ]); + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertForbidden(); + } + + public function test_tmp_false_and_collection_is_full_forbidden() + { + + $randomCount = rand(2, 10); + $collection = Collection::factory()->createQuietly([ + 'tmp_support' => false, + 'count' => $randomCount + ]); + + for ($i = $randomCount; $i > 0; $i--) { + $uuid = app()->uuid; + $this->one(File::class, [ + 'uuid' => $uuid, + 'user_id' => 1, + 'collection_id' => $collection->id, + 'server_path' => '/' . date('y') . '/' . date('m') . '/' . $uuid . '.' . $collection->ext, + ]); + } + + $data = [ + "file" => UploadedFile::fake()->image('test.png'), + "alts" => ['1', '2', '3'], + "description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', + "public" => 1 + ]; + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertForbidden(); + } + + public function test_file_is_not_isset_forbidden() + { + $collection = Collection::factory()->createQuietly([ + 'tmp_support' => true + ]); + $data = [ + "file" => app()->uuid, + "alts" => ['1', '2', '3'], + "description" => 'lfjdsklfslfsdlfasdfsfhgsfgsdf', + "public" => 1 + ]; + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertForbidden(); + } + + public function test_user_can_not_access_not_ownered_files() + { + $collection = Collection::factory()->createQuietly(); + $file = File::factory()->createQuietly([ + 'user_id' => auth()->id() + 1234, + 'collection_id' => $collection->id + ]); + + $data = [ + "file" => $file->uuid, + "public" => 1 + ]; + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertForbidden(); + } + + /** + * @dataProvider storeValidationTestProvider + */ + public function test_store_dynamic_validation_unprocessable($collectionFields, $dataFields) + { + $collection = Collection::factory()->createQuietly($collectionFields); + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $dataFields); + $response->assertUnprocessable(); + } + + public function test_store_static_validation_unprocessable() + { + $collection = Collection::factory()->createQuietly([ + 'min_file_size' => 0 + ]); + + + $data = [ + "file" => UploadedFile::fake()->image('lol.png'), + "description" => Str::random(1000) + ]; + + $this->modelWithPolicy('collections', ['permission:collections.store']) + ->loginAsAdmin() + ->postJson(route("api.files.store", ['collection_name' => $collection->name]), $data) + ->assertUnprocessable(); + } + + + /** + * @dataProvider storeValidationTestProvider + */ + public function test_store_dynamic_validation_stored_file_unprocessable($collectionFields) + { + $collectionFields['withImage'] = true; + $file = $this->one(File::class, dependencyAttributes: $collectionFields); + $collection = Collection::find($file->collection_id); + $data = [ + "file" => $file->uuid, + ]; + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertUnprocessable(); + } + + public function test_user_can_store_sent_file_count_multiple_without_image_processor_ok() + { + $collectionFields['withImage'] = true; + $file = $this->one(File::class, dependencyAttributes: $collectionFields); + $collection = Collection::find($file->collection_id); + + + $data = [ + "file" => UploadedFile::fake()->image('test.png'), + ]; + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertCreated(); + } + + public function test_user_can_store_sent_file_uuid_count_multiple_without_image_processor_ok() + { + $collectionFields['withImage'] = true; + $file = $this->one(File::class, dependencyAttributes: $collectionFields); + $collection = Collection::find($file->collection_id); + + $data = [ + "file" => $file->uuid, + "public" => 1 + ]; + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name]), $data); + $response->assertCreated(); + } + + public function test_user_can_store_sent_file_count_one_without_image_processor_ok() + { + $collection = Collection::factory()->createQuietly([ + 'count' => 1 + ]); + + + $data = [ + "file" => UploadedFile::fake()->image('lol.png'), + "public" => 1 + ]; + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name, 'model_id' => 100]), $data); + $response->assertCreated(); + } + + public function test_user_can_store_sent_file_uuid_count_one_without_image_processor_ok() + { + $collectionFields['withImage'] = true; + $collectionFields['count'] = 1; + $file = $this->one(File::class, dependencyAttributes: $collectionFields); + $collection = Collection::find($file->collection_id); + + + $data = [ + "file" => $file->uuid, + "public" => 1 + ]; + + $response = $this->loginAs()->postJson(route('api.files.store', ['collection_name' => $collection->name, 'model_id' => 100]), $data); + $response->assertCreated(); + } +} diff --git a/tests/Feature/FileUpdateTest.php b/tests/Feature/FileUpdateTest.php new file mode 100644 index 0000000..8de1d2e --- /dev/null +++ b/tests/Feature/FileUpdateTest.php @@ -0,0 +1,47 @@ +assertFalse("it's not mohammad's fault, I'm waiting for dynamic policy"); + // } + + public function test_user_update_not_exits_files_not_found() + { + $data = [ + "original_name" => 'original name is very bad', + "description" => 'this is a description for file and has beed updated', + ]; + + $response = $this->loginAs()->putJson(route('api.files.update', ['collection_name' => 'not_exits', 'uuid' => app()->uuid, 'extention' => 'jpg']), $data); + $response->assertNotFound(); + } + + public function test_user_can_update_file() + { + $file = $this->one(File::class, dependencyAttributes: ['withImage' => true]); + $collection = Collection::find($file->collection_id); + + $data = [ + "original_name" => 'original name is very bad', + "description" => 'this is a description for file and has beed updated', + ]; + + $response = $this->loginAs()->putJson(route('api.files.update', ['collection_name' => $collection->name, 'uuid' => $file->uuid, 'extention' => $collection->ext]), $data); + $response->assertok()->assertJson(['data' => $data]); + } +} diff --git a/tests/Feature/Traits/FileImageTrait.php b/tests/Feature/Traits/FileImageTrait.php new file mode 100644 index 0000000..098ec2a --- /dev/null +++ b/tests/Feature/Traits/FileImageTrait.php @@ -0,0 +1,22 @@ +createFakeImage(storage_path('stub') . '/image.png', Storage::disk($collection->disk)->path($file->server_path . $file->uuid . '.' . $collection->ext)); + } + + public function deleteFakeImageForModel(\App\Models\File $file): void + { + unlink($file->getPath()); + unlink($file->getModifiedPath()); + } +} diff --git a/tests/Feature/Traits/FileShowTrait.php b/tests/Feature/Traits/FileShowTrait.php new file mode 100644 index 0000000..163dcf4 --- /dev/null +++ b/tests/Feature/Traits/FileShowTrait.php @@ -0,0 +1,52 @@ + $image + ]; + $validator = Validator::make($data, [ + 'avatar' => [ + Rule::dimensions()->minWidth($minWidth)->minHeight($minHeight)->maxWidth($maxWidth)->maxHeight($maxHeight), + 'max:' . $maxSize, + 'min:' . $minSize + ], + ]); + + return !$validator->fails(); + } + + public function createCollectionAndFileModelWithImage() + { + $collection = Collection::factory()->createQuietly([ + 'alt_required' => false, + 'description_required' => false, + 'tmp_support' => true, + 'max_width' => 2000, + 'max_height' => 2000, + 'min_width' => 1, + 'min_height' => 1, + 'min_file_size' => 0 + ]); + $uuid = app()->uuid; + $file = File::factory()->createQuietly([ + 'uuid' => $uuid, + 'server_path' => '/' . date('y') . '/' . date('m') . '/', + 'user_id' => auth()->id(), + 'collection_id' => $collection->id + ]); + $this->createImageForModel($collection,$file); + + return ['file' => $file ,'collection' => $collection]; + } +} diff --git a/tests/Feature/Traits/FileTraits.php b/tests/Feature/Traits/FileTraits.php new file mode 100644 index 0000000..9d88668 --- /dev/null +++ b/tests/Feature/Traits/FileTraits.php @@ -0,0 +1,89 @@ + false, + 'description_required' => false, + 'tmp_support' => true, + 'max_width' => 2000, + 'max_height' => 2000, + 'min_width' => 1, + 'min_height' => 1, + 'min_file_size' => 0 + ], + [ + "file" => UploadedFile::fake()->image('test.png', 400, 400), + "public" => 1 + ] + ]; + } + + private function defaultArrayForUpdate() + { + return [ + [ + 'alt_required' => false, + 'description_required' => false, + 'tmp_support' => true, + 'max_width' => 2000, + 'max_height' => 2000, + 'min_width' => 1, + 'min_height' => 1, + 'min_file_size' => 0 + ] + ]; + } + + private function changeDefaultArrayAndReturn($defaultArray, $key, $value): array + { + $defaultArray[$key[0]][$key[1]] = $value; + return $defaultArray; + } + + public function storeValidationTestProvider() + { + + $data = [ + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'alt_required'], true), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'description_required'], true), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_width'], 2), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_height'], 2), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_width'], 1500), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_height'], 1500), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'min_file_size'], 1000), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'max_file_size'], 0), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'exts'], ['jpg']), + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'mimetypes'], ['image/jpg']), + ]; + return $data; + } + + + public function storeValidationWithUuidTestProvider() + { + + $data = [ + $this->changeDefaultArrayAndReturn($this->defaultArray(), [0, 'alt_required'], true), + ]; + return $data; + } + + public function updateValidationTestProvider() + { + + $data = [ + $this->changeDefaultArrayAndReturn($this->defaultArrayForUpdate(), [0, 'alt_required'], true), + $this->changeDefaultArrayAndReturn($this->defaultArrayForUpdate(), [0, 'description_required'], true), + ]; + return $data; + } +}