mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
FEATURE: Allow admins to lock users from TL3 promotion/demotion
Also, update the display logic for the leader promotion screen to account for the demotion grace period.
This commit is contained in:
parent
41d53c7222
commit
c8111ada6e
12 changed files with 121 additions and 18 deletions
|
@ -164,6 +164,24 @@ Discourse.AdminUser = Discourse.User.extend({
|
||||||
this.set('trustLevel.id', this.get('originalTrustLevel'));
|
this.set('trustLevel.id', this.get('originalTrustLevel'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
lockTrustLevel: function(locked) {
|
||||||
|
Discourse.ajax("/admin/users/" + this.id + "/trust_level_lock", {
|
||||||
|
type: 'PUT',
|
||||||
|
data: { locked: !!locked }
|
||||||
|
}).then(function() {
|
||||||
|
// succeeded
|
||||||
|
window.location.reload();
|
||||||
|
}, function(e) {
|
||||||
|
// failure
|
||||||
|
var error;
|
||||||
|
if (e.responseJSON && e.responseJSON.errors) {
|
||||||
|
error = e.responseJSON.errors[0];
|
||||||
|
}
|
||||||
|
error = error || I18n.t('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body });
|
||||||
|
bootbox.alert(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
isSuspended: Em.computed.equal('suspended', true),
|
isSuspended: Em.computed.equal('suspended', true),
|
||||||
canSuspend: Em.computed.not('staff'),
|
canSuspend: Em.computed.not('staff'),
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ Discourse.LeaderRequirements = Discourse.Model.extend({
|
||||||
flagged_posts: this.get('num_flagged_posts') <= this.get('max_flagged_posts'),
|
flagged_posts: this.get('num_flagged_posts') <= this.get('max_flagged_posts'),
|
||||||
flagged_by_users: this.get('num_flagged_by_users') <= this.get('max_flagged_by_users'),
|
flagged_by_users: this.get('num_flagged_by_users') <= this.get('max_flagged_by_users'),
|
||||||
likes_given: this.get('num_likes_given') >= this.get('min_likes_given'),
|
likes_given: this.get('num_likes_given') >= this.get('min_likes_given'),
|
||||||
likes_received: this.get('num_likes_received') >= this.get('min_likes_received')
|
likes_received: this.get('num_likes_received') >= this.get('min_likes_received'),
|
||||||
|
level_locked: this.get('trust_level_locked')
|
||||||
};
|
};
|
||||||
}.property('days_visited', 'min_days_visited',
|
}.property('days_visited', 'min_days_visited',
|
||||||
'num_topics_replied_to', 'min_topics_replied_to',
|
'num_topics_replied_to', 'min_topics_replied_to',
|
||||||
|
@ -29,5 +30,6 @@ Discourse.LeaderRequirements = Discourse.Model.extend({
|
||||||
'posts_read_all_time', 'min_posts_read_all_time',
|
'posts_read_all_time', 'min_posts_read_all_time',
|
||||||
'num_flagged_by_users', 'max_flagged_by_users',
|
'num_flagged_by_users', 'max_flagged_by_users',
|
||||||
'num_likes_given', 'min_likes_given',
|
'num_likes_given', 'min_likes_given',
|
||||||
'num_likes_received', 'min_likes_received')
|
'num_likes_received', 'min_likes_received',
|
||||||
|
'trust_level_locked')
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,5 +10,15 @@
|
||||||
Discourse.AdminUserLeaderRequirementsRoute = Discourse.Route.extend({
|
Discourse.AdminUserLeaderRequirementsRoute = Discourse.Route.extend({
|
||||||
model: function() {
|
model: function() {
|
||||||
return this.modelFor('adminUser');
|
return this.modelFor('adminUser');
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
lock_trust_level: function() {
|
||||||
|
this.modelFor('adminUser').lockTrustLevel(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
unlock_trust_level: function() {
|
||||||
|
this.modelFor('adminUser').lockTrustLevel(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -85,21 +85,50 @@
|
||||||
<td>{{num_likes_received}}</td>
|
<td>{{num_likes_received}}</td>
|
||||||
<td>{{min_likes_received}}</td>
|
<td>{{min_likes_received}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{i18n admin.user.tl3_requirements.trust_level_locked}}</th>
|
||||||
|
<td><i {{bind-attr class=":fa met.level_locked:fa-lock:fa-unlock"}}></i></td>
|
||||||
|
{{#if trust_level_locked}}
|
||||||
|
<td>{{i18n yes_value}}</td>
|
||||||
|
<td><a class="btn" {{action unlock_trust_level}}>{{i18n admin.user.tl3_requirements.unlock_tl}}</a></td>
|
||||||
|
{{else}}
|
||||||
|
<td>{{i18n no_value}}</td>
|
||||||
|
<td><a class="btn" {{action lock_trust_level}}>{{i18n admin.user.tl3_requirements.lock_tl}}</a></td>
|
||||||
|
{{/if}}
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<p>
|
<p>
|
||||||
{{#if leaderRequirements.requirements_met}}
|
{{#if isLeader}}
|
||||||
|
{{#if leaderRequirements.requirements_lost}}
|
||||||
|
{{! tl implicitly not locked }}
|
||||||
|
{{#if leaderRequirements.on_grace_period}}
|
||||||
|
<i class="fa fa-times"></i> {{i18n admin.user.tl3_requirements.on_grace_period}}
|
||||||
|
{{else}} {{! not on grace period }}
|
||||||
|
<i class="fa fa-times"></i> {{i18n admin.user.tl3_requirements.does_not_qualify}}
|
||||||
|
{{i18n admin.user.tl3_requirements.will_be_demoted}}
|
||||||
|
{{/if}}
|
||||||
|
{{else}} {{! requirements not lost - remains leader }}
|
||||||
|
{{#if leaderRequirements.trust_level_locked}}
|
||||||
|
<i class="fa fa-lock"></i> {{i18n admin.user.tl3_requirements.locked_will_not_be_demoted}}
|
||||||
|
{{else}} {{! tl not locked }}
|
||||||
|
<i class="fa fa-check"></i> {{i18n admin.user.tl3_requirements.qualifies}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{else}} {{! is not leader }}
|
||||||
|
{{#if leaderRequirements.requirements_met}}
|
||||||
|
{{! met & not leader - will be promoted}}
|
||||||
<i class="fa fa-check"></i> {{i18n admin.user.tl3_requirements.qualifies}}
|
<i class="fa fa-check"></i> {{i18n admin.user.tl3_requirements.qualifies}}
|
||||||
{{#unless isLeader}}
|
|
||||||
{{i18n admin.user.tl3_requirements.will_be_promoted}}
|
{{i18n admin.user.tl3_requirements.will_be_promoted}}
|
||||||
{{/unless}}
|
{{else}} {{! requirements not met - remains regular }}
|
||||||
|
{{#if leaderRequirements.trust_level_locked}}
|
||||||
|
<i class="fa fa-lock"></i> {{i18n admin.user.tl3_requirements.locked_will_not_be_promoted}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class="fa fa-times"></i> {{i18n admin.user.tl3_requirements.does_not_qualify}}
|
<i class="fa fa-times"></i> {{i18n admin.user.tl3_requirements.does_not_qualify}}
|
||||||
{{#if suspended}}
|
{{/if}}
|
||||||
{{i18n user.suspended_notice date="suspendedTillDate"}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Admin::UsersController < Admin::AdminController
|
||||||
:block,
|
:block,
|
||||||
:unblock,
|
:unblock,
|
||||||
:trust_level,
|
:trust_level,
|
||||||
|
:trust_level_lock,
|
||||||
:add_group,
|
:add_group,
|
||||||
:remove_group,
|
:remove_group,
|
||||||
:primary_group,
|
:primary_group,
|
||||||
|
@ -134,6 +135,19 @@ class Admin::UsersController < Admin::AdminController
|
||||||
render_json_error(e.message)
|
render_json_error(e.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def trust_level_lock
|
||||||
|
guardian.ensure_can_change_trust_level!(@user)
|
||||||
|
|
||||||
|
new_lock = params[:locked]
|
||||||
|
unless new_lock =~ /t|f|true|false/
|
||||||
|
return render_json_error I18n.t('errors.invalid_boolaen')
|
||||||
|
end
|
||||||
|
|
||||||
|
@user.trust_level_locked = !!(new_lock =~ /t|true/)
|
||||||
|
@user.save
|
||||||
|
render nothing: true
|
||||||
|
end
|
||||||
|
|
||||||
def approve
|
def approve
|
||||||
guardian.ensure_can_approve!(@user)
|
guardian.ensure_can_approve!(@user)
|
||||||
@user.approve(current_user)
|
@user.approve(current_user)
|
||||||
|
|
|
@ -8,11 +8,7 @@ module Jobs
|
||||||
demoted_user_ids = []
|
demoted_user_ids = []
|
||||||
User.real.where(trust_level: TrustLevel[3]).find_each do |u|
|
User.real.where(trust_level: TrustLevel[3]).find_each do |u|
|
||||||
# Don't demote too soon after being promoted
|
# Don't demote too soon after being promoted
|
||||||
next if UserHistory.for(u, :auto_trust_level_change)
|
next if user.on_leader_grace_period?
|
||||||
.where('created_at >= ?', SiteSetting.tl3_promotion_min_duration.to_i.days.ago)
|
|
||||||
.where(previous_value: TrustLevel[2].to_s)
|
|
||||||
.where(new_value: TrustLevel[3].to_s)
|
|
||||||
.exists?
|
|
||||||
|
|
||||||
if Promotion.tl3_lost?(u)
|
if Promotion.tl3_lost?(u)
|
||||||
demoted_user_ids << u.id
|
demoted_user_ids << u.id
|
||||||
|
|
|
@ -16,13 +16,15 @@ class TrustLevel3Requirements
|
||||||
:posts_read_all_time, :min_posts_read_all_time,
|
:posts_read_all_time, :min_posts_read_all_time,
|
||||||
:num_flagged_posts, :max_flagged_posts,
|
:num_flagged_posts, :max_flagged_posts,
|
||||||
:num_likes_given, :min_likes_given,
|
:num_likes_given, :min_likes_given,
|
||||||
:num_likes_received, :min_likes_received
|
:num_likes_received, :min_likes_received,
|
||||||
|
:trust_level_locked, :on_grace_period
|
||||||
|
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
@user = user
|
@user = user
|
||||||
end
|
end
|
||||||
|
|
||||||
def requirements_met?
|
def requirements_met?
|
||||||
|
return false if trust_level_locked
|
||||||
!@user.suspended? &&
|
!@user.suspended? &&
|
||||||
days_visited >= min_days_visited &&
|
days_visited >= min_days_visited &&
|
||||||
num_topics_replied_to >= min_topics_replied_to &&
|
num_topics_replied_to >= min_topics_replied_to &&
|
||||||
|
@ -37,6 +39,7 @@ class TrustLevel3Requirements
|
||||||
end
|
end
|
||||||
|
|
||||||
def requirements_lost?
|
def requirements_lost?
|
||||||
|
return false if trust_level_locked
|
||||||
@user.suspended? ||
|
@user.suspended? ||
|
||||||
days_visited < min_days_visited * LOW_WATER_MARK ||
|
days_visited < min_days_visited * LOW_WATER_MARK ||
|
||||||
num_topics_replied_to < min_topics_replied_to * LOW_WATER_MARK ||
|
num_topics_replied_to < min_topics_replied_to * LOW_WATER_MARK ||
|
||||||
|
@ -50,6 +53,14 @@ class TrustLevel3Requirements
|
||||||
num_likes_received < min_likes_received * LOW_WATER_MARK
|
num_likes_received < min_likes_received * LOW_WATER_MARK
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def trust_level_locked
|
||||||
|
@user.trust_level_locked
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_grace_period
|
||||||
|
@user.on_leader_grace_period?
|
||||||
|
end
|
||||||
|
|
||||||
def days_visited
|
def days_visited
|
||||||
@user.user_visits.where("visited_at > ? and posts_read > 0", TIME_PERIOD.days.ago).count
|
@user.user_visits.where("visited_at > ? and posts_read > 0", TIME_PERIOD.days.ago).count
|
||||||
end
|
end
|
||||||
|
|
|
@ -574,6 +574,14 @@ class User < ActiveRecord::Base
|
||||||
@lq ||= TrustLevel3Requirements.new(self)
|
@lq ||= TrustLevel3Requirements.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def on_leader_grace_period?
|
||||||
|
UserHistory.for(self, :auto_trust_level_change)
|
||||||
|
.where('created_at >= ?', SiteSetting.tl3_promotion_min_duration.to_i.days.ago)
|
||||||
|
.where(previous_value: TrustLevel[2].to_s)
|
||||||
|
.where(new_value: TrustLevel[3].to_s)
|
||||||
|
.exists?
|
||||||
|
end
|
||||||
|
|
||||||
def should_be_redirected_to_top
|
def should_be_redirected_to_top
|
||||||
redirected_to_top_reason.present?
|
redirected_to_top_reason.present?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
class TrustLevel3RequirementsSerializer < ApplicationSerializer
|
class TrustLevel3RequirementsSerializer < ApplicationSerializer
|
||||||
attributes :time_period,
|
attributes :time_period,
|
||||||
:requirements_met,
|
:requirements_met,
|
||||||
|
:requirements_lost,
|
||||||
|
:trust_level_locked, :on_grace_period,
|
||||||
:days_visited, :min_days_visited,
|
:days_visited, :min_days_visited,
|
||||||
:num_topics_replied_to, :min_topics_replied_to,
|
:num_topics_replied_to, :min_topics_replied_to,
|
||||||
:topics_viewed, :min_topics_viewed,
|
:topics_viewed, :min_topics_viewed,
|
||||||
|
@ -19,4 +21,8 @@ class TrustLevel3RequirementsSerializer < ApplicationSerializer
|
||||||
def requirements_met
|
def requirements_met
|
||||||
object.requirements_met?
|
object.requirements_met?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def requirements_lost
|
||||||
|
object.requirements_lost?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1975,9 +1975,16 @@ en:
|
||||||
flagged_by_users: "Users Who Flagged"
|
flagged_by_users: "Users Who Flagged"
|
||||||
likes_given: "Likes Given"
|
likes_given: "Likes Given"
|
||||||
likes_received: "Likes Received"
|
likes_received: "Likes Received"
|
||||||
|
trust_level_locked: "Trust Level Locked"
|
||||||
|
lock_tl: "Lock"
|
||||||
|
unlock_tl: "Unlock"
|
||||||
qualifies: "Qualifies for trust level 3."
|
qualifies: "Qualifies for trust level 3."
|
||||||
will_be_promoted: "Will be promoted within 24 hours."
|
|
||||||
does_not_qualify: "Doesn't qualify for trust level 3."
|
does_not_qualify: "Doesn't qualify for trust level 3."
|
||||||
|
will_be_promoted: "Will be promoted within 24 hours."
|
||||||
|
will_be_demoted: "Will be demoted within 24 hours."
|
||||||
|
on_grace_period: "Currently in promotion grace period, will not be demoted."
|
||||||
|
locked_will_not_be_promoted: "Trust level locked. Will never be promoted."
|
||||||
|
locked_will_not_be_demoted: "Trust level locked. Will never be demoted."
|
||||||
sso:
|
sso:
|
||||||
title: "Single Sign On"
|
title: "Single Sign On"
|
||||||
external_id: "External ID"
|
external_id: "External ID"
|
||||||
|
|
|
@ -41,6 +41,7 @@ en:
|
||||||
errors:
|
errors:
|
||||||
messages:
|
messages:
|
||||||
too_long_validation: "is limited to %{max} characters; you entered %{length}."
|
too_long_validation: "is limited to %{max} characters; you entered %{length}."
|
||||||
|
invalid_boolean: "Invalid boolean."
|
||||||
embed:
|
embed:
|
||||||
load_from_remote: "There was an error loading that post."
|
load_from_remote: "There was an error loading that post."
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ Discourse::Application.routes.draw do
|
||||||
put "block"
|
put "block"
|
||||||
put "unblock"
|
put "unblock"
|
||||||
put "trust_level"
|
put "trust_level"
|
||||||
|
put "trust_level_lock"
|
||||||
put "primary_group"
|
put "primary_group"
|
||||||
post "groups" => "users#add_group", constraints: AdminConstraint.new
|
post "groups" => "users#add_group", constraints: AdminConstraint.new
|
||||||
delete "groups/:group_id" => "users#remove_group", constraints: AdminConstraint.new
|
delete "groups/:group_id" => "users#remove_group", constraints: AdminConstraint.new
|
||||||
|
|
Loading…
Reference in a new issue