8
0

uploadadapter.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  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. /**
  13. * A plugin that enables file uploads in CKEditor 5 using the CKFinder server–side connector.
  14. *
  15. * See the {@glink features/image-upload/ckfinder "CKFinder file manager integration" guide} to learn how to configure
  16. * and use this feature as well as find out more about the full integration with the file manager
  17. * provided by the {@link module:ckfinder/ckfinder~CKFinder} plugin.
  18. *
  19. * Check out the {@glink features/image-upload/image-upload comprehensive "Image upload overview"} to learn about
  20. * other ways to upload images into CKEditor 5.
  21. *
  22. * @extends module:core/plugin~Plugin
  23. */
  24. export default class CKFinderUploadAdapter extends Plugin {
  25. /**
  26. * @inheritDoc
  27. */
  28. static get requires() {
  29. return [ FileRepository ];
  30. }
  31. /**
  32. * @inheritDoc
  33. */
  34. static get pluginName() {
  35. return 'CKFinderUploadAdapter';
  36. }
  37. /**
  38. * @inheritDoc
  39. */
  40. init() {
  41. const url = this.editor.config.get( 'ckfinder.uploadUrl' );
  42. if ( !url ) {
  43. return;
  44. }
  45. // Register CKFinderAdapter
  46. this.editor.plugins.get( FileRepository ).createUploadAdapter = loader => new UploadAdapter( loader, url, this.editor.t );
  47. }
  48. }
  49. /**
  50. * Upload adapter for CKFinder.
  51. *
  52. * @private
  53. * @implements module:upload/filerepository~UploadAdapter
  54. */
  55. class UploadAdapter {
  56. /**
  57. * Creates a new adapter instance.
  58. *
  59. * @param {module:upload/filerepository~FileLoader} loader
  60. * @param {String} url
  61. * @param {module:utils/locale~Locale#t} t
  62. */
  63. constructor( loader, url, t ) {
  64. /**
  65. * FileLoader instance to use during the upload.
  66. *
  67. * @member {module:upload/filerepository~FileLoader} #loader
  68. */
  69. this.loader = loader;
  70. /**
  71. * Upload URL.
  72. *
  73. * @member {String} #url
  74. */
  75. this.url = url;
  76. /**
  77. * Locale translation method.
  78. *
  79. * @member {module:utils/locale~Locale#t} #t
  80. */
  81. this.t = t;
  82. }
  83. /**
  84. * Starts the upload process.
  85. *
  86. * @see module:upload/filerepository~UploadAdapter#upload
  87. * @returns {Promise.<Object>}
  88. */
  89. upload() {
  90. return this.loader.file.then( file => {
  91. return new Promise( ( resolve, reject ) => {
  92. this._initRequest();
  93. this._initListeners( resolve, reject, file );
  94. this._sendRequest( file );
  95. } );
  96. } );
  97. }
  98. /**
  99. * Aborts the upload process.
  100. *
  101. * @see module:upload/filerepository~UploadAdapter#abort
  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. * @param {File} file File instance to be uploaded.
  125. */
  126. _initListeners( resolve, reject, file ) {
  127. const xhr = this.xhr;
  128. const loader = this.loader;
  129. const t = this.t;
  130. const genericError = t( 'Cannot upload file:' ) + ` ${ file.name }.`;
  131. xhr.addEventListener( 'error', () => reject( genericError ) );
  132. xhr.addEventListener( 'abort', () => reject() );
  133. xhr.addEventListener( 'load', () => {
  134. const response = xhr.response;
  135. if ( !response || !response.uploaded ) {
  136. return reject( response && response.error && response.error.message ? response.error.message : genericError );
  137. }
  138. resolve( {
  139. default: response.url
  140. } );
  141. } );
  142. // Upload progress when it's supported.
  143. /* istanbul ignore else */
  144. if ( xhr.upload ) {
  145. xhr.upload.addEventListener( 'progress', evt => {
  146. if ( evt.lengthComputable ) {
  147. loader.uploadTotal = evt.total;
  148. loader.uploaded = evt.loaded;
  149. }
  150. } );
  151. }
  152. }
  153. /**
  154. * Prepares the data and sends the request.
  155. *
  156. * @private
  157. * @param {File} file File instance to be uploaded.
  158. */
  159. _sendRequest( file ) {
  160. // Prepare form data.
  161. const data = new FormData();
  162. data.append( 'upload', file );
  163. data.append( 'ckCsrfToken', getCsrfToken() );
  164. // Send request.
  165. this.xhr.send( data );
  166. }
  167. }