uploadadapter.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals XMLHttpRequest, FormData */
  6. /**
  7. * @module adapter-ckfinder/uploadadapter
  8. */
  9. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  10. import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
  11. import { getCsrfToken } from './utils';
  12. import log from '@ckeditor/ckeditor5-utils/src/log';
  13. /**
  14. * A plugin that enables CKFinder uploads in CKEditor 5.
  15. * Configure proper upload URL under the `ckfinder.uploadUrl` key, for example:
  16. *
  17. * Editor.create( editorElement, {
  18. * plugins: [ ... ],
  19. * ckfinder: {
  20. * uploadUrl: 'http://example.com/upload'
  21. * }
  22. * } );
  23. *
  24. * @extends module:core/plugin~Plugin
  25. */
  26. export default class CKFinderUploadAdapter extends Plugin {
  27. /**
  28. * @inheritDoc
  29. */
  30. static get requires() {
  31. return [ FileRepository ];
  32. }
  33. /**
  34. * @inheritDoc
  35. */
  36. init() {
  37. const url = this.editor.config.get( 'ckfinder.uploadUrl' );
  38. if ( !url ) {
  39. /**
  40. * Please provide `ckfinder.uploadUrl` config option.
  41. *
  42. * @error ckfinder-upload-adapter-no-config
  43. */
  44. log.warn( 'ckfinder-upload-adapter-no-config: Please provide "ckfinder.uploadUrl" config option.' );
  45. }
  46. // Register CKFinderAdapter
  47. this.editor.plugins.get( FileRepository ).createAdapter = loader => new Adapter( loader, url, this.editor.t );
  48. }
  49. }
  50. /**
  51. * Upload adapter for CKFinder.
  52. *
  53. * @private
  54. * @implements module:upload/filerepository~Adapter
  55. */
  56. class Adapter {
  57. /**
  58. * Creates a new adapter instance.
  59. *
  60. * @param {module:upload/filerepository~FileLoader} loader
  61. * @param {String} url
  62. * @param {module:utils/locale~Locale#t} t
  63. */
  64. constructor( loader, url, t ) {
  65. /**
  66. * FileLoader instance to use during the upload.
  67. *
  68. * @member {module:upload/filerepository~FileLoader} #loader
  69. */
  70. this.loader = loader;
  71. /**
  72. * Upload URL.
  73. *
  74. * @member {String} #url
  75. */
  76. this.url = url;
  77. /**
  78. * Locale translation method.
  79. *
  80. * @member {module:utils/locale~Locale#t} #t
  81. */
  82. this.t = t;
  83. }
  84. /**
  85. * Starts the upload process.
  86. *
  87. * @see module:upload/filerepository~Adapter#upload
  88. * @returns {Promise}
  89. */
  90. upload() {
  91. return new Promise( ( resolve, reject ) => {
  92. this._initRequest();
  93. this._initListeners( resolve, reject );
  94. this._sendRequest();
  95. } );
  96. }
  97. /**
  98. * Aborts the upload process.
  99. *
  100. * @see module:upload/filerepository~Adapter#abort
  101. * @returns {Promise}
  102. */
  103. abort() {
  104. if ( this.xhr ) {
  105. this.xhr.abort();
  106. }
  107. }
  108. /**
  109. * Initializes the XMLHttpRequest object.
  110. *
  111. * @private
  112. */
  113. _initRequest() {
  114. const xhr = this.xhr = new XMLHttpRequest();
  115. xhr.open( 'POST', this.url, true );
  116. xhr.responseType = 'json';
  117. }
  118. /**
  119. * Initializes XMLHttpRequest listeners.
  120. *
  121. * @private
  122. * @param {Function} resolve Callback function to be called when the request is successful.
  123. * @param {Function} reject Callback function to be called when the request cannot be completed.
  124. */
  125. _initListeners( resolve, reject ) {
  126. const xhr = this.xhr;
  127. const loader = this.loader;
  128. const t = this.t;
  129. const genericError = t( 'Cannot upload file:' ) + ` ${ loader.file.name }.`;
  130. xhr.addEventListener( 'error', () => reject( genericError ) );
  131. xhr.addEventListener( 'abort', () => reject() );
  132. xhr.addEventListener( 'load', () => {
  133. const response = xhr.response;
  134. if ( !response || !response.uploaded ) {
  135. return reject( response && response.error && response.error.message ? response.error.message : genericError );
  136. }
  137. resolve( {
  138. original: response.url
  139. } );
  140. } );
  141. // Upload progress when it's supported.
  142. /* istanbul ignore else */
  143. if ( xhr.upload ) {
  144. xhr.upload.addEventListener( 'progress', evt => {
  145. if ( evt.lengthComputable ) {
  146. loader.uploadTotal = evt.total;
  147. loader.uploaded = evt.loaded;
  148. }
  149. } );
  150. }
  151. }
  152. /**
  153. * Prepares the data and sends the request.
  154. *
  155. * @private
  156. */
  157. _sendRequest() {
  158. // Prepare form data.
  159. const data = new FormData();
  160. data.append( 'upload', this.loader.file );
  161. data.append( 'ckCsrfToken', getCsrfToken() );
  162. // Send request.
  163. this.xhr.send( data );
  164. }
  165. }