discourse/lib/composer_messages_finder.rb
David Taylor 821bb1e8cb
FEATURE: Rename 'Discourse SSO' to DiscourseConnect ()
The 'Discourse SSO' protocol is being rebranded to DiscourseConnect. This should help to reduce confusion when 'SSO' is used in the generic sense.

This commit aims to:
- Rename `sso_` site settings. DiscourseConnect specific ones are prefixed `discourse_connect_`. Generic settings are prefixed `auth_`
- Add (server-side-only) backwards compatibility for the old setting names, with deprecation notices
- Copy `site_settings` database records to the new names
- Rename relevant translation keys
- Update relevant translations

This commit does **not** aim to:
- Rename any Ruby classes or methods. This might be done in a future commit
- Change any URLs. This would break existing integrations
- Make any changes to the protocol. This would break existing integrations
- Change any functionality. Further normalization across DiscourseConnect and other auth methods will be done separately

The risks are:
- There is no backwards compatibility for site settings on the client-side. Accessing auth-related site settings in Javascript is fairly rare, and an error on the client side would not be security-critical.
- If a plugin is monkey-patching parts of the auth process, changes to locale keys could cause broken error messages. This should also be unlikely. The old site setting names remain functional, so security-related overrides will remain working.

A follow-up commit will be made with a post-deploy migration to delete the old `site_settings` rows.
2021-02-08 10:04:33 +00:00

235 lines
7.3 KiB
Ruby

# frozen_string_literal: true
class ComposerMessagesFinder
def initialize(user, details)
@user = user
@details = details
@topic = Topic.find_by(id: details[:topic_id]) if details[:topic_id]
end
def self.check_methods
@check_methods ||= instance_methods.find_all { |m| m =~ /^check\_/ }
end
def find
return if editing_post?
self.class.check_methods.each do |m|
msg = public_send(m)
return msg if msg.present?
end
nil
end
# Determines whether to show the user education text
def check_education_message
return if @topic&.private_message?
if creating_topic?
count = @user.created_topic_count
education_key = 'education.new-topic'
else
count = @user.post_count
education_key = 'education.new-reply'
end
if count < SiteSetting.educate_until_posts
return {
id: 'education',
templateName: 'education',
wait_for_typing: true,
body: PrettyText.cook(
I18n.t(
education_key,
education_posts_text: I18n.t('education.until_posts', count: SiteSetting.educate_until_posts),
site_name: SiteSetting.title,
base_path: Discourse.base_path
)
)
}
end
nil
end
# New users have a limited number of replies in a topic
def check_new_user_many_replies
return unless replying? && @user.posted_too_much_in_topic?(@details[:topic_id])
{
id: 'too_many_replies',
templateName: 'education',
body: PrettyText.cook(I18n.t('education.too_many_replies', newuser_max_replies_per_topic: SiteSetting.newuser_max_replies_per_topic))
}
end
# Should a user be contacted to update their avatar?
def check_avatar_notification
# A user has to be basic at least to be considered for an avatar notification
return unless @user.has_trust_level?(TrustLevel[1])
# We don't notify users who have avatars or who have been notified already.
return if @user.uploaded_avatar_id || UserHistory.exists_for_user?(@user, :notified_about_avatar)
# Do not notify user if any of the following is true:
# - "disable avatar education message" is enabled
# - "sso overrides avatar" is enabled
# - "allow uploaded avatars" is disabled
return if SiteSetting.disable_avatar_education_message || SiteSetting.discourse_connect_overrides_avatar || !SiteSetting.allow_uploaded_avatars
# If we got this far, log that we've nagged them about the avatar
UserHistory.create!(action: UserHistory.actions[:notified_about_avatar], target_user_id: @user.id)
# Return the message
{
id: 'avatar',
templateName: 'education',
body: PrettyText.cook(I18n.t('education.avatar', profile_path: "/u/#{@user.username_lower}/preferences/account#profile-picture"))
}
end
# Is a user replying too much in succession?
def check_sequential_replies
return unless educate_reply?(:notified_about_sequential_replies)
# Count the posts made by this user in the last day
recent_posts_user_ids = Post.where(topic_id: @details[:topic_id])
.where("created_at > ?", 1.day.ago)
.where(post_type: Post.types[:regular])
.order('created_at desc')
.limit(SiteSetting.sequential_replies_threshold)
.pluck(:user_id)
# Did we get back as many posts as we asked for, and are they all by the current user?
return if recent_posts_user_ids.size != SiteSetting.sequential_replies_threshold ||
recent_posts_user_ids.detect { |u| u != @user.id }
# If we got this far, log that we've nagged them about the sequential replies
UserHistory.create!(action: UserHistory.actions[:notified_about_sequential_replies],
target_user_id: @user.id,
topic_id: @details[:topic_id])
{
id: 'sequential_replies',
templateName: 'education',
wait_for_typing: true,
extraClass: 'education-message',
body: PrettyText.cook(I18n.t('education.sequential_replies'))
}
end
def check_dominating_topic
return unless educate_reply?(:notified_about_dominating_topic)
return if @topic.blank? ||
@topic.user_id == @user.id ||
@topic.posts_count < SiteSetting.summary_posts_required ||
@topic.private_message?
posts_by_user = @user.posts.where(topic_id: @topic.id).count
ratio = (posts_by_user.to_f / @topic.posts_count.to_f)
return if ratio < (SiteSetting.dominating_topic_minimum_percent.to_f / 100.0)
# Log the topic notification
UserHistory.create!(action: UserHistory.actions[:notified_about_dominating_topic],
target_user_id: @user.id,
topic_id: @details[:topic_id])
{
id: 'dominating_topic',
templateName: 'education',
wait_for_typing: true,
extraClass: 'education-message',
body: PrettyText.cook(I18n.t('education.dominating_topic', percent: (ratio * 100).round))
}
end
def check_get_a_room(min_users_posted: 5)
return unless educate_reply?(:notified_about_get_a_room)
return unless @details[:post_id].present?
reply_to_user_id = Post.where(id: @details[:post_id]).pluck(:user_id)[0]
# Users's last x posts in the topic
last_x_replies = @topic.
posts.
where(user_id: @user.id).
order('created_at desc').
limit(SiteSetting.get_a_room_threshold).
pluck(:reply_to_user_id).
find_all { |uid| uid != @user.id && uid == reply_to_user_id }
return unless last_x_replies.size == SiteSetting.get_a_room_threshold
return unless @topic.posts.count('distinct user_id') >= min_users_posted
UserHistory.create!(action: UserHistory.actions[:notified_about_get_a_room],
target_user_id: @user.id,
topic_id: @details[:topic_id])
reply_username = User.where(id: last_x_replies[0]).pluck_first(:username)
{
id: 'get_a_room',
templateName: 'education',
wait_for_typing: true,
extraClass: 'education-message',
body: PrettyText.cook(
I18n.t(
'education.get_a_room',
count: SiteSetting.get_a_room_threshold,
reply_username: reply_username,
base_path: Discourse.base_path
)
)
}
end
def check_reviving_old_topic
return unless replying?
return if @topic.nil? ||
SiteSetting.warn_reviving_old_topic_age < 1 ||
@topic.last_posted_at.nil? ||
@topic.last_posted_at > SiteSetting.warn_reviving_old_topic_age.days.ago
{
id: 'reviving_old',
templateName: 'education',
wait_for_typing: false,
extraClass: 'education-message',
body: PrettyText.cook(
I18n.t(
'education.reviving_old_topic',
time_ago: FreedomPatches::Rails4.time_ago_in_words(@topic.last_posted_at, false, scope: :'datetime.distance_in_words_verbose')
)
)
}
end
private
def educate_reply?(type)
replying? &&
@details[:topic_id] &&
(@topic.present? && !@topic.private_message?) &&
(@user.post_count >= SiteSetting.educate_until_posts) &&
!UserHistory.exists_for_user?(@user, type, topic_id: @details[:topic_id])
end
def creating_topic?
@details[:composer_action] == "createTopic"
end
def replying?
@details[:composer_action] == "reply"
end
def editing_post?
@details[:composer_action] == "edit"
end
end