discourse/app/controllers/user_badges_controller.rb
Bianca Nenciu a22aa7562a
FIX: Make favorite work with multiple grant badges (#13492)
Badges that are awarded multiple times can be favorite and not favorite
at the same time. This caused few problems when users tried to favorite
them as they were counted multiple times or their state was incorrectly
displayed.
2021-06-23 14:41:23 +03:00

155 lines
4.7 KiB
Ruby

# frozen_string_literal: true
class UserBadgesController < ApplicationController
MAX_BADGES = 96 # This was limited in PR#2360 to make it divisible by 8
before_action :ensure_badges_enabled
def index
params.permit [:granted_before, :offset, :username]
badge = fetch_badge_from_params
user_badges = badge.user_badges.order('granted_at DESC, id DESC').limit(MAX_BADGES)
user_badges = user_badges.includes(:user, :granted_by, badge: :badge_type, post: :topic, user: :primary_group)
grant_count = nil
if params[:username]
user_id = User.where(username_lower: params[:username].downcase).pluck_first(:id)
user_badges = user_badges.where(user_id: user_id) if user_id
grant_count = badge.user_badges.where(user_id: user_id).count
end
if offset = params[:offset]
user_badges = user_badges.offset(offset.to_i)
end
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
params.permit [:grouped]
user = fetch_user_from_params(include_inactive: current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts))
raise Discourse::NotFound unless guardian.can_see_profile?(user)
user_badges = user.user_badges
if params[:grouped]
user_badges = user_badges.group(:badge_id).select_for_grouping
end
user_badges = user_badges.includes(badge: [:badge_grouping, :badge_type, :image_upload])
.includes(post: :topic)
.includes(:granted_by)
render_serialized(
user_badges,
DetailedUserBadgeSerializer,
root: :user_badges,
)
end
def create
params.require(:username)
user = fetch_user_from_params
unless can_assign_badge_to_user?(user)
return render json: failed_json, status: 403
end
badge = fetch_badge_from_params
post_id = nil
if params[:reason].present?
unless is_badge_reason_valid? params[:reason]
return render json: failed_json.merge(message: I18n.t('invalid_grant_badge_reason_link')), status: 400
end
if route = Discourse.route_for(params[:reason])
if route[:controller] == "topics" && route[:action] == "show"
topic_id = (route[:id] || route[:topic_id]).to_i
post_number = route[:post_number] || 1
post_id = Post.find_by(topic_id: topic_id, post_number: post_number)&.id if topic_id > 0
end
end
end
user_badge = BadgeGranter.grant(badge, user, granted_by: current_user, post_id: post_id)
render_serialized(user_badge, DetailedUserBadgeSerializer, root: "user_badge")
end
def destroy
params.require(:id)
user_badge = UserBadge.find(params[:id])
unless can_assign_badge_to_user?(user_badge.user)
render json: failed_json, status: 403
return
end
BadgeGranter.revoke(user_badge, revoked_by: current_user)
render json: success_json
end
def toggle_favorite
params.require(:user_badge_id)
user_badge = UserBadge.find(params[:user_badge_id])
user_badges = user_badge.user.user_badges
unless can_favorite_badge?(user_badge)
return render json: failed_json, status: 403
end
if !user_badge.is_favorite && user_badges.select(:badge_id).distinct.where(is_favorite: true).count >= SiteSetting.max_favorite_badges
return render json: failed_json, status: 400
end
UserBadge
.where(user_id: user_badge.user_id, badge_id: user_badge.badge_id)
.update(is_favorite: !user_badge.is_favorite)
UserBadge.update_featured_ranks!(user_badge.user_id)
render_serialized(user_badge, DetailedUserBadgeSerializer, root: :user_badge)
end
private
# Get the badge from either the badge name or id specified in the params.
def fetch_badge_from_params
badge = nil
params.permit(:badge_name)
if params[:badge_name].nil?
params.require(:badge_id)
badge = Badge.find_by(id: params[:badge_id], enabled: true)
else
badge = Badge.find_by(name: params[:badge_name], enabled: true)
end
raise Discourse::NotFound if badge.blank?
badge
end
def can_assign_badge_to_user?(user)
master_api_call = current_user.nil? && is_api?
master_api_call || guardian.can_grant_badges?(user)
end
def can_favorite_badge?(user_badge)
current_user == user_badge.user && !(1..4).include?(user_badge.badge_id)
end
def ensure_badges_enabled
raise Discourse::NotFound unless SiteSetting.enable_badges?
end
def is_badge_reason_valid?(reason)
route = Discourse.route_for(reason)
route && (route[:controller] == 'posts' || route[:controller] == 'topics')
end
end