mirror of
https://github.com/discourse/discourse.git
synced 2026-08-04 10:39:43 +08:00
For upcoming changes, we were having a problem where we
were notifying admins of upcoming changes in brand new sites.
A previous fix 2e5afb46e2f5c0547bc298b2bd1a9c39e813dc17 tried
to remedy this, but since it relies on the `new_site?` method, which
isn't true after 1 hour, the next time the scheduled notifications are
run for an admin they will be sent them even if their site is relatively
new.
This commit attempts to conclusively fix the issue by inserting
upcoming change `admins_notified_automatic_promotion` and
`admins_notified_available_change` events via `BackfillNotifiedEvents`
for changes that exist in particular statuses when the site is first
created. These events will have `event_data: {"backfilled" => true}`
to distinguish them from others.
It also fixes an adjacent issue where an admin was notified of a
plugin's upcoming changes as soon as they enable the plugin,
which isn't useful as the intent of upcoming changes is to notify
of changes for things the admin has already been using.
**Before this change, on brand new site with current upcoming change
state**
<img width="401" height="429" alt="image"
src="https://github.com/user-attachments/assets/a89cd641-59aa-4c48-a57c-9692021df176"
/>
**After this change**
No notification to show :) But when I moved an upcoming change to `beta`
it showed
the notification for the enabled upcoming change correctly:
<img width="378" height="174" alt="image"
src="https://github.com/user-attachments/assets/20e4096f-e83f-4c06-970d-a9b43561b12e"
/>
422 lines
10 KiB
Ruby
Vendored
422 lines
10 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class CurrentUserSerializer < BasicUserSerializer
|
|
include UserTagNotificationsMixin
|
|
include UserSidebarMixin
|
|
include UserStatusMixin
|
|
|
|
attributes :name,
|
|
:unread_notifications,
|
|
:unread_high_priority_notifications,
|
|
:all_unread_notifications_count,
|
|
:read_first_notification?,
|
|
:admin?,
|
|
:notification_channel_position,
|
|
:do_not_disturb_channel_position,
|
|
:moderator?,
|
|
:staff?,
|
|
:whisperer?,
|
|
:title,
|
|
:any_posts,
|
|
:trust_level,
|
|
:can_send_private_email_messages,
|
|
:can_send_private_messages,
|
|
:can_upload_avatar,
|
|
:can_edit,
|
|
:can_invite_to_forum,
|
|
:can_create_admin_invite,
|
|
:no_password,
|
|
:can_delete_account,
|
|
:can_post_anonymously,
|
|
:can_ignore_users,
|
|
:can_edit_tags,
|
|
:can_delete_all_posts_and_topics,
|
|
:custom_fields,
|
|
:muted_category_ids,
|
|
:indirectly_muted_category_ids,
|
|
:regular_category_ids,
|
|
:tracked_category_ids,
|
|
:watched_first_post_category_ids,
|
|
:watched_category_ids,
|
|
:watched_tags,
|
|
:watching_first_post_tags,
|
|
:tracked_tags,
|
|
:muted_tags,
|
|
:regular_tags,
|
|
:dismissed_banner_key,
|
|
:is_anonymous,
|
|
:reviewable_count,
|
|
:unseen_reviewable_count,
|
|
:new_personal_messages_notifications_count,
|
|
:read_faq?,
|
|
:previous_visit_at,
|
|
:seen_notification_id,
|
|
:primary_group_id,
|
|
:flair_group_id,
|
|
:can_create_topic,
|
|
:can_set_topic_timer,
|
|
:can_create_category,
|
|
:can_create_group,
|
|
:link_posting_access,
|
|
:external_id,
|
|
:associated_account_ids,
|
|
:top_category_ids,
|
|
:groups,
|
|
:needs_required_fields_check?,
|
|
:second_factor_enabled,
|
|
:ignored_users,
|
|
:featured_topic,
|
|
:do_not_disturb_until,
|
|
:can_review,
|
|
:draft_count,
|
|
:pending_posts_count,
|
|
:grouped_unread_notifications,
|
|
:display_sidebar_tags,
|
|
:sidebar_tags,
|
|
:sidebar_category_ids,
|
|
:sidebar_sections,
|
|
:unified_new_enabled?,
|
|
:can_view_raw_email,
|
|
:login_method,
|
|
:has_unseen_features,
|
|
:has_new_upcoming_changes,
|
|
:can_see_emails,
|
|
:can_localize_content?,
|
|
:effective_locale,
|
|
:can_see_ip,
|
|
:is_impersonating,
|
|
:impersonation_expires_at,
|
|
:can_change_post_owner,
|
|
:show_site_owner_onboarding
|
|
|
|
delegate :user_stat, to: :object, private: true
|
|
delegate :any_posts, :draft_count, :pending_posts_count, :read_faq?, to: :user_stat
|
|
|
|
has_one :user_option, embed: :object, serializer: CurrentUserOptionSerializer
|
|
has_many :all_sidebar_sections,
|
|
embed: :object,
|
|
key: :sidebar_sections,
|
|
serializer: SidebarSectionSerializer
|
|
|
|
def initialize(object, options = {})
|
|
super
|
|
options[:include_status] = true
|
|
end
|
|
|
|
def login_method
|
|
@options[:login_method]
|
|
end
|
|
|
|
def is_impersonating
|
|
!!object.is_impersonating
|
|
end
|
|
|
|
def impersonation_expires_at
|
|
object.impersonation_expires_at
|
|
end
|
|
|
|
def include_can_change_post_owner?
|
|
return true if admin?
|
|
return true if SiteSetting.moderators_change_post_ownership && moderator?
|
|
return true if object.in_any_groups?(SiteSetting.change_post_ownership_allowed_groups_map)
|
|
false
|
|
end
|
|
|
|
def can_change_post_owner
|
|
true
|
|
end
|
|
|
|
def groups
|
|
owned_group_ids = GroupUser.where(user_id: id, owner: true).pluck(:group_id).to_set
|
|
|
|
object
|
|
.visible_groups
|
|
.pluck(:id, :name, :has_messages)
|
|
.map do |id, name, has_messages|
|
|
group = { id: id, name: name, has_messages: has_messages }
|
|
group[:owner] = true if owned_group_ids.include?(id)
|
|
group
|
|
end
|
|
end
|
|
|
|
def link_posting_access
|
|
scope.link_posting_access
|
|
end
|
|
|
|
def can_create_topic
|
|
scope.can_create_topic?(nil)
|
|
end
|
|
|
|
def can_set_topic_timer
|
|
scope.can_set_topic_timer?
|
|
end
|
|
|
|
def can_create_category
|
|
true
|
|
end
|
|
|
|
def include_can_create_category?
|
|
scope.can_create_category?
|
|
end
|
|
|
|
def can_create_group
|
|
true
|
|
end
|
|
|
|
def include_can_create_group?
|
|
scope.can_create_group?
|
|
end
|
|
|
|
def can_send_private_email_messages
|
|
scope.can_send_private_messages_to_email?
|
|
end
|
|
|
|
def can_send_private_messages
|
|
scope.can_send_private_messages?
|
|
end
|
|
|
|
def include_show_site_owner_onboarding?
|
|
SiteSetting.enable_site_owner_onboarding && object.admin? &&
|
|
User.where(admin: true).human_users.minimum(:id) == object.id &&
|
|
object.created_at.after?(SiteSetting.site_owner_onboarding_max_days.days.ago)
|
|
end
|
|
|
|
def show_site_owner_onboarding
|
|
true
|
|
end
|
|
|
|
def include_has_unseen_features?
|
|
object.staff?
|
|
end
|
|
|
|
def has_unseen_features
|
|
DiscourseUpdates.has_unseen_features?(object.id)
|
|
end
|
|
|
|
def include_has_new_upcoming_changes?
|
|
object.staff?
|
|
end
|
|
|
|
def has_new_upcoming_changes
|
|
last_visited = object.custom_fields["last_visited_upcoming_changes_at"]
|
|
return false if last_visited.blank? && object.created_at < Discourse.site_creation_date + 1.hour
|
|
cutoff = last_visited.present? ? Time.zone.parse(last_visited) : object.created_at
|
|
UpcomingChangeEvent.added.not_backfilled.where("created_at > ?", cutoff).exists?
|
|
end
|
|
|
|
def can_post_anonymously
|
|
SiteSetting.allow_anonymous_mode &&
|
|
(is_anonymous || object.in_any_groups?(SiteSetting.anonymous_posting_allowed_groups_map))
|
|
end
|
|
|
|
def can_ignore_users
|
|
scope.can_ignore_users?
|
|
end
|
|
|
|
def can_delete_all_posts_and_topics
|
|
object.in_any_groups?(SiteSetting.delete_all_posts_and_topics_allowed_groups_map)
|
|
end
|
|
|
|
def can_upload_avatar
|
|
!is_anonymous && object.in_any_groups?(SiteSetting.uploaded_avatars_allowed_groups_map)
|
|
end
|
|
|
|
def can_edit
|
|
true
|
|
end
|
|
|
|
def can_edit_tags
|
|
scope.can_edit_tag_names?
|
|
end
|
|
|
|
def can_invite_to_forum
|
|
true
|
|
end
|
|
|
|
def include_can_invite_to_forum?
|
|
scope.can_invite_to_forum?
|
|
end
|
|
|
|
def can_create_admin_invite
|
|
true
|
|
end
|
|
|
|
def include_can_create_admin_invite?
|
|
scope.can_create_admin_invite?
|
|
end
|
|
|
|
def no_password
|
|
true
|
|
end
|
|
|
|
def include_no_password?
|
|
!object.has_password?
|
|
end
|
|
|
|
def can_delete_account
|
|
true
|
|
end
|
|
|
|
def include_can_delete_account?
|
|
scope.can_delete_user?(object)
|
|
end
|
|
|
|
def custom_fields
|
|
fields = nil
|
|
if SiteSetting.public_user_custom_fields.present?
|
|
fields = SiteSetting.public_user_custom_fields.split("|")
|
|
end
|
|
DiscoursePluginRegistry.serialized_current_user_fields.each do |f|
|
|
fields ||= []
|
|
fields << f
|
|
end
|
|
|
|
if fields.present?
|
|
User.custom_fields_for_ids([object.id], fields)[object.id] || {}
|
|
else
|
|
{}
|
|
end
|
|
end
|
|
|
|
def muted_category_ids
|
|
categories_with_notification_level(:muted)
|
|
end
|
|
|
|
def indirectly_muted_category_ids
|
|
CategoryUser.indirectly_muted_category_ids(object)
|
|
end
|
|
|
|
def regular_category_ids
|
|
categories_with_notification_level(:regular)
|
|
end
|
|
|
|
def tracked_category_ids
|
|
categories_with_notification_level(:tracking)
|
|
end
|
|
|
|
def watched_category_ids
|
|
categories_with_notification_level(:watching)
|
|
end
|
|
|
|
def watched_first_post_category_ids
|
|
categories_with_notification_level(:watching_first_post)
|
|
end
|
|
|
|
def ignored_users
|
|
IgnoredUser.where(user: object.id).joins(:ignored_user).pluck(:username)
|
|
end
|
|
|
|
def top_category_ids
|
|
omitted_notification_levels = [
|
|
CategoryUser.notification_levels[:muted],
|
|
CategoryUser.notification_levels[:regular],
|
|
]
|
|
CategoryUser
|
|
.where(user_id: object.id)
|
|
.where.not(notification_level: omitted_notification_levels)
|
|
.order(
|
|
"
|
|
CASE
|
|
WHEN notification_level = 3 THEN 1
|
|
WHEN notification_level = 2 THEN 2
|
|
WHEN notification_level = 4 THEN 3
|
|
END",
|
|
)
|
|
.pluck(:category_id)
|
|
.slice(0, SiteSetting.header_dropdown_category_count)
|
|
end
|
|
|
|
def dismissed_banner_key
|
|
object.user_profile.dismissed_banner_key
|
|
end
|
|
|
|
def is_anonymous
|
|
object.anonymous?
|
|
end
|
|
|
|
def can_review
|
|
scope.can_see_review_queue?
|
|
end
|
|
|
|
def include_primary_group_id?
|
|
object.primary_group_id.present?
|
|
end
|
|
|
|
def external_id
|
|
object&.single_sign_on_record&.external_id
|
|
end
|
|
|
|
def include_external_id?
|
|
SiteSetting.enable_discourse_connect
|
|
end
|
|
|
|
def associated_account_ids
|
|
values = {}
|
|
|
|
object.user_associated_accounts.map do |user_associated_account|
|
|
values[user_associated_account.provider_name] = user_associated_account.provider_uid
|
|
end
|
|
|
|
values
|
|
end
|
|
|
|
def include_associated_account_ids?
|
|
SiteSetting.include_associated_account_ids
|
|
end
|
|
|
|
def second_factor_enabled
|
|
object.totp_enabled? || object.security_keys_enabled?
|
|
end
|
|
|
|
def include_featured_topic?
|
|
scope.can_see_topic?(object.user_profile.featured_topic)
|
|
end
|
|
|
|
def featured_topic
|
|
BasicTopicSerializer.new(object.user_profile.featured_topic, scope: scope, root: false).as_json
|
|
end
|
|
|
|
def unseen_reviewable_count
|
|
Reviewable.unseen_reviewable_count(object)
|
|
end
|
|
|
|
def can_view_raw_email
|
|
scope.can_view_raw_emails?
|
|
end
|
|
|
|
def do_not_disturb_channel_position
|
|
MessageBus.last_id("/do-not-disturb/#{object.id}")
|
|
end
|
|
|
|
def can_see_emails
|
|
scope.can_see_emails?
|
|
end
|
|
|
|
def include_can_see_emails?
|
|
object.staff?
|
|
end
|
|
|
|
def can_localize_content?
|
|
scope.can_localize_content?
|
|
end
|
|
|
|
def include_can_localize_content?
|
|
SiteSetting.content_localization_enabled
|
|
end
|
|
|
|
def effective_locale
|
|
scope.user.effective_locale
|
|
end
|
|
|
|
def include_effective_locale?
|
|
SiteSetting.content_localization_enabled
|
|
end
|
|
|
|
def can_see_ip
|
|
scope.can_see_ip?
|
|
end
|
|
|
|
def include_can_see_ip?
|
|
object.admin? || (object.moderator? && SiteSetting.moderators_view_ips)
|
|
end
|
|
end
|