@ckeditor/ckeditor5-cloud-services 36.0.0 → 37.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  /*!
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md.
4
- */(()=>{var e={704:(e,t,r)=>{e.exports=r(79)("./src/core.js")},209:(e,t,r)=>{e.exports=r(79)("./src/utils.js")},79:e=>{"use strict";e.exports=CKEditor5.dll}},t={};function r(s){var o=t[s];if(void 0!==o)return o.exports;var n=t[s]={exports:{}};return e[s](n,n.exports,r),n.exports}r.d=(e,t)=>{for(var s in t)r.o(t,s)&&!r.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var s={};(()=>{"use strict";r.r(s),r.d(s,{CloudServices:()=>c,CloudServicesCore:()=>d});var e=r(704),t=r(209);const o={autoRefresh:!0},n=36e5;class i{constructor(e,r=o){if(!e)throw new t.CKEditorError("token-missing-token-url",this);r.initValue&&this._validateTokenValue(r.initValue),this.set("value",r.initValue),this._refresh="function"==typeof e?e:()=>{return r=e,new Promise(((e,s)=>{const o=new XMLHttpRequest;o.open("GET",r),o.addEventListener("load",(()=>{const r=o.status,n=o.response;return r<200||r>299?s(new t.CKEditorError("token-cannot-download-new-token",null)):e(n)})),o.addEventListener("error",(()=>s(new Error("Network Error")))),o.addEventListener("abort",(()=>s(new Error("Abort")))),o.send()}));var r},this._options=Object.assign({},o,r)}init(){return new Promise(((e,t)=>{this.value?(this._options.autoRefresh&&this._registerRefreshTokenTimeout(),e(this)):this.refreshToken().then(e).catch(t)}))}refreshToken(){return this._refresh().then((e=>{this._validateTokenValue(e),this.set("value",e),this._options.autoRefresh&&this._registerRefreshTokenTimeout()})).then((()=>this))}destroy(){clearTimeout(this._tokenRefreshTimeout)}_validateTokenValue(e){const r="string"==typeof e,s=!/^".*"$/.test(e),o=r&&3===e.split(".").length;if(!s||!o)throw new t.CKEditorError("token-not-in-jwt-format",this)}_registerRefreshTokenTimeout(){const e=this._getTokenRefreshTimeoutTime();clearTimeout(this._tokenRefreshTimeout),this._tokenRefreshTimeout=setTimeout((()=>{this.refreshToken()}),e)}_getTokenRefreshTimeoutTime(){try{const[,e]=this.value.split("."),{exp:t}=JSON.parse(atob(e));if(!t)return n;return Math.floor((1e3*t-Date.now())/2)}catch(e){return n}}static create(e,t=o){return new i(e,t).init()}}(0,t.mix)(i,t.ObservableMixin);const a=i,u=/^data:(\S*?);base64,/;class h{constructor(e,r,s){if(!e)throw new t.CKEditorError("fileuploader-missing-file",null);if(!r)throw new t.CKEditorError("fileuploader-missing-token",null);if(!s)throw new t.CKEditorError("fileuploader-missing-api-address",null);this.file=function(e){if("string"!=typeof e)return!1;const t=e.match(u);return!(!t||!t.length)}(e)?function(e,r=512){try{const t=e.match(u)[1],s=atob(e.replace(u,"")),o=[];for(let e=0;e<s.length;e+=r){const t=s.slice(e,e+r),n=new Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);o.push(new Uint8Array(n))}return new Blob(o,{type:t})}catch(e){throw new t.CKEditorError("fileuploader-decoding-image-data-error",null)}}(e):e,this._token=r,this._apiAddress=s}onProgress(e){return this.on("progress",((t,r)=>e(r))),this}onError(e){return this.once("error",((t,r)=>e(r))),this}abort(){this.xhr.abort()}send(){return this._prepareRequest(),this._attachXHRListeners(),this._sendRequest()}_prepareRequest(){const e=new XMLHttpRequest;e.open("POST",this._apiAddress),e.setRequestHeader("Authorization",this._token.value),e.responseType="json",this.xhr=e}_attachXHRListeners(){const e=this,t=this.xhr;function r(t){return()=>e.fire("error",t)}t.addEventListener("error",r("Network Error")),t.addEventListener("abort",r("Abort")),t.upload&&t.upload.addEventListener("progress",(e=>{e.lengthComputable&&this.fire("progress",{total:e.total,uploaded:e.loaded})})),t.addEventListener("load",(()=>{const e=t.status,r=t.response;if(e<200||e>299)return this.fire("error",r.message||r.error)}))}_sendRequest(){const e=new FormData,r=this.xhr;return e.append("file",this.file),new Promise(((s,o)=>{r.addEventListener("load",(()=>{const e=r.status,n=r.response;return e<200||e>299?n.message?o(new t.CKEditorError("fileuploader-uploading-data-failed",this,{message:n.message})):o(n.error):s(n)})),r.addEventListener("error",(()=>o(new Error("Network Error")))),r.addEventListener("abort",(()=>o(new Error("Abort")))),r.send(e)}))}}(0,t.mix)(h,t.EmitterMixin);class l{constructor(e,r){if(!e)throw new t.CKEditorError("uploadgateway-missing-token",null);if(!r)throw new t.CKEditorError("uploadgateway-missing-api-address",null);this._token=e,this._apiAddress=r}upload(e){return new h(e,this._token,this._apiAddress)}}class d extends e.ContextPlugin{static get pluginName(){return"CloudServicesCore"}createToken(e,t){return new a(e,t)}createUploadGateway(e,t){return new l(e,t)}}class c extends e.ContextPlugin{static get pluginName(){return"CloudServices"}static get requires(){return[d]}init(){const e=this.context.config.get("cloudServices")||{};for(const t in e)this[t]=e[t];if(this._tokens=new Map,this.tokenUrl)return this.token=this.context.plugins.get("CloudServicesCore").createToken(this.tokenUrl),this._tokens.set(this.tokenUrl,this.token),this.token.init();this.token=null}registerTokenUrl(e){if(this._tokens.has(e))return Promise.resolve(this.getTokenFor(e));const t=this.context.plugins.get("CloudServicesCore").createToken(e);return this._tokens.set(e,t),t.init()}getTokenFor(e){const r=this._tokens.get(e);if(!r)throw new t.CKEditorError("cloudservices-token-not-registered",this);return r}destroy(){super.destroy();for(const e of this._tokens.values())e.destroy()}}})(),(window.CKEditor5=window.CKEditor5||{}).cloudServices=s})();
4
+ */(()=>{var e={704:(e,t,r)=>{e.exports=r(79)("./src/core.js")},209:(e,t,r)=>{e.exports=r(79)("./src/utils.js")},79:e=>{"use strict";e.exports=CKEditor5.dll}},t={};function r(s){var o=t[s];if(void 0!==o)return o.exports;var n=t[s]={exports:{}};return e[s](n,n.exports,r),n.exports}r.d=(e,t)=>{for(var s in t)r.o(t,s)&&!r.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var s={};(()=>{"use strict";r.r(s),r.d(s,{CloudServices:()=>d,CloudServicesCore:()=>h});var e=r(704),t=r(209);const o={autoRefresh:!0},n=36e5;class i extends((0,t.ObservableMixin)()){constructor(e,r={}){if(super(),!e)throw new t.CKEditorError("token-missing-token-url",this);r.initValue&&this._validateTokenValue(r.initValue),this.set("value",r.initValue),this._refresh="function"==typeof e?e:()=>{return r=e,new Promise(((e,s)=>{const o=new XMLHttpRequest;o.open("GET",r),o.addEventListener("load",(()=>{const r=o.status,n=o.response;return r<200||r>299?s(new t.CKEditorError("token-cannot-download-new-token",null)):e(n)})),o.addEventListener("error",(()=>s(new Error("Network Error")))),o.addEventListener("abort",(()=>s(new Error("Abort")))),o.send()}));var r},this._options={...o,...r}}init(){return new Promise(((e,t)=>{this.value?(this._options.autoRefresh&&this._registerRefreshTokenTimeout(),e(this)):this.refreshToken().then(e).catch(t)}))}refreshToken(){return this._refresh().then((e=>(this._validateTokenValue(e),this.set("value",e),this._options.autoRefresh&&this._registerRefreshTokenTimeout(),this)))}destroy(){clearTimeout(this._tokenRefreshTimeout)}_validateTokenValue(e){const r="string"==typeof e,s=!/^".*"$/.test(e),o=r&&3===e.split(".").length;if(!s||!o)throw new t.CKEditorError("token-not-in-jwt-format",this)}_registerRefreshTokenTimeout(){const e=this._getTokenRefreshTimeoutTime();clearTimeout(this._tokenRefreshTimeout),this._tokenRefreshTimeout=setTimeout((()=>{this.refreshToken()}),e)}_getTokenRefreshTimeoutTime(){try{const[,e]=this.value.split("."),{exp:t}=JSON.parse(atob(e));if(!t)return n;return Math.floor((1e3*t-Date.now())/2)}catch(e){return n}}static create(e,t={}){return new i(e,t).init()}}const a=/^data:(\S*?);base64,/;class u extends((0,t.EmitterMixin)()){constructor(e,r,s){if(super(),!e)throw new t.CKEditorError("fileuploader-missing-file",null);if(!r)throw new t.CKEditorError("fileuploader-missing-token",null);if(!s)throw new t.CKEditorError("fileuploader-missing-api-address",null);this.file=function(e){if("string"!=typeof e)return!1;const t=e.match(a);return!(!t||!t.length)}(e)?function(e,r=512){try{const t=e.match(a)[1],s=atob(e.replace(a,"")),o=[];for(let e=0;e<s.length;e+=r){const t=s.slice(e,e+r),n=new Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);o.push(new Uint8Array(n))}return new Blob(o,{type:t})}catch(e){throw new t.CKEditorError("fileuploader-decoding-image-data-error",null)}}(e):e,this._token=r,this._apiAddress=s}onProgress(e){return this.on("progress",((t,r)=>e(r))),this}onError(e){return this.once("error",((t,r)=>e(r))),this}abort(){this.xhr.abort()}send(){return this._prepareRequest(),this._attachXHRListeners(),this._sendRequest()}_prepareRequest(){const e=new XMLHttpRequest;e.open("POST",this._apiAddress),e.setRequestHeader("Authorization",this._token.value),e.responseType="json",this.xhr=e}_attachXHRListeners(){const e=this.xhr,t=e=>()=>this.fire("error",e);e.addEventListener("error",t("Network Error")),e.addEventListener("abort",t("Abort")),e.upload&&e.upload.addEventListener("progress",(e=>{e.lengthComputable&&this.fire("progress",{total:e.total,uploaded:e.loaded})})),e.addEventListener("load",(()=>{const t=e.status,r=e.response;if(t<200||t>299)return this.fire("error",r.message||r.error)}))}_sendRequest(){const e=new FormData,r=this.xhr;return e.append("file",this.file),new Promise(((s,o)=>{r.addEventListener("load",(()=>{const e=r.status,n=r.response;return e<200||e>299?n.message?o(new t.CKEditorError("fileuploader-uploading-data-failed",this,{message:n.message})):o(n.error):s(n)})),r.addEventListener("error",(()=>o(new Error("Network Error")))),r.addEventListener("abort",(()=>o(new Error("Abort")))),r.send(e)}))}}class l{constructor(e,r){if(!e)throw new t.CKEditorError("uploadgateway-missing-token",null);if(!r)throw new t.CKEditorError("uploadgateway-missing-api-address",null);this._token=e,this._apiAddress=r}upload(e){return new u(e,this._token,this._apiAddress)}}class h extends e.ContextPlugin{static get pluginName(){return"CloudServicesCore"}createToken(e,t){return new i(e,t)}createUploadGateway(e,t){return new l(e,t)}}class d extends e.ContextPlugin{constructor(){super(...arguments),this.token=null,this._tokens=new Map}static get pluginName(){return"CloudServices"}static get requires(){return[h]}async init(){const e=this.context.config.get("cloudServices")||{};for(const[t,r]of Object.entries(e))this[t]=r;if(!this.tokenUrl)return void(this.token=null);const t=this.context.plugins.get("CloudServicesCore");this.token=await t.createToken(this.tokenUrl).init(),this._tokens.set(this.tokenUrl,this.token)}async registerTokenUrl(e){if(this._tokens.has(e))return this.getTokenFor(e);const t=this.context.plugins.get("CloudServicesCore"),r=await t.createToken(e).init();return this._tokens.set(e,r),r}getTokenFor(e){const r=this._tokens.get(e);if(!r)throw new t.CKEditorError("cloudservices-token-not-registered",this);return r}destroy(){super.destroy();for(const e of this._tokens.values())e.destroy()}}})(),(window.CKEditor5=window.CKEditor5||{}).cloudServices=s})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-cloud-services",
3
- "version": "36.0.0",
3
+ "version": "37.0.0-alpha.0",
4
4
  "description": "CKEditor 5's Cloud Services integration layer.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -11,14 +11,15 @@
11
11
  ],
12
12
  "main": "src/index.js",
13
13
  "dependencies": {
14
- "ckeditor5": "^36.0.0"
14
+ "ckeditor5": "^37.0.0-alpha.0"
15
15
  },
16
16
  "devDependencies": {
17
- "@ckeditor/ckeditor5-core": "^36.0.0",
18
- "@ckeditor/ckeditor5-dev-utils": "^32.0.0",
19
- "@ckeditor/ckeditor5-editor-classic": "^36.0.0",
20
- "@ckeditor/ckeditor5-theme-lark": "^36.0.0",
21
- "@ckeditor/ckeditor5-utils": "^36.0.0",
17
+ "@ckeditor/ckeditor5-core": "^37.0.0-alpha.0",
18
+ "@ckeditor/ckeditor5-dev-utils": "^34.0.0",
19
+ "@ckeditor/ckeditor5-editor-classic": "^37.0.0-alpha.0",
20
+ "@ckeditor/ckeditor5-theme-lark": "^37.0.0-alpha.0",
21
+ "@ckeditor/ckeditor5-utils": "^37.0.0-alpha.0",
22
+ "typescript": "^4.8.4",
22
23
  "webpack": "^5.58.1",
23
24
  "webpack-cli": "^4.9.0"
24
25
  },
@@ -37,13 +38,17 @@
37
38
  },
38
39
  "files": [
39
40
  "lang",
40
- "src",
41
+ "src/**/*.js",
42
+ "src/**/*.d.ts",
41
43
  "theme",
42
44
  "build",
43
45
  "ckeditor5-metadata.json",
44
46
  "CHANGELOG.md"
45
47
  ],
46
48
  "scripts": {
47
- "dll:build": "webpack"
48
- }
49
+ "dll:build": "webpack",
50
+ "build": "tsc -p ./tsconfig.release.json",
51
+ "postversion": "npm run build"
52
+ },
53
+ "types": "src/index.d.ts"
49
54
  }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module cloud-services/cloudservices
7
+ */
8
+ import { ContextPlugin, type ContextPluginDependencies } from 'ckeditor5/src/core';
9
+ import type { CloudServicesConfig, TokenUrl } from './cloudservicesconfig';
10
+ import type { InitializedToken } from './token/token';
11
+ /**
12
+ * Plugin introducing the integration between CKEditor 5 and CKEditor Cloud Services .
13
+ *
14
+ * It initializes the token provider based on
15
+ * the {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig `config.cloudService`}.
16
+ */
17
+ export default class CloudServices extends ContextPlugin implements CloudServicesConfig {
18
+ /**
19
+ * The authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
20
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
21
+ */
22
+ readonly tokenUrl?: TokenUrl;
23
+ /**
24
+ * The URL to which the files should be uploaded.
25
+ */
26
+ readonly uploadUrl?: string;
27
+ /**
28
+ * The URL for web socket communication, used by the `RealTimeCollaborativeEditing` plugin. Every customer (organization in the CKEditor
29
+ * Ecosystem dashboard) has their own, unique URLs to communicate with CKEditor Cloud Services. The URL can be found in the
30
+ * CKEditor Ecosystem customer dashboard.
31
+ *
32
+ * Note: Unlike most plugins, `RealTimeCollaborativeEditing` is not included in any CKEditor 5 build and needs to be installed manually.
33
+ * Check [Collaboration overview](https://ckeditor.com/docs/ckeditor5/latest/features/collaboration/overview.html) for more details.
34
+ */
35
+ readonly webSocketUrl?: string;
36
+ /**
37
+ * An optional parameter used for integration with CKEditor Cloud Services when uploading the editor build to cloud services.
38
+ *
39
+ * Whenever the editor build or the configuration changes, this parameter should be set to a new, unique value to differentiate
40
+ * the new bundle (build + configuration) from the old ones.
41
+ */
42
+ readonly bundleVersion?: string;
43
+ /**
44
+ * Other plugins use this token for the authorization process. It handles token requesting and refreshing.
45
+ * Its value is `null` when {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} is not provided.
46
+ *
47
+ * @readonly
48
+ */
49
+ token: InitializedToken | null;
50
+ /**
51
+ * A map of token object instances keyed by the token URLs.
52
+ */
53
+ private readonly _tokens;
54
+ /**
55
+ * @inheritDoc
56
+ */
57
+ static get pluginName(): 'CloudServices';
58
+ /**
59
+ * @inheritDoc
60
+ */
61
+ static get requires(): ContextPluginDependencies;
62
+ /**
63
+ * @inheritDoc
64
+ */
65
+ init(): Promise<void>;
66
+ /**
67
+ * Registers an additional authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
68
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
69
+ *
70
+ * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
71
+ */
72
+ registerTokenUrl(tokenUrl: TokenUrl): Promise<InitializedToken>;
73
+ /**
74
+ * Returns an authentication token provider previously registered by {@link #registerTokenUrl}.
75
+ *
76
+ * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
77
+ */
78
+ getTokenFor(tokenUrl: TokenUrl): InitializedToken;
79
+ /**
80
+ * @inheritDoc
81
+ */
82
+ destroy(): void;
83
+ }
84
+ declare module '@ckeditor/ckeditor5-core' {
85
+ interface PluginsMap {
86
+ [CloudServices.pluginName]: CloudServices;
87
+ }
88
+ }
@@ -2,262 +2,102 @@
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module cloud-services/cloudservices
8
7
  */
9
-
10
8
  import { ContextPlugin } from 'ckeditor5/src/core';
11
9
  import { CKEditorError } from 'ckeditor5/src/utils';
12
10
  import CloudServicesCore from './cloudservicescore';
13
-
14
11
  /**
15
12
  * Plugin introducing the integration between CKEditor 5 and CKEditor Cloud Services .
16
13
  *
17
14
  * It initializes the token provider based on
18
- * the {@link module:cloud-services/cloudservices~CloudServicesConfig `config.cloudService`}.
19
- *
20
- * @extends module:core/contextplugin~ContextPlugin
15
+ * the {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig `config.cloudService`}.
21
16
  */
22
17
  export default class CloudServices extends ContextPlugin {
23
- /**
24
- * @inheritdoc
25
- */
26
- static get pluginName() {
27
- return 'CloudServices';
28
- }
29
-
30
- /**
31
- * @inheritDoc
32
- */
33
- static get requires() {
34
- return [ CloudServicesCore ];
35
- }
36
-
37
- /**
38
- * @inheritDoc
39
- */
40
- init() {
41
- const config = this.context.config;
42
-
43
- const options = config.get( 'cloudServices' ) || {};
44
-
45
- for ( const optionName in options ) {
46
- this[ optionName ] = options[ optionName ];
47
- }
48
-
49
- /**
50
- * A map of token object instances keyed by the token URLs.
51
- *
52
- * @private
53
- * @type {Map.<String, module:cloud-services/token~Token>}
54
- */
55
- this._tokens = new Map();
56
-
57
- /**
58
- * The authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
59
- * {@link module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl} for more details.
60
- *
61
- * @readonly
62
- * @member {String|Function|undefined} #tokenUrl
63
- */
64
-
65
- /**
66
- * The URL to which the files should be uploaded.
67
- *
68
- * @readonly
69
- * @member {String} #uploadUrl
70
- */
71
-
72
- /**
73
- * Other plugins use this token for the authorization process. It handles token requesting and refreshing.
74
- * Its value is `null` when {@link module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl} is not provided.
75
- *
76
- * @readonly
77
- * @member {module:cloud-services/token~Token|null} #token
78
- */
79
-
80
- if ( !this.tokenUrl ) {
81
- this.token = null;
82
-
83
- return;
84
- }
85
-
86
- this.token = this.context.plugins.get( 'CloudServicesCore' ).createToken( this.tokenUrl );
87
-
88
- this._tokens.set( this.tokenUrl, this.token );
89
-
90
- return this.token.init();
91
- }
92
-
93
- /**
94
- * Registers an additional authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
95
- * {@link module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl} for more details.
96
- *
97
- * @param {String|Function} tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
98
- * @returns {Promise.<module:cloud-services/token~Token>}
99
- */
100
- registerTokenUrl( tokenUrl ) {
101
- // Reuse the token instance in case of multiple features using the same token URL.
102
- if ( this._tokens.has( tokenUrl ) ) {
103
- return Promise.resolve( this.getTokenFor( tokenUrl ) );
104
- }
105
-
106
- const token = this.context.plugins.get( 'CloudServicesCore' ).createToken( tokenUrl );
107
-
108
- this._tokens.set( tokenUrl, token );
109
-
110
- return token.init();
111
- }
112
-
113
- /**
114
- * Returns an authentication token provider previously registered by {@link #registerTokenUrl}.
115
- *
116
- * @param {String|Function} tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
117
- * @returns {module:cloud-services/token~Token}
118
- */
119
- getTokenFor( tokenUrl ) {
120
- const token = this._tokens.get( tokenUrl );
121
-
122
- if ( !token ) {
123
- /**
124
- * The provided `tokenUrl` was not registered by {@link module:cloud-services/cloudservices~CloudServices#registerTokenUrl}.
125
- *
126
- * @error cloudservices-token-not-registered
127
- */
128
- throw new CKEditorError( 'cloudservices-token-not-registered', this );
129
- }
130
-
131
- return token;
132
- }
133
-
134
- /**
135
- * @inheritDoc
136
- */
137
- destroy() {
138
- super.destroy();
139
-
140
- for ( const token of this._tokens.values() ) {
141
- token.destroy();
142
- }
143
- }
18
+ constructor() {
19
+ super(...arguments);
20
+ /**
21
+ * Other plugins use this token for the authorization process. It handles token requesting and refreshing.
22
+ * Its value is `null` when {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} is not provided.
23
+ *
24
+ * @readonly
25
+ */
26
+ this.token = null;
27
+ /**
28
+ * A map of token object instances keyed by the token URLs.
29
+ */
30
+ this._tokens = new Map();
31
+ }
32
+ /**
33
+ * @inheritDoc
34
+ */
35
+ static get pluginName() {
36
+ return 'CloudServices';
37
+ }
38
+ /**
39
+ * @inheritDoc
40
+ */
41
+ static get requires() {
42
+ return [CloudServicesCore];
43
+ }
44
+ /**
45
+ * @inheritDoc
46
+ */
47
+ async init() {
48
+ const config = this.context.config;
49
+ const options = config.get('cloudServices') || {};
50
+ for (const [key, value] of Object.entries(options)) {
51
+ this[key] = value;
52
+ }
53
+ if (!this.tokenUrl) {
54
+ this.token = null;
55
+ return;
56
+ }
57
+ const cloudServicesCore = this.context.plugins.get('CloudServicesCore');
58
+ this.token = await cloudServicesCore.createToken(this.tokenUrl).init();
59
+ this._tokens.set(this.tokenUrl, this.token);
60
+ }
61
+ /**
62
+ * Registers an additional authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
63
+ * {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} for more details.
64
+ *
65
+ * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
66
+ */
67
+ async registerTokenUrl(tokenUrl) {
68
+ // Reuse the token instance in case of multiple features using the same token URL.
69
+ if (this._tokens.has(tokenUrl)) {
70
+ return this.getTokenFor(tokenUrl);
71
+ }
72
+ const cloudServicesCore = this.context.plugins.get('CloudServicesCore');
73
+ const token = await cloudServicesCore.createToken(tokenUrl).init();
74
+ this._tokens.set(tokenUrl, token);
75
+ return token;
76
+ }
77
+ /**
78
+ * Returns an authentication token provider previously registered by {@link #registerTokenUrl}.
79
+ *
80
+ * @param tokenUrl The authentication token URL for CKEditor Cloud Services or a callback to the token value promise.
81
+ */
82
+ getTokenFor(tokenUrl) {
83
+ const token = this._tokens.get(tokenUrl);
84
+ if (!token) {
85
+ /**
86
+ * The provided `tokenUrl` was not registered by {@link module:cloud-services/cloudservices~CloudServices#registerTokenUrl}.
87
+ *
88
+ * @error cloudservices-token-not-registered
89
+ */
90
+ throw new CKEditorError('cloudservices-token-not-registered', this);
91
+ }
92
+ return token;
93
+ }
94
+ /**
95
+ * @inheritDoc
96
+ */
97
+ destroy() {
98
+ super.destroy();
99
+ for (const token of this._tokens.values()) {
100
+ token.destroy();
101
+ }
102
+ }
144
103
  }
145
-
146
- /**
147
- * The configuration of CKEditor Cloud Services. Introduced by the {@link module:cloud-services/cloudservices~CloudServices} plugin.
148
- *
149
- * Read more in {@link module:cloud-services/cloudservices~CloudServicesConfig}.
150
- *
151
- * @member {module:cloud-services/cloudservices~CloudServicesConfig} module:core/editor/editorconfig~EditorConfig#cloudServices
152
- */
153
-
154
- /**
155
- * The configuration for all plugins using CKEditor Cloud Services.
156
- *
157
- * ClassicEditor
158
- * .create( document.querySelector( '#editor' ), {
159
- * cloudServices: {
160
- * tokenUrl: 'https://example.com/cs-token-endpoint',
161
- * uploadUrl: 'https://your-organization-id.cke-cs.com/easyimage/upload/'
162
- * }
163
- * } )
164
- * .then( ... )
165
- * .catch( ... );
166
- *
167
- * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
168
- *
169
- * @interface CloudServicesConfig
170
- */
171
-
172
- /**
173
- * A token URL or a token request function.
174
- *
175
- * As a string, it should be a URL to the security token endpoint in your application. The role of this endpoint is to securely authorize
176
- * the end users of your application to use [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services) only
177
- * if they should have access e.g. to upload files with {@glink @cs guides/easy-image/quick-start Easy Image} or to use the
178
- * {@glink @cs guides/collaboration/quick-start Collaboration} service.
179
- *
180
- * ClassicEditor
181
- * .create( document.querySelector( '#editor' ), {
182
- * cloudServices: {
183
- * tokenUrl: 'https://example.com/cs-token-endpoint',
184
- * ...
185
- * }
186
- * } )
187
- * .then( ... )
188
- * .catch( ... );
189
- *
190
- * As a function, it should provide a promise to the token value, so you can highly customize the token and provide your token URL endpoint.
191
- * By using this approach you can set your own headers for the request.
192
- *
193
- * ClassicEditor
194
- * .create( document.querySelector( '#editor' ), {
195
- * cloudServices: {
196
- * tokenUrl: () => new Promise( ( resolve, reject ) => {
197
- * const xhr = new XMLHttpRequest();
198
- *
199
- * xhr.open( 'GET', 'https://example.com/cs-token-endpoint' );
200
- *
201
- * xhr.addEventListener( 'load', () => {
202
- * const statusCode = xhr.status;
203
- * const xhrResponse = xhr.response;
204
- *
205
- * if ( statusCode < 200 || statusCode > 299 ) {
206
- * return reject( new Error( 'Cannot download new token!' ) );
207
- * }
208
- *
209
- * return resolve( xhrResponse );
210
- * } );
211
- *
212
- * xhr.addEventListener( 'error', () => reject( new Error( 'Network Error' ) ) );
213
- * xhr.addEventListener( 'abort', () => reject( new Error( 'Abort' ) ) );
214
- *
215
- * xhr.setRequestHeader( customHeader, customValue );
216
- *
217
- * xhr.send();
218
- * } ),
219
- * ...
220
- * }
221
- * } )
222
- *
223
- * You can find more information about token endpoints in the
224
- * {@glink @cs guides/easy-image/quick-start#create-token-endpoint Cloud Services - Quick start}
225
- * and {@glink @cs guides/security/token-endpoint Cloud Services - Token endpoint} documentation.
226
- *
227
- * Without a properly working token endpoint (token URL) CKEditor plugins will not be able to connect to CKEditor Cloud Services.
228
- *
229
- * @member {String|Function} module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl
230
- */
231
-
232
- /**
233
- * The endpoint URL for [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services) uploads.
234
- * This option must be set for Easy Image to work correctly.
235
- *
236
- * The upload URL is unique for each customer and can be found in the
237
- * [CKEditor Ecosystem customer dashboard](https://dashboard.ckeditor.com) after subscribing to the Easy Image service.
238
- * To learn how to start using Easy Image, check the {@glink @cs guides/easy-image/quick-start Easy Image - Quick start} documentation.
239
- *
240
- * Note: Make sure to also set the {@link module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl} configuration option.
241
- *
242
- * @member {String} module:cloud-services/cloudservices~CloudServicesConfig#uploadUrl
243
- */
244
-
245
- /**
246
- * The URL for web socket communication, used by the `RealTimeCollaborativeEditing` plugin. Every customer (organization in the CKEditor
247
- * Ecosystem dashboard) has their own, unique URLs to communicate with CKEditor Cloud Services. The URL can be found in the
248
- * CKEditor Ecosystem customer dashboard.
249
- *
250
- * Note: Unlike most plugins, `RealTimeCollaborativeEditing` is not included in any CKEditor 5 build and needs to be installed manually.
251
- * Check [Collaboration overview](https://ckeditor.com/docs/ckeditor5/latest/features/collaboration/overview.html) for more details.
252
- *
253
- * @member {String} module:cloud-services/cloudservices~CloudServicesConfig#webSocketUrl
254
- */
255
-
256
- /**
257
- * An optional parameter used for integration with CKEditor Cloud Services when uploading the editor build to cloud services.
258
- *
259
- * Whenever the editor build or the configuration changes, this parameter should be set to a new, unique value to differentiate
260
- * the new bundle (build + configuration) from the old ones.
261
- *
262
- * @member {String} module:cloud-services/cloudservices~CloudServicesConfig#bundleVersion
263
- */
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module cloud-services/cloudservicesconfig
7
+ */
8
+ /**
9
+ * Endpoint address to download the token or a callback that provides the token.
10
+ */
11
+ export type TokenUrl = string | (() => Promise<string>);
12
+ /**
13
+ * The configuration for all plugins using CKEditor Cloud Services.
14
+ *
15
+ * ```ts
16
+ * ClassicEditor
17
+ * .create( document.querySelector( '#editor' ), {
18
+ * cloudServices: {
19
+ * tokenUrl: 'https://example.com/cs-token-endpoint',
20
+ * uploadUrl: 'https://your-organization-id.cke-cs.com/easyimage/upload/'
21
+ * }
22
+ * } )
23
+ * .then( ... )
24
+ * .catch( ... );
25
+ * ```
26
+ *
27
+ * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
28
+ */
29
+ export interface CloudServicesConfig {
30
+ /**
31
+ * A token URL or a token request function.
32
+ *
33
+ * As a string, it should be a URL to the security token endpoint in your application.
34
+ * The role of this endpoint is to securely authorize
35
+ * the end users of your application to use [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services) only
36
+ * if they should have access e.g. to upload files with {@glink @cs guides/easy-image/quick-start Easy Image} or to use the
37
+ * {@glink @cs guides/collaboration/quick-start Collaboration} service.
38
+ *
39
+ * ```ts
40
+ * ClassicEditor
41
+ * .create( document.querySelector( '#editor' ), {
42
+ * cloudServices: {
43
+ * tokenUrl: 'https://example.com/cs-token-endpoint',
44
+ * ...
45
+ * }
46
+ * } )
47
+ * .then( ... )
48
+ * .catch( ... );
49
+ * ```
50
+ *
51
+ * As a function, it should provide a promise to the token value,
52
+ * so you can highly customize the token and provide your token URL endpoint.
53
+ * By using this approach you can set your own headers for the request.
54
+ *
55
+ * ```ts
56
+ * ClassicEditor
57
+ * .create( document.querySelector( '#editor' ), {
58
+ * cloudServices: {
59
+ * tokenUrl: () => new Promise( ( resolve, reject ) => {
60
+ * const xhr = new XMLHttpRequest();
61
+ *
62
+ * xhr.open( 'GET', 'https://example.com/cs-token-endpoint' );
63
+ *
64
+ * xhr.addEventListener( 'load', () => {
65
+ * const statusCode = xhr.status;
66
+ * const xhrResponse = xhr.response;
67
+ *
68
+ * if ( statusCode < 200 || statusCode > 299 ) {
69
+ * return reject( new Error( 'Cannot download new token!' ) );
70
+ * }
71
+ *
72
+ * return resolve( xhrResponse );
73
+ * } );
74
+ *
75
+ * xhr.addEventListener( 'error', () => reject( new Error( 'Network Error' ) ) );
76
+ * xhr.addEventListener( 'abort', () => reject( new Error( 'Abort' ) ) );
77
+ *
78
+ * xhr.setRequestHeader( customHeader, customValue );
79
+ *
80
+ * xhr.send();
81
+ * } ),
82
+ * ...
83
+ * }
84
+ * } )
85
+ * ```
86
+ *
87
+ * You can find more information about token endpoints in the
88
+ * {@glink @cs guides/easy-image/quick-start#create-token-endpoint Cloud Services - Quick start}
89
+ * and {@glink @cs guides/security/token-endpoint Cloud Services - Token endpoint} documentation.
90
+ *
91
+ * Without a properly working token endpoint (token URL) CKEditor plugins will not be able to connect to CKEditor Cloud Services.
92
+ */
93
+ tokenUrl?: TokenUrl;
94
+ /**
95
+ * The endpoint URL for [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services) uploads.
96
+ * This option must be set for Easy Image to work correctly.
97
+ *
98
+ * The upload URL is unique for each customer and can be found in the
99
+ * [CKEditor Ecosystem customer dashboard](https://dashboard.ckeditor.com) after subscribing to the Easy Image service.
100
+ * To learn how to start using Easy Image, check the {@glink @cs guides/easy-image/quick-start Easy Image - Quick start} documentation.
101
+ *
102
+ * Note: Make sure to also set the {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig#tokenUrl} configuration option.
103
+ */
104
+ uploadUrl?: string;
105
+ /**
106
+ * The URL for web socket communication, used by the `RealTimeCollaborativeEditing` plugin. Every customer (organization in the CKEditor
107
+ * Ecosystem dashboard) has their own, unique URLs to communicate with CKEditor Cloud Services. The URL can be found in the
108
+ * CKEditor Ecosystem customer dashboard.
109
+ *
110
+ * Note: Unlike most plugins, `RealTimeCollaborativeEditing` is not included in any CKEditor 5 build and needs to be installed manually.
111
+ * Check [Collaboration overview](https://ckeditor.com/docs/ckeditor5/latest/features/collaboration/overview.html) for more details.
112
+ */
113
+ webSocketUrl?: string;
114
+ /**
115
+ * An optional parameter used for integration with CKEditor Cloud Services when uploading the editor build to cloud services.
116
+ *
117
+ * Whenever the editor build or the configuration changes, this parameter should be set to a new, unique value to differentiate
118
+ * the new bundle (build + configuration) from the old ones.
119
+ */
120
+ bundleVersion?: string;
121
+ }
122
+ declare module '@ckeditor/ckeditor5-core' {
123
+ interface EditorConfig {
124
+ /**
125
+ * The configuration of CKEditor Cloud Services. Introduced by the {@link module:cloud-services/cloudservices~CloudServices} plugin.
126
+ *
127
+ * Read more in {@link module:cloud-services/cloudservicesconfig~CloudServicesConfig}.
128
+ */
129
+ cloudServices?: CloudServicesConfig;
130
+ }
131
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ export {};