apostrophe 3.23.0 → 3.24.0-alpha.2022071801
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 +17 -17
- package/CHANGELOG.md +23 -0
- package/index.js +1 -1
- package/lib/locales.js +43 -0
- package/lib/moog-require.js +1 -1
- package/modules/@apostrophecms/i18n/index.js +16 -30
- package/modules/@apostrophecms/polymorphic-type/index.js +4 -1
- package/modules/@apostrophecms/schema/index.js +7 -1
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +6 -8
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +13 -3
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +5 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +5 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +4 -22
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputChoicesMixin.js +32 -0
- package/modules/@apostrophecms/task/index.js +3 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +3 -3
- package/package.json +9 -9
- package/test/caches.js +20 -0
- package/test/package.json +11 -0
- package/test/pages.js +2 -1
- package/test/pieces.js +2 -1
- package/test/static-i18n.js +14 -0
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
name: Tests
|
|
4
4
|
|
|
5
|
-
# Controls when the action will run.
|
|
5
|
+
# Controls when the action will run.
|
|
6
6
|
on:
|
|
7
7
|
push:
|
|
8
|
-
branches: [
|
|
8
|
+
branches: ["main"]
|
|
9
9
|
pull_request:
|
|
10
|
-
branches: [
|
|
10
|
+
branches: ["*"]
|
|
11
11
|
|
|
12
12
|
# Allows you to run this workflow manually from the Actions tab
|
|
13
13
|
workflow_dispatch:
|
|
@@ -25,21 +25,21 @@ jobs:
|
|
|
25
25
|
|
|
26
26
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
27
27
|
steps:
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
- name: Git checkout
|
|
29
|
+
uses: actions/checkout@v2
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
32
|
+
uses: actions/setup-node@v1
|
|
33
|
+
with:
|
|
34
|
+
node-version: ${{ matrix.node-version }}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
- name: Start MongoDB
|
|
37
|
+
uses: supercharge/mongodb-github-action@1.3.0
|
|
38
|
+
with:
|
|
39
|
+
mongodb-version: ${{ matrix.mongodb-version }}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
- run: npm install
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
- run: npm test
|
|
44
|
+
env:
|
|
45
|
+
CI: true
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
* Radio and Checkboxes schema inputs now support a server side `choices` function for supplying their choices array dynamically, like select supports. Future fields can opt into this functionality with the field flag `dynamicChoices`.
|
|
5
|
+
|
|
6
|
+
## 3.24.0 (2022-07-06)`
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
* Unpinned tiptap as the tiptap team has made releases that resolve the packaging errors that caused us to pin it in 3.22.1.<F2>
|
|
10
|
+
* Pinned `vue-loader` to the `15.9.x` minor release series for now. The `15.10.0` release breaks support for using `npm link` to develop the `apostrophe` module itself.
|
|
11
|
+
|
|
12
|
+
## 3.24.0 (2022-07-06)
|
|
13
|
+
|
|
14
|
+
### Adds
|
|
15
|
+
|
|
16
|
+
* Handle `private: true` locale option in i18n module, preventing logged out users from accessing the content of a private locale.
|
|
17
|
+
|
|
18
|
+
### Fixes
|
|
19
|
+
|
|
20
|
+
* Fix missing title translation in the "Array Editor" component.
|
|
21
|
+
* Add `follow: true` flag to `glob` functions (with `**` pattern) to allow registering symlink files and folders for nested modules
|
|
22
|
+
* Fix disabled context menu for relationship fields editing ([#3820](https://github.com/apostrophecms/apostrophe/issues/3820))
|
|
23
|
+
* In getReq method form the task module, extract the right `role` property from the options object.
|
|
24
|
+
* Fix `def:` option in `array` fields, in order to be able to see the default items in the array editor modal
|
|
25
|
+
|
|
3
26
|
## 3.23.0 (2022-06-22)
|
|
4
27
|
|
|
5
28
|
### Adds
|
package/index.js
CHANGED
|
@@ -371,7 +371,7 @@ async function apostrophe(options, telemetry, rootSpan) {
|
|
|
371
371
|
if (!options.nestedModuleSubdirs) {
|
|
372
372
|
return;
|
|
373
373
|
}
|
|
374
|
-
const configs = glob.sync(self.localModules + '/**/modules.js');
|
|
374
|
+
const configs = glob.sync(self.localModules + '/**/modules.js', { follow: true });
|
|
375
375
|
_.each(configs, function(config) {
|
|
376
376
|
try {
|
|
377
377
|
_.merge(self.options.modules, require(config));
|
package/lib/locales.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { stripIndent } = require('common-tags');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Make sure they are adequately distinguished by
|
|
5
|
+
// hostname and prefix
|
|
6
|
+
verifyLocales(locales, baseUrl) {
|
|
7
|
+
const taken = {};
|
|
8
|
+
let hostnamesCount = 0;
|
|
9
|
+
for (const [ name, options ] of Object.entries(locales)) {
|
|
10
|
+
const hostname = options.hostname || '__none';
|
|
11
|
+
const prefix = options.prefix || '__none';
|
|
12
|
+
const key = `${hostname}:${prefix}`;
|
|
13
|
+
|
|
14
|
+
hostnamesCount += options.hostname ? 1 : 0;
|
|
15
|
+
|
|
16
|
+
if (taken[key]) {
|
|
17
|
+
throw new Error(stripIndent`
|
|
18
|
+
The locale "${name}" cannot be distinguished from earlier locales.
|
|
19
|
+
Make sure it is uniquely distinguished by its hostname option,
|
|
20
|
+
prefix option or a combination of the two.
|
|
21
|
+
One locale per site may be a default with neither hostname nor prefix,
|
|
22
|
+
and one locale per hostname may be a default for that hostname without a prefix.
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
taken[key] = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
hostnamesCount > 0 &&
|
|
30
|
+
hostnamesCount < Object.keys(locales).length &&
|
|
31
|
+
!baseUrl
|
|
32
|
+
) {
|
|
33
|
+
throw new Error(stripIndent`
|
|
34
|
+
If some of your locales have hostnames, then they all must have
|
|
35
|
+
hostnames, or your top-level baseUrl option must be set.
|
|
36
|
+
|
|
37
|
+
In development, you can set baseUrl to http://localhost:3000
|
|
38
|
+
for testing purposes. In production it should always be set
|
|
39
|
+
to a real base URL for the site.
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
package/lib/moog-require.js
CHANGED
|
@@ -60,7 +60,7 @@ module.exports = function(options) {
|
|
|
60
60
|
// Fetching a list of index.js files on the first call and then searching it each time for
|
|
61
61
|
// one that refers to the right type name shaves as much as 60 seconds off the startup
|
|
62
62
|
// time in a large project, compared to using the glob cache feature
|
|
63
|
-
self._indexes = glob.sync(self.options.localModules + '/**/index.js');
|
|
63
|
+
self._indexes = glob.sync(self.options.localModules + '/**/index.js', { follow: true });
|
|
64
64
|
}
|
|
65
65
|
const matches = self._indexes.filter(function(index) {
|
|
66
66
|
// Double-check that we're not confusing "@apostrophecms/asset" with "asset" by
|
|
@@ -11,6 +11,7 @@ const _ = require('lodash');
|
|
|
11
11
|
const { stripIndent } = require('common-tags');
|
|
12
12
|
const ExpressSessionCookie = require('express-session/session/cookie');
|
|
13
13
|
const path = require('path');
|
|
14
|
+
const { verifyLocales } = require('../../../lib/locales');
|
|
14
15
|
|
|
15
16
|
const apostropheI18nDebugPlugin = {
|
|
16
17
|
type: 'postProcessor',
|
|
@@ -218,7 +219,8 @@ module.exports = {
|
|
|
218
219
|
} else {
|
|
219
220
|
locale = self.matchLocale(req);
|
|
220
221
|
}
|
|
221
|
-
const
|
|
222
|
+
const locales = self.filterPrivateLocales(req, self.locales);
|
|
223
|
+
const localeOptions = locales[locale];
|
|
222
224
|
if (localeOptions.prefix) {
|
|
223
225
|
// Remove locale prefix so URL parsing can proceed normally from here
|
|
224
226
|
if (req.path === localeOptions.prefix) {
|
|
@@ -464,8 +466,9 @@ module.exports = {
|
|
|
464
466
|
// possible the default locale is returned.
|
|
465
467
|
matchLocale(req) {
|
|
466
468
|
const hostname = req.hostname;
|
|
469
|
+
const locales = self.filterPrivateLocales(req, self.locales);
|
|
467
470
|
let best = false;
|
|
468
|
-
for (const [ name, options ] of Object.entries(
|
|
471
|
+
for (const [ name, options ] of Object.entries(locales)) {
|
|
469
472
|
const matchedHostname = options.hostname
|
|
470
473
|
? (hostname === options.hostname.split(':')[0]) : null;
|
|
471
474
|
const matchedPrefix = options.prefix
|
|
@@ -568,34 +571,7 @@ module.exports = {
|
|
|
568
571
|
label: 'English'
|
|
569
572
|
}
|
|
570
573
|
};
|
|
571
|
-
|
|
572
|
-
let hostnamesCount = 0;
|
|
573
|
-
for (const [ name, options ] of Object.entries(locales)) {
|
|
574
|
-
const key = (options.hostname || '__none') + ':' + (options.prefix || '__none');
|
|
575
|
-
hostnamesCount += (options.hostname ? 1 : 0);
|
|
576
|
-
if (taken[key]) {
|
|
577
|
-
throw new Error(stripIndent`
|
|
578
|
-
@apostrophecms/i18n: the locale ${name} cannot be distinguished from
|
|
579
|
-
earlier locales. Make sure it is uniquely distinguished by its hostname
|
|
580
|
-
option, prefix option or a combination of the two. One locale per site
|
|
581
|
-
may be a default with neither hostname nor prefix, and one locale per
|
|
582
|
-
hostname may be a default for that hostname without a prefix.
|
|
583
|
-
`);
|
|
584
|
-
}
|
|
585
|
-
taken[key] = true;
|
|
586
|
-
}
|
|
587
|
-
if ((hostnamesCount > 0) && (hostnamesCount < Object.keys(locales).length) && (!self.apos.options.baseUrl)) {
|
|
588
|
-
throw new Error(stripIndent`
|
|
589
|
-
If some of your locales have hostnames, then they all must have
|
|
590
|
-
hostnames, or your top-level baseUrl option must be set.
|
|
591
|
-
|
|
592
|
-
In development, you can set baseUrl to http://localhost:3000
|
|
593
|
-
for testing purposes. In production it should always be set
|
|
594
|
-
to a real base URL for the site.
|
|
595
|
-
`);
|
|
596
|
-
}
|
|
597
|
-
// Make sure they are adequately distinguished by
|
|
598
|
-
// hostname and prefix
|
|
574
|
+
verifyLocales(locales, self.apos.options.baseUrl);
|
|
599
575
|
return locales;
|
|
600
576
|
},
|
|
601
577
|
sanitizeLocaleName(locale) {
|
|
@@ -656,6 +632,16 @@ module.exports = {
|
|
|
656
632
|
}
|
|
657
633
|
return res.redirect(corresponding._url);
|
|
658
634
|
};
|
|
635
|
+
},
|
|
636
|
+
// Exclude private locales when logged out
|
|
637
|
+
filterPrivateLocales(req, locales) {
|
|
638
|
+
return req.user
|
|
639
|
+
? locales
|
|
640
|
+
: Object.fromEntries(
|
|
641
|
+
Object
|
|
642
|
+
.entries(locales)
|
|
643
|
+
.filter(([ name, options ]) => options.private !== true)
|
|
644
|
+
);
|
|
659
645
|
}
|
|
660
646
|
};
|
|
661
647
|
}
|
|
@@ -1453,6 +1453,12 @@ module.exports = {
|
|
|
1453
1453
|
_.each(self.apos.area.widgetManagers, function (manager, type) {
|
|
1454
1454
|
self.register('widget', type, manager.schema);
|
|
1455
1455
|
});
|
|
1456
|
+
},
|
|
1457
|
+
|
|
1458
|
+
async getChoices(req, field) {
|
|
1459
|
+
return typeof field.choices === 'string'
|
|
1460
|
+
? self.apos.modules[field.moduleName][field.choices](req)
|
|
1461
|
+
: field.choices;
|
|
1456
1462
|
}
|
|
1457
1463
|
|
|
1458
1464
|
};
|
|
@@ -1466,7 +1472,7 @@ module.exports = {
|
|
|
1466
1472
|
let choices = [];
|
|
1467
1473
|
if (
|
|
1468
1474
|
!field ||
|
|
1469
|
-
field.type
|
|
1475
|
+
!self.fieldTypes[field.type].dynamicChoices ||
|
|
1470
1476
|
!(field.choices && typeof field.choices === 'string')
|
|
1471
1477
|
) {
|
|
1472
1478
|
throw self.apos.error('invalid');
|
|
@@ -231,7 +231,9 @@ module.exports = (self) => {
|
|
|
231
231
|
|
|
232
232
|
self.addFieldType({
|
|
233
233
|
name: 'checkboxes',
|
|
234
|
+
dynamicChoices: true,
|
|
234
235
|
async convert(req, field, data, destination) {
|
|
236
|
+
const choices = await self.getChoices(req, field);
|
|
235
237
|
if (typeof data[field.name] === 'string') {
|
|
236
238
|
data[field.name] = self.apos.launder.string(data[field.name]).split(',');
|
|
237
239
|
|
|
@@ -241,14 +243,14 @@ module.exports = (self) => {
|
|
|
241
243
|
}
|
|
242
244
|
|
|
243
245
|
destination[field.name] = _.filter(data[field.name], function (choice) {
|
|
244
|
-
return _.includes(_.map(
|
|
246
|
+
return _.includes(_.map(choices, 'value'), choice);
|
|
245
247
|
});
|
|
246
248
|
} else {
|
|
247
249
|
if (!Array.isArray(data[field.name])) {
|
|
248
250
|
destination[field.name] = [];
|
|
249
251
|
} else {
|
|
250
252
|
destination[field.name] = _.filter(data[field.name], function (choice) {
|
|
251
|
-
return _.includes(_.map(
|
|
253
|
+
return _.includes(_.map(choices, 'value'), choice);
|
|
252
254
|
});
|
|
253
255
|
}
|
|
254
256
|
}
|
|
@@ -303,13 +305,9 @@ module.exports = (self) => {
|
|
|
303
305
|
|
|
304
306
|
self.addFieldType({
|
|
305
307
|
name: 'select',
|
|
308
|
+
dynamicChoices: true,
|
|
306
309
|
async convert(req, field, data, destination) {
|
|
307
|
-
|
|
308
|
-
if ((typeof field.choices) === 'string') {
|
|
309
|
-
choices = await self.apos.modules[field.moduleName][field.choices](req);
|
|
310
|
-
} else {
|
|
311
|
-
choices = field.choices;
|
|
312
|
-
}
|
|
310
|
+
const choices = await self.getChoices(req, field);
|
|
313
311
|
destination[field.name] = self.apos.launder.select(data[field.name], choices, field.def);
|
|
314
312
|
},
|
|
315
313
|
index: function (value, field, texts) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<AposModal
|
|
3
3
|
class="apos-array-editor" :modal="modal"
|
|
4
|
-
:modal-title="
|
|
4
|
+
:modal-title="modalTitle"
|
|
5
5
|
@inactive="modal.active = false" @show-modal="modal.showModal = true"
|
|
6
6
|
@esc="confirmAndCancel" @no-modal="$emit('safe-close')"
|
|
7
7
|
>
|
|
@@ -113,6 +113,12 @@ export default {
|
|
|
113
113
|
},
|
|
114
114
|
emits: [ 'modal-result', 'safe-close' ],
|
|
115
115
|
data() {
|
|
116
|
+
// Automatically add `_id` to default items
|
|
117
|
+
const items = this.items.map(item => ({
|
|
118
|
+
...item,
|
|
119
|
+
_id: item._id || cuid()
|
|
120
|
+
}));
|
|
121
|
+
|
|
116
122
|
return {
|
|
117
123
|
currentId: null,
|
|
118
124
|
currentDoc: null,
|
|
@@ -121,12 +127,16 @@ export default {
|
|
|
121
127
|
type: 'overlay',
|
|
122
128
|
showModal: false
|
|
123
129
|
},
|
|
130
|
+
modalTitle: {
|
|
131
|
+
key: 'apostrophe:editType',
|
|
132
|
+
type: this.$t(this.field.label)
|
|
133
|
+
},
|
|
124
134
|
titleFieldChoices: null,
|
|
125
135
|
// If we don't clone, then we're making
|
|
126
136
|
// permanent modifications whether the user
|
|
127
137
|
// clicks save or not
|
|
128
|
-
next: klona(
|
|
129
|
-
original: klona(
|
|
138
|
+
next: klona(items),
|
|
139
|
+
original: klona(items),
|
|
130
140
|
triggerValidation: false,
|
|
131
141
|
minError: false,
|
|
132
142
|
maxError: false,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<template #body>
|
|
9
9
|
<AposCheckbox
|
|
10
10
|
:for="getChoiceId(uid, choice.value)"
|
|
11
|
-
v-for="choice in
|
|
11
|
+
v-for="choice in choices"
|
|
12
12
|
:key="choice.value"
|
|
13
13
|
:id="getChoiceId(uid, choice.value)"
|
|
14
14
|
:choice="choice"
|
|
@@ -21,10 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
<script>
|
|
23
23
|
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
|
|
24
|
+
import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
|
|
24
25
|
|
|
25
26
|
export default {
|
|
26
27
|
name: 'AposInputCheckboxes',
|
|
27
|
-
mixins: [ AposInputMixin ],
|
|
28
|
+
mixins: [ AposInputMixin, AposInputChoicesMixin ],
|
|
28
29
|
beforeMount: function () {
|
|
29
30
|
this.value.data = Array.isArray(this.value.data) ? this.value.data : [];
|
|
30
31
|
},
|
|
@@ -38,7 +39,7 @@ export default {
|
|
|
38
39
|
},
|
|
39
40
|
validate(values) {
|
|
40
41
|
// The choices and values should always be arrays.
|
|
41
|
-
if (!Array.isArray(this.
|
|
42
|
+
if (!Array.isArray(this.choices) || !Array.isArray(values)) {
|
|
42
43
|
return 'malformed';
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -48,7 +49,7 @@ export default {
|
|
|
48
49
|
|
|
49
50
|
if (Array.isArray(values)) {
|
|
50
51
|
values.forEach(chosen => {
|
|
51
|
-
if (!this.
|
|
52
|
+
if (!this.choices.map(choice => {
|
|
52
53
|
return choice.value;
|
|
53
54
|
}).includes(chosen)) {
|
|
54
55
|
return 'invalid';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<template #body>
|
|
9
9
|
<label
|
|
10
10
|
class="apos-choice-label" :for="getChoiceId(uid, choice.value)"
|
|
11
|
-
v-for="choice in
|
|
11
|
+
v-for="choice in choices" :key="choice.value"
|
|
12
12
|
:class="{'apos-choice-label--disabled': field.readOnly}"
|
|
13
13
|
>
|
|
14
14
|
<input
|
|
@@ -43,12 +43,13 @@
|
|
|
43
43
|
|
|
44
44
|
<script>
|
|
45
45
|
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
|
|
46
|
+
import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
|
|
46
47
|
import InformationIcon from 'vue-material-design-icons/Information.vue';
|
|
47
48
|
|
|
48
49
|
export default {
|
|
49
50
|
name: 'AposInputRadio',
|
|
50
51
|
components: { InformationIcon },
|
|
51
|
-
mixins: [ AposInputMixin ],
|
|
52
|
+
mixins: [ AposInputMixin, AposInputChoicesMixin ],
|
|
52
53
|
methods: {
|
|
53
54
|
getChoiceId(uid, value) {
|
|
54
55
|
return (uid + JSON.stringify(value)).replace(/\s+/g, '');
|
|
@@ -58,7 +59,7 @@ export default {
|
|
|
58
59
|
return 'required';
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
if (value && !this.
|
|
62
|
+
if (value && !this.choices.find(choice => choice.value === value)) {
|
|
62
63
|
return 'invalid';
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -66,7 +67,7 @@ export default {
|
|
|
66
67
|
},
|
|
67
68
|
change(value) {
|
|
68
69
|
// Allows expression of non-string values
|
|
69
|
-
this.next = this.
|
|
70
|
+
this.next = this.choices.find(choice => choice.value === JSON.parse(value)).value;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
};
|
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
|
|
21
21
|
<script>
|
|
22
22
|
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
|
|
23
|
+
import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
|
|
23
24
|
|
|
24
25
|
export default {
|
|
25
26
|
name: 'AposInputSelect',
|
|
26
|
-
mixins: [ AposInputMixin ],
|
|
27
|
+
mixins: [ AposInputMixin, AposInputChoicesMixin ],
|
|
27
28
|
props: {
|
|
28
29
|
icon: {
|
|
29
30
|
type: String,
|
|
@@ -37,34 +38,15 @@ export default {
|
|
|
37
38
|
};
|
|
38
39
|
},
|
|
39
40
|
async mounted() {
|
|
40
|
-
let choices;
|
|
41
|
-
if (typeof this.field.choices === 'string') {
|
|
42
|
-
const action = this.options.action;
|
|
43
|
-
const response = await apos.http.get(
|
|
44
|
-
`${action}/choices`,
|
|
45
|
-
{
|
|
46
|
-
qs: {
|
|
47
|
-
fieldId: this.field._id
|
|
48
|
-
},
|
|
49
|
-
busy: true
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
if (response.choices) {
|
|
53
|
-
choices = response.choices;
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
choices = this.field.choices;
|
|
57
|
-
}
|
|
58
41
|
// Add an null option if there isn't one already
|
|
59
|
-
if (!this.field.required && !choices.find(choice => {
|
|
42
|
+
if (!this.field.required && !this.choices.find(choice => {
|
|
60
43
|
return choice.value === null;
|
|
61
44
|
})) {
|
|
62
|
-
this.choices.
|
|
45
|
+
this.choices.unshift({
|
|
63
46
|
label: '',
|
|
64
47
|
value: null
|
|
65
48
|
});
|
|
66
49
|
}
|
|
67
|
-
this.choices = this.choices.concat(choices);
|
|
68
50
|
this.$nextTick(() => {
|
|
69
51
|
// this has to happen on nextTick to avoid emitting before schemaReady is
|
|
70
52
|
// set in AposSchema
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Provides prep work for fetching choices from the server
|
|
3
|
+
* or defaulting to the choices provided with the field.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
data() {
|
|
8
|
+
return {
|
|
9
|
+
choices: []
|
|
10
|
+
};
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
async mounted() {
|
|
14
|
+
if (typeof this.field.choices === 'string') {
|
|
15
|
+
const action = this.options.action;
|
|
16
|
+
const response = await apos.http.get(
|
|
17
|
+
`${action}/choices`,
|
|
18
|
+
{
|
|
19
|
+
qs: {
|
|
20
|
+
fieldId: this.field._id
|
|
21
|
+
},
|
|
22
|
+
busy: true
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
if (response.choices) {
|
|
26
|
+
this.choices = response.choices;
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
this.choices = this.field.choices;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -287,9 +287,11 @@ module.exports = {
|
|
|
287
287
|
};
|
|
288
288
|
addCloneMethod(req);
|
|
289
289
|
req.res.__ = req.__;
|
|
290
|
-
const { _role, ...properties } = options || {};
|
|
290
|
+
const { role: _role, ...properties } = options || {};
|
|
291
|
+
|
|
291
292
|
Object.assign(req, properties);
|
|
292
293
|
self.apos.i18n.setPrefixUrls(req);
|
|
294
|
+
|
|
293
295
|
return req;
|
|
294
296
|
|
|
295
297
|
function addCloneMethod(req) {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
@item-clicked="$emit('item-clicked', item)"
|
|
35
35
|
menu-placement="bottom-start"
|
|
36
36
|
menu-offset="40, 10"
|
|
37
|
-
disabled="disabled"
|
|
37
|
+
:disabled="disabled"
|
|
38
38
|
/>
|
|
39
39
|
<AposButton
|
|
40
40
|
class="apos-slat__editor-btn"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
:icon-only="true"
|
|
49
49
|
:modifiers="['inline']"
|
|
50
50
|
@click="$emit('item-clicked', item)"
|
|
51
|
-
disabled="disabled"
|
|
51
|
+
:disabled="disabled"
|
|
52
52
|
/>
|
|
53
53
|
<a
|
|
54
54
|
class="apos-slat__control apos-slat__control--view"
|
|
@@ -169,7 +169,7 @@ export default {
|
|
|
169
169
|
},
|
|
170
170
|
computed: {
|
|
171
171
|
itemSize() {
|
|
172
|
-
const size = this.item.length
|
|
172
|
+
const size = this.item.length?.size;
|
|
173
173
|
if (size < 1000000) {
|
|
174
174
|
return `${(size / 1000).toFixed(0)}KB`;
|
|
175
175
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0-alpha.2022071801",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"@babel/preset-env": "^7.16.7",
|
|
36
36
|
"@opentelemetry/api": "^1.0.4",
|
|
37
37
|
"@opentelemetry/semantic-conventions": "^1.0.1",
|
|
38
|
-
"@tiptap/extension-highlight": "2.0.0-beta.33",
|
|
39
|
-
"@tiptap/extension-link": "2.0.0-beta.38",
|
|
40
|
-
"@tiptap/extension-text-align": "2.0.0-beta.29",
|
|
41
|
-
"@tiptap/extension-text-style": "2.0.0-beta.23",
|
|
42
|
-
"@tiptap/extension-underline": "2.0.0-beta.23",
|
|
43
|
-
"@tiptap/starter-kit": "2.0.0-beta.185",
|
|
44
|
-
"@tiptap/vue-2": "2.0.0-beta.79",
|
|
38
|
+
"@tiptap/extension-highlight": "^2.0.0-beta.33",
|
|
39
|
+
"@tiptap/extension-link": "^2.0.0-beta.38",
|
|
40
|
+
"@tiptap/extension-text-align": "^2.0.0-beta.29",
|
|
41
|
+
"@tiptap/extension-text-style": "^2.0.0-beta.23",
|
|
42
|
+
"@tiptap/extension-underline": "^2.0.0-beta.23",
|
|
43
|
+
"@tiptap/starter-kit": "^2.0.0-beta.185",
|
|
44
|
+
"@tiptap/vue-2": "^2.0.0-beta.79",
|
|
45
45
|
"autoprefixer": "^10.4.1",
|
|
46
46
|
"babel-loader": "^8.2.5",
|
|
47
47
|
"bluebird": "^3.7.2",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"vue": "^2.6.14",
|
|
120
120
|
"vue-advanced-cropper": "^1.10.1",
|
|
121
121
|
"vue-click-outside-element": "^1.0.15",
|
|
122
|
-
"vue-loader": "
|
|
122
|
+
"vue-loader": "~15.9.8",
|
|
123
123
|
"vue-material-design-icons": "~4.12.1",
|
|
124
124
|
"vue-style-loader": "^4.1.2",
|
|
125
125
|
"vue-template-compiler": "^2.6.14",
|
package/test/caches.js
CHANGED
|
@@ -22,6 +22,11 @@ describe('Caches', function() {
|
|
|
22
22
|
it('should allow us to store capuchin', async function() {
|
|
23
23
|
await apos.cache.set('test', 'capuchin', { message: 'eek eek' });
|
|
24
24
|
});
|
|
25
|
+
it('second cache can contain capuchin with a different value', async function() {
|
|
26
|
+
await apos.cache.set('test2', 'capuchin', { message: 'ook ook' });
|
|
27
|
+
assert.strictEqual((await apos.cache.get('test', 'capuchin')).message, 'eek eek');
|
|
28
|
+
assert.strictEqual((await apos.cache.get('test2', 'capuchin')).message, 'ook ook');
|
|
29
|
+
});
|
|
25
30
|
it('should now contain capuchin', async function() {
|
|
26
31
|
const monkey = await apos.cache.get('test', 'capuchin');
|
|
27
32
|
assert(monkey);
|
|
@@ -33,4 +38,19 @@ describe('Caches', function() {
|
|
|
33
38
|
it('should not contain capuchin anymore', async function() {
|
|
34
39
|
assert(!(await apos.cache.get('test', 'capuchin')));
|
|
35
40
|
});
|
|
41
|
+
it('but test2 cache still does contain capuchin', async function() {
|
|
42
|
+
assert.strictEqual((await apos.cache.get('test2', 'capuchin')).message, 'ook ook');
|
|
43
|
+
});
|
|
44
|
+
it('unique key index does block double insert in same namespace', async function() {
|
|
45
|
+
try {
|
|
46
|
+
await apos.cache.cacheCollection.insert({
|
|
47
|
+
name: 'test2',
|
|
48
|
+
key: 'capuchin'
|
|
49
|
+
});
|
|
50
|
+
// That's bad, we should be blocked
|
|
51
|
+
assert(false);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// That's good, we were blocked
|
|
54
|
+
}
|
|
55
|
+
});
|
|
36
56
|
});
|
package/test/pages.js
CHANGED
|
@@ -1229,10 +1229,11 @@ describe('Pages', function() {
|
|
|
1229
1229
|
try {
|
|
1230
1230
|
const publicUrl = generatePublicUrl(shareResponse);
|
|
1231
1231
|
await apos.http.get(publicUrl, { fullResponse: true });
|
|
1232
|
-
throw new Error('should have thrown 404 error');
|
|
1233
1232
|
} catch (error) {
|
|
1234
1233
|
assert(error.status === 404);
|
|
1234
|
+
return;
|
|
1235
1235
|
}
|
|
1236
|
+
throw new Error('should have thrown 404 error');
|
|
1236
1237
|
});
|
|
1237
1238
|
});
|
|
1238
1239
|
});
|
package/test/pieces.js
CHANGED
|
@@ -1818,10 +1818,11 @@ describe('Pieces', function() {
|
|
|
1818
1818
|
try {
|
|
1819
1819
|
const publicUrl = generatePublicUrl(shareResponse);
|
|
1820
1820
|
await apos.http.get(publicUrl, { fullResponse: true });
|
|
1821
|
-
throw new Error('should have thrown 404 error');
|
|
1822
1821
|
} catch (error) {
|
|
1823
1822
|
assert(error.status === 404);
|
|
1823
|
+
return;
|
|
1824
1824
|
}
|
|
1825
|
+
throw new Error('should have thrown 404 error');
|
|
1825
1826
|
});
|
|
1826
1827
|
});
|
|
1827
1828
|
});
|
package/test/static-i18n.js
CHANGED
|
@@ -21,6 +21,10 @@ describe('static i18n', function() {
|
|
|
21
21
|
en: {},
|
|
22
22
|
fr: {
|
|
23
23
|
prefix: '/fr'
|
|
24
|
+
},
|
|
25
|
+
es: {
|
|
26
|
+
prefix: '/es',
|
|
27
|
+
private: true
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
}
|
|
@@ -88,4 +92,14 @@ describe('static i18n', function() {
|
|
|
88
92
|
assert.strictEqual(browserData.i18n.en.custom.customTestOne, 'Custom Test One From Subtype');
|
|
89
93
|
});
|
|
90
94
|
|
|
95
|
+
it('should return a 404 HTTP error code when a logged out user tries to access to a content in a private locale', async function() {
|
|
96
|
+
try {
|
|
97
|
+
await apos.http.get('/es');
|
|
98
|
+
} catch (error) {
|
|
99
|
+
assert(error.status === 404);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
throw new Error('should have thrown 404 error');
|
|
103
|
+
});
|
|
104
|
+
|
|
91
105
|
});
|