/
uploadadapter.ts
186 lines (161 loc) · 4.43 KB
/
uploadadapter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/* globals XMLHttpRequest, FormData */
/**
* @module adapter-ckfinder/uploadadapter
*/
import { Plugin } from 'ckeditor5/src/core.js';
import {
FileRepository,
type UploadAdapter as UploadAdapterInterface,
type FileLoader,
type UploadResponse
} from 'ckeditor5/src/upload.js';
import type { LocaleTranslate } from 'ckeditor5/src/utils.js';
import { getCsrfToken } from './utils.js';
/**
* A plugin that enables file uploads in CKEditor 5 using the CKFinder server–side connector.
*
* See the {@glink features/file-management/ckfinder "CKFinder file manager integration"} guide to learn how to configure
* and use this feature as well as find out more about the full integration with the file manager
* provided by the {@link module:ckfinder/ckfinder~CKFinder} plugin.
*
* Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} guide to learn
* about other ways to upload images into CKEditor 5.
*/
export default class CKFinderUploadAdapter extends Plugin {
/**
* @inheritDoc
*/
public static get requires() {
return [ FileRepository ] as const;
}
/**
* @inheritDoc
*/
public static get pluginName() {
return 'CKFinderUploadAdapter' as const;
}
/**
* @inheritDoc
*/
public init(): void {
const url = this.editor.config.get( 'ckfinder.uploadUrl' )! as string;
if ( !url ) {
return;
}
// Register CKFinderAdapter
this.editor.plugins.get( FileRepository ).createUploadAdapter = loader => new UploadAdapter( loader, url, this.editor.t );
}
}
/**
* Upload adapter for CKFinder.
*/
class UploadAdapter implements UploadAdapterInterface {
/**
* FileLoader instance to use during the upload.
*/
public loader: FileLoader;
/**
* Upload URL.
*/
public url: string;
/**
* Locale translation method.
*/
public t: LocaleTranslate;
private xhr?: XMLHttpRequest;
/**
* Creates a new adapter instance.
*/
constructor( loader: FileLoader, url: string, t: LocaleTranslate ) {
this.loader = loader;
this.url = url;
this.t = t;
}
/**
* Starts the upload process.
*
* @see module:upload/filerepository~UploadAdapter#upload
*/
public upload() {
return this.loader.file.then( file => {
return new Promise<UploadResponse>( ( resolve, reject ) => {
this._initRequest();
this._initListeners( resolve, reject, file! );
this._sendRequest( file! );
} );
} );
}
/**
* Aborts the upload process.
*
* @see module:upload/filerepository~UploadAdapter#abort
*/
public abort() {
if ( this.xhr ) {
this.xhr.abort();
}
}
/**
* Initializes the XMLHttpRequest object.
*/
private _initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open( 'POST', this.url, true );
xhr.responseType = 'json';
}
/**
* Initializes XMLHttpRequest listeners.
*
* @param resolve Callback function to be called when the request is successful.
* @param reject Callback function to be called when the request cannot be completed.
* @param file File instance to be uploaded.
*/
private _initListeners(
resolve: ( value: UploadResponse ) => void,
reject: ( reason?: unknown ) => void,
file: File
) {
const xhr = this.xhr!;
const loader = this.loader;
const t = this.t;
const genericError = t( 'Cannot upload file:' ) + ` ${ file.name }.`;
xhr.addEventListener( 'error', () => reject( genericError ) );
xhr.addEventListener( 'abort', () => reject() );
xhr.addEventListener( 'load', () => {
const response = xhr.response;
if ( !response || !response.uploaded ) {
return reject( response && response.error && response.error.message ? response.error.message : genericError );
}
resolve( {
default: response.url
} );
} );
// Upload progress when it's supported.
/* istanbul ignore else -- @preserve */
if ( xhr.upload ) {
xhr.upload.addEventListener( 'progress', evt => {
if ( evt.lengthComputable ) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
} );
}
}
/**
* Prepares the data and sends the request.
*
* @param file File instance to be uploaded.
*/
private _sendRequest( file: File ) {
// Prepare form data.
const data = new FormData();
data.append( 'upload', file );
data.append( 'ckCsrfToken', getCsrfToken() );
// Send request.
this.xhr!.send( data );
}
}