diff --git a/app/assets/javascripts/admin/controllers/admin_user_controller.js b/app/assets/javascripts/admin/controllers/admin_user_controller.js
index 70a3d520e01..aac997c155e 100644
--- a/app/assets/javascripts/admin/controllers/admin_user_controller.js
+++ b/app/assets/javascripts/admin/controllers/admin_user_controller.js
@@ -6,7 +6,7 @@
@namespace Discourse
@module Discourse
**/
-Discourse.AdminUserController = Discourse.ObjectController.extend({
+Discourse.AdminUserIndexController = Discourse.ObjectController.extend({
editingTitle: false,
showApproval: function() {
diff --git a/app/assets/javascripts/admin/routes/admin_routes.js b/app/assets/javascripts/admin/routes/admin_routes.js
index 11e2ece7af9..fadb8d19e8c 100644
--- a/app/assets/javascripts/admin/routes/admin_routes.js
+++ b/app/assets/javascripts/admin/routes/admin_routes.js
@@ -42,7 +42,9 @@ Discourse.Route.buildRoutes(function() {
this.route('groups', {path: '/groups'});
this.resource('adminUsers', { path: '/users' }, function() {
- this.resource('adminUser', { path: '/:username' });
+ this.resource('adminUser', { path: '/:username' }, function() {
+ this.route('leaderRequirements', { path: '/leader_requirements' });
+ });
this.resource('adminUsersList', { path: '/list' }, function() {
_.each(['active', 'new', 'pending', 'admins', 'moderators', 'blocked', 'suspended',
'newuser', 'basic', 'regular', 'leaders', 'elders'], function(x) {
diff --git a/app/assets/javascripts/admin/routes/admin_user_leader_requirements_route.js b/app/assets/javascripts/admin/routes/admin_user_leader_requirements_route.js
new file mode 100644
index 00000000000..9f75ed5d136
--- /dev/null
+++ b/app/assets/javascripts/admin/routes/admin_user_leader_requirements_route.js
@@ -0,0 +1,14 @@
+/**
+ Shows all the requirements for being at trust level 3 and if the
+ given user is meeting them.
+
+ @class AdminUserLeaderRequirementsRoute
+ @extends Discourse.Route
+ @namespace Discourse
+ @module Discourse
+**/
+Discourse.AdminUserLeaderRequirementsRoute = Discourse.Route.extend({
+ model: function() {
+ return this.controllerFor('adminUser').get('model');
+ }
+});
diff --git a/app/assets/javascripts/admin/routes/admin_user_route.js b/app/assets/javascripts/admin/routes/admin_user_route.js
index 65192f6ec4d..4a2086c7db4 100644
--- a/app/assets/javascripts/admin/routes/admin_user_route.js
+++ b/app/assets/javascripts/admin/routes/admin_user_route.js
@@ -38,3 +38,9 @@ Discourse.AdminUserRoute = Discourse.Route.extend({
}
});
+
+Discourse.AdminUserIndexRoute = Discourse.Route.extend({
+ setupController: function(c) {
+ c.set('model', this.controllerFor('adminUser').get('model'));
+ }
+});
diff --git a/app/assets/javascripts/admin/templates/user.js.handlebars b/app/assets/javascripts/admin/templates/user_index.js.handlebars
similarity index 95%
rename from app/assets/javascripts/admin/templates/user.js.handlebars
rename to app/assets/javascripts/admin/templates/user_index.js.handlebars
index fe0d8276dac..eb26722b431 100644
--- a/app/assets/javascripts/admin/templates/user.js.handlebars
+++ b/app/assets/javascripts/admin/templates/user_index.js.handlebars
@@ -189,12 +189,16 @@
{{combobox content=trustLevels value=trust_level nameProperty="detailedName"}}
- {{#if dirty}}
-
-
-
-
- {{/if}}
+ {{#if leader_requirements}}
+ {{#link-to 'adminUser.leaderRequirements' this class="btn"}}{{i18n admin.user.trust_level_3_requirements}}{{/link-to}}
+ {{/if}}
+
+ {{#if dirty}}
+
+
+
+
+ {{/if}}
diff --git a/app/assets/javascripts/admin/templates/user_leader_requirements.js.handlebars b/app/assets/javascripts/admin/templates/user_leader_requirements.js.handlebars
new file mode 100644
index 00000000000..9054168a8bb
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/user_leader_requirements.js.handlebars
@@ -0,0 +1,64 @@
+
+
+
+ - {{#link-to 'adminUser' this}}{{i18n go_back}}{{/link-to}}
+
+
+
+
+
+
{{username}} - {{i18n admin.user.tl3_requirements.title}}
+
+
{{i18n admin.user.tl3_requirements.table_title}}
+
+ {{#with leader_requirements}}
+
+
+
+ | {{i18n admin.user.tl3_requirements.visits}} |
+
+ {{days_visited_percent}}% ({{days_visited}} / {{time_period}} {{i18n admin.user.tl3_requirements.days}})
+ |
+
+
+ | {{i18n admin.user.tl3_requirements.topics_with_replies}} |
+
+ {{num_topics_with_replies}}
+ |
+
+
+ | {{i18n admin.user.tl3_requirements.topics_replied_to}} |
+
+ {{num_topics_replied_to}}
+ |
+
+
+ | {{i18n admin.user.tl3_requirements.quality_content}} |
+
+ TODO
+ |
+
+
+ | {{i18n admin.user.tl3_requirements.reading}} |
+
+ TODO
+ |
+
+
+ | {{i18n admin.user.tl3_requirements.site_promotion}} |
+
+ TODO
+ |
+
+
+ | {{i18n admin.user.tl3_requirements.flagged_posts}} |
+
+ {{num_flagged_posts}}
+ |
+
+
+
+ {{/with}}
+
+
+
diff --git a/app/models/leader_requirements.rb b/app/models/leader_requirements.rb
new file mode 100644
index 00000000000..b92ae2a0838
--- /dev/null
+++ b/app/models/leader_requirements.rb
@@ -0,0 +1,34 @@
+# This class performs calculations to determine if a user qualifies for
+# the Leader (3) trust level.
+class LeaderRequirements
+
+ include ActiveModel::Serialization
+
+ attr_accessor :days_visited, :time_period, :num_topics_with_replies, :num_topics_replied_to,
+ :num_flagged_posts
+
+ def initialize(user)
+ @user = user
+ end
+
+ def time_period
+ 100 # days
+ end
+
+ def days_visited
+ # This is naive. The user may have visited the site, but not read any posts.
+ @user.user_visits.where("visited_at >= ?", time_period.days.ago).count
+ end
+
+ def num_topics_with_replies
+ @user.topics.where('posts_count > 1 AND participant_count > 1 AND created_at > ?', time_period.days.ago).count
+ end
+
+ def num_topics_replied_to
+ @user.posts.select('distinct topic_id').where('created_at > ? AND post_number > 1', time_period.days.ago).count
+ end
+
+ def num_flagged_posts
+ @user.posts.where('created_at > ? AND (off_topic_count > 0 OR spam_count > 0 OR illegal_count > 0 OR inappropriate_count > 0 OR notify_moderators_count > 0)', time_period.days.ago).count
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index ba82d50f41c..d58f3925d42 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -527,6 +527,10 @@ class User < ActiveRecord::Base
last_sent_email_address || email
end
+ def leader_requirements
+ @lq ||= LeaderRequirements.new(self)
+ end
+
protected
def cook
diff --git a/app/serializers/admin_detailed_user_serializer.rb b/app/serializers/admin_detailed_user_serializer.rb
index 27717311bb4..404cab2dafe 100644
--- a/app/serializers/admin_detailed_user_serializer.rb
+++ b/app/serializers/admin_detailed_user_serializer.rb
@@ -19,6 +19,7 @@ class AdminDetailedUserSerializer < AdminUserSerializer
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
has_one :api_key, serializer: ApiKeySerializer, embed: :objects
has_one :suspended_by, serializer: BasicUserSerializer, embed: :objects
+ has_one :leader_requirements, serializer: LeaderRequirementsSerializer, embed: :objects
def can_revoke_admin
scope.can_revoke_admin?(object)
@@ -60,4 +61,12 @@ class AdminDetailedUserSerializer < AdminUserSerializer
object.suspend_record.try(:acting_user)
end
+ def leader_requirements
+ object.leader_requirements
+ end
+
+ def include_leader_requirements?
+ object.trust_level == TrustLevel.levels[:regular]
+ end
+
end
diff --git a/app/serializers/leader_requirements_serializer.rb b/app/serializers/leader_requirements_serializer.rb
new file mode 100644
index 00000000000..a1df3a8d5a3
--- /dev/null
+++ b/app/serializers/leader_requirements_serializer.rb
@@ -0,0 +1,8 @@
+class LeaderRequirementsSerializer < ApplicationSerializer
+ attributes :time_period, :days_visited, :days_visited_percent,
+ :num_topics_with_replies, :num_topics_replied_to, :num_flagged_posts
+
+ def days_visited_percent
+ (days_visited * 100) / time_period
+ end
+end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index cde7b01edf8..a6360eb5e7e 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1486,6 +1486,18 @@ en:
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."
suspend_modal_title: "Suspend User"
+ trust_level_3_requirements: "Trust Level 3 Requirements"
+ tl3_requirements:
+ title: "Requirements for Trust Level 3"
+ table_title: "In the last 100 days:"
+ visits: "Visits"
+ days: "days"
+ topics_with_replies: "Topics with Replies"
+ topics_replied_to: "Topics Replied To"
+ quality_content: "Quality Content"
+ reading: "Reading"
+ site_promotion: "Site Promotion"
+ flagged_posts: "Flagged Posts"
site_content:
none: "Choose a type of content to begin editing."