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
@@ -29,7 +29,7 @@
29
29
  >
30
30
  <slot name="before" />
31
31
  <component
32
- v-for="field in schema" :key="field.name"
32
+ v-for="field in schema" :key="field.name.concat(field._id ?? '')"
33
33
  :data-apos-field="field.name"
34
34
  :is="fieldStyle === 'table' ? 'td' : 'div'"
35
35
  v-show="displayComponent(field)"
@@ -56,286 +56,10 @@
56
56
  </template>
57
57
 
58
58
  <script>
59
- import { detectFieldChange } from 'Modules/@apostrophecms/schema/lib/detectChange';
60
-
59
+ import AposSchemaLogic from '../logic/AposSchema';
61
60
  export default {
62
61
  name: 'AposSchema',
63
- props: {
64
- value: {
65
- type: Object,
66
- required: true
67
- },
68
- generation: {
69
- type: Number,
70
- required: false,
71
- default() {
72
- return null;
73
- }
74
- },
75
- schema: {
76
- type: Array,
77
- required: true
78
- },
79
- fieldStyle: {
80
- type: String,
81
- required: false,
82
- default: ''
83
- },
84
- currentFields: {
85
- type: Array,
86
- default() {
87
- return null;
88
- }
89
- },
90
- followingValues: {
91
- type: Object,
92
- default() {
93
- return {};
94
- }
95
- },
96
- conditionalFields: {
97
- type: Object,
98
- default() {
99
- return {};
100
- }
101
- },
102
- modifiers: {
103
- type: Array,
104
- default() {
105
- return [];
106
- }
107
- },
108
- triggerValidation: Boolean,
109
- utilityRail: {
110
- type: Boolean,
111
- default() {
112
- return false;
113
- }
114
- },
115
- docId: {
116
- type: String,
117
- default() {
118
- return null;
119
- }
120
- },
121
- serverErrors: {
122
- type: Object,
123
- default() {
124
- return null;
125
- }
126
- },
127
- displayOptions: {
128
- type: Object,
129
- default() {
130
- return {};
131
- }
132
- },
133
- changed: {
134
- type: Array,
135
- default() {
136
- return [];
137
- }
138
- }
139
- },
140
- emits: [
141
- 'input',
142
- 'reset',
143
- 'validate',
144
- 'update-doc-data'
145
- ],
146
- data() {
147
- return {
148
- schemaReady: false,
149
- next: {
150
- hasErrors: false,
151
- data: {},
152
- fieldErrors: {}
153
- },
154
- fieldState: {},
155
- fieldComponentMap: window.apos.schema.components.fields || {}
156
- };
157
- },
158
- computed: {
159
- fields() {
160
- const fields = {};
161
- this.schema.forEach(item => {
162
- fields[item.name] = {};
163
- fields[item.name].field = item;
164
- fields[item.name].value = {
165
- data: this.value[item.name]
166
- };
167
- fields[item.name].serverError = this.serverErrors && this.serverErrors[item.name];
168
- fields[item.name].modifiers = [
169
- ...(this.modifiers || []),
170
- ...(item.modifiers || [])
171
- ];
172
- });
173
- return fields;
174
- }
175
- },
176
- watch: {
177
- fieldState: {
178
- deep: true,
179
- handler() {
180
- this.updateNextAndEmit();
181
- }
182
- },
183
- schema() {
184
- this.populateDocData();
185
- },
186
- 'value.data._id'(_id) {
187
- // The doc might be swapped out completely in cases such as the media
188
- // library editor. Repopulate the fields if that happens.
189
- if (
190
- // If the fieldState had been cleared and there's new populated data
191
- (!this.fieldState._id && _id) ||
192
- // or if there *is* active fieldState, but the new data is a new doc
193
- (this.fieldState._id && _id !== this.fieldState._id.data)
194
- ) {
195
- // repopulate the schema.
196
- this.populateDocData();
197
- }
198
- },
199
- generation() {
200
- // repopulate the schema.
201
- this.populateDocData();
202
- },
203
- conditionalFields(newVal, oldVal) {
204
- for (const field in oldVal) {
205
- if (!this.fieldState[field] || (newVal[field] === oldVal[field]) || !this.fieldState[field].ranValidation) {
206
- continue;
207
- }
208
-
209
- if (
210
- (newVal[field] === false) ||
211
- (newVal[field] && this.fieldState[field].ranValidation)
212
- ) {
213
- this.$emit('validate');
214
- }
215
- }
216
- }
217
- },
218
- created() {
219
- this.populateDocData();
220
- },
221
- methods: {
222
- getDisplayOptions(fieldName) {
223
- let options = {};
224
- if (this.displayOptions) {
225
- options = { ...this.displayOptions };
226
- }
227
- if (this.changed && this.changed.includes(fieldName)) {
228
- options.changed = true;
229
- }
230
- return options;
231
- },
232
- populateDocData() {
233
- this.schemaReady = false;
234
- const next = {
235
- hasErrors: false,
236
- data: {}
237
- };
238
-
239
- const fieldState = {};
240
-
241
- // Though not in the schema, keep track of the _id field.
242
- if (this.value.data._id) {
243
- next.data._id = this.value.data._id;
244
- fieldState._id = { data: this.value.data._id };
245
- }
246
- // Though not *always* in the schema, keep track of the archived status.
247
- if (this.value.data.archived !== undefined) {
248
- next.data.archived = this.value.data.archived;
249
- fieldState.archived = { data: this.value.data.archived };
250
- }
251
-
252
- this.schema.forEach(field => {
253
- const value = this.value.data[field.name];
254
- fieldState[field.name] = {
255
- error: false,
256
- data: (value === undefined) ? field.def : value
257
- };
258
- next.data[field.name] = fieldState[field.name].data;
259
- });
260
- this.next = next;
261
- this.fieldState = fieldState;
262
-
263
- // Wait until the next tick so the parent editor component is done
264
- // updating. This is only really a concern in editors that can swap
265
- // the active doc/object without unmounting AposSchema.
266
- this.$nextTick(() => {
267
- this.schemaReady = true;
268
- // Signal that the schema data is ready to be tracked.
269
- this.$emit('reset');
270
- });
271
- },
272
- updateNextAndEmit() {
273
- if (!this.schemaReady) {
274
- return;
275
- }
276
- const oldHasErrors = this.next.hasErrors;
277
- // destructure these for non-linked comparison
278
- const oldFieldState = { ...this.next.fieldState };
279
- const newFieldState = { ...this.fieldState };
280
-
281
- let changeFound = false;
282
-
283
- this.next.hasErrors = false;
284
- this.next.fieldState = { ...this.fieldState };
285
-
286
- this.schema.filter(field => this.displayComponent(field)).forEach(field => {
287
- if (this.fieldState[field.name].error) {
288
- this.next.hasErrors = true;
289
- }
290
- if (
291
- this.fieldState[field.name].data !== undefined &&
292
- detectFieldChange(field, this.next.data[field.name], this.fieldState[field.name].data)
293
- ) {
294
- changeFound = true;
295
- this.next.data[field.name] = this.fieldState[field.name].data;
296
- } else {
297
- this.next.data[field.name] = this.value.data[field.name];
298
- }
299
- });
300
- if (
301
- oldHasErrors !== this.next.hasErrors ||
302
- oldFieldState !== newFieldState
303
- ) {
304
- // Otherwise the save button may never unlock
305
- changeFound = true;
306
- }
307
-
308
- if (changeFound) {
309
- // ... removes need for deep watch at parent level
310
- this.$emit('input', { ...this.next });
311
- }
312
- },
313
- displayComponent({ name, hidden = false }) {
314
- if (hidden === true) {
315
- return false;
316
- }
317
-
318
- if (this.currentFields && !this.currentFields.includes(name)) {
319
- return false;
320
- }
321
-
322
- // Might not be a conditional field at all, so test explicitly for false
323
- if (this.conditionalFields[name] === false) {
324
- return false;
325
- }
326
-
327
- return true;
328
- },
329
- scrollFieldIntoView(fieldName) {
330
- // The refs for a name are an array if that ref was assigned
331
- // in a v-for. We know there is only one in this case
332
- // https://forum.vuejs.org/t/this-refs-theid-returns-an-array/31995/9
333
- this.$refs[fieldName][0].$el.scrollIntoView();
334
- },
335
- onUpdateDocData(data) {
336
- this.$emit('update-doc-data', data);
337
- }
338
- }
62
+ mixins: [ AposSchemaLogic ]
339
63
  };
340
64
  </script>
341
65
 
@@ -41,91 +41,10 @@
41
41
  </template>
42
42
 
43
43
  <script>
44
-
44
+ import AposSearchListLogic from '../logic/AposSearchList';
45
45
  export default {
46
46
  name: 'AposSearchList',
47
- props: {
48
- list: {
49
- type: Array,
50
- default() {
51
- return [];
52
- }
53
- },
54
- customFields: {
55
- type: Array,
56
- default() {
57
- return [];
58
- }
59
- },
60
- selectedItems: {
61
- type: Array,
62
- default() {
63
- return [];
64
- }
65
- },
66
- disabledTooltip: {
67
- type: String,
68
- default: null
69
- },
70
- label: {
71
- type: String,
72
- default: ''
73
- },
74
- help: {
75
- type: [ String, Object ],
76
- default: ''
77
- },
78
- icon: {
79
- type: String,
80
- default: 'text-box-icon'
81
- },
82
- iconSize: {
83
- type: Number,
84
- default: 20
85
- },
86
- fields: {
87
- type: Array,
88
- default() {
89
- return [ 'slug' ];
90
- }
91
- }
92
- },
93
- emits: [ 'select' ],
94
- methods: {
95
- select(item, $event) {
96
- if (item.disabled) {
97
- $event.stopPropagation();
98
- return;
99
- }
100
- const selectedItems = this.selectedItems;
101
- if (!selectedItems.some(selectedItem => selectedItem._id === item._id)) {
102
- // Never modify a prop
103
- this.$emit('select', [ ...selectedItems, item ]);
104
- }
105
- },
106
- getClasses(item) {
107
- const classes = {
108
- 'apos-search__item': true
109
- };
110
- if (item.disabled) {
111
- classes['apos-search__item--disabled'] = true;
112
- }
113
- item.classes && Array.isArray(item.classes) && item.classes.forEach(suffix => {
114
- classes[`apos-search__item--${suffix}`] = true;
115
- });
116
-
117
- return classes;
118
- },
119
- getTooltip(item) {
120
- return item.disabled && item.tooltip !== false ? this.disabledTooltip : null;
121
- },
122
- getIcon(item) {
123
- return {
124
- icon: item.icon ?? this.icon,
125
- iconSize: item.iconSize || this.iconSize
126
- };
127
- }
128
- }
47
+ mixins: [ AposSearchListLogic ]
129
48
  };
130
49
  </script>
131
50
 
@@ -44,7 +44,16 @@ export function detectFieldChange(field, v1, v2) {
44
44
  if (isEqual(v1, v2)) {
45
45
  return false;
46
46
  } else if (!v1 && !v2) {
47
- return v1 !== v2;
47
+ // False values from select,
48
+ // radio and boolean fields are
49
+ // detected as a change when selected.
50
+ if (
51
+ (v1 === false && v2 !== false) ||
52
+ (v1 !== false && v2 === false)
53
+ ) {
54
+ return true;
55
+ }
56
+ return false;
48
57
  } else if (!v1 && Array.isArray(v2) && v2.length === 0) {
49
58
  return false;
50
59
  } else {