FEATURE: Make max number of favorite configurable (#13480)

It used to be hardcoded to 2 and now it uses max_favorite_badges site
setting. When zero, it disables favorite badges.
This commit is contained in:
Bianca Nenciu 2021-06-22 18:58:03 +03:00 committed by GitHub
parent e0e1e24c14
commit ee87d8c93b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 40 additions and 31 deletions

View File

@ -1,19 +1,18 @@
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import { action, computed } from "@ember/object"; import { action } from "@ember/object";
import { alias, filterBy, sort } from "@ember/object/computed"; import { alias, filterBy, sort } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
export default Controller.extend({ export default Controller.extend({
user: controller(), user: controller(),
username: alias("user.model.username_lower"), username: alias("user.model.username_lower"),
sortedBadges: sort("model", "badgeSortOrder"), sortedBadges: sort("model", "badgeSortOrder"),
favoriteBadges: filterBy("model", "is_favorite", true), favoriteBadges: filterBy("model", "is_favorite", true),
canFavoriteMoreBadges: computed(
"favoriteBadges.length", @discourseComputed("favoriteBadges.length")
"model.meta.max_favorites", canFavoriteMoreBadges(favoriteBadgesCount) {
function () { return favoriteBadgesCount < this.siteSettings.max_favorite_badges;
return this.favoriteBadges.length < this.model.meta.max_favorites; },
}
),
init() { init() {
this._super(...arguments); this._super(...arguments);

View File

@ -7,8 +7,6 @@ import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
const DEFAULT_USER_BADGES_META = { max_favorites: 2 };
const UserBadge = EmberObject.extend({ const UserBadge = EmberObject.extend({
@discourseComputed @discourseComputed
postUrl: function () { postUrl: function () {
@ -98,7 +96,6 @@ UserBadge.reopenClass({
userBadges.grant_count = json.user_badge_info.grant_count; userBadges.grant_count = json.user_badge_info.grant_count;
userBadges.username = json.user_badge_info.username; userBadges.username = json.user_badge_info.username;
} }
userBadges.meta = json.meta || DEFAULT_USER_BADGES_META;
return userBadges; return userBadges;
} }
}, },

View File

@ -1,6 +1,6 @@
{{#d-section pageClass="user-badges" class="user-content user-badges-list"}} {{#d-section pageClass="user-badges" class="user-content user-badges-list"}}
<p class="favorite-count"> <p class="favorite-count">
{{i18n "badges.favorite_count" count=this.favoriteBadges.length max=model.meta.max_favorites}} {{i18n "badges.favorite_count" count=this.favoriteBadges.length max=siteSettings.max_favorite_badges}}
</p> </p>
{{#each sortedBadges as |ub|}} {{#each sortedBadges as |ub|}}
{{badge-card {{badge-card

View File

@ -7,7 +7,7 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
background-color: var(--secondary); background-color: var(--secondary);
margin: 0 0 3px; margin: 4px 0 0;
img { img {
height: 16px; height: 16px;

View File

@ -225,8 +225,7 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
// badges // badges
.badge-section { .badge-section {
display: flex; line-height: 0;
align-items: flex-start;
.user-badge { .user-badge {
@include ellipsis; @include ellipsis;
background: var(--primary-very-low); background: var(--primary-very-low);
@ -236,11 +235,12 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
.user-card-badge-link { .user-card-badge-link {
overflow: hidden; overflow: hidden;
} }
.user-card-badge-link,
.more-user-badges { .more-user-badges {
flex: 0 0 auto; // this is more important than other badges, so don't allow it to shrink display: inline-block;
a { }
@extend .user-badge; .more-user-badges a {
} @extend .user-badge;
} }
} }
} }

View File

@ -42,7 +42,6 @@
.user-badge { .user-badge {
display: block; display: block;
max-width: 185px; max-width: 185px;
margin: 0 0.5em 0 0;
} }
.more-user-badges { .more-user-badges {
max-width: 125px; max-width: 125px;

View File

@ -53,6 +53,8 @@ $avatar_width: 120px;
.user-card { .user-card {
// badges // badges
.badge-section { .badge-section {
display: flex;
align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
.user-card-badge-link, .user-card-badge-link,
.more-user-badges { .more-user-badges {
@ -61,8 +63,7 @@ $avatar_width: 120px;
max-width: 50%; // for text ellipsis max-width: 50%; // for text ellipsis
padding: 2px 0; padding: 2px 0;
box-sizing: border-box; box-sizing: border-box;
&:nth-child(1), &:nth-child(odd) {
&:nth-child(3) {
padding-right: 4px; padding-right: 4px;
} }
a { a {

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class UserBadgesController < ApplicationController class UserBadgesController < ApplicationController
MAX_FAVORITES = 2
MAX_BADGES = 96 # This was limited in PR#2360 to make it divisible by 8 MAX_BADGES = 96 # This was limited in PR#2360 to make it divisible by 8
before_action :ensure_badges_enabled before_action :ensure_badges_enabled
@ -51,7 +50,6 @@ class UserBadgesController < ApplicationController
user_badges, user_badges,
DetailedUserBadgeSerializer, DetailedUserBadgeSerializer,
root: :user_badges, root: :user_badges,
meta: { max_favorites: MAX_FAVORITES },
) )
end end
@ -107,7 +105,7 @@ class UserBadgesController < ApplicationController
return render json: failed_json, status: 403 return render json: failed_json, status: 403
end end
if !user_badge.is_favorite && user_badges.where(is_favorite: true).count >= MAX_FAVORITES if !user_badge.is_favorite && user_badges.where(is_favorite: true).count >= SiteSetting.max_favorite_badges
return render json: failed_json, status: 400 return render json: failed_json, status: 400
end end

View File

@ -73,9 +73,11 @@ class User < ActiveRecord::Base
}, class_name: "UserSecurityKey" }, class_name: "UserSecurityKey"
has_many :badges, through: :user_badges has_many :badges, through: :user_badges
has_many :default_featured_user_badges, has_many :default_featured_user_badges, -> {
-> { for_enabled_badges.grouped_with_count.where("featured_rank <= ?", DEFAULT_FEATURED_BADGE_COUNT) }, max_featured_rank = SiteSetting.max_favorite_badges > 0 ? SiteSetting.max_favorite_badges + 1
class_name: "UserBadge" : DEFAULT_FEATURED_BADGE_COUNT
for_enabled_badges.grouped_with_count.where("featured_rank <= ?", max_featured_rank)
}, class_name: "UserBadge"
has_many :topics_allowed, through: :topic_allowed_users, source: :topic has_many :topics_allowed, through: :topic_allowed_users, source: :topic
has_many :groups, through: :group_users has_many :groups, through: :group_users
@ -1035,8 +1037,8 @@ class User < ActiveRecord::Base
user_stat&.distinct_badge_count user_stat&.distinct_badge_count
end end
def featured_user_badges(limit = DEFAULT_FEATURED_BADGE_COUNT) def featured_user_badges(limit = nil)
if limit == DEFAULT_FEATURED_BADGE_COUNT if limit.nil?
default_featured_user_badges default_featured_user_badges
else else
user_badges.grouped_with_count.where("featured_rank <= ?", limit) user_badges.grouped_with_count.where("featured_rank <= ?", limit)

View File

@ -25,6 +25,7 @@ class DetailedUserBadgeSerializer < BasicUserBadgeSerializer
end end
def can_favorite def can_favorite
SiteSetting.max_favorite_badges > 0 &&
(scope.current_user.present? && object.user_id == scope.current_user.id) && (scope.current_user.present? && object.user_id == scope.current_user.id) &&
!(1..4).include?(object.badge_id) !(1..4).include?(object.badge_id)
end end

View File

@ -1653,6 +1653,7 @@ en:
email_token_valid_hours: "Forgot password / activate account tokens are valid for (n) hours." email_token_valid_hours: "Forgot password / activate account tokens are valid for (n) hours."
enable_badges: "Enable the badge system" enable_badges: "Enable the badge system"
max_favorite_badges: "Maximum number of badges that user can select"
enable_whispers: "Allow staff private communication within topics." enable_whispers: "Allow staff private communication within topics."
allow_index_in_robots_txt: "Specify in robots.txt that this site is allowed to be indexed by web search engines. In exceptional cases you can permanently <a href='%{base_path}/admin/customize/robots'>override robots.txt</a>." allow_index_in_robots_txt: "Specify in robots.txt that this site is allowed to be indexed by web search engines. In exceptional cases you can permanently <a href='%{base_path}/admin/customize/robots'>override robots.txt</a>."

View File

@ -307,6 +307,11 @@ basic:
client: true client: true
default: false default: false
hidden: true hidden: true
max_favorite_badges:
client: true
default: 2
min: 0
max: 5
enable_whispers: enable_whispers:
client: true client: true
default: false default: false

View File

@ -277,12 +277,18 @@ describe UserBadgesController do
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it "checks that the user has less than two favorited badges" do it "checks that the user has less than max_favorites_badges favorited badges" do
sign_in(user) sign_in(user)
UserBadge.create(badge: Fabricate(:badge), user: user, granted_by: Discourse.system_user, granted_at: Time.now, is_favorite: true) UserBadge.create(badge: Fabricate(:badge), user: user, granted_by: Discourse.system_user, granted_at: Time.now, is_favorite: true)
UserBadge.create(badge: Fabricate(:badge), user: user, granted_by: Discourse.system_user, granted_at: Time.now, is_favorite: true) UserBadge.create(badge: Fabricate(:badge), user: user, granted_by: Discourse.system_user, granted_at: Time.now, is_favorite: true)
put "/user_badges/#{user_badge.id}/toggle_favorite.json" put "/user_badges/#{user_badge.id}/toggle_favorite.json"
expect(response.status).to eq(400) expect(response.status).to eq(400)
SiteSetting.max_favorite_badges = 3
put "/user_badges/#{user_badge.id}/toggle_favorite.json"
expect(response.status).to eq(200)
end end
it "favorites a badge" do it "favorites a badge" do