mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 19:15:32 +08:00
206 lines
7.4 KiB
Ruby
206 lines
7.4 KiB
Ruby
class PostAlertObserver < ActiveRecord::Observer
|
|
observe :post, :post_action, :post_revision
|
|
|
|
# Dispatch to an after_save_#{class_name} method
|
|
def after_save(model)
|
|
method_name = callback_for('after_save', model)
|
|
send(method_name, model) if respond_to?(method_name)
|
|
end
|
|
|
|
# Dispatch to an after_create_#{class_name} method
|
|
def after_create(model)
|
|
method_name = callback_for('after_create', model)
|
|
send(method_name, model) if respond_to?(method_name)
|
|
end
|
|
|
|
# We need to consider new people to mention / quote when a post is edited
|
|
def after_save_post(post)
|
|
return if post.topic.private_message?
|
|
|
|
mentioned_users = extract_mentioned_users(post)
|
|
quoted_users = extract_quoted_users(post)
|
|
|
|
reply_to_user = post.reply_notification_target
|
|
notify_users(mentioned_users - [reply_to_user], :mentioned, post)
|
|
notify_users(quoted_users - mentioned_users - [reply_to_user], :quoted, post)
|
|
end
|
|
|
|
def after_save_post_action(post_action)
|
|
# We only care about deleting post actions for now
|
|
return if post_action.deleted_at.blank?
|
|
Notification.where(post_action_id: post_action.id).each(&:destroy)
|
|
end
|
|
|
|
def after_create_post_action(post_action)
|
|
# We only notify on likes for now
|
|
return unless post_action.is_like?
|
|
|
|
post = post_action.post
|
|
return if post_action.user.blank?
|
|
return if post.topic.private_message?
|
|
|
|
create_notification(post.user,
|
|
Notification.types[:liked],
|
|
post,
|
|
display_username: post_action.user.username,
|
|
post_action_id: post_action.id)
|
|
end
|
|
|
|
def after_create_post_revision(post_revision)
|
|
post = post_revision.post
|
|
|
|
return unless post
|
|
return if post_revision.user.blank?
|
|
return if post_revision.user_id == post.user_id
|
|
return if post.topic.private_message?
|
|
|
|
create_notification(post.user, Notification.types[:edited], post, display_username: post_revision.user.username)
|
|
end
|
|
|
|
def after_create_post(post)
|
|
if post.topic.private_message?
|
|
# If it's a private message, notify the topic_allowed_users
|
|
post.topic.all_allowed_users.reject{ |user| user.id == post.user_id }.each do |user|
|
|
next if user.blank?
|
|
|
|
if TopicUser.get(post.topic, user).try(:notification_level) == TopicUser.notification_levels[:tracking]
|
|
next unless post.reply_to_post_number
|
|
next unless post.reply_to_post.user_id == user.id
|
|
end
|
|
|
|
create_notification(user, Notification.types[:private_message], post)
|
|
end
|
|
elsif post.post_type != Post.types[:moderator_action]
|
|
# If it's not a private message and it's not an automatic post caused by a moderator action, notify the users
|
|
notify_post_users(post)
|
|
end
|
|
end
|
|
|
|
protected
|
|
|
|
def callback_for(action, model)
|
|
"#{action}_#{model.class.name.underscore.gsub(/.+\//, '')}"
|
|
end
|
|
|
|
def unread_posts(user, topic)
|
|
Post.where('post_number > COALESCE((
|
|
SELECT last_read_post_number FROM topic_users tu
|
|
WHERE tu.user_id = ? AND tu.topic_id = ? ),0)',
|
|
user.id, topic.id)
|
|
.where('reply_to_user_id = ? OR exists(
|
|
SELECT 1 from topic_users tu
|
|
WHERE tu.user_id = ? AND
|
|
tu.topic_id = ? AND
|
|
notification_level = ?
|
|
)', user.id, user.id, topic.id, TopicUser.notification_levels[:watching])
|
|
.where(topic_id: topic.id)
|
|
end
|
|
|
|
def first_unread_post(user, topic)
|
|
unread_posts(user, topic).order('post_number').first
|
|
end
|
|
|
|
def unread_count(user, topic)
|
|
unread_posts(user, topic).count
|
|
end
|
|
|
|
def destroy_notifications(user, type, topic)
|
|
return if user.blank?
|
|
return unless Guardian.new(user).can_see?(topic)
|
|
|
|
user.notifications.where(notification_type: type,
|
|
topic_id: topic.id).destroy_all
|
|
end
|
|
|
|
def create_notification(user, type, post, opts={})
|
|
return if user.blank?
|
|
|
|
# Make sure the user can see the post
|
|
return unless Guardian.new(user).can_see?(post)
|
|
|
|
# skip if muted on the topic
|
|
return if TopicUser.get(post.topic, user).try(:notification_level) == TopicUser.notification_levels[:muted]
|
|
|
|
# Don't notify the same user about the same notification on the same post
|
|
return if user.notifications.exists?(notification_type: type, topic_id: post.topic_id, post_number: post.post_number)
|
|
|
|
collapsed = false
|
|
|
|
if type == Notification.types[:replied] ||
|
|
type == Notification.types[:posted]
|
|
|
|
destroy_notifications(user, Notification.types[:replied] , post.topic)
|
|
destroy_notifications(user, Notification.types[:posted] , post.topic)
|
|
collapsed = true
|
|
end
|
|
|
|
if type == Notification.types[:private_message]
|
|
destroy_notifications(user, type, post.topic)
|
|
collapsed = true
|
|
end
|
|
|
|
original_post = post
|
|
original_username = opts[:display_username] || post.username
|
|
|
|
if collapsed
|
|
post = first_unread_post(user,post.topic) || post
|
|
count = unread_count(user, post.topic)
|
|
opts[:display_username] = I18n.t('embed.replies', count: count) if count > 1
|
|
end
|
|
|
|
UserActionObserver.log_notification(original_post, user, type)
|
|
|
|
# Create the notification
|
|
user.notifications.create(notification_type: type,
|
|
topic_id: post.topic_id,
|
|
post_number: post.post_number,
|
|
post_action_id: opts[:post_action_id],
|
|
data: { topic_title: post.topic.title,
|
|
original_post_id: original_post.id,
|
|
original_username: original_username,
|
|
display_username: opts[:display_username] || post.user.username }.to_json)
|
|
end
|
|
|
|
# TODO: Move to post-analyzer?
|
|
# Returns a list users who have been mentioned
|
|
def extract_mentioned_users(post)
|
|
User.where(username_lower: post.raw_mentions).where("id <> ?", post.user_id)
|
|
end
|
|
|
|
# TODO: Move to post-analyzer?
|
|
# Returns a list of users who were quoted in the post
|
|
def extract_quoted_users(post)
|
|
post.raw.scan(/\[quote=\"([^,]+),.+\"\]/).uniq.map do |m|
|
|
User.where("username_lower = :username and id != :id", username: m.first.strip.downcase, id: post.user_id).first
|
|
end.compact
|
|
end
|
|
|
|
# Notify a bunch of users
|
|
def notify_users(users, type, post)
|
|
users = [users] unless users.is_a?(Array)
|
|
users.each do |u|
|
|
create_notification(u, Notification.types[type], post)
|
|
end
|
|
end
|
|
|
|
# TODO: This should use javascript for parsing rather than re-doing it this way.
|
|
def notify_post_users(post)
|
|
# Is this post a reply to a user?
|
|
reply_to_user = post.reply_notification_target
|
|
notify_users(reply_to_user, :replied, post)
|
|
|
|
exclude_user_ids = [] <<
|
|
post.user_id <<
|
|
extract_mentioned_users(post).map(&:id) <<
|
|
extract_quoted_users(post).map(&:id)
|
|
|
|
exclude_user_ids << reply_to_user.id if reply_to_user.present?
|
|
exclude_user_ids.flatten!
|
|
TopicUser
|
|
.where(topic_id: post.topic_id, notification_level: TopicUser.notification_levels[:watching])
|
|
.includes(:user).each do |tu|
|
|
create_notification(tu.user, Notification.types[:posted], post) unless exclude_user_ids.include?(tu.user_id)
|
|
end
|
|
end
|
|
end
|