Browse Source

Merge branch 'master' into t/ckeditor5/1791

Aleksander Nowodzinski 6 years ago
parent
commit
9014fe1fe0

+ 5 - 0
packages/ckeditor5-upload/docs/_snippets/features/base64-upload.html

@@ -0,0 +1,5 @@
+<div id="snippet-image-base64-upload">
+	<p>Paste or drop an image directly into the editor. You can also use the "Insert image" button in the toolbar.</p>
+</div>
+
+<button type="button" id="log-data">Log editor data</button>

+ 29 - 0
packages/ckeditor5-upload/docs/_snippets/features/base64-upload.js

@@ -0,0 +1,29 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals console, window, document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
+import Base64UploadAdapter from '@ckeditor/ckeditor5-upload/src/base64uploadadapter';
+
+ClassicEditor.builtinPlugins.push( Base64UploadAdapter );
+
+ClassicEditor
+	.create( document.querySelector( '#snippet-image-base64-upload' ), {
+		toolbar: {
+			viewportTopOffset: window.getViewportTopOffsetConfig()
+		}
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );
+
+// The "Log editor data" button logic.
+document.querySelector( '#log-data' ).addEventListener( 'click', () => {
+	console.log( window.editor.getData() );
+} );

+ 6 - 0
packages/ckeditor5-upload/docs/api/upload.md

@@ -12,6 +12,12 @@ This package implements various file upload utilities for CKEditor 5.
 
 See the {@link module:upload/filerepository~FileRepository} plugin documentation.
 
+## Upload Adapters
+
+This repository contains the following upload adapters:
+
+* {@link module:upload/base64uploadadapter~Base64UploadAdapter `Base64UploadAdapter`} - plugin that converts images inserted into the editor into [Base64 strings](https://en.wikipedia.org/wiki/Base64) in the {@glink builds/guides/integration/saving-data editor output}.
+
 ## Installation
 
 ```bash

+ 63 - 0
packages/ckeditor5-upload/docs/features/base64-upload-adapter.md

@@ -0,0 +1,63 @@
+---
+category: features-image-upload
+menu-title: Base64 upload adapter
+order: 40
+---
+
+# Base64 upload adapter
+
+The {@link module:upload/base64uploadadapter~Base64UploadAdapter Base64 image upload adapter} plugin converts images inserted into the editor into [Base64 strings](https://en.wikipedia.org/wiki/Base64) in the {@link builds/guides/integration/saving-data editor output}.
+
+This kind of image upload does not require server processing – images are stored with the rest of the text and displayed by the web browser without additional requests. On the downside, this approach can bloat your database with very long data strings which, in theory, could have a negative impact on the performance.
+
+<info-box>
+	Check out the comprehensive {@link features/image-upload Image upload overview} to learn about other ways to upload images into CKEditor 5.
+</info-box>
+
+## Example
+
+Use the editor below to see the adapter in action. Open the web browser console and click the button below to see the base64–encoded image in the editor output data.
+
+{@snippet features/base64-upload}
+
+## Installation
+
+<info-box info>
+	The [`@ckeditor/ckeditor5-upload`](https://www.npmjs.com/package/@ckeditor/ckeditor5-upload) package is available by default in all builds. The installation instructions are for developers interested in building their own, custom WYSIWYG editor.
+</info-box>
+
+To add this feature to your editor, install the [`@ckeditor/ckeditor5-upload`](https://www.npmjs.com/package/@ckeditor/ckeditor5-upload) package:
+
+```bash
+npm install --save @ckeditor/ckeditor5-upload
+```
+
+Then add the {@link module:upload/base64uploadadapter~Base64UploadAdapter `Base64UploadAdapter`} to your plugin list:
+
+```js
+import Base64UploadAdapter from '@ckeditor/ckeditor5-upload/src/base64uploadadapter';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ Base64UploadAdapter, ... ],
+		toolbar: [ ... ]
+	} )
+	.then( ... )
+	.catch( ... );
+```
+
+Once enabled in the plugin list, the upload adapter works out–of–the–box without additional configuration.
+
+<info-box info>
+	Read more about {@link builds/guides/integration/installing-plugins installing plugins}.
+</info-box>
+
+## What's next?
+
+Check out the comprehensive {@link features/image-upload Image upload overview} to learn more about different ways of uploading images in CKEditor 5.
+
+See the {@link features/image Image feature} guide to find out more about handling images in CKEditor 5.
+
+## Contribute
+
+The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-upload.

+ 108 - 0
packages/ckeditor5-upload/src/base64uploadadapter.js

@@ -0,0 +1,108 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module upload/base64uploadadapter
+ */
+
+/* globals window */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import FileRepository from './filerepository';
+
+/**
+ * A plugin that converts images inserted into the editor into [Base64 strings](https://en.wikipedia.org/wiki/Base64)
+ * in the {@glink builds/guides/integration/saving-data editor output}.
+ *
+ * This kind of image upload does not require server processing – images are stored with the rest of the text and
+ * displayed by the web browser without additional requests.
+ *
+ * Check out the {@glink features/image-upload/image-upload comprehensive "Image upload overview"} to learn about
+ * other ways to upload images into CKEditor 5.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Base64UploadAdapter extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ FileRepository ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'Base64UploadAdapter';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		this.editor.plugins.get( FileRepository ).createUploadAdapter = loader => new Adapter( loader );
+	}
+}
+
+/**
+ * The upload adapter that converts images inserted into the editor into Base64 strings.
+ *
+ * @private
+ * @implements module:upload/filerepository~UploadAdapter
+ */
+class Adapter {
+	/**
+	 * Creates a new adapter instance.
+	 *
+	 * @param {module:upload/filerepository~FileLoader} loader
+	 */
+	constructor( loader ) {
+		/**
+		 * `FileLoader` instance to use during the upload.
+		 *
+		 * @member {module:upload/filerepository~FileLoader} #loader
+		 */
+		this.loader = loader;
+	}
+
+	/**
+	 * Starts the upload process.
+	 *
+	 * @see module:upload/filerepository~UploadAdapter#upload
+	 * @returns {Promise}
+	 */
+	upload() {
+		return new Promise( ( resolve, reject ) => {
+			const reader = this.reader = new window.FileReader();
+
+			reader.addEventListener( 'load', () => {
+				resolve( { default: reader.result } );
+			} );
+
+			reader.addEventListener( 'error', err => {
+				reject( err );
+			} );
+
+			reader.addEventListener( 'abort', () => {
+				reject();
+			} );
+
+			this.loader.file.then( file => {
+				reader.readAsDataURL( file );
+			} );
+		} );
+	}
+
+	/**
+	 * Aborts the upload process.
+	 *
+	 * @see module:upload/filerepository~UploadAdapter#abort
+	 * @returns {Promise}
+	 */
+	abort() {
+		this.reader.abort();
+	}
+}

+ 164 - 0
packages/ckeditor5-upload/tests/base64uploadadapter.js

@@ -0,0 +1,164 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals window, setTimeout */
+
+import Base64UploadAdapter from '../src/base64uploadadapter';
+import FileRepository from '../src/filerepository';
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import { createNativeFileMock } from './_utils/mocks';
+
+describe( 'Base64UploadAdapter', () => {
+	let div, stubs;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		div = window.document.createElement( 'div' );
+		window.document.body.appendChild( div );
+
+		stubs = {
+			addEventListener( event, callback ) {
+				stubs[ `on${ event }` ] = callback;
+			},
+			readAsDataURL: testUtils.sinon.spy(),
+			abort: testUtils.sinon.spy(),
+			result: 'data:image/png;base64'
+		};
+
+		testUtils.sinon.stub( window, 'FileReader' ).callsFake( function FileReader() {
+			return stubs;
+		} );
+	} );
+
+	afterEach( () => {
+		window.document.body.removeChild( div );
+	} );
+
+	it( 'should require the FileRepository plugin', () => {
+		expect( Base64UploadAdapter.requires ).to.deep.equal( [ FileRepository ] );
+	} );
+
+	it( 'should be named', () => {
+		expect( Base64UploadAdapter.pluginName ).to.equal( 'Base64UploadAdapter' );
+	} );
+
+	describe( 'init()', () => {
+		it( 'should set the loader', () => {
+			return ClassicTestEditor
+				.create( div, {
+					plugins: [ Base64UploadAdapter ],
+				} )
+				.then( editor => {
+					expect( editor.plugins.get( FileRepository ).createUploadAdapter ).is.a( 'function' );
+
+					return editor.destroy();
+				} );
+		} );
+	} );
+
+	describe( 'Adapter', () => {
+		let editor, fileRepository, adapter;
+
+		beforeEach( () => {
+			return ClassicTestEditor.create( div, {
+				plugins: [ Base64UploadAdapter ],
+			} ).then( _editor => {
+				editor = _editor;
+				fileRepository = editor.plugins.get( FileRepository );
+				adapter = fileRepository.createLoader( createNativeFileMock() );
+			} );
+		} );
+
+		afterEach( () => {
+			return editor.destroy();
+		} );
+
+		it( 'crateAdapter method should be registered and have upload() and abort() methods', () => {
+			expect( adapter ).to.not.be.undefined;
+			expect( adapter.upload ).to.be.a( 'function' );
+			expect( adapter.abort ).to.be.a( 'function' );
+		} );
+
+		describe( 'upload()', () => {
+			it( 'returns a promise that resolves an image as a base64 string', () => {
+				setTimeout( () => {
+					// FileReader has loaded the file.
+					stubs.onload();
+				} );
+
+				return adapter.upload()
+					.then( response => {
+						expect( response.default ).to.equal( 'data:image/png;base64' );
+						expect( stubs.readAsDataURL.calledOnce ).to.equal( true );
+					} );
+			} );
+
+			it( 'returns a promise that rejects if something went wrong', () => {
+				const uploadError = new Error( 'Something went wrong.' );
+
+				setTimeout( () => {
+					// An error occurred while FileReader was reading the file.
+					stubs.onerror( uploadError );
+				} );
+
+				return adapter.upload()
+					.then(
+						() => {
+							return new Error( 'Supposed to be rejected.' );
+						},
+						err => {
+							expect( err ).to.equal( uploadError );
+							expect( stubs.readAsDataURL.calledOnce ).to.equal( true );
+						}
+					);
+			} );
+
+			it( 'returns a promise that rejects if FileReader aborted reading a file', () => {
+				setTimeout( () => {
+					// FileReader aborted reading the file.
+					stubs.onabort();
+				} );
+
+				return adapter.upload()
+					.then(
+						() => {
+							return new Error( 'Supposed to be rejected.' );
+						},
+						() => {
+							expect( stubs.readAsDataURL.calledOnce ).to.equal( true );
+						}
+					);
+			} );
+		} );
+
+		describe( 'abort()', () => {
+			it( 'should not call abort() on the non-existing FileReader uploader (loader#file not resolved)', () => {
+				const adapter = fileRepository.createLoader( createNativeFileMock() );
+
+				expect( () => {
+					adapter.upload();
+					adapter.abort();
+				} ).to.not.throw();
+
+				expect( stubs.abort.called ).to.equal( false );
+			} );
+
+			it( 'should call abort() on the FileReader uploader (loader#file resolved)', done => {
+				adapter.upload();
+
+				// Wait for the `loader.file` promise.
+				setTimeout( () => {
+					adapter.abort();
+
+					expect( stubs.abort.called ).to.equal( true );
+
+					done();
+				} );
+			} );
+		} );
+	} );
+} );