diff --git a/ghost/admin/app/components/settings/audit-log/table.hbs b/ghost/admin/app/components/settings/audit-log/table.hbs index dfa999b798..f2fc61a8b2 100644 --- a/ghost/admin/app/components/settings/audit-log/table.hbs +++ b/ghost/admin/app/components/settings/audit-log/table.hbs @@ -36,14 +36,14 @@ {{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 if (or ev.original.resource.title ev.original.resource.name ev.original.context.primary_name)}} + {{#if ev.linkTarget}} + + {{or ev.original.resource.title ev.original.resource.name}} {{else}} - {{or ev.resource.title ev.resource.name ev.original.context.primary_name}} + {{or ev.original.resource.title ev.original.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 95b76d164f..5db8845f56 100644 --- a/ghost/admin/app/helpers/audit-log-event-fetcher.js +++ b/ghost/admin/app/helpers/audit-log-event-fetcher.js @@ -75,7 +75,10 @@ export default class AuditLogEventFetcher extends Resource { this.isLoading = true; const url = this.ghostPaths.url.api('actions'); - const data = Object.assign({}, queryParams, {limit: this.args.named.pageSize}); + const data = Object.assign({}, queryParams, { + include: 'resource', + limit: this.args.named.pageSize + }); const {actions} = yield this.ajax.request(url, {data}); if (actions.length < data.limit) { diff --git a/ghost/admin/app/helpers/parse-audit-log-event.js b/ghost/admin/app/helpers/parse-audit-log-event.js index 0aaaecb5be..73538234ea 100644 --- a/ghost/admin/app/helpers/parse-audit-log-event.js +++ b/ghost/admin/app/helpers/parse-audit-log-event.js @@ -8,20 +8,15 @@ export default class ParseAuditLogEvent extends Helper { const action = getAction(ev); 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 contextResource = getContextResource(ev); - - const linkable = ['page', 'post'].includes(ev.resource_type) && ev.event !== 'deleted'; + const linkTarget = getLinkTarget(ev); return { get actor() { return getActor(); }, - get resource() { - return getResource(); - }, contextResource, - linkable, + linkTarget, actionIcon, action, original: ev @@ -29,6 +24,51 @@ export default class ParseAuditLogEvent extends Helper { } } +function getLinkTarget(ev) { + let resourceType = ev.resource_type; + + if (ev.event !== 'deleted') { + switch (ev.resource_type) { + case 'page': + case 'post': + if (!ev.resource.id) { + return null; + } + + if (resourceType === 'post') { + if (ev.context?.type) { + resourceType = ev.context?.type; + } + } + + return { + route: 'editor.edit', + models: [resourceType, ev.resource.id] + }; + case 'tag': + if (!ev.resource.slug) { + return null; + } + + return { + route: 'tag', + models: [ev.resource.slug] + }; + case 'user': + if (!ev.resource.slug) { + return null; + } + + return { + route: 'settings.staff.user', + models: [ev.resource.slug] + }; + } + } + + return null; +} + function getActionIcon(ev) { switch (ev.event) { case 'added': @@ -51,6 +91,14 @@ function getAction(ev) { resourceType = 'settings'; } + // Because a `page` and `post` both use the same model, we store the + // actual type in the context, so let's check if that exists + if (resourceType === 'post') { + if (ev.context?.type) { + resourceType = ev.context?.type; + } + } + return `${ev.event} ${resourceType}`; } diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/utils/clean.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/utils/clean.js index b230305747..a3c7e484d5 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/utils/clean.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/utils/clean.js @@ -130,9 +130,6 @@ const post = (attrs, frame) => { const action = (attrs) => { if (attrs.actor) { - delete attrs.actor_id; - delete attrs.resource_id; - if (attrs.actor_type === 'user') { attrs.actor = _.pick(attrs.actor, ['id', 'name', 'slug', 'profile_image']); attrs.actor.image = attrs.actor.profile_image; @@ -142,12 +139,11 @@ const action = (attrs) => { attrs.actor.image = attrs.actor.icon_image; delete attrs.actor.icon_image; } - } else if (attrs.resource) { - delete attrs.actor_id; - delete attrs.resource_id; + } + if (attrs.resource) { // @NOTE: we only support posts right now - attrs.resource = _.pick(attrs.resource, ['id', 'title', 'slug', 'feature_image']); + attrs.resource = _.pick(attrs.resource, ['id', 'title', 'slug', 'feature_image', 'name']); attrs.resource.image = attrs.resource.feature_image; delete attrs.resource.feature_image; } diff --git a/ghost/core/test/e2e-api/admin/utils.js b/ghost/core/test/e2e-api/admin/utils.js index 0b595cf4e1..d1781c10dd 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', 'context'], + action: ['id', 'resource_type', 'actor_type', 'event', 'created_at', 'actor', 'context', 'resource_id', 'actor_id'], config: [ 'version',