mirror of
https://github.com/discourse/discourse.git
synced 2024-11-29 00:55:06 +08:00
5f64fd0a21
Introduce new patterns for direct sql that are safe and fast. MiniSql is not prone to memory bloat that can happen with direct PG usage. It also has an extremely fast materializer and very a convenient API - DB.exec(sql, *params) => runs sql returns row count - DB.query(sql, *params) => runs sql returns usable objects (not a hash) - DB.query_hash(sql, *params) => runs sql returns an array of hashes - DB.query_single(sql, *params) => runs sql and returns a flat one dimensional array - DB.build(sql) => returns a sql builder See more at: https://github.com/discourse/mini_sql
170 lines
5.1 KiB
Ruby
170 lines
5.1 KiB
Ruby
class UserUpdater
|
|
|
|
CATEGORY_IDS = {
|
|
watched_first_post_category_ids: :watching_first_post,
|
|
watched_category_ids: :watching,
|
|
tracked_category_ids: :tracking,
|
|
muted_category_ids: :muted
|
|
}
|
|
|
|
TAG_NAMES = {
|
|
watching_first_post_tags: :watching_first_post,
|
|
watched_tags: :watching,
|
|
tracked_tags: :tracking,
|
|
muted_tags: :muted
|
|
}
|
|
|
|
OPTION_ATTR = [
|
|
:email_always,
|
|
:mailing_list_mode,
|
|
:mailing_list_mode_frequency,
|
|
:email_digests,
|
|
:email_direct,
|
|
:email_private_messages,
|
|
:external_links_in_new_tab,
|
|
:enable_quoting,
|
|
:dynamic_favicon,
|
|
:disable_jump_reply,
|
|
:automatically_unpin_topics,
|
|
:digest_after_minutes,
|
|
:new_topic_duration_minutes,
|
|
:auto_track_topics_after_msecs,
|
|
:notification_level_when_replying,
|
|
:email_previous_replies,
|
|
:email_in_reply_to,
|
|
:like_notification_frequency,
|
|
:include_tl0_in_digests,
|
|
:theme_key,
|
|
:allow_private_messages,
|
|
:homepage_id,
|
|
]
|
|
|
|
def initialize(actor, user)
|
|
@user = user
|
|
@guardian = Guardian.new(actor)
|
|
@actor = actor
|
|
end
|
|
|
|
def update(attributes = {})
|
|
user_profile = user.user_profile
|
|
user_profile.location = attributes.fetch(:location) { user_profile.location }
|
|
user_profile.dismissed_banner_key = attributes[:dismissed_banner_key] if attributes[:dismissed_banner_key].present?
|
|
user_profile.website = format_url(attributes.fetch(:website) { user_profile.website })
|
|
unless SiteSetting.enable_sso && SiteSetting.sso_overrides_bio
|
|
user_profile.bio_raw = attributes.fetch(:bio_raw) { user_profile.bio_raw }
|
|
end
|
|
user_profile.profile_background = attributes.fetch(:profile_background) { user_profile.profile_background }
|
|
user_profile.card_background = attributes.fetch(:card_background) { user_profile.card_background }
|
|
|
|
old_user_name = user.name.present? ? user.name : ""
|
|
user.name = attributes.fetch(:name) { user.name }
|
|
|
|
user.locale = attributes.fetch(:locale) { user.locale }
|
|
user.date_of_birth = attributes.fetch(:date_of_birth) { user.date_of_birth }
|
|
|
|
if attributes[:title] &&
|
|
attributes[:title] != user.title &&
|
|
guardian.can_grant_title?(user, attributes[:title])
|
|
user.title = attributes[:title]
|
|
if user.badges.where(name: user.title).exists?
|
|
user_profile.badge_granted_title = true
|
|
end
|
|
end
|
|
|
|
CATEGORY_IDS.each do |attribute, level|
|
|
if ids = attributes[attribute]
|
|
CategoryUser.batch_set(user, level, ids)
|
|
end
|
|
end
|
|
|
|
TAG_NAMES.each do |attribute, level|
|
|
if attributes.has_key?(attribute)
|
|
TagUser.batch_set(user, level, attributes[attribute]&.split(',') || [])
|
|
end
|
|
end
|
|
|
|
save_options = false
|
|
|
|
# special handling for theme_key cause we need to bump a sequence number
|
|
if attributes.key?(:theme_key) && user.user_option.theme_key != attributes[:theme_key]
|
|
user.user_option.theme_key_seq += 1
|
|
end
|
|
|
|
OPTION_ATTR.each do |attribute|
|
|
if attributes.key?(attribute)
|
|
save_options = true
|
|
|
|
if [true, false].include?(user.user_option.send(attribute))
|
|
val = attributes[attribute].to_s == 'true'
|
|
user.user_option.send("#{attribute}=", val)
|
|
else
|
|
user.user_option.send("#{attribute}=", attributes[attribute])
|
|
end
|
|
end
|
|
end
|
|
|
|
# automatically disable digests when mailing_list_mode is enabled
|
|
user.user_option.email_digests = false if user.user_option.mailing_list_mode
|
|
|
|
fields = attributes[:custom_fields]
|
|
if fields.present?
|
|
user.custom_fields = user.custom_fields.merge(fields)
|
|
end
|
|
|
|
saved = nil
|
|
|
|
User.transaction do
|
|
if attributes.key?(:muted_usernames)
|
|
update_muted_users(attributes[:muted_usernames])
|
|
end
|
|
|
|
if (saved = (!save_options || user.user_option.save) && user_profile.save && user.save) &&
|
|
(attributes[:name].present? && old_user_name.casecmp(attributes.fetch(:name)) != 0) ||
|
|
(attributes[:name].blank? && old_user_name.present?)
|
|
|
|
StaffActionLogger.new(@actor).log_name_change(
|
|
user.id,
|
|
old_user_name,
|
|
attributes.fetch(:name) { '' }
|
|
)
|
|
end
|
|
end
|
|
|
|
DiscourseEvent.trigger(:user_updated, user) if saved
|
|
saved
|
|
end
|
|
|
|
def update_muted_users(usernames)
|
|
usernames ||= ""
|
|
desired_ids = User.where(username: usernames.split(",")).pluck(:id)
|
|
if desired_ids.empty?
|
|
MutedUser.where(user_id: user.id).destroy_all
|
|
else
|
|
MutedUser.where('user_id = ? AND muted_user_id not in (?)', user.id, desired_ids).destroy_all
|
|
|
|
# SQL is easier here than figuring out how to do the same in AR
|
|
DB.exec(<<~SQL, now: Time.now, user_id: user.id, desired_ids: desired_ids)
|
|
INSERT into muted_users(user_id, muted_user_id, created_at, updated_at)
|
|
SELECT :user_id, id, :now, :now
|
|
FROM users
|
|
WHERE
|
|
id in (:desired_ids) AND
|
|
id NOT IN (
|
|
SELECT muted_user_id
|
|
FROM muted_users
|
|
WHERE user_id = :user_id
|
|
)
|
|
SQL
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :user, :guardian
|
|
|
|
def format_url(website)
|
|
return nil if website.blank?
|
|
website =~ /^http/ ? website : "http://#{website}"
|
|
end
|
|
end
|