Log site customization changes. Use a modal to show staff action log details for site customizations.

This commit is contained in:
Neil Lalonde 2013-08-21 10:49:35 -04:00
parent 7f69a58439
commit a95303fcd8
16 changed files with 202 additions and 8 deletions

View file

@ -0,0 +1,25 @@
/**
The modal for viewing the details of a staff action log record.
@class ChangeSiteCustomizationDetailsController
@extends Discourse.Controller
@namespace Discourse
@uses Discourse.ModalFunctionality
@module Discourse
**/
Discourse.ChangeSiteCustomizationDetailsController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
previousSelected: Ember.computed.equal('selectedTab', 'previous'),
newSelected: Ember.computed.equal('selectedTab', 'new'),
onShow: function() {
this.selectNew();
},
selectNew: function() {
this.set('selectedTab', 'new');
},
selectPrevious: function() {
this.set('selectedTab', 'previous');
}
});

View file

@ -17,8 +17,10 @@ Discourse.StaffActionLog = Discourse.Model.extend({
var formatted = "";
formatted += this.format('email', 'email');
formatted += this.format('admin.logs.staff_actions.ip_address', 'ip_address');
if (!this.get('useModalForDetails')) {
formatted += this.format('admin.logs.staff_actions.new_value', 'new_value');
formatted += this.format('admin.logs.staff_actions.previous_value', 'previous_value');
}
return formatted;
}.property('ip_address', 'email'),
@ -28,7 +30,11 @@ Discourse.StaffActionLog = Discourse.Model.extend({
} else {
return '';
}
}
},
useModalForDetails: function() {
return (this.get('action_name') === 'change_site_customization');
}.property('action_name')
});
Discourse.StaffActionLog.reopenClass({

View file

@ -33,6 +33,13 @@ Discourse.AdminLogsStaffActionLogsRoute = Discourse.Route.extend({
return controller.show();
},
events: {
showDetailsModal: function(logRecord) {
Discourse.Route.showModal(this, logRecord.action_name + '_details', logRecord);
this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal');
}
},
deactivate: function() {
this._super();

View file

@ -0,0 +1,14 @@
<section class="field">
<b>{{i18n admin.customize.css}}</b>: ({{i18n character_count count=stylesheet.length}})<br/>
{{textarea value=stylesheet class="plain"}}
</section>
<section class="field">
<b>{{i18n admin.customize.header}}</b>: ({{i18n character_count count=header.length}})<br/>
{{textarea value=header class="plain"}}
</section>
<section class="field">
<b>{{i18n admin.customize.enabled}}</b>: {{enabled}}
</section>
<section class="field">
<b>{{i18n admin.customize.override_default}}</b>: {{override_default_style}}
</section>

View file

@ -0,0 +1,29 @@
<div>
<ul class="nav nav-pills">
<li {{bindAttr class="newSelected:active"}}>
<a href="#" {{action selectNew}}>{{i18n admin.logs.staff_actions.new_value}}</a>
</li>
<li {{bindAttr class="previousSelected:active"}}>
<a href="#" {{action selectPrevious}}>{{i18n admin.logs.staff_actions.previous_value}}</a>
</li>
</ul>
<div class="modal-body">
<div {{bindAttr class=":modal-tab :new-tab newSelected::invisible"}}>
{{#with new_value}}
{{partial "admin/templates/logs/site_customization_change_details"}}
{{/with}}
</div>
<div {{bindAttr class=":modal-tab :previous-tab previousSelected::invisible"}}>
{{#if previous_value}}
{{#with previous_value}}
{{partial "admin/templates/logs/site_customization_change_details"}}
{{/with}}
{{else}}
{{i18n admin.logs.staff_actions.no_previous}}
{{/if}}
</div>
</div>
<div class="modal-footer">
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
</div>
</div>

View file

@ -17,12 +17,16 @@
<div class="col value created_at">{{unboundAgeWithTooltip created_at}}</div>
<div class="col value details">
{{{formattedDetails}}}
{{#if useModalForDetails}}
<a {{action showDetailsModal this}}>{{i18n admin.logs.staff_actions.show}}</a>
{{else}}
{{#if showFullDetails}}
<a {{action toggleFullDetails this}}>{{i18n less}}</a><br/>
{{details}}
{{else}}
<a {{action toggleFullDetails this}}>{{i18n more}}</a>
{{/if}}
{{/if}}
</div>
<div class="col value context">{{context}}</div>
<div class="clearfix"></div>

View file

@ -0,0 +1,12 @@
/**
A modal view for details of a staff action log record in a modal.
@class ChangeSiteCustomizationDetailsView
@extends Discourse.ModalBodyView
@namespace Discourse
@module Discourse
**/
Discourse.ChangeSiteCustomizationDetailsView = Discourse.ModalBodyView.extend({
templateName: 'admin/templates/logs/site_customization_change_modal',
title: I18n.t('admin.logs.staff_actions.modal_title')
});

View file

@ -806,3 +806,9 @@ table {
border-top: solid 1px #ddd;
}
}
.log-details-modal {
.modal-tab {
width: 95%;
}
}

View file

@ -253,6 +253,13 @@
}
}
.tabbed-modal {
.modal-body {
position: relative;
height: 350px;
}
}
.modal-tab {
position: absolute;
}

View file

@ -14,6 +14,7 @@ class Admin::SiteCustomizationsController < Admin::AdminController
respond_to do |format|
if @site_customization.save
log_site_customization_change(nil, site_customization_params)
format.json { render json: @site_customization, status: :created}
else
format.json { render json: @site_customization.errors, status: :unprocessable_entity }
@ -23,11 +24,13 @@ class Admin::SiteCustomizationsController < Admin::AdminController
def update
@site_customization = SiteCustomization.find(params[:id])
log_record = log_site_customization_change(@site_customization, site_customization_params)
respond_to do |format|
if @site_customization.update_attributes(site_customization_params)
format.json { head :no_content }
else
log_record.destroy if log_record
format.json { render json: @site_customization.errors, status: :unprocessable_entity }
end
end
@ -37,6 +40,8 @@ class Admin::SiteCustomizationsController < Admin::AdminController
@site_customization = SiteCustomization.find(params[:id])
@site_customization.destroy
# TODO: log this
respond_to do |format|
format.json { head :no_content }
end
@ -48,4 +53,8 @@ class Admin::SiteCustomizationsController < Admin::AdminController
params.require(:site_customization).permit(:name, :stylesheet, :header, :position, :enabled, :key, :override_default_style, :stylesheet_baked)
end
def log_site_customization_change(old_record, new_params)
StaffActionLogger.new(current_user).log_site_customization_change(old_record, new_params)
end
end

View file

@ -9,7 +9,7 @@ class StaffActionLog < ActiveRecord::Base
validates_presence_of :action
def self.actions
@actions ||= Enum.new(:delete_user, :change_trust_level, :change_site_setting)
@actions ||= Enum.new(:delete_user, :change_trust_level, :change_site_setting, :change_site_customization)
end
def self.with_filters(filters)
@ -25,6 +25,14 @@ class StaffActionLog < ActiveRecord::Base
query = query.where("subject = ?", filters[:subject]) if filters[:subject]
query
end
def new_value_is_json?
action == StaffActionLog.actions[:change_site_customization]
end
def previous_value_is_json?
new_value_is_json?
end
end
# == Schema Information

View file

@ -15,4 +15,20 @@ class StaffActionLogSerializer < ApplicationSerializer
def action_name
StaffActionLog.actions.key(object.action).to_s
end
def new_value
if object.new_value
object.new_value_is_json? ? ::JSON.parse(object.new_value) : object.new_value
else
nil
end
end
def previous_value
if object.previous_value
object.previous_value_is_json? ? ::JSON.parse(object.previous_value) : object.previous_value
else
nil
end
end
end

View file

@ -37,6 +37,17 @@ class StaffActionLogger
}))
end
def log_site_customization_change(old_record, site_customization_params, opts={})
raise Discourse::InvalidParameters.new('site_customization_params is nil') unless site_customization_params
logged_attrs = ['stylesheet', 'header', 'position', 'enabled', 'key', 'override_default_style']
StaffActionLog.create( params(opts).merge({
action: StaffActionLog.actions[:change_site_customization],
subject: site_customization_params[:name],
previous_value: old_record ? old_record.attributes.slice(*logged_attrs).to_json : nil,
new_value: site_customization_params.slice(*(logged_attrs.map(&:to_sym))).to_json
}))
end
private
def params(opts)

View file

@ -100,6 +100,9 @@ en:
read_more: 'read more'
more: "More"
less: "Less"
character_count:
one: "{{count}} character"
other: "{{count}} characters"
in_n_seconds:
one: "in 1 second"
@ -1196,10 +1199,15 @@ en:
ip_address: "IP"
previous_value: "Previous"
new_value: "New"
diff: "Diff"
show: "Show"
modal_title: "Details"
no_previous: "There is no previous value."
actions:
delete_user: "delete user"
change_trust_level: "change trust level"
change_site_setting: "change site setting"
change_site_customization: "change site customization"
screened_emails:
title: "Screened Emails"
description: "When someone tries to create a new account, the following email addresses will be checked and the registration will be blocked, or some other action performed."

View file

@ -34,6 +34,11 @@ describe Admin::SiteCustomizationsController do
xhr :post, :create, site_customization: {name: 'my test name'}
::JSON.parse(response.body).should be_present
end
it 'logs the change' do
StaffActionLogger.any_instance.expects(:log_site_customization_change).once
xhr :post, :create, site_customization: {name: 'my test name'}
end
end
end

View file

@ -73,4 +73,31 @@ describe StaffActionLogger do
expect { logger.log_site_setting_change('title', 'Discourse', 'My Site') }.to change { StaffActionLog.count }.by(1)
end
end
describe "log_site_customization_change" do
let(:valid_params) { {name: 'Cool Theme', stylesheet: "body {\n background-color: blue;\n}\n", header: "h1 {color: white;}"} }
it "raises an error when params are invalid" do
expect { logger.log_site_customization_change(nil, nil) }.to raise_error(Discourse::InvalidParameters)
end
it "logs new site customizations" do
log_record = logger.log_site_customization_change(nil, valid_params)
log_record.subject.should == valid_params[:name]
log_record.previous_value.should be_nil
log_record.new_value.should be_present
json = ::JSON.parse(log_record.new_value)
json['stylesheet'].should be_present
json['header'].should be_present
end
it "logs updated site customizations" do
existing = SiteCustomization.new(name: 'Banana', stylesheet: "body {color: yellow;}", header: "h1 {color: brown;}")
log_record = logger.log_site_customization_change(existing, valid_params)
log_record.previous_value.should be_present
json = ::JSON.parse(log_record.previous_value)
json['stylesheet'].should == existing.stylesheet
json['header'].should == existing.header
end
end
end