Skip to content

Commit

Permalink
Merge branch 't/13518' into major
Browse files Browse the repository at this point in the history
  • Loading branch information
Piotr Jasiun committed Oct 23, 2015
2 parents 336a28c + 8749b3f commit b01cee9
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -7,6 +7,7 @@ New Features:

* [#12077](https://dev.ckeditor.com/ticket/12077): Add support for HTML5 "download" attribute in link (a) elements. Thanks to [sbusse](https://github.com/sbusse)!
* [#13519](http://dev.ckeditor.com/ticket/13519): (http://docs.ckeditor.com/#!/api/CKEDITOR.fileTools) response is now more flexible.
* [#13518](http://dev.ckeditor.com/ticket/13518): (http://docs.ckeditor.com/#!/api/CKEDITOR.fileTools) request is now more flexible and [additionalRequestParameters](http://docs.ckeditor.com/#!/api/CKEDITOR.fileTools.uploadWidgetDefinition-property-additionalRequestParameters) property was introduced.
* [#13828](http://dev.ckeditor.com/ticket/13828): Adding a class to widget element will also add a prefixed class to it's wrapper.

## CKEditor 4.5.5
Expand Down
36 changes: 29 additions & 7 deletions plugins/filetools/plugin.js
Expand Up @@ -34,19 +34,35 @@
* @member CKEDITOR.editor
* @param data
* @param {CKEDITOR.fileTools.fileLoader} data.fileLoader File loader instance.
* @param {Object} data.requestData Object containing all data to be sent to the server.
*/
editor.on( 'fileUploadRequest', function( evt ) {
var fileLoader = evt.data.fileLoader;

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

// Adding file to event's data by default - allows overwriting it by user's event listeners. (#13518)
evt.data.requestData.upload = { file: fileLoader.file, name: fileLoader.fileName };
}, null, null, 5 );

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

for ( var name in requestData ) {
var value = requestData[ name ];

formData.append( 'upload', fileLoader.file, fileLoader.fileName );
fileLoader.xhr.send( formData );
// Treating files in special way
if ( typeof value === 'object' && value.file ) {
$formData.append( name, value.file, value.name );
}
else {
$formData.append( name, value );
}
}

fileLoader.xhr.send( $formData );
}, null, null, 999 );

/**
Expand Down Expand Up @@ -443,8 +459,10 @@
* * `uploaded`.
*
* @param {String} url The upload URL.
* @param {Object} [additionalRequestParameters] Additional parameters that would be passed into
* {@link CKEDITOR.editor#fileUploadRequest} event.
*/
loadAndUpload: function( url ) {
loadAndUpload: function( url, additionalRequestParameters ) {
var loader = this;

this.once( 'loaded', function( evt ) {
Expand All @@ -457,7 +475,7 @@
}, null, null, 0 );

// Start uploading.
loader.upload( url );
loader.upload( url, additionalRequestParameters );
}, null, null, 0 );

this.load();
Expand Down Expand Up @@ -518,8 +536,12 @@
* * `uploaded`.
*
* @param {String} url The upload URL.
* @param {Object} [additionalRequestParameters] Additional data that would be passed into
* {@link CKEDITOR.editor#fileUploadRequest} event.
*/
upload: function( url ) {
upload: function( url, additionalRequestParameters ) {
var requestData = additionalRequestParameters || {};

if ( !url ) {
this.message = this.lang.filetools.noUrlError;
this.changeStatus( 'error' );
Expand All @@ -529,7 +551,7 @@
this.xhr = new XMLHttpRequest();
this.attachRequestListeners();

if ( this.editor.fire( 'fileUploadRequest', { fileLoader: this } ) ) {
if ( this.editor.fire( 'fileUploadRequest', { fileLoader: this, requestData: requestData } ) ) {
this.changeStatus( 'uploading' );
}
}
Expand Down
38 changes: 33 additions & 5 deletions plugins/uploadwidget/plugin.js
@@ -1,4 +1,4 @@
/**
/**
* @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
Expand Down Expand Up @@ -84,6 +84,28 @@
* }
* } );
*
* If you need to pass additional data to the request, you can do this using
* {@link CKEDITOR.fileTools.uploadWidgetDefinition#additionalRequestParameters additionalRequestParameters} property.
* That data is then passed to the upload method, defined by {@link CKEDITOR.fileTools.uploadWidgetDefinition#loadMethod},
* and to {@link CKEDITOR.editor#fileUploadRequest} event (as part of `requestData` property).
* Syntax of that parameter is compatible with {@link CKEDITOR.editor#fileUploadRequest} `requestData` property.
*
* CKEDITOR.fileTools.addUploadWidget( editor, 'uploadFile', {
* additionalRequestParameters: {
* foo: 'bar'
* },
*
* fileToElement: function( file ) {
* var el = new CKEDITOR.dom.element( 'span' );
* el.setText( '...' );
* return el;
* },
*
* onUploaded: function( upload ) {
* this.replaceWith( '<a href="' + upload.url + '" target="_blank">' + upload.fileName + '</a>' );
* }
* } );
*
* If you need custom `paste` handling you need to mark the pasted element to be changed into an upload widget
* using {@link CKEDITOR.fileTools#markElement markElement}. For example, instead of the `fileToElement` helper from the
* example above, a `paste` listener can be created manually:
Expand Down Expand Up @@ -169,7 +191,7 @@
loader = uploads.create( file );

if ( el ) {
loader[ loadMethod ]( def.uploadUrl );
loader[ loadMethod ]( def.uploadUrl, def.additionalRequestParameters );

CKEDITOR.fileTools.markElement( el, name, loader.id );

Expand All @@ -195,9 +217,9 @@
* should not be overwritten.
*
* Also, the upload widget definition defines a few properties ({@link #fileToElement}, {@link #supportedTypes},
* {@link #loadMethod loadMethod} and {@link #uploadUrl}) used in the {@link CKEDITOR.editor#paste} listener
* which is registered by {@link CKEDITOR.fileTools#addUploadWidget} if the upload widget definition contains
* the {@link #fileToElement} callback.
* {@link #loadMethod loadMethod}, {@link #uploadUrl} and {@link #additionalRequestParameters}) used in the
* {@link CKEDITOR.editor#paste} listener which is registered by {@link CKEDITOR.fileTools#addUploadWidget}
* if the upload widget definition contains the {@link #fileToElement} callback.
*
* @abstract
* @class CKEDITOR.fileTools.uploadWidgetDefinition
Expand Down Expand Up @@ -341,6 +363,12 @@
* @property {String} [uploadUrl]
*/

/**
* Object containing additional data that should be based into function defined by {@link #loadMethod}.
*
* @property {Object} [additionalRequestParameters]
*/

/**
* The type of loading operation that should be executed as a result of pasting a file. Possible options are:
*
Expand Down
149 changes: 127 additions & 22 deletions tests/plugins/filetools/fileloader.js
Expand Up @@ -4,7 +4,10 @@
'use strict';

( function() {
var FileReaderBackup = window.FileReader,
var File = window.File,
Blob = window.Blob,
FormData = window.FormData,
FileReaderBackup = window.FileReader,
XMLHttpRequestBackup = window.XMLHttpRequest,
FileLoader, resumeAfter,
pngBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdjYGBgAAAABAABJzQnCgAAAABJRU5ErkJggg==',
Expand All @@ -19,6 +22,42 @@
}
};

function createFileMock() {
window.File = File = function( data, name ) {
var file = new Blob( data , {} );
file.name = name;

return file;
};
}

function createFormDataMock() {
window.FormData = function() {
var entries = {},
mock = {
get: function( name ) {
return entries[ name ] || null;
},
append: function( name, value, fileName ) {
if ( value instanceof File && ( value.name === fileName || !fileName ) )
entries[ name ] = value;
else if ( value instanceof Blob ) {
fileName = fileName || value.name || 'blob';

entries [ name ] = new File( [ value ], fileName );
}
else
entries[ name ] = value + '';
},
has: function( name ) {
return Object.prototype.hasOwnProperty.call( entries, name );
}
};

return mock;
};
}

function createFileReaderMock( scenario ) {
var isAborted = false;

Expand Down Expand Up @@ -197,6 +236,15 @@
assert.ignore();
}

// IE doesn't support File constructor, so there is a need to mimic it.
if ( typeof MSBlobBuilder === 'function' )
createFileMock();

// FormData in IE & Chrome 47- supports only adding data, not getting it, so mocking (polyfilling?) is required.
// Note that mocking is needed only for tests, as CKEditor.fileTools uses only append method
if ( !FormData.prototype.get || !FormData.prototype.has )
createFormDataMock();

FileLoader = CKEDITOR.fileTools.fileLoader;
resumeAfter = bender.tools.resumeAfter;
testFile = bender.tools.getTestPngFile();
Expand Down Expand Up @@ -348,6 +396,61 @@
wait();
},

'test upload with custom field name (#13518)': function() {
var loader = new FileLoader( editorMock, pngBase64, 'name.png' );

attachListener( editorMock, 'fileUploadRequest', function( evt ) {
var requestData = evt.data.requestData;

requestData.myFile = requestData.upload;

delete requestData.upload;
} );

createXMLHttpRequestMock( [ 'load' ] );

resumeAfter( loader, 'uploaded', function() {
assert.isTrue( lastFormData.has( 'myFile' ) );
assert.isFalse( lastFormData.has( 'upload' ) );

// FormData converts all Blob objects into File ones, so we must "revert" it
objectAssert.areEqual( new Blob( [ lastFormData.get( 'myFile' ) ], {} ), loader.file );
}, 3 );

loader.upload( 'http:\/\/url\/' );

wait();
},

'test upload with additional request parameters provided (#13518)': function() {
var loader = new FileLoader( editorMock, pngBase64, 'name.png' );

createXMLHttpRequestMock( [ 'load' ] );

resumeAfter( loader, 'uploaded', function() {
assert.areSame( 'test', lastFormData.get( 'test' ) );
}, 3 );

loader.upload( 'http:\/\/url\/', { test: 'test' } );

wait();
},

'test if name of file is correctly attached (#13518)': function() {
var name = 'customName.png',
loader = new FileLoader( editorMock, pngBase64, name );

createXMLHttpRequestMock( [ 'load' ] );

resumeAfter( loader, 'uploaded', function() {
assert.areSame( name, lastFormData.get( 'upload' ).name );
}, 3 );

loader.upload( 'http:\/\/url\/' );

wait();
},

'test upload response not encoded (#13030)': function() {
var loader = new FileLoader( editorMock, pngBase64, 'na me.png' ),
observer = observeEvents( loader );
Expand Down Expand Up @@ -768,27 +871,6 @@
wait();
},

'test response with custom fields (#13519)': function() {
var loader = new FileLoader( editorMock, testFile ),
response = {
fileName: 'name2.png',
uploaded: 1,
url: 'http:\/\/url\/name2.png',
foo: 'bar'
};

createXMLHttpRequestMock( [ 'progress', 'load' ],
{ responseText: JSON.stringify( response ) } );

resumeAfter( loader, 'uploaded', function() {
objectAssert.areEqual( response, loader.responseData );
} );

loader.upload( 'http:\/\/url\/' );

wait();
},

'test error 404 with message': function() {
editorMock.lang = { filetools: { httpError404: 'httpError404' } };

Expand Down Expand Up @@ -887,6 +969,29 @@
wait();
},

'test additional data passed to xhr via fileUploadRequest listener (#13518)': function() {
var loader = new FileLoader( editorMock, testFile ),
file = new File( [], 'a' );

attachListener( editorMock, 'fileUploadRequest', function( evt ) {
var requestData = evt.data.requestData;

requestData.customField = 'test';
requestData.customFile = file;
} );

createXMLHttpRequestMock( [ 'load' ] );

resumeAfter( loader, 'uploaded', function() {
assert.areSame( 'test', lastFormData.get( 'customField' ) );
objectAssert.areEqual( file, lastFormData.get( 'customFile' ) );
} );

loader.upload( 'http:\/\/url\/' );

wait();
},

'test additional data in fileUploadResponse (#13519)': function() {
var data,
loader = new FileLoader( editorMock, testFile );
Expand Down

0 comments on commit b01cee9

Please sign in to comment.