Skip to content

Commit e2f164f

Browse files
pxpmStyleCIBot
andcommitted
Clean up uploaders (#5725)
* wip * clean up * add get uploadedFilesFromRequest to clean up uploaders * Apply fixes from StyleCI [ci skip] [skip ci] * fix type cast * add summernote uploader * update summernote * add summernote * add more test assets --------- Co-authored-by: StyleCI Bot <bot@styleci.io>
1 parent 63918bc commit e2f164f

File tree

10 files changed

+110
-36
lines changed

10 files changed

+110
-36
lines changed

src/app/Library/Uploaders/MultipleFiles.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function uploadFiles(Model $entry, $value = null)
2222
}
2323

2424
$filesToDelete = $this->getFilesToDeleteFromRequest();
25-
$value = $value ?? collect(CRUD::getRequest()->file($this->getNameForRequest()))->flatten()->toArray();
25+
$value = $value ?? collect($value)->flatten()->toArray();
2626
$previousFiles = $this->getPreviousFiles($entry) ?? [];
2727

2828
if (is_array($previousFiles) && empty($previousFiles[0] ?? [])) {
@@ -108,12 +108,12 @@ public function uploadRepeatableFiles($files, $previousRepeatableValues, $entry
108108
return $fileOrder;
109109
}
110110

111-
protected function hasDeletedFiles($value): bool
111+
public function hasDeletedFiles($value): bool
112112
{
113113
return empty($this->getFilesToDeleteFromRequest()) ? false : true;
114114
}
115115

116-
protected function getEntryAttributeValue(Model $entry)
116+
public function getEntryAttributeValue(Model $entry)
117117
{
118118
$value = $entry->{$this->getAttributeName()};
119119

src/app/Library/Uploaders/SingleBase64Image.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ class SingleBase64Image extends Uploader
1212
{
1313
public function uploadFiles(Model $entry, $value = null)
1414
{
15-
$value = $value ?? CRUD::getRequest()->get($this->getName());
1615
$previousImage = $this->getPreviousFiles($entry);
1716

1817
if (! $value && $previousImage) {
@@ -61,7 +60,7 @@ public function uploadRepeatableFiles($values, $previousRepeatableValues, $entry
6160
return $values;
6261
}
6362

64-
protected function shouldUploadFiles($value): bool
63+
public function shouldUploadFiles($value): bool
6564
{
6665
return $value && is_string($value) && Str::startsWith($value, 'data:image');
6766
}
@@ -70,4 +69,9 @@ public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): boo
7069
{
7170
return $entry->exists && is_string($entryValue) && ! Str::startsWith($entryValue, 'data:image');
7271
}
72+
73+
public function getUploadedFilesFromRequest()
74+
{
75+
return CRUD::getRequest()->get($this->getNameForRequest());
76+
}
7377
}

src/app/Library/Uploaders/SingleFile.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ class SingleFile extends Uploader
1010
{
1111
public function uploadFiles(Model $entry, $value = null)
1212
{
13-
$value = $value ?? CrudPanelFacade::getRequest()->file($this->getName());
1413
$previousFile = $this->getPreviousFiles($entry);
1514

1615
if ($value === false && $previousFile) {
@@ -75,12 +74,12 @@ public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): boo
7574
return is_string($entryValue);
7675
}
7776

78-
protected function hasDeletedFiles($entryValue): bool
77+
public function hasDeletedFiles($entryValue): bool
7978
{
8079
return $entryValue === null;
8180
}
8281

83-
protected function shouldUploadFiles($value): bool
82+
public function shouldUploadFiles($value): bool
8483
{
8584
return is_a($value, 'Illuminate\Http\UploadedFile', true);
8685
}

src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ public static function for(array $field, array $configuration): UploaderInterfac
1515
/**
1616
* Default implementation functions.
1717
*/
18+
19+
// method called on `saving` event to store and update the entry with the uploaded files
1820
public function storeUploadedFiles(Model $entry);
1921

22+
// method called on `retrieved` event to populated the uploaded files in the entry
2023
public function retrieveUploadedFiles(Model $entry);
2124

25+
// method called on `deleting` event to delete the uploaded files
2226
public function deleteUploadedFiles(Model $entry);
2327

2428
/**
@@ -55,17 +59,20 @@ public function getIdentifier(): string;
5559

5660
public function getNameForRequest(): string;
5761

58-
public function shouldDeleteFiles(): bool;
59-
6062
public function canHandleMultipleFiles(): bool;
6163

6264
public function isRelationship(): bool;
6365

6466
public function getPreviousFiles(Model $entry): mixed;
6567

66-
public function getValueWithoutPath(?string $value = null): ?string;
68+
/**
69+
* Strategy methods.
70+
*/
71+
public function shouldDeleteFiles(): bool;
72+
73+
public function hasDeletedFiles($entryValue): bool;
6774

68-
public function isFake(): bool;
75+
public function shouldUploadFiles(mixed $value): bool;
6976

70-
public function getFakeAttribute(): bool|string;
77+
public function shouldKeepPreviousValueUnchanged(Model $entry, mixed $entryValue): bool;
7178
}

src/app/Library/Uploaders/Support/Traits/HandleRepeatableUploads.php

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ protected function handleRepeatableFiles(Model $entry): Model
6262
return $this->processRelationshipRepeatableUploaders($entry);
6363
}
6464

65-
$processedEntryValues = $this->processRepeatableUploads($entry, $value);
65+
$processedEntryValues = $this->processRepeatableUploads($entry, $value)->toArray();
6666

6767
if ($this->isFake()) {
6868
$fakeValues = $entry->{$this->getFakeAttribute()} ?? [];
@@ -147,17 +147,7 @@ protected function getEntryOriginalValue(Model $entry)
147147
return $entry->getOriginal($this->getAttributeName());
148148
}
149149

150-
protected function shouldUploadFiles($entryValue): bool
151-
{
152-
return true;
153-
}
154-
155-
protected function hasDeletedFiles($entryValue): bool
156-
{
157-
return $entryValue === false || $entryValue === null || $entryValue === [null];
158-
}
159-
160-
protected function processRepeatableUploads(Model $entry, Collection $values): array
150+
protected function processRepeatableUploads(Model $entry, Collection $values): Collection
161151
{
162152
foreach (app('UploadersRepository')->getRepeatableUploadersFor($this->getRepeatableContainerName()) as $uploader) {
163153
$uploadedValues = $uploader->uploadRepeatableFiles($values->pluck($uploader->getAttributeName())->toArray(), $this->getPreviousRepeatableValues($entry, $uploader));
@@ -169,7 +159,7 @@ protected function processRepeatableUploads(Model $entry, Collection $values): a
169159
});
170160
}
171161

172-
return $values->toArray();
162+
return $values;
173163
}
174164

175165
private function retrieveRepeatableFiles(Model $entry): Model

src/app/Library/Uploaders/Uploader.php

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Backpack\CRUD\app\Library\Uploaders;
44

5+
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
56
use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface;
67
use Backpack\CRUD\app\Library\Uploaders\Support\Traits\HandleFileNaming;
78
use Backpack\CRUD\app\Library\Uploaders\Support\Traits\HandleRepeatableUploads;
@@ -76,17 +77,19 @@ public function storeUploadedFiles(Model $entry): Model
7677
return $this->handleRepeatableFiles($entry);
7778
}
7879

80+
$values = $this->getUploadedFilesFromRequest();
81+
7982
if ($this->attachedToFakeField) {
8083
$fakeFieldValue = $entry->{$this->attachedToFakeField};
8184
$fakeFieldValue = is_string($fakeFieldValue) ? json_decode($fakeFieldValue, true) : (array) $fakeFieldValue;
82-
$fakeFieldValue[$this->getAttributeName()] = $this->uploadFiles($entry);
85+
$fakeFieldValue[$this->getAttributeName()] = $this->uploadFiles($entry, $values);
8386

8487
$entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $fakeFieldValue : json_encode($fakeFieldValue);
8588

8689
return $entry;
8790
}
8891

89-
$entry->{$this->getAttributeName()} = $this->uploadFiles($entry);
92+
$entry->{$this->getAttributeName()} = $this->uploadFiles($entry, $values);
9093

9194
return $entry;
9295
}
@@ -151,6 +154,21 @@ public function shouldDeleteFiles(): bool
151154
return $this->deleteWhenEntryIsDeleted;
152155
}
153156

157+
public function shouldUploadFiles($entryValue): bool
158+
{
159+
return true;
160+
}
161+
162+
public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
163+
{
164+
return $entry->exists && ($entryValue === null || $entryValue === [null]);
165+
}
166+
167+
public function hasDeletedFiles($entryValue): bool
168+
{
169+
return $entryValue === false || $entryValue === null || $entryValue === [null];
170+
}
171+
154172
public function getIdentifier(): string
155173
{
156174
if ($this->handleRepeatableFiles) {
@@ -191,6 +209,11 @@ public function getValueWithoutPath(?string $value = null): ?string
191209
return $value ? Str::after($value, $this->path) : null;
192210
}
193211

212+
public function getUploadedFilesFromRequest()
213+
{
214+
return CRUD::getRequest()->file($this->getNameForRequest());
215+
}
216+
194217
public function isFake(): bool
195218
{
196219
return $this->attachedToFakeField !== false;
@@ -201,11 +224,6 @@ public function getFakeAttribute(): bool|string
201224
return $this->attachedToFakeField;
202225
}
203226

204-
public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
205-
{
206-
return $entry->exists && ($entryValue === null || $entryValue === [null]);
207-
}
208-
209227
/*******************************
210228
* Setters - fluently configure the uploader
211229
*******************************/

src/resources/views/crud/fields/summernote.blade.php

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
name="{{ $field['name'] }}"
1414
data-init-function="bpFieldInitSummernoteElement"
1515
data-options="{{ json_encode($field['options']) }}"
16+
data-upload-enabled="{{ isset($field['withFiles']) || isset($field['withMedia']) || isset($field['imageUploadEndpoint']) ? 'true' : 'false'}}"
17+
data-upload-endpoint="{{ isset($field['imageUploadEndpoint']) ? $field['imageUploadEndpoint'] : 'false'}}"
18+
data-upload-operation="{{ $crud->get('ajax-upload.formOperation') }}"
1619
bp-field-main-input
1720
@include('crud::fields.inc.attributes', ['default_class' => 'form-control summernote'])
1821
>{{ old_empty_or_null($field['name'], '') ?? $field['value'] ?? $field['default'] ?? '' }}</textarea>
@@ -31,8 +34,8 @@
3134
{{-- FIELD CSS - will be loaded in the after_styles section --}}
3235
@push('crud_fields_styles')
3336
{{-- include summernote css --}}
34-
@basset('https://unpkg.com/summernote@0.8.20/dist/summernote-lite.min.css')
35-
@basset('https://unpkg.com/summernote@0.8.20/dist/font/summernote.woff2', false)
37+
@basset('https://unpkg.com/summernote@0.9.1/dist/summernote-lite.min.css')
38+
@basset('https://unpkg.com/summernote@0.9.1/dist/font/summernote.woff2', false)
3639
@bassetBlock('backpack/crud/fields/summernote-field.css')
3740
<style type="text/css">
3841
.note-editor.note-frame .note-status-output, .note-editor.note-airframe .note-status-output {
@@ -45,7 +48,7 @@
4548
{{-- FIELD JS - will be loaded in the after_scripts section --}}
4649
@push('crud_fields_scripts')
4750
{{-- include summernote js --}}
48-
@basset('https://unpkg.com/summernote@0.8.20/dist/summernote-lite.min.js')
51+
@basset('https://unpkg.com/summernote@0.9.1/dist/summernote-lite.min.js')
4952
@bassetBlock('backpack/crud/fields/summernote-field.js')
5053
<script>
5154
function bpFieldInitSummernoteElement(element) {
@@ -54,7 +57,59 @@ function bpFieldInitSummernoteElement(element) {
5457
let summernotCallbacks = {
5558
onChange: function(contents, $editable) {
5659
element.val(contents).trigger('change');
60+
},
61+
}
62+
63+
if(element.data('upload-enabled') === true){
64+
let imageUploadEndpoint = element.data('upload-endpoint') !== false ? element.data('upload-endpoint') : '{{ url($crud->route. '/ajax-upload') }}';
65+
let paramName = typeof element.attr('data-repeatable-input-name') !== 'undefined' ? element.closest('[data-repeatable-identifier]').attr('data-repeatable-identifier')+'#'+element.attr('data-repeatable-input-name') : element.attr('name');
66+
summernotCallbacks.onImageUpload = function(file) {
67+
var data = new FormData();
68+
data.append(paramName, file[0]);
69+
data.append('_token', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
70+
data.append('fieldName', paramName);
71+
data.append('operation', element.data('upload-operation'));
72+
73+
var xhr = new XMLHttpRequest();
74+
xhr.open('POST', imageUploadEndpoint, true);
75+
xhr.setRequestHeader('Accept', 'application/json');
76+
77+
xhr.onload = function() {
78+
if (xhr.status >= 200 && xhr.status < 300) {
79+
var response = JSON.parse(xhr.responseText);
80+
element.summernote('insertImage', response.data.filePath);
81+
} else {
82+
var response = JSON.parse(xhr.responseText);
83+
let errorBagName = paramName;
84+
// it's in a repeatable field
85+
if(errorBagName.includes('#')) {
86+
errorBagName = errorBagName.replace('#', '.0.');
87+
}
88+
let errorMessages = typeof response.errors !== 'undefined' ? response.errors[errorBagName].join('<br/>') : response + '<br/>';
89+
90+
let summernoteTextarea = element[0];
91+
92+
// remove previous error messages
93+
summernoteTextarea.parentNode.querySelector('.invalid-feedback')?.remove();
94+
95+
// add the red text classes
96+
summernoteTextarea.parentNode.classList.add('text-danger');
97+
98+
// create the error message container
99+
let errorContainer = document.createElement("div");
100+
errorContainer.classList.add('invalid-feedback', 'd-block');
101+
errorContainer.innerHTML = errorMessages;
102+
summernoteTextarea.parentNode.appendChild(errorContainer);
103+
}
104+
};
105+
106+
xhr.onerror = function() {
107+
console.error('An error occurred during the upload process');
108+
};
109+
110+
xhr.send(data);
57111
}
112+
58113
}
59114
60115
element.on('CrudField:disable', function(e) {
4.35 KB
Loading

tests/config/Uploads/assets/pic7.jpg

4.35 KB
Loading

tests/config/database/migrations/2024_02_15_125654_create_uploaders_table.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public function up(): void
1818
$table->json('upload_multiple')->nullable();
1919
$table->json('dropzone')->nullable();
2020
$table->json('easymde')->nullable();
21+
$table->json('summernote')->nullable();
2122
$table->json('repeatable')->nullable();
2223
$table->json('extras')->nullable();
2324
});

0 commit comments

Comments
 (0)