diff --git a/app/assets/javascripts/discourse/components/user-badge.js.es6 b/app/assets/javascripts/discourse/components/user-badge.js.es6
index c7fce8b1a..0db738d41 100644
--- a/app/assets/javascripts/discourse/components/user-badge.js.es6
+++ b/app/assets/javascripts/discourse/components/user-badge.js.es6
@@ -3,5 +3,13 @@ export default Ember.Component.extend({
showGrantCount: function() {
return this.get('count') && this.get('count') > 1;
- }.property('count')
+ }.property('count'),
+
+ badgeUrl: function(){
+ // NOTE: I tried using a link-to helper here but the queryParams mean it fails
+ var username = this.get('user.username_lower') || '';
+ username = username !== '' ? "?username=" + username : '';
+ return this.get('badge.url') + username;
+ }.property("badge", "user")
+
});
diff --git a/app/assets/javascripts/discourse/controllers/badges/show.js.es6 b/app/assets/javascripts/discourse/controllers/badges/show.js.es6
index a5a899f64..afe640bc6 100644
--- a/app/assets/javascripts/discourse/controllers/badges/show.js.es6
+++ b/app/assets/javascripts/discourse/controllers/badges/show.js.es6
@@ -1,17 +1,33 @@
import UserBadge from 'discourse/models/user-badge';
export default Ember.Controller.extend({
+ queryParams: ['username'],
noMoreBadges: false,
userBadges: null,
needs: ["application"],
+ user: function(){
+ if (this.get("username")) {
+ return this.get('userBadges')[0].get('user');
+ }
+ }.property("username"),
+
+ grantCount: function() {
+ if (this.get("username")) {
+ return this.get('userBadges.grant_count');
+ } else {
+ return this.get('model.grant_count');
+ }
+ }.property('username', 'model', 'userBadges'),
+
actions: {
loadMore() {
const self = this;
const userBadges = this.get('userBadges');
UserBadge.findByBadgeId(this.get('model.id'), {
- offset: userBadges.length
+ offset: userBadges.length,
+ username: this.get('username'),
}).then(function(result) {
userBadges.pushObjects(result);
if(userBadges.length === 0){
@@ -22,11 +38,12 @@ export default Ember.Controller.extend({
},
layoutClass: function(){
+ var user = this.get("user") ? " single-user" : "";
var ub = this.get("userBadges");
if(ub && ub[0] && ub[0].post_id){
- return "user-badge-with-posts";
+ return "user-badge-with-posts" + user;
} else {
- return "user-badge-no-posts";
+ return "user-badge-no-posts" + user;
}
}.property("userBadges"),
@@ -34,7 +51,7 @@ export default Ember.Controller.extend({
if (this.get('noMoreBadges')) { return false; }
if (this.get('userBadges')) {
- return this.get('model.grant_count') > this.get('userBadges.length');
+ return this.get('grantCount') > this.get('userBadges.length');
} else {
return false;
}
diff --git a/app/assets/javascripts/discourse/controllers/user-badges.js.es6 b/app/assets/javascripts/discourse/controllers/user-badges.js.es6
index 3ceae1ec1..76b2a05cd 100644
--- a/app/assets/javascripts/discourse/controllers/user-badges.js.es6
+++ b/app/assets/javascripts/discourse/controllers/user-badges.js.es6
@@ -1,4 +1,6 @@
export default Ember.ArrayController.extend({
+ needs: ["user"],
+ user: Em.computed.alias("controllers.user.model"),
sortProperties: ['badge.badge_type.sort_order', 'badge.name'],
orderBy: function(ub1, ub2){
var sr1 = ub1.get('badge.badge_type.sort_order');
diff --git a/app/assets/javascripts/discourse/models/badge.js.es6 b/app/assets/javascripts/discourse/models/badge.js.es6
index 1e4d79433..2b070a0ab 100644
--- a/app/assets/javascripts/discourse/models/badge.js.es6
+++ b/app/assets/javascripts/discourse/models/badge.js.es6
@@ -5,6 +5,10 @@ const Badge = RestModel.extend({
newBadge: Em.computed.none('id'),
+ url: function() {
+ return Discourse.getURL(`/badges/${this.get('id')}/${this.get('slug')}`);
+ }.property(),
+
/**
@private
diff --git a/app/assets/javascripts/discourse/models/user-badge.js.es6 b/app/assets/javascripts/discourse/models/user-badge.js.es6
index 8f938298d..a99c997d6 100644
--- a/app/assets/javascripts/discourse/models/user-badge.js.es6
+++ b/app/assets/javascripts/discourse/models/user-badge.js.es6
@@ -48,7 +48,7 @@ UserBadge.reopenClass({
if ("user_badge" in json) {
userBadges = [json.user_badge];
} else {
- userBadges = json.user_badges;
+ userBadges = (json.user_badge_info && json.user_badge_info.user_badges) || json.user_badges;
}
userBadges = userBadges.map(function(userBadgeJson) {
@@ -73,6 +73,10 @@ UserBadge.reopenClass({
if ("user_badge" in json) {
return userBadges[0];
} else {
+ if (json.user_badge_info) {
+ userBadges.grant_count = json.user_badge_info.grant_count;
+ userBadges.username = json.user_badge_info.username;
+ }
return userBadges;
}
},
diff --git a/app/assets/javascripts/discourse/routes/badges-show.js.es6 b/app/assets/javascripts/discourse/routes/badges-show.js.es6
index 42bc89e9a..74e04c644 100644
--- a/app/assets/javascripts/discourse/routes/badges-show.js.es6
+++ b/app/assets/javascripts/discourse/routes/badges-show.js.es6
@@ -2,6 +2,11 @@ import UserBadge from 'discourse/models/user-badge';
import Badge from 'discourse/models/badge';
export default Discourse.Route.extend({
+ queryParams: {
+ username: {
+ refreshModel: true
+ }
+ },
actions: {
didTransition() {
this.controllerFor("badges/show")._showFooter();
@@ -24,10 +29,13 @@ export default Discourse.Route.extend({
}
},
- afterModel(model) {
- return UserBadge.findByBadgeId(model.get("id")).then(userBadges => {
+ afterModel(model,transition) {
+ const username = transition.queryParams && transition.queryParams.username;
+
+ return UserBadge.findByBadgeId(model.get("id"), {username}).then(userBadges => {
this.userBadges = userBadges;
});
+
},
titleToken() {
diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs
index 4b245ee2f..7cfada243 100644
--- a/app/assets/javascripts/discourse/templates/badges/show.hbs
+++ b/app/assets/javascripts/discourse/templates/badges/show.hbs
@@ -9,7 +9,9 @@
{{user-badge badge=model}}
{{{model.displayDescriptionHtml}}}
-
{{i18n 'badges.granted' count=model.grant_count}}
+ {{#unless user}}
+
{{i18n 'badges.granted' count=grantCount}}
+ {{/unless}}
{{i18n 'badges.allow_title'}} {{{view.allowTitle}}}
{{i18n 'badges.multiple_grant'}} {{{view.multipleGrant}}}
@@ -22,23 +24,48 @@
{{/if}}
+ {{#if user}}
+
+ {{#link-to 'user' user}}
+ {{avatar user imageSize="extra_large"}}
+
+ {{poster-name post=user}}
+
+ {{/link-to}}
+
+ {{i18n 'badges.earned_n_times' count=grantCount}}
+
+
+ {{/if}}
+
{{#if userBadges}}
{{#each ub in userBadges}}
- {{#link-to 'user' ub.user classNames="badge-info"}}
- {{avatar ub.user imageSize="large"}}
-
- {{ub.user.username}}
- {{format-date ub.granted_at}}
-
- {{/link-to}}
+ {{#if user}}
+ {{format-date ub.granted_at}}
+ {{else}}
+ {{#link-to 'user' ub.user classNames="badge-info"}}
+ {{avatar ub.user imageSize="large"}}
+
+ {{ub.user.username}}
+ {{format-date ub.granted_at}}
+
+ {{/link-to}}
+ {{/if}}
{{#if ub.post_number}}
{{{ub.topic.fancyTitle}}}
{{/if}}
{{/each}}
+
+ {{#unless canLoadMore}}
+ {{#if user}}
+
{{i18n 'badges.more_with_badge'}}
+ {{/if}}
+ {{/unless}}
+
{{conditional-loading-spinner condition=canLoadMore}}
diff --git a/app/assets/javascripts/discourse/templates/components/user-badge.hbs b/app/assets/javascripts/discourse/templates/components/user-badge.hbs
index 69e5393dd..24334c6a7 100644
--- a/app/assets/javascripts/discourse/templates/components/user-badge.hbs
+++ b/app/assets/javascripts/discourse/templates/components/user-badge.hbs
@@ -1,7 +1,7 @@
-{{#link-to 'badges.show' badge}}
+
{{#badge-button badge=badge}}
{{#if showGrantCount}}
(× {{count}})
{{/if}}
{{/badge-button}}
-{{/link-to}}
+
diff --git a/app/assets/javascripts/discourse/templates/user-card.hbs b/app/assets/javascripts/discourse/templates/user-card.hbs
index 6c0762d22..9b8bf40bb 100644
--- a/app/assets/javascripts/discourse/templates/user-card.hbs
+++ b/app/assets/javascripts/discourse/templates/user-card.hbs
@@ -68,7 +68,7 @@
{{#if showBadges}}
{{#each ub in user.featured_user_badges}}
- {{user-badge badge=ub.badge}}
+ {{user-badge badge=ub.badge user=user}}
{{/each}}
{{#if showMoreBadges}}
{{#link-to 'user.badges' user class="btn more-user-badges"}}
diff --git a/app/assets/javascripts/discourse/templates/user/badges.hbs b/app/assets/javascripts/discourse/templates/user/badges.hbs
index e2d943dcb..7cfc318ba 100644
--- a/app/assets/javascripts/discourse/templates/user/badges.hbs
+++ b/app/assets/javascripts/discourse/templates/user/badges.hbs
@@ -1,5 +1,5 @@
{{#each ub in controller}}
- {{user-badge badge=ub.badge count=ub.count}}
+ {{user-badge badge=ub.badge count=ub.count user=user}}
{{/each}}
diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss
index a7d25e877..8f4d7a21b 100644
--- a/app/assets/stylesheets/common/base/user-badges.scss
+++ b/app/assets/stylesheets/common/base/user-badges.scss
@@ -187,6 +187,58 @@
}
}
+.show-badge .badge-user-info {
+ margin-left: 2%;
+ .earned {
+ margin-top: 15px;
+ font-size: 1.3em;
+ }
+ .username {
+ margin-top: 5px;
+ display: block;
+ color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%));
+ }
+}
+
+.show-badge .single-user {
+ margin-left: 2%;
+ padding-bottom: 20px;
+ .load-more {
+ padding-top: 30px;
+ display: block;
+ font-size: 1.2em;
+ }
+}
+
+.show-badge .single-user .badge-user {
+ padding-left: 0;
+
+ text-align: left;
+ display: block;
+ margin: 20px 0;
+ .badge-info {
+ display: none;
+ }
+ .date {
+ display: inline-block;
+ font-size: 1.1em;
+ margin-left: 10px;
+ }
+ .post-link {
+ font-size: 1.3em;
+ width: 500px;
+ margin: 0;
+ padding: 0;
+ }
+ width: 800px;
+
+ &:after {
+ content: "";
+ clear: both;
+ display: table;
+ }
+}
+
.long-description.banner {
width: 88%;
margin-bottom: 20px;
diff --git a/app/controllers/user_badges_controller.rb b/app/controllers/user_badges_controller.rb
index 397ce503f..5f1499f91 100644
--- a/app/controllers/user_badges_controller.rb
+++ b/app/controllers/user_badges_controller.rb
@@ -1,16 +1,28 @@
class UserBadgesController < ApplicationController
def index
- params.permit [:granted_before, :offset]
+ params.permit [:granted_before, :offset, :username]
badge = fetch_badge_from_params
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(96)
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic)
+ grant_count = nil
+
+ if params[:username]
+ user_id = User.where(username_lower: params[:username].downcase).pluck(:id).first
+ user_badges = user_badges.where(user_id: user_id) if user_id
+ grant_count = user_badges.count
+ end
+
if offset = params[:offset]
user_badges = user_badges.offset(offset.to_i)
end
- render_serialized(user_badges, UserBadgeSerializer, root: "user_badges", include_long_description: true)
+ user_badges = UserBadges.new(user_badges: user_badges,
+ username: params[:username],
+ grant_count: grant_count)
+
+ render_serialized(user_badges, UserBadgesSerializer, root: :user_badge_info, include_long_description: true)
end
def username
@@ -28,7 +40,7 @@ class UserBadgesController < ApplicationController
.includes(post: :topic)
.includes(:granted_by)
- render_serialized(user_badges, DetailedUserBadgeSerializer, root: "user_badges")
+ render_serialized(user_badges, DetailedUserBadgeSerializer, root: :user_badges)
end
def create
diff --git a/app/models/user_badges.rb b/app/models/user_badges.rb
new file mode 100644
index 000000000..187ee1bc9
--- /dev/null
+++ b/app/models/user_badges.rb
@@ -0,0 +1,12 @@
+# view model for user badges
+class UserBadges
+ alias :read_attribute_for_serialization :send
+
+ attr_accessor :user_badges, :username, :grant_count
+
+ def initialize(opts={})
+ @user_badges = opts[:user_badges]
+ @username = opts[:username]
+ @grant_count = opts[:grant_count]
+ end
+end
diff --git a/app/serializers/user_badge_serializer.rb b/app/serializers/user_badge_serializer.rb
index cb6d2341b..9a5e6d9eb 100644
--- a/app/serializers/user_badge_serializer.rb
+++ b/app/serializers/user_badge_serializer.rb
@@ -1,9 +1,14 @@
class UserBadgeSerializer < ApplicationSerializer
+
+ class UserSerializer < BasicUserSerializer
+ attributes :name, :moderator, :admin
+ end
+
attributes :id, :granted_at, :count, :post_id, :post_number
has_one :badge
- has_one :user, serializer: BasicUserSerializer, root: :users
- has_one :granted_by, serializer: BasicUserSerializer, root: :users
+ has_one :user, serializer: UserSerializer, root: :users
+ has_one :granted_by, serializer: UserSerializer, root: :users
has_one :topic, serializer: BasicTopicSerializer
def include_count?
diff --git a/app/serializers/user_badges_serializer.rb b/app/serializers/user_badges_serializer.rb
new file mode 100644
index 000000000..178438b17
--- /dev/null
+++ b/app/serializers/user_badges_serializer.rb
@@ -0,0 +1,4 @@
+class UserBadgesSerializer < ApplicationSerializer
+ has_many :user_badges, embed: :objects
+ attributes :grant_count, :username
+end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index fadc6ab72..14f6b2b43 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2746,6 +2746,10 @@ en:
mark_watching: 'm, w Watch topic'
badges:
+ earned_n_times:
+ one: "Earned this badge 1 time"
+ other: "Earned this badge %{count} times"
+ more_with_badge: "Others with this badge"
title: Badges
allow_title: "can be used as a title"
multiple_grant: "can be awarded multiple times"
diff --git a/spec/controllers/user_badges_controller_spec.rb b/spec/controllers/user_badges_controller_spec.rb
index 3ad8840bc..0046f6140 100644
--- a/spec/controllers/user_badges_controller_spec.rb
+++ b/spec/controllers/user_badges_controller_spec.rb
@@ -12,9 +12,11 @@ describe UserBadgesController do
xhr :get, :index, badge_id: badge.id
expect(response.status).to eq(200)
+
parsed = JSON.parse(response.body)
expect(parsed["topics"]).to eq(nil)
- expect(parsed["user_badges"][0]["post_id"]).to eq(nil)
+ expect(parsed["badges"].length).to eq(1)
+ expect(parsed["user_badge_info"]["user_badges"][0]["post_id"]).to eq(nil)
end
end
@@ -38,7 +40,7 @@ describe UserBadgesController do
expect(response.status).to eq(200)
parsed = JSON.parse(response.body)
- expect(parsed["user_badges"].length).to eq(1)
+ expect(parsed["user_badge_info"]["user_badges"].length).to eq(1)
end
it 'includes counts when passed the aggregate argument' do