apostrophe 3.61.1 → 3.63.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 +49 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +1 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +6 -4
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +9 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +8 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +6 -3
- package/modules/@apostrophecms/doc/index.js +256 -7
- package/modules/@apostrophecms/doc/ui/apos/mixins/AposFieldMetaUtilsMixin.js +93 -0
- package/modules/@apostrophecms/doc-type/index.js +78 -12
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +9 -1
- package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +24 -6
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +8 -7
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +1 -5
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +5 -2
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +1 -5
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +4 -2
- package/modules/@apostrophecms/login/index.js +25 -19
- package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +11 -1
- package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +46 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +8 -3
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +3 -0
- package/modules/@apostrophecms/page/index.js +118 -27
- package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +3 -1
- package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +7 -0
- package/modules/@apostrophecms/page-type/index.js +81 -4
- package/modules/@apostrophecms/permission/index.js +60 -31
- package/modules/@apostrophecms/piece-type/index.js +19 -41
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +9 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue +16 -1
- package/modules/@apostrophecms/rich-text-widget/index.js +141 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +8 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +7 -7
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +38 -79
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Link.js +11 -0
- package/modules/@apostrophecms/schema/index.js +9 -0
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +22 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +7 -8
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +76 -30
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +2 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +7 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +13 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +5 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +21 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +12 -8
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +35 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +6 -0
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +41 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +7 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +8 -0
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +10 -4
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +2 -2
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +7 -0
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +6 -0
- package/package.json +2 -2
- package/test/areas.js +1 -1
- package/test/assets.js +2 -2
- package/test/attachments.js +2 -2
- package/test/base-module.js +2 -1
- package/test/base-url-env-var.js +2 -2
- package/test/change-doc-ids.js +33 -31
- package/test/command-menu.js +2 -2
- package/test/content-i18n.js +47 -46
- package/test/db.js +2 -2
- package/test/docs.js +301 -126
- package/test/draft-published.js +2 -2
- package/test/email.js +2 -1
- package/test/express.js +3 -2
- package/test/external-front.js +4 -4
- package/test/field-meta.js +363 -0
- package/test/global.js +2 -1
- package/test/http.js +4 -2
- package/test/images.js +87 -88
- package/test/job.js +34 -34
- package/test/locks.js +2 -2
- package/test/login-requirements.js +3 -2
- package/test/middleware-and-route-order.js +2 -2
- package/test/page-type.js +2 -1
- package/test/pages-autocomplete.js +192 -0
- package/test/pages-public-api.js +2 -2
- package/test/pages-rest.js +4 -4
- package/test/pages.js +389 -57
- package/test/parked-pages.js +47 -47
- package/test/permissions.js +76 -0
- package/test/pieces-page-type.js +2 -1
- package/test/pieces-public-api.js +38 -38
- package/test/pieces.js +4 -4
- package/test/published-pages.js +16 -16
- package/test/rich-text-widget.js +164 -0
- package/test/schema-queryBuilders.js +180 -0
- package/test/schemaBuilders.js +4 -4
- package/test/schemas.js +220 -221
- package/test/search.js +2 -2
- package/test/soft-redirects.js +2 -1
- package/test/templates.js +2 -2
- package/test/users.js +2 -1
|
@@ -19,7 +19,9 @@ module.exports = {
|
|
|
19
19
|
relationshipSuggestionIcon: 'text-box-icon',
|
|
20
20
|
relationshipSuggestionFields: [ 'slug' ]
|
|
21
21
|
},
|
|
22
|
-
|
|
22
|
+
// Adding permissions for advanced permissions to allow modules to use it without
|
|
23
|
+
// being forced to check if the module is used with advanced permissions or not.
|
|
24
|
+
cascades: [ 'fields', 'permissions' ],
|
|
23
25
|
fields(self) {
|
|
24
26
|
return {
|
|
25
27
|
add: {
|
|
@@ -117,7 +119,7 @@ module.exports = {
|
|
|
117
119
|
type: 'command-menu-manager-create-new'
|
|
118
120
|
},
|
|
119
121
|
permission: {
|
|
120
|
-
action: '
|
|
122
|
+
action: 'create',
|
|
121
123
|
type: self.__meta.name
|
|
122
124
|
},
|
|
123
125
|
shortcut: 'C'
|
|
@@ -145,7 +147,7 @@ module.exports = {
|
|
|
145
147
|
type: 'command-menu-manager-archive-selected'
|
|
146
148
|
},
|
|
147
149
|
permission: {
|
|
148
|
-
action: '
|
|
150
|
+
action: 'delete',
|
|
149
151
|
type: self.__meta.name
|
|
150
152
|
},
|
|
151
153
|
shortcut: 'E'
|
|
@@ -326,6 +328,15 @@ module.exports = {
|
|
|
326
328
|
}
|
|
327
329
|
}
|
|
328
330
|
},
|
|
331
|
+
afterDelete: {
|
|
332
|
+
async deleteRelatedReverseId(req, doc) {
|
|
333
|
+
// When deleting an unlocalized or draft document,
|
|
334
|
+
// we remove related reverse IDs of documents having a relation to the deleted one
|
|
335
|
+
if (!doc.aposMode || doc.aposMode === 'draft') {
|
|
336
|
+
await self.deleteRelatedReverseId(doc, true);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
329
340
|
afterRescue: {
|
|
330
341
|
async revertDeduplication(req, doc) {
|
|
331
342
|
const $set = await self.getRevertDeduplicationSet(req, doc);
|
|
@@ -369,18 +380,27 @@ module.exports = {
|
|
|
369
380
|
|
|
370
381
|
methods(self) {
|
|
371
382
|
return {
|
|
372
|
-
async
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
383
|
+
async deleteRelatedReverseId(doc, deleting = false) {
|
|
384
|
+
const locales = doc.aposLocale && deleting
|
|
385
|
+
? [
|
|
386
|
+
doc.aposLocale.replace(':draft', ':published'),
|
|
387
|
+
doc.aposLocale.replace(':published', ':draft')
|
|
388
|
+
]
|
|
389
|
+
: [ doc.aposLocale ];
|
|
390
|
+
return self.apos.doc.db.updateMany({
|
|
378
391
|
relatedReverseIds: { $in: [ doc.aposDocId ] },
|
|
379
|
-
aposLocale: { $in: [
|
|
392
|
+
aposLocale: { $in: [ ...locales, null ] }
|
|
380
393
|
}, {
|
|
381
394
|
$pull: { relatedReverseIds: doc.aposDocId },
|
|
382
395
|
$set: { cacheInvalidatedAt: doc.updatedAt }
|
|
383
396
|
});
|
|
397
|
+
},
|
|
398
|
+
async updateCacheField(req, doc) {
|
|
399
|
+
const relatedDocsIds = self.getRelatedDocsIds(req, doc);
|
|
400
|
+
|
|
401
|
+
// - Remove current doc reference from docs that include it
|
|
402
|
+
// - Update these docs' cache field
|
|
403
|
+
await this.deleteRelatedReverseId(doc);
|
|
384
404
|
|
|
385
405
|
if (relatedDocsIds.length) {
|
|
386
406
|
// - Add current doc reference to related docs
|
|
@@ -421,7 +441,8 @@ module.exports = {
|
|
|
421
441
|
label: 'apostrophe:shareDraft',
|
|
422
442
|
modal: 'AposModalShareDraft',
|
|
423
443
|
manuallyPublished: true,
|
|
424
|
-
hasUrl: true
|
|
444
|
+
hasUrl: true,
|
|
445
|
+
conditions: [ 'canShareDraft' ]
|
|
425
446
|
});
|
|
426
447
|
},
|
|
427
448
|
getRelatedDocsIds(req, doc) {
|
|
@@ -1476,11 +1497,52 @@ module.exports = {
|
|
|
1476
1497
|
return field.viewPermission && !self.apos.permission.can(req, field.viewPermission.action, field.viewPermission.type);
|
|
1477
1498
|
});
|
|
1478
1499
|
|
|
1500
|
+
if (doc.aposMeta && !self.apos.permission.can(req, 'edit', doc)) {
|
|
1501
|
+
forbiddenSchemaFields.push({
|
|
1502
|
+
name: 'aposMeta'
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1479
1506
|
forbiddenSchemaFields.forEach(field => {
|
|
1480
1507
|
delete doc[field.name];
|
|
1481
1508
|
});
|
|
1482
1509
|
|
|
1483
1510
|
return doc;
|
|
1511
|
+
},
|
|
1512
|
+
|
|
1513
|
+
composeFilters() {
|
|
1514
|
+
self.filters = Object.keys(self.filters).map((key) => ({
|
|
1515
|
+
name: key,
|
|
1516
|
+
...self.filters[key],
|
|
1517
|
+
inputType: self.filters[key].inputType || 'select'
|
|
1518
|
+
}));
|
|
1519
|
+
// Add a null choice if not already added or set to `required`
|
|
1520
|
+
self.filters.forEach((filter) => {
|
|
1521
|
+
if (filter.choices) {
|
|
1522
|
+
if (
|
|
1523
|
+
!filter.required &&
|
|
1524
|
+
filter.choices &&
|
|
1525
|
+
!filter.choices.find((choice) => choice.value === null)
|
|
1526
|
+
) {
|
|
1527
|
+
filter.def = null;
|
|
1528
|
+
filter.choices.push({
|
|
1529
|
+
value: null,
|
|
1530
|
+
label: 'apostrophe:none'
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
} else {
|
|
1534
|
+
// Dynamic choices from the REST API, but
|
|
1535
|
+
// we need a label for "no opinion"
|
|
1536
|
+
filter.nullLabel = 'Choose One';
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
},
|
|
1540
|
+
|
|
1541
|
+
composeColumns() {
|
|
1542
|
+
self.columns = Object.keys(self.columns).map((key) => ({
|
|
1543
|
+
name: key,
|
|
1544
|
+
...self.columns[key]
|
|
1545
|
+
}));
|
|
1484
1546
|
}
|
|
1485
1547
|
};
|
|
1486
1548
|
},
|
|
@@ -1499,8 +1561,10 @@ module.exports = {
|
|
|
1499
1561
|
label,
|
|
1500
1562
|
pluralLabel,
|
|
1501
1563
|
relatedDocument: self.options.relatedDocument,
|
|
1564
|
+
canCreate: self.apos.permission.can(req, 'create', self.name, 'draft'),
|
|
1502
1565
|
canEdit: self.apos.permission.can(req, 'edit', self.name, 'draft'),
|
|
1503
|
-
canPublish: self.apos.permission.can(req, 'publish', self.name)
|
|
1566
|
+
canPublish: self.apos.permission.can(req, 'publish', self.name),
|
|
1567
|
+
canArchive: self.apos.permission.can(req, 'delete', self.name)
|
|
1504
1568
|
};
|
|
1505
1569
|
browserOptions.canLocalize = browserOptions.canEdit &&
|
|
1506
1570
|
self.options.localized &&
|
|
@@ -1868,8 +1932,10 @@ module.exports = {
|
|
|
1868
1932
|
after(results) {
|
|
1869
1933
|
// In all cases we mark the docs with ._edit and ._publish if
|
|
1870
1934
|
// the req is permitted to do those things
|
|
1935
|
+
self.apos.permission.annotate(query.req, 'create', results);
|
|
1871
1936
|
self.apos.permission.annotate(query.req, 'edit', results);
|
|
1872
1937
|
self.apos.permission.annotate(query.req, 'publish', results);
|
|
1938
|
+
self.apos.permission.annotate(query.req, 'delete', results);
|
|
1873
1939
|
}
|
|
1874
1940
|
},
|
|
1875
1941
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
:current="docFields.data"
|
|
20
20
|
:published="published"
|
|
21
21
|
:show-edit="false"
|
|
22
|
+
:can-delete-draft="moduleOptions.canDeleteDraft"
|
|
22
23
|
@close="close"
|
|
23
24
|
/>
|
|
24
25
|
<AposButton
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
:conditional-fields="conditionalFields"
|
|
68
69
|
:doc-id="docId"
|
|
69
70
|
:value="docFields"
|
|
71
|
+
:meta="docMeta"
|
|
70
72
|
:server-errors="serverErrors"
|
|
71
73
|
:ref="tab.name"
|
|
72
74
|
:generation="generation"
|
|
@@ -92,6 +94,7 @@
|
|
|
92
94
|
:conditional-fields="conditionalFields"
|
|
93
95
|
:doc-id="docId"
|
|
94
96
|
:value="docFields"
|
|
97
|
+
:meta="docMeta"
|
|
95
98
|
@input="updateDocFields"
|
|
96
99
|
@validate="triggerValidate"
|
|
97
100
|
:modifiers="['small', 'inverted']"
|
|
@@ -195,8 +198,13 @@ export default {
|
|
|
195
198
|
|
|
196
199
|
return this.moduleOptions.canPublish;
|
|
197
200
|
},
|
|
201
|
+
canCreate() {
|
|
202
|
+
return this.original &&
|
|
203
|
+
!this.original._id &&
|
|
204
|
+
this.moduleOptions.canCreate;
|
|
205
|
+
},
|
|
198
206
|
saveDisabled() {
|
|
199
|
-
if (!this.canEdit) {
|
|
207
|
+
if (!this.canCreate && !this.canEdit) {
|
|
200
208
|
return true;
|
|
201
209
|
}
|
|
202
210
|
if (this.restoreOnly) {
|
|
@@ -48,6 +48,12 @@ export default {
|
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
|
+
canDeleteDraft: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default() {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
51
57
|
showCopy: {
|
|
52
58
|
type: Boolean,
|
|
53
59
|
default() {
|
|
@@ -105,7 +111,7 @@ export default {
|
|
|
105
111
|
action: 'edit'
|
|
106
112
|
}
|
|
107
113
|
] : []),
|
|
108
|
-
...((this.showPreview && this.
|
|
114
|
+
...((this.showPreview && this.canPreview) ? [
|
|
109
115
|
{
|
|
110
116
|
label: 'apostrophe:preview',
|
|
111
117
|
action: 'preview'
|
|
@@ -245,7 +251,10 @@ export default {
|
|
|
245
251
|
if (!this.context._id) {
|
|
246
252
|
return false;
|
|
247
253
|
}
|
|
248
|
-
if (!this.
|
|
254
|
+
if (!this.context.lastPublishedAt && !this.canDeleteDraft && !this.context._delete) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
if (this.context.lastPublishedAt && (!this.context.modified || !this.context._edit)) {
|
|
249
258
|
return false;
|
|
250
259
|
}
|
|
251
260
|
return (
|
|
@@ -262,12 +271,12 @@ export default {
|
|
|
262
271
|
},
|
|
263
272
|
canArchive() {
|
|
264
273
|
return (
|
|
265
|
-
this.
|
|
274
|
+
this.context._delete &&
|
|
266
275
|
this.context._id &&
|
|
267
276
|
!this.moduleOptions.singleton &&
|
|
268
277
|
!this.context.archived &&
|
|
269
278
|
!this.context.parked &&
|
|
270
|
-
((this.
|
|
279
|
+
(Boolean(this.canPublish && this.context.lastPublishedAt) || !this.manuallyPublished)
|
|
271
280
|
);
|
|
272
281
|
},
|
|
273
282
|
canUnpublish() {
|
|
@@ -280,7 +289,8 @@ export default {
|
|
|
280
289
|
);
|
|
281
290
|
},
|
|
282
291
|
canCopy() {
|
|
283
|
-
return this.
|
|
292
|
+
return this.moduleOptions.canCreate &&
|
|
293
|
+
this.canEdit &&
|
|
284
294
|
this.moduleOptions.canEdit &&
|
|
285
295
|
!this.moduleOptions.singleton &&
|
|
286
296
|
this.context._id;
|
|
@@ -290,9 +300,17 @@ export default {
|
|
|
290
300
|
this.canEdit &&
|
|
291
301
|
this.context._id &&
|
|
292
302
|
this.context.archived &&
|
|
293
|
-
((this.
|
|
303
|
+
((!this.manuallyPublished && this.canPublish) || this.manuallyPublished)
|
|
294
304
|
);
|
|
295
305
|
},
|
|
306
|
+
canPreview() {
|
|
307
|
+
return this.hasUrl &&
|
|
308
|
+
!this.context.archived;
|
|
309
|
+
},
|
|
310
|
+
canShareDraft() {
|
|
311
|
+
return this.canEdit &&
|
|
312
|
+
!this.context.archived;
|
|
313
|
+
},
|
|
296
314
|
manuallyPublished() {
|
|
297
315
|
return this.moduleOptions.localized && !this.autopublish;
|
|
298
316
|
},
|
|
@@ -165,6 +165,7 @@
|
|
|
165
165
|
"errorWhilePublishing": "An Error Occurred While Publishing",
|
|
166
166
|
"errorWhilePublishingDocument": "An error occurred while publishing the document.",
|
|
167
167
|
"errorWhilePublishingParentPage": "An error occurred while publishing a parent page.",
|
|
168
|
+
"errorWhilePublishingParentPageForbidden": "Because you do not have permission to publish the parent page, we are saving and submitting your draft. Please let your manager know that the parent page needs to be published.",
|
|
168
169
|
"errorWhileRestoring": "An error occurred while restoring the previously published version.",
|
|
169
170
|
"errorWhileRestoringArchive": "An error occurred while restoring the document from the archive.",
|
|
170
171
|
"errorWhileRestoringPrevious": "An error occurred while restoring the previously published version.",
|
|
@@ -71,7 +71,6 @@
|
|
|
71
71
|
:accept="accept"
|
|
72
72
|
:items="items"
|
|
73
73
|
:module-options="moduleOptions"
|
|
74
|
-
:can-edit="moduleOptions.canEdit"
|
|
75
74
|
@edit="updateEditing"
|
|
76
75
|
v-model="checked"
|
|
77
76
|
@select="select"
|
|
@@ -106,7 +105,6 @@
|
|
|
106
105
|
/>
|
|
107
106
|
<AposMediaManagerSelections
|
|
108
107
|
:items="selected"
|
|
109
|
-
:can-edit="moduleOptions.canEdit"
|
|
110
108
|
@clear="clearSelected" @edit="updateEditing"
|
|
111
109
|
v-show="!editing"
|
|
112
110
|
/>
|
|
@@ -248,7 +246,8 @@ export default {
|
|
|
248
246
|
async getMedia (options) {
|
|
249
247
|
const qs = {
|
|
250
248
|
...this.filterValues,
|
|
251
|
-
page: this.currentPage
|
|
249
|
+
page: this.currentPage,
|
|
250
|
+
viewContext: this.relationshipField ? 'relationship' : 'manage'
|
|
252
251
|
};
|
|
253
252
|
const filtered = !!Object.keys(this.filterValues).length;
|
|
254
253
|
if (this.moduleOptions && Array.isArray(this.moduleOptions.filters)) {
|
|
@@ -354,9 +353,7 @@ export default {
|
|
|
354
353
|
this.editing = undefined;
|
|
355
354
|
},
|
|
356
355
|
async updateEditing(id) {
|
|
357
|
-
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
356
|
+
const item = this.items.find(item => item._id === id);
|
|
360
357
|
// We only care about the current doc for this prompt,
|
|
361
358
|
// we are not in danger of discarding a selection when
|
|
362
359
|
// we switch images
|
|
@@ -371,7 +368,11 @@ export default {
|
|
|
371
368
|
return false;
|
|
372
369
|
}
|
|
373
370
|
}
|
|
374
|
-
|
|
371
|
+
if (!item?._edit) {
|
|
372
|
+
this.editing = null;
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
this.editing = item;
|
|
375
376
|
return true;
|
|
376
377
|
},
|
|
377
378
|
// select setters
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="apos-media-manager-display">
|
|
3
3
|
<div class="apos-media-manager-display__grid">
|
|
4
4
|
<AposMediaUploader
|
|
5
|
-
v-if="
|
|
5
|
+
v-if="moduleOptions.canCreate"
|
|
6
6
|
:disabled="maxReached"
|
|
7
7
|
:action="moduleOptions.action"
|
|
8
8
|
:accept="accept"
|
|
@@ -83,10 +83,6 @@ export default {
|
|
|
83
83
|
event: 'change'
|
|
84
84
|
},
|
|
85
85
|
props: {
|
|
86
|
-
canEdit: {
|
|
87
|
-
type: Boolean,
|
|
88
|
-
default: false
|
|
89
|
-
},
|
|
90
86
|
maxReached: {
|
|
91
87
|
type: Boolean,
|
|
92
88
|
default: false
|
|
@@ -171,7 +171,7 @@ export default {
|
|
|
171
171
|
action: 'localize'
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
|
-
if (this.activeMedia._id && !this.restoreOnly) {
|
|
174
|
+
if (this.activeMedia._id && this.activeMedia._delete && !this.restoreOnly) {
|
|
175
175
|
menu.push({
|
|
176
176
|
label: 'apostrophe:archiveImage',
|
|
177
177
|
action: 'archive',
|
|
@@ -209,7 +209,9 @@ export default {
|
|
|
209
209
|
return dayjs(this.activeMedia.attachment.createdAt).format(this.$t('apostrophe:dayjsMediaCreatedDateFormat'));
|
|
210
210
|
},
|
|
211
211
|
isArchived() {
|
|
212
|
-
|
|
212
|
+
// ?. necessary to avoid reference to null due to
|
|
213
|
+
// race condition when toggling selection off
|
|
214
|
+
return this.media?.archived;
|
|
213
215
|
}
|
|
214
216
|
},
|
|
215
217
|
watch: {
|
|
@@ -248,6 +250,7 @@ export default {
|
|
|
248
250
|
this[action]();
|
|
249
251
|
},
|
|
250
252
|
async updateActiveDoc(newMedia) {
|
|
253
|
+
newMedia = newMedia || {};
|
|
251
254
|
this.showReplace = false;
|
|
252
255
|
this.activeMedia = klona(newMedia);
|
|
253
256
|
this.restoreOnly = !!this.activeMedia.archived;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
{{ item.title }}
|
|
31
31
|
</div>
|
|
32
32
|
<AposButton
|
|
33
|
-
v-if="
|
|
33
|
+
v-if="item._edit"
|
|
34
34
|
label="apostrophe:edit"
|
|
35
35
|
type="quiet"
|
|
36
36
|
:modifiers="['no-motion']"
|
|
@@ -49,10 +49,6 @@
|
|
|
49
49
|
<script>
|
|
50
50
|
export default {
|
|
51
51
|
props: {
|
|
52
|
-
canEdit: {
|
|
53
|
-
type: Boolean,
|
|
54
|
-
default: false
|
|
55
|
-
},
|
|
56
52
|
items: {
|
|
57
53
|
type: Array,
|
|
58
54
|
required: true
|
|
@@ -141,9 +141,11 @@ export default {
|
|
|
141
141
|
for (const file of files) {
|
|
142
142
|
try {
|
|
143
143
|
const img = await this.insertImage(file, emptyDoc);
|
|
144
|
-
|
|
144
|
+
if (img?._id) {
|
|
145
|
+
imageIds.push(img._id);
|
|
146
|
+
}
|
|
145
147
|
} catch (e) {
|
|
146
|
-
const msg = e.body && e.body.message ? e.body.message : this.$t('
|
|
148
|
+
const msg = e.body && e.body.message ? e.body.message : this.$t('apostrophe:uploadError');
|
|
147
149
|
await apos.notify(msg, {
|
|
148
150
|
type: 'danger',
|
|
149
151
|
icon: 'alert-circle-icon',
|
|
@@ -713,6 +713,18 @@ module.exports = {
|
|
|
713
713
|
};
|
|
714
714
|
},
|
|
715
715
|
|
|
716
|
+
async verifyRequirements(req, requirements) {
|
|
717
|
+
for (const [ name, requirement ] of Object.entries(requirements)) {
|
|
718
|
+
try {
|
|
719
|
+
await requirement.verify(req, req.body.requirements && req.body.requirements[name]);
|
|
720
|
+
} catch (e) {
|
|
721
|
+
e.data = e.data || {};
|
|
722
|
+
e.data.requirement = name;
|
|
723
|
+
throw e;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
|
|
716
728
|
// Implementation detail of the login route. Log in the user, or if there are
|
|
717
729
|
// `requirements` that require password verification occur first, return an incomplete token.
|
|
718
730
|
async initialLogin(req) {
|
|
@@ -734,16 +746,14 @@ module.exports = {
|
|
|
734
746
|
|
|
735
747
|
try {
|
|
736
748
|
// Initial login step
|
|
737
|
-
const {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
}
|
|
749
|
+
const {
|
|
750
|
+
earlyRequirements,
|
|
751
|
+
onTimeRequirements,
|
|
752
|
+
lateRequirements
|
|
753
|
+
} = self.filterRequirements();
|
|
754
|
+
await self.verifyRequirements(req, earlyRequirements);
|
|
755
|
+
await self.verifyRequirements(req, onTimeRequirements);
|
|
756
|
+
|
|
747
757
|
// send log information
|
|
748
758
|
const user = await self.apos.login.verifyLogin(
|
|
749
759
|
username,
|
|
@@ -758,7 +768,6 @@ module.exports = {
|
|
|
758
768
|
}
|
|
759
769
|
|
|
760
770
|
const requirementsToVerify = Object.keys(lateRequirements);
|
|
761
|
-
|
|
762
771
|
if (requirementsToVerify.length) {
|
|
763
772
|
const token = cuid();
|
|
764
773
|
|
|
@@ -813,15 +822,12 @@ module.exports = {
|
|
|
813
822
|
},
|
|
814
823
|
|
|
815
824
|
filterRequirements() {
|
|
825
|
+
const requirements = Object.entries(self.requirements);
|
|
826
|
+
|
|
816
827
|
return {
|
|
817
|
-
earlyRequirements: Object.fromEntries(
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
),
|
|
821
|
-
lateRequirements: Object.fromEntries(
|
|
822
|
-
Object.entries(self.requirements)
|
|
823
|
-
.filter(([ _, requirement ]) => requirement.phase === 'afterPasswordVerified')
|
|
824
|
-
)
|
|
828
|
+
earlyRequirements: Object.fromEntries(requirements.filter(([ , requirement ]) => requirement.phase === 'beforeSubmit')),
|
|
829
|
+
onTimeRequirements: Object.fromEntries(requirements.filter(requirement => requirement.phase === 'uponSubmit')),
|
|
830
|
+
lateRequirements: Object.fromEntries(requirements.filter(([ , requirement ]) => requirement.phase === 'afterPasswordVerified'))
|
|
825
831
|
};
|
|
826
832
|
},
|
|
827
833
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
key="1"
|
|
4
4
|
class="apos-login-form"
|
|
5
|
-
v-if="phase === 'beforeSubmit'"
|
|
5
|
+
v-if="phase === 'beforeSubmit' || phase === 'uponSubmit'"
|
|
6
6
|
>
|
|
7
7
|
<TheAposLoginHeader
|
|
8
8
|
:env="context.env"
|
|
@@ -30,6 +30,16 @@
|
|
|
30
30
|
@done="requirementDone(requirement, $event)"
|
|
31
31
|
@block="requirementBlock(requirement)"
|
|
32
32
|
/>
|
|
33
|
+
<template v-if="phase === 'uponSubmit'">
|
|
34
|
+
<Component
|
|
35
|
+
v-for="requirement in uponSubmitRequirements"
|
|
36
|
+
:key="requirement.name"
|
|
37
|
+
:is="requirement.component"
|
|
38
|
+
v-bind="getRequirementProps(requirement.name)"
|
|
39
|
+
@done="requirementDone(requirement, $event)"
|
|
40
|
+
@block="requirementBlock(requirement)"
|
|
41
|
+
/>
|
|
42
|
+
</template>
|
|
33
43
|
<AposButton
|
|
34
44
|
data-apos-test="loginSubmit"
|
|
35
45
|
:busy="busy"
|
|
@@ -41,6 +41,9 @@ export default {
|
|
|
41
41
|
beforeSubmitRequirements() {
|
|
42
42
|
return this.requirements.filter(requirement => requirement.phase === 'beforeSubmit');
|
|
43
43
|
},
|
|
44
|
+
uponSubmitRequirements() {
|
|
45
|
+
return this.requirements.filter(requirement => requirement.phase === 'uponSubmit');
|
|
46
|
+
},
|
|
44
47
|
// The currently active requirement expecting a solo presentation.
|
|
45
48
|
// Currently it only concerns `afterPasswordVerified` requirements.
|
|
46
49
|
// beforeSubmit requirements are not presented solo.
|
|
@@ -83,6 +86,37 @@ export default {
|
|
|
83
86
|
} else {
|
|
84
87
|
return null;
|
|
85
88
|
}
|
|
89
|
+
},
|
|
90
|
+
uponSubmitRequirements: {
|
|
91
|
+
deep: true,
|
|
92
|
+
async handler(newVal) {
|
|
93
|
+
if (this.phase !== 'uponSubmit') {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const isUponSubmitRequirementsPending = newVal.some(requirement => requirement.done === null);
|
|
98
|
+
if (isUponSubmitRequirementsPending) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const isUponSubmitRequirementsDone = newVal.every(requirement => requirement.done === true) || this.uponSubmitRequirements.length === 0;
|
|
103
|
+
if (isUponSubmitRequirementsDone) {
|
|
104
|
+
await this.postSubmit();
|
|
105
|
+
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const isUponSubmitRequirementsBlocked = newVal.some(requirement => requirement.done === false);
|
|
110
|
+
if (isUponSubmitRequirementsBlocked) {
|
|
111
|
+
for (const requirement of this.uponSubmitRequirements) {
|
|
112
|
+
requirement.done = null;
|
|
113
|
+
requirement.value = null;
|
|
114
|
+
}
|
|
115
|
+
this.phase = 'beforeSubmit';
|
|
116
|
+
this.busy = false;
|
|
117
|
+
this.error = '';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
86
120
|
}
|
|
87
121
|
},
|
|
88
122
|
created() {
|
|
@@ -96,6 +130,15 @@ export default {
|
|
|
96
130
|
this.busy = true;
|
|
97
131
|
this.error = '';
|
|
98
132
|
|
|
133
|
+
this.uponSubmitRequirements.length
|
|
134
|
+
? this.uponSubmit()
|
|
135
|
+
: await this.postSubmit();
|
|
136
|
+
},
|
|
137
|
+
uponSubmit() {
|
|
138
|
+
this.phase = 'uponSubmit';
|
|
139
|
+
// Note: uponSubmitRequirements watcher will handle the next step
|
|
140
|
+
},
|
|
141
|
+
async postSubmit() {
|
|
99
142
|
await this.invokeInitialLoginApi();
|
|
100
143
|
},
|
|
101
144
|
async invokeInitialLoginApi() {
|
|
@@ -169,7 +212,7 @@ export default {
|
|
|
169
212
|
const requirement = this.requirements
|
|
170
213
|
.find(requirement => requirement.name === requirementDone.name);
|
|
171
214
|
|
|
172
|
-
if (requirement.phase === 'beforeSubmit') {
|
|
215
|
+
if (requirement.phase === 'beforeSubmit' || requirement.phase === 'uponSubmit') {
|
|
173
216
|
requirement.done = true;
|
|
174
217
|
requirement.value = value;
|
|
175
218
|
return;
|
|
@@ -226,7 +269,7 @@ function getRequirements() {
|
|
|
226
269
|
name,
|
|
227
270
|
component: requirement.component || name,
|
|
228
271
|
...requirement,
|
|
229
|
-
done:
|
|
272
|
+
done: null,
|
|
230
273
|
value: null,
|
|
231
274
|
success: null,
|
|
232
275
|
error: null
|
|
@@ -234,6 +277,7 @@ function getRequirements() {
|
|
|
234
277
|
});
|
|
235
278
|
return [
|
|
236
279
|
...requirements.filter(r => r.phase === 'beforeSubmit'),
|
|
280
|
+
...requirements.filter(r => r.phase === 'uponSubmit'),
|
|
237
281
|
...requirements.filter(r => r.phase === 'afterPasswordVerified')
|
|
238
282
|
];
|
|
239
283
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
class="apos-share-draft__toggle"
|
|
31
31
|
@toggle="toggle"
|
|
32
32
|
/>
|
|
33
|
-
<p class="apos-share-draft__toggle-label">
|
|
33
|
+
<p @click="toggle" class="apos-share-draft__toggle-label">
|
|
34
34
|
{{ $t('apostrophe:shareDraftEnable') }}
|
|
35
35
|
</p>
|
|
36
36
|
</div>
|
|
@@ -266,6 +266,7 @@ export default {
|
|
|
266
266
|
.apos-share-draft__toggle-wrapper {
|
|
267
267
|
display: flex;
|
|
268
268
|
align-items: center;
|
|
269
|
+
margin-bottom: $spacing-base;
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
.apos-share-draft__toggle {
|
|
@@ -273,14 +274,18 @@ export default {
|
|
|
273
274
|
}
|
|
274
275
|
|
|
275
276
|
.apos-share-draft__toggle-label {
|
|
276
|
-
@include type-
|
|
277
|
+
@include type-large;
|
|
278
|
+
flex-grow: 1;
|
|
277
279
|
max-width: 370px;
|
|
278
280
|
line-height: var(--a-line-tallest);
|
|
279
281
|
margin: 0;
|
|
282
|
+
&:hover {
|
|
283
|
+
cursor: pointer;
|
|
284
|
+
}
|
|
280
285
|
}
|
|
281
286
|
|
|
282
287
|
.apos-share-draft__description {
|
|
283
|
-
@include type-
|
|
288
|
+
@include type-base;
|
|
284
289
|
line-height: var(--a-line-tall);
|
|
285
290
|
max-width: 355px;
|
|
286
291
|
color: var(--a-base-2);
|