From 4598091ccf21db421664ab72b7ae5d9b1d2ff713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Tue, 27 Aug 2019 19:58:14 +0200 Subject: [PATCH 1/7] Add configuration option for image file mime-types. --- src/imageupload/imageuploadediting.js | 24 +++++++++++++++++-- src/imageupload/imageuploadui.js | 8 ++++--- src/imageupload/utils.js | 17 +++++++++----- tests/imageupload/utils.js | 33 +++++++++++++++------------ 4 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/imageupload/imageuploadediting.js b/src/imageupload/imageuploadediting.js index b7122ed3..813db468 100644 --- a/src/imageupload/imageuploadediting.js +++ b/src/imageupload/imageuploadediting.js @@ -14,7 +14,8 @@ import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter'; import env from '@ckeditor/ckeditor5-utils/src/env'; import ImageUploadCommand from '../../src/imageupload/imageuploadcommand'; -import { fetchLocalImage, isImageType, isLocalImage } from '../../src/imageupload/utils'; +import { fetchLocalImage, isLocalImage } from '../../src/imageupload/utils'; +import { createImageTypeRegExp } from './utils'; /** * The editing part of the image upload feature. It registers the `'imageUpload'` command. @@ -29,6 +30,23 @@ export default class ImageUploadEditing extends Plugin { return [ FileRepository, Notification ]; } + static get pluginName() { + return 'ImageUploadEditing'; + } + + /** + * @inheritDoc + */ + constructor( editor ) { + super( editor ); + + editor.config.define( 'image', { + upload: { + types: [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ] + } + } ); + } + /** * @inheritDoc */ @@ -39,6 +57,8 @@ export default class ImageUploadEditing extends Plugin { const conversion = editor.conversion; const fileRepository = editor.plugins.get( FileRepository ); + const imageTypes = createImageTypeRegExp( editor.config.get( 'image.upload.types' ) ); + // Setup schema to allow uploadId and uploadStatus for images. schema.extend( 'image', { allowAttributes: [ 'uploadId', 'uploadStatus' ] @@ -74,7 +94,7 @@ export default class ImageUploadEditing extends Plugin { return false; } - return isImageType( file ); + return imageTypes.test( file.type ); } ); const ranges = data.targetRanges.map( viewRange => editor.editing.mapper.toModelRange( viewRange ) ); diff --git a/src/imageupload/imageuploadui.js b/src/imageupload/imageuploadui.js index ff8e0055..c0fdaba6 100644 --- a/src/imageupload/imageuploadui.js +++ b/src/imageupload/imageuploadui.js @@ -10,7 +10,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview'; import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg'; -import { isImageType } from './utils'; +import { createImageTypeRegExp } from './utils'; /** * The image upload button plugin. @@ -33,9 +33,11 @@ export default class ImageUploadUI extends Plugin { editor.ui.componentFactory.add( 'imageUpload', locale => { const view = new FileDialogButtonView( locale ); const command = editor.commands.get( 'imageUpload' ); + const imageTypes = editor.config.get( 'image.upload.types' ); + const imageTypesRegExp = createImageTypeRegExp( imageTypes ); view.set( { - acceptedType: 'image/*', + acceptedType: imageTypes.map( type => `image/${ type }` ).join( ',' ), allowMultipleFiles: true } ); @@ -48,7 +50,7 @@ export default class ImageUploadUI extends Plugin { view.buttonView.bind( 'isEnabled' ).to( command ); view.on( 'done', ( evt, files ) => { - const imagesToUpload = Array.from( files ).filter( isImageType ); + const imagesToUpload = Array.from( files ).filter( file => imageTypesRegExp.test( file.type ) ); if ( imagesToUpload.length ) { editor.execute( 'imageUpload', { file: imagesToUpload } ); diff --git a/src/imageupload/utils.js b/src/imageupload/utils.js index 7bc21a0f..8bb65268 100644 --- a/src/imageupload/utils.js +++ b/src/imageupload/utils.js @@ -10,15 +10,20 @@ /* global fetch, File */ /** - * Checks if a given file is an image. + * Creates a RegExp used to test for image files. * - * @param {File} file - * @returns {Boolean} + * const imageType = createImageTypeRegExp( [ 'png', 'jpeg', 'svg+xml', 'vnd.microsoft.icon' ] ); + * + * console.log( 'is supported image', imageType.test( file.type ) ); + * + * @param {Array.} types + * @returns {RegExp} */ -export function isImageType( file ) { - const types = /^image\/(jpeg|png|gif|bmp)$/; +export function createImageTypeRegExp( types ) { + // Sanitize mime-type name which may include: "+", "-" or ".". + const regExpSafeNames = types.map( type => type.replace( '+', '\\+' ) ); - return types.test( file.type ); + return new RegExp( `^image\\/(${ regExpSafeNames.join( '|' ) })$` ); } /** diff --git a/tests/imageupload/utils.js b/tests/imageupload/utils.js index 15d69662..2a1839ab 100644 --- a/tests/imageupload/utils.js +++ b/tests/imageupload/utils.js @@ -3,29 +3,32 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -import { isImageType } from '../../src/imageupload/utils'; +import { createImageTypeRegExp } from '../../src/imageupload/utils'; describe( 'upload utils', () => { - describe( 'isImageType()', () => { - it( 'should return true for png mime type', () => { - expect( isImageType( { type: 'image/png' } ) ).to.be.true; + describe( 'createImageTypeRegExp()', () => { + it( 'should return RegExp for testing regular mime type', () => { + expect( createImageTypeRegExp( [ 'png' ] ).test( 'image/png' ) ).to.be.true; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'foo/png' ) ).to.be.false; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'png' ) ).to.be.false; } ); - it( 'should return true for jpeg mime type', () => { - expect( isImageType( { type: 'image/jpeg' } ) ).to.be.true; + it( 'should return RegExp for testing mime type with dot', () => { + expect( createImageTypeRegExp( [ 'vnd.microsoft.icon' ] ).test( 'image/vnd.microsoft.icon' ) ).to.be.true; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'foo/vnd.microsoft.icon' ) ).to.be.false; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'vnd.microsoft.icon' ) ).to.be.false; } ); - it( 'should return true for gif mime type', () => { - expect( isImageType( { type: 'image/gif' } ) ).to.be.true; + it( 'should return RegExp for testing mime type with dash', () => { + expect( createImageTypeRegExp( [ 'x-xbitmap' ] ).test( 'image/x-xbitmap' ) ).to.be.true; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'foo/x-xbitmap' ) ).to.be.false; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'x-xbitmap' ) ).to.be.false; } ); - it( 'should return true for bmp mime type', () => { - expect( isImageType( { type: 'image/bmp' } ) ).to.be.true; - } ); - - it( 'should return false for other mime types', () => { - expect( isImageType( { type: 'audio/mp3' } ) ).to.be.false; - expect( isImageType( { type: 'video/mpeg' } ) ).to.be.false; + it( 'should return RegExp for testing mime type with plus', () => { + expect( createImageTypeRegExp( [ 'svg+xml' ] ).test( 'image/svg+xml' ) ).to.be.true; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'foo/svg+xml' ) ).to.be.false; + expect( createImageTypeRegExp( [ 'png' ] ).test( 'svg+xml' ) ).to.be.false; } ); } ); } ); From 74259f41eba6598aa03e53e1f6101f4e7df32457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 28 Aug 2019 09:48:39 +0200 Subject: [PATCH 2/7] Fix typo in image.resizeUnit configuration option code example. --- src/imageresize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imageresize.js b/src/imageresize.js index 59a1ee99..1e5371f0 100644 --- a/src/imageresize.js +++ b/src/imageresize.js @@ -150,7 +150,7 @@ export default class ImageResize extends Plugin { * ClassicEditor * .create( editorElement, { * image: { - * resizeUnit: 'px' + * resizeUnit: 'px' * } * } ) * .then( ... ) From 48f42227825edaada343ecae72eae64868bb7066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 28 Aug 2019 09:58:29 +0200 Subject: [PATCH 3/7] Add test for image upload button accepted mime-types. --- tests/imageupload/imageuploadui.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/imageupload/imageuploadui.js b/tests/imageupload/imageuploadui.js index fbc25f13..e82d8a01 100644 --- a/tests/imageupload/imageuploadui.js +++ b/tests/imageupload/imageuploadui.js @@ -62,6 +62,14 @@ describe( 'ImageUploadUI', () => { expect( button ).to.be.instanceOf( FileDialogButtonView ); } ); + it( 'should set proper accepted mime-types for imageUpload button as defined in configuration', () => { + editor.config.set( 'image.upload.types', [ 'svg+xml', 'jpeg', 'vnd.microsoft.icon', 'x-xbitmap' ] ); + + const button = editor.ui.componentFactory.create( 'imageUpload' ); + + expect( button.acceptedType ).to.equal( 'image/svg+xml,image/jpeg,image/vnd.microsoft.icon,image/x-xbitmap' ); + } ); + it( 'should be disabled while ImageUploadCommand is disabled', () => { const button = editor.ui.componentFactory.create( 'imageUpload' ); const command = editor.commands.get( 'imageUpload' ); From 0309e9fe48a384e9a2929f69a11e957fe730f29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 28 Aug 2019 10:28:48 +0200 Subject: [PATCH 4/7] Add test for inserting an image of a not configured type. --- tests/imageupload/imageuploadediting.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/imageupload/imageuploadediting.js b/tests/imageupload/imageuploadediting.js index 1662ccce..95d03218 100644 --- a/tests/imageupload/imageuploadediting.js +++ b/tests/imageupload/imageuploadediting.js @@ -226,6 +226,30 @@ describe( 'ImageUploadEditing', () => { expect( eventInfo.stop.called ).to.be.undefined; } ); + it( 'should not insert image when file is not an configured image type', () => { + const viewDocument = editor.editing.view.document; + const fileMock = { + type: 'image/svg+xml', + size: 1024 + }; + const dataTransfer = new DataTransfer( { + files: [ fileMock ], + types: [ 'Files' ], + getData: () => '' + } ); + + setModelData( model, 'foo[]' ); + + const targetRange = doc.selection.getFirstRange(); + const targetViewRange = editor.editing.mapper.toViewRange( targetRange ); + + const eventInfo = new EventInfo( viewDocument, 'clipboardInput' ); + viewDocument.fire( eventInfo, { dataTransfer, targetRanges: [ targetViewRange ] } ); + + expect( getModelData( model ) ).to.equal( 'foo[]' ); + expect( eventInfo.stop.called ).to.be.undefined; + } ); + it( 'should not insert image when file is null', () => { const viewDocument = editor.editing.view.document; const dataTransfer = new DataTransfer( { files: [ null ], types: [ 'Files' ], getData: () => null } ); From 7dde008f35ce727b8328b475679ef7ae26a89d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 28 Aug 2019 10:29:32 +0200 Subject: [PATCH 5/7] Add ImageUploadConfig documentation. --- src/imageupload.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/imageupload.js b/src/imageupload.js index 20cecffd..a7ea3037 100644 --- a/src/imageupload.js +++ b/src/imageupload.js @@ -40,3 +40,46 @@ export default class ImageUpload extends Plugin { return [ ImageUploadEditing, ImageUploadUI, ImageUploadProgress ]; } } + +/** + * Image upload configuration. + * + * @member {module:image/imageupload~ImageUploadConfig} module:image/image~ImageConfig#upload + */ + +/** + * The configuration of the image upload feature. Used by the image upload feature in the `@ckeditor/ckeditor5-image` package. + * + * ClassicEditor + * .create( editorElement, { + * image: { + * upload: ... // Image upload feature options. + * } + * } ) + * .then( ... ) + * .catch( ... ); + * + * See {@link module:core/editor/editorconfig~EditorConfig all editor options}. + * + * @interface module:image/imageupload~ImageUploadConfig + */ + +/** + * List of accepted image types. + * + * The accepted types of images can be customize to allow only certain types of images: + * + * // Allow only JPEG and PNG images: + * const imageUploadConfig = { + * types: [ 'png', 'jpeg' ] + * }; + * + * The type string should match [one of the sub-types](https://www.iana.org/assignments/media-types/media-types.xhtml#image) + * of the image mime-type. E.g. for the `image/jpeg` mime-type use `jpeg`. + * + * **Note:** This setting only restricts some image types to be selected and uploaded through the CKEditor UI and commands. Image type + * recognition and filtering should be also implemented on the server that accepts images uploads. + * + * @member {Array.} module:image/imageupload~ImageUploadConfig#types + * @default [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ] + */ From 1f76ea936c5eb9b05b937d1142c7b4b776d7167b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 28 Aug 2019 10:31:14 +0200 Subject: [PATCH 6/7] Update image upload types configuration description. --- src/imageupload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imageupload.js b/src/imageupload.js index a7ea3037..12d5ba81 100644 --- a/src/imageupload.js +++ b/src/imageupload.js @@ -75,7 +75,7 @@ export default class ImageUpload extends Plugin { * }; * * The type string should match [one of the sub-types](https://www.iana.org/assignments/media-types/media-types.xhtml#image) - * of the image mime-type. E.g. for the `image/jpeg` mime-type use `jpeg`. + * of the image mime-type. E.g. for the `image/jpeg` mime-type add `'jpeg'` to types array. * * **Note:** This setting only restricts some image types to be selected and uploaded through the CKEditor UI and commands. Image type * recognition and filtering should be also implemented on the server that accepts images uploads. From d7fe0b119948b2b66eadceda94636bc61d2ffd11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Mon, 2 Sep 2019 13:04:13 +0200 Subject: [PATCH 7/7] Wording. --- src/imageupload.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imageupload.js b/src/imageupload.js index 12d5ba81..f750ff4a 100644 --- a/src/imageupload.js +++ b/src/imageupload.js @@ -75,10 +75,10 @@ export default class ImageUpload extends Plugin { * }; * * The type string should match [one of the sub-types](https://www.iana.org/assignments/media-types/media-types.xhtml#image) - * of the image mime-type. E.g. for the `image/jpeg` mime-type add `'jpeg'` to types array. + * of the image mime-type. E.g. for the `image/jpeg` mime-type add `'jpeg'`. * * **Note:** This setting only restricts some image types to be selected and uploaded through the CKEditor UI and commands. Image type - * recognition and filtering should be also implemented on the server that accepts images uploads. + * recognition and filtering should be also implemented on the server which accepts image uploads * * @member {Array.} module:image/imageupload~ImageUploadConfig#types * @default [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]