mirror of
https://github.com/discourse/discourse.git
synced 2025-02-17 21:32:46 +08:00
![Martin Brennan](/assets/img/avatar_default.png)
When replying to a user_private_message email originating from a group PM that does _not_ have a reply key (e.g. when replying directly to the group's SMTP address), we were mistakenly linking the new post created from the reply to the OP and the user who created the topic, based on the first IncomingEmail message ID in the topic, rather than using the correct reply to user and post number that the user actually replied to. We now use the In-Reply-To header to look up the corresponding EmailLog record when the user who replied was sent a user_private_message email, and use the post from that as the reply_to_user/post. This also removes superfluous filtering of incoming_email records. After already filtering by message_id and then addressed_to_user (which only returns incoming emails where the to, from, or cc address includes any of the user's emails), we were filtering again but in the ruby code for the exact same conditions. After removing this all existing tests still pass.
112 lines
2.7 KiB
Ruby
112 lines
2.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class EmailLog < ActiveRecord::Base
|
|
CRITICAL_EMAIL_TYPES ||= Set.new %w{
|
|
account_created
|
|
admin_login
|
|
confirm_new_email
|
|
confirm_old_email
|
|
confirm_old_email_add
|
|
forgot_password
|
|
notify_old_email
|
|
notify_old_email_add
|
|
signup
|
|
signup_after_approval
|
|
}
|
|
|
|
belongs_to :user
|
|
belongs_to :post
|
|
has_one :topic, through: :post
|
|
|
|
validates :email_type, :to_address, presence: true
|
|
|
|
scope :bounced, -> { where(bounced: true) }
|
|
|
|
scope :addressed_to_user, ->(user) do
|
|
where(<<~SQL, user_id: user.id)
|
|
EXISTS(
|
|
SELECT 1
|
|
FROM user_emails
|
|
WHERE user_emails.user_id = :user_id AND
|
|
email_logs.to_address = user_emails.email
|
|
)
|
|
SQL
|
|
end
|
|
|
|
after_create do
|
|
# Update last_emailed_at if the user_id is present and email was sent
|
|
User.where(id: user_id).update_all("last_emailed_at = CURRENT_TIMESTAMP") if user_id.present?
|
|
end
|
|
|
|
def self.unique_email_per_post(post, user)
|
|
return yield unless post && user
|
|
|
|
DistributedMutex.synchronize("email_log_#{post.id}_#{user.id}") do
|
|
if where(post_id: post.id, user_id: user.id).exists?
|
|
nil
|
|
else
|
|
yield
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.reached_max_emails?(user, email_type = nil)
|
|
return false if SiteSetting.max_emails_per_day_per_user == 0 || CRITICAL_EMAIL_TYPES.include?(email_type)
|
|
|
|
count = where('created_at > ?', 1.day.ago)
|
|
.where(user_id: user.id)
|
|
.count
|
|
|
|
count >= SiteSetting.max_emails_per_day_per_user
|
|
end
|
|
|
|
def self.count_per_day(start_date, end_date)
|
|
where("created_at BETWEEN ? AND ?", start_date, end_date)
|
|
.group("DATE(created_at)")
|
|
.order("DATE(created_at)")
|
|
.count
|
|
end
|
|
|
|
def self.for(reply_key)
|
|
self.find_by(reply_key: reply_key)
|
|
end
|
|
|
|
def self.last_sent_email_address
|
|
self.where(email_type: "signup")
|
|
.order(created_at: :desc)
|
|
.limit(1)
|
|
.pluck(:to_address)
|
|
.first
|
|
end
|
|
|
|
def bounce_key
|
|
super&.delete('-')
|
|
end
|
|
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: email_logs
|
|
#
|
|
# id :integer not null, primary key
|
|
# to_address :string not null
|
|
# email_type :string not null
|
|
# user_id :integer
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# post_id :integer
|
|
# bounce_key :uuid
|
|
# bounced :boolean default(FALSE), not null
|
|
# message_id :string
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_email_logs_on_bounce_key (bounce_key) UNIQUE WHERE (bounce_key IS NOT NULL)
|
|
# index_email_logs_on_bounced (bounced)
|
|
# index_email_logs_on_created_at (created_at)
|
|
# index_email_logs_on_message_id (message_id)
|
|
# index_email_logs_on_post_id (post_id)
|
|
# index_email_logs_on_user_id (user_id)
|
|
#
|