diff --git a/app/assets/javascripts/admin/controllers/change_site_customization_details_controller.js b/app/assets/javascripts/admin/controllers/change_site_customization_details_controller.js
new file mode 100644
index 000000000..7fe0d014f
--- /dev/null
+++ b/app/assets/javascripts/admin/controllers/change_site_customization_details_controller.js
@@ -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');
+ }
+});
diff --git a/app/assets/javascripts/admin/models/staff_action_log.js b/app/assets/javascripts/admin/models/staff_action_log.js
index e52d9bf60..aa5a93ea6 100644
--- a/app/assets/javascripts/admin/models/staff_action_log.js
+++ b/app/assets/javascripts/admin/models/staff_action_log.js
@@ -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');
- formatted += this.format('admin.logs.staff_actions.new_value', 'new_value');
- formatted += this.format('admin.logs.staff_actions.previous_value', 'previous_value');
+ 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({
diff --git a/app/assets/javascripts/admin/routes/admin_logs_routes.js b/app/assets/javascripts/admin/routes/admin_logs_routes.js
index c14010109..94bb15029 100644
--- a/app/assets/javascripts/admin/routes/admin_logs_routes.js
+++ b/app/assets/javascripts/admin/routes/admin_logs_routes.js
@@ -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();
diff --git a/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.js.handlebars b/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.js.handlebars
new file mode 100644
index 000000000..cb4c50fa7
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/logs/_site_customization_change_details.js.handlebars
@@ -0,0 +1,14 @@
+
+ {{i18n admin.customize.css}}: ({{i18n character_count count=stylesheet.length}})
+ {{textarea value=stylesheet class="plain"}}
+
+
+ {{i18n admin.customize.header}}: ({{i18n character_count count=header.length}})
+ {{textarea value=header class="plain"}}
+
+
+ {{i18n admin.customize.enabled}}: {{enabled}}
+
+
+ {{i18n admin.customize.override_default}}: {{override_default_style}}
+
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.js.handlebars b/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.js.handlebars
new file mode 100644
index 000000000..ca30597ed
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/logs/site_customization_change_modal.js.handlebars
@@ -0,0 +1,29 @@
+
+
+
+
+ {{#with new_value}}
+ {{partial "admin/templates/logs/site_customization_change_details"}}
+ {{/with}}
+
+
+ {{#if previous_value}}
+ {{#with previous_value}}
+ {{partial "admin/templates/logs/site_customization_change_details"}}
+ {{/with}}
+ {{else}}
+ {{i18n admin.logs.staff_actions.no_previous}}
+ {{/if}}
+
+
+
+
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.js.handlebars b/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.js.handlebars
index 3da2168aa..7296db2f0 100644
--- a/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.js.handlebars
+++ b/app/assets/javascripts/admin/templates/logs/staff_action_logs_list_item.js.handlebars
@@ -17,11 +17,15 @@
{{unboundAgeWithTooltip created_at}}
{{context}}
diff --git a/app/assets/javascripts/admin/views/modals/change_site_customization_details_view.js b/app/assets/javascripts/admin/views/modals/change_site_customization_details_view.js
new file mode 100644
index 000000000..1bafebf0d
--- /dev/null
+++ b/app/assets/javascripts/admin/views/modals/change_site_customization_details_view.js
@@ -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')
+});
diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss
index 6ff9b8ace..384a4ee74 100644
--- a/app/assets/stylesheets/admin/admin_base.scss
+++ b/app/assets/stylesheets/admin/admin_base.scss
@@ -806,3 +806,9 @@ table {
border-top: solid 1px #ddd;
}
}
+
+.log-details-modal {
+ .modal-tab {
+ width: 95%;
+ }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/application/modal.css.scss b/app/assets/stylesheets/application/modal.css.scss
index 7292b81d9..2e82fa849 100644
--- a/app/assets/stylesheets/application/modal.css.scss
+++ b/app/assets/stylesheets/application/modal.css.scss
@@ -253,6 +253,13 @@
}
}
+.tabbed-modal {
+ .modal-body {
+ position: relative;
+ height: 350px;
+ }
+}
+
.modal-tab {
position: absolute;
}
diff --git a/app/controllers/admin/site_customizations_controller.rb b/app/controllers/admin/site_customizations_controller.rb
index 046926a0b..d55caa076 100644
--- a/app/controllers/admin/site_customizations_controller.rb
+++ b/app/controllers/admin/site_customizations_controller.rb
@@ -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
diff --git a/app/models/staff_action_log.rb b/app/models/staff_action_log.rb
index 1a9ff9bee..4bc3477a7 100644
--- a/app/models/staff_action_log.rb
+++ b/app/models/staff_action_log.rb
@@ -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
diff --git a/app/serializers/staff_action_log_serializer.rb b/app/serializers/staff_action_log_serializer.rb
index 35ec8e79a..5451af8af 100644
--- a/app/serializers/staff_action_log_serializer.rb
+++ b/app/serializers/staff_action_log_serializer.rb
@@ -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
\ No newline at end of file
diff --git a/app/services/staff_action_logger.rb b/app/services/staff_action_logger.rb
index faf9f3843..8ad76453b 100644
--- a/app/services/staff_action_logger.rb
+++ b/app/services/staff_action_logger.rb
@@ -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)
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 1bbc2c835..d00f5318c 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -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."
diff --git a/spec/controllers/admin/site_customizations_controller_spec.rb b/spec/controllers/admin/site_customizations_controller_spec.rb
index e0e58ee80..76700ff3f 100644
--- a/spec/controllers/admin/site_customizations_controller_spec.rb
+++ b/spec/controllers/admin/site_customizations_controller_spec.rb
@@ -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
diff --git a/spec/services/staff_action_logger_spec.rb b/spec/services/staff_action_logger_spec.rb
index d1af03c4a..ca6a9ed5e 100644
--- a/spec/services/staff_action_logger_spec.rb
+++ b/spec/services/staff_action_logger_spec.rb
@@ -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