mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
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:
parent
e0e1e24c14
commit
ee87d8c93b
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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>."
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user