Simple Uploads Plugin for CKEditor

Introduction

This is a plugin that provides a rich set of features to upload files from your local computer to the server in CKEditor. The goal is to make it work as simple as possible and at the same time provide lots of functionality and flexibility to the developers.

Author:

Alfonso Martínez de Lizarrondo

Installation

1. Copying the files

Extract the contents of the zip in you plugins directory, so it ends up like this

ckeditor\
	...
	images\
	lang\
	plugins\
		...
		simpleuploads\
			plugin.js
			docs\
				Install.html
			icons\
			lang\
		...
	skins\
	themes\

2. Adding it to CKEditor

Now add the plugin in your config.js or custom js configuration file:

config.extraPlugins='simpleuploads'; 
If you are already using other additional plugins, then you must have a single extraPlugins statements with all the plugins separated by commas:
config.extraPlugins='confighelper,simpleuploads'; 
If you have used previously the ImagePaste plugin to test if this one will work, then remember to remove it from the extraPlugins entry.

3. Configuration

Please, refer to the SimpleUploads page in my blog as well as related posts to find further info, examples and updates

You can find all the configuration options as well as events, etc... documented at the end of the plugin.js in JSDoc format

You have to configure the filebrowserImageUploadUrl and filebrowserUploadUrl entries as you might have already done to allow the user to upload files and images. You can use this basic sample if you want to use some existing code.

The goal is to send a response like this (using the proper parameters for $funcNum and $url):

<script type='text/javascript'> window.parent.CKEDITOR.tools.callFunction($funcNum, '$url', '$message')</script>

Since 4.2.5 it's possible to embed the files as base64 data: URLs if the filebrowserUploadUrl is set to "base64". You must be very careful about using this option: CKEditor can choke on the content if the user tries to add big files, data processing will be slower, you should extract the embedded files when the user saves the data because otherwise the visitors to the page will have a worse experience due to the lack of caching and parallel download of resources.

If you're using Liferay, check these detailed instructions

Since 2.7 it's possible to specify the shadows (or whatever CSS effects that you prefer) to use while a file is dragged over the page and an editor.
The plugin detects those situations and adds/removes the "SimpleUploadsOverEditor" and "SimpleUploadsOverContainer" classes.
Here "container" refers to the whole CKEditor including toolbar and whatever is part of the UI. This is always an element in the main page, so you can override it also in the page where you're using CKEditor (keep in mind that the plugin might add the rule defined in the configuration after your styles, so check the priority of the CSS rules if you can't get it to work that way.
This is the default value:

CKEDITOR.config.simpleuploads_containerover = 'box-shadow: 0 0 10px 1px #99DD99 !important;'
Important: the config is picked from CKEDITOR.config, this is a setting shared by all the editor instances. If you want to specify different settings for each editor container you must do it in your page.
ex:
#cke_editor1.SimpleUploadsOverContainer {
	box-shadow: 0 0 10px 1px #99FF99;
}
#cke_editor2.SimpleUploadsOverContainer {
	border:2px solid black; // be careful, styling the container can be tricky to get a good efect
}
The second class is used when the user drags over an editor, in this case the behavior is different between CKEditor 3 and 4. In CKEditor 3 we're always using an iframe so this setting can be specified for each instance, but in CKEditor 4 due to the introduction of inline editing they've changed the way that addCss works so in a similar way to the previous setting this value is the same for all the instances, but you can easily override it in the styles of your page.
Default value:
CKEDITOR.config.simpleuploads_editorover = 'box-shadow: 0 0 10px 1px #999999 inset !important;'
You can add different configuration in the config.contentsCss file, or in v4 inline mode in the page itself:
// for v4 inline
#cke_editor1.SimpleUploadsOverEditor {
	box-shadow: 0 0 10px 1px #99FF99;
}
#cke_editor2.SimpleUploadsOverEditor {
	background-color: green;
}

Since 3.0 there are two buttons that can be used to provide a single click to launch the file picker and add a file or image to the document.
The name of such buttons are "addFile" and "addImage" if you want to add them to your toolbar.
If you use CKEditor 4 they will be added automatically to the "insert" toolbar, if you don't want one or either of them, you can specify those names in a "removeButtons" entry in your config file:

config.removeButtons = "addFile"; // Removes the "add files" button and leaves the one to add images.

These buttons are available for all the browsers, including IE8 and IE9 but in this case it's using a trick to allow the single click browse and upload so it might fail in some environments (I guess so, but I have no such data). I might try to fix such problems if you report back to me, but you must realize that I'm already forzing IE to work in an unexpected way.
Anyway, as usual with my plugins, IE6 and IE7 are not supported, nor pages in Quirks mode. It might work, but if it fails I can't try to fix things when the solution is to upgrade to a current browser or make your pages compliant with the standards.

Since 4.1 you can opt to always use the plain text upload instead of the image preview with the grayscale by setting the global option simpleuploads_hideImageProgress to true

CKEDITOR.config.simpleuploads_hideImageProgress = true

In 4.2 there are two new properties:

CKEDITOR.config.simpleuploads_convertBmp = true
Turns selected bmp images to png before they are uploaded to the server (you must add the "bmp" to the list of allowed extensions)
The simpleuploads_maximumDimensions option allows to define an object that specifies the maximum dimensions in pixels for uploaded images
CKEDITOR.config.simpleuploads_maximumDimensions = {width:500, height:400}

4. Events

The plugin generates three custom events so you can show a notification like in the demo page while a file is being uploaded.
These are the signatures of the methods:

 /**
 * Fired when file starts being uploaded by the "simpleuploads" plugin
 * @name CKEDITOR.editor#simpleuploads.startUpload
 * @event
 * @param {String} [name] The file name.
 * @param {String} [url] The url that will be used for the upload. It can be modified to your needs on each upload.
 * @param {String|Object} [context] Context that caused the upload (a string if it's a pasted image, a DOM event for drag&drop and copied files, the toolbar button for those cases)
 * @param {Object} [file] The file itself (if available).
  */

/**
 * Fired when the server sends the response of an upload.
 *
 * @since 4.3.6
 * @name CKEDITOR.editor#simpleuploads.serverResponse
 * @event
 * @param {Object} [xhr] The XHR with the request.
 * @param {Object} [data] The original data object of this upload.
 * Upon processing this event, a listener can set a "url" property on the event.data object and that will tell to the SimpleUploads plugin
 * that your code has processed the response.
 * If url is an empty string it means that the upload has failed and that the upload placeholder must be removed silently
 * Otherwise it will be treated as the response from the server
 * This way you can use different responses from your server that doesn't follow the QuickUpload pattern, as well as hook any additional processing that
 * you might need.
 * Please, note that this is fired only for uploads using XHR, old IEs are excluded and they need the default response from the server.
 */

/**
 * Fired when file upload finishes on the "simpleuploads" plugin
 * @name CKEDITOR.editor#simpleuploads.endUpload
 * @event
 * @param {String} [name] The file name.
 * @param {Boolean} [ok] Whether the file has been correctly uploaded or not
 */

/**
 * Fired when the final element has been inserted by the "simpleuploads" plugin (after it has been uploaded)
 * @name CKEDITOR.editor#simpleuploads.finishedUpload
 * @event
 * @param {String} [name] The file name.
 * @param {CKEDITOR.dom.element} [element] The element node that has been inserted
 */
 

Since 3.1 it's possible to use the startUpload event to cancel the upload or change the URL where the file will be uploaded

// Let's add this to every editor on the page. You can instead add it only to a specific editor.
CKEDITOR.on('instanceReady', function(e) {
	// the real listener
	e.editor.on( 'simpleuploads.startUpload' , function(ev) {

		var name = ev.data.name;
		if ( (/\.rar$/i).test( name ) )
		{
			// Silly example
			alert("Rar files are outdated, please use 7-Zip");
			ev.cancel();
		}
	});
});

Since 4.0 it's possible to add an "extraFields" property to the event data to specify additional fields that should be send in the upload (for example anti-CSRF tokens or extra parameters in the dialog)

// Let's add this to every editor on the page. You can instead add it only to a specific editor.
CKEDITOR.on('instanceReady', function(e) {
	// the real listener
	e.editor.on( 'simpleuploads.startUpload' , function(ev) {

		// add CSRF tokens
		var extraFields = {
			// These is sample code, the CKEDITOR.config.action, CKEDITOR.config.formID and Core objects
			// are items specific to a custom implementation of CKEditor.
			// Fill the extraFields object with whatever you might need.
			"Action" : CKEDITOR.config.action,
			"FormID" : CKEDITOR.config.formID,
			"CKEditorFuncNum" : "2"
		};
		extraFields[ Core.Config.Get("SessionName") ] = Core.Config.Get("SessionID");

		ev.data.extraFields = extraFields;
	});
});

Since 4.3.0 there's a new optional "extraHeaders" property to add custom http headers to the XHR request

The finishedUpload event introduced in 3.3.4 allows to modify the final element, for example removing the dimension attributes and adding our own class:

// Let's add this to every editor on the page. You can instead add it only to a specific editor.
CKEDITOR.on('instanceReady', function(e) {
	// the real listener
	e.editor.on( 'simpleuploads.finishedUpload' , function(ev) {

		var element = ev.data.element;
		// remove dimension attributes added in v 3.3.2
		element.removeAttribute("width");
		element.removeAttribute("height");

		// add our custom class
		element.addClass("picture");

	});
});

Version 4.2 adds a "simpleuploads.localImageReady" event that takes care of loading selected images into an Image element so it's possible for example to check its dimensions, convert between formats or modify it.
This event is used to implement the new simpleuploads_convertBmp and simpleuploads_maximumDimensions options (you can check that code to better understand how to use it. I might refine it in a future version and I'll provide samples in my blog according to the requests)
This event (and the new options) aren't available for IE 8 and 9

5. Cross domain file uploads

In version 3.4 I've added support to upload files across domains.
This is based on the CORS spec. At the moment this won't work with IE < 10: IE8 doesn't support XHR uploads and IE9 requires the use of the custom XDomainRequest that also seems to be restricted about what it can do; I don't plan to work on those older versions of IE.

If you don't use cross domain (one server for the uploaded files and another for the editing page) then this change shouldn't affect you. Otherwise you can enable this feature by modifying your uploader to send just two headers in response to an OPTIONS request: Some simple PHP code:

if (isset($_SERVER["HTTP_ORIGIN"])) {
	$origin = $_SERVER["HTTP_ORIGIN"];
	// Warning: this is not secure, it allows usage of the page from any domain.
	// You must verify that the $origin domain is on your white-list
	header('Access-Control-Allow-Origin: ' . $origin);
	header('Access-Control-Allow-Credentials: true');
}
if ($_SERVER['REQUEST_METHOD']=='OPTIONS')
{
	exit(0);
}

First: check if the browser has sent an Origin header. That means that it's a cross domain request. You can check that domain with the list of domains that you want to allow, this sample code just repeats back the header so it allows any domain to call this page.
Second: Send an Access-Control-Allow-Credentials header specifying that the browser is allowed to make a request that will use the credentials of the user at this domain. This means that if the user is logged in, the browser will send the cookies required to allow you check his/her identity.
Last: as the OPTIONS request doesn't require more data in the response you can stop any further processing here. After this first request the browser will upload the file and you must send back the two Access-Control headers as shown in the sample code.

You must be careful if you want to allow this feature, after all it opens the possibility of another attack vector. If you're hosting the files in the same physical server, you might be able to get the same functionality by modifying just the URL that it's returned after you upload a file and keeping all your code in the admin.example.com domain.

6. Use it

Now you can use any of the multiple methods provided by the plugin to upload files to your server: drag and drop from the editor into the editor or any dialog with an Upload tab, paste an image from Photoshop, Paint.net, etc... into the editor, add a file or image by clicking the new toolbar buttons and let the plugin do all its magic :-)
Fully supported browsers:

Safari might work or not, but I'm not gonna buy a new computer just to test this plugin. Current versions of Opera should behave just like Chrome. Older versions of IE (IE 8 & 9) only allow to insert files using the toolbar buttons.

See also

Changelog of this plugin

More tips and configuration guides

Other plugins

Disclaimers

CKEditor is © CKSource.com

File and Image icons by the Tango Desktop Project