discourse/app/models/email_log.rb
Martin Brennan e9dc88a7b6
FIX: Link up reply to post correctly when emailing group (#13339)
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.
2021-06-10 15:28:50 +10:00

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)
#