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

ckeditor / ckeditor5 / b3dabf98-2b9b-44b5-be08-f689e3b11415

16 Dec 2024 09:55AM UTC coverage: 100.0%. Remained the same
b3dabf98-2b9b-44b5-be08-f689e3b11415

push

circleci

web-flow
Merge stable into master

14416 of 14416 branches covered (100.0%)

Branch coverage included in aggregate %.

38314 of 38314 relevant lines covered (100.0%)

10098.11 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-2024, 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
/* globals XMLHttpRequest, FormData */
7

8
/**
9
 * @module adapter-ckfinder/uploadadapter
10
 */
11

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

21
import { getCsrfToken } from './utils.js';
22

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

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

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

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

61
                if ( !url ) {
14✔
62
                        return;
1✔
63
                }
64

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

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

79
        /**
80
         * Upload URL.
81
         */
82
        public url: string;
83

84
        /**
85
         * Locale translation method.
86
         */
87
        public t: LocaleTranslate;
88

89
        private xhr?: XMLHttpRequest;
90

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

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

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

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

132
                xhr.open( 'POST', this.url, true );
7✔
133
                xhr.responseType = 'json';
7✔
134
        }
135

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

153
                xhr.addEventListener( 'error', () => reject( genericError ) );
7✔
154
                xhr.addEventListener( 'abort', () => reject() );
7✔
155
                xhr.addEventListener( 'load', () => {
7✔
156
                        const response = xhr.response;
3✔
157

158
                        if ( !response || !response.uploaded ) {
3✔
159
                                return reject( response && response.error && response.error.message ? response.error.message : genericError );
2✔
160
                        }
161

162
                        resolve( {
1✔
163
                                default: response.url
164
                        } );
165
                } );
166

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

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

190
                // Send request.
191
                this.xhr!.send( data );
7✔
192
        }
193
}
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