apostrophe 3.52.0 → 3.53.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 +60 -2
- package/defaults.js +1 -0
- package/index.js +3 -2
- package/lib/check-if-conditions.js +44 -0
- package/lib/moog-require.js +23 -1
- package/modules/@apostrophecms/admin-bar/index.js +30 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +4 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +14 -8
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +8 -2
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +4 -0
- package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +1 -0
- package/modules/@apostrophecms/doc/index.js +13 -7
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +36 -22
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +35 -27
- package/modules/@apostrophecms/i18n/i18n/en.json +8 -0
- package/modules/@apostrophecms/i18n/index.js +49 -2
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +16 -1
- package/modules/@apostrophecms/login/index.js +5 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +2 -0
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +37 -40
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +1 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +3 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +4 -5
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposFocusMixin.js +91 -0
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposModalTabsMixin.js +16 -4
- package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +9 -3
- package/modules/@apostrophecms/piece-type/index.js +1 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +2 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +1 -1
- package/modules/@apostrophecms/schema/index.js +13 -0
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +3 -10
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +0 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +1 -15
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +20 -13
- package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +164 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +141 -0
- package/modules/@apostrophecms/settings/index.js +627 -0
- package/modules/@apostrophecms/settings/ui/apos/apps/TheAposSettings.js +8 -0
- package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +162 -0
- package/modules/@apostrophecms/settings/ui/apos/logic/AposSettingsManager.js +169 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +10 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +23 -6
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellLabels.vue +1 -7
- package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +136 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +6 -6
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposCellMixin.js +9 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_admin.scss +9 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +5 -1
- package/modules/@apostrophecms/user/index.js +30 -3
- package/package.json +1 -1
- package/test/i18n.js +168 -0
- package/test/settings.js +544 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.53.0 (2023-08-03)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Accessibility improved for navigation inside modals and various UI elements.
|
|
8
|
+
Pages/Docs Manager and Doc Editor modal now have better keyboard accessibility.
|
|
9
|
+
They keep the focus on elements inside modals and give it back to their parent modal when closed.
|
|
10
|
+
This implementation is evolving and will likely switch to use the `dialog` HTML element soon.
|
|
11
|
+
* Adds support for a new `if` property in `addContextOperation` in order to show or not a context operation based on the current document properties.
|
|
12
|
+
* Add `update-doc-fields` event to call `AposDocEditor.updateDocFields` method
|
|
13
|
+
* Add schema field `hidden` property to always hide a field
|
|
14
|
+
* Hide empty schema tabs in `AposDocEditor` when all fields are hidden due to `if` conditions
|
|
15
|
+
* The front end UI now respects the `_aposEditorModal` and `_aposAutopublish`
|
|
16
|
+
properties of a document if present, and otherwise falls back to module
|
|
17
|
+
configuration. This is a powerful addition to custom editor components
|
|
18
|
+
for piece and page types, allowing "virtual piece types" on the back end that
|
|
19
|
+
deal with many content types to give better hints to the UI.
|
|
20
|
+
* Respect the `_aposAutopublish` property of a document if present, otherwise
|
|
21
|
+
fall back to module configuration.
|
|
22
|
+
* For convenience in custom editor components, pass the new prop `type`, the original type of the document being copied or edited.
|
|
23
|
+
* For better results in custom editor components, pass the prop `copyOfId`, which implies
|
|
24
|
+
the custom editor should fetch the original itself by its means of choice.
|
|
25
|
+
For backwards compatibility `copyOf` is still passed, but it may be an
|
|
26
|
+
incomplete projection and should not be used in new code.
|
|
27
|
+
* Custom context operations now receive a `docId` prop, which should
|
|
28
|
+
be used in preference to `doc` because `doc` may be an incomplete
|
|
29
|
+
projection.
|
|
30
|
+
* Those creating custom context operations for documents can now
|
|
31
|
+
specify both a `props` object for additional properties to be passed to
|
|
32
|
+
their modal and a `docProps` object to map properties from the document
|
|
33
|
+
to props of their choosing.
|
|
34
|
+
* Adds support to add context labels in admin bar.
|
|
35
|
+
* Adds support for admin UI language configuration in the `@apostrophecms/i18n` module. The new options allow control over the default admin UI language and configures the list of languages, that any individual logged in user can choose from. See the [documentation](https://v3.docs.apostrophecms.org/reference/modules/i18n.html) for more details.
|
|
36
|
+
* Adds `adminLocale` User field to allow users to set their preferred admin UI language, but only when the `@apostrophecms/i18n` is configured accordingly (see above).
|
|
37
|
+
* Adds `@apostrophecms/settings` module and a "Personal Settings" feature. See the [documentation](https://v3.docs.apostrophecms.org/reference/modules/settings.html) for more details.
|
|
38
|
+
* Adds `$and` operator on `addContextOperation` `if` property in order to check multiple fields before showing or hiding a context operation.
|
|
39
|
+
|
|
40
|
+
### Fixes
|
|
41
|
+
|
|
42
|
+
* `AposDocEditor` `onSave` method signature. We now always expect an object when a parameter is passed to the function to check
|
|
43
|
+
the value of `navigate` flag.
|
|
44
|
+
* Fixes a problem in the rich text editor where the slash would not be deleted after item selectin from the insert menu.
|
|
45
|
+
* Modules that have a `public` or `i18n` subdirectory no longer generate a
|
|
46
|
+
warning if they export no code.
|
|
47
|
+
* Clean up focus parent event handlers when components are destroyed. Prevents a slow degradation of performance while editing.
|
|
48
|
+
Thanks to [Joshua N. Miller](https://github.com/jmiller-rise8).
|
|
49
|
+
* Fixes a visual discrepancy in the rich text editor where empty paragraphs would appear smaller in preview mode compared to edit mode.
|
|
50
|
+
|
|
51
|
+
### Changes
|
|
52
|
+
|
|
53
|
+
* To make life easier for module developers, modules that are `npm link`ed to
|
|
54
|
+
the project no longer have to be listed in `package.json` as
|
|
55
|
+
dependencies. To prevent surprises this is still a requirement for modules
|
|
56
|
+
that are not symlinked.
|
|
57
|
+
|
|
3
58
|
## 3.52.0 (2023-07-06)
|
|
4
59
|
|
|
5
60
|
### Changes
|
|
61
|
+
|
|
6
62
|
* Foreign widget UI no longer uses inverted theme styles.
|
|
7
63
|
|
|
8
64
|
### Adds
|
|
65
|
+
|
|
9
66
|
* Allows users to double-click a nested widget's breadcrumb entry and open its editor.
|
|
10
67
|
* Adds support for a new `conditions` property in `addContextOperation` and validation of `addContextOperation` configuration.
|
|
11
68
|
|
|
@@ -17,6 +74,7 @@ by an `if` condition fails to satisfy a condition such as `min` or `max`
|
|
|
17
74
|
or is otherwise invalid. Instead the invalid value is discarded for safety.
|
|
18
75
|
Note that `required` has always been ignored when an `if` condition is not
|
|
19
76
|
satisfied.
|
|
77
|
+
* Errors thrown in `@apostrophecms/login:afterSessionLogin` event handlers are now properly passed back to Passport as such, avoiding a process restart.
|
|
20
78
|
|
|
21
79
|
## 3.51.1 (2023-06-23)
|
|
22
80
|
|
|
@@ -89,8 +147,8 @@ are made before the last `apostrophe:modulesRegistered` handler has fired.
|
|
|
89
147
|
If you need to call Apostrophe's `find()` methods at startup,
|
|
90
148
|
it is best to wait for the `@apostrophecms/doc:beforeReplicate` event.
|
|
91
149
|
* Allow `@` when a piece is a template and `/@` for page templates (doc-template-library module).
|
|
92
|
-
* Adds a `prefix` option to the http frontend util module.
|
|
93
|
-
If explicitly set to `false`, prevents the prefix from being automatically added to the URL,
|
|
150
|
+
* Adds a `prefix` option to the http frontend util module.
|
|
151
|
+
If explicitly set to `false`, prevents the prefix from being automatically added to the URL,
|
|
94
152
|
when making calls with already-prefixed URLs for instance.
|
|
95
153
|
* Adds the `redirectToFirstLocale` option to the `i18n` module to prevent users from reaching a version of their site that would not match any locale when requesting the site without a locale prefix in the URL.
|
|
96
154
|
* If just one instance of a piece type should always exist (per locale if localized), the
|
package/defaults.js
CHANGED
package/index.js
CHANGED
|
@@ -728,8 +728,9 @@ async function apostrophe(options, telemetry, rootSpan) {
|
|
|
728
728
|
if (code) {
|
|
729
729
|
return true;
|
|
730
730
|
}
|
|
731
|
-
|
|
732
|
-
|
|
731
|
+
const subdirs = [ 'ui/apos', 'ui/src', 'ui/public', 'public', 'i18n' ];
|
|
732
|
+
if (d.__meta.dirname && subdirs.find(dir => fs.existsSync(`${d.__meta.dirname}/${dir}`))) {
|
|
733
|
+
// Assets that will be bundled, or localizations, instead of server code
|
|
733
734
|
return true;
|
|
734
735
|
}
|
|
735
736
|
return false;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
export default function checkIfConditions(doc, conditions) {
|
|
3
|
+
return Object.entries(conditions).every(([ key, value ]) => {
|
|
4
|
+
if (key === '$or') {
|
|
5
|
+
return checkOrConditions(doc, value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (key === '$and') {
|
|
9
|
+
return checkAndConditions(doc, value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const isNotEqualCondition = typeof value === 'object' &&
|
|
13
|
+
!Array.isArray(value) &&
|
|
14
|
+
value !== null &&
|
|
15
|
+
Object.hasOwn(value, '$ne');
|
|
16
|
+
|
|
17
|
+
if (isNotEqualCondition) {
|
|
18
|
+
return getNestedPropValue(doc, key) !== value.$ne;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return getNestedPropValue(doc, key) === value;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function checkOrConditions(doc, conditions) {
|
|
26
|
+
return conditions.some((condition) => {
|
|
27
|
+
return checkIfConditions(doc, condition);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function checkAndConditions(doc, conditions) {
|
|
32
|
+
return conditions.every((condition) => {
|
|
33
|
+
return checkIfConditions(doc, condition);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getNestedPropValue(doc, key) {
|
|
38
|
+
if (key.includes('.')) {
|
|
39
|
+
const keys = key.split('.');
|
|
40
|
+
return keys.reduce((acc, cur) => acc[cur], doc);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return doc[key];
|
|
44
|
+
}
|
package/lib/moog-require.js
CHANGED
|
@@ -198,8 +198,9 @@ module.exports = function(options) {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
self.npmRoot = folder;
|
|
201
202
|
}
|
|
202
|
-
if (!self.validPackages.has(type)) {
|
|
203
|
+
if (!self.validPackages.has(type) && !symlinked(type)) {
|
|
203
204
|
return null;
|
|
204
205
|
}
|
|
205
206
|
try {
|
|
@@ -211,6 +212,27 @@ module.exports = function(options) {
|
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
function symlinked(type) {
|
|
216
|
+
self.symlinksCache ||= new Map();
|
|
217
|
+
if (self.symlinksCache.has(type)) {
|
|
218
|
+
return self.symlinksCache.get(type);
|
|
219
|
+
}
|
|
220
|
+
const symlink = `${self.npmRoot}/node_modules/${type}`;
|
|
221
|
+
let link;
|
|
222
|
+
try {
|
|
223
|
+
link = fs.lstatSync(symlink).isSymbolicLink();
|
|
224
|
+
} catch (e) {
|
|
225
|
+
if (e.code === 'ENOENT') {
|
|
226
|
+
// Just doesn't exist
|
|
227
|
+
link = false;
|
|
228
|
+
} else {
|
|
229
|
+
throw e;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
self.symlinksCache.set(type, link);
|
|
233
|
+
return link;
|
|
234
|
+
};
|
|
235
|
+
|
|
214
236
|
self.isImprovement = function(name) {
|
|
215
237
|
return _.has(self.improvements, name);
|
|
216
238
|
};
|
|
@@ -92,6 +92,7 @@ module.exports = {
|
|
|
92
92
|
self.groups = [];
|
|
93
93
|
self.groupLabels = {};
|
|
94
94
|
self.bars = [];
|
|
95
|
+
self.contextLabels = [];
|
|
95
96
|
self.enableBrowserData();
|
|
96
97
|
},
|
|
97
98
|
handlers(self) {
|
|
@@ -355,7 +356,8 @@ module.exports = {
|
|
|
355
356
|
tabId: cuid(),
|
|
356
357
|
contextEditorName,
|
|
357
358
|
pageTree: self.options.pageTree && self.apos.permission.can(req, 'edit', '@apostrophecms/any-page-type', 'draft'),
|
|
358
|
-
bars: self.bars
|
|
359
|
+
bars: self.bars,
|
|
360
|
+
contextLabels: self.contextLabels
|
|
359
361
|
};
|
|
360
362
|
},
|
|
361
363
|
|
|
@@ -383,6 +385,33 @@ module.exports = {
|
|
|
383
385
|
}
|
|
384
386
|
return b.last === true ? -1 : 1;
|
|
385
387
|
});
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
// Add custom context labels and place the ones
|
|
391
|
+
// that have `last: true` at the end
|
|
392
|
+
// of the list so that they will be
|
|
393
|
+
// displayed after the others.
|
|
394
|
+
//
|
|
395
|
+
// Example:
|
|
396
|
+
//
|
|
397
|
+
// ```js
|
|
398
|
+
// self.addContextLabel({
|
|
399
|
+
// id: 'myLabel'
|
|
400
|
+
// label: 'apos:myLabel',
|
|
401
|
+
// tooltip: 'apos:myTooltip',
|
|
402
|
+
// last: true,
|
|
403
|
+
// modifiers: ['apos-is-warning', 'apos-is-filled']
|
|
404
|
+
// });
|
|
405
|
+
// ```
|
|
406
|
+
addContextLabel(label) {
|
|
407
|
+
self.contextLabels.push(label);
|
|
408
|
+
|
|
409
|
+
self.contextLabels.sort((a, b) => {
|
|
410
|
+
if (a.last === true && b.last === true) {
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
return b.last === true ? -1 : 1;
|
|
414
|
+
});
|
|
386
415
|
}
|
|
387
416
|
};
|
|
388
417
|
}
|
|
@@ -129,6 +129,9 @@ export default {
|
|
|
129
129
|
},
|
|
130
130
|
canRedo() {
|
|
131
131
|
return this.undone.length > 0;
|
|
132
|
+
},
|
|
133
|
+
autopublish() {
|
|
134
|
+
return this.context.autopublish ?? this.moduleOptions.autopublish;
|
|
132
135
|
}
|
|
133
136
|
},
|
|
134
137
|
watch: {
|
|
@@ -715,7 +718,7 @@ export default {
|
|
|
715
718
|
},
|
|
716
719
|
async getPublished() {
|
|
717
720
|
const moduleOptions = window.apos.modules[this.context.type];
|
|
718
|
-
const manuallyPublished = moduleOptions.localized && !
|
|
721
|
+
const manuallyPublished = moduleOptions.localized && !this.autopublish;
|
|
719
722
|
if (manuallyPublished && this.context.lastPublishedAt) {
|
|
720
723
|
const action = window.apos.modules[this.context.type].action;
|
|
721
724
|
const doc = await apos.http.get(`${action}/${this.context._id}`, {
|
|
@@ -33,10 +33,18 @@
|
|
|
33
33
|
/>
|
|
34
34
|
<AposLabel
|
|
35
35
|
v-else
|
|
36
|
-
:label="
|
|
37
|
-
:tooltip="
|
|
36
|
+
:label="'apostrophe:draft'"
|
|
37
|
+
:tooltip="'apostrophe:notYetPublished'"
|
|
38
38
|
:modifiers="['apos-is-warning', 'apos-is-filled']"
|
|
39
39
|
/>
|
|
40
|
+
<AposLabel
|
|
41
|
+
class="apos-admin-bar__title-context-label"
|
|
42
|
+
v-for="{id, label, tooltip = '', modifiers = []} in moduleOptions.contextLabels"
|
|
43
|
+
:key="id"
|
|
44
|
+
:label="label"
|
|
45
|
+
:tooltip="tooltip"
|
|
46
|
+
:modifiers="modifiers"
|
|
47
|
+
/>
|
|
40
48
|
</span>
|
|
41
49
|
</transition-group>
|
|
42
50
|
</template>
|
|
@@ -109,12 +117,6 @@ export default {
|
|
|
109
117
|
},
|
|
110
118
|
moduleOptions() {
|
|
111
119
|
return window.apos.adminBar;
|
|
112
|
-
},
|
|
113
|
-
unpublishedLabel() {
|
|
114
|
-
return this.moduleOptions.unpublishedLabel || 'apostrophe:draft';
|
|
115
|
-
},
|
|
116
|
-
unpublishedTooltip() {
|
|
117
|
-
return this.moduleOptions.unpublishedTooltip || 'apostrophe:notYetPublished';
|
|
118
120
|
}
|
|
119
121
|
},
|
|
120
122
|
mounted() {
|
|
@@ -167,6 +169,10 @@ export default {
|
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
.apos-admin-bar__title-context-label {
|
|
173
|
+
margin-left: 5px;
|
|
174
|
+
}
|
|
175
|
+
|
|
170
176
|
.apos-admin-bar__title__indicator {
|
|
171
177
|
margin-right: 5px;
|
|
172
178
|
color: var(--a-text-primary);
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
}`
|
|
25
25
|
]"
|
|
26
26
|
>
|
|
27
|
-
<
|
|
27
|
+
<button
|
|
28
28
|
v-for="(item, itemIndex) in group.widgets"
|
|
29
29
|
:key="itemIndex"
|
|
30
30
|
class="apos-widget"
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<p v-if="item.description" class="apos-widget__help">
|
|
55
55
|
{{ $t(item.description) }}
|
|
56
56
|
</p>
|
|
57
|
-
</
|
|
57
|
+
</button>
|
|
58
58
|
</div>
|
|
59
59
|
</div>
|
|
60
60
|
</template>
|
|
@@ -239,6 +239,12 @@ export default {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
.apos-widget {
|
|
242
|
+
@include type-base;
|
|
243
|
+
padding: 0;
|
|
244
|
+
border: none;
|
|
245
|
+
background: none;
|
|
246
|
+
text-align: inherit;
|
|
247
|
+
|
|
242
248
|
.apos-widget__preview {
|
|
243
249
|
transition: opacity 250ms ease-in-out;
|
|
244
250
|
.apos-icon--add {
|
|
@@ -387,6 +387,10 @@ export default {
|
|
|
387
387
|
apos.bus.$emit('widget-focus', this.widget._id);
|
|
388
388
|
}
|
|
389
389
|
},
|
|
390
|
+
destroyed() {
|
|
391
|
+
// Remove the focus parent listener when unmounted
|
|
392
|
+
apos.bus.$off('widget-focus-parent', this.focusParent);
|
|
393
|
+
},
|
|
390
394
|
methods: {
|
|
391
395
|
// Focus parent, useful for obtrusive UI
|
|
392
396
|
focusParent() {
|
|
@@ -1184,7 +1184,7 @@ module.exports = {
|
|
|
1184
1184
|
];
|
|
1185
1185
|
|
|
1186
1186
|
function validate ({
|
|
1187
|
-
action, context, label, modal, conditions
|
|
1187
|
+
action, context, label, modal, conditions, if: ifProps
|
|
1188
1188
|
}) {
|
|
1189
1189
|
const allowedConditions = [
|
|
1190
1190
|
'canPublish',
|
|
@@ -1199,19 +1199,25 @@ module.exports = {
|
|
|
1199
1199
|
];
|
|
1200
1200
|
|
|
1201
1201
|
if (!action || !context || !label || !modal) {
|
|
1202
|
-
throw self.apos.error('invalid', 'addContextOperation requires action, context, label and modal properties');
|
|
1202
|
+
throw self.apos.error('invalid', 'addContextOperation requires action, context, label and modal properties.');
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
|
-
if (
|
|
1206
|
-
|
|
1205
|
+
if (
|
|
1206
|
+
conditions &&
|
|
1207
|
+
(!Array.isArray(conditions) ||
|
|
1208
|
+
conditions.some((perm) => !allowedConditions.includes(perm)))
|
|
1209
|
+
) {
|
|
1210
|
+
throw self.apos.error(
|
|
1211
|
+
'invalid', `The conditions property in addContextOperation must be an array containing one or multiple of these values:\n\t${allowedConditions.join('\n\t')}.`
|
|
1212
|
+
);
|
|
1207
1213
|
}
|
|
1208
1214
|
|
|
1209
1215
|
if (
|
|
1210
|
-
|
|
1211
|
-
|
|
1216
|
+
ifProps &&
|
|
1217
|
+
(typeof ifProps !== 'object' || Array.isArray(ifProps))
|
|
1212
1218
|
) {
|
|
1213
1219
|
throw self.apos.error(
|
|
1214
|
-
'invalid',
|
|
1220
|
+
'invalid', 'The if property in addContextOperation must be an object containing properties and values that will be checked against the current document in order to show or not the context operation.'
|
|
1215
1221
|
);
|
|
1216
1222
|
}
|
|
1217
1223
|
}
|
|
@@ -23,6 +23,7 @@ import { detectDocChange } from 'Modules/@apostrophecms/schema/lib/detectChange'
|
|
|
23
23
|
import AposPublishMixin from 'Modules/@apostrophecms/ui/mixins/AposPublishMixin';
|
|
24
24
|
import AposArchiveMixin from 'Modules/@apostrophecms/ui/mixins/AposArchiveMixin';
|
|
25
25
|
import AposModifiedMixin from 'Modules/@apostrophecms/ui/mixins/AposModifiedMixin';
|
|
26
|
+
import checkIfConditions from 'apostrophe/lib/check-if-conditions';
|
|
26
27
|
|
|
27
28
|
export default {
|
|
28
29
|
name: 'AposDocContextMenu',
|
|
@@ -196,7 +197,7 @@ export default {
|
|
|
196
197
|
},
|
|
197
198
|
customOperationsByContext() {
|
|
198
199
|
return this.customOperations.filter(({
|
|
199
|
-
manuallyPublished, hasUrl, conditions, context
|
|
200
|
+
manuallyPublished, hasUrl, conditions, context, if: ifProps
|
|
200
201
|
}) => {
|
|
201
202
|
if (typeof manuallyPublished === 'boolean' && manuallyPublished !== this.manuallyPublished) {
|
|
202
203
|
return false;
|
|
@@ -214,6 +215,14 @@ export default {
|
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
if (ifProps) {
|
|
219
|
+
const canSeeOperation = checkIfConditions(this.doc, ifProps);
|
|
220
|
+
|
|
221
|
+
if (!canSeeOperation) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
217
226
|
return context === 'update' && this.isUpdateOperation;
|
|
218
227
|
});
|
|
219
228
|
},
|
|
@@ -306,7 +315,10 @@ export default {
|
|
|
306
315
|
);
|
|
307
316
|
},
|
|
308
317
|
manuallyPublished() {
|
|
309
|
-
return this.moduleOptions.localized && !this.
|
|
318
|
+
return this.moduleOptions.localized && !this.autopublish;
|
|
319
|
+
},
|
|
320
|
+
autopublish() {
|
|
321
|
+
return this.context._aposAutopublish ?? this.moduleOptions.autopublish;
|
|
310
322
|
},
|
|
311
323
|
isModified() {
|
|
312
324
|
if (!this.current) {
|
|
@@ -383,9 +395,10 @@ export default {
|
|
|
383
395
|
this[action](this.context);
|
|
384
396
|
},
|
|
385
397
|
async edit(doc) {
|
|
386
|
-
await apos.modal.execute(this.moduleOptions.components.editorModal, {
|
|
398
|
+
await apos.modal.execute(doc._aposEditorModal || this.moduleOptions.components.editorModal, {
|
|
387
399
|
moduleName: this.moduleName,
|
|
388
|
-
docId: doc._id
|
|
400
|
+
docId: doc._id,
|
|
401
|
+
type: doc.type
|
|
389
402
|
});
|
|
390
403
|
},
|
|
391
404
|
preview(doc) {
|
|
@@ -401,28 +414,32 @@ export default {
|
|
|
401
414
|
this.$emit('close', doc);
|
|
402
415
|
}
|
|
403
416
|
}
|
|
404
|
-
// Because the page or piece manager might give us just a projected,
|
|
405
|
-
// minimum number of properties otherwise
|
|
406
|
-
const complete = await apos.http.get(`${this.moduleOptions.action}/${doc._id}`, {
|
|
407
|
-
busy: true
|
|
408
|
-
});
|
|
409
|
-
Object.assign(doc, complete);
|
|
410
417
|
|
|
411
|
-
apos.
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
418
|
+
await apos.modal.execute(doc._aposEditorModal || this.moduleOptions.components.editorModal, {
|
|
419
|
+
moduleName: this.moduleName,
|
|
420
|
+
copyOfId: doc._id,
|
|
421
|
+
// Passed for bc
|
|
422
|
+
copyOf: {
|
|
423
|
+
...this.current || doc,
|
|
424
|
+
_id: doc._id
|
|
425
|
+
},
|
|
426
|
+
type: doc.type
|
|
419
427
|
});
|
|
428
|
+
|
|
420
429
|
},
|
|
421
430
|
async customAction(doc, operation) {
|
|
422
431
|
await apos.modal.execute(operation.modal, {
|
|
423
432
|
moduleName: operation.moduleName,
|
|
424
|
-
|
|
433
|
+
// For backwards compatibility
|
|
434
|
+
doc,
|
|
435
|
+
...docProps(doc),
|
|
436
|
+
...operation.props
|
|
425
437
|
});
|
|
438
|
+
function docProps(doc) {
|
|
439
|
+
return Object.fromEntries(Object.entries(operation.docProps || {}).map(([ key, value ]) => {
|
|
440
|
+
return [ key, doc[value] ];
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
426
443
|
},
|
|
427
444
|
async localize(doc) {
|
|
428
445
|
// If there are changes warn the user before discarding them before
|
|
@@ -447,6 +464,3 @@ export default {
|
|
|
447
464
|
}
|
|
448
465
|
};
|
|
449
466
|
</script>
|
|
450
|
-
|
|
451
|
-
<style lang="scss" scoped>
|
|
452
|
-
</style>
|
|
@@ -67,11 +67,12 @@
|
|
|
67
67
|
:conditional-fields="conditionalFields('other')"
|
|
68
68
|
:doc-id="docId"
|
|
69
69
|
:value="docFields"
|
|
70
|
-
@input="updateDocFields"
|
|
71
|
-
@validate="triggerValidate"
|
|
72
70
|
:server-errors="serverErrors"
|
|
73
71
|
:ref="tab.name"
|
|
74
72
|
:generation="generation"
|
|
73
|
+
@input="updateDocFields"
|
|
74
|
+
@validate="triggerValidate"
|
|
75
|
+
@update-doc-data="onUpdateDocFields"
|
|
75
76
|
/>
|
|
76
77
|
</div>
|
|
77
78
|
</template>
|
|
@@ -140,8 +141,12 @@ export default {
|
|
|
140
141
|
type: String,
|
|
141
142
|
default: null
|
|
142
143
|
},
|
|
143
|
-
|
|
144
|
-
type:
|
|
144
|
+
type: {
|
|
145
|
+
type: String,
|
|
146
|
+
default: null
|
|
147
|
+
},
|
|
148
|
+
copyOfId: {
|
|
149
|
+
type: String,
|
|
145
150
|
default: null
|
|
146
151
|
}
|
|
147
152
|
},
|
|
@@ -153,6 +158,7 @@ export default {
|
|
|
153
158
|
fieldErrors: {},
|
|
154
159
|
modal: {
|
|
155
160
|
active: false,
|
|
161
|
+
triggerFocusRefresh: 0,
|
|
156
162
|
type: 'overlay',
|
|
157
163
|
showModal: false
|
|
158
164
|
},
|
|
@@ -265,22 +271,12 @@ export default {
|
|
|
265
271
|
};
|
|
266
272
|
}
|
|
267
273
|
},
|
|
268
|
-
currentFields() {
|
|
269
|
-
if (this.currentTab) {
|
|
270
|
-
const tabFields = this.tabs.find((item) => {
|
|
271
|
-
return item.name === this.currentTab;
|
|
272
|
-
});
|
|
273
|
-
return this.filterOutParkedFields(tabFields.fields);
|
|
274
|
-
} else {
|
|
275
|
-
return [];
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
274
|
saveLabel() {
|
|
279
275
|
if (this.restoreOnly) {
|
|
280
276
|
return 'apostrophe:restore';
|
|
281
277
|
} else if (this.manuallyPublished) {
|
|
282
278
|
if (this.canPublish) {
|
|
283
|
-
if (this.
|
|
279
|
+
if (this.copyOfId) {
|
|
284
280
|
return 'apostrophe:publish';
|
|
285
281
|
} else if (this.original && this.original.lastPublishedAt) {
|
|
286
282
|
return 'apostrophe:update';
|
|
@@ -288,7 +284,7 @@ export default {
|
|
|
288
284
|
return 'apostrophe:publish';
|
|
289
285
|
}
|
|
290
286
|
} else {
|
|
291
|
-
if (this.
|
|
287
|
+
if (this.copyOfId) {
|
|
292
288
|
return 'apostrophe:submit';
|
|
293
289
|
} else if (this.original && this.original.lastPublishedAt) {
|
|
294
290
|
return 'apostrophe:submitUpdate';
|
|
@@ -374,19 +370,25 @@ export default {
|
|
|
374
370
|
});
|
|
375
371
|
}
|
|
376
372
|
}
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
this.modal.triggerFocusRefresh++;
|
|
374
|
+
} else if (this.copyOfId) {
|
|
375
|
+
// Because the page or piece manager might give us just a projected,
|
|
376
|
+
// minimum number of properties otherwise, and because we need to
|
|
377
|
+
// make sure we use our preferred module to fetch the content
|
|
378
|
+
const newInstance = await apos.http.get(`${this.moduleOptions.action}/${this.copyOfId}`, {
|
|
379
|
+
busy: true
|
|
380
|
+
});
|
|
379
381
|
delete newInstance.parked;
|
|
380
|
-
newInstance.title = `Copy of ${
|
|
381
|
-
if (
|
|
382
|
-
const matches =
|
|
382
|
+
newInstance.title = `Copy of ${newInstance.title}`;
|
|
383
|
+
if (newInstance.slug.startsWith('/')) {
|
|
384
|
+
const matches = newInstance.slug.match(/\/([^/]+)$/);
|
|
383
385
|
if (matches) {
|
|
384
386
|
newInstance.slug = `${apos.page.page.slug}/copy-of-${matches[1]}`;
|
|
385
387
|
} else {
|
|
386
388
|
newInstance.slug = '/copy-of-home-page';
|
|
387
389
|
}
|
|
388
390
|
} else {
|
|
389
|
-
newInstance.slug =
|
|
391
|
+
newInstance.slug = newInstance.slug.replace(/([^/]+)$/, 'copy-of-$1');
|
|
390
392
|
}
|
|
391
393
|
delete newInstance._id;
|
|
392
394
|
delete newInstance._url;
|
|
@@ -399,9 +401,11 @@ export default {
|
|
|
399
401
|
this.docFields.data = newInstance;
|
|
400
402
|
this.prepErrors();
|
|
401
403
|
this.docReady = true;
|
|
404
|
+
this.modal.triggerFocusRefresh++;
|
|
402
405
|
} else {
|
|
403
|
-
this.$nextTick(() => {
|
|
404
|
-
this.loadNewInstance();
|
|
406
|
+
this.$nextTick(async () => {
|
|
407
|
+
await this.loadNewInstance();
|
|
408
|
+
this.modal.triggerFocusRefresh++;
|
|
405
409
|
});
|
|
406
410
|
}
|
|
407
411
|
apos.bus.$on('content-changed', this.onContentChanged);
|
|
@@ -505,7 +509,7 @@ export default {
|
|
|
505
509
|
await this.restore(this.original);
|
|
506
510
|
await this.loadDoc();
|
|
507
511
|
},
|
|
508
|
-
async onSave(navigate = false) {
|
|
512
|
+
async onSave({ navigate = false } = {}) {
|
|
509
513
|
if (this.canPublish || !this.manuallyPublished) {
|
|
510
514
|
await this.save({
|
|
511
515
|
andPublish: this.manuallyPublished,
|
|
@@ -566,8 +570,8 @@ export default {
|
|
|
566
570
|
body._targetId = apos.page.page._id.replace(':published', ':draft');
|
|
567
571
|
body._position = 'lastChild';
|
|
568
572
|
}
|
|
569
|
-
if (this.
|
|
570
|
-
body._copyingId = this.
|
|
573
|
+
if (this.copyOfId) {
|
|
574
|
+
body._copyingId = this.copyOfId;
|
|
571
575
|
}
|
|
572
576
|
}
|
|
573
577
|
let doc;
|
|
@@ -665,6 +669,10 @@ export default {
|
|
|
665
669
|
itemName: `${this.moduleName}:editor`
|
|
666
670
|
});
|
|
667
671
|
},
|
|
672
|
+
onUpdateDocFields(value) {
|
|
673
|
+
this.updateDocFields(value);
|
|
674
|
+
this.generation++;
|
|
675
|
+
},
|
|
668
676
|
updateDocFields(value) {
|
|
669
677
|
this.updateFieldErrors(value.fieldState);
|
|
670
678
|
this.docFields.data = {
|