浏览代码

Initial rough implementation.

Szymon Kupś 8 年之前
父节点
当前提交
32b10fe0e9

+ 15 - 2
packages/ckeditor5-adapter-ckfinder/package.json

@@ -3,9 +3,22 @@
   "version": "0.0.1",
   "description": "CKFinder adapter for CKEditor 5.",
   "keywords": [],
-  "dependencies": {},
+  "dependencies": {
+	  "@ckeditor/ckeditor5-core": "^0.8.0"
+  },
   "devDependencies": {
-  	"@ckeditor/ckeditor5-dev-lint": "^2.0.2",
+    "@ckeditor/ckeditor5-basic-styles": "^0.8.0",
+    "@ckeditor/ckeditor5-clipboard": "^0.5.0",
+    "@ckeditor/ckeditor5-dev-lint": "^2.0.2",
+    "@ckeditor/ckeditor5-editor-classic": "^0.7.2",
+    "@ckeditor/ckeditor5-enter": "^0.9.0",
+    "@ckeditor/ckeditor5-heading": "^0.9.0",
+    "@ckeditor/ckeditor5-image": "^0.5.0",
+    "@ckeditor/ckeditor5-list": "^0.6.0",
+    "@ckeditor/ckeditor5-paragraph": "^0.7.0",
+    "@ckeditor/ckeditor5-typing": "^0.9.0",
+    "@ckeditor/ckeditor5-undo": "^0.8.0",
+    "@ckeditor/ckeditor5-upload": "^0.0.1",
   	"gulp": "^3.9.1",
   	"guppy-pre-commit": "^0.4.0"
   },

+ 147 - 0
packages/ckeditor5-adapter-ckfinder/src/ckfinderadapter.js

@@ -0,0 +1,147 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals window, document, XMLHttpRequest, FormData */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
+
+// TODO: load it from config.
+const TEMPORARY_UPLOAD_URL =
+	'https://cksource.com/weuy2g4ryt278ywiue/core/connector/php/connector.php?command=QuickUpload&type=Files&responseType=json';
+export default class CKFinderAdapter extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ FileRepository ];
+	}
+
+	init() {
+		// Register CKFinderAdapter
+		this.editor.plugins.get( FileRepository ).createAdapter = loader => new Adapter( loader, this.editor.t );
+	}
+}
+
+class Adapter {
+	constructor( loader, t ) {
+		// Save Loader instance to update upload progress.
+		this.loader = loader;
+
+		this.t = t;
+	}
+
+	upload() {
+		const loader = this.loader;
+		const t = this.t;
+
+		return new Promise( ( resolve, reject ) => {
+			const xhr = this.xhr = new XMLHttpRequest();
+
+			// Prepare request.
+			xhr.open( 'POST', TEMPORARY_UPLOAD_URL, true );
+			xhr.responseType = 'json';
+
+			// Prepare listeners.
+			// TODO: Move strings to language files.
+			const genericError = t( 'Cannot upload file:' ) + loader.file.name + '.';
+			xhr.addEventListener( 'error', () => reject( genericError ) );
+			xhr.addEventListener( 'abort', () => reject() );
+			xhr.addEventListener( 'load', () => {
+				const response = xhr.response;
+
+				if ( !response.uploaded ) {
+					return reject( response.error && response.error.message ? response.error.message : genericError );
+				}
+
+				// Resolve promise with data from server.
+				resolve( {
+					original: response.url
+				} );
+			} );
+
+			// Upload progress when it's supported.
+			if ( xhr.upload ) {
+				xhr.upload.addEventListener( 'progress', ( evt ) => {
+					if ( evt.lengthComputable ) {
+						loader.uploadTotal = evt.total;
+						loader.uploaded = evt.loaded;
+					}
+				} );
+			}
+
+			// Prepare form data.
+			const data = new FormData();
+			data.append( 'upload', loader.file );
+			data.append( 'ckCsrfToken', getCsrfToken() );
+
+			// Send request.
+			xhr.send( data );
+		} );
+	}
+
+	abort() {
+		if ( this.xhr ) {
+			this.xhr.abort();
+		}
+	}
+}
+
+const TOKEN_COOKIE_NAME = 'ckCsrfToken';
+const TOKEN_LENGTH = 40;
+const tokenCharset = 'abcdefghijklmnopqrstuvwxyz0123456789';
+
+function getCsrfToken() {
+	let token = getCookie( TOKEN_COOKIE_NAME );
+
+	if ( !token || token.length != TOKEN_LENGTH ) {
+		token = generateToken( TOKEN_LENGTH );
+		setCookie( TOKEN_COOKIE_NAME, token );
+	}
+
+	return token;
+}
+
+function getCookie( name ) {
+	name = name.toLowerCase();
+	const parts = document.cookie.split( ';' );
+	let pair, key;
+
+	for ( let i = 0; i < parts.length; i++ ) {
+		pair = parts[ i ].split( '=' );
+		key = decodeURIComponent( pair[ 0 ].trim().toLowerCase() );
+
+		if ( key === name ) {
+			return decodeURIComponent( pair.length > 1 ? pair[ 1 ] : '' );
+		}
+	}
+
+	return null;
+}
+
+function setCookie( name, value ) {
+	document.cookie = encodeURIComponent( name ) + '=' + encodeURIComponent( value ) + ';path=/';
+}
+
+function generateToken( length ) {
+	let randValues = [];
+	let result = '';
+
+	if ( window.crypto && window.crypto.getRandomValues ) {
+		randValues = new Uint8Array( length );
+		window.crypto.getRandomValues( randValues );
+	} else {
+		for ( let i = 0; i < length; i++ ) {
+			randValues.push( Math.floor( Math.random() * 256 ) );
+		}
+	}
+
+	for ( let j = 0; j < randValues.length; j++ ) {
+		let character = tokenCharset.charAt( randValues[ j ] % tokenCharset.length );
+		result += Math.random() > 0.5 ? character.toUpperCase() : character;
+	}
+
+	return result;
+}

+ 17 - 0
packages/ckeditor5-adapter-ckfinder/tests/manual/imageupload.html

@@ -0,0 +1,17 @@
+<div id="editor">
+	<h2>Image upload</h2>
+	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.</p>
+</div>
+
+<div id="button-container"></div>
+
+<style>
+	#button-container div {
+		margin-top: 10px;
+	}
+
+	#button-container button {
+		margin-right: 10px;
+	}
+</style>
+

+ 35 - 0
packages/ckeditor5-adapter-ckfinder/tests/manual/imageupload.js

@@ -0,0 +1,35 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document, console */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
+import Undo from '@ckeditor/ckeditor5-undo/src/undo';
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
+import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar';
+import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import List from '@ckeditor/ckeditor5-list/src/list';
+import ImageUpload from '@ckeditor/ckeditor5-upload/src/imageupload';
+import ImageUploadProgress from '@ckeditor/ckeditor5-upload/src/imageuploadprogress';
+import CKFinderAdapter from '../../src/ckfinderadapter';
+
+ClassicEditor.create( document.querySelector( '#editor' ), {
+	plugins: [
+		Enter, Typing, Paragraph, Heading, Undo, Bold, Italic, Heading, List, Image, ImageToolbar, Clipboard,
+		ImageCaption, ImageStyle, ImageUpload, CKFinderAdapter, ImageUploadProgress
+	],
+	toolbar: [ 'headings', 'undo', 'redo', 'bold', 'italic', 'bulletedList', 'numberedList', 'insertImage' ]
+} )
+.catch( err => {
+	console.error( err.stack );
+} );

+ 10 - 0
packages/ckeditor5-adapter-ckfinder/tests/manual/imageupload.md

@@ -0,0 +1,10 @@
+## Image upload
+
+1. Drop an image into editor.
+1. Image should be read and displayed.
+1. Press "Upload progress" button couple times to simulate upload process.
+1. After uploading is complete your image should be replaced with sample image from server.
+
+Repeat all the steps with:
+* dropping multiple images,
+* using toolbar button to add one and multiple images.

二进制
packages/ckeditor5-adapter-ckfinder/tests/manual/sample.jpg