@dotcms/client 0.0.1-beta.9 → 1.0.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.
- package/README.md +565 -153
- package/index.cjs.js +1418 -741
- package/index.esm.js +1418 -732
- package/internal.cjs.d.ts +1 -0
- package/internal.cjs.default.js +1 -0
- package/internal.cjs.js +85 -0
- package/internal.cjs.mjs +2 -0
- package/internal.esm.d.ts +1 -0
- package/internal.esm.js +83 -0
- package/package.json +14 -17
- package/src/index.d.ts +1 -8
- package/src/internal.d.ts +1 -0
- package/src/lib/client/client.d.ts +1 -29
- package/src/lib/client/content/builders/collection/collection.d.ts +1 -1
- package/src/lib/client/content/content-api.d.ts +3 -6
- package/src/lib/client/content/shared/types.d.ts +1 -42
- package/src/lib/client/navigation/navigation-api.d.ts +3 -20
- package/src/lib/client/page/page-api.d.ts +14 -84
- package/src/lib/utils/graphql/transforms.d.ts +2 -13
- package/src/lib/utils/index.d.ts +0 -1
- package/next.cjs.d.ts +0 -1
- package/next.cjs.default.js +0 -1
- package/next.cjs.js +0 -553
- package/next.cjs.mjs +0 -2
- package/next.esm.d.ts +0 -1
- package/next.esm.js +0 -551
- package/src/lib/client/models/types.d.ts +0 -516
- package/src/lib/deprecated/editor/listeners/listeners.d.ts +0 -45
- package/src/lib/deprecated/editor/models/client.model.d.ts +0 -111
- package/src/lib/deprecated/editor/models/editor.model.d.ts +0 -62
- package/src/lib/deprecated/editor/models/inline-event.model.d.ts +0 -9
- package/src/lib/deprecated/editor/models/listeners.model.d.ts +0 -55
- package/src/lib/deprecated/editor/sdk-editor-vtl.d.ts +0 -1
- package/src/lib/deprecated/editor/sdk-editor.d.ts +0 -92
- package/src/lib/deprecated/editor/utils/editor.utils.d.ts +0 -159
- package/src/lib/deprecated/editor/utils/traditional-vtl.utils.d.ts +0 -4
- package/src/lib/deprecated/sdk-js-client.d.ts +0 -276
- package/src/lib/utils/page/common-utils.d.ts +0 -33
- package/src/next.d.ts +0 -1
- package/src/types.d.ts +0 -2
- package/transforms.cjs.js +0 -1145
- package/transforms.esm.js +0 -1139
- package/types.cjs.d.ts +0 -1
- package/types.cjs.default.js +0 -1
- package/types.cjs.js +0 -2
- package/types.cjs.mjs +0 -2
- package/types.esm.d.ts +0 -1
- package/types.esm.js +0 -1
package/index.cjs.js
CHANGED
|
@@ -1,914 +1,1591 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var consola = require('consola');
|
|
4
|
+
var internal = require('./internal.cjs.js');
|
|
4
5
|
|
|
6
|
+
/******************************************************************************
|
|
7
|
+
Copyright (c) Microsoft Corporation.
|
|
8
|
+
|
|
9
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
10
|
+
purpose with or without fee is hereby granted.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
13
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
14
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
15
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
16
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
17
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
18
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
***************************************************************************** */
|
|
20
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
24
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
25
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
26
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
30
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
31
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
32
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
33
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
37
|
+
var e = new Error(message);
|
|
38
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Default variant identifier used in the application.
|
|
43
|
+
*/
|
|
5
44
|
/**
|
|
6
|
-
*
|
|
45
|
+
* Fields that should not be formatted when sanitizing the query.
|
|
46
|
+
* These fields are essential for maintaining the integrity of the content type.
|
|
47
|
+
*/
|
|
48
|
+
const CONTENT_TYPE_MAIN_FIELDS = ['live', 'variant', 'contentType', 'languageId'];
|
|
49
|
+
/**
|
|
50
|
+
* URL endpoint for the content API search functionality.
|
|
51
|
+
*/
|
|
52
|
+
const CONTENT_API_URL = '/api/content/_search';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @description
|
|
56
|
+
* Sanitizes the query for the given content type.
|
|
57
|
+
* It replaces the fields that are not content type fields with the correct format.
|
|
58
|
+
* Example: +field: -> +contentTypeVar.field:
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* const query = '+field: value';
|
|
64
|
+
* const contentType = 'contentTypeVar';
|
|
65
|
+
* const sanitizedQuery = sanitizeQueryForContentType(query, contentType); // Output: '+contentTypeVar.field: value'
|
|
66
|
+
* ```
|
|
7
67
|
*
|
|
8
68
|
* @export
|
|
9
|
-
* @
|
|
69
|
+
* @param {string} query - The query string to be sanitized.
|
|
70
|
+
* @param {string} contentType - The content type to be used for formatting the fields.
|
|
71
|
+
* @returns {string} The sanitized query string.
|
|
10
72
|
*/
|
|
11
|
-
|
|
12
|
-
(
|
|
73
|
+
function sanitizeQueryForContentType(query, contentType) {
|
|
74
|
+
return query.replace(/\+([^+:]*?):/g, (original, field) => {
|
|
75
|
+
return !CONTENT_TYPE_MAIN_FIELDS.includes(field) // Fields that are not content type fields
|
|
76
|
+
? `+${contentType}.${field}:` // Should have this format: +contentTypeVar.field:
|
|
77
|
+
: original; // Return the field if it is a content type field
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
var _Field_query;
|
|
82
|
+
/**
|
|
83
|
+
* The `Field` class is used to build a query with a specific field.
|
|
84
|
+
* A Lucene Field is a key used to search for a specific value in a document.
|
|
85
|
+
*
|
|
86
|
+
* @export
|
|
87
|
+
* @class Field
|
|
88
|
+
*/
|
|
89
|
+
class Field {
|
|
13
90
|
/**
|
|
14
|
-
*
|
|
91
|
+
* Creates an instance of the `Field` class.
|
|
92
|
+
*
|
|
93
|
+
* @param {string} query - The initial query string.
|
|
15
94
|
*/
|
|
16
|
-
|
|
95
|
+
constructor(query) {
|
|
96
|
+
this.query = query;
|
|
97
|
+
_Field_query.set(this, '');
|
|
98
|
+
__classPrivateFieldSet(this, _Field_query, this.query, "f");
|
|
99
|
+
}
|
|
17
100
|
/**
|
|
18
|
-
*
|
|
101
|
+
* Appends a term to the query that should be included in the search.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const field = new Field("+myField");
|
|
106
|
+
* field.equals("myValue");
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @param {string} term - The term that should be included in the search.
|
|
110
|
+
* @return {Equals} - An instance of `Equals`.
|
|
111
|
+
* @memberof Field
|
|
19
112
|
*/
|
|
20
|
-
|
|
113
|
+
equals(term) {
|
|
114
|
+
return buildEquals(__classPrivateFieldGet(this, _Field_query, "f"), term);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
_Field_query = new WeakMap();
|
|
118
|
+
|
|
119
|
+
var _NotOperand_query;
|
|
120
|
+
/**
|
|
121
|
+
* 'NotOperand' Is a Typescript class that provides the ability to use the NOT operand in the lucene query string.
|
|
122
|
+
*
|
|
123
|
+
* @export
|
|
124
|
+
* @class NotOperand
|
|
125
|
+
*/
|
|
126
|
+
class NotOperand {
|
|
127
|
+
constructor(query) {
|
|
128
|
+
this.query = query;
|
|
129
|
+
_NotOperand_query.set(this, '');
|
|
130
|
+
__classPrivateFieldSet(this, _NotOperand_query, this.query, "f");
|
|
131
|
+
}
|
|
21
132
|
/**
|
|
22
|
-
*
|
|
133
|
+
* This method appends to the query a term that should be included in the search.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const notOperand = new NotOperand("+myField");
|
|
138
|
+
* notOperand.equals("myValue");
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @param {string} term - The term that should be included in the search.
|
|
142
|
+
* @return {*} {Equals} - An instance of Equals.
|
|
143
|
+
* @memberof NotOperand
|
|
23
144
|
*/
|
|
24
|
-
|
|
145
|
+
equals(term) {
|
|
146
|
+
return buildEquals(__classPrivateFieldGet(this, _NotOperand_query, "f"), term);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
_NotOperand_query = new WeakMap();
|
|
150
|
+
|
|
151
|
+
var _Operand_query;
|
|
152
|
+
/**
|
|
153
|
+
* 'Operand' Is a Typescript class that provides the ability to use operands in the lucene query string.}
|
|
154
|
+
* An operand is a logical operator used to join two or more conditions in a query.
|
|
155
|
+
*
|
|
156
|
+
* @export
|
|
157
|
+
* @class Operand
|
|
158
|
+
*/
|
|
159
|
+
class Operand {
|
|
160
|
+
constructor(query) {
|
|
161
|
+
this.query = query;
|
|
162
|
+
_Operand_query.set(this, '');
|
|
163
|
+
__classPrivateFieldSet(this, _Operand_query, this.query, "f");
|
|
164
|
+
}
|
|
25
165
|
/**
|
|
26
|
-
*
|
|
166
|
+
* This method appends to the query a term that should be excluded in the search.
|
|
167
|
+
*
|
|
168
|
+
* Ex: "-myValue"
|
|
169
|
+
*
|
|
170
|
+
* @param {string} field - The field that should be excluded in the search.
|
|
171
|
+
* @return {*} {Field} - An instance of a Lucene Field. A field is a key used to search for a specific value in a document.
|
|
172
|
+
* @memberof Operand
|
|
27
173
|
*/
|
|
28
|
-
|
|
174
|
+
excludeField(field) {
|
|
175
|
+
return buildExcludeField(__classPrivateFieldGet(this, _Operand_query, "f"), field);
|
|
176
|
+
}
|
|
29
177
|
/**
|
|
30
|
-
*
|
|
178
|
+
* This method appends to the query a field that should be included in the search.
|
|
179
|
+
*
|
|
180
|
+
* Ex: "+myField:"
|
|
181
|
+
*
|
|
182
|
+
* @param {string} field - The field that should be included in the search.
|
|
183
|
+
* @return {*} {Field} - An instance of a Lucene Field. A field is a key used to search for a specific value in a document.
|
|
184
|
+
* @memberof Operand
|
|
31
185
|
*/
|
|
32
|
-
|
|
186
|
+
field(field) {
|
|
187
|
+
return buildField(__classPrivateFieldGet(this, _Operand_query, "f"), field);
|
|
188
|
+
}
|
|
33
189
|
/**
|
|
34
|
-
*
|
|
190
|
+
* This method appends to the query a term that should be included in the search.
|
|
191
|
+
*
|
|
192
|
+
* Ex: myValue or "My value"
|
|
193
|
+
*
|
|
194
|
+
* @param {string} term - The term that should be included in the search.
|
|
195
|
+
* @return {*} {Equals} - An instance of Equals.
|
|
196
|
+
* @memberof Operand
|
|
35
197
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
198
|
+
equals(term) {
|
|
199
|
+
return buildEquals(__classPrivateFieldGet(this, _Operand_query, "f"), term);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
_Operand_query = new WeakMap();
|
|
38
203
|
|
|
39
204
|
/**
|
|
40
|
-
*
|
|
205
|
+
* Enum for common Operands
|
|
41
206
|
*
|
|
42
207
|
* @export
|
|
43
|
-
* @
|
|
44
|
-
* @return {ContainerBound[]} An array of objects containing the bounding information for each page element.
|
|
45
|
-
* @example
|
|
46
|
-
* ```ts
|
|
47
|
-
* const containers = document.querySelectorAll('.container');
|
|
48
|
-
* const bounds = getPageElementBound(containers);
|
|
49
|
-
* console.log(bounds);
|
|
50
|
-
* ```
|
|
208
|
+
* @enum {number}
|
|
51
209
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
y: containerRect.y,
|
|
59
|
-
width: containerRect.width,
|
|
60
|
-
height: containerRect.height,
|
|
61
|
-
payload: JSON.stringify({
|
|
62
|
-
container: getContainerData(container)
|
|
63
|
-
}),
|
|
64
|
-
contentlets: getContentletsBound(containerRect, contentlets)
|
|
65
|
-
};
|
|
66
|
-
});
|
|
67
|
-
}
|
|
210
|
+
var OPERAND;
|
|
211
|
+
(function (OPERAND) {
|
|
212
|
+
OPERAND["OR"] = "OR";
|
|
213
|
+
OPERAND["AND"] = "AND";
|
|
214
|
+
OPERAND["NOT"] = "NOT";
|
|
215
|
+
})(OPERAND || (OPERAND = {}));
|
|
68
216
|
/**
|
|
69
|
-
*
|
|
217
|
+
* This function removes extra spaces from a string.
|
|
70
218
|
*
|
|
71
|
-
* @export
|
|
72
|
-
* @param {DOMRect} containerRect - The bounding rectangle of the container.
|
|
73
|
-
* @param {HTMLDivElement[]} contentlets - An array of HTMLDivElement representing the contentlets.
|
|
74
|
-
* @return {ContentletBound[]} An array of objects containing the bounding information for each contentlet.
|
|
75
219
|
* @example
|
|
76
220
|
* ```ts
|
|
77
|
-
*
|
|
78
|
-
* const contentlets = container.querySelectorAll('.contentlet');
|
|
79
|
-
* const bounds = getContentletsBound(containerRect, contentlets);
|
|
80
|
-
* console.log(bounds); // Element bounds within the container
|
|
221
|
+
* sanitizeQuery(" my query "); // Output: "my query"
|
|
81
222
|
* ```
|
|
223
|
+
*
|
|
224
|
+
* @export
|
|
225
|
+
* @param {string} str
|
|
226
|
+
* @return {*} {string}
|
|
82
227
|
*/
|
|
83
|
-
function
|
|
84
|
-
return
|
|
85
|
-
const contentletRect = contentlet.getBoundingClientRect();
|
|
86
|
-
return {
|
|
87
|
-
x: 0,
|
|
88
|
-
y: contentletRect.y - containerRect.y,
|
|
89
|
-
width: contentletRect.width,
|
|
90
|
-
height: contentletRect.height,
|
|
91
|
-
payload: JSON.stringify({
|
|
92
|
-
container: contentlet.dataset?.['dotContainer']
|
|
93
|
-
? JSON.parse(contentlet.dataset?.['dotContainer'])
|
|
94
|
-
: getClosestContainerData(contentlet),
|
|
95
|
-
contentlet: {
|
|
96
|
-
identifier: contentlet.dataset?.['dotIdentifier'],
|
|
97
|
-
title: contentlet.dataset?.['dotTitle'],
|
|
98
|
-
inode: contentlet.dataset?.['dotInode'],
|
|
99
|
-
contentType: contentlet.dataset?.['dotType']
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
};
|
|
103
|
-
});
|
|
228
|
+
function sanitizeQuery(str) {
|
|
229
|
+
return str.replace(/\s{2,}/g, ' ').trim();
|
|
104
230
|
}
|
|
105
231
|
/**
|
|
106
|
-
*
|
|
232
|
+
* This function sanitizes a term by adding quotes if it contains spaces.
|
|
233
|
+
* In lucene, a term with spaces should be enclosed in quotes.
|
|
107
234
|
*
|
|
108
|
-
* @export
|
|
109
|
-
* @param {HTMLElement} container - The container element.
|
|
110
|
-
* @return {object} An object containing the container data.
|
|
111
235
|
* @example
|
|
112
236
|
* ```ts
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* console.log(data);
|
|
237
|
+
* sanitizePhrases(`my term`); // Output: `"my term"`
|
|
238
|
+
* sanitizePhrases(`myterm`); // Output: `myterm`
|
|
116
239
|
* ```
|
|
240
|
+
*
|
|
241
|
+
* @export
|
|
242
|
+
* @param {string} term
|
|
243
|
+
* @return {*} {string}
|
|
117
244
|
*/
|
|
118
|
-
function
|
|
119
|
-
return {
|
|
120
|
-
acceptTypes: container.dataset?.['dotAcceptTypes'] || '',
|
|
121
|
-
identifier: container.dataset?.['dotIdentifier'] || '',
|
|
122
|
-
maxContentlets: container.dataset?.['maxContentlets'] || '',
|
|
123
|
-
uuid: container.dataset?.['dotUuid'] || ''
|
|
124
|
-
};
|
|
245
|
+
function sanitizePhrases(term) {
|
|
246
|
+
return term.includes(' ') ? `'${term}'` : term;
|
|
125
247
|
}
|
|
126
248
|
/**
|
|
127
|
-
*
|
|
249
|
+
* This function builds a term to be used in a lucene query.
|
|
250
|
+
* We need to sanitize the term before adding it to the query.
|
|
128
251
|
*
|
|
129
|
-
* @export
|
|
130
|
-
* @param {Element} element - The contentlet element.
|
|
131
|
-
* @return {object | null} An object containing the closest container data or null if no container is found.
|
|
132
252
|
* @example
|
|
133
253
|
* ```ts
|
|
134
|
-
* const
|
|
135
|
-
* const data = getClosestContainerData(contentlet);
|
|
136
|
-
* console.log(data);
|
|
254
|
+
* const equals = buildEquals("+myField: ", "myValue"); // Current query: "+myField: myValue"
|
|
137
255
|
* ```
|
|
138
|
-
*/
|
|
139
|
-
function getClosestContainerData(element) {
|
|
140
|
-
// Find the closest ancestor element with data-dot-object="container" attribute
|
|
141
|
-
const container = element.closest('[data-dot-object="container"]');
|
|
142
|
-
// If a container element is found
|
|
143
|
-
if (container) {
|
|
144
|
-
// Return the dataset of the container element
|
|
145
|
-
return getContainerData(container);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
// If no container element is found, return null
|
|
149
|
-
console.warn('No container found for the contentlet');
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Find the closest contentlet element based on HTMLElement.
|
|
155
256
|
*
|
|
156
257
|
* @export
|
|
157
|
-
* @param {
|
|
158
|
-
* @
|
|
159
|
-
* @
|
|
160
|
-
* const element = document.querySelector('.some-element');
|
|
161
|
-
* const contentlet = findDotElement(element);
|
|
162
|
-
* console.log(contentlet);
|
|
258
|
+
* @param {string} query
|
|
259
|
+
* @param {string} term
|
|
260
|
+
* @return {*} {Equals}
|
|
163
261
|
*/
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (element?.dataset?.['dotObject'] === 'contentlet' ||
|
|
168
|
-
(element?.dataset?.['dotObject'] === 'container' && element.children.length === 0)) {
|
|
169
|
-
return element;
|
|
170
|
-
}
|
|
171
|
-
return findDotElement(element?.['parentElement']);
|
|
262
|
+
function buildEquals(query, term) {
|
|
263
|
+
const newQuery = query + sanitizePhrases(term);
|
|
264
|
+
return new Equals(newQuery);
|
|
172
265
|
}
|
|
173
266
|
/**
|
|
174
|
-
*
|
|
267
|
+
* This function builds a term to be used in a lucene query.
|
|
268
|
+
* We need to sanitize the raw query before adding it to the query.
|
|
175
269
|
*
|
|
176
|
-
* @export
|
|
177
|
-
* @param {HTMLElement} target - The target element to search within.
|
|
178
|
-
* @return {Array<{ inode: string, name: string }> | null} An array of objects containing VTL data or null if none found.
|
|
179
270
|
* @example
|
|
180
271
|
* ```ts
|
|
181
|
-
* const
|
|
182
|
-
* const
|
|
183
|
-
* console.log(vtlData);
|
|
272
|
+
* const query = "+myField: myValue";
|
|
273
|
+
* const field = buildRawEquals(query, "-myField2: myValue2"); // Current query: "+myField: myValue -myField2: myValue"
|
|
184
274
|
* ```
|
|
275
|
+
*
|
|
276
|
+
* @export
|
|
277
|
+
* @param {string} query
|
|
278
|
+
* @param {string} raw
|
|
279
|
+
* @return {*} {Equals}
|
|
185
280
|
*/
|
|
186
|
-
function
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
return Array.from(vltElements).map((vltElement) => {
|
|
192
|
-
return {
|
|
193
|
-
inode: vltElement.dataset?.['dotInode'],
|
|
194
|
-
name: vltElement.dataset?.['dotUrl']
|
|
195
|
-
};
|
|
196
|
-
});
|
|
281
|
+
function buildRawEquals(query, raw) {
|
|
282
|
+
const newQuery = query + ` ${raw}`;
|
|
283
|
+
return new Equals(sanitizeQuery(newQuery));
|
|
197
284
|
}
|
|
198
285
|
/**
|
|
199
|
-
*
|
|
286
|
+
* This function builds a field to be used in a lucene query.
|
|
287
|
+
* We need to format the field before adding it to the query.
|
|
200
288
|
*
|
|
201
|
-
* @export
|
|
202
|
-
* @return {boolean} True if the scroll position is at the bottom, otherwise false.
|
|
203
289
|
* @example
|
|
204
290
|
* ```ts
|
|
205
|
-
*
|
|
206
|
-
* console.log('Scrolled to the bottom');
|
|
207
|
-
* }
|
|
291
|
+
* const field = buildField("+myField: ", "myValue"); // Current query: "+myField: myValue"
|
|
208
292
|
* ```
|
|
209
|
-
*/
|
|
210
|
-
function scrollIsInBottom() {
|
|
211
|
-
const documentHeight = document.documentElement.scrollHeight;
|
|
212
|
-
const viewportHeight = window.innerHeight;
|
|
213
|
-
const scrollY = window.scrollY;
|
|
214
|
-
return scrollY + viewportHeight >= documentHeight;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Represents an array of DotCMSPageEditorSubscription objects.
|
|
219
|
-
* Used to store the subscriptions for the editor and unsubscribe later.
|
|
220
|
-
*/
|
|
221
|
-
const subscriptions = [];
|
|
222
|
-
/**
|
|
223
|
-
* Sets the bounds of the containers in the editor.
|
|
224
|
-
* Retrieves the containers from the DOM and sends their position data to the editor.
|
|
225
|
-
* @private
|
|
226
|
-
* @memberof DotCMSPageEditor
|
|
227
|
-
*/
|
|
228
|
-
function setBounds() {
|
|
229
|
-
const containers = Array.from(document.querySelectorAll('[data-dot-object="container"]'));
|
|
230
|
-
const positionData = getPageElementBound(containers);
|
|
231
|
-
postMessageToEditor({
|
|
232
|
-
action: exports.CLIENT_ACTIONS.SET_BOUNDS,
|
|
233
|
-
payload: positionData
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Listens for editor messages and performs corresponding actions based on the received message.
|
|
238
293
|
*
|
|
239
|
-
* @
|
|
240
|
-
* @
|
|
294
|
+
* @export
|
|
295
|
+
* @param {string} query
|
|
296
|
+
* @param {string} field
|
|
297
|
+
* @return {*} {Field}
|
|
241
298
|
*/
|
|
242
|
-
function
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
[exports.NOTIFY_CLIENT.UVE_RELOAD_PAGE]: () => {
|
|
246
|
-
window.location.reload();
|
|
247
|
-
},
|
|
248
|
-
[exports.NOTIFY_CLIENT.UVE_REQUEST_BOUNDS]: () => {
|
|
249
|
-
setBounds();
|
|
250
|
-
},
|
|
251
|
-
[exports.NOTIFY_CLIENT.UVE_SCROLL_INSIDE_IFRAME]: () => {
|
|
252
|
-
const direction = event.data.direction;
|
|
253
|
-
if ((window.scrollY === 0 && direction === 'up') ||
|
|
254
|
-
(scrollIsInBottom() && direction === 'down')) {
|
|
255
|
-
// If the iframe scroll is at the top or bottom, do not send anything.
|
|
256
|
-
// This avoids losing the scrollend event.
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const scrollY = direction === 'up' ? -120 : 120;
|
|
260
|
-
window.scrollBy({ left: 0, top: scrollY, behavior: 'smooth' });
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
ACTIONS_NOTIFICATION[event.data.name]?.();
|
|
264
|
-
};
|
|
265
|
-
window.addEventListener('message', messageCallback);
|
|
266
|
-
subscriptions.push({
|
|
267
|
-
type: 'listener',
|
|
268
|
-
event: 'message',
|
|
269
|
-
callback: messageCallback
|
|
270
|
-
});
|
|
299
|
+
function buildField(query, field) {
|
|
300
|
+
const newQuery = query + ` +${field}:`;
|
|
301
|
+
return new Field(newQuery);
|
|
271
302
|
}
|
|
272
303
|
/**
|
|
273
|
-
*
|
|
304
|
+
* This function builds an exclude field to be used in a lucene query.
|
|
305
|
+
* We need to format the field before adding it to the query.
|
|
274
306
|
*
|
|
275
|
-
* @
|
|
276
|
-
*
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const foundElement = findDotElement(event.target);
|
|
281
|
-
if (!foundElement)
|
|
282
|
-
return;
|
|
283
|
-
const { x, y, width, height } = foundElement.getBoundingClientRect();
|
|
284
|
-
const isContainer = foundElement.dataset?.['dotObject'] === 'container';
|
|
285
|
-
const contentletForEmptyContainer = {
|
|
286
|
-
identifier: 'TEMP_EMPTY_CONTENTLET',
|
|
287
|
-
title: 'TEMP_EMPTY_CONTENTLET',
|
|
288
|
-
contentType: 'TEMP_EMPTY_CONTENTLET_TYPE',
|
|
289
|
-
inode: 'TEMPY_EMPTY_CONTENTLET_INODE',
|
|
290
|
-
widgetTitle: 'TEMP_EMPTY_CONTENTLET',
|
|
291
|
-
baseType: 'TEMP_EMPTY_CONTENTLET',
|
|
292
|
-
onNumberOfPages: 1
|
|
293
|
-
};
|
|
294
|
-
const contentlet = {
|
|
295
|
-
identifier: foundElement.dataset?.['dotIdentifier'],
|
|
296
|
-
title: foundElement.dataset?.['dotTitle'],
|
|
297
|
-
inode: foundElement.dataset?.['dotInode'],
|
|
298
|
-
contentType: foundElement.dataset?.['dotType'],
|
|
299
|
-
baseType: foundElement.dataset?.['dotBasetype'],
|
|
300
|
-
widgetTitle: foundElement.dataset?.['dotWidgetTitle'],
|
|
301
|
-
onNumberOfPages: foundElement.dataset?.['dotOnNumberOfPages']
|
|
302
|
-
};
|
|
303
|
-
const vtlFiles = findVTLData(foundElement);
|
|
304
|
-
const contentletPayload = {
|
|
305
|
-
container:
|
|
306
|
-
// Here extract dot-container from contentlet if it is Headless
|
|
307
|
-
// or search in parent container if it is VTL
|
|
308
|
-
foundElement.dataset?.['dotContainer']
|
|
309
|
-
? JSON.parse(foundElement.dataset?.['dotContainer'])
|
|
310
|
-
: getClosestContainerData(foundElement),
|
|
311
|
-
contentlet: isContainer ? contentletForEmptyContainer : contentlet,
|
|
312
|
-
vtlFiles
|
|
313
|
-
};
|
|
314
|
-
postMessageToEditor({
|
|
315
|
-
action: exports.CLIENT_ACTIONS.SET_CONTENTLET,
|
|
316
|
-
payload: {
|
|
317
|
-
x,
|
|
318
|
-
y,
|
|
319
|
-
width,
|
|
320
|
-
height,
|
|
321
|
-
payload: contentletPayload
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
};
|
|
325
|
-
document.addEventListener('pointermove', pointerMoveCallback);
|
|
326
|
-
subscriptions.push({
|
|
327
|
-
type: 'listener',
|
|
328
|
-
event: 'pointermove',
|
|
329
|
-
callback: pointerMoveCallback
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Attaches a scroll event listener to the window
|
|
334
|
-
* and sends a message to the editor when the window is scrolled.
|
|
307
|
+
* @example
|
|
308
|
+
* ```ts
|
|
309
|
+
* const query = "+myField: myValue";
|
|
310
|
+
* const field = buildExcludeField(query, "myField2"); // Current query: "+myField: myValue -myField2:"
|
|
311
|
+
* ```
|
|
335
312
|
*
|
|
336
|
-
* @
|
|
337
|
-
* @
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const scrollCallback = () => {
|
|
341
|
-
postMessageToEditor({
|
|
342
|
-
action: exports.CLIENT_ACTIONS.IFRAME_SCROLL
|
|
343
|
-
});
|
|
344
|
-
// In case it doesn't have a dotUVE object, we create it with the initial values.
|
|
345
|
-
window.dotUVE = {
|
|
346
|
-
...(window.dotUVE ?? INITIAL_DOT_UVE),
|
|
347
|
-
lastScrollYPosition: window.scrollY
|
|
348
|
-
};
|
|
349
|
-
};
|
|
350
|
-
const scrollEndCallback = () => {
|
|
351
|
-
postMessageToEditor({
|
|
352
|
-
action: exports.CLIENT_ACTIONS.IFRAME_SCROLL_END
|
|
353
|
-
});
|
|
354
|
-
};
|
|
355
|
-
window.addEventListener('scroll', scrollCallback);
|
|
356
|
-
window.addEventListener('scrollend', scrollEndCallback);
|
|
357
|
-
subscriptions.push({
|
|
358
|
-
type: 'listener',
|
|
359
|
-
event: 'scroll',
|
|
360
|
-
callback: scrollEndCallback
|
|
361
|
-
});
|
|
362
|
-
subscriptions.push({
|
|
363
|
-
type: 'listener',
|
|
364
|
-
event: 'scroll',
|
|
365
|
-
callback: scrollCallback
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Sends a message to the editor to get the page data.
|
|
370
|
-
* @param {string} pathname - The pathname of the page.
|
|
371
|
-
* @private
|
|
372
|
-
* @memberof DotCMSPageEditor
|
|
313
|
+
* @export
|
|
314
|
+
* @param {string} query
|
|
315
|
+
* @param {string} field
|
|
316
|
+
* @return {*} {Field}
|
|
373
317
|
*/
|
|
374
|
-
function
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
payload: {
|
|
378
|
-
pathname
|
|
379
|
-
}
|
|
380
|
-
});
|
|
318
|
+
function buildExcludeField(query, field) {
|
|
319
|
+
const newQuery = query + ` -${field}:`;
|
|
320
|
+
return new Field(newQuery);
|
|
381
321
|
}
|
|
382
|
-
|
|
383
322
|
/**
|
|
384
|
-
*
|
|
323
|
+
* This function builds an operand to be used in a lucene query.
|
|
324
|
+
* We need to format the operand before adding it to the query.
|
|
385
325
|
*
|
|
386
|
-
* @param {string} pathname - The pathname to update the navigation with.
|
|
387
|
-
* @memberof DotCMSPageEditor
|
|
388
326
|
* @example
|
|
389
|
-
*
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
* You can use this function to edit a contentlet in the editor.
|
|
401
|
-
*
|
|
402
|
-
* Calling this function inside the editor, will prompt the UVE to open a dialog to edit the contentlet.
|
|
403
|
-
*
|
|
327
|
+
* <caption>E.g. Using the AND operand</caption>
|
|
328
|
+
* ```ts
|
|
329
|
+
* const query = "+myField: myValue";
|
|
330
|
+
* const field = buildOperand(query, OPERAND.AND); // Current query: "+myField: myValue AND"
|
|
331
|
+
* ```
|
|
332
|
+
* @example
|
|
333
|
+
* <caption>E.g. Using the OR operand</caption>
|
|
334
|
+
* ```ts
|
|
335
|
+
* const query = "+myField: myValue";
|
|
336
|
+
* const field = buildOperand(query, OPERAND.OR); // Current query: "+myField: myValue OR"
|
|
337
|
+
* ```
|
|
404
338
|
* @export
|
|
405
|
-
* @
|
|
406
|
-
* @param {
|
|
339
|
+
* @param {string} query
|
|
340
|
+
* @param {OPERAND} operand
|
|
341
|
+
* @return {*} {Operand}
|
|
407
342
|
*/
|
|
408
|
-
function
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
payload: contentlet
|
|
412
|
-
});
|
|
343
|
+
function buildOperand(query, operand) {
|
|
344
|
+
const newQuery = query + ` ${operand} `;
|
|
345
|
+
return new Operand(newQuery);
|
|
413
346
|
}
|
|
414
347
|
/**
|
|
415
|
-
*
|
|
348
|
+
* This function builds a NOT operand to be used in a lucene query.
|
|
349
|
+
* We need to format the operand before adding it to the query.
|
|
416
350
|
*
|
|
417
|
-
* @
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
* * @example
|
|
423
|
-
* ```html
|
|
424
|
-
* <div onclick="initInlineEditing('BLOCK_EDITOR', { inode, languageId, contentType, fieldName, content })">
|
|
425
|
-
* ${My Content}
|
|
426
|
-
* </div>
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* const query = "+myField: myValue";
|
|
354
|
+
* const field = buildNotOperand(query); // Current query: "+myField: myValue NOT"
|
|
427
355
|
* ```
|
|
428
|
-
*/
|
|
429
|
-
function initInlineEditing(type, data) {
|
|
430
|
-
postMessageToEditor({
|
|
431
|
-
action: exports.CLIENT_ACTIONS.INIT_INLINE_EDITING,
|
|
432
|
-
payload: {
|
|
433
|
-
type,
|
|
434
|
-
data
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
/*
|
|
439
|
-
* Reorders the menu based on the provided configuration.
|
|
440
|
-
*
|
|
441
|
-
* @param {ReorderMenuConfig} [config] - Optional configuration for reordering the menu.
|
|
442
|
-
* @param {number} [config.startLevel=1] - The starting level of the menu to reorder.
|
|
443
|
-
* @param {number} [config.depth=2] - The depth of the menu to reorder.
|
|
444
356
|
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
357
|
+
* @export
|
|
358
|
+
* @param {string} query
|
|
359
|
+
* @return {*} {NotOperand}
|
|
447
360
|
*/
|
|
448
|
-
function
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
action: exports.CLIENT_ACTIONS.REORDER_MENU,
|
|
452
|
-
payload: { startLevel, depth }
|
|
453
|
-
});
|
|
361
|
+
function buildNotOperand(query) {
|
|
362
|
+
const newQuery = query + ` ${OPERAND.NOT} `;
|
|
363
|
+
return new NotOperand(newQuery);
|
|
454
364
|
}
|
|
365
|
+
|
|
366
|
+
var _Equals_query;
|
|
455
367
|
/**
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
* Checks if the code is running inside the DotCMS Universal Visual Editor (UVE).
|
|
368
|
+
* 'Equal' Is a Typescript class that provides the ability to use terms in the lucene query string.
|
|
369
|
+
* A term is a value used to search for a specific value in a document. It can be a word or a phrase.
|
|
459
370
|
*
|
|
460
|
-
*
|
|
461
|
-
* 1. If window is defined (for SSR environments)
|
|
462
|
-
* 2. If the page is not in preview mode
|
|
463
|
-
* 3. If the current window is embedded in a parent frame
|
|
371
|
+
* Ex: myValue or "My Value"
|
|
464
372
|
*
|
|
465
|
-
* @
|
|
466
|
-
* @
|
|
467
|
-
* ```ts
|
|
468
|
-
* // Check if code is running in editor before initializing editor-specific features
|
|
469
|
-
* if (isInsideEditor()) {
|
|
470
|
-
* initEditor(config);
|
|
471
|
-
* } else {
|
|
472
|
-
* initStandaloneMode();
|
|
473
|
-
* }
|
|
474
|
-
* ```
|
|
373
|
+
* @export
|
|
374
|
+
* @class Equal
|
|
475
375
|
*/
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
376
|
+
class Equals {
|
|
377
|
+
constructor(query) {
|
|
378
|
+
this.query = query;
|
|
379
|
+
_Equals_query.set(this, '');
|
|
380
|
+
__classPrivateFieldSet(this, _Equals_query, this.query, "f");
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* This method appends to the query a term that should be excluded in the search.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```ts
|
|
387
|
+
* const equals = new Equals("+myField: myValue");
|
|
388
|
+
* equals.excludeField("myField2").equals("myValue2"); // Current query: "+myField: myValue -myField2: myValue2"
|
|
389
|
+
* ```
|
|
390
|
+
*
|
|
391
|
+
* @param {string} field - The field that should be excluded in the search.
|
|
392
|
+
* @return {*} {Field} - An instance of a Lucene Field. A field is a key used to search for a specific value in a document.
|
|
393
|
+
* @memberof Equal
|
|
394
|
+
*/
|
|
395
|
+
excludeField(field) {
|
|
396
|
+
return buildExcludeField(__classPrivateFieldGet(this, _Equals_query, "f"), field);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* This method appends to the query a field that should be included in the search.
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```ts
|
|
403
|
+
* const equals = new Equals("+myField: myValue");
|
|
404
|
+
* equals.field("myField2").equals("myValue2"); // Current query: "+myField: myValue +myField2: myValue2"
|
|
405
|
+
*```
|
|
406
|
+
* @param {string} field - The field that should be included in the search.
|
|
407
|
+
* @return {*} {Field} - An instance of a Lucene Field. A field is a key used to search for a specific value in a document.
|
|
408
|
+
* @memberof Equal
|
|
409
|
+
*/
|
|
410
|
+
field(field) {
|
|
411
|
+
return buildField(__classPrivateFieldGet(this, _Equals_query, "f"), field);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* This method appends to the query an operand to use logic operators in the query.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* @example
|
|
418
|
+
* ```ts
|
|
419
|
+
* const equals = new Equals("+myField: myValue");
|
|
420
|
+
* equals.or().field("myField2").equals("myValue2"); // Current query: "+myField: myValue OR +myField2: myValue2"
|
|
421
|
+
* ```
|
|
422
|
+
*
|
|
423
|
+
* @return {*} {Operand} - An instance of a Lucene Operand. An operand is a logical operator used to combine terms or phrases in a query.
|
|
424
|
+
* @memberof Equal
|
|
425
|
+
*/
|
|
426
|
+
or() {
|
|
427
|
+
return buildOperand(__classPrivateFieldGet(this, _Equals_query, "f"), OPERAND.OR);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* This method appends to the query an operand to use logic operators in the query.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* const equals = new Equals("+myField: myValue");
|
|
435
|
+
* equals.and().field("myField2").equals("myValue2"); // Current query: "+myField: myValue AND +myField2: myValue2"
|
|
436
|
+
* ```
|
|
437
|
+
*
|
|
438
|
+
* @return {*} {Operand} - An instance of a Lucene Operand. An operand is a logical operator used to combine terms or phrases in a query.
|
|
439
|
+
* @memberof Equal
|
|
440
|
+
*/
|
|
441
|
+
and() {
|
|
442
|
+
return buildOperand(__classPrivateFieldGet(this, _Equals_query, "f"), OPERAND.AND);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* This method appends to the query an operand to use logic operators in the query.
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```ts
|
|
449
|
+
* const equals = new Equals("+myField: myValue");
|
|
450
|
+
* equals.not().field("myField").equals("myValue2"); // Current query: "+myField: myValue NOT +myField: myValue2"
|
|
451
|
+
* ```
|
|
452
|
+
*
|
|
453
|
+
* @return {*} {NotOperand} - An instance of a Lucene Not Operand. A not operand is a logical operator used to exclude terms or phrases in a query.
|
|
454
|
+
* @memberof Equal
|
|
455
|
+
*/
|
|
456
|
+
not() {
|
|
457
|
+
return buildNotOperand(__classPrivateFieldGet(this, _Equals_query, "f"));
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* This method allows to pass a raw query string to the query builder.
|
|
461
|
+
* This raw query should end in a Lucene Equal.
|
|
462
|
+
* This method is useful when you want to append a complex query or an already written query to the query builder.
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* ```ts
|
|
466
|
+
* // This builds the follow raw query "+myField: value AND (someOtherValue OR anotherValue)"
|
|
467
|
+
* const equals = new Equals("+myField: value");
|
|
468
|
+
* equals.raw("+myField2: value2"); // Current query: "+myField: value +myField2: anotherValue"
|
|
469
|
+
* ```
|
|
470
|
+
*
|
|
471
|
+
* @param {string} query - A raw query string.
|
|
472
|
+
* @return {*} {Equal} - An instance of a Lucene Equal. A term is a value used to search for a specific value in a document.
|
|
473
|
+
* @memberof QueryBuilder
|
|
474
|
+
*/
|
|
475
|
+
raw(query) {
|
|
476
|
+
return buildRawEquals(__classPrivateFieldGet(this, _Equals_query, "f"), query);
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* This method returns the final query string.
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```ts
|
|
483
|
+
* const equals = new Equals("+myField: myValue");
|
|
484
|
+
* equals.field("myField2").equals("myValue2").build(); // Returns "+myField: myValue +myField2: myValue2"
|
|
485
|
+
* ```
|
|
486
|
+
*
|
|
487
|
+
* @return {*} {string} - The final query string.
|
|
488
|
+
* @memberof Equal
|
|
489
|
+
*/
|
|
490
|
+
build() {
|
|
491
|
+
return sanitizeQuery(__classPrivateFieldGet(this, _Equals_query, "f"));
|
|
479
492
|
}
|
|
480
|
-
return window.parent !== window;
|
|
481
|
-
}
|
|
482
|
-
function initDotUVE() {
|
|
483
|
-
window.dotUVE = INITIAL_DOT_UVE;
|
|
484
493
|
}
|
|
494
|
+
_Equals_query = new WeakMap();
|
|
495
|
+
|
|
496
|
+
var _QueryBuilder_query;
|
|
485
497
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* @param {DotCMSPageEditorConfig} config - Optional configuration for the editor.
|
|
498
|
+
* 'QueryBuilder' Is a Typescript class that provides the ability to build a query string using the Lucene syntax in a more readable way.
|
|
489
499
|
* @example
|
|
490
500
|
* ```ts
|
|
491
|
-
*
|
|
492
|
-
*
|
|
501
|
+
* const qb = new QueryBuilder();
|
|
502
|
+
* const query = qb
|
|
503
|
+
* .field('contentType')
|
|
504
|
+
* .equals('Blog')
|
|
505
|
+
* .field('conhost')
|
|
506
|
+
* .equals('my-super-cool-site')
|
|
507
|
+
* .build(); // Output: `+contentType:Blog +conhost:my-super-cool-site"`
|
|
493
508
|
* ```
|
|
494
|
-
*/
|
|
495
|
-
function initEditor(config) {
|
|
496
|
-
initDotUVE();
|
|
497
|
-
fetchPageDataFromInsideUVE(config.pathname);
|
|
498
|
-
listenEditorMessages();
|
|
499
|
-
listenHoveredContentlet();
|
|
500
|
-
scrollHandler();
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* Destroys the editor by removing event listeners and disconnecting observers.
|
|
504
509
|
*
|
|
505
510
|
* @example
|
|
506
511
|
* ```ts
|
|
507
|
-
*
|
|
512
|
+
* const qb = new QueryBuilder();
|
|
513
|
+
* const query = qb
|
|
514
|
+
* .field('contentType')
|
|
515
|
+
* .equals('Blog')
|
|
516
|
+
* .field('title')
|
|
517
|
+
* .equals('Football')
|
|
518
|
+
* .excludeField('summary')
|
|
519
|
+
* .equals('Lionel Messi')
|
|
520
|
+
* .build(); // Output: `+contentType:Blog +title:Football -summary:"Lionel Messi"`
|
|
508
521
|
* ```
|
|
522
|
+
* @export
|
|
523
|
+
* @class QueryBuilder
|
|
509
524
|
*/
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
525
|
+
class QueryBuilder {
|
|
526
|
+
constructor() {
|
|
527
|
+
_QueryBuilder_query.set(this, '');
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* This method appends to the query a field that should be included in the search.
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```ts
|
|
534
|
+
* const qb = new QueryBuilder();
|
|
535
|
+
* qb.field("+myField: ", "myValue"); // Current query: "+myField: myValue"
|
|
536
|
+
* ```
|
|
537
|
+
*
|
|
538
|
+
* @param {string} field - The field that should be included in the search.
|
|
539
|
+
* @return {*} {Field} - An instance of a Lucene Field. A field is a key used to search for a specific value in a document.
|
|
540
|
+
* @memberof QueryBuilder
|
|
541
|
+
*/
|
|
542
|
+
field(field) {
|
|
543
|
+
return buildField(__classPrivateFieldGet(this, _QueryBuilder_query, "f"), field);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* This method appends to the query a field that should be excluded from the search.
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```ts
|
|
550
|
+
* const qb = new QueryBuilder();
|
|
551
|
+
* qb.excludeField("myField").equals("myValue"); // Current query: "-myField: myValue"
|
|
552
|
+
* ```
|
|
553
|
+
*
|
|
554
|
+
* @param {string} field - The field that should be excluded from the search.
|
|
555
|
+
* @return {*} {Field} - An instance of a Lucene Exclude Field. An exclude field is a key used to exclude for a specific value in a document.
|
|
556
|
+
* @memberof QueryBuilder
|
|
557
|
+
*/
|
|
558
|
+
excludeField(field) {
|
|
559
|
+
return buildExcludeField(__classPrivateFieldGet(this, _QueryBuilder_query, "f"), field);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* This method allows to pass a raw query string to the query builder.
|
|
563
|
+
* This raw query should end in Equals.
|
|
564
|
+
* This method is useful when you want to append a complex query or an already written query to the query builder.
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```ts
|
|
568
|
+
* const qb = new QueryBuilder();
|
|
569
|
+
* qb.raw("+myField: value AND (someOtherValue OR anotherValue)"); // Current query: "+myField: value AND (someOtherValue OR anotherValue)"
|
|
570
|
+
* ```
|
|
571
|
+
*
|
|
572
|
+
* @param {string} query - A raw query string.
|
|
573
|
+
* @return {*} {Equals} - An instance of Equals. A term is a value used to search for a specific value in a document.
|
|
574
|
+
* @memberof QueryBuilder
|
|
575
|
+
*/
|
|
576
|
+
raw(query) {
|
|
577
|
+
return buildRawEquals(__classPrivateFieldGet(this, _QueryBuilder_query, "f"), query);
|
|
578
|
+
}
|
|
519
579
|
}
|
|
580
|
+
_QueryBuilder_query = new WeakMap();
|
|
520
581
|
|
|
521
|
-
|
|
522
|
-
editContentlet,
|
|
523
|
-
initInlineEditing,
|
|
524
|
-
reorderMenu,
|
|
525
|
-
lastScrollYPosition: 0
|
|
526
|
-
};
|
|
582
|
+
var _CollectionBuilder_page, _CollectionBuilder_limit, _CollectionBuilder_depth, _CollectionBuilder_render, _CollectionBuilder_sortBy, _CollectionBuilder_contentType, _CollectionBuilder_defaultQuery, _CollectionBuilder_query, _CollectionBuilder_rawQuery, _CollectionBuilder_languageId, _CollectionBuilder_draft, _CollectionBuilder_serverUrl, _CollectionBuilder_requestOptions;
|
|
527
583
|
/**
|
|
528
|
-
*
|
|
584
|
+
* Creates a Builder to filter and fetch content from the content API for a specific content type.
|
|
529
585
|
*
|
|
530
586
|
* @export
|
|
531
|
-
* @
|
|
587
|
+
* @class CollectionBuilder
|
|
588
|
+
* @template T Represents the type of the content type to fetch. Defaults to unknown.
|
|
532
589
|
*/
|
|
533
|
-
|
|
534
|
-
(function (CLIENT_ACTIONS) {
|
|
590
|
+
class CollectionBuilder {
|
|
535
591
|
/**
|
|
536
|
-
*
|
|
592
|
+
* Creates an instance of CollectionBuilder.
|
|
593
|
+
* @param {ClientOptions} requestOptions Options for the client request.
|
|
594
|
+
* @param {string} serverUrl The server URL.
|
|
595
|
+
* @param {string} contentType The content type to fetch.
|
|
596
|
+
* @memberof CollectionBuilder
|
|
537
597
|
*/
|
|
538
|
-
|
|
598
|
+
constructor(requestOptions, serverUrl, contentType) {
|
|
599
|
+
_CollectionBuilder_page.set(this, 1);
|
|
600
|
+
_CollectionBuilder_limit.set(this, 10);
|
|
601
|
+
_CollectionBuilder_depth.set(this, 0);
|
|
602
|
+
_CollectionBuilder_render.set(this, false);
|
|
603
|
+
_CollectionBuilder_sortBy.set(this, void 0);
|
|
604
|
+
_CollectionBuilder_contentType.set(this, void 0);
|
|
605
|
+
_CollectionBuilder_defaultQuery.set(this, void 0);
|
|
606
|
+
_CollectionBuilder_query.set(this, void 0);
|
|
607
|
+
_CollectionBuilder_rawQuery.set(this, void 0);
|
|
608
|
+
_CollectionBuilder_languageId.set(this, 1);
|
|
609
|
+
_CollectionBuilder_draft.set(this, false);
|
|
610
|
+
_CollectionBuilder_serverUrl.set(this, void 0);
|
|
611
|
+
_CollectionBuilder_requestOptions.set(this, void 0);
|
|
612
|
+
__classPrivateFieldSet(this, _CollectionBuilder_requestOptions, requestOptions, "f");
|
|
613
|
+
__classPrivateFieldSet(this, _CollectionBuilder_serverUrl, serverUrl, "f");
|
|
614
|
+
__classPrivateFieldSet(this, _CollectionBuilder_contentType, contentType, "f");
|
|
615
|
+
// Build the default query with the contentType field
|
|
616
|
+
__classPrivateFieldSet(this, _CollectionBuilder_defaultQuery, new QueryBuilder().field('contentType').equals(__classPrivateFieldGet(this, _CollectionBuilder_contentType, "f")), "f");
|
|
617
|
+
}
|
|
539
618
|
/**
|
|
540
|
-
*
|
|
619
|
+
* Returns the sort query in the format: field order, field order, ...
|
|
620
|
+
*
|
|
621
|
+
* @readonly
|
|
622
|
+
* @private
|
|
623
|
+
* @memberof CollectionBuilder
|
|
541
624
|
*/
|
|
542
|
-
|
|
625
|
+
get sort() {
|
|
626
|
+
return __classPrivateFieldGet(this, _CollectionBuilder_sortBy, "f")?.map((sort) => `${sort.field} ${sort.order}`).join(',');
|
|
627
|
+
}
|
|
543
628
|
/**
|
|
544
|
-
*
|
|
629
|
+
* Returns the offset for pagination.
|
|
630
|
+
*
|
|
631
|
+
* @readonly
|
|
632
|
+
* @private
|
|
633
|
+
* @memberof CollectionBuilder
|
|
545
634
|
*/
|
|
546
|
-
|
|
635
|
+
get offset() {
|
|
636
|
+
return __classPrivateFieldGet(this, _CollectionBuilder_limit, "f") * (__classPrivateFieldGet(this, _CollectionBuilder_page, "f") - 1);
|
|
637
|
+
}
|
|
547
638
|
/**
|
|
548
|
-
*
|
|
639
|
+
* Returns the full URL for the content API.
|
|
640
|
+
*
|
|
641
|
+
* @readonly
|
|
642
|
+
* @private
|
|
643
|
+
* @memberof CollectionBuilder
|
|
549
644
|
*/
|
|
550
|
-
|
|
645
|
+
get url() {
|
|
646
|
+
return `${__classPrivateFieldGet(this, _CollectionBuilder_serverUrl, "f")}${CONTENT_API_URL}`;
|
|
647
|
+
}
|
|
551
648
|
/**
|
|
552
|
-
*
|
|
649
|
+
* Returns the current query built.
|
|
650
|
+
*
|
|
651
|
+
* @readonly
|
|
652
|
+
* @private
|
|
653
|
+
* @memberof CollectionBuilder
|
|
553
654
|
*/
|
|
554
|
-
|
|
655
|
+
get currentQuery() {
|
|
656
|
+
return __classPrivateFieldGet(this, _CollectionBuilder_query, "f") ?? __classPrivateFieldGet(this, _CollectionBuilder_defaultQuery, "f");
|
|
657
|
+
}
|
|
555
658
|
/**
|
|
556
|
-
*
|
|
659
|
+
* Filters the content by the specified language ID.
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```typescript
|
|
663
|
+
* const client = new DotCMSClient(config);
|
|
664
|
+
* const collectionBuilder = client.content.getCollection("Blog");
|
|
665
|
+
* collectionBuilder.language(1);
|
|
666
|
+
* ```
|
|
667
|
+
*
|
|
668
|
+
* @param {number | string} languageId The language ID to filter the content by.
|
|
669
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
670
|
+
* @memberof CollectionBuilder
|
|
557
671
|
*/
|
|
558
|
-
|
|
672
|
+
language(languageId) {
|
|
673
|
+
__classPrivateFieldSet(this, _CollectionBuilder_languageId, languageId, "f");
|
|
674
|
+
return this;
|
|
675
|
+
}
|
|
559
676
|
/**
|
|
560
|
-
*
|
|
677
|
+
* Setting this to true will server side render (using velocity) any widgets that are returned by the content query.
|
|
678
|
+
*
|
|
679
|
+
* More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
|
|
680
|
+
*
|
|
681
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
682
|
+
* @memberof CollectionBuilder
|
|
561
683
|
*/
|
|
562
|
-
|
|
684
|
+
render() {
|
|
685
|
+
__classPrivateFieldSet(this, _CollectionBuilder_render, true, "f");
|
|
686
|
+
return this;
|
|
687
|
+
}
|
|
563
688
|
/**
|
|
564
|
-
*
|
|
565
|
-
*
|
|
689
|
+
* Sorts the content by the specified fields and orders.
|
|
690
|
+
*
|
|
691
|
+
* @example
|
|
692
|
+
* ```typescript
|
|
693
|
+
* const client = new DotCMSClient(config);
|
|
694
|
+
* const collectionBuilder = client.content.getCollection("Blog");
|
|
695
|
+
* const sortBy = [{ field: 'title', order: 'asc' }, { field: 'modDate', order: 'desc' }];
|
|
696
|
+
* collectionBuilder("Blog").sortBy(sortBy);
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* @param {SortBy[]} sortBy Array of constraints to sort the content by.
|
|
700
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
701
|
+
* @memberof CollectionBuilder
|
|
566
702
|
*/
|
|
567
|
-
|
|
703
|
+
sortBy(sortBy) {
|
|
704
|
+
__classPrivateFieldSet(this, _CollectionBuilder_sortBy, sortBy, "f");
|
|
705
|
+
return this;
|
|
706
|
+
}
|
|
568
707
|
/**
|
|
569
|
-
*
|
|
708
|
+
* Sets the maximum amount of content to fetch.
|
|
709
|
+
*
|
|
710
|
+
* @param {number} limit The maximum amount of content to fetch.
|
|
711
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
712
|
+
* @memberof CollectionBuilder
|
|
570
713
|
*/
|
|
571
|
-
|
|
714
|
+
limit(limit) {
|
|
715
|
+
__classPrivateFieldSet(this, _CollectionBuilder_limit, limit, "f");
|
|
716
|
+
return this;
|
|
717
|
+
}
|
|
572
718
|
/**
|
|
573
|
-
*
|
|
719
|
+
* Sets the page number to fetch.
|
|
720
|
+
*
|
|
721
|
+
* @param {number} page The page number to fetch.
|
|
722
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
723
|
+
* @memberof CollectionBuilder
|
|
574
724
|
*/
|
|
575
|
-
|
|
725
|
+
page(page) {
|
|
726
|
+
__classPrivateFieldSet(this, _CollectionBuilder_page, page, "f");
|
|
727
|
+
return this;
|
|
728
|
+
}
|
|
729
|
+
query(arg) {
|
|
730
|
+
if (typeof arg === 'string') {
|
|
731
|
+
__classPrivateFieldSet(this, _CollectionBuilder_rawQuery, arg, "f");
|
|
732
|
+
return this;
|
|
733
|
+
}
|
|
734
|
+
if (typeof arg !== 'function') {
|
|
735
|
+
throw new Error(`Parameter for query method should be a buildQuery function or a string.\nExample:\nclient.content.getCollection('Activity').query((queryBuilder) => queryBuilder.field('title').equals('Hello World'))\nor\nclient.content.getCollection('Activity').query('+Activity.title:"Hello World"') \nSee documentation for more information.`);
|
|
736
|
+
}
|
|
737
|
+
const builtQuery = arg(new QueryBuilder());
|
|
738
|
+
// This can be use in Javascript so we cannot rely on the type checking
|
|
739
|
+
if (builtQuery instanceof Equals) {
|
|
740
|
+
__classPrivateFieldSet(this, _CollectionBuilder_query, builtQuery.raw(this.currentQuery.build()), "f");
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
throw new Error('Provided query is not valid. A query should end in an equals method call.\nExample:\n(queryBuilder) => queryBuilder.field("title").equals("Hello World")\nSee documentation for more information.');
|
|
744
|
+
}
|
|
745
|
+
return this;
|
|
746
|
+
}
|
|
576
747
|
/**
|
|
577
|
-
*
|
|
748
|
+
* Retrieves draft content.
|
|
749
|
+
* @example
|
|
750
|
+
* ```ts
|
|
751
|
+
* const client = new DotCMSClient(config);
|
|
752
|
+
* const collectionBuilder = client.content.getCollection("Blog");
|
|
753
|
+
* collectionBuilder
|
|
754
|
+
* .draft() // This will retrieve draft/working content
|
|
755
|
+
* .then((response) => // Your code here })
|
|
756
|
+
* .catch((error) => // Your code here })
|
|
757
|
+
* ```
|
|
758
|
+
*
|
|
759
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
760
|
+
* @memberof CollectionBuilder
|
|
578
761
|
*/
|
|
579
|
-
|
|
762
|
+
draft() {
|
|
763
|
+
__classPrivateFieldSet(this, _CollectionBuilder_draft, true, "f");
|
|
764
|
+
return this;
|
|
765
|
+
}
|
|
580
766
|
/**
|
|
581
|
-
*
|
|
767
|
+
* Filters the content by a variant ID for [Experiments](https://www.dotcms.com/docs/latest/experiments-and-a-b-testing)
|
|
768
|
+
*
|
|
769
|
+
* More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
|
|
770
|
+
*
|
|
771
|
+
* @example
|
|
772
|
+
* ```ts
|
|
773
|
+
* const client = new DotCMSClient(config);
|
|
774
|
+
* const collectionBuilder = client.content.getCollection("Blog");
|
|
775
|
+
* collectionBuilder
|
|
776
|
+
* .variant("YOUR_VARIANT_ID")
|
|
777
|
+
* .then((response) => // Your code here })
|
|
778
|
+
* .catch((error) => // Your code here })
|
|
779
|
+
* ```
|
|
780
|
+
*
|
|
781
|
+
* @param {string} variantId A string that represents a variant ID.
|
|
782
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
783
|
+
* @memberof CollectionBuilder
|
|
582
784
|
*/
|
|
583
|
-
|
|
785
|
+
variant(variantId) {
|
|
786
|
+
__classPrivateFieldSet(this, _CollectionBuilder_query, this.currentQuery.field('variant').equals(variantId), "f");
|
|
787
|
+
return this;
|
|
788
|
+
}
|
|
584
789
|
/**
|
|
585
|
-
*
|
|
790
|
+
* Sets the depth of the relationships of the content.
|
|
791
|
+
* Specifies the depth of related content to return in the results.
|
|
792
|
+
*
|
|
793
|
+
* More information here: {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying#ParamsOptional}
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
* ```ts
|
|
797
|
+
* const client = new DotCMSClient(config);
|
|
798
|
+
* const collectionBuilder = client.content.getCollection("Blog");
|
|
799
|
+
* collectionBuilder
|
|
800
|
+
* .depth(1)
|
|
801
|
+
* .then((response) => // Your code here })
|
|
802
|
+
* .catch((error) => // Your code here })
|
|
803
|
+
* ```
|
|
804
|
+
*
|
|
805
|
+
* @param {number} depth The depth of the relationships of the content.
|
|
806
|
+
* @return {CollectionBuilder} A CollectionBuilder instance.
|
|
807
|
+
* @memberof CollectionBuilder
|
|
586
808
|
*/
|
|
587
|
-
|
|
809
|
+
depth(depth) {
|
|
810
|
+
if (depth < 0 || depth > 3) {
|
|
811
|
+
throw new Error('Depth value must be between 0 and 3');
|
|
812
|
+
}
|
|
813
|
+
__classPrivateFieldSet(this, _CollectionBuilder_depth, depth, "f");
|
|
814
|
+
return this;
|
|
815
|
+
}
|
|
588
816
|
/**
|
|
589
|
-
*
|
|
817
|
+
* Executes the fetch and returns a promise that resolves to the content or rejects with an error.
|
|
818
|
+
*
|
|
819
|
+
* @example
|
|
820
|
+
* ```ts
|
|
821
|
+
* const client = new DotCMSClient(config);
|
|
822
|
+
* const collectionBuilder = client.content.getCollection("Blog");
|
|
823
|
+
* collectionBuilder
|
|
824
|
+
* .limit(10)
|
|
825
|
+
* .then((response) => // Your code here })
|
|
826
|
+
* .catch((error) => // Your code here })
|
|
827
|
+
* ```
|
|
828
|
+
*
|
|
829
|
+
* @param {OnFullfilled} [onfulfilled] A callback that is called when the fetch is successful.
|
|
830
|
+
* @param {OnRejected} [onrejected] A callback that is called when the fetch fails.
|
|
831
|
+
* @return {Promise<GetCollectionResponse<T> | GetCollectionError>} A promise that resolves to the content or rejects with an error.
|
|
832
|
+
* @memberof CollectionBuilder
|
|
590
833
|
*/
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
834
|
+
then(onfulfilled, onrejected) {
|
|
835
|
+
return this.fetch().then(async (response) => {
|
|
836
|
+
const data = await response.json();
|
|
837
|
+
if (response.ok) {
|
|
838
|
+
const formattedResponse = this.formatResponse(data);
|
|
839
|
+
const finalResponse = typeof onfulfilled === 'function'
|
|
840
|
+
? onfulfilled(formattedResponse)
|
|
841
|
+
: formattedResponse;
|
|
842
|
+
return finalResponse;
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
return {
|
|
846
|
+
status: response.status,
|
|
847
|
+
...data
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
}, onrejected);
|
|
608
851
|
}
|
|
609
|
-
|
|
610
|
-
|
|
852
|
+
/**
|
|
853
|
+
* Formats the response to the desired format.
|
|
854
|
+
*
|
|
855
|
+
* @private
|
|
856
|
+
* @param {GetCollectionRawResponse<T>} data The raw response data.
|
|
857
|
+
* @return {GetCollectionResponse<T>} The formatted response.
|
|
858
|
+
* @memberof CollectionBuilder
|
|
859
|
+
*/
|
|
860
|
+
formatResponse(data) {
|
|
861
|
+
const contentlets = data.entity.jsonObjectView.contentlets;
|
|
862
|
+
const total = data.entity.resultsSize;
|
|
863
|
+
const mappedResponse = {
|
|
864
|
+
contentlets,
|
|
865
|
+
total,
|
|
866
|
+
page: __classPrivateFieldGet(this, _CollectionBuilder_page, "f"),
|
|
867
|
+
size: contentlets.length
|
|
868
|
+
};
|
|
869
|
+
return __classPrivateFieldGet(this, _CollectionBuilder_sortBy, "f")
|
|
870
|
+
? {
|
|
871
|
+
...mappedResponse,
|
|
872
|
+
sortedBy: __classPrivateFieldGet(this, _CollectionBuilder_sortBy, "f")
|
|
873
|
+
}
|
|
874
|
+
: mappedResponse;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Calls the content API to fetch the content.
|
|
878
|
+
*
|
|
879
|
+
* @private
|
|
880
|
+
* @return {Promise<Response>} The fetch response.
|
|
881
|
+
* @memberof CollectionBuilder
|
|
882
|
+
*/
|
|
883
|
+
fetch() {
|
|
884
|
+
const finalQuery = this.currentQuery
|
|
885
|
+
.field('languageId')
|
|
886
|
+
.equals(__classPrivateFieldGet(this, _CollectionBuilder_languageId, "f").toString())
|
|
887
|
+
.field('live')
|
|
888
|
+
.equals((!__classPrivateFieldGet(this, _CollectionBuilder_draft, "f")).toString())
|
|
889
|
+
.build();
|
|
890
|
+
const sanitizedQuery = sanitizeQueryForContentType(finalQuery, __classPrivateFieldGet(this, _CollectionBuilder_contentType, "f"));
|
|
891
|
+
const query = __classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f") ? `${sanitizedQuery} ${__classPrivateFieldGet(this, _CollectionBuilder_rawQuery, "f")}` : sanitizedQuery;
|
|
892
|
+
return fetch(this.url, {
|
|
893
|
+
...__classPrivateFieldGet(this, _CollectionBuilder_requestOptions, "f"),
|
|
894
|
+
method: 'POST',
|
|
895
|
+
headers: {
|
|
896
|
+
...__classPrivateFieldGet(this, _CollectionBuilder_requestOptions, "f").headers,
|
|
897
|
+
'Content-Type': 'application/json'
|
|
898
|
+
},
|
|
899
|
+
body: JSON.stringify({
|
|
900
|
+
query,
|
|
901
|
+
render: __classPrivateFieldGet(this, _CollectionBuilder_render, "f"),
|
|
902
|
+
sort: this.sort,
|
|
903
|
+
limit: __classPrivateFieldGet(this, _CollectionBuilder_limit, "f"),
|
|
904
|
+
offset: this.offset,
|
|
905
|
+
depth: __classPrivateFieldGet(this, _CollectionBuilder_depth, "f")
|
|
906
|
+
//userId: This exist but we currently don't use it
|
|
907
|
+
//allCategoriesInfo: This exist but we currently don't use it
|
|
908
|
+
})
|
|
909
|
+
});
|
|
611
910
|
}
|
|
612
911
|
}
|
|
912
|
+
_CollectionBuilder_page = new WeakMap(), _CollectionBuilder_limit = new WeakMap(), _CollectionBuilder_depth = new WeakMap(), _CollectionBuilder_render = new WeakMap(), _CollectionBuilder_sortBy = new WeakMap(), _CollectionBuilder_contentType = new WeakMap(), _CollectionBuilder_defaultQuery = new WeakMap(), _CollectionBuilder_query = new WeakMap(), _CollectionBuilder_rawQuery = new WeakMap(), _CollectionBuilder_languageId = new WeakMap(), _CollectionBuilder_draft = new WeakMap(), _CollectionBuilder_serverUrl = new WeakMap(), _CollectionBuilder_requestOptions = new WeakMap();
|
|
913
|
+
|
|
914
|
+
var _Content_requestOptions, _Content_serverUrl;
|
|
613
915
|
/**
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
* @
|
|
618
|
-
* @property {ClientConfig} config - The configuration object for the DotCMS client.
|
|
619
|
-
* @property {Content} content - Provides methods to interact with content in DotCMS.
|
|
916
|
+
* Creates a builder to filter and fetch a collection of content items.
|
|
917
|
+
* @param contentType - The content type to retrieve.
|
|
918
|
+
* @returns A CollectionBuilder instance for chaining filters and executing the query.
|
|
919
|
+
* @template T - The type of the content items (defaults to unknown).
|
|
620
920
|
*
|
|
621
|
-
* @
|
|
921
|
+
* @example Fetch blog posts with async/await
|
|
922
|
+
* ```typescript
|
|
923
|
+
* const response = await client.content
|
|
924
|
+
* .getCollection<BlogPost>('Blog')
|
|
925
|
+
* .limit(10)
|
|
926
|
+
* .page(2)
|
|
927
|
+
* .sortBy([{ field: 'title', order: 'asc' }])
|
|
928
|
+
* .query(q => q.field('author').equals('John Doe'))
|
|
929
|
+
* .depth(1)
|
|
622
930
|
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
* @see {@link https://www.dotcms.com/docs/latest/page-rest-api-layout-as-a-service-laas}
|
|
626
|
-
*
|
|
627
|
-
* @method nav.get(options: NavApiOptions = { depth: 0, path: '/', languageId: 1 }): Promise<NavApiResponse> - Retrieves information about the dotCMS file and folder tree.
|
|
628
|
-
* The Navigation API allows you to fetch the site structure and menu items.
|
|
629
|
-
* @see {@link https://www.dotcms.com/docs/latest/navigation-rest-api}
|
|
630
|
-
*
|
|
631
|
-
* @method content.get(options: ContentApiOptions): Promise<ContentApiResponse> - Retrieves content items based on specified criteria.
|
|
632
|
-
* The Content API allows you to query and retrieve content by ID, inode, or using Lucene queries.
|
|
633
|
-
* @see {@link https://www.dotcms.com/docs/latest/content-api-retrieval-and-querying}
|
|
634
|
-
*
|
|
635
|
-
* @method editor.on(action: string, callbackFn: (payload: unknown) => void) - Allows you to react to actions issued by the Universal Visual Editor (UVE).
|
|
636
|
-
* @method editor.off(action: string) - Stops listening to an action issued by UVE.
|
|
931
|
+
* console.log(response.contentlets);
|
|
932
|
+
* ```
|
|
637
933
|
*
|
|
638
|
-
* @
|
|
639
|
-
*
|
|
640
|
-
*
|
|
934
|
+
* @example Fetch blog posts with Promise chain
|
|
935
|
+
* ```typescript
|
|
936
|
+
* client.content
|
|
937
|
+
* .getCollection<BlogPost>('Blog')
|
|
938
|
+
* .limit(10)
|
|
939
|
+
* .page(2)
|
|
940
|
+
* .sortBy([{ field: 'title', order: 'asc' }])
|
|
941
|
+
* .query(q => q.field('author').equals('John Doe'))
|
|
942
|
+
* .depth(1)
|
|
943
|
+
* .then(response => console.log(response.contentlets))
|
|
944
|
+
* .catch(error => console.error(error));
|
|
945
|
+
* ```
|
|
641
946
|
*
|
|
642
|
-
* @example
|
|
643
|
-
* ```
|
|
644
|
-
*
|
|
947
|
+
* @example Using a custom type
|
|
948
|
+
* ```typescript
|
|
949
|
+
* interface BlogPost {
|
|
950
|
+
* summary: string;
|
|
951
|
+
* author: string;
|
|
952
|
+
* title: string;
|
|
953
|
+
* }
|
|
645
954
|
*
|
|
646
|
-
*
|
|
647
|
-
*
|
|
955
|
+
* const posts = await client.content
|
|
956
|
+
* .getCollection<BlogPost>('Blog')
|
|
957
|
+
* .limit(10)
|
|
648
958
|
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
959
|
+
* posts.contentlets.forEach(post => {
|
|
960
|
+
* console.log(post.title, post.author, post.summary);
|
|
961
|
+
* });
|
|
962
|
+
* ```
|
|
963
|
+
*/
|
|
964
|
+
class Content {
|
|
965
|
+
/**
|
|
966
|
+
* Creates an instance of Content.
|
|
967
|
+
* @param {RequestOptions} requestOptions - The options for the client request.
|
|
968
|
+
* @param {string} serverUrl - The server URL.
|
|
969
|
+
*/
|
|
970
|
+
constructor(requestOptions, serverUrl) {
|
|
971
|
+
_Content_requestOptions.set(this, void 0);
|
|
972
|
+
_Content_serverUrl.set(this, void 0);
|
|
973
|
+
__classPrivateFieldSet(this, _Content_requestOptions, requestOptions, "f");
|
|
974
|
+
__classPrivateFieldSet(this, _Content_serverUrl, serverUrl, "f");
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Takes a content type and returns a builder to filter and fetch the collection.
|
|
978
|
+
* @param {string} contentType - The content type to get the collection.
|
|
979
|
+
* @return {CollectionBuilder<T>} CollectionBuilder to filter and fetch the collection.
|
|
980
|
+
* @template T - Represents the type of the content type to fetch. Defaults to unknown.
|
|
981
|
+
* @memberof Content
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```javascript
|
|
985
|
+
* // Using await and async
|
|
986
|
+
* const collectionResponse = await client.content
|
|
987
|
+
* .getCollection('Blog')
|
|
988
|
+
* .limit(10)
|
|
989
|
+
* .page(2)
|
|
990
|
+
* .sortBy([{ field: 'title', order: 'asc' }])
|
|
991
|
+
* .query((queryBuilder) => queryBuilder.field('author').equals('John Doe'))
|
|
992
|
+
* .depth(1);
|
|
993
|
+
* ```
|
|
994
|
+
* @example
|
|
995
|
+
* ```javascript
|
|
996
|
+
* // Using then and catch
|
|
997
|
+
* client.content
|
|
998
|
+
* .getCollection('Blog')
|
|
999
|
+
* .limit(10)
|
|
1000
|
+
* .page(2)
|
|
1001
|
+
* .sortBy([{ field: 'title', order: 'asc' }])
|
|
1002
|
+
* .query((queryBuilder) => queryBuilder.field('author').equals('John Doe'))
|
|
1003
|
+
* .depth(1)
|
|
1004
|
+
* .then((response) => {
|
|
1005
|
+
* console.log(response.contentlets);
|
|
1006
|
+
* })
|
|
1007
|
+
* .catch((error) => {
|
|
1008
|
+
* console.error(error);
|
|
1009
|
+
* });
|
|
1010
|
+
* ```
|
|
1011
|
+
* @example
|
|
1012
|
+
* ```typescript
|
|
1013
|
+
* // Using a specific type for your content
|
|
1014
|
+
*
|
|
1015
|
+
* type Blog = {
|
|
1016
|
+
* summary: string;
|
|
1017
|
+
* author: string;
|
|
1018
|
+
* title: string;
|
|
1019
|
+
* };
|
|
1020
|
+
*
|
|
1021
|
+
* client.content
|
|
1022
|
+
* .getCollection<Blog>('Blog')
|
|
1023
|
+
* .limit(10)
|
|
1024
|
+
* .page(2)
|
|
1025
|
+
* .sortBy([{ field: 'title', order: 'asc' }])
|
|
1026
|
+
* .query((queryBuilder) => queryBuilder.field('author').equals('John Doe'))
|
|
1027
|
+
* .depth(1)
|
|
1028
|
+
* .then((response) => {
|
|
1029
|
+
* response.contentlets.forEach((blog) => {
|
|
1030
|
+
* console.log(blog.title);
|
|
1031
|
+
* console.log(blog.author);
|
|
1032
|
+
* console.log(blog.summary);
|
|
1033
|
+
* });
|
|
1034
|
+
* })
|
|
1035
|
+
* .catch((error) => {
|
|
1036
|
+
* console.error(error);
|
|
1037
|
+
* });
|
|
1038
|
+
* ```
|
|
1039
|
+
*
|
|
1040
|
+
*/
|
|
1041
|
+
getCollection(contentType) {
|
|
1042
|
+
return new CollectionBuilder(__classPrivateFieldGet(this, _Content_requestOptions, "f"), __classPrivateFieldGet(this, _Content_serverUrl, "f"), contentType);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
_Content_requestOptions = new WeakMap(), _Content_serverUrl = new WeakMap();
|
|
1046
|
+
|
|
1047
|
+
class NavigationClient {
|
|
1048
|
+
constructor(config, requestOptions) {
|
|
1049
|
+
this.requestOptions = requestOptions;
|
|
1050
|
+
this.BASE_URL = `${config?.dotcmsUrl}/api/v1/nav`;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Retrieves information about the dotCMS file and folder tree.
|
|
1054
|
+
* @param {NavigationApiOptions} options - The options for the Navigation API call. Defaults to `{ depth: 0, path: '/', languageId: 1 }`.
|
|
1055
|
+
* @returns {Promise<DotCMSNavigationItem[]>} - A Promise that resolves to the response from the DotCMS API.
|
|
1056
|
+
* @throws {Error} - Throws an error if the options are not valid.
|
|
1057
|
+
*/
|
|
1058
|
+
async get(path, params) {
|
|
1059
|
+
if (!path) {
|
|
1060
|
+
throw new Error("The 'path' parameter is required for the Navigation API");
|
|
1061
|
+
}
|
|
1062
|
+
const navParams = params ? this.mapToBackendParams(params) : {};
|
|
1063
|
+
const urlParams = new URLSearchParams(navParams).toString();
|
|
1064
|
+
const parsedPath = path.replace(/^\/+/, '/').replace(/\/+$/, '/');
|
|
1065
|
+
const url = `${this.BASE_URL}${parsedPath}${urlParams ? `?${urlParams}` : ''}`;
|
|
1066
|
+
const response = await fetch(url, this.requestOptions);
|
|
1067
|
+
if (!response.ok) {
|
|
1068
|
+
throw new Error(`Failed to fetch navigation data: ${response.statusText} - ${response.status}`);
|
|
1069
|
+
}
|
|
1070
|
+
return response.json().then((data) => data.entity);
|
|
1071
|
+
}
|
|
1072
|
+
mapToBackendParams(params) {
|
|
1073
|
+
const backendParams = {};
|
|
1074
|
+
if (params.depth) {
|
|
1075
|
+
backendParams['depth'] = String(params.depth);
|
|
1076
|
+
}
|
|
1077
|
+
if (params.languageId) {
|
|
1078
|
+
backendParams['language_id'] = String(params.languageId);
|
|
1079
|
+
}
|
|
1080
|
+
return backendParams;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* A record of HTTP status codes and their corresponding error messages.
|
|
651
1086
|
*
|
|
652
|
-
*
|
|
653
|
-
*
|
|
1087
|
+
* @type {Record<number, string>}
|
|
1088
|
+
* @property {string} 401 - Unauthorized. Check the token and try again.
|
|
1089
|
+
* @property {string} 403 - Forbidden. Check the permissions and try again.
|
|
1090
|
+
* @property {string} 404 - Not Found. Check the URL and try again.
|
|
1091
|
+
* @property {string} 500 - Internal Server Error. Try again later.
|
|
1092
|
+
* @property {string} 502 - Bad Gateway. Try again later.
|
|
1093
|
+
* @property {string} 503 - Service Unavailable. Try again later.
|
|
1094
|
+
*/
|
|
1095
|
+
const ErrorMessages = {
|
|
1096
|
+
401: 'Unauthorized. Check the token and try again.',
|
|
1097
|
+
403: 'Forbidden. Check the permissions and try again.',
|
|
1098
|
+
404: 'Not Found. Check the URL and try again.',
|
|
1099
|
+
500: 'Internal Server Error. Try again later.',
|
|
1100
|
+
502: 'Bad Gateway. Try again later.',
|
|
1101
|
+
503: 'Service Unavailable. Try again later.'
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
const DEFAULT_PAGE_CONTENTLETS_CONTENT = `
|
|
1105
|
+
publishDate
|
|
1106
|
+
inode
|
|
1107
|
+
identifier
|
|
1108
|
+
archived
|
|
1109
|
+
urlMap
|
|
1110
|
+
urlMap
|
|
1111
|
+
locked
|
|
1112
|
+
contentType
|
|
1113
|
+
creationDate
|
|
1114
|
+
modDate
|
|
1115
|
+
title
|
|
1116
|
+
baseType
|
|
1117
|
+
working
|
|
1118
|
+
live
|
|
1119
|
+
publishUser {
|
|
1120
|
+
firstName
|
|
1121
|
+
lastName
|
|
1122
|
+
}
|
|
1123
|
+
owner {
|
|
1124
|
+
lastName
|
|
1125
|
+
}
|
|
1126
|
+
conLanguage {
|
|
1127
|
+
language
|
|
1128
|
+
languageCode
|
|
1129
|
+
}
|
|
1130
|
+
modUser {
|
|
1131
|
+
firstName
|
|
1132
|
+
lastName
|
|
1133
|
+
}
|
|
1134
|
+
`;
|
|
1135
|
+
/**
|
|
1136
|
+
* Builds a GraphQL query for retrieving page content from DotCMS.
|
|
654
1137
|
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
1138
|
+
* @param {string} pageQuery - Custom fragment fields to include in the ClientPage fragment
|
|
1139
|
+
* @param {string} additionalQueries - Additional GraphQL queries to include in the main query
|
|
1140
|
+
* @returns {string} Complete GraphQL query string for page content
|
|
658
1141
|
*/
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
* `nav.get` is an asynchronous method of the `DotCmsClient` class that retrieves information about the dotCMS file and folder tree.
|
|
768
|
-
* It takes a `NavApiOptions` object as a parameter (with default values) and returns a Promise that resolves to the response from the DotCMS API.
|
|
769
|
-
*
|
|
770
|
-
* The navigation REST API enables you to retrieve information about the dotCMS file and folder tree through REST API calls.
|
|
771
|
-
* @link https://www.dotcms.com/docs/latest/navigation-rest-api
|
|
772
|
-
* @async
|
|
773
|
-
* @param {NavApiOptions} options - The options for the Nav API call. Defaults to `{ depth: 0, path: '/', languageId: 1 }`.
|
|
774
|
-
* @returns {Promise<unknown>} - A Promise that resolves to the response from the DotCMS API.
|
|
775
|
-
* @throws {Error} - Throws an error if the options are not valid.
|
|
776
|
-
* @example
|
|
777
|
-
* ```ts
|
|
778
|
-
* const client = new DotCmsClient({ dotcmsUrl: 'https://your.dotcms.com', authToken: 'your-auth-token', siteId: 'your-site-id' }});
|
|
779
|
-
* client.nav.get({ path: '/about-us', depth: 2 }).then(response => console.log(response));
|
|
780
|
-
* ```
|
|
781
|
-
*/
|
|
782
|
-
get: async (options = { depth: 0, path: '/', languageId: 1 }) => {
|
|
783
|
-
this.validateNavOptions(options);
|
|
784
|
-
// Extract the 'path' from the options and prepare the rest as query parameters
|
|
785
|
-
const { path, ...queryParamsOptions } = options;
|
|
786
|
-
const queryParamsObj = {};
|
|
787
|
-
Object.entries(queryParamsOptions).forEach(([key, value]) => {
|
|
788
|
-
if (value !== undefined) {
|
|
789
|
-
queryParamsObj[key] = String(value);
|
|
790
|
-
}
|
|
791
|
-
});
|
|
792
|
-
const queryParams = new URLSearchParams(queryParamsObj).toString();
|
|
793
|
-
// Format the URL correctly depending on the 'path' value
|
|
794
|
-
const formattedPath = path === '/' ? '/' : `/${path}`;
|
|
795
|
-
const url = `${transforms.__classPrivateFieldGet(this, _DotCmsClient_config, "f").dotcmsUrl}/api/v1/nav${formattedPath}${queryParams ? `?${queryParams}` : ''}`;
|
|
796
|
-
const response = await fetch(url, transforms.__classPrivateFieldGet(this, _DotCmsClient_requestOptions, "f"));
|
|
797
|
-
return response.json();
|
|
1142
|
+
const buildPageQuery = ({ page, fragments, additionalQueries }) => {
|
|
1143
|
+
if (!page) {
|
|
1144
|
+
consola.consola.warn("[DotCMS Client]: No page query was found, so we're loading all content using _map. This might slow things down. For better performance, we recommend adding a specific query in the page attribute.");
|
|
1145
|
+
}
|
|
1146
|
+
return `
|
|
1147
|
+
fragment DotCMSPage on DotPage {
|
|
1148
|
+
publishDate
|
|
1149
|
+
type
|
|
1150
|
+
httpsRequired
|
|
1151
|
+
inode
|
|
1152
|
+
path
|
|
1153
|
+
identifier
|
|
1154
|
+
hasTitleImage
|
|
1155
|
+
sortOrder
|
|
1156
|
+
extension
|
|
1157
|
+
canRead
|
|
1158
|
+
pageURI
|
|
1159
|
+
canEdit
|
|
1160
|
+
archived
|
|
1161
|
+
friendlyName
|
|
1162
|
+
workingInode
|
|
1163
|
+
url
|
|
1164
|
+
pageURI
|
|
1165
|
+
hasLiveVersion
|
|
1166
|
+
deleted
|
|
1167
|
+
pageUrl
|
|
1168
|
+
shortyWorking
|
|
1169
|
+
mimeType
|
|
1170
|
+
locked
|
|
1171
|
+
stInode
|
|
1172
|
+
contentType
|
|
1173
|
+
creationDate
|
|
1174
|
+
liveInode
|
|
1175
|
+
name
|
|
1176
|
+
shortyLive
|
|
1177
|
+
modDate
|
|
1178
|
+
title
|
|
1179
|
+
baseType
|
|
1180
|
+
working
|
|
1181
|
+
canLock
|
|
1182
|
+
live
|
|
1183
|
+
isContentlet
|
|
1184
|
+
statusIcons
|
|
1185
|
+
canEdit
|
|
1186
|
+
canLock
|
|
1187
|
+
canRead
|
|
1188
|
+
canEdit
|
|
1189
|
+
canLock
|
|
1190
|
+
canRead
|
|
1191
|
+
runningExperimentId
|
|
1192
|
+
urlContentMap {
|
|
1193
|
+
_map
|
|
1194
|
+
}
|
|
1195
|
+
host {
|
|
1196
|
+
identifier
|
|
1197
|
+
hostName
|
|
1198
|
+
googleMap
|
|
1199
|
+
archived
|
|
1200
|
+
contentType
|
|
1201
|
+
}
|
|
1202
|
+
vanityUrl {
|
|
1203
|
+
action
|
|
1204
|
+
forwardTo
|
|
1205
|
+
uri
|
|
1206
|
+
}
|
|
1207
|
+
conLanguage {
|
|
1208
|
+
id
|
|
1209
|
+
language
|
|
1210
|
+
languageCode
|
|
1211
|
+
}
|
|
1212
|
+
template {
|
|
1213
|
+
drawed
|
|
1214
|
+
anonymous
|
|
1215
|
+
theme
|
|
1216
|
+
identifier
|
|
1217
|
+
}
|
|
1218
|
+
containers {
|
|
1219
|
+
path
|
|
1220
|
+
identifier
|
|
1221
|
+
maxContentlets
|
|
1222
|
+
containerStructures {
|
|
1223
|
+
id
|
|
1224
|
+
code
|
|
1225
|
+
structureId
|
|
1226
|
+
containerId
|
|
1227
|
+
contentTypeVar
|
|
1228
|
+
containerInode
|
|
1229
|
+
}
|
|
1230
|
+
containerContentlets {
|
|
1231
|
+
uuid
|
|
1232
|
+
contentlets {
|
|
1233
|
+
${page ? DEFAULT_PAGE_CONTENTLETS_CONTENT : '_map'}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
layout {
|
|
1238
|
+
header
|
|
1239
|
+
footer
|
|
1240
|
+
body {
|
|
1241
|
+
rows {
|
|
1242
|
+
columns {
|
|
1243
|
+
leftOffset
|
|
1244
|
+
styleClass
|
|
1245
|
+
width
|
|
1246
|
+
left
|
|
1247
|
+
containers {
|
|
1248
|
+
identifier
|
|
1249
|
+
uuid
|
|
798
1250
|
}
|
|
799
|
-
|
|
800
|
-
if (!config.dotcmsUrl) {
|
|
801
|
-
throw new Error("Invalid configuration - 'dotcmsUrl' is required");
|
|
1251
|
+
}
|
|
802
1252
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
viewAs {
|
|
1256
|
+
visitor {
|
|
1257
|
+
persona {
|
|
1258
|
+
modDate
|
|
1259
|
+
inode
|
|
1260
|
+
name
|
|
1261
|
+
identifier
|
|
1262
|
+
keyTag
|
|
1263
|
+
photo {
|
|
1264
|
+
versionPath
|
|
1265
|
+
}
|
|
806
1266
|
}
|
|
807
|
-
|
|
808
|
-
|
|
1267
|
+
}
|
|
1268
|
+
persona {
|
|
1269
|
+
modDate
|
|
1270
|
+
inode
|
|
1271
|
+
name
|
|
1272
|
+
identifier
|
|
1273
|
+
keyTag
|
|
1274
|
+
photo {
|
|
1275
|
+
versionPath
|
|
809
1276
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1277
|
+
}
|
|
1278
|
+
language {
|
|
1279
|
+
id
|
|
1280
|
+
languageCode
|
|
1281
|
+
countryCode
|
|
1282
|
+
language
|
|
1283
|
+
country
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
${page ? ` fragment ClientPage on DotPage { ${page} } ` : ''}
|
|
1289
|
+
|
|
1290
|
+
${fragments ? fragments.join('\n\n') : ''}
|
|
1291
|
+
|
|
1292
|
+
query PageContent($url: String!, $languageId: String, $mode: String, $personaId: String, $fireRules: Boolean, $publishDate: String, $siteId: String, $variantName: String) {
|
|
1293
|
+
page: page(url: $url, languageId: $languageId, pageMode: $mode, persona: $personaId, fireRules: $fireRules, publishDate: $publishDate, site: $siteId, variantName: $variantName) {
|
|
1294
|
+
...DotCMSPage
|
|
1295
|
+
${page ? '...ClientPage' : ''}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
${additionalQueries}
|
|
1299
|
+
}
|
|
1300
|
+
`;
|
|
1301
|
+
};
|
|
1302
|
+
/**
|
|
1303
|
+
* Converts a record of query strings into a single GraphQL query string.
|
|
1304
|
+
*
|
|
1305
|
+
* @param {Record<string, string>} queryData - Object containing named query strings
|
|
1306
|
+
* @returns {string} Combined query string or empty string if no queryData provided
|
|
1307
|
+
*/
|
|
1308
|
+
function buildQuery(queryData) {
|
|
1309
|
+
if (!queryData)
|
|
1310
|
+
return '';
|
|
1311
|
+
return Object.entries(queryData)
|
|
1312
|
+
.map(([key, query]) => `${key}: ${query}`)
|
|
1313
|
+
.join(' ');
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Filters response data to include only specified keys.
|
|
1317
|
+
*
|
|
1318
|
+
* @param {Record<string, string>} responseData - Original response data object
|
|
1319
|
+
* @param {string[]} keys - Array of keys to extract from the response data
|
|
1320
|
+
* @returns {Record<string, string>} New object containing only the specified keys
|
|
1321
|
+
*/
|
|
1322
|
+
function mapResponseData(responseData, keys) {
|
|
1323
|
+
return keys.reduce((accumulator, key) => {
|
|
1324
|
+
if (responseData[key] !== undefined) {
|
|
1325
|
+
accumulator[key] = responseData[key];
|
|
1326
|
+
}
|
|
1327
|
+
return accumulator;
|
|
1328
|
+
}, {});
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Executes a GraphQL query against the DotCMS API.
|
|
1332
|
+
*
|
|
1333
|
+
* @param {Object} options - Options for the fetch request
|
|
1334
|
+
* @param {string} options.body - GraphQL query string
|
|
1335
|
+
* @param {Record<string, string>} options.headers - HTTP headers for the request
|
|
1336
|
+
* @returns {Promise<any>} Parsed JSON response from the GraphQL API
|
|
1337
|
+
* @throws {Error} If the HTTP response is not successful
|
|
1338
|
+
*/
|
|
1339
|
+
async function fetchGraphQL({ baseURL, body, headers }) {
|
|
1340
|
+
const url = new URL(baseURL);
|
|
1341
|
+
url.pathname = '/api/v1/graphql';
|
|
1342
|
+
const response = await fetch(url.toString(), {
|
|
1343
|
+
method: 'POST',
|
|
1344
|
+
body,
|
|
1345
|
+
headers
|
|
1346
|
+
});
|
|
1347
|
+
if (!response.ok) {
|
|
1348
|
+
const error = {
|
|
1349
|
+
status: response.status,
|
|
1350
|
+
message: ErrorMessages[response.status] || response.statusText
|
|
1351
|
+
};
|
|
1352
|
+
throw error;
|
|
822
1353
|
}
|
|
1354
|
+
return await response.json();
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* Client for interacting with the DotCMS Page API.
|
|
1359
|
+
* Provides methods to retrieve and manipulate pages.
|
|
1360
|
+
*/
|
|
1361
|
+
class PageClient {
|
|
823
1362
|
/**
|
|
824
|
-
*
|
|
825
|
-
* If an instance already exists, it returns the existing instance.
|
|
1363
|
+
* Creates a new PageClient instance.
|
|
826
1364
|
*
|
|
827
|
-
* @param {
|
|
828
|
-
* @
|
|
1365
|
+
* @param {DotCMSClientConfig} config - Configuration options for the DotCMS client
|
|
1366
|
+
* @param {RequestOptions} requestOptions - Options for fetch requests including authorization headers
|
|
829
1367
|
* @example
|
|
830
|
-
* ```
|
|
831
|
-
* const
|
|
1368
|
+
* ```typescript
|
|
1369
|
+
* const pageClient = new PageClient(
|
|
1370
|
+
* {
|
|
1371
|
+
* dotcmsUrl: 'https://demo.dotcms.com',
|
|
1372
|
+
* authToken: 'your-auth-token',
|
|
1373
|
+
* siteId: 'demo.dotcms.com'
|
|
1374
|
+
* },
|
|
1375
|
+
* {
|
|
1376
|
+
* headers: {
|
|
1377
|
+
* Authorization: 'Bearer your-auth-token'
|
|
1378
|
+
* }
|
|
1379
|
+
* }
|
|
1380
|
+
* );
|
|
832
1381
|
* ```
|
|
833
1382
|
*/
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
return this.instance ?? (this.instance = new DotCmsClient(config));
|
|
1383
|
+
constructor(config, requestOptions) {
|
|
1384
|
+
this.requestOptions = requestOptions;
|
|
1385
|
+
this.siteId = config.siteId || '';
|
|
1386
|
+
this.dotcmsUrl = config.dotcmsUrl;
|
|
839
1387
|
}
|
|
840
1388
|
/**
|
|
841
|
-
* Retrieves
|
|
1389
|
+
* Retrieves a page from DotCMS using GraphQL.
|
|
842
1390
|
*
|
|
843
|
-
* @
|
|
1391
|
+
* @param {string} url - The URL of the page to retrieve
|
|
1392
|
+
* @param {DotCMSPageRequestParams} [options] - Options for the request
|
|
1393
|
+
* @template T - The type of the page and content, defaults to DotCMSBasicPage and Record<string, unknown> | unknown
|
|
1394
|
+
* @returns {Promise<DotCMSComposedPageResponse<T>>} A Promise that resolves to the page data
|
|
1395
|
+
*
|
|
1396
|
+
* @example Using GraphQL
|
|
1397
|
+
* ```typescript
|
|
1398
|
+
* const page = await pageClient.get<{ page: MyPageWithBanners; content: { blogPosts: { blogTitle: string } } }>(
|
|
1399
|
+
* '/index',
|
|
1400
|
+
* {
|
|
1401
|
+
* languageId: '1',
|
|
1402
|
+
* mode: 'LIVE',
|
|
1403
|
+
* graphql: {
|
|
1404
|
+
* page: `
|
|
1405
|
+
* containers {
|
|
1406
|
+
* containerContentlets {
|
|
1407
|
+
* contentlets {
|
|
1408
|
+
* ... on Banner {
|
|
1409
|
+
* ...bannerFragment
|
|
1410
|
+
* }
|
|
1411
|
+
* }
|
|
1412
|
+
* }
|
|
1413
|
+
* `,
|
|
1414
|
+
* content: {
|
|
1415
|
+
* blogPosts: `
|
|
1416
|
+
* BlogCollection(limit: 3) {
|
|
1417
|
+
* ...blogFragment
|
|
1418
|
+
* }
|
|
1419
|
+
* `,
|
|
1420
|
+
* },
|
|
1421
|
+
* fragments: [
|
|
1422
|
+
* `
|
|
1423
|
+
* fragment bannerFragment on Banner {
|
|
1424
|
+
* caption
|
|
1425
|
+
* }
|
|
1426
|
+
* `,
|
|
1427
|
+
* `
|
|
1428
|
+
* fragment blogFragment on Blog {
|
|
1429
|
+
* title
|
|
1430
|
+
* urlTitle
|
|
1431
|
+
* }
|
|
1432
|
+
* `
|
|
1433
|
+
* ]
|
|
1434
|
+
* }
|
|
1435
|
+
* });
|
|
1436
|
+
* ```
|
|
844
1437
|
*/
|
|
845
|
-
|
|
846
|
-
|
|
1438
|
+
async get(url, options) {
|
|
1439
|
+
const { languageId = '1', mode = 'LIVE', siteId = this.siteId, fireRules = false, personaId, publishDate, variantName, graphql = {} } = options || {};
|
|
1440
|
+
const { page, content = {}, variables, fragments } = graphql;
|
|
1441
|
+
const contentQuery = buildQuery(content);
|
|
1442
|
+
const completeQuery = buildPageQuery({
|
|
1443
|
+
page,
|
|
1444
|
+
fragments,
|
|
1445
|
+
additionalQueries: contentQuery
|
|
1446
|
+
});
|
|
1447
|
+
const requestVariables = {
|
|
1448
|
+
// The url is expected to have a leading slash to comply on VanityURL Matching, some frameworks like Angular will not add the leading slash
|
|
1449
|
+
url: url.startsWith('/') ? url : `/${url}`,
|
|
1450
|
+
mode,
|
|
1451
|
+
languageId,
|
|
1452
|
+
personaId,
|
|
1453
|
+
fireRules,
|
|
1454
|
+
publishDate,
|
|
1455
|
+
siteId,
|
|
1456
|
+
variantName,
|
|
1457
|
+
...variables
|
|
1458
|
+
};
|
|
1459
|
+
const requestHeaders = this.requestOptions.headers;
|
|
1460
|
+
const requestBody = JSON.stringify({ query: completeQuery, variables: requestVariables });
|
|
1461
|
+
try {
|
|
1462
|
+
const { data, errors } = await fetchGraphQL({
|
|
1463
|
+
baseURL: this.dotcmsUrl,
|
|
1464
|
+
body: requestBody,
|
|
1465
|
+
headers: requestHeaders
|
|
1466
|
+
});
|
|
1467
|
+
if (errors) {
|
|
1468
|
+
errors.forEach((error) => {
|
|
1469
|
+
consola.error('[DotCMS GraphQL Error]: ', error.message);
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
const pageResponse = internal.graphqlToPageEntity(data);
|
|
1473
|
+
if (!pageResponse) {
|
|
1474
|
+
throw new Error('No page data found');
|
|
1475
|
+
}
|
|
1476
|
+
const contentResponse = mapResponseData(data, Object.keys(content));
|
|
1477
|
+
return {
|
|
1478
|
+
pageAsset: pageResponse,
|
|
1479
|
+
content: contentResponse,
|
|
1480
|
+
graphql: {
|
|
1481
|
+
query: completeQuery,
|
|
1482
|
+
variables: requestVariables
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
catch (error) {
|
|
1487
|
+
const errorMessage = {
|
|
1488
|
+
error,
|
|
1489
|
+
message: 'Failed to retrieve page data',
|
|
1490
|
+
graphql: {
|
|
1491
|
+
query: completeQuery,
|
|
1492
|
+
variables: requestVariables
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
throw errorMessage;
|
|
1496
|
+
}
|
|
847
1497
|
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
/**
|
|
1501
|
+
* Parses a string into a URL object.
|
|
1502
|
+
*
|
|
1503
|
+
* @param url - The URL string to parse
|
|
1504
|
+
* @returns A URL object if parsing is successful, undefined otherwise
|
|
1505
|
+
*/
|
|
1506
|
+
function parseURL(url) {
|
|
1507
|
+
try {
|
|
1508
|
+
return new URL(url);
|
|
1509
|
+
}
|
|
1510
|
+
catch {
|
|
1511
|
+
consola.consola.error('[DotCMS Client]: Invalid URL:', url);
|
|
1512
|
+
return undefined;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Default configuration for the DotCMS client.
|
|
1517
|
+
*/
|
|
1518
|
+
const defaultConfig = {
|
|
1519
|
+
dotcmsUrl: '',
|
|
1520
|
+
authToken: '',
|
|
1521
|
+
requestOptions: {}
|
|
1522
|
+
};
|
|
1523
|
+
/**
|
|
1524
|
+
* Client for interacting with the DotCMS REST API.
|
|
1525
|
+
* Provides access to content, page, and navigation functionality.
|
|
1526
|
+
*/
|
|
1527
|
+
class DotCMSClient {
|
|
848
1528
|
/**
|
|
849
|
-
*
|
|
1529
|
+
* Creates a new DotCMS client instance.
|
|
850
1530
|
*
|
|
851
|
-
* @
|
|
1531
|
+
* @param config - Configuration options for the client
|
|
1532
|
+
* @throws Warning if dotcmsUrl is invalid or authToken is missing
|
|
852
1533
|
*/
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1534
|
+
constructor(config = defaultConfig) {
|
|
1535
|
+
this.config = config;
|
|
1536
|
+
this.requestOptions = this.createAuthenticatedRequestOptions(this.config);
|
|
1537
|
+
// Initialize clients
|
|
1538
|
+
this.page = new PageClient(this.config, this.requestOptions);
|
|
1539
|
+
this.nav = new NavigationClient(this.config, this.requestOptions);
|
|
1540
|
+
this.content = new Content(this.requestOptions, this.config.dotcmsUrl);
|
|
857
1541
|
}
|
|
858
1542
|
/**
|
|
859
|
-
*
|
|
1543
|
+
* Creates request options with authentication headers.
|
|
860
1544
|
*
|
|
861
|
-
*
|
|
1545
|
+
* @param config - The client configuration
|
|
1546
|
+
* @returns Request options with authorization headers
|
|
862
1547
|
*/
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1548
|
+
createAuthenticatedRequestOptions(config) {
|
|
1549
|
+
return {
|
|
1550
|
+
...config.requestOptions,
|
|
1551
|
+
headers: {
|
|
1552
|
+
...config.requestOptions?.headers,
|
|
1553
|
+
Authorization: `Bearer ${config.authToken}`
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
867
1556
|
}
|
|
868
1557
|
}
|
|
869
|
-
_DotCmsClient_config = new WeakMap(), _DotCmsClient_requestOptions = new WeakMap(), _DotCmsClient_listeners = new WeakMap();
|
|
870
|
-
|
|
871
1558
|
/**
|
|
872
|
-
*
|
|
1559
|
+
* Creates and returns a new DotCMS client instance.
|
|
873
1560
|
*
|
|
874
|
-
* @param
|
|
875
|
-
* @returns
|
|
1561
|
+
* @param config - Configuration options for the client
|
|
1562
|
+
* @returns A configured DotCMS client instance
|
|
876
1563
|
* @example
|
|
877
|
-
* ```
|
|
878
|
-
* const
|
|
1564
|
+
* ```typescript
|
|
1565
|
+
* const client = dotCMSCreateClient({
|
|
1566
|
+
* dotcmsUrl: 'https://demo.dotcms.com',
|
|
1567
|
+
* authToken: 'your-auth-token'
|
|
1568
|
+
* });
|
|
1569
|
+
*
|
|
1570
|
+
* // Use the client to fetch content
|
|
1571
|
+
* const pages = await client.page.get('/about-us');
|
|
879
1572
|
* ```
|
|
880
1573
|
*/
|
|
881
|
-
const
|
|
882
|
-
const
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
if (copiedParams['publishDate']) {
|
|
896
|
-
finalParams['publishDate'] = copiedParams['publishDate'];
|
|
897
|
-
}
|
|
898
|
-
return {
|
|
899
|
-
path,
|
|
900
|
-
...finalParams
|
|
1574
|
+
const createDotCMSClient = (clientConfig) => {
|
|
1575
|
+
const { dotcmsUrl, authToken } = clientConfig || {};
|
|
1576
|
+
const instanceUrl = parseURL(dotcmsUrl)?.origin;
|
|
1577
|
+
if (!instanceUrl) {
|
|
1578
|
+
throw new TypeError("Invalid configuration - 'dotcmsUrl' must be a valid URL");
|
|
1579
|
+
}
|
|
1580
|
+
if (!authToken) {
|
|
1581
|
+
throw new TypeError("Invalid configuration - 'authToken' is required");
|
|
1582
|
+
}
|
|
1583
|
+
const config = {
|
|
1584
|
+
...clientConfig,
|
|
1585
|
+
authToken,
|
|
1586
|
+
dotcmsUrl: instanceUrl
|
|
901
1587
|
};
|
|
1588
|
+
return new DotCMSClient(config);
|
|
902
1589
|
};
|
|
903
1590
|
|
|
904
|
-
exports.
|
|
905
|
-
exports.DotCmsClient = DotCmsClient;
|
|
906
|
-
exports.destroyEditor = destroyEditor;
|
|
907
|
-
exports.editContentlet = editContentlet;
|
|
908
|
-
exports.getPageRequestParams = getPageRequestParams;
|
|
909
|
-
exports.initEditor = initEditor;
|
|
910
|
-
exports.initInlineEditing = initInlineEditing;
|
|
911
|
-
exports.isInsideEditor = isInsideEditor;
|
|
912
|
-
exports.postMessageToEditor = postMessageToEditor;
|
|
913
|
-
exports.reorderMenu = reorderMenu;
|
|
914
|
-
exports.updateNavigation = updateNavigation;
|
|
1591
|
+
exports.createDotCMSClient = createDotCMSClient;
|