Implemented context on Actions events

refs https://github.com/TryGhost/Toolbox/issues/356

- in order to show data that we might not necessarily still have around
  (ie. when you delete a post, you might want the title), we're going to
  start utilizing the `context` column
- right now, we store the `primary_name` for deleted events, and we also
  store the `setting` `key` and `group` so we can reference it in the
  audit log
This commit is contained in:
Daniel Lockyer 2022-08-23 14:11:24 +02:00 committed by Daniel Lockyer
parent 7f6aa78c01
commit 9effa119c6
7 changed files with 50 additions and 18 deletions

View File

@ -31,14 +31,16 @@
</div>
<div class="gh-list-data gh-audit-log-object">
<div class="gh-audit-log-container">
{{#if (or ev.resource.title ev.resource.name)}}
{{#if ev.linkable}}
{{#if ev.contextResource}}
<span>{{capitalize-first-letter ev.contextResource.first}} - <code>{{ev.contextResource.second}}</code></span>
{{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)}}
<LinkTo @route="editor.edit" @models={{array ev.resource.displayName ev.resource.id}} class="permalink">
<strong>{{or ev.resource.title ev.resource.name}}</strong>
</LinkTo>
{{else}}
<span class="midgrey">
<strong>{{or ev.resource.title ev.resource.name}}</strong>
<strong>{{or ev.resource.title ev.resource.name ev.original.context.primary_name}}</strong>
</span>
{{/if}}
{{else}}

View File

@ -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;

View File

@ -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;
}

View File

@ -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() {

View File

@ -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;
},
/**

View File

@ -99,6 +99,7 @@ Settings = ghostBookshelf.Model.extend({
actionsCollectCRUD: true,
actionsResourceType: 'setting',
actionsExtraContext: ['key', 'group'],
emitChange: function emitChange(event, options) {
const eventToTrigger = 'settings' + '.' + event;

View File

@ -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',