apostrophe 3.8.1 → 3.9.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/.github/workflows/main.yml +45 -0
- package/CHANGELOG.md +22 -0
- package/README.md +1 -2
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +15 -10
- package/modules/@apostrophecms/asset/index.js +28 -2
- package/modules/@apostrophecms/attachment/index.js +0 -4
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +0 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +8 -0
- package/modules/@apostrophecms/i18n/i18n/es.json +1 -1
- package/modules/@apostrophecms/i18n/index.js +26 -5
- package/modules/@apostrophecms/job/index.js +10 -17
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +1 -1
- package/modules/@apostrophecms/module/index.js +1 -4
- package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +1 -1
- package/modules/@apostrophecms/page/index.js +47 -22
- package/modules/@apostrophecms/page-type/index.js +5 -1
- package/modules/@apostrophecms/piece-type/index.js +5 -0
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +2 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +27 -24
- package/modules/@apostrophecms/schema/index.js +0 -18
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +9 -3
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +11 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +0 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposLogo.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposLogoIcon.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposLogoPadless.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +0 -1
- package/modules/@apostrophecms/search/index.js +53 -33
- package/modules/@apostrophecms/task/index.js +5 -1
- package/modules/@apostrophecms/template/index.js +5 -11
- package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +9 -3
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +3 -2
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +5 -27
- package/package.json +1 -1
- package/.circleci/config.yml +0 -94
- package/.scratch.md +0 -2
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# This is a basic workflow to help you get started with Actions
|
|
2
|
+
|
|
3
|
+
name: Tests
|
|
4
|
+
|
|
5
|
+
# Controls when the action will run.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ '*' ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ '*' ]
|
|
11
|
+
|
|
12
|
+
# Allows you to run this workflow manually from the Actions tab
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
16
|
+
jobs:
|
|
17
|
+
# This workflow contains a single job called "build"
|
|
18
|
+
build:
|
|
19
|
+
# The type of runner that the job will run on
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
strategy:
|
|
22
|
+
matrix:
|
|
23
|
+
node-version: [12, 14, 16]
|
|
24
|
+
mongodb-version: [4.2, 4.4, 5.0]
|
|
25
|
+
|
|
26
|
+
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
27
|
+
steps:
|
|
28
|
+
- name: Git checkout
|
|
29
|
+
uses: actions/checkout@v2
|
|
30
|
+
|
|
31
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
32
|
+
uses: actions/setup-node@v1
|
|
33
|
+
with:
|
|
34
|
+
node-version: ${{ matrix.node-version }}
|
|
35
|
+
|
|
36
|
+
- name: Start MongoDB
|
|
37
|
+
uses: supercharge/mongodb-github-action@1.3.0
|
|
38
|
+
with:
|
|
39
|
+
mongodb-version: ${{ matrix.mongodb-version }}
|
|
40
|
+
|
|
41
|
+
- run: npm install
|
|
42
|
+
|
|
43
|
+
- run: npm test
|
|
44
|
+
env:
|
|
45
|
+
CI: true
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.9.0 - 2021-12-08
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Developers can now override any Vue component of the ApostropheCMS admin UI by providing a component of the same name in the `ui/apos/components` folder of their own module. This is not always the best approach, see the documentation for details.
|
|
8
|
+
* When running a job, we now trigger the notification before to run the job, this way the progress notification ID is available from the job and the notification can be dismissed if needed.
|
|
9
|
+
* Adds `maxUi`, `maxLabel`, `minUi`, and `minLabel` localization strings for array input and other UI.
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
* Fully removes references to the A2 `self.partial` module method. It appeared only once outside of comments, but was not actually used by the UI. The `self.render` method should be used for simple template rendering.
|
|
14
|
+
* Fixes string interpolation for the confirmation modal when publishing a page that has an unpublished parent page.
|
|
15
|
+
* No more "cannot set headers after they are sent to the client" and "req.res.redirect not defined" messages when handling URLs with extra trailing slashes.
|
|
16
|
+
* The `apos.util.runPlayers` method is not called until all of the widgets in a particular tree of areas and sub-areas have been added to the DOM. This means a parent area widget player will see the expected markup for any sub-widgets when the "Edit" button is clicked.
|
|
17
|
+
* Properly activates the `apostropheI18nDebugPlugin` i18next debugging plugin when using the `APOS_SHOW_I18N` environment variable. The full set of l10n emoji indicators previously available for the UI is now available for template and server-side strings.
|
|
18
|
+
* Actually registers piece types for site search unless the `searchable` option is `false`.
|
|
19
|
+
* Fixes the methods required for the search `index` task.
|
|
20
|
+
|
|
21
|
+
### Changes
|
|
22
|
+
|
|
23
|
+
* Adds localization keys for the password field component's min and max error messages.
|
|
24
|
+
|
|
3
25
|
## 3.8.1 - 2021-11-23
|
|
4
26
|
|
|
5
27
|
### Fixes
|
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
[](https://circleci.com/gh/apostrophecms/apostrophe/tree/main)
|
|
1
|
+

|
|
3
2
|
[](https://chat.apostrophecms.org)
|
|
4
3
|
|
|
5
4
|
<p align="center">
|
|
@@ -3,27 +3,32 @@ import { klona } from 'klona';
|
|
|
3
3
|
|
|
4
4
|
export default function() {
|
|
5
5
|
|
|
6
|
+
let widgetsRendering = 0;
|
|
7
|
+
|
|
6
8
|
createWidgetClipboardApp();
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
createAreaApps();
|
|
9
11
|
|
|
10
12
|
document.documentElement.style.setProperty('--a-widget-margin', apos.ui.widgetMargin);
|
|
11
13
|
|
|
14
|
+
apos.bus.$on('widget-rendering', function() {
|
|
15
|
+
widgetsRendering++;
|
|
16
|
+
});
|
|
17
|
+
|
|
12
18
|
apos.bus.$on('widget-rendered', function() {
|
|
13
|
-
|
|
19
|
+
widgetsRendering--;
|
|
20
|
+
createAreaAppsAndRunPlayersIfDone();
|
|
14
21
|
});
|
|
22
|
+
|
|
15
23
|
apos.bus.$on('refreshed', function() {
|
|
16
|
-
|
|
24
|
+
createAreaAppsAndRunPlayersIfDone();
|
|
17
25
|
});
|
|
18
26
|
|
|
19
|
-
function
|
|
20
|
-
// Doing this first allows markup to be captured for the editor
|
|
21
|
-
// before players alter it
|
|
27
|
+
function createAreaAppsAndRunPlayersIfDone() {
|
|
22
28
|
createAreaApps();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
apos.util.runPlayers();
|
|
29
|
+
if (widgetsRendering === 0) {
|
|
30
|
+
apos.util.runPlayers();
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
function createAreaApps() {
|
|
@@ -192,7 +192,10 @@ module.exports = {
|
|
|
192
192
|
let iconImports, componentImports, tiptapExtensionImports, appImports, indexJsImports, indexSassImports;
|
|
193
193
|
if (options.apos) {
|
|
194
194
|
iconImports = getIcons();
|
|
195
|
-
componentImports = getImports(`${source}/components`, '*.vue', {
|
|
195
|
+
componentImports = getImports(`${source}/components`, '*.vue', {
|
|
196
|
+
registerComponents: true,
|
|
197
|
+
importLastVersion: true
|
|
198
|
+
});
|
|
196
199
|
tiptapExtensionImports = getImports(`${source}/tiptap-extensions`, '*.js', { registerTiptapExtensions: true });
|
|
197
200
|
appImports = getImports(`${source}/apps`, '*.js', {
|
|
198
201
|
invokeApps: true,
|
|
@@ -441,6 +444,25 @@ module.exports = {
|
|
|
441
444
|
paths: []
|
|
442
445
|
};
|
|
443
446
|
|
|
447
|
+
if (options.importLastVersion) {
|
|
448
|
+
// Reverse the list so we can easily find the last configured import
|
|
449
|
+
// of a given component, allowing "improve" modules to win over
|
|
450
|
+
// the originals when shipping an override of a Vue component
|
|
451
|
+
// with the same name, and filter out earlier versions
|
|
452
|
+
components.reverse();
|
|
453
|
+
const seen = new Set();
|
|
454
|
+
components = components.filter(component => {
|
|
455
|
+
const name = getComponentName(component, options);
|
|
456
|
+
if (seen.has(name)) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
seen.add(name);
|
|
460
|
+
return true;
|
|
461
|
+
});
|
|
462
|
+
// Put the components back in their original order
|
|
463
|
+
components.reverse();
|
|
464
|
+
}
|
|
465
|
+
|
|
444
466
|
components.forEach((component, i) => {
|
|
445
467
|
if (options.requireDefaultExport) {
|
|
446
468
|
if (!fs.readFileSync(component, 'utf8').match(/export[\s\n]+default/)) {
|
|
@@ -453,7 +475,7 @@ module.exports = {
|
|
|
453
475
|
}
|
|
454
476
|
}
|
|
455
477
|
const jsFilename = JSON.stringify(component);
|
|
456
|
-
const name =
|
|
478
|
+
const name = getComponentName(component, options, i);
|
|
457
479
|
const jsName = JSON.stringify(name);
|
|
458
480
|
output.paths.push(component);
|
|
459
481
|
const importCode = `
|
|
@@ -492,6 +514,10 @@ module.exports = {
|
|
|
492
514
|
|
|
493
515
|
return pkgTimestamp > parseInt(timestamp);
|
|
494
516
|
}
|
|
517
|
+
|
|
518
|
+
function getComponentName(component, options, i) {
|
|
519
|
+
return require('path').basename(component).replace(/\.\w+/, '') + (options.enumerateImports ? `_${i}` : '');
|
|
520
|
+
}
|
|
495
521
|
}
|
|
496
522
|
}
|
|
497
523
|
};
|
|
@@ -230,7 +230,6 @@ module.exports = {
|
|
|
230
230
|
addFieldType() {
|
|
231
231
|
self.apos.schema.addFieldType({
|
|
232
232
|
name: self.name,
|
|
233
|
-
partial: self.fieldTypePartial,
|
|
234
233
|
convert: self.convert,
|
|
235
234
|
index: self.index,
|
|
236
235
|
register: self.register
|
|
@@ -274,9 +273,6 @@ module.exports = {
|
|
|
274
273
|
await self.db.replaceOne({ _id: info._id }, info);
|
|
275
274
|
object[field.name] = info;
|
|
276
275
|
},
|
|
277
|
-
fieldTypePartial(data) {
|
|
278
|
-
return self.partial('attachment', data);
|
|
279
|
-
},
|
|
280
276
|
index(value, field, texts) {
|
|
281
277
|
const silent = field.silent === undefined ? true : field.silent;
|
|
282
278
|
texts.push({
|
|
@@ -124,6 +124,7 @@
|
|
|
124
124
|
"errorPageMessage": "An error has occurred",
|
|
125
125
|
"errorPageStatusCode": "500",
|
|
126
126
|
"errorPageTitle": "An error has occurred",
|
|
127
|
+
"errorBatchOperationNoti": "Batch operation {{ operation }} failed.",
|
|
127
128
|
"everythingElse": "Everything Else",
|
|
128
129
|
"exit": "Exit",
|
|
129
130
|
"fetchPublishedVersionFailed": "An error occurred fetching the published version of the document.",
|
|
@@ -176,6 +177,8 @@
|
|
|
176
177
|
"manageDocType": "Manage {{ type }}",
|
|
177
178
|
"manageDraftSubmissions": "Manage Draft Submissions",
|
|
178
179
|
"managePages": "Manage Pages",
|
|
180
|
+
"maxLabel": "Max:",
|
|
181
|
+
"maxUi": "Max: {{ number }}",
|
|
179
182
|
"mediaCreatedDate": "Uploaded: {{ createdDate }}",
|
|
180
183
|
"mediaDimensions": "Dimensions: {{ width }} 𝗑 {{ height }}",
|
|
181
184
|
"mediaFileSize": "File Size: {{ fileSize }}",
|
|
@@ -183,6 +186,8 @@
|
|
|
183
186
|
"mediaMB": "{{ size }}MB",
|
|
184
187
|
"mediaUploadViaDrop": "Drop ’em when you’re ready",
|
|
185
188
|
"mediaUploadViaExplorer": "Or click to open the file explorer",
|
|
189
|
+
"minLabel": "Min:",
|
|
190
|
+
"minUi": "Min: {{ number }}",
|
|
186
191
|
"modify": "Modify",
|
|
187
192
|
"modifyOrDelete": "Modify / Delete",
|
|
188
193
|
"moreOptions": "More Options",
|
|
@@ -213,6 +218,7 @@
|
|
|
213
218
|
"notYetPublished": "This document hasn't been published yet.",
|
|
214
219
|
"nudgeDown": "Nudge Down",
|
|
215
220
|
"nudgeUp": "Nudge Up",
|
|
221
|
+
"numberAdded": "{{ count }} Added",
|
|
216
222
|
"office": "Office",
|
|
217
223
|
"openGlobal": "Open Global Site Settings",
|
|
218
224
|
"page": "Page",
|
|
@@ -248,6 +254,8 @@
|
|
|
248
254
|
"richTextUndo": "Undo",
|
|
249
255
|
"richTextStyleConfigWarning": "Misconfigured rich text style: label: {{ label }}, {{ tag }}",
|
|
250
256
|
"password": "Password",
|
|
257
|
+
"passwordErrorMin": "Minimum of {{ min }} characters",
|
|
258
|
+
"passwordErrorMax": "Maximum of {{ max }} characters",
|
|
251
259
|
"passwordResetRequest": "Your request to reset your password on {{ site }}",
|
|
252
260
|
"pasteWidget": "Paste {{ widget }}",
|
|
253
261
|
"pending": "Pending",
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
"notFoundPageStatusCode": "404",
|
|
205
205
|
"notFoundPageTitle": "404 - Página no encontrada",
|
|
206
206
|
"notInLocale": "La página actual no existe en {{ label }}. ¿Traducir la versión desde la configuración regional {{ currentLocale }}?",
|
|
207
|
-
"noTypeFound": "Ningún {{ type }}
|
|
207
|
+
"noTypeFound": "Ningún {{ type }} Encontrado",
|
|
208
208
|
"parentNotLocalized": "Primero traduzca la configuración regional de la página principal",
|
|
209
209
|
"notYetPublished": "Este documento aún no ha sido publicado.",
|
|
210
210
|
"nudgeDown": "Mover Hacia Arriba",
|
|
@@ -13,11 +13,27 @@ const ExpressSessionCookie = require('express-session/session/cookie');
|
|
|
13
13
|
|
|
14
14
|
const apostropheI18nDebugPlugin = {
|
|
15
15
|
type: 'postProcessor',
|
|
16
|
-
name: '
|
|
16
|
+
name: 'apostropheI18nDebugPlugin',
|
|
17
17
|
process(value, key, options, translator) {
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
18
|
+
// The key is passed as an array (theoretically to include multiple keys).
|
|
19
|
+
// We confirm that and grab the primary one for comparison.
|
|
20
|
+
const l10nKey = Array.isArray(key) ? key[0] : key;
|
|
21
|
+
|
|
22
|
+
if (value === l10nKey) {
|
|
23
|
+
if (l10nKey.match(/^\S+:/)) {
|
|
24
|
+
// The l10n key does not have a value assigned (or the key is
|
|
25
|
+
// actually the same as the phrase). The key seems to have a
|
|
26
|
+
// namespace, so might be from the Apostrophe UI.
|
|
27
|
+
return `❌ ${value}`;
|
|
28
|
+
} else {
|
|
29
|
+
// The l10n key does not have a value assigned (or the key is
|
|
30
|
+
// actually the same as the phrase). It is in the default namespace.
|
|
31
|
+
return `🕳 ${value}`;
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
// The phrase is fully localized.
|
|
35
|
+
return `🌍 ${value}`;
|
|
36
|
+
}
|
|
21
37
|
}
|
|
22
38
|
};
|
|
23
39
|
|
|
@@ -73,7 +89,12 @@ module.exports = {
|
|
|
73
89
|
if (self.show) {
|
|
74
90
|
self.i18next.use(apostropheI18nDebugPlugin);
|
|
75
91
|
}
|
|
76
|
-
|
|
92
|
+
|
|
93
|
+
const i18nextOptions = self.show ? {
|
|
94
|
+
postProcess: 'apostropheI18nDebugPlugin'
|
|
95
|
+
} : {};
|
|
96
|
+
|
|
97
|
+
await self.i18next.init(i18nextOptions);
|
|
77
98
|
self.addInitialResources();
|
|
78
99
|
self.enableBrowserData();
|
|
79
100
|
},
|
|
@@ -189,38 +189,31 @@ module.exports = {
|
|
|
189
189
|
async run(req, doTheWork, options = {}) {
|
|
190
190
|
const res = req.res;
|
|
191
191
|
let job;
|
|
192
|
-
let notification;
|
|
193
192
|
let total;
|
|
194
193
|
|
|
195
194
|
try {
|
|
196
195
|
job = await self.start(options);
|
|
197
|
-
run();
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// It's only relevant to pass a job ID to the notification if
|
|
202
|
-
// the notification will show progress. Without a total number we
|
|
203
|
-
// can't show progress.
|
|
204
|
-
jobId: total && job._id,
|
|
205
|
-
count: total
|
|
197
|
+
const notification = await self.triggerNotification(req, 'progress', {
|
|
198
|
+
jobId: job._id
|
|
206
199
|
});
|
|
207
200
|
|
|
201
|
+
run({ notificationId: notification.noteId });
|
|
202
|
+
|
|
208
203
|
return {
|
|
209
204
|
jobId: job._id
|
|
210
205
|
};
|
|
211
206
|
} catch (err) {
|
|
212
207
|
self.apos.util.error(err);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// stopped talking to the user
|
|
216
|
-
self.apos.util.error(err);
|
|
217
|
-
} else {
|
|
208
|
+
|
|
209
|
+
if (!job) {
|
|
218
210
|
// If we never made a job, then we never responded
|
|
211
|
+
// otherwise we already stopped talking to the user
|
|
219
212
|
return res.status(500).send('error');
|
|
220
213
|
}
|
|
221
214
|
}
|
|
222
215
|
|
|
223
|
-
async function run() {
|
|
216
|
+
async function run(info) {
|
|
224
217
|
let results;
|
|
225
218
|
let good = false;
|
|
226
219
|
try {
|
|
@@ -240,7 +233,7 @@ module.exports = {
|
|
|
240
233
|
setResults (_results) {
|
|
241
234
|
results = _results;
|
|
242
235
|
}
|
|
243
|
-
});
|
|
236
|
+
}, info);
|
|
244
237
|
good = true;
|
|
245
238
|
} finally {
|
|
246
239
|
await self.end(job, good, results);
|
|
@@ -253,7 +246,7 @@ module.exports = {
|
|
|
253
246
|
// Dismiss the progress notification. It will delay 4 seconds
|
|
254
247
|
// because "completed" notification will dismiss in 5 and we want
|
|
255
248
|
// to maintain the feeling of process order for users.
|
|
256
|
-
await self.apos.notification.dismiss(req,
|
|
249
|
+
await self.apos.notification.dismiss(req, info.notificationId, 4000);
|
|
257
250
|
}
|
|
258
251
|
}
|
|
259
252
|
},
|
|
@@ -292,10 +292,7 @@ module.exports = {
|
|
|
292
292
|
// your version wins if it exists.
|
|
293
293
|
//
|
|
294
294
|
// You MUST pass req as the first argument. This allows
|
|
295
|
-
// internationalization/localization to work.
|
|
296
|
-
// are writing a Nunjucks helper function, use
|
|
297
|
-
// self.partial instead. This method is primarily used
|
|
298
|
-
// to implement routes that respond with HTML fragments.
|
|
295
|
+
// internationalization/localization to work.
|
|
299
296
|
//
|
|
300
297
|
// All properties of `data` appear in Nunjucks templates as
|
|
301
298
|
// properties of the `data` object. Nunjucks helper functions
|
|
@@ -173,7 +173,7 @@ export default {
|
|
|
173
173
|
this.job.processed = job.processed;
|
|
174
174
|
this.job.percentage = job.percentage;
|
|
175
175
|
|
|
176
|
-
if (this.job.processed < this.job.total) {
|
|
176
|
+
if (this.job.processed < this.job.total && !job.ended) {
|
|
177
177
|
await new Promise(resolve => {
|
|
178
178
|
setTimeout(resolve, 500);
|
|
179
179
|
});
|
|
@@ -1385,6 +1385,24 @@ database.`);
|
|
|
1385
1385
|
// home page.
|
|
1386
1386
|
async serveGetPage(req) {
|
|
1387
1387
|
req.slug = req.params[0];
|
|
1388
|
+
self.normalizeSlug(req);
|
|
1389
|
+
// Had to change the URL, so redirect to it. TODO: this
|
|
1390
|
+
// contains an assumption that we are mounted at /
|
|
1391
|
+
if (req.slug !== req.params[0]) {
|
|
1392
|
+
req.redirect = req.slug;
|
|
1393
|
+
}
|
|
1394
|
+
const builders = self.getServePageBuilders();
|
|
1395
|
+
const query = self.find(req);
|
|
1396
|
+
query.applyBuilders(builders);
|
|
1397
|
+
self.matchPageAndPrefixes(query, req.slug);
|
|
1398
|
+
await self.emit('serveQuery', query);
|
|
1399
|
+
req.data.bestPage = await query.toObject();
|
|
1400
|
+
self.evaluatePageMatch(req);
|
|
1401
|
+
},
|
|
1402
|
+
// Normalize req.slug to account for unneeded trailing whitespace,
|
|
1403
|
+
// trailing slashes other than the root, and double slash based open
|
|
1404
|
+
// redirect attempts
|
|
1405
|
+
normalizeSlug(req) {
|
|
1388
1406
|
// Fix common screwups in URLs: leading/trailing whitespace,
|
|
1389
1407
|
// presence of trailing slashes (but always restore the
|
|
1390
1408
|
// leading slash). Express leaves escape codes uninterpreted
|
|
@@ -1399,18 +1417,6 @@ database.`);
|
|
|
1399
1417
|
if (!req.slug.length || req.slug.charAt(0) !== '/') {
|
|
1400
1418
|
req.slug = '/' + req.slug;
|
|
1401
1419
|
}
|
|
1402
|
-
// Had to change the URL, so redirect to it. TODO: this
|
|
1403
|
-
// contains an assumption that we are mounted at /
|
|
1404
|
-
if (req.slug !== req.params[0]) {
|
|
1405
|
-
return req.res.redirect(req.slug);
|
|
1406
|
-
}
|
|
1407
|
-
const builders = self.getServePageBuilders();
|
|
1408
|
-
const query = self.find(req);
|
|
1409
|
-
query.applyBuilders(builders);
|
|
1410
|
-
self.matchPageAndPrefixes(query, req.slug);
|
|
1411
|
-
await self.emit('serveQuery', query);
|
|
1412
|
-
req.data.bestPage = await query.toObject();
|
|
1413
|
-
self.evaluatePageMatch(req);
|
|
1414
1420
|
},
|
|
1415
1421
|
// Remove trailing slashes from a slug. This is factored out
|
|
1416
1422
|
// so that it can be overridden, for instance by the
|
|
@@ -1446,8 +1452,27 @@ database.`);
|
|
|
1446
1452
|
mode: 'draft',
|
|
1447
1453
|
locale: req.locale
|
|
1448
1454
|
});
|
|
1449
|
-
|
|
1450
|
-
|
|
1455
|
+
let again;
|
|
1456
|
+
do {
|
|
1457
|
+
again = false;
|
|
1458
|
+
await self.serveGetPage(testReq);
|
|
1459
|
+
await self.emit('serve', testReq);
|
|
1460
|
+
if (testReq.res.redirectedTo) {
|
|
1461
|
+
again = true;
|
|
1462
|
+
testReq.url = testReq.res.redirectedTo;
|
|
1463
|
+
const qat = testReq.url.indexOf('?');
|
|
1464
|
+
if (qat >= 0) {
|
|
1465
|
+
testReq.slug = testReq.url.substring(0, qat);
|
|
1466
|
+
} else {
|
|
1467
|
+
testReq.slug = testReq.url;
|
|
1468
|
+
}
|
|
1469
|
+
testReq.path = testReq.slug;
|
|
1470
|
+
testReq.params = {
|
|
1471
|
+
0: testReq.path
|
|
1472
|
+
};
|
|
1473
|
+
testReq.res.redirectedTo = null;
|
|
1474
|
+
}
|
|
1475
|
+
} while (again);
|
|
1451
1476
|
if (self.isFound(testReq)) {
|
|
1452
1477
|
req.redirect = self.apos.url.build(req.url, {
|
|
1453
1478
|
aposMode: 'draft'
|
|
@@ -1473,14 +1498,6 @@ database.`);
|
|
|
1473
1498
|
},
|
|
1474
1499
|
async serveDeliver(req, err) {
|
|
1475
1500
|
let providePage = true;
|
|
1476
|
-
// A2 treats req as a notepad of things we'd
|
|
1477
|
-
// like to happen in res; that allows various
|
|
1478
|
-
// pageServe methods to override each other.
|
|
1479
|
-
// Now we're finally ready to enact those
|
|
1480
|
-
// things on res
|
|
1481
|
-
if (req.contentType) {
|
|
1482
|
-
req.res.setHeader('Content-Type', req.contentType);
|
|
1483
|
-
}
|
|
1484
1501
|
if (req.statusCode) {
|
|
1485
1502
|
req.res.statusCode = req.statusCode;
|
|
1486
1503
|
}
|
|
@@ -1500,6 +1517,14 @@ database.`);
|
|
|
1500
1517
|
}
|
|
1501
1518
|
return req.res.redirect(status, req.redirect);
|
|
1502
1519
|
}
|
|
1520
|
+
// Apostrophe treats req as a notepad of things we'd
|
|
1521
|
+
// like to happen in res; that allows various
|
|
1522
|
+
// pageServe methods to override each other.
|
|
1523
|
+
// Now we're finally ready to enact those
|
|
1524
|
+
// things on res
|
|
1525
|
+
if (req.contentType) {
|
|
1526
|
+
req.res.setHeader('Content-Type', req.contentType);
|
|
1527
|
+
}
|
|
1503
1528
|
// Handle 500 errors
|
|
1504
1529
|
if (err) {
|
|
1505
1530
|
self.apos.util.error(err);
|
|
@@ -152,7 +152,11 @@ module.exports = {
|
|
|
152
152
|
type: 1
|
|
153
153
|
}).toArray();
|
|
154
154
|
throw self.apos.error('invalid', {
|
|
155
|
-
unpublishedAncestors: draftAncestors.filter(draftAncestor =>
|
|
155
|
+
unpublishedAncestors: draftAncestors.filter(draftAncestor => {
|
|
156
|
+
return !publishedAncestors.find(publishedAncestor => {
|
|
157
|
+
return draftAncestor.aposDocId === publishedAncestor.aposDocId;
|
|
158
|
+
});
|
|
159
|
+
})
|
|
156
160
|
});
|
|
157
161
|
}
|
|
158
162
|
}
|
|
@@ -481,10 +481,11 @@ export default {
|
|
|
481
481
|
}
|
|
482
482
|
});
|
|
483
483
|
} catch (error) {
|
|
484
|
-
apos.notify('
|
|
484
|
+
apos.notify('apostrophe:errorBatchOperationNoti', {
|
|
485
485
|
interpolate: { operation: label },
|
|
486
486
|
type: 'danger'
|
|
487
487
|
});
|
|
488
|
+
console.error(error);
|
|
488
489
|
}
|
|
489
490
|
}
|
|
490
491
|
},
|
|
@@ -4,26 +4,26 @@
|
|
|
4
4
|
:duration="300"
|
|
5
5
|
>
|
|
6
6
|
<div
|
|
7
|
-
v-
|
|
7
|
+
v-show="showSelectAll"
|
|
8
8
|
class="apos-select-box"
|
|
9
9
|
>
|
|
10
10
|
<div class="apos-select-box__content">
|
|
11
11
|
<p class="apos-select-box__text">
|
|
12
12
|
{{ selectBoxMessage }}
|
|
13
|
-
<
|
|
13
|
+
<AposButton
|
|
14
14
|
v-if="!allPiecesSelection.isSelected"
|
|
15
|
-
|
|
15
|
+
type="subtle" :modifiers="['inline', 'small', 'no-motion']"
|
|
16
|
+
:label="selectBoxMessageButton" class="apos-select-box__select-all"
|
|
16
17
|
@click="$emit('select-all')"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<button
|
|
18
|
+
text-color="var(--a-primary)"
|
|
19
|
+
/>
|
|
20
|
+
<AposButton
|
|
21
21
|
v-else
|
|
22
|
-
|
|
22
|
+
type="subtle" :modifiers="['inline', 'small', 'no-motion']"
|
|
23
|
+
label="apostrophe:clearSelection" class="apos-select-box__select-all"
|
|
24
|
+
text-color="var(--a-primary)"
|
|
23
25
|
@click="clearSelection"
|
|
24
|
-
|
|
25
|
-
{{ $t('apostrophe:clearSelection') }}.
|
|
26
|
-
</button>
|
|
26
|
+
/>
|
|
27
27
|
</p>
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
@@ -56,6 +56,15 @@ export default {
|
|
|
56
56
|
},
|
|
57
57
|
emits: [ 'select-all', 'clear-select', 'set-all-pieces-selection' ],
|
|
58
58
|
computed: {
|
|
59
|
+
showSelectAll() {
|
|
60
|
+
if (
|
|
61
|
+
this.allPiecesSelection.isSelected ||
|
|
62
|
+
(this.selectedState === 'checked' && this.allPiecesSelection.total > this.displayedItems)
|
|
63
|
+
) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
},
|
|
59
68
|
selectBoxMessage () {
|
|
60
69
|
const checkedCount = this.checkedIds.length;
|
|
61
70
|
const showAllWord = (checkedCount === this.allPiecesSelection.total) &&
|
|
@@ -104,35 +113,29 @@ export default {
|
|
|
104
113
|
.apos-select-box {
|
|
105
114
|
box-sizing: border-box;
|
|
106
115
|
overflow: hidden;
|
|
107
|
-
height:
|
|
108
|
-
transition:
|
|
116
|
+
max-height: 65px;
|
|
117
|
+
transition: max-height 200ms ease-in;
|
|
109
118
|
|
|
110
119
|
&.collapse-enter, &.collapse-leave-to {
|
|
111
|
-
height: 0;
|
|
120
|
+
max-height: 0;
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
&__content {
|
|
115
124
|
display: flex;
|
|
116
125
|
align-items: center;
|
|
117
126
|
justify-content: center;
|
|
118
|
-
background-color: var(--a-base-
|
|
127
|
+
background-color: var(--a-base-10);
|
|
119
128
|
margin-top: 1rem;
|
|
120
129
|
color: var(--a-text-primary);
|
|
130
|
+
border-radius: var(--a-border-radius);
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
&__text {
|
|
124
|
-
@include type-
|
|
134
|
+
@include type-base;
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
&__select-all {
|
|
128
|
-
|
|
129
|
-
cursor: pointer;
|
|
130
|
-
margin-left: 0.4rem;
|
|
131
|
-
border: none;
|
|
132
|
-
|
|
133
|
-
&:hover {
|
|
134
|
-
text-decoration: underline;
|
|
135
|
-
}
|
|
138
|
+
margin-left: $spacing-half;
|
|
136
139
|
}
|
|
137
140
|
}
|
|
138
141
|
</style>
|
|
@@ -1292,24 +1292,6 @@ module.exports = {
|
|
|
1292
1292
|
return field.group && field.group.name === defaultGroup.name;
|
|
1293
1293
|
}));
|
|
1294
1294
|
|
|
1295
|
-
_.each(schema, function (field) {
|
|
1296
|
-
|
|
1297
|
-
// A field can have a custom template, which can be a
|
|
1298
|
-
// template name (relative to the @apostrophecms/schema module)
|
|
1299
|
-
// or a function (called to render it)
|
|
1300
|
-
|
|
1301
|
-
if (field.template) {
|
|
1302
|
-
if (typeof field.template === 'string') {
|
|
1303
|
-
field.partial = self.partialer(field.template);
|
|
1304
|
-
delete field.template;
|
|
1305
|
-
} else {
|
|
1306
|
-
field.partial = field.template;
|
|
1307
|
-
delete field.template;
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
});
|
|
1312
|
-
|
|
1313
1295
|
// Shallowly clone the fields. This allows modules
|
|
1314
1296
|
// like workflow to patch schema fields of various modules
|
|
1315
1297
|
// without inadvertently impacting other apos instances
|
|
@@ -150,7 +150,9 @@ export default {
|
|
|
150
150
|
return (this.field.schema || []).filter(field => apos.schema.components.fields[field.type]);
|
|
151
151
|
},
|
|
152
152
|
countLabel() {
|
|
153
|
-
return
|
|
153
|
+
return this.$t('apostrophe:numberAdded', {
|
|
154
|
+
count: this.next.length
|
|
155
|
+
});
|
|
154
156
|
},
|
|
155
157
|
// Here in the array editor we use effectiveMin to factor in the
|
|
156
158
|
// required property because there is no other good place to do that,
|
|
@@ -158,14 +160,18 @@ export default {
|
|
|
158
160
|
// representation of "required".
|
|
159
161
|
minLabel() {
|
|
160
162
|
if (this.effectiveMin) {
|
|
161
|
-
return
|
|
163
|
+
return this.$t('apostrophe:minUi', {
|
|
164
|
+
number: this.effectiveMin
|
|
165
|
+
});
|
|
162
166
|
} else {
|
|
163
167
|
return false;
|
|
164
168
|
}
|
|
165
169
|
},
|
|
166
170
|
maxLabel() {
|
|
167
171
|
if ((typeof this.field.max) === 'number') {
|
|
168
|
-
return
|
|
172
|
+
return this.$t('apostrophe:maxUi', {
|
|
173
|
+
number: this.field.max
|
|
174
|
+
});
|
|
169
175
|
} else {
|
|
170
176
|
return false;
|
|
171
177
|
}
|
|
@@ -28,7 +28,7 @@ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin'
|
|
|
28
28
|
export default {
|
|
29
29
|
name: 'AposInputPassword',
|
|
30
30
|
mixins: [ AposInputMixin ],
|
|
31
|
-
emits: ['return'],
|
|
31
|
+
emits: [ 'return' ],
|
|
32
32
|
computed: {
|
|
33
33
|
tabindex () {
|
|
34
34
|
return this.field.disableFocus ? '-1' : '0';
|
|
@@ -43,12 +43,20 @@ export default {
|
|
|
43
43
|
}
|
|
44
44
|
if (this.field.min) {
|
|
45
45
|
if (value.length && (value.length < this.field.min)) {
|
|
46
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
message: this.$t('apostrophe:passwordErrorMin', {
|
|
48
|
+
min: this.field.min
|
|
49
|
+
})
|
|
50
|
+
};
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
if (this.field.max) {
|
|
50
54
|
if (value.length && (value.length > this.field.max)) {
|
|
51
|
-
return {
|
|
55
|
+
return {
|
|
56
|
+
message: this.$t('apostrophe:passwordErrorMax', {
|
|
57
|
+
max: this.field.max
|
|
58
|
+
})
|
|
59
|
+
};
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
return false;
|
|
@@ -56,4 +64,3 @@ export default {
|
|
|
56
64
|
}
|
|
57
65
|
};
|
|
58
66
|
</script>
|
|
59
|
-
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
<div class="apos-range__scale">
|
|
21
21
|
<span>
|
|
22
22
|
<span class="apos-sr-only">
|
|
23
|
-
|
|
23
|
+
{{ $t('apostrophe:minLabel') }}
|
|
24
24
|
</span>
|
|
25
25
|
{{ minLabel }}
|
|
26
26
|
</span>
|
|
27
27
|
<span>
|
|
28
28
|
<span class="apos-sr-only">
|
|
29
|
-
|
|
29
|
+
{{ $t('apostrophe:maxLabel') }}
|
|
30
30
|
</span>
|
|
31
31
|
{{ maxLabel }}
|
|
32
32
|
</span>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
<div class="apos-field__wrapper">
|
|
3
3
|
<component :is="wrapEl" :class="classList">
|
|
4
4
|
<div class="apos-field__info">
|
|
5
|
-
<!-- TODO i18n -->
|
|
6
5
|
<component
|
|
7
6
|
v-if="field.label" :class="{'apos-sr-only': field.hideLabel }"
|
|
8
7
|
class="apos-field__label"
|
|
@@ -32,7 +31,6 @@
|
|
|
32
31
|
/>
|
|
33
32
|
</span>
|
|
34
33
|
</component>
|
|
35
|
-
<!-- TODO i18n -->
|
|
36
34
|
<p
|
|
37
35
|
v-if="(field.help || field.htmlHelp) && !displayOptions.helpTooltip"
|
|
38
36
|
class="apos-field__help"
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
// are edge cases not relevant enough to explicitly offer a filter for, but
|
|
45
45
|
// which should nevertheless be included in results.
|
|
46
46
|
|
|
47
|
+
const { stripIndent } = require('common-tags');
|
|
47
48
|
const _ = require('lodash');
|
|
48
49
|
|
|
49
50
|
module.exports = {
|
|
@@ -98,37 +99,7 @@ module.exports = {
|
|
|
98
99
|
},
|
|
99
100
|
'@apostrophecms/doc-type:beforeSave': {
|
|
100
101
|
indexDoc(req, doc) {
|
|
101
|
-
|
|
102
|
-
const texts = self.getSearchTexts(doc);
|
|
103
|
-
|
|
104
|
-
_.each(texts, function (text) {
|
|
105
|
-
if (text.text === undefined) {
|
|
106
|
-
text.text = '';
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const highTexts = _.filter(texts, function (text) {
|
|
111
|
-
return text.weight > 10;
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const searchSummary = _.map(_.filter(texts, function (text) {
|
|
115
|
-
return !text.silent;
|
|
116
|
-
}), function (text) {
|
|
117
|
-
return text.text;
|
|
118
|
-
}).join(' ');
|
|
119
|
-
const highText = self.boilTexts(highTexts);
|
|
120
|
-
const lowText = self.boilTexts(texts);
|
|
121
|
-
const titleSortified = self.apos.util.sortify(doc.title);
|
|
122
|
-
const highWords = _.uniq(highText.split(/ /));
|
|
123
|
-
|
|
124
|
-
// merge our doc with its various search texts
|
|
125
|
-
_.assign(doc, {
|
|
126
|
-
titleSortified: titleSortified,
|
|
127
|
-
highSearchText: highText,
|
|
128
|
-
highSearchWords: highWords,
|
|
129
|
-
lowSearchText: lowText,
|
|
130
|
-
searchSummary: searchSummary
|
|
131
|
-
});
|
|
102
|
+
self.indexDoc(req, doc);
|
|
132
103
|
}
|
|
133
104
|
}
|
|
134
105
|
};
|
|
@@ -261,13 +232,58 @@ module.exports = {
|
|
|
261
232
|
self.dispatch('/', self.indexPage);
|
|
262
233
|
},
|
|
263
234
|
|
|
235
|
+
indexDoc(req, doc) {
|
|
236
|
+
|
|
237
|
+
const texts = self.getSearchTexts(doc);
|
|
238
|
+
|
|
239
|
+
_.each(texts, function (text) {
|
|
240
|
+
if (text.text === undefined) {
|
|
241
|
+
text.text = '';
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const highTexts = _.filter(texts, function (text) {
|
|
246
|
+
return text.weight > 10;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const searchSummary = _.map(_.filter(texts, function (text) {
|
|
250
|
+
return !text.silent;
|
|
251
|
+
}), function (text) {
|
|
252
|
+
return text.text;
|
|
253
|
+
}).join(' ');
|
|
254
|
+
const highText = self.boilTexts(highTexts);
|
|
255
|
+
const lowText = self.boilTexts(texts);
|
|
256
|
+
const titleSortified = self.apos.util.sortify(doc.title);
|
|
257
|
+
const highWords = _.uniq(highText.split(/ /));
|
|
258
|
+
|
|
259
|
+
// merge our doc with its various search texts
|
|
260
|
+
_.assign(doc, {
|
|
261
|
+
titleSortified: titleSortified,
|
|
262
|
+
highSearchText: highText,
|
|
263
|
+
highSearchWords: highWords,
|
|
264
|
+
lowSearchText: lowText,
|
|
265
|
+
searchSummary: searchSummary
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
|
|
264
269
|
// Indexes just one document as part of the implementation of the
|
|
265
270
|
// `@apostrophecms/search:index` task. This isn't the method you want to
|
|
266
271
|
// override. See `indexDoc` and `getSearchTexts`
|
|
267
272
|
|
|
268
273
|
async indexTaskOne(req, doc) {
|
|
269
274
|
self.indexDoc(req, doc);
|
|
270
|
-
|
|
275
|
+
|
|
276
|
+
return self.apos.doc.db.updateOne({
|
|
277
|
+
_id: doc._id
|
|
278
|
+
}, {
|
|
279
|
+
$set: {
|
|
280
|
+
titleSortified: doc.titleSortified,
|
|
281
|
+
highSearchText: doc.highSearchText,
|
|
282
|
+
highSearchWords: doc.highSearchWords,
|
|
283
|
+
lowSearchText: doc.lowSearchText,
|
|
284
|
+
searchSummary: doc.searchSummary
|
|
285
|
+
}
|
|
286
|
+
});
|
|
271
287
|
},
|
|
272
288
|
|
|
273
289
|
// Returns texts which are a reasonable basis for
|
|
@@ -348,7 +364,11 @@ module.exports = {
|
|
|
348
364
|
tasks(self) {
|
|
349
365
|
return {
|
|
350
366
|
index: {
|
|
351
|
-
usage:
|
|
367
|
+
usage: stripIndent`
|
|
368
|
+
Rebuild the search index. Normally this happens automatically.
|
|
369
|
+
This should only be needed if you have changed the"searchable" property
|
|
370
|
+
for various fields or types.
|
|
371
|
+
`,
|
|
352
372
|
task(argv) {
|
|
353
373
|
const req = self.apos.task.getReq();
|
|
354
374
|
return self.apos.migration.eachDoc({}, _.partial(self.indexTaskOne, req));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Implements template rendering via Nunjucks. **You should use the
|
|
2
|
-
// `self.render`
|
|
2
|
+
// `self.render` method of *your own* module**,
|
|
3
3
|
// which exist courtesy of [@apostrophecms/module](../@apostrophecms/module/index.html)
|
|
4
4
|
// and invoke methods of this module more conveniently for you.
|
|
5
5
|
//
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// you have a custom version of Nunjucks that is compatible.
|
|
22
22
|
//
|
|
23
23
|
// ### `viewsFolderFallback`: specifies a folder to be checked for templates
|
|
24
|
-
// if they are not found in the module that called `self.render`
|
|
24
|
+
// if they are not found in the module that called `self.render`
|
|
25
25
|
// or those it extends. This is a handy place for project-wide macro files.
|
|
26
26
|
// Often set to `__dirname + '/views'` in `app.js`.
|
|
27
27
|
|
|
@@ -203,7 +203,7 @@ module.exports = {
|
|
|
203
203
|
|
|
204
204
|
async renderForModule(req, name, data, module) {
|
|
205
205
|
if (typeof req !== 'object') {
|
|
206
|
-
throw new Error('The first argument to module.render must be req.
|
|
206
|
+
throw new Error('The first argument to module.render must be req.');
|
|
207
207
|
}
|
|
208
208
|
return self.renderBody(req, 'file', name, data, module);
|
|
209
209
|
},
|
|
@@ -214,7 +214,7 @@ module.exports = {
|
|
|
214
214
|
|
|
215
215
|
async renderStringForModule(req, s, data, module) {
|
|
216
216
|
if (typeof req !== 'object') {
|
|
217
|
-
throw new Error('The first argument to module.render must be req.
|
|
217
|
+
throw new Error('The first argument to module.render must be req.');
|
|
218
218
|
}
|
|
219
219
|
return self.renderBody(req, 'string', s, data, module);
|
|
220
220
|
},
|
|
@@ -289,12 +289,6 @@ module.exports = {
|
|
|
289
289
|
|
|
290
290
|
args.data = merged;
|
|
291
291
|
|
|
292
|
-
// // Allows templates to render other templates in an independent
|
|
293
|
-
// // nunjucks environment, rather than including them
|
|
294
|
-
// args.partial = function(name, data) {
|
|
295
|
-
// return self.partialForModule(name, data, module);
|
|
296
|
-
// };
|
|
297
|
-
|
|
298
292
|
if (req.data) {
|
|
299
293
|
_.defaults(merged, req.data);
|
|
300
294
|
}
|
|
@@ -334,7 +328,7 @@ module.exports = {
|
|
|
334
328
|
|
|
335
329
|
// Fetch a nunjucks environment in which `include`, `extends`, etc. search
|
|
336
330
|
// the views directories of the specified module and its ancestors.
|
|
337
|
-
// Typically you will call `self.render`
|
|
331
|
+
// Typically you will call `self.render` on your module
|
|
338
332
|
// object rather than calling this directly.
|
|
339
333
|
//
|
|
340
334
|
// `req` is effectively here for bc purposes only. This method
|
|
@@ -61,7 +61,9 @@ export default {
|
|
|
61
61
|
return maxError;
|
|
62
62
|
},
|
|
63
63
|
countLabel() {
|
|
64
|
-
return
|
|
64
|
+
return this.$t('apostrophe:numberAdded', {
|
|
65
|
+
count: this.value.length
|
|
66
|
+
});
|
|
65
67
|
},
|
|
66
68
|
// Here in the array editor we use effectiveMin to factor in the
|
|
67
69
|
// required property because there is no other good place to do that,
|
|
@@ -69,14 +71,18 @@ export default {
|
|
|
69
71
|
// representation of "required".
|
|
70
72
|
minLabel() {
|
|
71
73
|
if (this.effectiveMin) {
|
|
72
|
-
return
|
|
74
|
+
return this.$t('apostrophe:minUi', {
|
|
75
|
+
number: this.effectiveMin
|
|
76
|
+
});
|
|
73
77
|
} else {
|
|
74
78
|
return false;
|
|
75
79
|
}
|
|
76
80
|
},
|
|
77
81
|
maxLabel() {
|
|
78
82
|
if ((typeof this.field.max) === 'number') {
|
|
79
|
-
return
|
|
83
|
+
return this.$t('apostrophe:maxUi', {
|
|
84
|
+
number: this.field.max
|
|
85
|
+
});
|
|
80
86
|
} else {
|
|
81
87
|
return false;
|
|
82
88
|
}
|
|
@@ -49,9 +49,10 @@ export default {
|
|
|
49
49
|
if ((e.name === 'invalid') && e.body && e.body.data && e.body.data.unpublishedAncestors) {
|
|
50
50
|
if (await apos.confirm({
|
|
51
51
|
heading: 'apostrophe:unpublishedParent',
|
|
52
|
-
description: 'apostrophe:unpublishedParentDescription'
|
|
52
|
+
description: 'apostrophe:unpublishedParentDescription'
|
|
53
|
+
}, {
|
|
53
54
|
interpolate: {
|
|
54
|
-
unpublishedParents:
|
|
55
|
+
unpublishedParents: e.body.data.unpublishedAncestors.map(page => page.title).join(this.$t('apostrophe:listJoiner'))
|
|
55
56
|
}
|
|
56
57
|
})) {
|
|
57
58
|
try {
|
|
@@ -22,17 +22,11 @@ export default {
|
|
|
22
22
|
},
|
|
23
23
|
data() {
|
|
24
24
|
return {
|
|
25
|
-
rendered: '...'
|
|
26
|
-
playerOpts: null,
|
|
27
|
-
playerEl: null
|
|
25
|
+
rendered: '...'
|
|
28
26
|
};
|
|
29
27
|
},
|
|
30
28
|
mounted() {
|
|
31
29
|
this.renderContent();
|
|
32
|
-
this.playerOpts = apos.util.widgetPlayers[this.type] || null;
|
|
33
|
-
},
|
|
34
|
-
updated () {
|
|
35
|
-
this.runPlayer();
|
|
36
30
|
},
|
|
37
31
|
computed: {
|
|
38
32
|
moduleOptions() {
|
|
@@ -41,7 +35,7 @@ export default {
|
|
|
41
35
|
},
|
|
42
36
|
methods: {
|
|
43
37
|
async renderContent() {
|
|
44
|
-
|
|
38
|
+
apos.bus.$emit('widget-rendering');
|
|
45
39
|
const parameters = {
|
|
46
40
|
_docId: this.docId,
|
|
47
41
|
widget: this.value,
|
|
@@ -51,7 +45,6 @@ export default {
|
|
|
51
45
|
try {
|
|
52
46
|
if (this.rendering && (isEqual(this.rendering.parameters, parameters))) {
|
|
53
47
|
this.rendered = this.rendering.html;
|
|
54
|
-
this.runPlayer();
|
|
55
48
|
} else {
|
|
56
49
|
this.rendered = '...';
|
|
57
50
|
this.rendered = await apos.http.post(`${apos.area.action}/render-widget?aposEdit=1&aposMode=draft`, {
|
|
@@ -59,11 +52,10 @@ export default {
|
|
|
59
52
|
body: parameters
|
|
60
53
|
});
|
|
61
54
|
}
|
|
62
|
-
// Wait for reactivity to
|
|
63
|
-
//
|
|
64
|
-
//
|
|
55
|
+
// Wait for reactivity to render v-html so that markup is
|
|
56
|
+
// in the DOM before hinting that it might be time to prepare
|
|
57
|
+
// sub-area editors and run players
|
|
65
58
|
setTimeout(function() {
|
|
66
|
-
self.setPlayerEl();
|
|
67
59
|
apos.bus.$emit('widget-rendered');
|
|
68
60
|
}, 0);
|
|
69
61
|
} catch (e) {
|
|
@@ -71,20 +63,6 @@ export default {
|
|
|
71
63
|
console.error('Unable to render widget. Possibly the schema has been changed and the existing widget does not pass validation.', e);
|
|
72
64
|
}
|
|
73
65
|
},
|
|
74
|
-
setPlayerEl() {
|
|
75
|
-
if (this.playerOpts) {
|
|
76
|
-
const el = this.$el.querySelector(this.playerOpts.selector);
|
|
77
|
-
if (el && this.playerOpts.player) {
|
|
78
|
-
this.playerEl = el;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
runPlayer() {
|
|
83
|
-
if (this.playerEl && !this.playerEl.aposWidgetPlayed) {
|
|
84
|
-
this.playerOpts.player(this.playerEl);
|
|
85
|
-
this.playerEl.aposWidgetPlayed = true;
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
66
|
clicked(e) {
|
|
89
67
|
// If you do not want a particular click to swap to the edit view
|
|
90
68
|
// for your widget, you should make sure it does not bubble
|
package/package.json
CHANGED
package/.circleci/config.yml
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
jobs:
|
|
3
|
-
build-node14-mongo5:
|
|
4
|
-
docker:
|
|
5
|
-
- image: circleci/node:14-browsers
|
|
6
|
-
- image: mongo:5.0
|
|
7
|
-
steps:
|
|
8
|
-
- checkout
|
|
9
|
-
- run:
|
|
10
|
-
name: update-npm
|
|
11
|
-
command: 'sudo npm install -g npm@7'
|
|
12
|
-
- restore_cache:
|
|
13
|
-
key: dependency-cache-{{ checksum "package.json" }}
|
|
14
|
-
- run:
|
|
15
|
-
name: install-npm-wee
|
|
16
|
-
command: npm install
|
|
17
|
-
- save_cache:
|
|
18
|
-
key: dependency-cache-{{ checksum "package.json" }}
|
|
19
|
-
paths:
|
|
20
|
-
- ./node_modules
|
|
21
|
-
- run:
|
|
22
|
-
name: test
|
|
23
|
-
command: npm test
|
|
24
|
-
build-node14-mongo44:
|
|
25
|
-
docker:
|
|
26
|
-
- image: circleci/node:14-browsers
|
|
27
|
-
- image: mongo:4.4
|
|
28
|
-
steps:
|
|
29
|
-
- checkout
|
|
30
|
-
- run:
|
|
31
|
-
name: update-npm
|
|
32
|
-
command: 'sudo npm install -g npm@7'
|
|
33
|
-
- restore_cache:
|
|
34
|
-
key: dependency-cache-{{ checksum "package.json" }}
|
|
35
|
-
- run:
|
|
36
|
-
name: install-npm-wee
|
|
37
|
-
command: npm install
|
|
38
|
-
- save_cache:
|
|
39
|
-
key: dependency-cache-{{ checksum "package.json" }}
|
|
40
|
-
paths:
|
|
41
|
-
- ./node_modules
|
|
42
|
-
- run:
|
|
43
|
-
name: test
|
|
44
|
-
command: npm test
|
|
45
|
-
build-node14-mongo42:
|
|
46
|
-
docker:
|
|
47
|
-
- image: circleci/node:14-browsers
|
|
48
|
-
- image: mongo:4.2
|
|
49
|
-
steps:
|
|
50
|
-
- checkout
|
|
51
|
-
- run:
|
|
52
|
-
name: update-npm
|
|
53
|
-
command: 'sudo npm install -g npm@7'
|
|
54
|
-
- restore_cache:
|
|
55
|
-
key: dependency-cache-{{ checksum "package.json" }}
|
|
56
|
-
- run:
|
|
57
|
-
name: install-npm-wee
|
|
58
|
-
command: npm install
|
|
59
|
-
- save_cache:
|
|
60
|
-
key: dependency-cache-{{ checksum "package.json" }}
|
|
61
|
-
paths:
|
|
62
|
-
- ./node_modules
|
|
63
|
-
- run:
|
|
64
|
-
name: test
|
|
65
|
-
command: npm test
|
|
66
|
-
build-node12:
|
|
67
|
-
docker:
|
|
68
|
-
- image: circleci/node:12-browsers
|
|
69
|
-
- image: mongo:3.6.11
|
|
70
|
-
steps:
|
|
71
|
-
- checkout
|
|
72
|
-
- run:
|
|
73
|
-
name: update-npm
|
|
74
|
-
command: "sudo npm install -g npm"
|
|
75
|
-
- restore_cache:
|
|
76
|
-
key: dependency-cache-{{ .Branch }}-{{ checksum "package-lock.json" }}
|
|
77
|
-
- run:
|
|
78
|
-
name: install-npm-wee
|
|
79
|
-
command: npm install
|
|
80
|
-
- save_cache:
|
|
81
|
-
key: dependency-cache-{{ .Branch }}-{{ checksum "package-lock.json" }}
|
|
82
|
-
paths:
|
|
83
|
-
- ./node_modules
|
|
84
|
-
- run:
|
|
85
|
-
name: test
|
|
86
|
-
command: npm test
|
|
87
|
-
workflows:
|
|
88
|
-
version: 2
|
|
89
|
-
build:
|
|
90
|
-
jobs:
|
|
91
|
-
- build-node14-mongo5
|
|
92
|
-
- build-node14-mongo44
|
|
93
|
-
- build-node14-mongo42
|
|
94
|
-
- build-node12
|
package/.scratch.md
DELETED