From 3da37506da146d3170d33b5caf26f83223542929 Mon Sep 17 00:00:00 2001 From: Navin Date: Wed, 3 Jul 2013 10:27:40 +0200 Subject: [PATCH] Back end - temporary boosting of trust levels --- .../javascripts/admin/models/admin_user.js | 7 +-- app/controllers/admin/users_controller.rb | 9 +++- config/locales/client.en.yml | 2 +- config/locales/server.en.yml | 2 + config/routes.rb | 1 + lib/boost_trust_level.rb | 40 ++++++++++++++++ lib/guardian.rb | 4 ++ spec/components/boost_trust_level_spec.rb | 48 +++++++++++++++++++ .../admin/users_controller_spec.rb | 33 +++++++++++++ 9 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 lib/boost_trust_level.rb create mode 100644 spec/components/boost_trust_level_spec.rb diff --git a/app/assets/javascripts/admin/models/admin_user.js b/app/assets/javascripts/admin/models/admin_user.js index 97170627f3f..a144c6c4d22 100644 --- a/app/assets/javascripts/admin/models/admin_user.js +++ b/app/assets/javascripts/admin/models/admin_user.js @@ -70,10 +70,7 @@ Discourse.AdminUser = Discourse.User.extend({ trustLevels: function() { var site = Discourse.Site.instance(); - // downgrading trust levels is not supported - var current = this.get('trust_level'); - var levels = site.get('trust_levels'); - return jQuery.grep(levels, function(e) { return e.id >= current }) + return site.get('trust_levels'); }.property('trust_level'), dirty: function() { @@ -89,7 +86,7 @@ Discourse.AdminUser = Discourse.User.extend({ window.location.reload(); }, function(e) { // failure - var error = Em.String.i18n('admin.user.trustlevel_change_failed', { error: "http: " + e.status + " - " + e.body }); + var error = Em.String.i18n('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body }); bootbox.alert(error); }); }, diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 1c51293b656..6c1e1de5ca7 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -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) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 85ffe5e129f..8fcb2f01055 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1209,7 +1209,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." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index b7d6d51aa82..21b906f1947 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -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." diff --git a/config/routes.rb b/config/routes.rb index 556ea294eb4..7cb1d650f51 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -49,6 +49,7 @@ Discourse::Application.routes.draw do put 'deactivate' put 'block' put 'unblock' + put 'trust_level' end resources :impersonate, constraints: AdminConstraint.new diff --git a/lib/boost_trust_level.rb b/lib/boost_trust_level.rb new file mode 100644 index 00000000000..597f380866e --- /dev/null +++ b/lib/boost_trust_level.rb @@ -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 diff --git a/lib/guardian.rb b/lib/guardian.rb index ed49762376b..4a7589f875a 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -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 diff --git a/spec/components/boost_trust_level_spec.rb b/spec/components/boost_trust_level_spec.rb new file mode 100644 index 00000000000..26412079337 --- /dev/null +++ b/spec/components/boost_trust_level_spec.rb @@ -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 diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 8467594137e..35fbaab5887 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -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)