apostrophe 3.24.0 → 3.25.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/CHANGELOG.md +10 -0
- package/modules/@apostrophecms/schema/index.js +7 -1
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +6 -8
- 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/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.25.0 (2022-07-20)
|
|
4
|
+
|
|
5
|
+
* `radio` and `checkboxes` input field types now support a server side `choices` function for supplying their `choices` array dynamically, just like `select` fields do. Future custom field types can opt into this functionality with the field type flag `dynamicChoices: true`.
|
|
6
|
+
|
|
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.
|
|
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
|
+
* Minimum version of `sanitize-html` bumped to ensure a potential denial-of-service vector is closed.
|
|
12
|
+
|
|
3
13
|
## 3.24.0 (2022-07-06)
|
|
4
14
|
|
|
5
15
|
### Adds
|
|
@@ -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) {
|
|
@@ -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
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.25.0",
|
|
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",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"regexp-quote": "0.0.0",
|
|
103
103
|
"resolve": "^1.19.0",
|
|
104
104
|
"resolve-from": "^5.0.0",
|
|
105
|
-
"sanitize-html": "^2.
|
|
105
|
+
"sanitize-html": "^2.7.1",
|
|
106
106
|
"sass": "^1.52.3",
|
|
107
107
|
"sass-loader": "^10.1.1",
|
|
108
108
|
"server-destroy": "^1.0.1",
|
|
@@ -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",
|