From f6cfff3ceafd86f8b04ad66fd95f34dbd022f0f5 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Fri, 6 Apr 2018 14:29:27 -0400 Subject: [PATCH] UX: user preferences allows users to choose which title to use from their badges and groups --- .../controllers/preferences/account.js.es6 | 10 ++++--- .../javascripts/discourse/models/user.js.es6 | 20 +++++++++++++ .../routes/preferences-account.js.es6 | 12 +++++++- .../templates/preferences/account.hbs | 6 ++-- config/locales/client.en.yml | 1 + lib/guardian.rb | 7 +++-- spec/components/guardian_spec.rb | 29 +++++++++++++++++++ 7 files changed, 76 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/preferences/account.js.es6 b/app/assets/javascripts/discourse/controllers/preferences/account.js.es6 index a89a97d412..e370efdfaf 100644 --- a/app/assets/javascripts/discourse/controllers/preferences/account.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences/account.js.es6 @@ -7,12 +7,13 @@ import { popupAjaxError } from 'discourse/lib/ajax-error'; export default Ember.Controller.extend(CanCheckEmails, PreferencesTabController, { - saveAttrNames: ['name'], + saveAttrNames: ['name', 'title'], canEditName: setting('enable_names'), canSaveUser: true, newNameInput: null, + newTitleInput: null, passwordProgress: null, @@ -30,9 +31,9 @@ export default Ember.Controller.extend(CanCheckEmails, PreferencesTabController, return I18n.t(this.siteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions'); }, - @computed("model.has_title_badges") - canSelectTitle(hasTitleBadges) { - return this.siteSettings.enable_badges && hasTitleBadges; + @computed('model.availableTitles') + canSelectTitle(availableTitles) { + return availableTitles.length > 0; }, @computed() @@ -52,6 +53,7 @@ export default Ember.Controller.extend(CanCheckEmails, PreferencesTabController, const model = this.get('model'); model.set('name', this.get('newNameInput')); + model.set('title', this.get('newTitleInput')); return model.save(this.get('saveAttrNames')).then(() => { this.set('saved', true); diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 5816cd2642..6663fc9050 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -218,6 +218,7 @@ const User = RestModel.extend({ 'website', 'location', 'name', + 'title', 'locale', 'custom_fields', 'user_fields', @@ -569,6 +570,25 @@ const User = RestModel.extend({ canManageGroup(group) { return group.get('automatic') ? false : (this.get('admin') || group.get('is_group_owner')); + }, + + @computed('groups.@each.title', 'badges.@each') + availableTitles() { + let titles = []; + + _.each(this.get('groups'), group => { + if (group.get('title')) { + titles.push(group.get('title')); + } + }); + + _.each(this.get('badges'), badge => { + if (badge.get('allow_title')) { + titles.push(badge.get('name')); + } + }); + + return titles; } }); diff --git a/app/assets/javascripts/discourse/routes/preferences-account.js.es6 b/app/assets/javascripts/discourse/routes/preferences-account.js.es6 index bde67c6ce9..b5e574c772 100644 --- a/app/assets/javascripts/discourse/routes/preferences-account.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-account.js.es6 @@ -1,13 +1,23 @@ +import UserBadge from 'discourse/models/user-badge'; import RestrictedUserRoute from "discourse/routes/restricted-user"; export default RestrictedUserRoute.extend({ showFooter: true, + model: function() { + const user = this.modelFor('user'); + return UserBadge.findByUsername(this.modelFor('user').get('username')).then(userBadges => { + user.set('badges', userBadges.map(ub => ub.badge)); + return user; + }); + }, + setupController(controller, user) { controller.reset(); controller.setProperties({ model: user, - newNameInput: user.get('name') + newNameInput: user.get('name'), + newTitleInput: user.get('title') }); } }); diff --git a/app/assets/javascripts/discourse/templates/preferences/account.hbs b/app/assets/javascripts/discourse/templates/preferences/account.hbs index 6862fac382..c9a6b01105 100644 --- a/app/assets/javascripts/discourse/templates/preferences/account.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/account.hbs @@ -100,8 +100,10 @@
- {{model.title}} - {{#link-to "preferences.badgeTitle" class="btn btn-small btn-icon pad-left no-text"}}{{d-icon "pencil"}}{{/link-to}} + {{combo-box + value=newTitleInput + content=model.availableTitles + none="user.title.none"}}
{{/if}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 95a62064d1..95a4b4d2ed 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1014,6 +1014,7 @@ en: header_title: "profile, messages, bookmarks and preferences" title: title: "Title" + none: "(none)" filters: all: "All" diff --git a/lib/guardian.rb b/lib/guardian.rb index b9f73aa6c7..11a7ee9661 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -215,8 +215,11 @@ class Guardian can_administer?(user) && !user.moderator? end - def can_grant_title?(user) - user && is_staff? + def can_grant_title?(user, title = nil) + return true if user && is_staff? + return false if user != @user + return true if user.badges.where(name: title, allow_title: true).exists? + user.groups.where(title: title).exists? end def can_change_primary_group?(user) diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 9d715817f4..41e65232d7 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -2186,6 +2186,35 @@ describe Guardian do it 'is false without a user to look at' do expect(Guardian.new(admin).can_grant_title?(nil)).to be_falsey end + + context 'with title argument' do + let(:title_badge) { Fabricate(:badge, name: 'Helper', allow_title: true) } + let(:no_title_badge) { Fabricate(:badge, name: 'Writer', allow_title: false) } + let(:group) { Fabricate(:group, title: 'Groupie') } + + it 'returns true if title belongs to a badge that user has' do + BadgeGranter.grant(title_badge, user) + expect(Guardian.new(user).can_grant_title?(user, title_badge.name)).to eq(true) + end + + it "returns false if title belongs to a badge that user doesn't have" do + expect(Guardian.new(user).can_grant_title?(user, title_badge.name)).to eq(false) + end + + it "returns false if title belongs to a badge that user has but can't be used as a title" do + BadgeGranter.grant(no_title_badge, user) + expect(Guardian.new(user).can_grant_title?(user, no_title_badge.name)).to eq(false) + end + + it 'returns true if title is from a group the user belongs to' do + group.add(user) + expect(Guardian.new(user).can_grant_title?(user, group.title)).to eq(true) + end + + it "returns false if title is from a group the user doesn't belong to" do + expect(Guardian.new(user).can_grant_title?(user, group.title)).to eq(false) + end + end end describe 'can_change_trust_level?' do