Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] add method to get ajax uploaders #5440

Closed
wants to merge 59 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
b5c5ce0
add method to get ajax uploaders
pxpm Feb 6, 2024
85083b6
Apply fixes from StyleCI
StyleCIBot Feb 6, 2024
29c8418
use an abstract class
pxpm Feb 7, 2024
61176aa
wip
pxpm Feb 8, 2024
20693fc
Apply fixes from StyleCI
StyleCIBot Feb 8, 2024
16244ab
refactor uploaders
pxpm Feb 9, 2024
4a3e5a5
Apply fixes from StyleCI
StyleCIBot Feb 9, 2024
2230c66
refactor rules
pxpm Feb 11, 2024
653f66e
Apply fixes from StyleCI
StyleCIBot Feb 11, 2024
5f5791b
move ajax to PRO, cleanup
pxpm Feb 11, 2024
3e8b7ce
Apply fixes from StyleCI
StyleCIBot Feb 11, 2024
4bdf319
make attributes available for all subfields
pxpm Feb 20, 2024
1446e05
add option to escape label output
pxpm Feb 6, 2024
a95b8cc
Apply fixes from StyleCI
StyleCIBot Feb 6, 2024
c2d9069
Use white text-color on single save action button
phpfour Nov 6, 2023
7c608c9
Accept classes on routes
promatik Feb 9, 2024
395adf4
fix expiration and temporary in uploaders
pxpm Feb 13, 2024
9454be0
minutes not seconds
pxpm Feb 13, 2024
eb24d05
allow themes to register components
pxpm Feb 22, 2024
d49261a
add with columns to component
pxpm Feb 22, 2024
b894f49
Apply fixes from StyleCI
StyleCIBot Feb 22, 2024
5403af5
translate missing keys ca
Feb 22, 2024
8b52470
Removed redundant translations
promatik Feb 26, 2024
2a3b0b5
improve delete button swal display
pxpm Mar 7, 2024
be55fdf
make swal consistent
pxpm Mar 7, 2024
7030248
translate missing keys es
Mar 12, 2024
aa0d1c8
wip
pxpm Mar 18, 2024
7e1ee89
l11
pxpm Feb 28, 2024
b959d2c
Update composer.json
pxpm Feb 28, 2024
f7a4697
Update composer.json
pxpm Feb 28, 2024
1637ff7
Update composer.json
pxpm Feb 28, 2024
5fcd3a4
Update composer.json
pxpm Feb 28, 2024
97a11e9
l11
pxpm Feb 28, 2024
384a042
Update composer.json
pxpm Feb 28, 2024
92a497b
Update composer.json
pxpm Feb 28, 2024
da37b1b
Update composer.json
pxpm Feb 28, 2024
be4caee
update composer
pxpm Mar 12, 2024
fa02100
use l11 basset
pxpm Mar 12, 2024
b34f18c
add laravel 11
pxpm Mar 13, 2024
b6118b6
update workflow
pxpm Mar 13, 2024
1351a6e
wip
pxpm Mar 13, 2024
1021170
Apply fixes from StyleCI
StyleCIBot Mar 13, 2024
1784ee2
wip
pxpm Mar 14, 2024
cd6487d
Apply fixes from StyleCI
StyleCIBot Mar 14, 2024
e039907
exclude php 8.1 from laravel 11 tests
pxpm Mar 14, 2024
43dc6ee
exclude dbal from laravel 11 too
pxpm Mar 14, 2024
700a174
Update src/app/Models/Traits/HasRelationshipFields.php
pxpm Mar 14, 2024
0dd6289
make widget compatible with L11
pxpm Mar 14, 2024
d3c4080
Apply fixes from StyleCI
StyleCIBot Mar 14, 2024
78df643
add dbal 4.0
pxpm Mar 14, 2024
164407e
Check if uploader column is translatable in translation enabled models
ablunier Mar 11, 2024
2f010d9
Update src/app/Library/Uploaders/Uploader.php
pxpm Mar 18, 2024
0a3d11e
Apply fixes from StyleCI
StyleCIBot Mar 18, 2024
d6cc548
Apply fixes from StyleCI
StyleCIBot Mar 18, 2024
1751f49
cleanup dbal
pxpm Mar 18, 2024
2aa9cea
Apply fixes from StyleCI
StyleCIBot Mar 18, 2024
caad11e
fix PHPDoc CrudController
makss Mar 18, 2024
597874e
styleci
makss Mar 18, 2024
a167a4c
Merge branch 'main' into add-method-to-get-ajax-uploaders
pxpm Mar 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ protected function makeSureSubfieldsHaveNecessaryAttributes($field)
$subfield['name'] = Str::replace(' ', '', $subfield['name']);

$subfield['parentFieldName'] = $field['name'];
$subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name'];
$subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.');

if (! isset($field['model'])) {
// we're inside a simple 'repeatable' with no model/relationship, so
Expand All @@ -290,8 +292,6 @@ protected function makeSureSubfieldsHaveNecessaryAttributes($field)
$currentEntity = $subfield['baseEntity'] ?? $field['entity'];
$subfield['baseModel'] = $subfield['baseModel'] ?? $field['model'];
$subfield['baseEntity'] = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$currentEntity : $currentEntity;
$subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name'];
$subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.');
}

$field['subfields'][$key] = $this->makeSureFieldHasNecessaryAttributes($subfield);
Expand Down
71 changes: 71 additions & 0 deletions src/app/Library/Uploaders/Support/UploadersRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Backpack\CRUD\app\Library\Uploaders\Support;

use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface;
use Illuminate\Support\Str;

final class UploadersRepository
{
Expand Down Expand Up @@ -119,4 +121,73 @@ public function getRegisteredUploadNames(string $uploadName): array
return $uploader->getName();
}, $this->getRepeatableUploadersFor($uploadName));
}

/**
* Get the uploaders classes for the given group of uploaders.
*/
public function getAjaxUploadTypes(string $uploaderMacro = 'withFiles'): array
{
$ajaxFieldTypes = [];
foreach ($this->uploaderClasses[$uploaderMacro] as $fieldType => $uploader) {
if (is_a($uploader, 'Backpack\Pro\Uploads\BackpackAjaxUploader', true)) {
$ajaxFieldTypes[] = $fieldType;
}
}

return $ajaxFieldTypes;
}

/**
* Get an uploader instance for a given crud object.
*/
public function getFieldUploaderInstance(string $requestInputName): UploaderInterface
{
if (strpos($requestInputName, '#') !== false) {
$repeatableContainerName = Str::before($requestInputName, '#');
$requestInputName = Str::after($requestInputName, '#');
$uploaders = $this->getRepeatableUploadersFor($repeatableContainerName);
//TODO: Implement the logic for repeatable uploaders
dd('here');
}

if (empty($crudObject = CRUD::fields()[$requestInputName])) {
abort(500, 'Could not find the field in the CRUD fields.');
}

if (! $uploaderMacro = $this->getUploadCrudObjectMacroType($crudObject)) {
abort(500, 'There is no uploader defined for the given field type.');
}

if (! $this->isValidUploadField($crudObject, $uploaderMacro)) {
abort(500, 'Invalid field for upload.');
}

$uploaderConfiguration = $crudObject[$uploaderMacro] ?? [];
$uploaderConfiguration = ! is_array($uploaderConfiguration) ? [] : $uploaderConfiguration;
$uploaderClass = $this->getUploadFor($crudObject['type'], $uploaderMacro);

return new $uploaderClass(['name' => $requestInputName], $uploaderConfiguration);
}

/**
* Get the upload field macro type for the given object.
*/
private function getUploadCrudObjectMacroType(array $crudObject): string
{
return isset($crudObject['withFiles']) ? 'withFiles' : ($crudObject['withMedia'] ? 'withMedia' : null);
}

private function isValidUploadField($crudObject, $uploaderMacro)
{
if (Str::contains($crudObject['name'], '#')) {
$container = Str::before($crudObject['name'], '#');
$field = array_filter(CRUD::fields()[$container]['subfields'] ?? [], function ($item) use ($crudObject, $uploaderMacro) {
return $item['name'] === $crudObject['name'] && in_array($item['type'], $this->getAjaxUploadTypes($uploaderMacro));
});

return ! empty($field);
}

return in_array($crudObject['type'], $this->getAjaxUploadTypes($uploaderMacro));
}
}
141 changes: 130 additions & 11 deletions src/app/Library/Validation/Rules/BackpackCustomRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

namespace Backpack\CRUD\app\Library\Validation\Rules;

use Backpack\CRUD\app\Library\Validation\Rules\Support\ValidateArrayContract;
use Closure;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;

/**
* @method static static itemRules()
*/
abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule
{
use Support\HasFiles;

/**
* @var \Illuminate\Contracts\Validation\Validator
*/
Expand All @@ -30,6 +33,12 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self
$instance = new static();
$instance->fieldRules = self::getRulesAsArray($rules);

if ($instance->validatesArrays()) {
if (! in_array('array', $instance->getFieldRules())) {
$instance->fieldRules[] = 'array';
}
}

return $instance;
}

Expand All @@ -43,7 +52,18 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
// is the extending class reponsability the implementation of the validation logic
$value = $this->ensureValueIsValid($value);

if ($value === false) {
$fail('Invalid value for the attribute.')->translate();

return;
}

$errors = $this->validateOnSubmit($attribute, $value);
foreach ($errors as $error) {
$fail($error)->translate();
}
}

/**
Expand Down Expand Up @@ -96,19 +116,118 @@ protected static function getRulesAsArray($rules)
return $rules;
}

protected function ensureValueIsValid($value)
{
if ($this->validatesArrays() && ! is_array($value)) {
try {
$value = json_decode($value, true) ?? [];
} catch(\Exception $e) {
return false;
}
}

return $value;
}

private function validatesArrays(): bool
{
return is_a($this, ValidateArrayContract::class);
}

private function validateAndGetErrors(string $attribute, mixed $value, array $rules): array
{
$validator = Validator::make($value, [
$attribute => $rules,
], $this->validator->customMessages, $this->validator->customAttributes);

return $validator->errors()->messages()[$attribute] ?? [];
}

protected function getValidationAttributeString(string $attribute)
{
return Str::substrCount($attribute, '.') > 1 ?
Str::before($attribute, '.').'.*.'.Str::afterLast($attribute, '.') :
$attribute;
}

protected function validateOnSubmit(string $attribute, mixed $value): array
{
return $this->validateRules($attribute, $value);
}

protected function validateFieldAndFile(string $attribute, null|array $data = null, array|null $customRules = null): array
{
$fieldErrors = $this->validateFieldRules($attribute, $data, $customRules);
$fileErrors = $this->validateFileRules($attribute, $data);

return array_merge($fieldErrors, $fileErrors);
}

/**
* Implementation.
*/
public function validateFieldRules(string $attribute, mixed $value, Closure $fail): void
public function validateFieldRules(string $attribute, null|array|UploadedFile $data = null, array|null $customRules = null): array
{
$validator = Validator::make([$attribute => $value], [
$attribute => $this->getFieldRules(),
], $this->validator->customMessages, $this->validator->customAttributes);
$data = $data ?? $this->data;
$validationRuleAttribute = $this->getValidationAttributeString($attribute);

$data = $this->prepareValidatorData($data, $attribute);

return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules());
}

if ($validator->fails()) {
foreach ($validator->errors()->messages()[$attribute] as $message) {
$fail($message)->translate();
protected function prepareValidatorData(array|UploadedFile $data, string $attribute): array
{
if ($this->validatesArrays() && is_array($data)) {
return Arr::has($data, $attribute) ? $data : [$attribute => Arr::get($data, $attribute)];
}

return [$attribute => $data];
}

protected function validateFileRules(string $attribute, mixed $data): array
{
$data = $data ?? $this->data;
$items = is_array($data) && array_key_exists($attribute, $data) ? $data[$attribute] : $data;
$items = is_array($items) ? $items : [$items];
$errors = [];
// we validate each file individually to avoid returning messages like: `field.0` is not a pdf.
foreach ($items as $sentFiles) {
if (! is_array($sentFiles)) {
try {
if (is_file($sentFiles)) {
$errors[] = $this->validateAndGetErrors($attribute, [$attribute => $sentFiles], $this->getFileRules());
}
continue;
} catch(\Exception) {
$errors[] = 'Unknown datatype, aborting upload process.';
break;
}
}

if (is_multidimensional_array($sentFiles)) {
foreach ($sentFiles as $key => $value) {
foreach ($value[$attribute] as $file) {
if (is_file($file)) {
$errors[] = $this->validateAndGetErrors($attribute, [$attribute => $file], $this->getFileRules());
}
}
}
continue;
}

foreach ($sentFiles as $key => $value) {
if (is_file($value)) {
$errors[] = $this->validateAndGetErrors($attribute, [$attribute => $value], $this->getFileRules());
}
}
}

return array_unique(array_merge(...$errors));
}

public function validateRules(string $attribute, mixed $value): array
{
return $this->validateFieldAndFile($attribute, $value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Backpack\CRUD\app\Library\Validation\Rules\Support;

interface ValidateArrayContract
{
}
101 changes: 0 additions & 101 deletions src/app/Library/Validation/Rules/ValidFileArray.php

This file was deleted.

Loading
Loading