discourse/app/models/user_profile.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

252 lines
6.9 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
class UserProfile < ActiveRecord::Base
self.ignored_columns = ["badge_granted_title"] # TODO: Remove when 20240212034010_drop_deprecated_columns has been promoted to pre-deploy
BAKED_VERSION = 1
2014-06-10 13:19:08 +08:00
belongs_to :user, inverse_of: :user_profile
belongs_to :card_background_upload, class_name: "Upload"
belongs_to :profile_background_upload, class_name: "Upload"
FIX: Badge and user title interaction fixes (#8282) * Fix user title logic when badge name customized * Fix an issue where a user's title was not considered a badge granted title when the user used a badge for their title and the badge name was customized. this affected the effectiveness of revoke_ungranted_titles! which only operates on badge_granted_titles. * When a user's title is set now it is considered a badge_granted_title if the badge name OR the badge custom name from TranslationOverride is the same as the title * When a user's badge is revoked we now also revoke their title if the user's title matches the badge name OR the badge custom name from TranslationOverride * Add a user history log when the title is revoked to remove confusion about why titles are revoked * Add granted_title_badge_id to user_profile, now when we set badge_granted_title on a user profile when updating a user's title based on a badge, we also remember which badge matched the title * When badge name (or custom text) changes update titles of users in a background job * When the name of a badge changes, or in the case of system badges when their custom translation text changes, then we need to update the title of all corresponding users who have a badge_granted_title and matching granted_title_badge_id. In the case of system badges we need to first get the proper badge ID based on the translation key e.g. badges.regular.name * Add migration to backfill all granted_title_badge_ids for both normal badge name titles and titles using custom badge text.
2019-11-08 13:34:24 +08:00
belongs_to :granted_title_badge, class_name: "Badge"
belongs_to :featured_topic, class_name: "Topic"
has_many :upload_references, as: :target, dependent: :destroy
has_many :user_profile_views, dependent: :destroy
2014-06-10 13:19:08 +08:00
validates :bio_raw, length: { maximum: 3000 }, watched_words: true
validates :website,
url: true,
length: {
maximum: 3000,
},
allow_blank: true,
if: :validate_website?
validates :location, length: { maximum: 3000 }, watched_words: true
2014-06-10 13:19:08 +08:00
validates :user, presence: true
validate :website_domain_validator, if: :validate_website?
2014-06-10 13:19:08 +08:00
before_save :cook
before_save :apply_watched_words, if: :location?
after_save :trigger_badges
after_save :pull_hotlinked_image
2014-06-10 13:19:08 +08:00
after_save do
if saved_change_to_profile_background_upload_id? ||
saved_change_to_card_background_upload_id? || saved_change_to_bio_raw?
upload_ids =
[self.profile_background_upload_id, self.card_background_upload_id] +
Upload.extract_upload_ids(self.bio_raw)
UploadReference.ensure_exist!(upload_ids: upload_ids, target: self)
end
end
attr_accessor :skip_pull_hotlinked_image
def bio_excerpt(length = 350, opts = {})
return nil if bio_cooked.blank?
excerpt = PrettyText.excerpt(bio_cooked, length, opts).sub(/<br>\z/, "")
return excerpt if excerpt.blank? || (user.has_trust_level?(TrustLevel[1]) && !user.suspended?)
2014-06-10 13:19:08 +08:00
PrettyText.strip_links(excerpt)
end
def bio_processed
if bio_cooked.blank? || (user.has_trust_level?(TrustLevel[1]) && !user.suspended?)
return bio_cooked
end
2014-06-10 13:19:08 +08:00
PrettyText.strip_links(bio_cooked)
end
def bio_summary
bio_excerpt(500, strip_links: true, text_entities: true)
2014-06-10 13:19:08 +08:00
end
def recook_bio
self.bio_raw_will_change!
cook
end
def upload_card_background(upload)
self.update!(card_background_upload: upload)
end
def clear_card_background
self.update!(card_background_upload: nil)
end
def upload_profile_background(upload)
self.update!(profile_background_upload: upload)
end
def clear_profile_background
self.update!(profile_background_upload: nil)
end
2014-08-05 14:37:56 +08:00
def self.rebake_old(limit)
problems = []
UserProfile
.where("bio_cooked_version IS NULL OR bio_cooked_version < ?", BAKED_VERSION)
.limit(limit)
.each do |p|
begin
p.rebake!
rescue => e
problems << { profile: p, ex: e }
end
2014-08-05 14:37:56 +08:00
end
problems
end
def rebake!
update_columns(bio_cooked: cooked, bio_cooked_version: BAKED_VERSION)
end
def self.import_url_for_user(background_url, user, options = nil)
if SiteSetting.verbose_upload_logging
Rails.logger.warn(
"Verbose Upload Logging: Downloading profile background from #{background_url}",
)
end
tempfile =
FileHelper.download(
background_url,
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
tmp_file_name: "sso-profile-background",
follow_redirect: true,
skip_rate_limit: true,
)
return unless tempfile
ext = FastImage.type(tempfile).to_s
tempfile.rewind
is_card_background = !options || options[:is_card_background]
type = is_card_background ? "card_background" : "profile_background"
upload =
UploadCreator.new(
tempfile,
"external-profile-background." + ext,
origin: background_url,
type: type,
).create_for(user.id)
if (is_card_background)
user.user_profile.upload_card_background(upload)
else
user.user_profile.upload_profile_background(upload)
end
rescue Net::ReadTimeout, OpenURI::HTTPError
# skip saving, we are not connected to the net
ensure
tempfile.close! if tempfile && tempfile.respond_to?(:close!)
end
protected
def trigger_badges
BadgeGranter.queue_badge_grant(Badge::Trigger::UserChange, user: self)
end
def pull_hotlinked_image
if !skip_pull_hotlinked_image && saved_change_to_bio_raw?
Jobs.enqueue_in(
SiteSetting.editing_grace_period,
:pull_user_profile_hotlinked_images,
user_id: self.user_id,
)
end
end
2014-06-10 13:19:08 +08:00
private
def self.remove_featured_topic_from_all_profiles(topic)
where(featured_topic_id: topic.id).update_all(featured_topic_id: nil)
end
2014-08-05 14:37:56 +08:00
def cooked
if self.bio_raw.present?
PrettyText.cook(
self.bio_raw,
omit_nofollow: user.has_trust_level?(TrustLevel[3]) && !SiteSetting.tl3_links_no_follow,
)
2014-08-05 14:37:56 +08:00
else
nil
end
end
2014-06-10 13:19:08 +08:00
def cook
if self.bio_raw.present?
2014-08-05 14:37:56 +08:00
if bio_raw_changed?
self.bio_cooked = cooked
self.bio_cooked_version = BAKED_VERSION
end
2014-06-10 13:19:08 +08:00
else
self.bio_cooked = nil
end
end
def apply_watched_words
self.location = WordWatcher.apply_to_text(location)
end
def website_domain_validator
allowed_domains = SiteSetting.allowed_user_website_domains
return if (allowed_domains.blank? || self.website.blank?)
2018-05-17 15:51:24 +08:00
domain =
begin
URI.parse(self.website).host
rescue URI::Error
end
unless allowed_domains.split("|").include?(domain)
self.errors.add :base,
(
I18n.t(
"user.website.domain_not_allowed",
domains: allowed_domains.split("|").join(", "),
)
)
2018-05-17 15:51:24 +08:00
end
end
def validate_website?
new_record? || website_changed?
end
end
# == Schema Information
#
# Table name: user_profiles
#
# user_id :integer not null, primary key
# location :string(3000)
# website :string(3000)
# bio_raw :text
# bio_cooked :text
# dismissed_banner_key :integer
# bio_cooked_version :integer
# views :integer default(0), not null
# profile_background_upload_id :integer
# card_background_upload_id :integer
2019-11-19 18:20:14 +08:00
# granted_title_badge_id :bigint
2019-12-30 14:13:27 +08:00
# featured_topic_id :integer
2014-08-07 11:33:11 +08:00
#
# Indexes
#
# index_user_profiles_on_bio_cooked_version (bio_cooked_version)
# index_user_profiles_on_card_background_upload_id (card_background_upload_id)
# index_user_profiles_on_granted_title_badge_id (granted_title_badge_id)
# index_user_profiles_on_profile_background_upload_id (profile_background_upload_id)
#
# Foreign Keys
#
# fk_rails_... (card_background_upload_id => uploads.id)
FIX: Badge and user title interaction fixes (#8282) * Fix user title logic when badge name customized * Fix an issue where a user's title was not considered a badge granted title when the user used a badge for their title and the badge name was customized. this affected the effectiveness of revoke_ungranted_titles! which only operates on badge_granted_titles. * When a user's title is set now it is considered a badge_granted_title if the badge name OR the badge custom name from TranslationOverride is the same as the title * When a user's badge is revoked we now also revoke their title if the user's title matches the badge name OR the badge custom name from TranslationOverride * Add a user history log when the title is revoked to remove confusion about why titles are revoked * Add granted_title_badge_id to user_profile, now when we set badge_granted_title on a user profile when updating a user's title based on a badge, we also remember which badge matched the title * When badge name (or custom text) changes update titles of users in a background job * When the name of a badge changes, or in the case of system badges when their custom translation text changes, then we need to update the title of all corresponding users who have a badge_granted_title and matching granted_title_badge_id. In the case of system badges we need to first get the proper badge ID based on the translation key e.g. badges.regular.name * Add migration to backfill all granted_title_badge_ids for both normal badge name titles and titles using custom badge text.
2019-11-08 13:34:24 +08:00
# fk_rails_... (granted_title_badge_id => badges.id)
2019-11-19 18:20:14 +08:00
# fk_rails_... (profile_background_upload_id => uploads.id)
#