apostrophe 3.62.0 → 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 +29 -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 +254 -5
- package/modules/@apostrophecms/doc/ui/apos/mixins/AposFieldMetaUtilsMixin.js +93 -0
- package/modules/@apostrophecms/doc-type/index.js +70 -10
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +2 -0
- package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +12 -3
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- 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 +87 -20
- package/modules/@apostrophecms/page-type/index.js +67 -2
- package/modules/@apostrophecms/piece-type/index.js +1 -34
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +8 -0
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue +16 -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/schema/index.js +9 -0
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +3 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +3 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -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 +74 -29
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +1 -0
- 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/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/mixins/AposPublishMixin.js +10 -4
- 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 +0 -11
- 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/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/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
package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue
CHANGED
|
@@ -161,6 +161,14 @@ export default {
|
|
|
161
161
|
return {};
|
|
162
162
|
}
|
|
163
163
|
},
|
|
164
|
+
// not used, but we need to keep it here to avoid
|
|
165
|
+
// an attribute [object Object]
|
|
166
|
+
meta: {
|
|
167
|
+
type: Object,
|
|
168
|
+
default() {
|
|
169
|
+
return {};
|
|
170
|
+
}
|
|
171
|
+
},
|
|
164
172
|
docId: {
|
|
165
173
|
type: String,
|
|
166
174
|
required: false,
|
|
@@ -42,13 +42,6 @@ export default {
|
|
|
42
42
|
active: false
|
|
43
43
|
};
|
|
44
44
|
},
|
|
45
|
-
watch: {
|
|
46
|
-
hasSelection(newVal, oldVal) {
|
|
47
|
-
if (!newVal) {
|
|
48
|
-
this.close();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
45
|
computed: {
|
|
53
46
|
attributes() {
|
|
54
47
|
return this.editor.getAttributes('image');
|
|
@@ -71,6 +64,13 @@ export default {
|
|
|
71
64
|
return text !== '' || type?.name === 'image';
|
|
72
65
|
}
|
|
73
66
|
},
|
|
67
|
+
watch: {
|
|
68
|
+
hasSelection(newVal, oldVal) {
|
|
69
|
+
if (!newVal) {
|
|
70
|
+
this.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
74
|
methods: {
|
|
75
75
|
click() {
|
|
76
76
|
if (this.hasSelection) {
|
|
@@ -28,6 +28,7 @@ module.exports = {
|
|
|
28
28
|
self.fieldsById = {};
|
|
29
29
|
self.arrayManagers = {};
|
|
30
30
|
self.objectManagers = {};
|
|
31
|
+
self.fieldMetadataComponents = [];
|
|
31
32
|
|
|
32
33
|
self.enableBrowserData();
|
|
33
34
|
|
|
@@ -1127,6 +1128,13 @@ module.exports = {
|
|
|
1127
1128
|
return self.fieldTypes[typeName];
|
|
1128
1129
|
},
|
|
1129
1130
|
|
|
1131
|
+
addFieldMetadataComponent(namespace, component) {
|
|
1132
|
+
self.fieldMetadataComponents.push({
|
|
1133
|
+
name: component,
|
|
1134
|
+
namespace
|
|
1135
|
+
});
|
|
1136
|
+
},
|
|
1137
|
+
|
|
1130
1138
|
// Given a schema and a query, add query builders to the query
|
|
1131
1139
|
// for each of the fields in the schema, based on their field type,
|
|
1132
1140
|
// if supported by the field type. If the field already has a
|
|
@@ -1762,6 +1770,7 @@ module.exports = {
|
|
|
1762
1770
|
}
|
|
1763
1771
|
browserOptions.action = self.action;
|
|
1764
1772
|
browserOptions.components = { fields: fields };
|
|
1773
|
+
browserOptions.fieldMetadataComponents = self.fieldMetadataComponents;
|
|
1765
1774
|
return browserOptions;
|
|
1766
1775
|
}
|
|
1767
1776
|
};
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
:error="effectiveError" :uid="uid"
|
|
5
5
|
:display-options="displayOptions"
|
|
6
6
|
:modifiers="[...modifiers, 'full-width']"
|
|
7
|
+
:items="next.items"
|
|
8
|
+
:meta="areaMeta"
|
|
7
9
|
>
|
|
8
10
|
<template #body>
|
|
9
11
|
<!-- data-apos-schema-area lets all the child areas know that this area is in a schema (which is in a modal)
|
|
@@ -18,6 +20,7 @@
|
|
|
18
20
|
:is="editorComponent"
|
|
19
21
|
:options="field.options"
|
|
20
22
|
:items="next.items"
|
|
23
|
+
:meta="areaMeta"
|
|
21
24
|
:choices="choices"
|
|
22
25
|
:id="next._id"
|
|
23
26
|
:field-id="field._id"
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
:modifiers="[
|
|
9
9
|
...field.style === 'table' ? ['full-width'] : []
|
|
10
10
|
]"
|
|
11
|
+
:meta="arrayMeta"
|
|
11
12
|
>
|
|
12
13
|
<template #additional>
|
|
13
14
|
<AposMinMaxCount
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
>
|
|
62
63
|
<AposSchema
|
|
63
64
|
v-model="item.schemaInput"
|
|
65
|
+
:meta="arrayMeta[item._id]?.aposMeta"
|
|
64
66
|
v-for="(item, index) in items"
|
|
65
67
|
class="apos-input-array-inline-item"
|
|
66
68
|
:class="item.open && !alwaysExpand ? 'apos-input-array-inline-item--active' : null"
|
|
@@ -114,9 +116,9 @@
|
|
|
114
116
|
>
|
|
115
117
|
<AposButton
|
|
116
118
|
label="apostrophe:removeItem"
|
|
117
|
-
icon="
|
|
119
|
+
icon="close-icon"
|
|
118
120
|
type="subtle"
|
|
119
|
-
:modifiers="['inline',
|
|
121
|
+
:modifiers="['inline','no-motion']"
|
|
120
122
|
:icon-only="true"
|
|
121
123
|
@click="remove(item._id)"
|
|
122
124
|
/>
|
|
@@ -171,12 +173,6 @@ export default {
|
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
::v-deep .apos-input-relationship {
|
|
174
|
-
.apos-button__wrapper {
|
|
175
|
-
display: none;
|
|
176
|
-
}
|
|
177
|
-
.apos-input {
|
|
178
|
-
width: auto;
|
|
179
|
-
}
|
|
180
176
|
.apos-slat__main {
|
|
181
177
|
min-width: 130px;
|
|
182
178
|
}
|
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
:error="null"
|
|
7
7
|
:uid="uid"
|
|
8
8
|
:display-options="displayOptions"
|
|
9
|
+
:meta="objectMeta"
|
|
9
10
|
>
|
|
10
11
|
<template #body>
|
|
11
12
|
<div class="apos-input-object">
|
|
12
13
|
<div class="apos-input-wrapper">
|
|
13
14
|
<AposSchema
|
|
14
15
|
v-model="schemaInput"
|
|
16
|
+
:meta="currentDocMeta"
|
|
15
17
|
ref="schema"
|
|
16
18
|
:schema="schema"
|
|
17
19
|
:trigger-validation="triggerValidation"
|
|
@@ -9,37 +9,64 @@
|
|
|
9
9
|
<component :is="wrapEl" :class="classList">
|
|
10
10
|
<div class="apos-field__info">
|
|
11
11
|
<component
|
|
12
|
-
v-if="field.label"
|
|
12
|
+
v-if="field.label"
|
|
13
13
|
class="apos-field__label"
|
|
14
|
-
:
|
|
14
|
+
:class="{'apos-sr-only': field.hideLabel }"
|
|
15
|
+
:is="labelEl"
|
|
16
|
+
:for="uid"
|
|
17
|
+
:data-apos-test-name="field.name"
|
|
18
|
+
:data-apos-test-label="field.label"
|
|
19
|
+
data-apos-test="field-label"
|
|
15
20
|
>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
v-if="(field.help || field.htmlHelp) && displayOptions.helpTooltip"
|
|
28
|
-
class="apos-field__help-tooltip"
|
|
29
|
-
>
|
|
30
|
-
<AposIndicator
|
|
31
|
-
icon="help-circle-icon"
|
|
32
|
-
class="apos-field__help-tooltip__icon"
|
|
33
|
-
:tooltip="field.help || field.htmlHelp"
|
|
34
|
-
:icon-size="11"
|
|
35
|
-
icon-color="var(--a-base-4)"
|
|
21
|
+
<span class="apos-field_label-info">
|
|
22
|
+
{{ $t(label) }}
|
|
23
|
+
<span v-if="field.required" class="apos-field__required">
|
|
24
|
+
*
|
|
25
|
+
</span>
|
|
26
|
+
<AposLabel
|
|
27
|
+
class="apos-field__tag"
|
|
28
|
+
v-if="field.tag"
|
|
29
|
+
:label="field.tag.value || field.tag"
|
|
30
|
+
:modifiers="[ `apos-is-${field.tag.type || 'success'}`, 'apos-is-filled' ]"
|
|
31
|
+
data-apos-test="field-tag"
|
|
36
32
|
/>
|
|
33
|
+
<span
|
|
34
|
+
v-if="(field.help || field.htmlHelp) && displayOptions.helpTooltip"
|
|
35
|
+
data-apos-test="field-help-tooltip"
|
|
36
|
+
class="apos-field__help-tooltip"
|
|
37
|
+
>
|
|
38
|
+
<AposIndicator
|
|
39
|
+
icon="help-circle-icon"
|
|
40
|
+
class="apos-field__help-tooltip__icon"
|
|
41
|
+
:tooltip="field.help || field.htmlHelp"
|
|
42
|
+
:icon-size="11"
|
|
43
|
+
icon-color="var(--a-base-4)"
|
|
44
|
+
/>
|
|
45
|
+
</span>
|
|
46
|
+
<span
|
|
47
|
+
v-if="displayOptions.changed" class="apos-field__changed"
|
|
48
|
+
data-apos-test="field-changed"
|
|
49
|
+
>
|
|
50
|
+
<AposLabel
|
|
51
|
+
label="apostrophe:changed" class="apos-field__changed__label"
|
|
52
|
+
:modifiers="[ 'apos-is-warning', 'apos-is-filled' ]"
|
|
53
|
+
tooltip="apostrophe:fieldHasUnpublishedChanges"
|
|
54
|
+
/>
|
|
55
|
+
</span>
|
|
37
56
|
</span>
|
|
38
|
-
<span
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
:
|
|
42
|
-
|
|
57
|
+
<span data-apos-test="field-meta-wrapper" class="apos-field__label-meta">
|
|
58
|
+
<component
|
|
59
|
+
v-for="component in metaComponents"
|
|
60
|
+
:key="component.name"
|
|
61
|
+
:is="component.name"
|
|
62
|
+
:field="field"
|
|
63
|
+
:items="items"
|
|
64
|
+
:namespace="component.namespace"
|
|
65
|
+
:meta="component.data"
|
|
66
|
+
:meta-raw="meta"
|
|
67
|
+
:data-apos-test-component="component.name"
|
|
68
|
+
:data-apos-test-namespace="component.namespace"
|
|
69
|
+
data-apos-test="field-meta"
|
|
43
70
|
/>
|
|
44
71
|
</span>
|
|
45
72
|
</component>
|
|
@@ -53,7 +80,10 @@
|
|
|
53
80
|
<slot name="additional" />
|
|
54
81
|
</div>
|
|
55
82
|
<slot name="body" />
|
|
56
|
-
<div
|
|
83
|
+
<div
|
|
84
|
+
v-if="errorMessage" class="apos-field__error"
|
|
85
|
+
data-apos-test="field-error"
|
|
86
|
+
>
|
|
57
87
|
{{ $t(errorMessage) }}
|
|
58
88
|
</div>
|
|
59
89
|
</component>
|
|
@@ -93,10 +123,25 @@ export default {
|
|
|
93
123
|
|
|
94
124
|
.apos-field__label {
|
|
95
125
|
@include type-label;
|
|
96
|
-
display:
|
|
126
|
+
display: flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
justify-content: space-between;
|
|
129
|
+
flex-wrap: nowrap;
|
|
130
|
+
gap: $spacing-double;
|
|
97
131
|
margin: 0 0 $spacing-base;
|
|
98
132
|
padding: 0;
|
|
99
133
|
color: var(--a-text-primary);
|
|
134
|
+
|
|
135
|
+
&-info {
|
|
136
|
+
display: block;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&-meta {
|
|
140
|
+
display: inline-flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
gap: $spacing-half;
|
|
143
|
+
justify-content: flex-end;
|
|
144
|
+
}
|
|
100
145
|
}
|
|
101
146
|
|
|
102
147
|
.apos-field__help {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
:following-values="followingValues[field.name]"
|
|
43
43
|
:condition-met="conditionalFields?.if[field.name]"
|
|
44
44
|
:field="fields[field.name].field"
|
|
45
|
+
:meta="meta"
|
|
45
46
|
:modifiers="fields[field.name].modifiers"
|
|
46
47
|
:display-options="getDisplayOptions(field.name)"
|
|
47
48
|
:trigger-validation="triggerValidation"
|
|
@@ -16,6 +16,10 @@ export default {
|
|
|
16
16
|
required: true,
|
|
17
17
|
type: Array
|
|
18
18
|
},
|
|
19
|
+
meta: {
|
|
20
|
+
type: Object,
|
|
21
|
+
default: () => ({})
|
|
22
|
+
},
|
|
19
23
|
field: {
|
|
20
24
|
required: true,
|
|
21
25
|
type: Object
|
|
@@ -133,6 +137,9 @@ export default {
|
|
|
133
137
|
}
|
|
134
138
|
});
|
|
135
139
|
return serverErrors;
|
|
140
|
+
},
|
|
141
|
+
currentDocMeta() {
|
|
142
|
+
return this.meta[this.currentId]?.aposMeta || {};
|
|
136
143
|
}
|
|
137
144
|
},
|
|
138
145
|
async mounted() {
|
|
@@ -43,10 +43,22 @@ export default {
|
|
|
43
43
|
for (const [ name, options ] of Object.entries(widgets)) {
|
|
44
44
|
result.push({
|
|
45
45
|
name,
|
|
46
|
-
label: options.addLabel || apos.modules[`${name}-widget`].label
|
|
46
|
+
label: options.addLabel || apos.modules[`${name}-widget`].label,
|
|
47
|
+
icon: apos.modules[`${name}-widget`].icon
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
50
|
return result;
|
|
51
|
+
},
|
|
52
|
+
areaMeta() {
|
|
53
|
+
const meta = this.convertMetaToItems(this.next.items);
|
|
54
|
+
// Get meta for the area itself
|
|
55
|
+
if (this.meta?.[`@${this.next._id}`]) {
|
|
56
|
+
return {
|
|
57
|
+
...meta,
|
|
58
|
+
...this.meta[`@${this.next._id}`]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return meta;
|
|
50
62
|
}
|
|
51
63
|
},
|
|
52
64
|
methods: {
|
|
@@ -78,6 +78,9 @@ export default {
|
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
80
|
return error;
|
|
81
|
+
},
|
|
82
|
+
arrayMeta() {
|
|
83
|
+
return this.convertMetaToItems(this.items);
|
|
81
84
|
}
|
|
82
85
|
},
|
|
83
86
|
watch: {
|
|
@@ -187,7 +190,8 @@ export default {
|
|
|
187
190
|
items: this.next,
|
|
188
191
|
serverError: this.serverError,
|
|
189
192
|
docId: this.docId,
|
|
190
|
-
parentFollowingValues: this.followingValues
|
|
193
|
+
parentFollowingValues: this.followingValues,
|
|
194
|
+
meta: this.arrayMeta
|
|
191
195
|
});
|
|
192
196
|
if (result) {
|
|
193
197
|
this.next = result;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
|
|
2
|
+
import { klona } from 'klona';
|
|
2
3
|
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin.js';
|
|
3
4
|
import AposInputFollowingMixin from 'Modules/@apostrophecms/schema/mixins/AposInputFollowingMixin.js';
|
|
4
5
|
import AposInputConditionalFieldsMixin from 'Modules/@apostrophecms/schema/mixins/AposInputConditionalFieldsMixin.js';
|
|
@@ -46,6 +47,26 @@ export default {
|
|
|
46
47
|
},
|
|
47
48
|
values() {
|
|
48
49
|
return this.schemaInput.data || {};
|
|
50
|
+
},
|
|
51
|
+
objectMeta() {
|
|
52
|
+
const meta = klona(this.fieldMeta);
|
|
53
|
+
const shared = {};
|
|
54
|
+
|
|
55
|
+
for (const fieldName of Object.keys(this.meta)) {
|
|
56
|
+
if (fieldName.startsWith('@')) {
|
|
57
|
+
shared[fieldName] = this.meta[fieldName];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
meta.aposMeta = {
|
|
62
|
+
...(this.fieldMeta.aposMeta || {}),
|
|
63
|
+
...shared
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return meta;
|
|
67
|
+
},
|
|
68
|
+
currentDocMeta() {
|
|
69
|
+
return this.objectMeta.aposMeta || {};
|
|
49
70
|
}
|
|
50
71
|
},
|
|
51
72
|
watch: {
|
|
@@ -14,6 +14,12 @@ export default {
|
|
|
14
14
|
type: Object,
|
|
15
15
|
required: true
|
|
16
16
|
},
|
|
17
|
+
meta: {
|
|
18
|
+
type: Object,
|
|
19
|
+
default() {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
},
|
|
17
23
|
error: {
|
|
18
24
|
type: [ String, Boolean, Object ],
|
|
19
25
|
default: null
|
|
@@ -107,6 +113,35 @@ export default {
|
|
|
107
113
|
} else {
|
|
108
114
|
return false;
|
|
109
115
|
}
|
|
116
|
+
},
|
|
117
|
+
// Meta components receive the original data (key `_original`) and
|
|
118
|
+
// the "pure" keys (no namespace prefix) and values from their namespace.
|
|
119
|
+
// The `_original` key is useful for analyzing e.g. `area`, `array`, etc fields
|
|
120
|
+
// inside the metadata components.
|
|
121
|
+
// All registered metadata components will be rendered. It's the external
|
|
122
|
+
// component responsibility to not render itself when no matching conditions
|
|
123
|
+
// from its namespace are met.
|
|
124
|
+
metaComponents() {
|
|
125
|
+
const meta = {};
|
|
126
|
+
for (const metaKey of Object.keys(this.meta)) {
|
|
127
|
+
const [ ns, key ] = metaKey.split(':', 2);
|
|
128
|
+
if (!key) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!meta[ns]) {
|
|
132
|
+
meta[ns] = {};
|
|
133
|
+
}
|
|
134
|
+
meta[ns][key] = this.meta[metaKey];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return apos.schema.fieldMetadataComponents
|
|
138
|
+
.map(component => {
|
|
139
|
+
return {
|
|
140
|
+
name: component.name,
|
|
141
|
+
namespace: component.namespace,
|
|
142
|
+
data: meta[component.namespace] || {}
|
|
143
|
+
};
|
|
144
|
+
});
|
|
110
145
|
}
|
|
111
146
|
},
|
|
112
147
|
mounted: function () {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { klona } from 'klona';
|
|
2
|
+
|
|
1
3
|
export default {
|
|
2
4
|
// Implements v-model pattern
|
|
3
5
|
emits: [ 'input' ],
|
|
@@ -12,6 +14,12 @@ export default {
|
|
|
12
14
|
type: Object,
|
|
13
15
|
required: true
|
|
14
16
|
},
|
|
17
|
+
meta: {
|
|
18
|
+
type: Object,
|
|
19
|
+
default() {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
},
|
|
15
23
|
modifiers: {
|
|
16
24
|
default: function () {
|
|
17
25
|
return [];
|
|
@@ -88,6 +96,9 @@ export default {
|
|
|
88
96
|
},
|
|
89
97
|
effectiveError () {
|
|
90
98
|
return this.error || this.serverError;
|
|
99
|
+
},
|
|
100
|
+
fieldMeta() {
|
|
101
|
+
return this.meta?.[this.field.name] || {};
|
|
91
102
|
}
|
|
92
103
|
},
|
|
93
104
|
watch: {
|
|
@@ -152,6 +163,36 @@ export default {
|
|
|
152
163
|
// experience.
|
|
153
164
|
convert() {
|
|
154
165
|
return this.next;
|
|
166
|
+
},
|
|
167
|
+
// Accepts an array of object values and convertts the current meta to items
|
|
168
|
+
// so that it contains only the item _id's as keys (without leading `@`) and
|
|
169
|
+
// meta keys for the current field if any.
|
|
170
|
+
// This util is meant to be used in array and widget wrappers or
|
|
171
|
+
// custom fields that manage array of object values having unique `_id`.
|
|
172
|
+
convertMetaToItems(valueItems = []) {
|
|
173
|
+
const fieldMeta = klona(this.fieldMeta);
|
|
174
|
+
const meta = this.meta || {};
|
|
175
|
+
|
|
176
|
+
const shared = {};
|
|
177
|
+
for (const fieldName of Object.keys(meta)) {
|
|
178
|
+
if (fieldName.startsWith('@') && !valueItems.some(item => meta[`@${item._id}`])) {
|
|
179
|
+
shared[fieldName] = meta[fieldName];
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (const item of valueItems) {
|
|
184
|
+
const itemMeta = meta[`@${item._id}`] || {};
|
|
185
|
+
const subMeta = itemMeta.aposMeta;
|
|
186
|
+
fieldMeta[item._id] = {
|
|
187
|
+
...itemMeta,
|
|
188
|
+
aposMeta: {
|
|
189
|
+
...(subMeta || {}),
|
|
190
|
+
...shared
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return fieldMeta;
|
|
155
196
|
}
|
|
156
197
|
}
|
|
157
198
|
};
|
|
@@ -65,9 +65,10 @@ export default {
|
|
|
65
65
|
// Retry now that ancestors are published
|
|
66
66
|
return this.publish(doc);
|
|
67
67
|
} catch (e) {
|
|
68
|
+
const errorMessage = e.name === 'forbidden' ? 'apostrophe:errorWhilePublishingParentPageForbidden' : e.message;
|
|
68
69
|
await apos.alert({
|
|
69
70
|
heading: this.$t('apostrophe:errorWhilePublishing'),
|
|
70
|
-
description:
|
|
71
|
+
description: errorMessage || this.$t('apostrophe:errorWhilePublishingParentPage'),
|
|
71
72
|
localize: false
|
|
72
73
|
});
|
|
73
74
|
}
|
|
@@ -118,13 +119,18 @@ export default {
|
|
|
118
119
|
body: {},
|
|
119
120
|
draft: true
|
|
120
121
|
});
|
|
122
|
+
const newDoc = {
|
|
123
|
+
...doc,
|
|
124
|
+
submitted
|
|
125
|
+
};
|
|
121
126
|
apos.notify('apostrophe:submittedForReview', {
|
|
122
127
|
type: 'success',
|
|
123
128
|
icon: 'list-status-icon',
|
|
124
129
|
dismiss: true
|
|
125
130
|
});
|
|
131
|
+
|
|
126
132
|
apos.bus.$emit('content-changed', {
|
|
127
|
-
doc:
|
|
133
|
+
doc: newDoc,
|
|
128
134
|
action: 'submit'
|
|
129
135
|
});
|
|
130
136
|
return submitted;
|
|
@@ -151,12 +157,12 @@ export default {
|
|
|
151
157
|
dismiss: true,
|
|
152
158
|
icon: 'close-circle-icon'
|
|
153
159
|
});
|
|
154
|
-
|
|
160
|
+
const newDoc = {
|
|
155
161
|
...doc,
|
|
156
162
|
submitted: null
|
|
157
163
|
};
|
|
158
164
|
apos.bus.$emit('content-changed', {
|
|
159
|
-
doc,
|
|
165
|
+
doc: newDoc,
|
|
160
166
|
action: 'dismiss-submission'
|
|
161
167
|
});
|
|
162
168
|
} catch (e) {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
:trigger-validation="triggerValidation"
|
|
19
19
|
:schema="schema"
|
|
20
20
|
:value="docFields"
|
|
21
|
+
:meta="meta"
|
|
21
22
|
@input="updateDocFields"
|
|
22
23
|
@validate="triggerValidate"
|
|
23
24
|
:following-values="followingValues()"
|
|
@@ -74,6 +75,12 @@ export default {
|
|
|
74
75
|
return {};
|
|
75
76
|
}
|
|
76
77
|
},
|
|
78
|
+
meta: {
|
|
79
|
+
type: Object,
|
|
80
|
+
default() {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
},
|
|
77
84
|
focused: {
|
|
78
85
|
type: Boolean,
|
|
79
86
|
default: false
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.63.0",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"pretest": "npm run lint",
|
|
8
|
-
"test": "nyc --reporter=html mocha -t 10000",
|
|
8
|
+
"test": "nyc --reporter=html mocha -t 10000 --ignore=test/assets.js && nyc --reporter=html mocha -t 10000 test/assets.js",
|
|
9
9
|
"eslint": "eslint --ext .js,.vue .",
|
|
10
10
|
"eslint-fix": "npm run eslint -- --fix",
|
|
11
11
|
"i18n": "node scripts/lint-i18n",
|