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 @@ +
+
+ +
+
+ +
+

{{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."