apostrophe 3.53.0 → 3.55.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/defaults.js +1 -0
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +5 -2
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +28 -19
  5. package/modules/@apostrophecms/any-doc-type/index.js +2 -2
  6. package/modules/@apostrophecms/any-page-type/index.js +2 -2
  7. package/modules/@apostrophecms/doc/index.js +55 -29
  8. package/modules/@apostrophecms/doc-type/index.js +11 -6
  9. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +4 -440
  10. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +445 -0
  11. package/modules/@apostrophecms/i18n/i18n/de.json +113 -105
  12. package/modules/@apostrophecms/i18n/i18n/es.json +10 -0
  13. package/modules/@apostrophecms/i18n/i18n/fr.json +8 -0
  14. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +10 -0
  15. package/modules/@apostrophecms/i18n/i18n/sk.json +8 -0
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +1 -0
  17. package/modules/@apostrophecms/log/index.js +429 -0
  18. package/modules/@apostrophecms/login/index.js +47 -4
  19. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +14 -1
  20. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +1 -1
  21. package/modules/@apostrophecms/module/index.js +32 -6
  22. package/modules/@apostrophecms/module/lib/log.js +68 -0
  23. package/modules/@apostrophecms/page/index.js +71 -19
  24. package/modules/@apostrophecms/page/lib/legacy-migrations.js +0 -57
  25. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +8 -285
  26. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +291 -0
  27. package/modules/@apostrophecms/page-type/index.js +39 -26
  28. package/modules/@apostrophecms/piece-type/index.js +19 -11
  29. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  30. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +2 -357
  31. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +2 -86
  32. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +2 -254
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +2 -77
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -44
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +2 -64
  36. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +2 -94
  37. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +3 -47
  38. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -82
  39. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +2 -37
  40. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +2 -26
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -57
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +2 -259
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +2 -38
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +2 -275
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -167
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +2 -115
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +3 -279
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +2 -83
  49. package/modules/@apostrophecms/schema/ui/apos/lib/detectChange.js +10 -1
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +361 -0
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +89 -0
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +257 -0
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +81 -0
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputBoolean.js +48 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +68 -0
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +98 -0
  57. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +49 -0
  58. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +86 -0
  59. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputPassword.js +41 -0
  60. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +29 -0
  61. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRange.js +60 -0
  62. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +262 -0
  63. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +41 -0
  64. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +278 -0
  65. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +170 -0
  66. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +118 -0
  67. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +281 -0
  68. package/modules/@apostrophecms/schema/ui/apos/logic/AposSearchList.js +85 -0
  69. package/modules/@apostrophecms/template/index.js +1 -1
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -2
  71. package/modules/@apostrophecms/util/index.js +83 -13
  72. package/modules/@apostrophecms/util/lib/logger.js +19 -17
  73. package/package.json +1 -1
  74. package/test/docs.js +35 -2
  75. package/test/log.js +1765 -0
  76. package/test/pages.js +57 -0
  77. package/test-lib/util.js +1 -1
@@ -27,90 +27,10 @@
27
27
  </template>
28
28
 
29
29
  <script>
30
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin.js';
31
- import AposInputFollowingMixin from 'Modules/@apostrophecms/schema/mixins/AposInputFollowingMixin.js';
32
- import AposInputConditionalFieldsMixin from 'Modules/@apostrophecms/schema/mixins/AposInputConditionalFieldsMixin.js';
33
-
30
+ import AposInputObjectLogic from '../logic/AposInputObject';
34
31
  export default {
35
32
  name: 'AposInputObject',
36
- mixins: [
37
- AposInputMixin,
38
- AposInputFollowingMixin,
39
- AposInputConditionalFieldsMixin
40
- ],
41
- props: {
42
- generation: {
43
- type: Number,
44
- required: false,
45
- default() {
46
- return null;
47
- }
48
- },
49
- docId: {
50
- type: String,
51
- required: false,
52
- default() {
53
- return null;
54
- }
55
- }
56
- },
57
- data () {
58
- const next = this.getNext();
59
- return {
60
- schemaInput: {
61
- data: next
62
- },
63
- next
64
- };
65
- },
66
- computed: {
67
- followingValuesWithParent() {
68
- return this.computeFollowingValues(this.schemaInput.data);
69
- },
70
- // Reqiured for AposInputConditionalFieldsMixin
71
- schema() {
72
- return this.field.schema;
73
- },
74
- values() {
75
- return this.schemaInput.data;
76
- }
77
- },
78
- watch: {
79
- schemaInput: {
80
- deep: true,
81
- handler() {
82
- if (!this.schemaInput.hasErrors) {
83
- this.next = this.schemaInput.data;
84
- }
85
- // Our validate method was called first before that of
86
- // the subfields, so remedy that by calling again on any
87
- // change to the subfield state during validation
88
- if (this.triggerValidation) {
89
- this.validateAndEmit();
90
- }
91
- }
92
- },
93
- generation() {
94
- this.next = this.getNext();
95
- this.schemaInput = {
96
- data: this.next
97
- };
98
- }
99
- },
100
- async created() {
101
- await this.evaluateExternalConditions();
102
- },
103
- methods: {
104
- validate (value) {
105
- if (this.schemaInput.hasErrors) {
106
- return 'invalid';
107
- }
108
- },
109
- // Return next at mount or when generation changes
110
- getNext() {
111
- return this.value ? this.value.data : (this.field.def || {});
112
- }
113
- }
33
+ mixins: [ AposInputObjectLogic ]
114
34
  };
115
35
  </script>
116
36
 
@@ -23,44 +23,9 @@
23
23
  </template>
24
24
 
25
25
  <script>
26
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
27
-
26
+ import AposInputPasswordLogic from '../logic/AposInputPassword';
28
27
  export default {
29
28
  name: 'AposInputPassword',
30
- mixins: [ AposInputMixin ],
31
- emits: [ 'return' ],
32
- computed: {
33
- tabindex () {
34
- return this.field.disableFocus ? '-1' : '0';
35
- }
36
- },
37
- methods: {
38
- validate(value) {
39
- if (this.field.required) {
40
- if (!value.length) {
41
- return { message: 'required' };
42
- }
43
- }
44
- if (this.field.min) {
45
- if (value.length && (value.length < this.field.min)) {
46
- return {
47
- message: this.$t('apostrophe:passwordErrorMin', {
48
- min: this.field.min
49
- })
50
- };
51
- }
52
- }
53
- if (this.field.max) {
54
- if (value.length && (value.length > this.field.max)) {
55
- return {
56
- message: this.$t('apostrophe:passwordErrorMax', {
57
- max: this.field.max
58
- })
59
- };
60
- }
61
- }
62
- return false;
63
- }
64
- }
29
+ mixins: [ AposInputPasswordLogic ]
65
30
  };
66
31
  </script>
@@ -42,34 +42,10 @@
42
42
  </template>
43
43
 
44
44
  <script>
45
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
46
- import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
47
- import InformationIcon from 'vue-material-design-icons/Information.vue';
48
-
45
+ import AposInputRadioLogic from '../logic/AposInputRadio';
49
46
  export default {
50
47
  name: 'AposInputRadio',
51
- components: { InformationIcon },
52
- mixins: [ AposInputMixin, AposInputChoicesMixin ],
53
- methods: {
54
- getChoiceId(uid, value) {
55
- return (uid + JSON.stringify(value)).replace(/\s+/g, '');
56
- },
57
- validate(value) {
58
- if (this.field.required && (value === '')) {
59
- return 'required';
60
- }
61
-
62
- if (value && !this.choices.find(choice => choice.value === value)) {
63
- return 'invalid';
64
- }
65
-
66
- return false;
67
- },
68
- change(value) {
69
- // Allows expression of non-string values
70
- this.next = this.choices.find(choice => choice.value === JSON.parse(value)).value;
71
- }
72
- }
48
+ mixins: [ AposInputRadioLogic ]
73
49
  };
74
50
  </script>
75
51
 
@@ -51,65 +51,10 @@
51
51
  </template>
52
52
 
53
53
  <script>
54
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
55
-
54
+ import AposInputRangeLogic from '../logic/AposInputRange';
56
55
  export default {
57
56
  name: 'AposInputRange',
58
- mixins: [ AposInputMixin ],
59
- data() {
60
- return {
61
- unit: this.field.unit || ''
62
- };
63
- },
64
- computed: {
65
- minLabel() {
66
- return this.field.min + this.unit;
67
- },
68
- maxLabel() {
69
- return this.field.max + this.unit;
70
- },
71
- valueLabel() {
72
- return this.next + this.unit;
73
- },
74
- isSet() {
75
- // Detect whether or not a range is currently unset
76
- // Use this flag to hide/show UI elements
77
- if (this.next >= this.field.min) {
78
- return true;
79
- } else {
80
- return false;
81
- }
82
- }
83
- },
84
- mounted() {
85
- // The range spec defaults to a value of midway between the min and max
86
- // Example: a range with an unset value and a min of 0 and max of 100 will become 50
87
- // This does not allow ranges to go unset :(
88
- if (!this.next) {
89
- this.unset();
90
- }
91
- },
92
- methods: {
93
- // Default to a value outside the range when no def is defined,
94
- // to be used as a flag.
95
- // The value will be set to null later in validation
96
- unset() {
97
- this.next = typeof this.field.def === 'number'
98
- ? this.field.def
99
- : this.field.min - 1;
100
- },
101
- validate(value) {
102
- if (this.field.required) {
103
- if (!value) {
104
- return 'required';
105
- }
106
- }
107
- return false;
108
- },
109
- convert(value) {
110
- return parseFloat(value);
111
- }
112
- }
57
+ mixins: [ AposInputRangeLogic ]
113
58
  };
114
59
  </script>
115
60
 
@@ -76,267 +76,10 @@
76
76
  </template>
77
77
 
78
78
  <script>
79
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
80
- import { klona } from 'klona';
81
-
79
+ import AposInputRelationshipLogic from '../logic/AposInputRelationship';
82
80
  export default {
83
81
  name: 'AposInputRelationship',
84
- mixins: [ AposInputMixin ],
85
- emits: [ 'input' ],
86
- data () {
87
- const next = (this.value && Array.isArray(this.value.data))
88
- ? klona(this.value.data) : (klona(this.field.def) || []);
89
-
90
- // Remember relationship subfield values even if a document
91
- // is temporarily deselected, easing the user's pain if they
92
- // inadvertently deselect something for a moment
93
- const subfields = Object.fromEntries(
94
- (next || []).filter(doc => doc._fields)
95
- .map(doc => [ doc._id, doc._fields ])
96
- );
97
-
98
- return {
99
- searchTerm: '',
100
- searchList: [],
101
- next,
102
- subfields,
103
- disabled: false,
104
- searching: false,
105
- choosing: false,
106
- relationshipSchema: null
107
- };
108
- },
109
- computed: {
110
- limitReached() {
111
- return this.field.max === this.next.length;
112
- },
113
- pluralLabel() {
114
- return apos.modules[this.field.withType].pluralLabel;
115
- },
116
- // TODO get 'Search' server for better i18n
117
- placeholder() {
118
- return this.field.placeholder || {
119
- key: 'apostrophe:searchDocType',
120
- type: this.$t(this.pluralLabel)
121
- };
122
- },
123
- // TODO get 'Browse' for better i18n
124
- browseLabel() {
125
- return {
126
- key: 'apostrophe:browseDocType',
127
- type: this.$t(this.pluralLabel)
128
- };
129
- },
130
- suggestion() {
131
- return {
132
- disabled: true,
133
- tooltip: false,
134
- icon: false,
135
- classes: [ 'suggestion' ],
136
- title: this.$t(this.field.suggestionLabel),
137
- help: this.$t({
138
- key: this.field.suggestionHelp || 'apostrophe:relationshipSuggestionHelp',
139
- type: this.$t(this.pluralLabel)
140
- }),
141
- customFields: [ 'help' ]
142
- };
143
- },
144
- hint() {
145
- return {
146
- disabled: true,
147
- tooltip: false,
148
- icon: 'binoculars-icon',
149
- iconSize: 35,
150
- classes: [ 'hint' ],
151
- title: this.$t('apostrophe:relationshipSuggestionNoResults'),
152
- help: this.$t({
153
- key: this.field.browse
154
- ? 'apostrophe:relationshipSuggestionSearchAndBrowse'
155
- : 'apostrophe:relationshipSuggestionSearch',
156
- type: this.$t(this.pluralLabel)
157
- }),
158
- customFields: [ 'help' ]
159
- };
160
- },
161
- chooserComponent () {
162
- return apos.modules[this.field.withType].components.managerModal;
163
- },
164
- disableUnpublished() {
165
- return apos.modules[this.field.withType].localized;
166
- },
167
- buttonModifiers() {
168
- const modifiers = [ 'small' ];
169
- if (this.modifiers.includes('no-search')) {
170
- modifiers.push('block');
171
- }
172
- return modifiers;
173
- },
174
- minSize() {
175
- const [ widgetOptions = {} ] = apos.area.widgetOptions;
176
-
177
- return widgetOptions.minSize || [];
178
- },
179
- duplicate () {
180
- return this.value.duplicate ? 'apos-input--error' : null;
181
- }
182
- },
183
- watch: {
184
- next(after, before) {
185
- for (const doc of before) {
186
- this.subfields[doc._id] = doc._fields;
187
- }
188
- for (const doc of after) {
189
- if (Object.keys(doc._fields || {}).length) {
190
- continue;
191
- }
192
- doc._fields = this.field.schema && (this.subfields[doc._id]
193
- ? this.subfields[doc._id]
194
- : this.getDefault());
195
- }
196
- }
197
- },
198
- mounted () {
199
- this.checkLimit();
200
- },
201
- methods: {
202
- validate(value) {
203
- this.checkLimit();
204
-
205
- if (this.field.required && !value.length) {
206
- return { message: 'required' };
207
- }
208
-
209
- if (this.field.min && this.field.min > value.length) {
210
- return { message: `minimum of ${this.field.min} required` };
211
- }
212
-
213
- return false;
214
- },
215
- checkLimit() {
216
- if (this.limitReached) {
217
- this.searchTerm = 'Limit reached!';
218
- } else if (this.searchTerm === 'Limit reached!') {
219
- this.searchTerm = '';
220
- }
221
-
222
- this.disabled = !!this.limitReached;
223
- },
224
- updateSelected(items) {
225
- this.next = items;
226
- },
227
- async search(qs) {
228
- if (this.field.suggestionLimit) {
229
- qs.perPage = this.field.suggestionLimit;
230
- }
231
- if (this.field.suggestionSort) {
232
- qs.sort = this.field.suggestionSort;
233
- }
234
- if (this.field.withType === '@apostrophecms/image') {
235
- apos.bus.$emit('piece-relationship-query', qs);
236
- }
237
-
238
- this.searching = true;
239
- const list = await apos.http.get(
240
- apos.modules[this.field.withType].action,
241
- {
242
- busy: false,
243
- draft: true,
244
- qs
245
- }
246
- );
247
-
248
- const removeSelectedItem = item => !this.next.map(i => i._id).includes(item._id);
249
- const formatItems = item => ({
250
- ...item,
251
- disabled: this.disableUnpublished && !item.lastPublishedAt
252
- });
253
-
254
- const results = (list.results || [])
255
- .filter(removeSelectedItem)
256
- .map(formatItems);
257
-
258
- const suggestion = !qs.autocomplete && this.suggestion;
259
- const hint = (!qs.autocomplete || !results.length) && this.hint;
260
-
261
- this.searchList = [ suggestion, ...results, hint ].filter(Boolean);
262
- this.searching = false;
263
- },
264
- async input () {
265
- if (this.searching) {
266
- return;
267
- }
268
-
269
- const trimmed = this.searchTerm.trim();
270
- const qs = trimmed.length
271
- ? {
272
- autocomplete: trimmed
273
- }
274
- : {};
275
-
276
- await this.search(qs);
277
- },
278
- handleFocusOut() {
279
- // hide search list when click outside the input
280
- // timeout to execute "@select" method before
281
- setTimeout(() => {
282
- this.searchList = [];
283
- }, 200);
284
- },
285
- watchValue () {
286
- this.error = this.value.error;
287
- // Ensure the internal state is an array.
288
- this.next = Array.isArray(this.value.data) ? this.value.data : [];
289
- },
290
- async choose () {
291
- const result = await apos.modal.execute(this.chooserComponent, {
292
- title: this.field.label || this.field.name,
293
- moduleName: this.field.withType,
294
- chosen: this.next,
295
- relationshipField: this.field
296
- });
297
- if (result) {
298
- this.updateSelected(result);
299
- }
300
- },
301
- async editRelationship (item) {
302
- const editor = this.field.editor || 'AposRelationshipEditor';
303
-
304
- const result = await apos.modal.execute(editor, {
305
- schema: this.field.schema,
306
- item,
307
- title: item.title,
308
- value: item._fields
309
- });
310
-
311
- if (result) {
312
- const index = this.next.findIndex(_item => _item._id === item._id);
313
- this.$set(this.next, index, {
314
- ...this.next[index],
315
- _fields: result
316
- });
317
- }
318
- },
319
- getEditRelationshipLabel () {
320
- if (this.field.editor === 'AposImageRelationshipEditor') {
321
- return 'apostrophe:editImageAdjustments';
322
- }
323
- },
324
- getDefault() {
325
- const object = {};
326
- this.field.schema.forEach(field => {
327
- if (field.name.startsWith('_')) {
328
- return;
329
- }
330
- // Using `hasOwn` here, not simply checking if `field.def` is truthy
331
- // so that `false`, `null`, `''` or `0` are taken into account:
332
- const hasDefaultValue = Object.hasOwn(field, 'def');
333
- object[field.name] = hasDefaultValue
334
- ? klona(field.def)
335
- : null;
336
- });
337
- return object;
338
- }
339
- }
82
+ mixins: [ AposInputRelationshipLogic ]
340
83
  };
341
84
  </script>
342
85
 
@@ -20,45 +20,9 @@
20
20
  </template>
21
21
 
22
22
  <script>
23
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
24
- import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
25
-
23
+ import AposInputSelectLogic from '../logic/AposInputSelect';
26
24
  export default {
27
25
  name: 'AposInputSelect',
28
- mixins: [ AposInputMixin, AposInputChoicesMixin ],
29
- props: {
30
- icon: {
31
- type: String,
32
- default: 'menu-down-icon'
33
- }
34
- },
35
- data() {
36
- return {
37
- next: (this.value.data == null) ? null : this.value.data,
38
- choices: []
39
- };
40
- },
41
- computed: {
42
- classes () {
43
- return [ this.value.duplicate && 'apos-input--error' ];
44
- }
45
- },
46
- methods: {
47
- validate(value) {
48
- if (this.field.required && (value === null)) {
49
- return 'required';
50
- }
51
-
52
- if (value && !this.choices.find(choice => choice.value === value)) {
53
- return 'invalid';
54
- }
55
-
56
- return false;
57
- },
58
- change(value) {
59
- // Allows expression of non-string values
60
- this.next = this.choices.find(choice => choice.value === value).value;
61
- }
62
- }
26
+ mixins: [ AposInputSelectLogic ]
63
27
  };
64
28
  </script>