FEATURE: Add attachments to outgoing emails

This feature is off by default and can can be configured with the `email_total_attachment_size_limit_kb` site setting.

Co-authored-by: Maja Komel <maja.komel@gmail.com>
This commit is contained in:
Gerhard Schlager 2019-07-25 14:04:00 +02:00
parent 0e1d6151b9
commit 7e0eeed292
5 changed files with 101 additions and 0 deletions

View File

@ -1812,6 +1812,7 @@ en:
enable_forwarded_emails: "[BETA] Allow users to create a topic by forwarding an email in."
always_show_trimmed_content: "Always show trimmed part of incoming emails. WARNING: might reveal email addresses."
private_email: "Don't include content from posts or topics in email title or email body. NOTE: also disables digest emails."
email_total_attachment_size_limit_kb: "Max total size of files attached to outgoing emails. Set to 0 to disable sending of attachments."
manual_polling_enabled: "Push emails using the API for email replies."
pop3_polling_enabled: "Poll via POP3 for email replies."

View File

@ -1021,6 +1021,9 @@ email:
enable_forwarded_emails: false
always_show_trimmed_content: false
private_email: false
email_total_attachment_size_limit_kb:
default: 0
max: 51200
files:
max_image_size_kb:

View File

@ -100,6 +100,8 @@ module Email
# guards against deleted posts
return skip(SkippedEmailLog.reason_types[:sender_post_deleted]) unless post
add_attachments(post)
topic = post.topic
first_post = topic.ordered_posts.first
@ -239,6 +241,38 @@ module Email
private
def add_attachments(post)
max_email_size = SiteSetting.email_total_attachment_size_limit_kb.kilobytes
return if max_email_size == 0
email_size = 0
post.uploads.each do |upload|
next if FileHelper.is_supported_image?(upload.original_filename)
next if email_size + upload.filesize > max_email_size
begin
path = if upload.local?
Discourse.store.path_for(upload)
else
Discourse.store.download(upload).path
end
@message.attachments[upload.original_filename] = File.read(path)
email_size += File.size(path)
rescue => e
Discourse.warn_exception(
e,
message: "Failed to attach file to email",
env: {
post_id: post.id,
upload_id: upload.id,
filename: upload.original_filename
}
)
end
end
end
def header_value(name)
header = @message.header[name]
return nil unless header

View File

@ -351,6 +351,69 @@ describe Email::Sender do
end
end
context "with attachments" do
fab!(:small_pdf) do
SiteSetting.authorized_extensions = 'pdf'
UploadCreator.new(file_from_fixtures("small.pdf", "pdf"), "small.pdf")
.create_for(Discourse.system_user.id)
end
fab!(:large_pdf) do
SiteSetting.authorized_extensions = 'pdf'
UploadCreator.new(file_from_fixtures("large.pdf", "pdf"), "large.pdf")
.create_for(Discourse.system_user.id)
end
fab!(:csv_file) do
SiteSetting.authorized_extensions = 'csv'
UploadCreator.new(file_from_fixtures("words.csv", "csv"), "words.csv")
.create_for(Discourse.system_user.id)
end
fab!(:image) do
SiteSetting.authorized_extensions = 'png'
UploadCreator.new(file_from_fixtures("logo.png", "images"), "logo.png")
.create_for(Discourse.system_user.id)
end
fab!(:post) { Fabricate(:post) }
fab!(:reply) do
raw = <<~RAW
Hello world!
#{DiscourseMarkdown.attachment_markdown(small_pdf)}
#{DiscourseMarkdown.attachment_markdown(large_pdf)}
#{DiscourseMarkdown.image_markdown(image)}
#{DiscourseMarkdown.attachment_markdown(csv_file)}
RAW
reply = Fabricate(:post, raw: raw, topic: post.topic, user: Fabricate(:user))
reply.link_post_uploads
reply
end
fab!(:notification) { Fabricate(:posted_notification, user: post.user, post: reply) }
let(:message) do
UserNotifications.user_posted(
post.user,
post: reply,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
end
it "adds only non-image uploads as attachments to the email" do
SiteSetting.email_total_attachment_size_limit_kb = 10_000
Email::Sender.new(message, :valid_type).send
expect(message.attachments.length).to eq(3)
expect(message.attachments.map(&:filename))
.to contain_exactly(*[small_pdf, large_pdf, csv_file].map(&:original_filename))
end
it "respects the size limit and attaches only files that fit into the max email size" do
SiteSetting.email_total_attachment_size_limit_kb = 40
Email::Sender.new(message, :valid_type).send
expect(message.attachments.length).to eq(2)
expect(message.attachments.map(&:filename))
.to contain_exactly(*[small_pdf, csv_file].map(&:original_filename))
end
end
context 'with a deleted post' do
it 'should skip sending the email' do

BIN
spec/fixtures/pdf/large.pdf vendored Normal file

Binary file not shown.