mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-25 02:31:59 -05:00
Implemented resource linking in Audit Log
refs https://github.com/TryGhost/Toolbox/issues/356 - we have a very crude version of this before but it just wasn't maintainable - one of the first things I did here was to add `include=resource` on the API call, so it returns the fields we need without extra API requests - after we have the id/slug, I could build a route and model array dynamically, or return null if we can't redirect to the object (it doesn't exist)
This commit is contained in:
parent
35ff83afa9
commit
7f0996d986
5 changed files with 68 additions and 21 deletions
ghost
admin/app
core
|
@ -36,14 +36,14 @@
|
||||||
<strong>{{capitalize-first-letter ev.contextResource.first}}</strong>
|
<strong>{{capitalize-first-letter ev.contextResource.first}}</strong>
|
||||||
<code>({{ev.contextResource.second}})</code>
|
<code>({{ev.contextResource.second}})</code>
|
||||||
</span>
|
</span>
|
||||||
{{else if (or ev.resource.title ev.resource.name ev.original.context.primary_name)}}
|
{{else if (or ev.original.resource.title ev.original.resource.name ev.original.context.primary_name)}}
|
||||||
{{#if (and (or ev.resource.title ev.resource.name) ev.linkable)}}
|
{{#if ev.linkTarget}}
|
||||||
<LinkTo @route="editor.edit" @models={{array ev.resource.displayName ev.resource.id}} class="permalink">
|
<LinkTo @route={{ev.linkTarget.route}} @models={{ev.linkTarget.models}} class="permalink">
|
||||||
<strong>{{or ev.resource.title ev.resource.name}}</strong>
|
<strong>{{or ev.original.resource.title ev.original.resource.name}}</strong>
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="midgrey">
|
<span class="midgrey">
|
||||||
<strong>{{or ev.resource.title ev.resource.name ev.original.context.primary_name}}</strong>
|
<strong>{{or ev.original.resource.title ev.original.resource.name ev.original.context.primary_name}}</strong>
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -75,7 +75,10 @@ export default class AuditLogEventFetcher extends Resource {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
const url = this.ghostPaths.url.api('actions');
|
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});
|
const {actions} = yield this.ajax.request(url, {data});
|
||||||
|
|
||||||
if (actions.length < data.limit) {
|
if (actions.length < data.limit) {
|
||||||
|
|
|
@ -8,20 +8,15 @@ export default class ParseAuditLogEvent extends Helper {
|
||||||
const action = getAction(ev);
|
const action = getAction(ev);
|
||||||
const actionIcon = getActionIcon(ev);
|
const actionIcon = getActionIcon(ev);
|
||||||
const getActor = () => this.store.findRecord(ev.actor_type, ev.actor_id, {reload: false});
|
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 contextResource = getContextResource(ev);
|
||||||
|
const linkTarget = getLinkTarget(ev);
|
||||||
const linkable = ['page', 'post'].includes(ev.resource_type) && ev.event !== 'deleted';
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get actor() {
|
get actor() {
|
||||||
return getActor();
|
return getActor();
|
||||||
},
|
},
|
||||||
get resource() {
|
|
||||||
return getResource();
|
|
||||||
},
|
|
||||||
contextResource,
|
contextResource,
|
||||||
linkable,
|
linkTarget,
|
||||||
actionIcon,
|
actionIcon,
|
||||||
action,
|
action,
|
||||||
original: ev
|
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) {
|
function getActionIcon(ev) {
|
||||||
switch (ev.event) {
|
switch (ev.event) {
|
||||||
case 'added':
|
case 'added':
|
||||||
|
@ -51,6 +91,14 @@ function getAction(ev) {
|
||||||
resourceType = 'settings';
|
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}`;
|
return `${ev.event} ${resourceType}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,9 +130,6 @@ const post = (attrs, frame) => {
|
||||||
|
|
||||||
const action = (attrs) => {
|
const action = (attrs) => {
|
||||||
if (attrs.actor) {
|
if (attrs.actor) {
|
||||||
delete attrs.actor_id;
|
|
||||||
delete attrs.resource_id;
|
|
||||||
|
|
||||||
if (attrs.actor_type === 'user') {
|
if (attrs.actor_type === 'user') {
|
||||||
attrs.actor = _.pick(attrs.actor, ['id', 'name', 'slug', 'profile_image']);
|
attrs.actor = _.pick(attrs.actor, ['id', 'name', 'slug', 'profile_image']);
|
||||||
attrs.actor.image = attrs.actor.profile_image;
|
attrs.actor.image = attrs.actor.profile_image;
|
||||||
|
@ -142,12 +139,11 @@ const action = (attrs) => {
|
||||||
attrs.actor.image = attrs.actor.icon_image;
|
attrs.actor.image = attrs.actor.icon_image;
|
||||||
delete 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
|
// @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;
|
attrs.resource.image = attrs.resource.feature_image;
|
||||||
delete attrs.resource.feature_image;
|
delete attrs.resource.feature_image;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ const expectedProperties = {
|
||||||
members: ['members', 'meta'],
|
members: ['members', 'meta'],
|
||||||
snippets: ['snippets', '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: [
|
config: [
|
||||||
'version',
|
'version',
|
||||||
|
|
Loading…
Add table
Reference in a new issue