Skip to content

Commit

Permalink
Merge branch 't/12952' into major
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinmar committed Mar 10, 2015
2 parents 88d5cae + c42fb22 commit a5d0bf2
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 148 deletions.
295 changes: 200 additions & 95 deletions plugins/filetools/plugin.js
Expand Up @@ -23,6 +23,165 @@
* @member CKEDITOR.editor
*/
editor.uploadsRepository = new UploadsRepository( editor );

/**
* This event if fired when {@link CKEDITOR.fileTools.fileLoader FileLoader} should send XHR. If event will not be
* {@link CKEDITOR.eventInfo#stop stopped} or {@link CKEDITOR.eventInfo#cancel canceled}, then the default request
* will be sent (file as a form data with a field `'upload'`).
*
* If you want to change that behavior you can add custom listener with the default priority and
* {@link CKEDITOR.eventInfo#stop stop} the event, what will prevent the default behavior. For example to send
* data directly (without a form):
*
* editor.on( 'fileUploadRequest', function( evt ) {
* var xhr = evt.data.fileLoader.xhr;
*
* xhr.setRequestHeader( 'Cache-Control', 'no-cache' );
* xhr.setRequestHeader( 'X-File-Name', this.fileName );
* xhr.setRequestHeader( 'X-File-Size', this.total );
* xhr.send( this.file );
*
* // Prevented default behavior.
* evt.stop();
* } );
*
*
* You can also add custom request headers or set flags for the default request. This is especially useful for
* enabling Cross-Origin requests. For more information about Cross-Origin Resource Sharing see
* [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS):
*
* editor.on( 'fileUploadRequest', function( evt ) {
* var xhr = evt.data.fileLoader.xhr;
*
* xhr.setRequestHeader( 'Cache-Control', 'no-cache' );
* xhr.setRequestHeader( 'X-CUSTOM', 'HEADER' );
* xhr.withCredentials = true;
* } );
*
* When you listen on `fileUploadRequest` event with the default priority you will get XHR object which is opened as
* `POST` asynchronous request. This happens in a listener with the priority `5`, so if you want to overwrite also
* request open you need to listen with the lower priority. For example to send a `PUT` request:
*
* editor.on( 'fileUploadRequest', function( evt ) {
* var fileLoader = evt.data.fileLoader,
* formData = new FormData(),
* xhr = fileLoader.xhr;
*
* xhr.open( 'PUT', fileLoader.uploadUrl, true );
* formData.append( 'upload', fileLoader.file, fileLoader.fileName );
* fileLoader.xhr.send( formData );
*
* // Prevented default behavior.
* evt.stop();
* }, null, null, 4 ); // Listener with priority 4 will be executed before priority 5.
*
* Finally, you can also tell the {@link CKEDITOR.fileTools.fileLoader file loader} that request was not send, so it will not
* change its {@link CKEDITOR.fileTools.fileLoader#status status}. To do it you need to
* {@link CKEDITOR.eventInfo#cancel canceled} event:
*
* editor.on( 'fileUploadRequest', function( evt ) {
* // ...
*
* // Cancel event so file loader will not change its status.
* evt.cancel();
* } );
*
* @since 4.5
* @event fileUploadRequest
* @member CKEDITOR.editor
* @param data
* @param {CKEDITOR.fileTools.fileLoader} data.fileLoader File loader instance.
*/
editor.on( 'fileUploadRequest', function( evt ) {
var fileLoader = evt.data.fileLoader;

fileLoader.xhr.open( 'POST', fileLoader.uploadUrl, true );
}, null, null, 5 );

editor.on( 'fileUploadRequest', function( evt ) {
var fileLoader = evt.data.fileLoader,
formData = new FormData();

formData.append( 'upload', fileLoader.file, fileLoader.fileName );
fileLoader.xhr.send( formData );
}, null, null, 999 );

/**
* This event is fired when {CKEDITOR.fileTools.fileLoader file upload} response is received and needs to be parsed.
* If event will not be {@link CKEDITOR.eventInfo#stop stopped} or {@link CKEDITOR.eventInfo#cancel canceled}, then
* the default response handler will be used, which expects the response to be JSON data with the following structure:
*
* {
* fileName: <String> // The name of the file on the server.
* url: <String> // The URL to the file.
* uploaded: <Boolean> // True if uploading finished with success.
* error: {
* message: <String> // Optional message.
* }
* }
*
* If you want to handle a response manually you need to add a listener to this event and call {@link CKEDITOR.eventInfo#stop stop}
* to prevent the default behavior. Listener should set URL to the file on the server and the file name and can set additionally
* message from the server. If the response is to the error message, so the upload failed, then the event should be
* {@link CKEDITOR.eventInfo#cancel canceled}, so file loader will change {@link CKEDITOR.fileTools.fileLoader#status its status}
* to `error`.
*
* For example if the response is 'fileUrl|optionalErrorMessage':
*
* editor.on( 'fileUploadResponse', function( evt ) {
* // Prevent the default response handler.
* evt.stop();
*
* // Ger XHR and response.
* var data = evt.data,
* xhr = data.fileLoader.xhr,
* response = xhr.responseText.split( '|' );
*
* if ( response[ 1 ] ) {
* // Error occurred during upload.
* data.message = response[ 1 ];
* evt.cancel();
* } else {
* data.url = response[ 0 ];
* }
* } );
*
* @since 4.5
* @event fileUploadResponse
* @member CKEDITOR.editor
* @param data
* @param {CKEDITOR.fileTools.fileLoader} data.fileLoader file loader instance.
* @param {String} data.message Message form server, needs to be set in the listener, see example.
* @param {String} data.fileName File name on server, needs to be set in the listener, see example.
* @param {String} data.url URL to the uploaded file, needs to be set in the listener, see example.
*/
editor.on( 'fileUploadResponse', function( evt ) {
var fileLoader = evt.data.fileLoader,
xhr = fileLoader.xhr,
data = evt.data;

try {
var response = JSON.parse( xhr.responseText );

// Error message does not need to mean that upload finished unsuccessfully.
// It could mean that ex. file name was changes during upload due to naming collision.
if ( response.error && response.error.message ) {
data.message = response.error.message;
}

// But !uploaded means error.
if ( !response.uploaded ) {
evt.cancel();
} else {
data.fileName = response.fileName;
data.url = encodeURI( response.url );
}
} catch ( err ) {
// Response parsing error.
data.message = fileLoader.lang.filetools.responseError.replace( '%1', xhr.responseText );
evt.cancel();
}
}, null, null, 999 );
}
} );

Expand Down Expand Up @@ -118,34 +277,34 @@
* be called so the progress will be refreshed.
*
* Default requests and responses formats will work with CKFinder 2.4.3 and above. If you need a custom request
* or response handling you need to overwrite {@link #sendRequest sendRequest} or {@link #handleResponse handleResponse}
* method.
* or response handling you need to overwrite default behavior using the {@link CKEDITOR.editor#fileUploadRequest} and
* {@link CKEDITOR.editor#fileUploadResponse} events. For more information see their documentation.
*
* To create a `FileLoader` instance use the {@link CKEDITOR.fileTools.uploadsRepository} class.
*
* Here is a simple usage of `FileLoader`:
*
* editor.on( 'paste', function( evt ) {
* for ( var i = 0; i < evt.data.dataTransfer.getFilesCount(); i++ ) {
* var file = evt.data.dataTransfer.getFile( i );
* editor.on( 'paste', function( evt ) {
* for ( var i = 0; i < evt.data.dataTransfer.getFilesCount(); i++ ) {
* var file = evt.data.dataTransfer.getFile( i );
*
* if ( CKEDITOR.fileTools.isTypeSupported( file, /image\/png/ ) ) {
* var loader = editor.uploadsRepository.create( file );
* if ( CKEDITOR.fileTools.isTypeSupported( file, /image\/png/ ) ) {
* var loader = editor.uploadsRepository.create( file );
*
* loader.on( 'update', function () {
* document.getElementById( 'uploadProgress' ).innerHTML = loader.status;
* } );
* loader.on( 'update', function () {
* document.getElementById( 'uploadProgress' ).innerHTML = loader.status;
* } );
*
* loader.on( 'error', function () {
* alert( 'Error!' );
* loader.on( 'error', function () {
* alert( 'Error!' );
* } );
*
* loader.loadAndUpload( 'http://upload.url/' );
* loader.loadAndUpload( 'http://upload.url/' );
*
* evt.data.dataValue += 'loading...'
* evt.data.dataValue += 'loading...'
* }
* }
* }
* } );
* } );
*
* Note that `FileLoader` use file API which is support since Internet Explorer 10.
*
Expand All @@ -161,6 +320,7 @@
* the MIME type by replacing '/' with '.', for example for `image/png` the file name will be `image.png`.
*/
function FileLoader( editor, fileOrData, fileName ) {
this.editor = editor;
this.lang = editor.lang;

if ( typeof fileOrData === 'string' ) {
Expand Down Expand Up @@ -400,15 +560,14 @@
this.message = this.lang.filetools.noUrlError;
this.changeStatus( 'error' );
} else {
this.xhr = new XMLHttpRequest();

this.uploadUrl = url;

this.changeStatus( 'uploading' );

this.xhr = new XMLHttpRequest();
this.attachRequestListeners();

this.sendRequest();
if ( this.editor.fire( 'fileUploadRequest', { fileLoader: this } ) ) {
this.changeStatus( 'uploading' );
}
}
},

Expand Down Expand Up @@ -450,79 +609,29 @@
}
loader.changeStatus( 'error' );
} else {
loader.handleResponse( xhr );
var data = {
fileLoader: loader
},
// Values to copy from event to FileLoader.
valuesToCopy = [ 'message', 'fileName', 'url' ],
success = loader.editor.fire( 'fileUploadResponse', data );

for ( var i = 0; i < valuesToCopy.length; i++ ) {
var key = valuesToCopy[ i ];
if ( typeof data[ key ] === 'string' ) {
loader[ key ] = data[ key ];
}
}

if ( success === false ) {
loader.changeStatus( 'error' );
} else {
loader.changeStatus( 'uploaded' );
}
}
};
},

/**
* Send asynchronous request. Overwrite this method for a custom request.
*
* For example to send data directly (without a form):
*
* CKEDITOR.fileTools.fileLoader.prototype.sendRequest = function() {
* var xhr = this.xhr;
*
* xhr.open( 'POST', this.uploadUrl, true );
* xhr.setRequestHeader( 'Cache-Control', 'no-cache' );
* xhr.setRequestHeader( 'X-File-Name', this.fileName );
* xhr.setRequestHeader( 'X-File-Size', this.total );
* xhr.send( this.file );
* };
*
* @private
* @param {XMLHttpRequest} xhr XML HTTP Request object with attached listeners.
*/
sendRequest: function() {
var formData = new FormData(),
xhr = this.xhr;

formData.append( 'upload', this.file, this.fileName );
xhr.open( 'POST', this.uploadUrl, true );
xhr.send( formData );
},

/**
* Handle response from the server. Overwrite this method for custom response handling.
*
* For example if the response is 'fileUrl|errorMessage':
*
* CKEDITOR.fileTools.fileLoader.prototype.handleResponse = function() {
* var repsonse = this.xhr.responseText.split( '|' );
* if ( repsonse[ 1 ] ) {
* this.message = repsonse[ 1 ];
* this.changeStatus( 'error' );
* } else {
* this.url = response[ 0 ];
* this.changeStatus( 'uploaded' );
* }
* };
*
* @private
* @param {XMLHttpRequest} xhr XML HTTP Request object with response.
*/
handleResponse: function() {
try {
var response = JSON.parse( this.xhr.responseText );
} catch ( e ) {
this.message = this.lang.filetools.responseError.replace( '%1', this.xhr.responseText );
this.changeStatus( 'error' );
return;
}

if ( response.error ) {
this.message = response.error.message;
}

if ( !response.uploaded ) {
this.changeStatus( 'error' );
} else {
this.fileName = response.fileName;
this.url = encodeURI( response.url );
this.changeStatus( 'uploaded' );
}
},

/**
* Changes {@link #status} to the new status, update {@link #method-abort} method if needed and fire two events:
* new status and {@link #event-update}.
Expand Down Expand Up @@ -599,10 +708,6 @@
CKEDITOR.event.implementOn( UploadsRepository.prototype );
CKEDITOR.event.implementOn( FileLoader.prototype );

//
// HELPERS ----------------------------------------------------------------
//

var base64HeaderRegExp = /^data:(\S*?);base64,/;

// Transforms Base64 string data into file and creates name for that file based on the mime type.
Expand Down Expand Up @@ -721,4 +826,4 @@
* @since 4.5
* @cfg {String} [uploadUrl='']
* @member CKEDITOR.config
*/
*/
34 changes: 34 additions & 0 deletions plugins/uploadwidget/dev/cors.html
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<!--
Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or http://ckeditor.com/license
-->
<html>
<head>
<meta charset="utf-8">
<title>CORS sample</title>
<script src="../../../ckeditor.js"></script>
<script src="filereaderplugin.js"></script>
<script src="../../filetools/dev/uploaddebugger.js"></script>
<link href="../../../samples/sample.css" rel="stylesheet">
</head>
<body>
<h1 class="samples">CORS sample</h1>
<textarea cols="80" id="editor1" name="editor1" rows="10">
&lt;h1&gt;&lt;img alt=&quot;Saturn V carrying Apollo 11&quot; class=&quot;right&quot; src=&quot;../../../samples/assets/sample.jpg&quot;/&gt; Apollo 11&lt;/h1&gt; &lt;p&gt;&lt;b&gt;Apollo 11&lt;/b&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt; &lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt; &lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt; &lt;table align=&quot;right&quot; border=&quot;1&quot; bordercolor=&quot;#ccc&quot; cellpadding=&quot;5&quot; cellspacing=&quot;0&quot; style=&quot;border-collapse:collapse;margin:10px 0 10px 15px;&quot;&gt; &lt;caption&gt;&lt;strong&gt;Mission crew&lt;/strong&gt;&lt;/caption&gt; &lt;thead&gt; &lt;tr&gt; &lt;th scope=&quot;col&quot;&gt;Position&lt;/th&gt; &lt;th scope=&quot;col&quot;&gt;Astronaut&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Commander&lt;/td&gt; &lt;td&gt;Neil A. Armstrong&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Command Module Pilot&lt;/td&gt; &lt;td&gt;Michael Collins&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Lunar Module Pilot&lt;/td&gt; &lt;td&gt;Edwin &amp;quot;Buzz&amp;quot; E. Aldrin, Jr.&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt; &lt;hr/&gt; &lt;p style=&quot;text-align: right;&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
</textarea>
<script>
var editor = CKEDITOR.replace( 'editor1', {
extraPlugins: 'uploadimage,filetools',
imageUploadUrl: 'http://sub.ckeditor.dev/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images&responseType=json'
} );

editor.on( 'fileUploadRequest', function( e ) {
var xhr = e.data.fileLoader.xhr;

xhr.withCredentials = true;
} );

</script>
</body>
</html>

0 comments on commit a5d0bf2

Please sign in to comment.