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 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 !== 'select' ||
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(field.choices, 'value'), choice);
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(field.choices, 'value'), choice);
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
- let choices;
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 field.choices"
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.field.choices) || !Array.isArray(values)) {
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.field.choices.map(choice => {
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 field.choices" :key="choice.value"
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.field.choices.find(choice => choice.value === value)) {
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.field.choices.find(choice => choice.value === JSON.parse(value)).value;
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.push({
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.24.0",
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.0.0",
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": "^15.9.6",
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",