I have CKEditor5 installed as my HTML editor inside my Laravel 9 project. The way I have the code now, when the image is uploaded, the original is left alone and is set as default. In addition, two new images are created. One is a thumbnail. The other is a preview (800 pixels wide). When the image is brought into the editor, the image is set to width=”800”. I need to change the with to be width=”100%”. I tried using the following javascript code to accomplish it, but it is not working. Any suggestions?
This is my textarea that is converted to a CKEditor
<textarea name="body" id="body">{{ $kb->body }}</textarea>
This is the javascript code (broken into four script blocks) for the HTML editor. The main init script, a link to the CKEDITOR5 javscript, a custom uploader and a size modifier.:
<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;
}
// 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);
// Important note: This is the right place to implement security mechanisms
// like authentication and CSRF protection. For instance, you can use
// XMLHttpRequest.setRequestHeader() to set the request headers containing
// the CSRF token generated earlier by your application.
// 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>
class MyCustomImagePlugin {
static get pluginName() {
return 'MyCustomImagePlugin';
}
constructor(editor) {
console.log('MyCustomImagePlugin constructor called');
this.editor = editor;
}
init() {
const editor = this.editor;
// Modify the image output to set width to 100%
editor.conversion.for('downcast').add(dispatcher => {
dispatcher.on('attribute:src:image', (evt, data, conversionApi) => {
const viewWriter = conversionApi.writer;
const viewFigure = conversionApi.mapper.toViewElement(data.item);
const viewImg = viewFigure.getChild(0);
if (data.attributeNewValue !== null) {
viewWriter.setAttribute('src', data.attributeNewValue, viewImg);
} else {
viewWriter.removeAttribute('src', viewImg);
}
});
dispatcher.on('insert:image', (evt, data, conversionApi) => {
const viewWriter = conversionApi.writer;
const viewFigure = conversionApi.mapper.toViewElement(data.item);
const viewImg = viewFigure.getChild(0);
viewWriter.setStyle('width', '100%', viewImg);
// If you need to set other styles, you can do so here
});
});
}
}
</script>
<script>
ClassicEditor
.create(document.querySelector('#body'), {
extraPlugins: [MyCustomUploadAdapterPlugin, MyCustomImagePlugin],
// More configuration options.
// ...
})
.catch(error => {
console.log(error);
});
</script>
This is my ImageUploadController:
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
]);
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/posts', $filenametostore);
// Create and store thumbnail
$thumbnail = Image::make($uploadedFile)->resize(500, 150, function ($constraint) {
$constraint->aspectRatio();
});
$thumbnailPath = 'public/uploads/posts/thumbnail/' . $filenametostore;
Storage::put($thumbnailPath, (string)$thumbnail->encode());
// Create and store Preview
$preview = Image::make($uploadedFile)->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
});
$previewPath = 'public/uploads/posts/preview/' . $filenametostore;
Storage::put($previewPath, (string)$preview->encode());
Log::info('storage/uploads/post/' . $filenametostore);
// Return response
return response()->json([
'default' => asset('storage/uploads/posts/' . $filenametostore),
'800' => asset('storage/uploads/posts/preview/' . $filenametostore),
]);
}
// Handle case where no file is uploaded
return response()->json(['error' => 'No file uploaded'], 400);
}
}
The image uploader and the thumbnail creator code is working as expected. However, the code that displays the image is saved as follows:
<figure class="image">
<img src="https://dev.fireops.local/storage/uploads/posts/27971931_10155469749288693_4937844076375170817_n_1710860566.jpg"
srcset="https://dev.fireops.local/storage/uploads/posts/preview/27971931_10155469749288693_4937844076375170817_n_1710860566.jpg 800w"
sizes="100vw"
width="800">
</figure>
How can I change with width to be 100% instead of 800px?