diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index a378f907997..ada4943a005 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -63,6 +63,22 @@ class Admin::UsersController < Admin::AdminController
render_serialized(@user, AdminUserSerializer)
end
+ def revoke_moderation
+ @moderator = User.where(id: params[:user_id]).first
+ guardian.ensure_can_revoke_moderation!(@moderator)
+ @moderator.change_trust_level(:advanced)
+ @moderator.save
+ render nothing: true
+ end
+
+ def grant_moderation
+ @user = User.where(id: params[:user_id]).first
+ guardian.ensure_can_grant_moderation!(@user)
+ @user.change_trust_level(:moderator)
+ @user.save
+ render_serialized(@user, AdminUserSerializer)
+ end
+
def approve
@user = User.where(id: params[:user_id]).first
guardian.ensure_can_approve!(@user)
diff --git a/app/models/user.rb b/app/models/user.rb
index 56cdff20ade..5d484acff3d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -401,6 +401,11 @@ class User < ActiveRecord::Base
(self.trust_level || TrustLevel.Levels[:new]) >= TrustLevel.Levels[level]
end
+ def change_trust_level(level)
+ raise "Invalid trust level #{level}" unless TrustLevel.Levels.has_key?(level)
+ self.trust_level = TrustLevel.Levels[level]
+ end
+
def guardian
Guardian.new(self)
end
diff --git a/app/serializers/admin_detailed_user_serializer.rb b/app/serializers/admin_detailed_user_serializer.rb
index 24694d11b50..ab458b4301c 100644
--- a/app/serializers/admin_detailed_user_serializer.rb
+++ b/app/serializers/admin_detailed_user_serializer.rb
@@ -2,8 +2,10 @@ class AdminDetailedUserSerializer < AdminUserSerializer
attributes :moderator,
:can_grant_admin,
- :can_impersonate,
:can_revoke_admin,
+ :can_grant_moderation,
+ :can_revoke_moderation,
+ :can_impersonate,
:like_count,
:post_count,
:flags_given_count,
@@ -21,6 +23,14 @@ class AdminDetailedUserSerializer < AdminUserSerializer
scope.can_grant_admin?(object)
end
+ def can_revoke_moderation
+ scope.can_revoke_moderation?(object)
+ end
+
+ def can_grant_moderation
+ scope.can_grant_moderation?(object)
+ end
+
def can_delete_all_posts
scope.can_delete_all_posts?(object)
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a1bd47e8b3a..920e5f16347 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -390,6 +390,8 @@ en:
impersonate: 'Impersonate'
revoke_admin: 'Revoke Admin'
grant_admin: 'Grant Admin'
+ revoke_moderation: 'Revoke Moderation'
+ grant_moderation: 'Grant Moderation'
basics: Basics
reputation: Reputation
permissions: Permissions
diff --git a/config/routes.rb b/config/routes.rb
index a5980b3e695..855654b7279 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -34,6 +34,8 @@ Discourse::Application.routes.draw do
put 'unban' => 'users#unban'
put 'revoke_admin' => 'users#revoke_admin'
put 'grant_admin' => 'users#grant_admin'
+ put 'revoke_moderation' => 'users#revoke_moderation'
+ put 'grant_moderation' => 'users#grant_moderation'
put 'approve' => 'users#approve'
post 'refresh_browsers' => 'users#refresh_browsers'
end
diff --git a/lib/guardian.rb b/lib/guardian.rb
index d8df070ffac..7577afaf2fe 100644
--- a/lib/guardian.rb
+++ b/lib/guardian.rb
@@ -131,6 +131,23 @@ class Guardian
true
end
+ def can_revoke_moderation?(moderator)
+ return false unless @user.try(:admin?)
+ return false if moderator.blank?
+ return false if @user.id == moderator.id
+ return false unless moderator.trust_level == TrustLevel.Levels[:moderator]
+ true
+ end
+
+ def can_grant_moderation?(user)
+ return false unless @user.try(:admin?)
+ return false if user.blank?
+ return false if @user.id == user.id
+ return false if user.admin?
+ return false if user.has_trust_level?(:moderator)
+ true
+ end
+
# Can we see who acted on a post in a particular way?
def can_see_post_actors?(topic, post_action_type_id)
return false unless topic.present?
diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb
index 4c12e0e8de8..a63005a034b 100644
--- a/spec/components/guardian_spec.rb
+++ b/spec/components/guardian_spec.rb
@@ -761,6 +761,46 @@ describe Guardian do
end
end
+ context 'can_grant_moderation?' do
+ it "wont allow a non logged in user to grant an moderator's access" do
+ Guardian.new.can_grant_moderation?(user).should be_false
+ end
+
+ it "wont allow a regular user to revoke an modearator's access" do
+ Guardian.new(user).can_grant_moderation?(moderator).should be_false
+ end
+
+ it 'wont allow an admin to grant their own access' do
+ Guardian.new(admin).can_grant_moderation?(admin).should be_false
+ end
+
+ it 'wont allow an admin to grant it to an already moderator' do
+ Guardian.new(admin).can_grant_moderation?(moderator).should be_false
+ end
+
+ it "allows an admin to grant a regular user access" do
+ Guardian.new(admin).can_grant_moderation?(user).should be_true
+ end
+ end
+
+ context 'can_revoke_moderation?' do
+ it "wont allow a non logged in user to revoke an moderator's access" do
+ Guardian.new.can_revoke_moderation?(moderator).should be_false
+ end
+
+ it "wont allow a regular user to revoke an moderator's access" do
+ Guardian.new(user).can_revoke_moderation?(moderator).should be_false
+ end
+
+ it 'wont allow an moderator to revoke their own moderator' do
+ Guardian.new(moderator).can_revoke_moderation?(moderator).should be_false
+ end
+
+ it "allows an admin to revoke a moderator's access" do
+ Guardian.new(admin).can_revoke_moderation?(moderator).should be_true
+ end
+ end
+
context "can_see_pending_invites_from?" do
it 'is false without a logged in user' do
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 1268fddba62..e882b7ab86f 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -111,8 +111,47 @@ describe Admin::UsersController do
end
end
+ describe '.revoke_moderation' do
+ before do
+ @moderator = Fabricate(:moderator)
+ end
+
+ it 'raises an error unless the user can revoke access' do
+ Guardian.any_instance.expects(:can_revoke_moderation?).with(@moderator).returns(false)
+ xhr :put, :revoke_moderation, user_id: @moderator.id
+ response.should be_forbidden
+ end
+
+ it 'updates the moderator flag' do
+ xhr :put, :revoke_moderation, user_id: @moderator.id
+ @moderator.reload
+ @moderator.has_trust_level?(:moderator).should_not be_true
+ end
+ end
+
+ context '.grant_moderation' 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_grant_moderation?).with(@another_user).returns(false)
+ xhr :put, :grant_moderation, user_id: @another_user.id
+ response.should be_forbidden
+ end
+
+ it "returns a 404 if the username doesn't exist" do
+ xhr :put, :grant_moderation, user_id: 123123
+ response.should be_forbidden
+ end
+
+ it 'updates the moderator flag' do
+ xhr :put, :grant_moderation, user_id: @another_user.id
+ @another_user.reload
+ @another_user.has_trust_level?(:moderator).should be_true
+ end
+ end
+
end
-
-
-end
\ No newline at end of file
+end