diff --git a/ghost/admin/app/components/settings/audit-log/table.hbs b/ghost/admin/app/components/settings/audit-log/table.hbs index 31714d0e16..8a10106dca 100644 --- a/ghost/admin/app/components/settings/audit-log/table.hbs +++ b/ghost/admin/app/components/settings/audit-log/table.hbs @@ -31,14 +31,16 @@
- {{#if (or ev.resource.title ev.resource.name)}} - {{#if ev.linkable}} + {{#if ev.contextResource}} + {{capitalize-first-letter ev.contextResource.first}} - {{ev.contextResource.second}} + {{else if (or ev.resource.title ev.resource.name ev.original.context.primary_name)}} + {{#if (and (or ev.resource.title ev.resource.name) ev.linkable)}} {{or ev.resource.title ev.resource.name}} {{else}} - {{or ev.resource.title ev.resource.name}} + {{or ev.resource.title ev.resource.name ev.original.context.primary_name}} {{/if}} {{else}} diff --git a/ghost/admin/app/helpers/audit-log-event-fetcher.js b/ghost/admin/app/helpers/audit-log-event-fetcher.js index 11ac85503f..95b76d164f 100644 --- a/ghost/admin/app/helpers/audit-log-event-fetcher.js +++ b/ghost/admin/app/helpers/audit-log-event-fetcher.js @@ -82,6 +82,10 @@ export default class AuditLogEventFetcher extends Resource { this.hasReachedEnd = true; } + actions.forEach((a) => { + a.context = JSON.parse(a.context); + }); + this.data.push(...actions); } catch (e) { this.isError = true; diff --git a/ghost/admin/app/helpers/parse-audit-log-event.js b/ghost/admin/app/helpers/parse-audit-log-event.js index 27f360be2e..0aaaecb5be 100644 --- a/ghost/admin/app/helpers/parse-audit-log-event.js +++ b/ghost/admin/app/helpers/parse-audit-log-event.js @@ -9,7 +9,9 @@ export default class ParseAuditLogEvent extends Helper { const actionIcon = getActionIcon(ev); const getActor = () => this.store.findRecord(ev.actor_type, ev.actor_id, {reload: false}); const getResource = () => this.store.findRecord(ev.resource_type, ev.resource_id, {reload: false}); - const linkable = ['page', 'post'].includes(ev.resource_type); + const contextResource = getContextResource(ev); + + const linkable = ['page', 'post'].includes(ev.resource_type) && ev.event !== 'deleted'; return { get actor() { @@ -18,6 +20,7 @@ export default class ParseAuditLogEvent extends Helper { get resource() { return getResource(); }, + contextResource, linkable, actionIcon, action, @@ -50,3 +53,16 @@ function getAction(ev) { return `${ev.event} ${resourceType}`; } + +function getContextResource(ev) { + if (ev.resource_type === 'setting') { + if (ev.context?.group && ev.context?.key) { + return { + first: ev.context.group, + second: ev.context.key + }; + } + } + + return null; +} diff --git a/ghost/core/core/server/models/action.js b/ghost/core/core/server/models/action.js index 37293c1bf2..4648453667 100644 --- a/ghost/core/core/server/models/action.js +++ b/ghost/core/core/server/models/action.js @@ -19,16 +19,6 @@ const Action = ghostBookshelf.Model.extend({ resource() { return this.morphTo('resource', ['resource_type', 'resource_id'], ...candidates); - }, - - toJSON(unfilteredOptions) { - const options = Action.filterOptions(unfilteredOptions, 'toJSON'); - const attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options); - - // @TODO: context is not implemented yet - delete attrs.context; - - return attrs; } }, { orderDefaultOptions: function orderDefaultOptions() { diff --git a/ghost/core/core/server/models/base/plugins/actions.js b/ghost/core/core/server/models/base/plugins/actions.js index f6b44747b7..9f7b1e54f6 100644 --- a/ghost/core/core/server/models/base/plugins/actions.js +++ b/ghost/core/core/server/models/base/plugins/actions.js @@ -29,14 +29,33 @@ module.exports = function (Bookshelf) { resourceType = resourceType.bind(this)(); } - // @TODO: implement context - return { - event: event, + let context = {}; + + if (this.actionsExtraContext && Array.isArray(this.actionsExtraContext)) { + for (const c of this.actionsExtraContext) { + context[c] = this.get(c) || this.previous(c); + } + } + + if (event === 'deleted') { + context.primary_name = (this.previous('title') || this.previous('name')); + } else if (event === 'edited') { + context.primary_name = (this.get('title') || this.get('name') || this.previous('title') || this.previous('name')); + } + + const data = { + event, resource_id: this.id || this.previous('id'), resource_type: resourceType, actor_id: actor.id, actor_type: actor.type }; + + if (context && Object.keys(context).length) { + data.context = context; + } + + return data; }, /** diff --git a/ghost/core/core/server/models/settings.js b/ghost/core/core/server/models/settings.js index 6655307b93..e906f05a6f 100644 --- a/ghost/core/core/server/models/settings.js +++ b/ghost/core/core/server/models/settings.js @@ -99,6 +99,7 @@ Settings = ghostBookshelf.Model.extend({ actionsCollectCRUD: true, actionsResourceType: 'setting', + actionsExtraContext: ['key', 'group'], emitChange: function emitChange(event, options) { const eventToTrigger = 'settings' + '.' + event; diff --git a/ghost/core/test/e2e-api/admin/utils.js b/ghost/core/test/e2e-api/admin/utils.js index a736d28e75..0b595cf4e1 100644 --- a/ghost/core/test/e2e-api/admin/utils.js +++ b/ghost/core/test/e2e-api/admin/utils.js @@ -26,7 +26,7 @@ const expectedProperties = { members: ['members', 'meta'], snippets: ['snippets', 'meta'], - action: ['id', 'resource_type', 'actor_type', 'event', 'created_at', 'actor'], + action: ['id', 'resource_type', 'actor_type', 'event', 'created_at', 'actor', 'context'], config: [ 'version',