Sfoglia il codice sorgente

Merge pull request #323 from ckeditor/t/ckeditor5/674

Feature: Introduced `config.image.upload.types` configuration option for setting allowed image mime-types. Closes #295. Closes ckeditor/ckeditor5#674.

BREAKING CHANGE: Removed `isImageType()` util.
Piotrek Koszuliński 6 anni fa
parent
commit
5b55936109

+ 1 - 1
packages/ckeditor5-image/src/imageresize.js

@@ -150,7 +150,7 @@ export default class ImageResize extends Plugin {
  *		ClassicEditor
  *			.create( editorElement, {
  *				image: {
- *					 resizeUnit: 'px'
+ *					resizeUnit: 'px'
  *				}
  *			} )
  *			.then( ... )

+ 43 - 0
packages/ckeditor5-image/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 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 which accepts image uploads
+ *
+ * @member {Array.<String>} module:image/imageupload~ImageUploadConfig#types
+ * @default [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]
+ */

+ 22 - 2
packages/ckeditor5-image/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 ) );

+ 5 - 3
packages/ckeditor5-image/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 } );

+ 11 - 6
packages/ckeditor5-image/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.<String>} 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( '|' ) })$` );
 }
 
 /**

+ 24 - 0
packages/ckeditor5-image/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, '<paragraph>foo[]</paragraph>' );
+
+		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( '<paragraph>foo[]</paragraph>' );
+		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 } );

+ 8 - 0
packages/ckeditor5-image/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' );

+ 18 - 15
packages/ckeditor5-image/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;
 		} );
 	} );
 } );