• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ckeditor / ckeditor5 / 25989

pending completion
25989

push

CKEditor5 code coverage

pomek
Internal: Release - remove built typings and JS after preparing packages to release.

12387 of 12387 branches covered (100.0%)

Branch coverage included in aggregate %.

32548 of 32548 relevant lines covered (100.0%)

11974.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/packages/ckeditor5-adapter-ckfinder/src/uploadadapter.ts
1
/**
2
 * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
3
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
 */
5

6
/**
7
 * @module adapter-ckfinder/uploadadapter
8
 */
9

10
import { Plugin } from 'ckeditor5/src/core.js';
11
import {
12
        FileRepository,
13
        type UploadAdapter as UploadAdapterInterface,
14
        type FileLoader,
15
        type UploadResponse
16
} from 'ckeditor5/src/upload.js';
17
import type { LocaleTranslate } from 'ckeditor5/src/utils.js';
18

19
import { getCsrfToken } from './utils.js';
20

21
/**
22
 * A plugin that enables file uploads in CKEditor 5 using the CKFinder server–side connector.
23
 *
24
 * See the {@glink features/file-management/ckfinder "CKFinder file manager integration"} guide to learn how to configure
25
 * and use this feature as well as find out more about the full integration with the file manager
26
 * provided by the {@link module:ckfinder/ckfinder~CKFinder} plugin.
27
 *
28
 * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} guide to learn
29
 * about other ways to upload images into CKEditor 5.
30
 */
31
export class CKFinderUploadAdapter extends Plugin {
32
        /**
33
         * @inheritDoc
34
         */
35
        public static get requires() {
36
                return [ FileRepository ] as const;
37
        }
38

60✔
39
        /**
40
         * @inheritDoc
41
         */
42
        public static get pluginName() {
43
                return 'CKFinderUploadAdapter' as const;
44
        }
45

48✔
46
        /**
47
         * @inheritDoc
48
         */
49
        public static override get isOfficialPlugin(): true {
50
                return true;
51
        }
52

12✔
53
        /**
54
         * @inheritDoc
12✔
55
         */
1✔
56
        public init(): void {
57
                const url = this.editor.config.get( 'ckfinder.uploadUrl' )! as string;
58

59
                if ( !url ) {
20✔
60
                        return;
61
                }
62

63
                // Register CKFinderAdapter
64
                this.editor.plugins.get( FileRepository ).createUploadAdapter = loader => new UploadAdapter( loader, url, this.editor.t );
65
        }
66
}
67

68
/**
69
 * Upload adapter for CKFinder.
70
 */
71
class UploadAdapter implements UploadAdapterInterface {
72
        /**
73
         * FileLoader instance to use during the upload.
74
         */
75
        public loader: FileLoader;
76

77
        /**
78
         * Upload URL.
79
         */
80
        public url: string;
81

82
        /**
83
         * Locale translation method.
84
         */
85
        public t: LocaleTranslate;
86

87
        private xhr?: XMLHttpRequest;
88

20✔
89
        /**
20✔
90
         * Creates a new adapter instance.
20✔
91
         */
92
        constructor( loader: FileLoader, url: string, t: LocaleTranslate ) {
93
                this.loader = loader;
94
                this.url = url;
95
                this.t = t;
96
        }
97

98
        /**
99
         * Starts the upload process.
7✔
100
         *
7✔
101
         * @see module:upload/filerepository~UploadAdapter#upload
7✔
102
         */
7✔
103
        public upload() {
7✔
104
                return this.loader.file.then( file => {
105
                        return new Promise<UploadResponse>( ( resolve, reject ) => {
106
                                this._initRequest();
107
                                this._initListeners( resolve, reject, file! );
108
                                this._sendRequest( file! );
109
                        } );
110
                } );
111
        }
112

113
        /**
114
         * Aborts the upload process.
2✔
115
         *
1✔
116
         * @see module:upload/filerepository~UploadAdapter#abort
117
         */
118
        public abort() {
119
                if ( this.xhr ) {
120
                        this.xhr.abort();
121
                }
122
        }
123

7✔
124
        /**
125
         * Initializes the XMLHttpRequest object.
7✔
126
         */
7✔
127
        private _initRequest() {
128
                const xhr = this.xhr = new XMLHttpRequest();
129

130
                xhr.open( 'POST', this.url, true );
131
                xhr.responseType = 'json';
132
        }
133

134
        /**
135
         * Initializes XMLHttpRequest listeners.
136
         *
137
         * @param resolve Callback function to be called when the request is successful.
138
         * @param reject Callback function to be called when the request cannot be completed.
139
         * @param file File instance to be uploaded.
140
         */
141
        private _initListeners(
7✔
142
                resolve: ( value: UploadResponse ) => void,
7✔
143
                reject: ( reason?: unknown ) => void,
7✔
144
                file: File
7✔
145
        ) {
146
                const xhr = this.xhr!;
7✔
147
                const loader = this.loader;
7✔
148
                const t = this.t;
7✔
149
                const genericError = t( 'Cannot upload file:' ) + ` ${ file.name }.`;
3✔
150

151
                xhr.addEventListener( 'error', () => reject( genericError ) );
3✔
152
                xhr.addEventListener( 'abort', () => reject() );
2✔
153
                xhr.addEventListener( 'load', () => {
154
                        const response = xhr.response;
155

1✔
156
                        if ( !response || !response.uploaded ) {
157
                                return reject( response && response.error && response.error.message ? response.error.message : genericError );
158
                        }
159

160
                        resolve( {
161
                                default: response.url
162
                        } );
7✔
163
                } );
7✔
164

6✔
165
                // Upload progress when it's supported.
4✔
166
                /* istanbul ignore else -- @preserve */
4✔
167
                if ( xhr.upload ) {
168
                        xhr.upload.addEventListener( 'progress', evt => {
169
                                if ( evt.lengthComputable ) {
170
                                        loader.uploadTotal = evt.total;
171
                                        loader.uploaded = evt.loaded;
172
                                }
173
                        } );
174
                }
175
        }
176

177
        /**
178
         * Prepares the data and sends the request.
179
         *
7✔
180
         * @param file File instance to be uploaded.
7✔
181
         */
7✔
182
        private _sendRequest( file: File ) {
183
                // Prepare form data.
184
                const data = new FormData();
7✔
185
                data.append( 'upload', file );
186
                data.append( 'ckCsrfToken', getCsrfToken() );
187

188
                // Send request.
189
                this.xhr!.send( data );
190
        }
191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc