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
@@ -0,0 +1,445 @@
1
+ import { detectDocChange } from 'Modules/@apostrophecms/schema/lib/detectChange';
2
+ import AposPublishMixin from 'Modules/@apostrophecms/ui/mixins/AposPublishMixin';
3
+ import AposArchiveMixin from 'Modules/@apostrophecms/ui/mixins/AposArchiveMixin';
4
+ import AposModifiedMixin from 'Modules/@apostrophecms/ui/mixins/AposModifiedMixin';
5
+ import checkIfConditions from 'apostrophe/lib/check-if-conditions';
6
+
7
+ export default {
8
+ name: 'AposDocContextMenu',
9
+ mixins: [ AposPublishMixin, AposArchiveMixin, AposModifiedMixin ],
10
+ props: {
11
+ doc: {
12
+ type: Object,
13
+ required: true
14
+ },
15
+ // If editing in a modal, pass the current value object from the editor here
16
+ // so that the visibility of options takes unsaved changes into account
17
+ current: {
18
+ type: Object,
19
+ default() {
20
+ return null;
21
+ }
22
+ },
23
+ // Existing published version of doc, if there is one
24
+ published: {
25
+ type: Object,
26
+ default() {
27
+ return null;
28
+ }
29
+ },
30
+ // These props all default to true, and can be passed with a value of false to hide
31
+ // functionality even if the user can otherwise perform the action on this document.
32
+ // The component will figure out on its own if the user can perform each action or not.
33
+ showEdit: {
34
+ type: Boolean,
35
+ default() {
36
+ return true;
37
+ }
38
+ },
39
+ showPreview: {
40
+ type: Boolean,
41
+ default() {
42
+ return true;
43
+ }
44
+ },
45
+ showDiscardDraft: {
46
+ type: Boolean,
47
+ default() {
48
+ return true;
49
+ }
50
+ },
51
+ showCopy: {
52
+ type: Boolean,
53
+ default() {
54
+ return true;
55
+ }
56
+ },
57
+ showArchive: {
58
+ type: Boolean,
59
+ default() {
60
+ return true;
61
+ }
62
+ },
63
+ showRestore: {
64
+ type: Boolean,
65
+ default() {
66
+ return true;
67
+ }
68
+ },
69
+ // Unlike the others, this one defaults to false because it is
70
+ // generally only allowed in the Manage Submissions view.
71
+ showDismissSubmission: {
72
+ type: Boolean,
73
+ default() {
74
+ return false;
75
+ }
76
+ },
77
+ disabled: {
78
+ type: Boolean,
79
+ default: false
80
+ }
81
+ },
82
+ emits: [ 'menu-open', 'menu-close', 'close' ],
83
+ data() {
84
+ return {
85
+ // Updated by both the context prop and any content-changed apos events
86
+ context: this.doc,
87
+ // Custom context menu operations
88
+ customOperations: apos.modules['@apostrophecms/doc'].contextOperations
89
+ };
90
+ },
91
+ computed: {
92
+ menu() {
93
+ const menu = [
94
+ // TODO
95
+ // ...(this.isModifiedFromPublished ? [
96
+ // {
97
+ // label: 'Share Draft',
98
+ // action: 'share'
99
+ // }
100
+ // ] : []),
101
+ ...((this.showEdit && this.context._edit) ? [
102
+ {
103
+ // When archived the edit action opens a read-only "editor"
104
+ label: this.context.archived ? 'apostrophe:view' : 'apostrophe:edit',
105
+ action: 'edit'
106
+ }
107
+ ] : []),
108
+ ...((this.showPreview && this.hasUrl) ? [
109
+ {
110
+ label: 'apostrophe:preview',
111
+ action: 'preview'
112
+ }
113
+ ] : []),
114
+ ...((this.showDismissSubmission && this.canDismissSubmission) ? [
115
+ {
116
+ label: 'apostrophe:dismissSubmission',
117
+ action: 'dismissSubmission'
118
+ }
119
+ ] : []),
120
+ ...(this.showCopy && this.canCopy ? [
121
+ {
122
+ label: 'apostrophe:duplicate',
123
+ action: 'copy'
124
+ }
125
+ ] : []),
126
+ ...(this.canLocalize ? [
127
+ {
128
+ label: 'apostrophe:localize',
129
+ action: 'localize'
130
+ }
131
+ ] : []),
132
+ ...this.customMenusByContext,
133
+ ...((this.showDiscardDraft && this.canDiscardDraft) ? [
134
+ {
135
+ label: this.context.lastPublishedAt ? 'apostrophe:discardDraft' : 'apostrophe:deleteDraft',
136
+ action: 'discardDraft',
137
+ modifiers: [ 'danger' ]
138
+ }
139
+ ] : []),
140
+ ...(this.showArchive && this.canArchive ? [
141
+ {
142
+ label: 'apostrophe:archive',
143
+ action: 'archive',
144
+ modifiers: [ 'danger' ]
145
+ }
146
+ ] : []),
147
+ ...(this.canUnpublish ? [
148
+ {
149
+ label: 'apostrophe:unpublish',
150
+ action: 'unpublish',
151
+ modifiers: [ 'danger' ]
152
+ }
153
+ ] : []),
154
+ ...(this.showRestore && this.canRestore ? [
155
+ {
156
+ label: 'apostrophe:restore',
157
+ action: 'restore'
158
+ }
159
+ ] : [])
160
+ ];
161
+ return menu;
162
+ },
163
+ customMenusByContext() {
164
+ if (!this.canEdit) {
165
+ return [];
166
+ }
167
+
168
+ const menus = this.customOperationsByContext
169
+ .map(op => ({
170
+ label: op.label,
171
+ action: op.action,
172
+ modifiers: op.modifiers || []
173
+ }));
174
+ menus.sort((a, b) => a.modifiers.length - b.modifiers.length);
175
+ return menus;
176
+ },
177
+ customOperationsByContext() {
178
+ return this.customOperations.filter(({
179
+ manuallyPublished, hasUrl, conditions, context, if: ifProps, moduleIf
180
+ }) => {
181
+ if (typeof manuallyPublished === 'boolean' && manuallyPublished !== this.manuallyPublished) {
182
+ return false;
183
+ }
184
+
185
+ if (typeof hasUrl === 'boolean' && hasUrl !== this.hasUrl) {
186
+ return false;
187
+ }
188
+
189
+ if (conditions) {
190
+ const notAllowed = conditions.some((action) => !this[action]);
191
+
192
+ if (notAllowed) {
193
+ return false;
194
+ }
195
+ }
196
+
197
+ ifProps = ifProps || {};
198
+ moduleIf = moduleIf || {};
199
+ const canSeeOperation = checkIfConditions(this.doc, ifProps) && checkIfConditions(this.moduleOptions, moduleIf);
200
+
201
+ if (!canSeeOperation) {
202
+ return false;
203
+ }
204
+
205
+ return context === 'update' && this.isUpdateOperation;
206
+ });
207
+ },
208
+ moduleName() {
209
+ if (apos.modules[this.context.type].action === apos.modules['@apostrophecms/page'].action) {
210
+ return '@apostrophecms/page';
211
+ } else {
212
+ return this.context.type;
213
+ }
214
+ },
215
+ moduleOptions() {
216
+ return apos.modules[this.moduleName];
217
+ },
218
+ isUpdateOperation() {
219
+ return !!this.context._id;
220
+ },
221
+ hasUrl() {
222
+ return !!this.context._url;
223
+ },
224
+ canPublish() {
225
+ if (this.context._id) {
226
+ return this.context._publish;
227
+ } else {
228
+ return this.moduleOptions.canPublish;
229
+ }
230
+ },
231
+ canEdit() {
232
+ if (this.context._id) {
233
+ return this.context._edit;
234
+ } else {
235
+ return true;
236
+ }
237
+ },
238
+ canDismissSubmission() {
239
+ return this.canEdit && this.context.submitted && (this.canPublish || (this.context.submitted.byId === apos.login.user._id));
240
+ },
241
+ canDiscardDraft() {
242
+ if (!this.manuallyPublished) {
243
+ return false;
244
+ }
245
+ if (!this.context._id) {
246
+ return false;
247
+ }
248
+ if (!this.canEdit) {
249
+ return false;
250
+ }
251
+ return (
252
+ (!this.context.lastPublishedAt) &&
253
+ !this.moduleOptions.singleton
254
+ ) || (
255
+ this.context.lastPublishedAt &&
256
+ this.isModifiedFromPublished
257
+ );
258
+ },
259
+ canLocalize() {
260
+ return this.moduleOptions.canLocalize &&
261
+ this.context._id;
262
+ },
263
+ canArchive() {
264
+ return (
265
+ this.canEdit &&
266
+ this.context._id &&
267
+ !this.moduleOptions.singleton &&
268
+ !this.context.archived &&
269
+ !this.context.parked &&
270
+ ((this.moduleOptions.canPublish && this.context.lastPublishedAt) || !this.manuallyPublished)
271
+ );
272
+ },
273
+ canUnpublish() {
274
+ return (
275
+ this.canEdit &&
276
+ !this.context.parked &&
277
+ this.moduleOptions.canPublish &&
278
+ this.context.lastPublishedAt &&
279
+ this.manuallyPublished
280
+ );
281
+ },
282
+ canCopy() {
283
+ return this.canEdit &&
284
+ this.moduleOptions.canEdit &&
285
+ !this.moduleOptions.singleton &&
286
+ this.context._id;
287
+ },
288
+ canRestore() {
289
+ return (
290
+ this.canEdit &&
291
+ this.context._id &&
292
+ this.context.archived &&
293
+ ((this.moduleOptions.canPublish && this.context.lastPublishedAt) || !this.manuallyPublished)
294
+ );
295
+ },
296
+ manuallyPublished() {
297
+ return this.moduleOptions.localized && !this.autopublish;
298
+ },
299
+ autopublish() {
300
+ return this.context._aposAutopublish ?? this.moduleOptions.autopublish;
301
+ },
302
+ isModified() {
303
+ if (!this.current) {
304
+ return false;
305
+ }
306
+ return detectDocChange(this.schema, this.context, this.current);
307
+ },
308
+ isModifiedFromPublished() {
309
+ if (this.context.modified) {
310
+ // In a list context, we won't have every area property to
311
+ // compare, but we will have this previously set flag
312
+ return true;
313
+ }
314
+ if (!this.context.lastPublishedAt) {
315
+ return false;
316
+ }
317
+ if (!this.published) {
318
+ return false;
319
+ }
320
+ const result = detectDocChange(this.schema, this.published, this.context);
321
+ return result;
322
+ },
323
+ schema() {
324
+ // moduleOptions gives us the action, etc. but here we need the schema
325
+ // which is always type specific, even for pages so get it ourselves
326
+ let schema = (apos.modules[this.context.type].schema || []).filter(field => apos.schema.components.fields[field.type]);
327
+ // Archive UI is handled via action buttons
328
+ schema = schema.filter(field => field.name !== 'archived');
329
+ return schema;
330
+ }
331
+ },
332
+ watch: {
333
+ doc() {
334
+ this.context = this.doc;
335
+ }
336
+ },
337
+ mounted() {
338
+ apos.bus.$on('content-changed', this.onContentChanged);
339
+ apos.bus.$on('command-menu-admin-bar-discard-draft', this.customDiscardDraft);
340
+ },
341
+ destroyed() {
342
+ apos.bus.$off('content-changed', this.onContentChanged);
343
+ apos.bus.$off('command-menu-admin-bar-discard-draft', this.customDiscardDraft);
344
+ },
345
+ methods: {
346
+ customDiscardDraft() {
347
+ if (this.showDiscardDraft && this.canDiscardDraft) {
348
+ this.menuHandler('discardDraft');
349
+ }
350
+ },
351
+ async onContentChanged(e) {
352
+ if (e.doc && (e.doc._id === this.context._id)) {
353
+ this.context = e.doc;
354
+ } else if (e.docIds && e.docIds.includes(this.context._id)) {
355
+ try {
356
+ this.context = await apos.http.get(`${this.moduleOptions.action}/${this.context._id}`, {
357
+ busy: true
358
+ });
359
+ } catch (error) {
360
+ // If not found it is likely that there was an archiving or restoring
361
+ // batch operation.
362
+ if (error.name !== 'notfound') {
363
+ console.error(error);
364
+ }
365
+ }
366
+ }
367
+ },
368
+ menuHandler(action) {
369
+ const operation = this.customOperations.find(op => op.action === action);
370
+ if (operation) {
371
+ this.customAction(this.context, operation);
372
+ return;
373
+ }
374
+ this[action](this.context);
375
+ },
376
+ async edit(doc) {
377
+ await apos.modal.execute(doc._aposEditorModal || this.moduleOptions.components.editorModal, {
378
+ moduleName: this.moduleName,
379
+ docId: doc._id,
380
+ type: doc.type
381
+ });
382
+ },
383
+ preview(doc) {
384
+ window.open(doc._url, '_blank').focus();
385
+ },
386
+ async copy(doc) {
387
+ // If there are changes warn the user before discarding them before
388
+ // the copy operation
389
+ if (this.current) {
390
+ if (!await this.confirmAndCancel()) {
391
+ return;
392
+ } else {
393
+ this.$emit('close', doc);
394
+ }
395
+ }
396
+
397
+ await apos.modal.execute(doc._aposEditorModal || this.moduleOptions.components.editorModal, {
398
+ moduleName: this.moduleName,
399
+ copyOfId: doc._id,
400
+ // Passed for bc
401
+ copyOf: {
402
+ ...this.current || doc,
403
+ _id: doc._id
404
+ },
405
+ type: doc.type
406
+ });
407
+
408
+ },
409
+ async customAction(doc, operation) {
410
+ const props = {
411
+ moduleName: operation.moduleName || this.moduleName,
412
+ // For backwards compatibility
413
+ doc,
414
+ ...docProps(doc),
415
+ ...operation.props
416
+ };
417
+ await apos.modal.execute(operation.modal, props);
418
+ function docProps(doc) {
419
+ return Object.fromEntries(Object.entries(operation.docProps || {}).map(([ key, value ]) => {
420
+ return [ key, doc[value] ];
421
+ }));
422
+ }
423
+ },
424
+ async localize(doc) {
425
+ // If there are changes warn the user before discarding them before
426
+ // the localize operation
427
+ if (this.current) {
428
+ if (!await this.confirmAndCancel()) {
429
+ return;
430
+ } else {
431
+ this.$emit('close', doc);
432
+ }
433
+ }
434
+ apos.bus.$emit('admin-menu-click', {
435
+ itemName: '@apostrophecms/i18n:localize',
436
+ props: {
437
+ doc
438
+ }
439
+ });
440
+ },
441
+ close() {
442
+ this.$emit('close', this.doc);
443
+ }
444
+ }
445
+ };