mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
Add a way to view staff action logs in admin
This commit is contained in:
parent
d2fb6ec53f
commit
5c8c52482a
16 changed files with 231 additions and 24 deletions
|
@ -13,7 +13,6 @@ Discourse.AdminLogsBlockedEmailsController = Ember.ArrayController.extend(Discou
|
||||||
var self = this;
|
var self = this;
|
||||||
this.set('loading', true);
|
this.set('loading', true);
|
||||||
Discourse.BlockedEmail.findAll().then(function(result) {
|
Discourse.BlockedEmail.findAll().then(function(result) {
|
||||||
console.log('findAll done');
|
|
||||||
self.set('content', result);
|
self.set('content', result);
|
||||||
self.set('loading', false);
|
self.set('loading', false);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
This controller supports the interface for listing staff action logs in the admin section.
|
||||||
|
|
||||||
|
@class AdminLogsStaffActionLogsController
|
||||||
|
@extends Ember.ArrayController
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.AdminLogsStaffActionLogsController = Ember.ArrayController.extend(Discourse.Presence, {
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
var self = this;
|
||||||
|
this.set('loading', true);
|
||||||
|
Discourse.StaffActionLog.findAll().then(function(result) {
|
||||||
|
self.set('content', result);
|
||||||
|
self.set('loading', false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleFullDetails: function(target) {
|
||||||
|
target.set('showFullDetails', !target.get('showFullDetails'));
|
||||||
|
}
|
||||||
|
});
|
|
@ -9,7 +9,7 @@
|
||||||
**/
|
**/
|
||||||
Discourse.BlockedEmail = Discourse.Model.extend({
|
Discourse.BlockedEmail = Discourse.Model.extend({
|
||||||
actionName: function() {
|
actionName: function() {
|
||||||
return I18n.t("admin.logs.actions." + this.get('action'));
|
return I18n.t("admin.logs.blocked_emails.actions." + this.get('action'));
|
||||||
}.property('action')
|
}.property('action')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,9 +18,7 @@ Discourse.BlockedEmail.reopenClass({
|
||||||
return Discourse.ajax("/admin/logs/blocked_emails.json").then(function(blocked_emails) {
|
return Discourse.ajax("/admin/logs/blocked_emails.json").then(function(blocked_emails) {
|
||||||
return blocked_emails.map(function(b) {
|
return blocked_emails.map(function(b) {
|
||||||
return Discourse.BlockedEmail.create(b);
|
return Discourse.BlockedEmail.create(b);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
46
app/assets/javascripts/admin/models/staff_action_log.js
Normal file
46
app/assets/javascripts/admin/models/staff_action_log.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
Represents an action taken by a staff member that has been logged.
|
||||||
|
|
||||||
|
@class StaffActionLog
|
||||||
|
@extends Discourse.Model
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.StaffActionLog = Discourse.Model.extend({
|
||||||
|
showFullDetails: false,
|
||||||
|
|
||||||
|
actionName: function() {
|
||||||
|
return I18n.t("admin.logs.staff_actions.actions." + this.get('action_name'));
|
||||||
|
}.property('action_name'),
|
||||||
|
|
||||||
|
formattedDetails: function() {
|
||||||
|
var formatted = "";
|
||||||
|
if (this.get('email')) {
|
||||||
|
formatted += "<b>Email:</b> " + this.get('email') + "<br/>";
|
||||||
|
}
|
||||||
|
if (this.get('ip_address')) {
|
||||||
|
formatted += "<b>IP:</b> " + this.get('ip_address') + "<br/>";
|
||||||
|
}
|
||||||
|
return formatted;
|
||||||
|
}.property('ip_address', 'email')
|
||||||
|
});
|
||||||
|
|
||||||
|
Discourse.StaffActionLog.reopenClass({
|
||||||
|
create: function(attrs) {
|
||||||
|
if (attrs.staff_user) {
|
||||||
|
attrs.staff_user = Discourse.AdminUser.create(attrs.staff_user);
|
||||||
|
}
|
||||||
|
if (attrs.target_user) {
|
||||||
|
attrs.target_user = Discourse.AdminUser.create(attrs.target_user);
|
||||||
|
}
|
||||||
|
return this._super(attrs);
|
||||||
|
},
|
||||||
|
|
||||||
|
findAll: function(filter) {
|
||||||
|
return Discourse.ajax("/admin/logs/staff_action_logs.json").then(function(staff_actions) {
|
||||||
|
return staff_actions.map(function(s) {
|
||||||
|
return Discourse.StaffActionLog.create(s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -8,7 +8,7 @@
|
||||||
**/
|
**/
|
||||||
Discourse.AdminLogsIndexRoute = Discourse.Route.extend({
|
Discourse.AdminLogsIndexRoute = Discourse.Route.extend({
|
||||||
redirect: function() {
|
redirect: function() {
|
||||||
this.transitionTo('adminLogs.blockedEmails');
|
this.transitionTo('adminLogs.staffActionLogs');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,10 +21,6 @@ Discourse.AdminLogsIndexRoute = Discourse.Route.extend({
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.AdminLogsBlockedEmailsRoute = Discourse.Route.extend({
|
Discourse.AdminLogsBlockedEmailsRoute = Discourse.Route.extend({
|
||||||
// model: function() {
|
|
||||||
// return Discourse.BlockedEmail.findAll();
|
|
||||||
// },
|
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
this.render('admin/templates/logs/blocked_emails', {into: 'adminLogs'});
|
this.render('admin/templates/logs/blocked_emails', {into: 'adminLogs'});
|
||||||
},
|
},
|
||||||
|
@ -32,4 +28,22 @@ Discourse.AdminLogsBlockedEmailsRoute = Discourse.Route.extend({
|
||||||
setupController: function() {
|
setupController: function() {
|
||||||
return this.controllerFor('adminLogsBlockedEmails').show();
|
return this.controllerFor('adminLogsBlockedEmails').show();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
The route that lists staff actions that were logged.
|
||||||
|
|
||||||
|
@class AdminLogsStaffActionLogsRoute
|
||||||
|
@extends Discourse.Route
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.AdminLogsStaffActionLogsRoute = Discourse.Route.extend({
|
||||||
|
renderTemplate: function() {
|
||||||
|
this.render('admin/templates/logs/staff_action_logs', {into: 'adminLogs'});
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController: function() {
|
||||||
|
return this.controllerFor('adminLogsStaffActionLogs').show();
|
||||||
|
}
|
||||||
});
|
});
|
|
@ -31,6 +31,7 @@ Discourse.Route.buildRoutes(function() {
|
||||||
|
|
||||||
this.resource('adminLogs', { path: '/logs' }, function() {
|
this.resource('adminLogs', { path: '/logs' }, function() {
|
||||||
this.route('blockedEmails', { path: '/blocked_emails' });
|
this.route('blockedEmails', { path: '/blocked_emails' });
|
||||||
|
this.route('staffActionLogs', { path: '/staff_action_logs' });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route('groups', {path: '/groups'});
|
this.route('groups', {path: '/groups'});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<div class='admin-controls'>
|
<div class='admin-controls'>
|
||||||
<div class='span15'>
|
<div class='span15'>
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
<li>{{#linkTo 'adminLogs.blockedEmails'}}{{i18n admin.logs.blocked_emails}}{{/linkTo}}</li>
|
<li>{{#linkTo 'adminLogs.staffActionLogs'}}{{i18n admin.logs.staff_actions.title}}{{/linkTo}}</li>
|
||||||
|
<li>{{#linkTo 'adminLogs.blockedEmails'}}{{i18n admin.logs.blocked_emails.title}}{{/linkTo}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
{{#if model.length}}
|
{{#if model.length}}
|
||||||
<table class='table blocked-emails'>
|
<table class='table blocked-emails'>
|
||||||
<thead>
|
<thead>
|
||||||
<th class="email">{{i18n admin.logs.email}}</th>
|
<th class="email">{{i18n admin.logs.blocked_emails.email}}</th>
|
||||||
<th class="action">{{i18n admin.logs.action}}</th>
|
<th class="action">{{i18n admin.logs.action}}</th>
|
||||||
<th class="match_count">{{i18n admin.logs.match_count}}</th>
|
<th class="match_count">{{i18n admin.logs.blocked_emails.match_count}}</th>
|
||||||
<th class="last_match_at">{{i18n admin.logs.last_match_at}}</th>
|
<th class="last_match_at">{{i18n admin.logs.blocked_emails.last_match_at}}</th>
|
||||||
<th class="created_at">{{i18n admin.logs.created_at}}</th>
|
<th class="created_at">{{i18n admin.logs.created_at}}</th>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
{{#if loading}}
|
||||||
|
<div class='admin-loading'>{{i18n loading}}</div>
|
||||||
|
{{else}}
|
||||||
|
{{#if model.length}}
|
||||||
|
<table class='table staff-actions'>
|
||||||
|
<thead>
|
||||||
|
<th class="action">{{i18n admin.logs.action}}</th>
|
||||||
|
<th class="staff_user">{{i18n admin.logs.staff_actions.staff_user}}</th>
|
||||||
|
<th class="target_user">{{i18n admin.logs.staff_actions.target_user}}</th>
|
||||||
|
<th class="context">{{i18n admin.logs.staff_actions.context}}</th>
|
||||||
|
<th class="created_at">{{i18n admin.logs.created_at}}</th>
|
||||||
|
<th class="details">{{i18n admin.logs.staff_actions.details}}</th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each model}}
|
||||||
|
<tr>
|
||||||
|
<td class="action">{{actionName}}</td>
|
||||||
|
<td class="staff_user">
|
||||||
|
{{#linkTo 'adminUser' staff_user}}{{avatar staff_user imageSize="tiny"}}{{/linkTo}}
|
||||||
|
{{#linkTo 'adminUser' staff_user}}{{staff_user.username}}{{/linkTo}}
|
||||||
|
</td>
|
||||||
|
<td class="target_user">
|
||||||
|
{{#if target_user}}
|
||||||
|
{{#linkTo 'adminUser' target_user}}{{avatar target_user imageSize="tiny"}}{{/linkTo}}
|
||||||
|
{{#linkTo 'adminUser' target_user}}{{target_user.username}}{{/linkTo}}
|
||||||
|
{{else}}
|
||||||
|
—
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
<td class="context">{{context}}</td>
|
||||||
|
<td class="created_at">{{unboundAgeWithTooltip created_at}}</td>
|
||||||
|
<td class="details">
|
||||||
|
{{{formattedDetails}}}
|
||||||
|
{{#if showFullDetails}}
|
||||||
|
{{details}}
|
||||||
|
<br/>
|
||||||
|
<a {{action toggleFullDetails this}}>Less</a>
|
||||||
|
{{else}}
|
||||||
|
<a {{action toggleFullDetails this}}>More</a>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
No results.
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
|
@ -700,4 +700,22 @@ table {
|
||||||
.match_count, .last_match_at, .created_at {
|
.match_count, .last_match_at, .created_at {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.staff-actions {
|
||||||
|
.action {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
.staff_user, .target_user {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.created_at {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.details {
|
||||||
|
width: 500px;
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class Admin::BlockedEmailsController < Admin::AdminController
|
class Admin::BlockedEmailsController < Admin::AdminController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
blocked_emails = BlockedEmail.limit(50).order('created_at desc').to_a
|
blocked_emails = BlockedEmail.limit(50).order('last_match_at desc').to_a
|
||||||
render_serialized(blocked_emails, BlockedEmailSerializer)
|
render_serialized(blocked_emails, BlockedEmailSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
8
app/controllers/admin/staff_action_logs_controller.rb
Normal file
8
app/controllers/admin/staff_action_logs_controller.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class Admin::StaffActionLogsController < Admin::AdminController
|
||||||
|
|
||||||
|
def index
|
||||||
|
staff_actions = StaffActionLog.limit(50).order('created_at desc').to_a
|
||||||
|
render_serialized(staff_actions, StaffActionLogSerializer)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
15
app/serializers/staff_action_log_serializer.rb
Normal file
15
app/serializers/staff_action_log_serializer.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
class StaffActionLogSerializer < ApplicationSerializer
|
||||||
|
attributes :action_name,
|
||||||
|
:details,
|
||||||
|
:context,
|
||||||
|
:ip_address,
|
||||||
|
:email,
|
||||||
|
:created_at
|
||||||
|
|
||||||
|
has_one :staff_user, serializer: BasicUserSerializer, embed: :objects
|
||||||
|
has_one :target_user, serializer: BasicUserSerializer, embed: :objects
|
||||||
|
|
||||||
|
def action_name
|
||||||
|
StaffActionLog.actions.key(object.action).to_s
|
||||||
|
end
|
||||||
|
end
|
|
@ -1162,15 +1162,25 @@ en:
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
title: "Logs"
|
title: "Logs"
|
||||||
blocked_emails: "Blocked Emails"
|
|
||||||
email: "Email Address"
|
|
||||||
action: "Action"
|
action: "Action"
|
||||||
last_match_at: "Last Matched"
|
|
||||||
match_count: "Matches"
|
|
||||||
created_at: "Created"
|
created_at: "Created"
|
||||||
actions:
|
blocked_emails:
|
||||||
block: "block"
|
title: "Blocked Emails"
|
||||||
do_nothing: "do nothing"
|
email: "Email Address"
|
||||||
|
last_match_at: "Last Matched"
|
||||||
|
match_count: "Matches"
|
||||||
|
actions:
|
||||||
|
block: "block"
|
||||||
|
do_nothing: "do nothing"
|
||||||
|
staff_actions:
|
||||||
|
title: "Staff Actions"
|
||||||
|
staff_user: "Staff User"
|
||||||
|
target_user: "Target User"
|
||||||
|
context: "Context"
|
||||||
|
details: "Details"
|
||||||
|
actions:
|
||||||
|
delete_user: "delete user"
|
||||||
|
change_trust_level: "change trust level"
|
||||||
|
|
||||||
impersonate:
|
impersonate:
|
||||||
title: "Impersonate User"
|
title: "Impersonate User"
|
||||||
|
|
|
@ -63,7 +63,8 @@ Discourse::Application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
scope '/logs' do
|
scope '/logs' do
|
||||||
resources :blocked_emails, only: [:index, :create, :update, :destroy]
|
resources :blocked_emails, only: [:index, :create, :update, :destroy]
|
||||||
|
resources :staff_action_logs, only: [:index, :create, :update, :destroy]
|
||||||
end
|
end
|
||||||
|
|
||||||
get 'customize' => 'site_customizations#index', constraints: AdminConstraint.new
|
get 'customize' => 'site_customizations#index', constraints: AdminConstraint.new
|
||||||
|
|
22
spec/controllers/admin/staff_action_logs_controller_spec.rb
Normal file
22
spec/controllers/admin/staff_action_logs_controller_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Admin::StaffActionLogsController do
|
||||||
|
it "is a subclass of AdminController" do
|
||||||
|
(Admin::StaffActionLogsController < Admin::AdminController).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:user) { log_in(:admin) }
|
||||||
|
|
||||||
|
context '.index' do
|
||||||
|
before do
|
||||||
|
xhr :get, :index
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { response }
|
||||||
|
it { should be_success }
|
||||||
|
|
||||||
|
it 'returns JSON' do
|
||||||
|
::JSON.parse(subject.body).should be_a(Array)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue