I use a component to implement my CKEditor on numerous pages throughout my project.
<x-ckeditor folderName="posts" />
This component code includes a block about uploading an image to an ImageUploadContoller. When the image is uploaded via javascript, several versions of the image are created. I want the foldername that I am passing into the compoent to direct the location where the images should be saved. this is my component:
<script src="{{ asset('/plugins/ckeditor5/ckeditor.js') }}"></script>
<script>
class MyUploadAdapter {
constructor(loader) {
// The file loader instance to use during the upload.
this.loader = loader;
this.folderName = '{{ $folderName }}'; // Add folderName property
}
// Starts the upload process.
upload() {
return this.loader.file
.then(file => new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
}));
}
// Aborts the upload process.
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
// Initializes the XMLHttpRequest object using the URL passed to the constructor.
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
// Note that your request may look different. It is up to you and your editor
// integration to choose the right communication channel. This example uses
// a POST request with JSON as a data structure but your configuration
// could be different.
xhr.open('POST', "{{ route('image.upload', ['_token' => csrf_token()]) }}", true);
xhr.responseType = 'json';
}
// Initializes XMLHttpRequest listeners.
_initListeners(resolve, reject, file) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `Couldn't upload file: ${ file.name }.`;
xhr.addEventListener('error', () => reject(genericErrorText));
xhr.addEventListener('abort', () => reject());
xhr.addEventListener('load', () => {
const response = xhr.response;
// This example assumes the XHR server's "response" object will come with
// an "error" which has its own "message" that can be passed to reject()
// in the upload promise.
//
// Your integration may handle upload errors in a different way so make sure
// it is done properly. The reject() function must be called when the upload fails.
if (!response || response.error) {
return reject(response && response.error ? response.error.message : genericErrorText);
}
// If the upload is successful, resolve the upload promise with an object containing
// at least the "default" URL, pointing to the image on the server.
// This URL will be used to display the image in the content. Learn more in the
// UploadAdapter#upload documentation.
resolve(response);
});
// Upload progress when it is supported. The file loader has the #uploadTotal and #uploaded
// properties which are used e.g. to display the upload progress bar in the editor
// user interface.
if (xhr.upload) {
xhr.upload.addEventListener('progress', evt => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
// Prepares the data and sends the request.
_sendRequest(file) {
// Prepare the form data.
const data = new FormData();
data.append('upload', file);
data.append('folderName', this.folderName); // Append the folderName to the FormData
// Send the request.
this.xhr.send(data);
}
}
function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
// Configure the URL to the upload script in your back-end here!
return new MyUploadAdapter(loader);
};
}
</script>
<script>
// If you want inserted images in a CKEditor to be responsive
// you can use the following code. It creates a htmlfilter for the
// image tag that replaces inline "width" and "style" definitions with
// their corresponding attributes and adds (in this example) the
// Bootstrap "img-responsive" class.
ClassicEditor
.create(document.querySelector('#body'), {
extraPlugins: [MyCustomUploadAdapterPlugin],
})
.then(editor => {
editor.model.schema.extend('$block', {
allowAttributes: 'style'
});
editor.model.schema.extend('img', {
allowAttributes: 'style'
});
editor.conversion.for('upcast').attributeToAttribute({
model: 'style',
view: 'style'
});
editor.conversion.for('downcast').attributeToAttribute({
model: 'style',
view: 'style'
});
editor.data.processor.htmlFilter.addRules({
elements: {
img: function(el) {
// Add Bootstrap "img-responsive" class to each inserted image
el.addClass('img-responsive');
// Process inline "width" and "style" definitions to support percentages
var style = el.attributes.style;
if (style) {
// Get the width from the style
var match = /(?:^|\s)width\s*:\s*(\d+(?:\.\d+)?)(%|px);?/i.exec(style),
width = match && match[1],
unit = match && match[2];
// Replace the width
if (width) {
el.attributes.style = el.attributes.style.replace(
/(?:^|\s)width\s*:\s*(\d+(?:\.\d+)?)%?px;?/i, '');
el.attributes.width = unit === '%' ? width : null;
el.attributes.style += 'width: ' + width + (unit === '%' ? '%' : 'px') +
';';
}
}
// Remove the style attribute if it is empty
if (!el.attributes.style)
delete el.attributes.style;
return el;
}
}
});
})
.catch(error => {
console.error(error);
});
</script>
Here is the current route that is being used to direct the javascript to the appropriate controller:
Route::post('/image-upload/', [ImageUploadController::class, 'upload'])->name('image.upload');
Here is the ImageUploadController class where the image processing happens. Where you see {$folderName}, I want to replace that with the passed folderName:
class ImageUploadController extends Controller
{
public function upload(Request $request)
{
// Validate the uploaded file
$request->validate([
'upload' => 'required|image|mimes:jpeg,png,jpg,gif|max:10000', // Example validation rules
'folderName' => 'required|string' // Validate that folderName is provided and is a string
]);
if ($request->hasFile('upload')) {
$uploadedFile = $request->file('upload');
// Generate a unique filename
$filename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
$extension = $uploadedFile->getClientOriginalExtension();
$filenametostore = $filename . '_' . time() . '.' . $extension;
// Store the uploaded file
$uploadedFile->storeAs('public/uploads/{$folderName}/full', $filenametostore);
// Create and store thumbnail
$thumbnail = Image::make($uploadedFile)->resize(500, 150, function ($constraint) {
$constraint->aspectRatio();
});
$thumbnailPath = 'public/uploads/{$folderName}/thumbnail/' . $filenametostore;
Storage::put($thumbnailPath, (string)$thumbnail->encode());
// Create and store Preview
$preview = Image::make($uploadedFile)->resize(1000, null, function ($constraint) {
$constraint->aspectRatio();
});
$previewPath = 'public/uploads/{$folderName}/preview/' . $filenametostore;
Storage::put($previewPath, (string)$preview->encode());
Log::info('storage/uploads/{$folderName}/' . $filenametostore);
// Return response
return response()->json([
'default' => asset('storage/uploads/{$folderName}/' . $filenametostore),
'500' => asset('storage/uploads/{$folderName}/thumbnail/' . $filenametostore),
'1000' => asset('storage/uploads/{$folderName}/preview/' . $filenametostore),
]);
}
// Handle case where no file is uploaded
return response()->json(['error' => 'No file uploaded'], 400);
}
}
How can I accomplish this?