-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use XHR as possible uploading mechanism in filebrowser plugin #1352
Changes from 12 commits
9791777
7bf2375
f3449bd
56259ab
b491787
156fb10
ef08686
ccfffa1
9642a81
5fcd547
9985680
4cd6c1f
f9f54df
40a1d06
d2da180
1886055
f299c36
ce4afc2
c2c7565
cfd75e8
2c7bf71
4c95176
8276465
76cacbe
1eeab6f
5a577fe
cd36fe8
1140dbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,7 @@ | |
*/ | ||
|
||
( function() { | ||
'use strict'; | ||
// Default input element name for CSRF protection token. | ||
var TOKEN_INPUT_NAME = 'ckCsrfToken'; | ||
|
||
|
@@ -200,7 +201,7 @@ | |
} | ||
} | ||
|
||
// The onlick function assigned to the 'Upload' button. Makes the final | ||
// The onclick function assigned to the 'Upload' button. Makes the final | ||
// decision whether form is really submitted and updates target field when | ||
// file is uploaded. | ||
// | ||
|
@@ -296,22 +297,43 @@ | |
|
||
if ( url ) { | ||
var onClick = element.onClick; | ||
|
||
// "element" here means the definition object, so we need to find the correct | ||
// button to scope the event call | ||
element.onClick = function( evt ) { | ||
// "element" here means the definition object, so we need to find the correct | ||
// button to scope the event call | ||
var sender = evt.sender; | ||
if ( onClick && onClick.call( sender, evt ) === false ) | ||
var fileInput = sender.getDialog().getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement(); | ||
|
||
if ( onClick && onClick.call( sender, evt ) === false ) { | ||
return false; | ||
} | ||
|
||
if ( uploadFile.call( sender, evt ) ) { | ||
var fileInput = sender.getDialog().getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement(); | ||
|
||
// Append token preventing CSRF attacks. | ||
appendToken( fileInput ); | ||
return true; | ||
// Backward compatibility for IE8 and IE9 (https://cksource.tpondemand.com/entity/3117). | ||
// With version 4.9.0 change: `!== 'xhr'` to `=== 'form'`. | ||
if ( editor.config.filebrowserUploadMethod !== 'xhr' || !( CKEDITOR.fileTools && CKEDITOR.fileTools.isFileUploadSupported ) ) { | ||
// Append token preventing CSRF attacks. | ||
appendToken( fileInput ); | ||
return true; | ||
|
||
} else { | ||
var loader = editor.uploadRepository.create( fileInput.$.files[ 0 ] ); | ||
|
||
loader.on( 'uploaded', function( evt ) { | ||
var response = evt.sender.responseData; | ||
setUrl.call( evt.sender.editor, response.url, response.message ); | ||
} ); | ||
|
||
// Return non-false value will disable fileButton in dialogui, | ||
// below listeners takes care of such situation and re-enable "send" button. | ||
loader.on( 'error', xhrUploadErrorHandler.bind( this ) ); | ||
loader.on( 'abort', xhrUploadErrorHandler.bind( this ) ); | ||
|
||
loader.loadAndUpload( url ); | ||
|
||
return 'xhr'; | ||
} | ||
} | ||
|
||
|
||
return false; | ||
}; | ||
|
||
|
@@ -323,6 +345,12 @@ | |
} | ||
} | ||
|
||
function xhrUploadErrorHandler( evt ) { | ||
// `this` is a reference to ui.dialog.fileButton. | ||
this.enable(); | ||
alert( evt.sender.message ); // jshint ignore:line | ||
} | ||
|
||
// Updates the target element with the url of uploaded/selected file. | ||
// | ||
// @param {String} | ||
|
@@ -571,3 +599,26 @@ | |
* @cfg {Number/String} [filebrowserWindowHeight='70%'] | ||
* @member CKEDITOR.config | ||
*/ | ||
|
||
/** | ||
* Define preferred option to submit file upload with filebrowser plugin. If value is not specified, | ||
* `'form'` option is used. Additional XHR headers might be set up with {@link CKEDITOR.config#xmlHttpRequestHeaders}. | ||
* | ||
* Available values: | ||
* | ||
* * `'xhr'` - XMLHttpRequest is used to upload file | ||
* * `'form'` - Form submit is used to upload file | ||
* * `undefiend` - when configuration option is not specified `'form'` configuration is used | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo -> undefined. |
||
* | ||
* Note: please be aware that `'xhr'` requires {@link CKEDITOR.fileTools} for proper work. Missing this plugins | ||
* or using browsers which isn't supported {@link CKEDITOR.fileTools#isFileUploadSupported}, | ||
* will force using `'form'` behaviour despite configuration option. | ||
* | ||
* // Modern browsers will use XMLHttpRequest to upload files. | ||
* // IE8 and IE9 will use form submit even tough config option is set to 'xhr'. | ||
* config.filebrowserUploadMethod = 'xhr'; | ||
* | ||
* @since 4.8.1 | ||
* @cfg {Boolean} [filebrowserUploadMethod=undefined] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong type. This is not a bool but a string. Also I have asked for a |
||
* @member CKEDITOR.config | ||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,7 +48,9 @@ | |
editor.on( 'fileUploadRequest', function( evt ) { | ||
var fileLoader = evt.data.fileLoader, | ||
$formData = new FormData(), | ||
requestData = evt.data.requestData; | ||
requestData = evt.data.requestData, | ||
configXHRHeaders = editor.config.xmlHttpRequestHeaders, | ||
header; | ||
|
||
for ( var name in requestData ) { | ||
var value = requestData[ name ]; | ||
|
@@ -64,6 +66,12 @@ | |
// Append token preventing CSRF attacks. | ||
$formData.append( 'ckCsrfToken', CKEDITOR.tools.getCsrfToken() ); | ||
|
||
if ( configXHRHeaders ) { | ||
for ( header in configXHRHeaders ) { | ||
fileLoader.xhr.setRequestHeader( header, configXHRHeaders[ header ] ); | ||
} | ||
} | ||
|
||
fileLoader.xhr.send( $formData ); | ||
}, null, null, 999 ); | ||
|
||
|
@@ -856,7 +864,25 @@ | |
*/ | ||
isTypeSupported: function( file, supportedTypes ) { | ||
return !!file.type.match( supportedTypes ); | ||
} | ||
}, | ||
|
||
/** | ||
* Property inform if current browser poses native methods used by {@link CKEDITOR.fileTools} to upload files. | ||
* | ||
* @property {Boolean} isFileUploadSupported | ||
*/ | ||
isFileUploadSupported: ( function() { | ||
if ( typeof FileReader === 'function' && | ||
typeof ( new FileReader() ).readAsDataURL === 'function' && | ||
typeof FormData === 'function' && | ||
typeof ( new FormData() ).append === 'function' && | ||
typeof XMLHttpRequest === 'function' && | ||
typeof Blob === 'function' ) { | ||
|
||
return true; | ||
} | ||
return false; | ||
} )() | ||
} ); | ||
} )(); | ||
|
||
|
@@ -885,3 +911,16 @@ | |
* @cfg {String} [fileTools_defaultFileName=''] | ||
* @member CKEDITOR.config | ||
*/ | ||
|
||
/** | ||
* Additional headers of XMLHttpRequest used during file upload with plugins: {@link CKEDITOR.fileTools} and filebrowser. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These headers are not added only for file uploading but any request made by filetools. |
||
* | ||
* config.xmlHttpRequestHeaders = { | ||
* 'Cache-Control': 'no-cache', | ||
* 'X-CUSTOM': 'HEADER' | ||
* }; | ||
* | ||
* @since 4.8.1 | ||
* @cfg {Object} [xmlHttpRequestHeaders=null] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This config property should be prefixed with |
||
* @member CKEDITOR.config | ||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<textarea id="classic"> | ||
|
||
</textarea> | ||
|
||
<div id="output" style="background-color:lightgreen;"></div> | ||
|
||
<script> | ||
var xhr, requests; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not specify multiple variables in a single line. |
||
|
||
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { | ||
bender.ignore(); | ||
} else { | ||
// Mock XHR | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing dot. |
||
xhr = sinon.useFakeXMLHttpRequest(); | ||
requests = []; | ||
xhr.onCreate = function( xhr ) { | ||
requests.push( xhr ); | ||
}; | ||
window.onunload = function() { | ||
xhr.restore(); | ||
} | ||
} | ||
|
||
var classic = CKEDITOR.replace( 'classic', { | ||
xmlHttpRequestHeaders: { | ||
'hello': 'world', | ||
'foo': 'bar' | ||
}, | ||
filebrowserUploadUrl: 'fake-url', | ||
filebrowserUploadMethod: 'xhr' | ||
} ); | ||
|
||
// Display XHR details when CKEDITOR process entire request. | ||
classic.on( 'fileUploadRequest', function( evt ) { | ||
var output = document.getElementById( 'output' ); | ||
var outputString = CKEDITOR.tools.array.reduce( requests, function( acc, item ) { | ||
var line = ''; | ||
for ( header in item.requestHeaders ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
if ( item.requestHeaders.hasOwnProperty( header ) ) { | ||
line += ' | <code>header: ' + header + ', value: ' + item.requestHeaders[ header ] + '</code> |'; | ||
} | ||
} | ||
acc += '<li>' + line + '</li>'; | ||
return acc; | ||
}, '' ); | ||
output.innerHTML = '<ol>' + outputString + '</ol>'; | ||
}, null, null, 1000 ) | ||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
@bender-tags: 4.8.1, feature, tp3117 | ||
@bender-ui: collapsed | ||
@bender-ckeditor-plugins: wysiwygarea, toolbar, filebrowser, filetools, image, link, flash | ||
|
||
---- | ||
1. Open image dialog | ||
2. Go to upload tab | ||
3. Select some image and send it to server | ||
4. Close dialog (There might appear warning that image url is not set up) | ||
|
||
Repeat those steps for Link and Flash plugin. | ||
|
||
_Note:_ When new upload request is made, it should be visible as separate line below. | ||
|
||
**Expected:** Below editor will appear green div with listed headers attempted to send. There are 2 headers in single line: | ||
`hello: world, foo: bar`. | ||
|
||
**Unexpected:** Headers are not listed below. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<textarea id="classic"> | ||
|
||
</textarea> | ||
|
||
<div id="output" style="background-color:lightgreen;"></div> | ||
|
||
<script> | ||
var xhr, requests; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused variables. |
||
|
||
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { | ||
bender.ignore(); | ||
} | ||
|
||
var classic = CKEDITOR.replace( 'classic', { | ||
filebrowserUploadUrl: 'fake-url', | ||
filebrowserUploadMethod: 'xhr' | ||
} ); | ||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
@bender-tags: 4.8.1, feature, tp3117 | ||
@bender-ui: collapsed | ||
@bender-ckeditor-plugins: wysiwygarea, toolbar, filebrowser, filetools, image, link, flash | ||
|
||
---- | ||
1. Open image dialog | ||
2. Go to upload tab | ||
3. Select some image and send it to server | ||
4. **Alert** should be displayed | ||
5. Try to upload file again | ||
|
||
Repeat those steps for Link and Flash plugin. | ||
|
||
**Expected:** After first upload attempt, send button is enabled. Which means that every next click trigger new uploading process and generate new alert message. | ||
|
||
**Unexpected:** After first upload send button is disabled. Second and further upload attempts don't trigger upload process, and alert is not shown. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need to provide an error handling. E.g. user is connected to the wifi, and he losts the connection, upload returns 404.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can simplify it to showing an
alert
. I'll save you some time, and here is the snippet for it:however you still need to enable the upload button (note that it gets disabled).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a separate unit and manual test for the above. You could just stub
window.alert
and then ensure it was called with an error message.Remember to set config.language to 'en' in the unit test, otherwise person opening on a different locale could have
alert
called with message other than expected. 🙂