2019-05-03 06:17:27 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-11-02 05:06:20 +08:00
|
|
|
class UserUpdater
|
2014-01-21 06:58:02 +08:00
|
|
|
CATEGORY_IDS = {
|
2016-07-08 12:08:10 +08:00
|
|
|
watched_first_post_category_ids: :watching_first_post,
|
2014-01-21 06:58:02 +08:00
|
|
|
watched_category_ids: :watching,
|
|
|
|
tracked_category_ids: :tracking,
|
2020-08-20 03:05:04 +08:00
|
|
|
regular_category_ids: :regular,
|
2014-01-21 06:58:02 +08:00
|
|
|
muted_category_ids: :muted,
|
|
|
|
}
|
|
|
|
|
2016-07-08 10:58:18 +08:00
|
|
|
TAG_NAMES = {
|
2016-07-23 04:16:45 +08:00
|
|
|
watching_first_post_tags: :watching_first_post,
|
2016-07-08 10:58:18 +08:00
|
|
|
watched_tags: :watching,
|
|
|
|
tracked_tags: :tracking,
|
|
|
|
muted_tags: :muted,
|
|
|
|
}
|
|
|
|
|
2016-02-17 12:46:19 +08:00
|
|
|
OPTION_ATTR = %i[
|
|
|
|
mailing_list_mode
|
2016-05-21 21:17:54 +08:00
|
|
|
mailing_list_mode_frequency
|
2016-02-17 12:46:19 +08:00
|
|
|
email_digests
|
2019-03-15 22:55:11 +08:00
|
|
|
email_level
|
|
|
|
email_messages_level
|
2015-02-26 23:50:01 +08:00
|
|
|
external_links_in_new_tab
|
|
|
|
enable_quoting
|
2019-05-31 13:43:17 +08:00
|
|
|
enable_defer
|
2020-08-28 22:36:52 +08:00
|
|
|
color_scheme_id
|
2020-08-06 21:45:37 +08:00
|
|
|
dark_scheme_id
|
2015-02-26 23:50:01 +08:00
|
|
|
dynamic_favicon
|
2015-11-18 01:21:40 +08:00
|
|
|
automatically_unpin_topics
|
2016-03-03 04:26:27 +08:00
|
|
|
digest_after_minutes
|
2016-02-18 13:57:22 +08:00
|
|
|
new_topic_duration_minutes
|
2016-02-19 10:56:52 +08:00
|
|
|
auto_track_topics_after_msecs
|
2016-10-01 00:36:43 +08:00
|
|
|
notification_level_when_replying
|
2016-02-25 21:05:40 +08:00
|
|
|
email_previous_replies
|
2016-03-02 20:16:52 +08:00
|
|
|
email_in_reply_to
|
2016-03-18 05:35:23 +08:00
|
|
|
like_notification_frequency
|
2017-05-13 00:41:26 +08:00
|
|
|
include_tl0_in_digests
|
2018-07-12 12:18:21 +08:00
|
|
|
theme_ids
|
2017-10-06 15:56:58 +08:00
|
|
|
allow_private_messages
|
2020-07-21 05:23:49 +08:00
|
|
|
enable_allowed_pm_users
|
2017-11-10 03:45:19 +08:00
|
|
|
homepage_id
|
2019-01-14 21:21:46 +08:00
|
|
|
hide_profile_and_presence
|
2019-04-12 07:02:18 +08:00
|
|
|
text_size
|
2019-11-25 08:49:27 +08:00
|
|
|
title_count_mode
|
2020-08-14 21:40:56 +08:00
|
|
|
timezone
|
2021-10-06 11:11:52 +08:00
|
|
|
skip_new_user_tips
|
2022-10-12 23:38:45 +08:00
|
|
|
seen_popups
|
2022-10-18 10:21:52 +08:00
|
|
|
default_calendar
|
2022-12-16 06:50:31 +08:00
|
|
|
bookmark_auto_delete_preference
|
2023-06-23 00:04:13 +08:00
|
|
|
sidebar_link_to_filtered_list
|
|
|
|
sidebar_show_count_of_new_items
|
2023-07-04 13:08:29 +08:00
|
|
|
watched_precedence_over_muted
|
2014-01-21 06:58:02 +08:00
|
|
|
]
|
|
|
|
|
2021-01-21 00:31:52 +08:00
|
|
|
NOTIFICATION_SCHEDULE_ATTRS = -> do
|
|
|
|
attrs = [:enabled]
|
|
|
|
7.times do |n|
|
|
|
|
attrs.push("day_#{n}_start_time".to_sym)
|
|
|
|
attrs.push("day_#{n}_end_time".to_sym)
|
|
|
|
end
|
|
|
|
{ user_notification_schedule: attrs }
|
|
|
|
end.call
|
|
|
|
|
2013-12-11 01:46:35 +08:00
|
|
|
def initialize(actor, user)
|
2013-11-02 05:06:20 +08:00
|
|
|
@user = user
|
2013-12-11 01:46:35 +08:00
|
|
|
@guardian = Guardian.new(actor)
|
2017-02-23 13:48:57 +08:00
|
|
|
@actor = actor
|
2013-11-02 05:06:20 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def update(attributes = {})
|
2014-06-07 12:54:32 +08:00
|
|
|
user_profile = user.user_profile
|
2015-05-20 07:39:58 +08:00
|
|
|
user_profile.dismissed_banner_key = attributes[:dismissed_banner_key] if attributes[
|
|
|
|
:dismissed_banner_key
|
|
|
|
].present?
|
2021-02-08 18:04:33 +08:00
|
|
|
unless SiteSetting.enable_discourse_connect && SiteSetting.discourse_connect_overrides_bio
|
2016-08-01 13:29:28 +08:00
|
|
|
user_profile.bio_raw = attributes.fetch(:bio_raw) { user_profile.bio_raw }
|
|
|
|
end
|
2019-04-29 11:58:52 +08:00
|
|
|
|
2021-02-08 18:04:33 +08:00
|
|
|
unless SiteSetting.enable_discourse_connect && SiteSetting.discourse_connect_overrides_location
|
2020-04-28 14:06:35 +08:00
|
|
|
user_profile.location = attributes.fetch(:location) { user_profile.location }
|
|
|
|
end
|
|
|
|
|
2021-02-08 18:04:33 +08:00
|
|
|
unless SiteSetting.enable_discourse_connect && SiteSetting.discourse_connect_overrides_website
|
2020-04-28 14:06:35 +08:00
|
|
|
user_profile.website = format_url(attributes.fetch(:website) { user_profile.website })
|
|
|
|
end
|
|
|
|
|
2020-08-18 00:37:45 +08:00
|
|
|
if attributes[:profile_background_upload_url] == "" ||
|
|
|
|
!guardian.can_upload_profile_header?(user)
|
2019-05-02 16:53:17 +08:00
|
|
|
user_profile.profile_background_upload_id = nil
|
|
|
|
elsif upload = Upload.get_from_url(attributes[:profile_background_upload_url])
|
|
|
|
user_profile.profile_background_upload_id = upload.id
|
2019-04-29 11:58:52 +08:00
|
|
|
end
|
|
|
|
|
2020-08-18 00:37:45 +08:00
|
|
|
if attributes[:card_background_upload_url] == "" ||
|
|
|
|
!guardian.can_upload_user_card_background?(user)
|
2019-05-02 16:53:17 +08:00
|
|
|
user_profile.card_background_upload_id = nil
|
|
|
|
elsif upload = Upload.get_from_url(attributes[:card_background_upload_url])
|
|
|
|
user_profile.card_background_upload_id = upload.id
|
2019-04-29 11:58:52 +08:00
|
|
|
end
|
2014-01-02 14:58:49 +08:00
|
|
|
|
2021-01-21 00:31:52 +08:00
|
|
|
if attributes[:user_notification_schedule]
|
|
|
|
user_notification_schedule =
|
|
|
|
user.user_notification_schedule || UserNotificationSchedule.new(user: user)
|
|
|
|
user_notification_schedule.assign_attributes(attributes[:user_notification_schedule])
|
|
|
|
end
|
|
|
|
|
2017-03-01 16:12:17 +08:00
|
|
|
old_user_name = user.name.present? ? user.name : ""
|
2021-07-28 14:07:18 +08:00
|
|
|
|
|
|
|
user.name = attributes.fetch(:name) { user.name } if guardian.can_edit_name?(user)
|
2017-03-01 16:12:17 +08:00
|
|
|
|
2014-02-08 11:24:10 +08:00
|
|
|
user.locale = attributes.fetch(:locale) { user.locale }
|
2016-11-15 16:10:20 +08:00
|
|
|
user.date_of_birth = attributes.fetch(:date_of_birth) { user.date_of_birth }
|
2014-01-02 14:58:49 +08:00
|
|
|
|
2018-04-27 04:50:50 +08:00
|
|
|
if attributes[:title] && attributes[:title] != user.title &&
|
|
|
|
guardian.can_grant_title?(user, attributes[:title])
|
|
|
|
user.title = attributes[:title]
|
2013-11-02 05:06:20 +08:00
|
|
|
end
|
|
|
|
|
2019-10-29 01:46:27 +08:00
|
|
|
if SiteSetting.user_selected_primary_groups && attributes[:primary_group_id] &&
|
|
|
|
attributes[:primary_group_id] != user.primary_group_id &&
|
|
|
|
guardian.can_use_primary_group?(user, attributes[:primary_group_id])
|
|
|
|
user.primary_group_id = attributes[:primary_group_id]
|
2020-01-18 06:43:54 +08:00
|
|
|
elsif SiteSetting.user_selected_primary_groups && attributes[:primary_group_id] &&
|
2020-01-18 22:30:16 +08:00
|
|
|
attributes[:primary_group_id].blank?
|
2020-01-18 06:43:54 +08:00
|
|
|
user.primary_group_id = nil
|
2019-10-29 01:46:27 +08:00
|
|
|
end
|
|
|
|
|
2021-07-08 15:46:21 +08:00
|
|
|
if attributes[:flair_group_id] && attributes[:flair_group_id] != user.flair_group_id &&
|
2021-07-09 12:41:26 +08:00
|
|
|
(
|
|
|
|
attributes[:flair_group_id].blank? ||
|
2021-07-21 19:41:04 +08:00
|
|
|
guardian.can_use_flair_group?(user, attributes[:flair_group_id])
|
2023-01-09 20:20:10 +08:00
|
|
|
)
|
2021-07-08 15:46:21 +08:00
|
|
|
user.flair_group_id = attributes[:flair_group_id]
|
|
|
|
end
|
|
|
|
|
2021-07-17 02:50:40 +08:00
|
|
|
if @guardian.can_change_tracking_preferences?(user)
|
|
|
|
CATEGORY_IDS.each do |attribute, level|
|
|
|
|
if ids = attributes[attribute]
|
|
|
|
CategoryUser.batch_set(user, level, ids)
|
|
|
|
end
|
2014-01-21 06:58:02 +08:00
|
|
|
end
|
|
|
|
|
2021-07-17 02:50:40 +08:00
|
|
|
TAG_NAMES.each do |attribute, level|
|
|
|
|
if attributes.has_key?(attribute)
|
|
|
|
TagUser.batch_set(user, level, attributes[attribute]&.split(",") || [])
|
|
|
|
end
|
2018-03-30 03:08:22 +08:00
|
|
|
end
|
2016-07-08 10:58:18 +08:00
|
|
|
end
|
|
|
|
|
2016-02-17 12:46:19 +08:00
|
|
|
save_options = false
|
2016-02-25 21:05:40 +08:00
|
|
|
|
2018-07-12 12:18:21 +08:00
|
|
|
# special handling for theme_id cause we need to bump a sequence number
|
|
|
|
if attributes.key?(:theme_ids)
|
|
|
|
user_guardian = Guardian.new(user)
|
2018-08-10 19:12:02 +08:00
|
|
|
attributes[:theme_ids].reject!(&:blank?)
|
2018-07-12 12:18:21 +08:00
|
|
|
attributes[:theme_ids].map!(&:to_i)
|
|
|
|
if user_guardian.allow_themes?(attributes[:theme_ids])
|
|
|
|
user.user_option.theme_key_seq += 1 if user.user_option.theme_ids != attributes[:theme_ids]
|
|
|
|
else
|
|
|
|
attributes.delete(:theme_ids)
|
|
|
|
end
|
2017-05-16 00:48:08 +08:00
|
|
|
end
|
|
|
|
|
2019-01-28 19:19:50 +08:00
|
|
|
if attributes.key?(:text_size)
|
|
|
|
user.user_option.text_size_seq += 1 if user.user_option.text_size.to_s !=
|
|
|
|
attributes[:text_size]
|
|
|
|
end
|
|
|
|
|
2016-02-17 12:46:19 +08:00
|
|
|
OPTION_ATTR.each do |attribute|
|
2016-02-25 21:05:40 +08:00
|
|
|
if attributes.key?(attribute)
|
2016-02-17 12:46:19 +08:00
|
|
|
save_options = true
|
|
|
|
|
2019-05-07 09:27:05 +08:00
|
|
|
if [true, false].include?(user.user_option.public_send(attribute))
|
2016-02-17 12:46:19 +08:00
|
|
|
val = attributes[attribute].to_s == "true"
|
2019-05-07 09:27:05 +08:00
|
|
|
user.user_option.public_send("#{attribute}=", val)
|
2016-02-17 12:46:19 +08:00
|
|
|
else
|
2019-05-07 09:27:05 +08:00
|
|
|
user.user_option.public_send("#{attribute}=", attributes[attribute])
|
2016-02-17 12:46:19 +08:00
|
|
|
end
|
2013-11-02 05:06:20 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-08 00:27:10 +08:00
|
|
|
if attributes.key?(:skip_new_user_tips) && user.user_option.skip_new_user_tips
|
|
|
|
user.user_option.seen_popups = [-1]
|
2022-10-12 23:38:45 +08:00
|
|
|
end
|
|
|
|
|
2016-11-28 22:52:35 +08:00
|
|
|
# automatically disable digests when mailing_list_mode is enabled
|
|
|
|
user.user_option.email_digests = false if user.user_option.mailing_list_mode
|
|
|
|
|
2014-09-23 01:23:15 +08:00
|
|
|
fields = attributes[:custom_fields]
|
2014-09-27 02:48:34 +08:00
|
|
|
user.custom_fields = user.custom_fields.merge(fields) if fields.present?
|
2014-06-11 13:50:37 +08:00
|
|
|
|
2017-03-16 14:44:09 +08:00
|
|
|
saved = nil
|
|
|
|
|
2020-04-24 06:26:29 +08:00
|
|
|
User.transaction do
|
|
|
|
update_muted_users(attributes[:muted_usernames]) if attributes.key?(:muted_usernames)
|
2015-03-24 08:55:22 +08:00
|
|
|
|
2020-07-21 05:23:49 +08:00
|
|
|
if attributes.key?(:allowed_pm_usernames)
|
|
|
|
update_allowed_pm_users(attributes[:allowed_pm_usernames])
|
|
|
|
end
|
|
|
|
|
2022-11-18 22:37:21 +08:00
|
|
|
if attributes.key?(:discourse_connect)
|
|
|
|
update_discourse_connect(attributes[:discourse_connect])
|
|
|
|
end
|
|
|
|
|
2022-03-04 00:17:02 +08:00
|
|
|
if attributes.key?(:user_associated_accounts)
|
|
|
|
updated_associated_accounts(attributes[:user_associated_accounts])
|
|
|
|
end
|
|
|
|
|
2022-06-30 14:54:20 +08:00
|
|
|
if attributes.key?(:sidebar_category_ids)
|
2022-12-01 09:32:35 +08:00
|
|
|
SidebarSectionLinksUpdater.update_category_section_links(
|
|
|
|
user,
|
|
|
|
category_ids: attributes[:sidebar_category_ids],
|
|
|
|
)
|
2022-06-30 14:54:20 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
if attributes.key?(:sidebar_tag_names) && SiteSetting.tagging_enabled
|
2022-12-01 09:32:35 +08:00
|
|
|
SidebarSectionLinksUpdater.update_tag_section_links(
|
|
|
|
user,
|
|
|
|
tag_names: attributes[:sidebar_tag_names],
|
|
|
|
)
|
2022-06-30 14:54:20 +08:00
|
|
|
end
|
|
|
|
|
2022-10-13 03:35:25 +08:00
|
|
|
if SiteSetting.enable_user_status?
|
2022-11-17 01:42:56 +08:00
|
|
|
update_user_status(attributes[:status]) if attributes.has_key?(:status)
|
2022-10-13 03:35:25 +08:00
|
|
|
end
|
|
|
|
|
2020-04-24 06:26:29 +08:00
|
|
|
name_changed = user.name_changed?
|
2022-10-13 03:35:25 +08:00
|
|
|
saved =
|
|
|
|
(!save_options || user.user_option.save) &&
|
|
|
|
(user_notification_schedule.nil? || user_notification_schedule.save) &&
|
|
|
|
user_profile.save && user.save
|
|
|
|
|
|
|
|
if saved && (name_changed && old_user_name.casecmp(attributes.fetch(:name)) != 0)
|
2020-04-24 06:26:29 +08:00
|
|
|
StaffActionLogger.new(@actor).log_name_change(
|
|
|
|
user.id,
|
|
|
|
old_user_name,
|
|
|
|
attributes.fetch(:name) { "" },
|
|
|
|
)
|
2017-03-01 16:12:17 +08:00
|
|
|
end
|
2023-05-26 08:26:38 +08:00
|
|
|
DiscourseEvent.trigger(:within_user_updater_transaction, user, attributes)
|
2020-04-23 03:50:45 +08:00
|
|
|
rescue Addressable::URI::InvalidURIError => e
|
|
|
|
# Prevent 500 for crazy url input
|
|
|
|
return saved
|
2014-05-28 01:54:04 +08:00
|
|
|
end
|
2017-03-16 14:44:09 +08:00
|
|
|
|
2021-01-21 00:31:52 +08:00
|
|
|
if saved
|
|
|
|
if user_notification_schedule
|
2021-01-23 03:02:11 +08:00
|
|
|
if user_notification_schedule.enabled
|
|
|
|
user_notification_schedule.create_do_not_disturb_timings(delete_existing: true)
|
2023-01-09 20:20:10 +08:00
|
|
|
else
|
2021-01-23 03:02:11 +08:00
|
|
|
user_notification_schedule.destroy_scheduled_timings
|
2023-01-09 20:20:10 +08:00
|
|
|
end
|
2021-01-21 00:31:52 +08:00
|
|
|
end
|
2022-11-22 02:57:02 +08:00
|
|
|
if attributes.key?(:seen_popups) || attributes.key?(:skip_new_user_tips)
|
2023-01-30 19:48:09 +08:00
|
|
|
MessageBus.publish(
|
|
|
|
"/user-tips/#{user.id}",
|
|
|
|
user.user_option.seen_popups,
|
|
|
|
user_ids: [user.id],
|
|
|
|
)
|
2022-11-22 02:57:02 +08:00
|
|
|
end
|
2021-01-21 00:31:52 +08:00
|
|
|
DiscourseEvent.trigger(:user_updated, user)
|
|
|
|
end
|
|
|
|
|
2017-03-16 14:44:09 +08:00
|
|
|
saved
|
2013-11-02 05:06:20 +08:00
|
|
|
end
|
|
|
|
|
2015-03-24 08:55:22 +08:00
|
|
|
def update_muted_users(usernames)
|
|
|
|
usernames ||= ""
|
2019-03-20 18:18:46 +08:00
|
|
|
desired_usernames = usernames.split(",").reject { |username| user.username == username }
|
|
|
|
desired_ids = User.where(username: desired_usernames).pluck(:id)
|
2015-03-24 08:55:22 +08:00
|
|
|
if desired_ids.empty?
|
|
|
|
MutedUser.where(user_id: user.id).destroy_all
|
|
|
|
else
|
2015-03-31 07:16:11 +08:00
|
|
|
MutedUser.where("user_id = ? AND muted_user_id not in (?)", user.id, desired_ids).destroy_all
|
2015-03-24 08:55:22 +08:00
|
|
|
|
|
|
|
# SQL is easier here than figuring out how to do the same in AR
|
2018-06-19 14:13:14 +08:00
|
|
|
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
|
2019-03-06 19:21:58 +08:00
|
|
|
WHERE id in (:desired_ids)
|
|
|
|
ON CONFLICT DO NOTHING
|
2018-06-19 14:13:14 +08:00
|
|
|
SQL
|
2015-03-24 08:55:22 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-21 05:23:49 +08:00
|
|
|
def update_allowed_pm_users(usernames)
|
|
|
|
usernames ||= ""
|
|
|
|
desired_usernames = usernames.split(",").reject { |username| user.username == username }
|
2020-07-31 23:52:19 +08:00
|
|
|
desired_ids = User.where(username: desired_usernames).pluck(:id)
|
|
|
|
|
2020-07-21 05:23:49 +08:00
|
|
|
if desired_ids.empty?
|
|
|
|
AllowedPmUser.where(user_id: user.id).destroy_all
|
|
|
|
else
|
|
|
|
AllowedPmUser.where(
|
|
|
|
"user_id = ? AND allowed_pm_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.zone.now, user_id: user.id, desired_ids: desired_ids)
|
|
|
|
INSERT into allowed_pm_users(user_id, allowed_pm_user_id, created_at, updated_at)
|
|
|
|
SELECT :user_id, id, :now, :now
|
|
|
|
FROM users
|
|
|
|
WHERE id in (:desired_ids)
|
|
|
|
ON CONFLICT DO NOTHING
|
|
|
|
SQL
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-03-04 00:17:02 +08:00
|
|
|
def updated_associated_accounts(associations)
|
|
|
|
associations.each do |association|
|
|
|
|
user_associated_account =
|
|
|
|
UserAssociatedAccount.find_or_initialize_by(
|
|
|
|
user_id: user.id,
|
|
|
|
provider_name: association[:provider_name],
|
|
|
|
)
|
|
|
|
if association[:provider_uid].present?
|
|
|
|
user_associated_account.update!(provider_uid: association[:provider_uid])
|
|
|
|
else
|
|
|
|
user_associated_account.destroy!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-03-31 07:16:11 +08:00
|
|
|
private
|
|
|
|
|
2022-10-13 03:35:25 +08:00
|
|
|
def update_user_status(status)
|
|
|
|
if status.blank?
|
|
|
|
@user.clear_status!
|
|
|
|
else
|
|
|
|
@user.set_status!(status[:description], status[:emoji], status[:ends_at])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-18 22:37:21 +08:00
|
|
|
def update_discourse_connect(discourse_connect)
|
|
|
|
external_id = discourse_connect[:external_id]
|
|
|
|
sso = SingleSignOnRecord.find_or_initialize_by(user_id: user.id)
|
|
|
|
|
|
|
|
if external_id.present?
|
|
|
|
sso.update!(
|
|
|
|
external_id: discourse_connect[:external_id],
|
|
|
|
last_payload: "external_id=#{discourse_connect[:external_id]}",
|
|
|
|
)
|
|
|
|
else
|
|
|
|
sso.destroy!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-03-31 07:16:11 +08:00
|
|
|
attr_reader :user, :guardian
|
|
|
|
|
2013-11-02 05:06:20 +08:00
|
|
|
def format_url(website)
|
2016-02-06 04:49:48 +08:00
|
|
|
return nil if website.blank?
|
2023-01-21 02:52:49 +08:00
|
|
|
website =~ /\Ahttp/ ? website : "http://#{website}"
|
2013-11-02 05:06:20 +08:00
|
|
|
end
|
|
|
|
end
|