Skip to content

Commit ff2e38c

Browse files
List inline attachments separately in AM previews
This commit separates inline attachments from normal attachments when listing attachments in Action Mailer previews. For example, attachments that were previously listed like > Attachments: logo.png file1.pdf file2.pdf will now be listed like > Attachments: file1.pdf file2.pdf (Inline: logo.png) Co-authored-by: Jonathan Hefner <[email protected]>
1 parent e956aaf commit ff2e38c

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

railties/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
* In Action Mailer previews, list inline attachments separately from normal
2+
attachments. For example, attachments that were previously listed like
3+
4+
> Attachments: logo.png file1.pdf file2.pdf
5+
6+
will now be listed like
7+
8+
> Attachments: file1.pdf file2.pdf (Inline: logo.png)
9+
10+
*Christian Schmidt* and *Jonathan Hefner*
11+
112
* In mailer preview, only show SMTP-To if it differs from the union of To, Cc and Bcc.
213

314
*Christian Schmidt*

railties/lib/rails/mailers_controller.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "rails/application_controller"
4+
require "active_support/core_ext/enumerable"
45

56
class Rails::MailersController < Rails::ApplicationController # :nodoc:
67
prepend_view_path ActionDispatch::DebugView::RESCUES_TEMPLATE_PATHS
@@ -9,7 +10,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc:
910
before_action :find_preview, only: [:preview, :download]
1011
before_action :require_local!, unless: :show_previews?
1112

12-
helper_method :part_query, :locale_query
13+
helper_method :attachment_url, :part_query, :locale_query
1314

1415
content_security_policy(false)
1516

@@ -38,6 +39,8 @@ def preview
3839
if @preview.email_exists?(@email_action)
3940
@page_title = "Mailer Preview for #{@preview.preview_name}##{@email_action}"
4041
@email = @preview.call(@email_action, params)
42+
@attachments = attachments_for(@email).reject { |filename, attachment| attachment.inline? }
43+
@inline_attachments = attachments_for(@email).select { |filename, attachment| attachment.inline? }
4144

4245
if params[:part]
4346
part_type = Mime::Type.lookup(params[:part])
@@ -95,6 +98,16 @@ def find_part(format) # :doc:
9598
end
9699
end
97100

101+
def attachments_for(email)
102+
email.all_parts.to_a.select(&:attachment?).index_by do |attachment|
103+
attachment.respond_to?(:original_filename) ? attachment.original_filename : attachment.filename
104+
end
105+
end
106+
107+
def attachment_url(attachment)
108+
"data:application/octet-stream;charset=utf-8;base64,#{Base64.encode64(attachment.body.to_s)}"
109+
end
110+
98111
def part_query(mime_type)
99112
request.query_parameters.merge(part: mime_type).to_query
100113
end

railties/lib/rails/templates/rails/mailers/email.html.erb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,16 @@
9898
<dt>Subject:</dt>
9999
<dd><strong id="subject"><%= @email.subject %></strong></dd>
100100

101-
<% unless @email.attachments.nil? || @email.attachments.empty? %>
101+
<% if @attachments.any? || @inline_attachments.any? %>
102102
<dt>Attachments:</dt>
103103
<dd>
104-
<% @email.attachments.each do |a| %>
105-
<% filename = a.respond_to?(:original_filename) ? a.original_filename : a.filename %>
106-
<%= link_to filename, "data:application/octet-stream;charset=utf-8;base64,#{Base64.encode64(a.body.to_s)}", download: filename %>
104+
<% @attachments.each do |filename, attachment| %>
105+
<%= link_to filename, attachment_url(attachment), download: filename %>
106+
<% end %>
107+
108+
<% if @inline_attachments.any? %>
109+
(Inline: <% @inline_attachments.each do |filename, attachment| %>
110+
<%= link_to filename, attachment_url(attachment), download: filename %><% end %>)
107111
<% end %>
108112
</dd>
109113
<% end %>

railties/test/application/mailer_previews_test.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,10 @@ def foo
771771

772772
get "/rails/mailers/notifier/foo"
773773
assert_equal 200, last_response.status
774-
assert_match %r[<iframe name="messageBody"], last_response.body
774+
assert_match %[<iframe name="messageBody"], last_response.body
775+
assert_match %[<dt>Attachments:</dt>], last_response.body
776+
assert_no_match %[Inline:], last_response.body
777+
assert_match %[<a download="pixel.png" href="data:application/octet-stream;charset=utf-8;base64,iVBORw0K], last_response.body
775778

776779
get "/rails/mailers/notifier/foo?part=text/plain"
777780
assert_equal 200, last_response.status
@@ -819,7 +822,10 @@ def foo
819822

820823
get "/rails/mailers/notifier/foo"
821824
assert_equal 200, last_response.status
822-
assert_match %r[<iframe name="messageBody"], last_response.body
825+
assert_match %[<iframe name="messageBody"], last_response.body
826+
assert_match %[<dt>Attachments:</dt>], last_response.body
827+
assert_no_match %[Inline:], last_response.body
828+
assert_match %[<a download="pixel.png" href="data:application/octet-stream;charset=utf-8;base64,iVBORw0K], last_response.body
823829

824830
get "/rails/mailers/notifier/foo?part=text/plain"
825831
assert_equal 200, last_response.status
@@ -838,7 +844,7 @@ class Notifier < ActionMailer::Base
838844
default from: "[email protected]"
839845
840846
def foo
841-
attachments['pixel.png'] = File.binread("#{app_path}/public/images/pixel.png")
847+
attachments.inline['pixel.png'] = File.binread("#{app_path}/public/images/pixel.png")
842848
mail to: "[email protected]"
843849
end
844850
end
@@ -865,7 +871,9 @@ def foo
865871

866872
get "/rails/mailers/notifier/foo"
867873
assert_equal 200, last_response.status
868-
assert_match %r[<iframe name="messageBody"], last_response.body
874+
assert_match %[<iframe name="messageBody"], last_response.body
875+
assert_match %[<dt>Attachments:</dt>], last_response.body
876+
assert_match %r[\(Inline:\s+<a download="pixel.png" href="data:application/octet-stream;charset=utf-8;base64,iVBORw0K], last_response.body
869877

870878
get "/rails/mailers/notifier/foo?part=text/plain"
871879
assert_equal 200, last_response.status
@@ -875,6 +883,10 @@ def foo
875883
assert_equal 200, last_response.status
876884
assert_match %r[<p>Hello, World!</p>], last_response.body
877885
assert_match %r[src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo="], last_response.body
886+
887+
get "/rails/mailers/download/notifier/foo"
888+
email = Mail.read_from_string(last_response.body)
889+
assert_equal "inline; filename=pixel.png", email.attachments.inline["pixel.png"].content_disposition
878890
end
879891

880892
test "multipart mailer preview with attached email" do
@@ -923,7 +935,10 @@ def foo
923935

924936
get "/rails/mailers/notifier/foo"
925937
assert_equal 200, last_response.status
926-
assert_match %r[<iframe name="messageBody"], last_response.body
938+
assert_match %[<iframe name="messageBody"], last_response.body
939+
assert_match %[<dt>Attachments:</dt>], last_response.body
940+
assert_no_match %[Inline:], last_response.body
941+
assert_match %[<a download="message.eml" href="data:application/octet-stream;charset=utf-8;base64,RGF0ZTog], last_response.body
927942

928943
get "/rails/mailers/notifier/foo?part=text/plain"
929944
assert_equal 200, last_response.status

0 commit comments

Comments
 (0)