diff --git a/modules/@apostrophecms/file-tag/index.js b/modules/@apostrophecms/file-tag/index.js index 4e7cd5bde4..db4a836539 100644 --- a/modules/@apostrophecms/file-tag/index.js +++ b/modules/@apostrophecms/file-tag/index.js @@ -5,6 +5,7 @@ module.exports = { pluralLabel: 'apostrophe:fileTags', quickCreate: false, autopublish: true, + versions: true, editRole: 'editor', publishRole: 'editor', shortcut: 'G,Shift+F', diff --git a/modules/@apostrophecms/file/index.js b/modules/@apostrophecms/file/index.js index 2047416628..32d679dc73 100644 --- a/modules/@apostrophecms/file/index.js +++ b/modules/@apostrophecms/file/index.js @@ -18,6 +18,7 @@ module.exports = { insertViaUpload: true, slugPrefix: 'file-', autopublish: true, + versions: true, editRole: 'editor', publishRole: 'editor', showPermissions: true, diff --git a/modules/@apostrophecms/image-tag/index.js b/modules/@apostrophecms/image-tag/index.js index 2e9a732566..e64bed8fdd 100644 --- a/modules/@apostrophecms/image-tag/index.js +++ b/modules/@apostrophecms/image-tag/index.js @@ -5,6 +5,7 @@ module.exports = { pluralLabel: 'apostrophe:imageTags', quickCreate: false, autopublish: true, + versions: true, editRole: 'editor', publishRole: 'editor', shortcut: 'G,Shift+I', diff --git a/modules/@apostrophecms/image/index.js b/modules/@apostrophecms/image/index.js index 102987f568..49e98325bb 100644 --- a/modules/@apostrophecms/image/index.js +++ b/modules/@apostrophecms/image/index.js @@ -24,6 +24,7 @@ module.exports = { searchable: false, slugPrefix: 'image-', autopublish: true, + versions: true, editRole: 'editor', publishRole: 'editor', showPermissions: true, diff --git a/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue b/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue index 86449f32b5..e21d629abf 100644 --- a/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +++ b/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue @@ -113,6 +113,7 @@ import dayjs from 'dayjs'; import { isEqual } from 'lodash'; import advancedFormat from 'dayjs/plugin/advancedFormat'; import { createId } from '@paralleldrive/cuid2'; +import checkIfConditions from 'apostrophe/lib/check-if-conditions'; dayjs.extend(advancedFormat); @@ -139,7 +140,7 @@ export default { } } }, - emits: [ 'back', 'modified' ], + emits: [ 'back', 'modified', 'close' ], data() { return { // Primarily use `activeMedia` to support hot-swapping image docs. @@ -150,7 +151,8 @@ export default { original: klona(this.media), lipKey: '', triggerValidation: false, - showReplace: false + showReplace: false, + customOperations: apos.modules['@apostrophecms/doc'].contextOperations }; }, computed: { @@ -160,11 +162,97 @@ export default { canLocalize() { return this.moduleOptions.canLocalize && this.activeMedia._id; }, + canPublish() { + if (this.activeMedia._id) { + console.log('this.activeMedia._publish', this.activeMedia._publish); + return this.activeMedia._publish; + } else { + return this.moduleOptions.canPublish; + } + }, + canEdit() { + if (this.activeMedia._id) { + console.log('this.activeMedia._edit', this.activeMedia._edit); + return this.activeMedia._edit; + } + return this.moduleOptions.canEdit; + }, + customMenusByContext() { + if (!this.canEdit) { + return []; + } + + const menus = this.customOperationsByContext + .map(op => ({ + label: op.label, + action: op.action, + modifiers: op.modifiers || [] + })); + menus.sort((a, b) => a.modifiers.length - b.modifiers.length); + return menus; + }, + customOperationsByContext() { + console.log(this.customOperations); + return this.customOperations.filter(({ + manuallyPublished, hasUrl, conditions, context, if: ifProps, moduleIf + }) => { + if (typeof manuallyPublished === 'boolean' && manuallyPublished !== this.manuallyPublished) { + return false; + } + + if (typeof hasUrl === 'boolean' && hasUrl !== this.hasUrl) { + return false; + } + + if (conditions) { + const notAllowed = conditions.some((action) => !this[action]); + + if (notAllowed) { + return false; + } + } + + ifProps = ifProps || {}; + moduleIf = moduleIf || {}; + const canSeeOperation = checkIfConditions(this.activeMedia, ifProps) && + checkIfConditions(this.moduleOptions, moduleIf); + console.log(this.moduleName, canSeeOperation); + + if (!canSeeOperation) { + return false; + } + + return context === 'update' && this.isUpdateOperation; + }); + }, + moduleName() { + console.log('this.activeMedia.type', this.activeMedia.type); + return this.activeMedia.type; + }, + isUpdateOperation() { + console.log('this.activeMedia._id', this.activeMedia._id); + return !!this.activeMedia._id; + }, + hasUrl() { + console.log('this.activeMedia._url', this.activeMedia._url); + return !!this.activeMedia._url; + }, + manuallyPublished() { + return this.moduleOptions.localized && !this.autopublish; + }, + autopublish() { + return this.activeMedia._aposAutopublish ?? this.moduleOptions.autopublish; + }, moreMenu() { - const menu = [ { - label: 'apostrophe:discardChanges', - action: 'cancel' - } ]; + console.log(this.customMenusByContext); + const menu = [ + { + label: 'apostrophe:discardChanges', + action: 'cancel' + }, + ...this.customMenusByContext + ]; + if (this.canLocalize) { menu.push({ label: 'apostrophe:localize', @@ -248,8 +336,46 @@ export default { }, methods: { moreMenuHandler(action) { + const operation = this.customOperations.find(op => op.action === action); + if (operation) { + this.customAction(this.activeMedia, operation); + return; + } + this[action](); }, + async customAction(doc, operation) { + if (operation.replaces) { + const confirm = await apos.confirm({ + heading: 'apostrophe:replaceHeadingPrompt', + description: this.$t('apostrophe:replaceDescPrompt'), + affirmativeLabel: 'apostrophe:replace', + icon: false + }); + if (!confirm) { + return; + } + this.$emit('close', doc); + } + const props = { + moduleName: operation.moduleName || this.moduleName, + moduleLabels: this.moduleLabels, + // For backwards compatibility + doc, + ...docProps(doc), + ...operation.props + }; + if (operation.type === 'event') { + apos.bus.$emit(operation.action, props); + return; + } + await apos.modal.execute(operation.modal, props); + function docProps(doc) { + return Object.fromEntries(Object.entries(operation.docProps || {}).map(([ key, value ]) => { + return [ key, doc[value] ]; + })); + } + }, async updateActiveDoc(newMedia) { newMedia = newMedia || {}; this.showReplace = false; diff --git a/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue b/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue index a46a0fda62..b94c9ae403 100644 --- a/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue +++ b/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue @@ -80,6 +80,7 @@ export default { if (modal) { await apos.modal.execute(modal, { + moduleName: this.moduleOptions.name, moduleAction: this.moduleOptions.action, action, labels: this.moduleLabels,