Merge pull request from novemberkilo/feature/temporary-boost-trust-levels

Feature: Admin can modify user trust levels
This commit is contained in:
Robin Ward 2013-07-04 06:57:59 -07:00
commit ea11286ab6
11 changed files with 186 additions and 3 deletions

View file

@ -67,6 +67,37 @@ Discourse.AdminUser = Discourse.User.extend({
return site.get('trust_levels').findProperty('id', this.get('trust_level'));
}.property('trust_level'),
setOriginalTrustLevel: function() {
this.set('originalTrustLevel', this.get('trust_level'));
},
trustLevels: function() {
var site = Discourse.Site.instance();
return site.get('trust_levels');
}.property('trust_level'),
dirty: function() {
return this.get('originalTrustLevel') !== parseInt(this.get('trustLevel.id'), 10);
}.property('originalTrustLevel', 'trustLevel.id'),
saveTrustLevel: function() {
Discourse.ajax("/admin/users/" + this.id + "/trust_level", {
type: 'PUT',
data: {level: this.get('trustLevel.id')}
}).then(function () {
// succeeded
window.location.reload();
}, function(e) {
// failure
var error = Em.String.i18n('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error);
});
},
restoreTrustLevel: function() {
this.set('trustLevel.id', this.get('originalTrustLevel'));
},
isBanned: (function() {
return this.get('is_banned') === true;
}).property('is_banned'),

View file

@ -16,6 +16,11 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
},
setupController: function(controller, model) {
controller.set('model', model);
model.setOriginalTrustLevel();
},
renderTemplate: function() {
this.render({into: 'admin/templates/admin'});
},

View file

@ -161,10 +161,22 @@
</div>
</div>
<div class='display-row'>
<div class='field'>{{i18n trust_level}}</div>
<div class='value'>{{trustLevel.name}}</div>
<div class="value">
{{combobox content=trustLevels value=trustLevel.id }}
</div>
<div class="controls">
{{#if dirty}}
<div>
<button class='btn ok' {{action saveTrustLevel target="content"}}><i class='icon-ok'></i></button>
<button class='btn cancel' {{action restoreTrustLevel target="content"}}><i class='icon-remove'></i></button>
</div>
{{/if}}
</div>
</div>
<div class='display-row'>
<div class='field'>{{i18n admin.user.banned}}</div>
<div class='value'>{{isBanned}}</div>

View file

@ -1,9 +1,10 @@
require_dependency 'user_destroyer'
require_dependency 'admin_user_index_query'
require_dependency 'boost_trust_level'
class Admin::UsersController < Admin::AdminController
before_filter :fetch_user, only: [:ban, :unban, :refresh_browsers, :revoke_admin, :grant_admin, :revoke_moderation, :grant_moderation, :approve, :activate, :deactivate, :block, :unblock]
before_filter :fetch_user, only: [:ban, :unban, :refresh_browsers, :revoke_admin, :grant_admin, :revoke_moderation, :grant_moderation, :approve, :activate, :deactivate, :block, :unblock, :trust_level]
def index
query = ::AdminUserIndexQuery.new(params)
@ -69,6 +70,12 @@ class Admin::UsersController < Admin::AdminController
render_serialized(@user, AdminUserSerializer)
end
def trust_level
guardian.ensure_can_change_trust_level!(@user)
BoostTrustLevel.new(@user, params[:level]).save!
render_serialized(@user, AdminUserSerializer)
end
def approve
guardian.ensure_can_approve!(@user)
@user.approve(current_user)

View file

@ -1207,7 +1207,7 @@ en:
deactivate_explanation: "A deactivated user must re-validate their email."
banned_explanation: "A banned user can't log in."
block_explanation: "A blocked user can't post or start topics."
trust_level_change_failed: "There was a problem changing the user's trust level."
site_content:
none: "Choose a type of content to begin editing."

View file

@ -134,6 +134,8 @@ en:
title: "leader"
elder:
title: "elder"
change_failed_explanation: "You attempted to demote %{user_name} to '%{new_trust_level}'. However their trust level is already '%{current_trust_level}'. %{user_name} will remain at '%{current_trust_level}'"
rate_limiter:
too_many_requests: "You're doing that too often. Please wait %{time_left} before trying again."

View file

@ -49,6 +49,7 @@ Discourse::Application.routes.draw do
put 'deactivate'
put 'block'
put 'unblock'
put 'trust_level'
end
resources :impersonate, constraints: AdminConstraint.new

40
lib/boost_trust_level.rb Normal file
View file

@ -0,0 +1,40 @@
require_dependency 'promotion'
class BoostTrustLevel
def initialize(user, level)
@user = user
@level = level.to_i
@promotion = Promotion.new(@user)
@trust_levels = TrustLevel.levels
end
def save!
if @level < @user.trust_level
demote!
else
@user.update_attributes!(trust_level: @level)
end
end
protected
def demote!
current_trust_level = @user.trust_level
@user.update_attributes!(trust_level: @level)
if @promotion.review
@user.update_attributes!(trust_level: current_trust_level)
raise Discourse::InvalidAccess.new, I18n.t('trust_levels.change_failed_explanation',
user_name: @user.name,
new_trust_level: trust_level_lookup(@level),
current_trust_level: trust_level_lookup(current_trust_level))
else
true
end
end
def trust_level_lookup(level)
@trust_levels.key(level).id2name
end
end

View file

@ -137,6 +137,10 @@ class Guardian
user && is_staff?
end
def can_change_trust_level?(user)
can_administer?(user)
end
def can_block_user?(user)
user && is_staff? && not(user.staff?)
end

View file

@ -0,0 +1,48 @@
require 'spec_helper'
require 'boost_trust_level'
describe BoostTrustLevel do
let(:user) { Fabricate(:user) }
it "should upgrade the trust level of a user" do
boostr = BoostTrustLevel.new(user, TrustLevel.levels[:basic])
boostr.save!.should be_true
user.trust_level.should == TrustLevel.levels[:basic]
end
describe "demotions" do
before { user.update_attributes(trust_level: TrustLevel.levels[:newuser]) }
context "for a user that has not done the requisite things to attain their trust level" do
before do
# scenario: admin mistakenly promotes user's trust level
user.update_attributes(trust_level: TrustLevel.levels[:basic])
end
it "should demote the user" do
boostr = BoostTrustLevel.new(user, TrustLevel.levels[:newuser])
boostr.save!.should be_true
user.trust_level.should == TrustLevel.levels[:newuser]
end
end
context "for a user that has done the requisite things to attain their trust level" do
before do
user.topics_entered = SiteSetting.basic_requires_topics_entered + 1
user.posts_read_count = SiteSetting.basic_requires_read_posts + 1
user.time_read = SiteSetting.basic_requires_time_spent_mins * 60
user.save!
user.update_attributes(trust_level: TrustLevel.levels[:basic])
end
it "should not demote the user" do
boostr = BoostTrustLevel.new(user, TrustLevel.levels[:newuser])
expect { boostr.save! }.to raise_error(Discourse::InvalidAccess, "You attempted to demote #{user.name} to 'newuser'. However their trust level is already 'basic'. #{user.name} will remain at 'basic'")
user.trust_level.should == TrustLevel.levels[:basic]
end
end
end
end

View file

@ -120,6 +120,39 @@ describe Admin::UsersController do
end
end
context '.trust_level' do
before do
@another_user = Fabricate(:coding_horror)
end
it "raises an error when the user doesn't have permission" do
Guardian.any_instance.expects(:can_change_trust_level?).with(@another_user).returns(false)
xhr :put, :trust_level, user_id: @another_user.id
response.should be_forbidden
end
it "returns a 404 if the username doesn't exist" do
xhr :put, :trust_level, user_id: 123123
response.should be_forbidden
end
it "upgrades the user's trust level" do
xhr :put, :trust_level, user_id: @another_user.id, level: 2
@another_user.reload
@another_user.trust_level.should == 2
end
it "raises an error when demoting a user below their current trust level" do
@another_user.topics_entered = SiteSetting.basic_requires_topics_entered + 1
@another_user.posts_read_count = SiteSetting.basic_requires_read_posts + 1
@another_user.time_read = SiteSetting.basic_requires_time_spent_mins * 60
@another_user.save!
@another_user.update_attributes(trust_level: TrustLevel.levels[:basic])
xhr :put, :trust_level, user_id: @another_user.id, level: TrustLevel.levels[:newuser]
response.should be_forbidden
end
end
describe '.revoke_moderation' do
before do
@moderator = Fabricate(:moderator)