add layout for notifications

This commit is contained in:
scossar 2016-01-19 16:42:56 -08:00
parent 6a7bdfecc8
commit 3f09ec2aca
4 changed files with 122 additions and 68 deletions

View File

@ -5,6 +5,7 @@ require_dependency 'age_words'
class UserNotifications < ActionMailer::Base
helper :application
default charset: 'UTF-8'
layout 'user_notifications'
include Email::BuildEmailHelper
@ -290,6 +291,7 @@ class UserNotifications < ActionMailer::Base
else
html = UserNotificationRenderer.new(Rails.configuration.paths["app/views"]).render(
template: 'email/notification',
layout: 'layouts/user_notifications',
format: :html,
locals: { context_posts: context_posts,
post: post,

View File

@ -0,0 +1,42 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--[if !mso]><!-->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!--<![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<!--[if (gte mso 9)|(IE)]>
<style type="text/css">
table {border-collapse: collapse;}
</style>
<![endif]-->
</head>
<body style="Margin: 0;padding:0;min-width:100%;background-color:#ffffff">
<center class="wrapper" style="width:100%;table-layout:fixed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<div class="webkit" style="max-width:600px">
<!--[if (gte mso 9)|(IE)]>
<table width="600" align="center" cellspacing="0" cellpadding="0" border="0">
<tr>
<td>
<![endif]-->
<table class="outer" align="center" cellspacing="0" cellpadding="0" border="0" style="Margin: 0 auto; width: 100%; max-width: 600px">
<tr>
<td style="padding: 10px;">
<%= yield %>
</td>
</tr>
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</div>
</center>
</body>
</html>

View File

@ -9,7 +9,7 @@ module Email
def initialize(html, opts=nil)
@html = html
@opts = opts || {}
@fragment = Nokogiri::HTML.fragment(@html)
@doc = Nokogiri::HTML(@html)
end
def self.register_plugin_style(&block)
@ -30,7 +30,7 @@ module Email
uri = URI(Discourse.base_url)
# images
@fragment.css('img').each do |img|
@doc.css('img').each do |img|
next if img['class'] == 'site-logo'
if img['class'] == "emoji" || img['src'] =~ /plugins\/emoji/
@ -56,15 +56,16 @@ module Email
end
# add max-width to big images
big_images = @fragment.css('img[width="auto"][height="auto"]') -
@fragment.css('aside.onebox img') -
@fragment.css('img.site-logo, img.emoji')
big_images = @doc.css('img[width="auto"][height="auto"]') -
@doc.css('aside.onebox img') -
@doc.css('img.site-logo, img.emoji')
big_images.each do |img|
add_styles(img, 'max-width: 100%;') if img['style'] !~ /max-width/
add_styles(img, 'width: 100%; height: auto; max-width: 600px;') if img['style'] !~ /max-width/
add_attributes(img, width: '600')
end
# attachments
@fragment.css('a.attachment').each do |a|
@doc.css('a.attachment').each do |a|
# ensure all urls are absolute
if a['href'] =~ /^\/[^\/]/
a['href'] = "#{Discourse.base_url}#{a['href']}"
@ -117,12 +118,12 @@ module Email
style('aside.clearfix', "clear: both")
# Finally, convert all `aside` tags to `div`s
@fragment.css('aside, article, header').each do |n|
@doc.css('aside, article, header').each do |n|
n.name = "div"
end
# iframes can't go in emails, so replace them with clickable links
@fragment.css('iframe').each do |i|
@doc.css('iframe').each do |i|
begin
src_uri = URI(i['src'])
@ -157,20 +158,21 @@ module Email
# this method is reserved for styles specific to plugin
def plugin_styles
@@plugin_callbacks.each { |block| block.call(@fragment, @opts) }
@@plugin_callbacks.each { |block| block.call(@doc, @opts) }
end
def to_html
return "" unless has_body?
strip_classes_and_ids
replace_relative_urls
@fragment.to_html.tap do |result|
@doc.to_html.tap do |result|
result.gsub!(/\[email-indent\]/, "<div style='margin-left: 15px'>")
result.gsub!(/\[\/email-indent\]/, "</div>")
end
end
def strip_avatars_and_emojis
@fragment.search('img').each do |img|
@doc.search('img').each do |img|
if img['src'] =~ /_avatar/
img.parent['style'] = "vertical-align: top;" if img.parent.name == 'td'
img.remove
@ -182,7 +184,7 @@ module Email
end
end
@fragment.to_s
@doc.to_s
end
private
@ -192,7 +194,7 @@ module Email
host = forum_uri.host
scheme = forum_uri.scheme
@fragment.css('[href]').each do |element|
@doc.css('[href]').each do |element|
href = element['href']
if href =~ /^\/\/#{host}/
element['href'] = "#{scheme}:#{href}"
@ -201,14 +203,14 @@ module Email
end
def correct_first_body_margin
@fragment.css('.body p').each do |element|
@doc.css('.body p').each do |element|
element['style'] = "margin-top:0; border: 0;"
end
end
def correct_footer_style
footernum = 0
@fragment.css('.footer').each do |element|
@doc.css('.footer').each do |element|
element['style'] = "color:#666;"
linknum = 0
element.css('a').each do |inner|
@ -225,7 +227,7 @@ module Email
end
def strip_classes_and_ids
@fragment.css('*').each do |element|
@doc.css('*').each do |element|
element.delete('class')
element.delete('id')
end
@ -235,13 +237,21 @@ module Email
style('table', nil, cellspacing: '0', cellpadding: '0', border: '0')
end
def style(selector, style, attribs = {})
@fragment.css(selector).each do |element|
add_styles(element, style) if style
def add_attributes(element, attribs)
attribs.each do |k, v|
element[k] = v
end
end
def has_body?
@doc.at_css('body')
end
def style(selector, style, attribs = {})
@doc.css(selector).each do |element|
add_styles(element, style) if style
add_attributes(element, attribs)
end
end
end
end

View File

@ -3,17 +3,17 @@ require 'email'
describe Email::Styles do
def basic_fragment(html)
def basic_doc(html)
styler = Email::Styles.new(html)
styler.format_basic
Nokogiri::HTML.fragment(styler.to_html)
Nokogiri::HTML(styler.to_html)
end
def html_fragment(html)
def html_doc(html)
styler = Email::Styles.new(html)
styler.format_basic
styler.format_html
Nokogiri::HTML.fragment(styler.to_html)
Nokogiri::HTML(styler.to_html)
end
context "basic formatter" do
@ -26,24 +26,24 @@ describe Email::Styles do
# Pending due to email effort @coding-horror made in d2fb2bc4c
skip "adds a max-width to images" do
frag = basic_fragment("<img src='gigantic.jpg'>")
expect(frag.at("img")["style"]).to match("max-width")
doc = basic_doc("<img src='gigantic.jpg'>")
expect(doc.at("img")["style"]).to match("max-width")
end
it "adds a width and height to images with an emoji path" do
frag = basic_fragment("<img src='/images/emoji/fish.png' class='emoji'>")
expect(frag.at("img")["width"]).to eq("20")
expect(frag.at("img")["height"]).to eq("20")
doc = basic_doc("<img src='/images/emoji/fish.png' class='emoji'>")
expect(doc.at("img")["width"]).to eq("20")
expect(doc.at("img")["height"]).to eq("20")
end
it "converts relative paths to absolute paths" do
frag = basic_fragment("<img src='/some-image.png'>")
expect(frag.at("img")["src"]).to eq("#{Discourse.base_url}/some-image.png")
doc = basic_doc("<img src='/some-image.png'>")
expect(doc.at("img")["src"]).to eq("#{Discourse.base_url}/some-image.png")
end
it "strips classes and ids" do
frag = basic_fragment("<div class='foo' id='bar'><div class='foo' id='bar'></div></div>")
expect(frag.to_html).to eq("<div><div></div></div>")
doc = basic_doc("<div class='foo' id='bar'><div class='foo' id='bar'></div></div>")
expect(doc.to_html).to match(/<div><div><\/div><\/div>/)
end
end
@ -56,51 +56,51 @@ describe Email::Styles do
end
it "attaches a style to h3 tags" do
frag = html_fragment("<h3>hello</h3>")
expect(frag.at('h3')['style']).to be_present
doc = html_doc("<h3>hello</h3>")
expect(doc.at('h3')['style']).to be_present
end
it "attaches a style to hr tags" do
frag = html_fragment("hello<hr>")
expect(frag.at('hr')['style']).to be_present
doc = html_doc("hello<hr>")
expect(doc.at('hr')['style']).to be_present
end
it "attaches a style to a tags" do
frag = html_fragment("<a href>wat</a>")
expect(frag.at('a')['style']).to be_present
doc = html_doc("<a href>wat</a>")
expect(doc.at('a')['style']).to be_present
end
it "attaches a style to a tags" do
frag = html_fragment("<a href>wat</a>")
expect(frag.at('a')['style']).to be_present
doc = html_doc("<a href>wat</a>")
expect(doc.at('a')['style']).to be_present
end
it "attaches a style to ul and li tags" do
frag = html_fragment("<ul><li>hello</li></ul>")
expect(frag.at('ul')['style']).to be_present
expect(frag.at('li')['style']).to be_present
doc = html_doc("<ul><li>hello</li></ul>")
expect(doc.at('ul')['style']).to be_present
expect(doc.at('li')['style']).to be_present
end
it "converts iframes to links" do
iframe_url = "http://www.youtube.com/embed/7twifrxOTQY?feature=oembed&wmode=opaque"
frag = html_fragment("<iframe src=\"#{iframe_url}\"></iframe>")
expect(frag.at('iframe')).to be_blank
expect(frag.at('a')).to be_present
expect(frag.at('a')['href']).to eq(iframe_url)
doc = html_doc("<iframe src=\"#{iframe_url}\"></iframe>")
expect(doc.at('iframe')).to be_blank
expect(doc.at('a')).to be_present
expect(doc.at('a')['href']).to eq(iframe_url)
end
it "won't allow non URLs in iframe src, strips them with no link" do
iframe_url = "alert('xss hole')"
frag = html_fragment("<iframe src=\"#{iframe_url}\"></iframe>")
expect(frag.at('iframe')).to be_blank
expect(frag.at('a')).to be_blank
doc = html_doc("<iframe src=\"#{iframe_url}\"></iframe>")
expect(doc.at('iframe')).to be_blank
expect(doc.at('a')).to be_blank
end
end
context "rewriting protocol relative URLs to the forum" do
it "doesn't rewrite a url to another site" do
frag = html_fragment('<a href="//youtube.com/discourse">hello</a>')
expect(frag.at('a')['href']).to eq("//youtube.com/discourse")
doc = html_doc('<a href="//youtube.com/discourse">hello</a>')
expect(doc.at('a')['href']).to eq("//youtube.com/discourse")
end
context "without https" do
@ -109,18 +109,18 @@ describe Email::Styles do
end
it "rewrites the href to have http" do
frag = html_fragment('<a href="//test.localhost/discourse">hello</a>')
expect(frag.at('a')['href']).to eq("http://test.localhost/discourse")
doc = html_doc('<a href="//test.localhost/discourse">hello</a>')
expect(doc.at('a')['href']).to eq("http://test.localhost/discourse")
end
it "rewrites the href for attachment files to have http" do
frag = html_fragment('<a class="attachment" href="//try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt">attachment_file.txt</a>')
expect(frag.at('a')['href']).to eq("http://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt")
doc = html_doc('<a class="attachment" href="//try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt">attachment_file.txt</a>')
expect(doc.at('a')['href']).to eq("http://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt")
end
it "rewrites the src to have http" do
frag = html_fragment('<img src="//test.localhost/blah.jpg">')
expect(frag.at('img')['src']).to eq("http://test.localhost/blah.jpg")
doc = html_doc('<img src="//test.localhost/blah.jpg">')
expect(doc.at('img')['src']).to eq("http://test.localhost/blah.jpg")
end
end
@ -130,18 +130,18 @@ describe Email::Styles do
end
it "rewrites the forum URL to have https" do
frag = html_fragment('<a href="//test.localhost/discourse">hello</a>')
expect(frag.at('a')['href']).to eq("https://test.localhost/discourse")
doc = html_doc('<a href="//test.localhost/discourse">hello</a>')
expect(doc.at('a')['href']).to eq("https://test.localhost/discourse")
end
it "rewrites the href for attachment files to have https" do
frag = html_fragment('<a class="attachment" href="//try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt">attachment_file.txt</a>')
expect(frag.at('a')['href']).to eq("https://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt")
doc = html_doc('<a class="attachment" href="//try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt">attachment_file.txt</a>')
expect(doc.at('a')['href']).to eq("https://try-discourse.global.ssl.fastly.net/uploads/default/368/40b610b0aa90cfcf.txt")
end
it "rewrites the src to have https" do
frag = html_fragment('<img src="//test.localhost/blah.jpg">')
expect(frag.at('img')['src']).to eq("https://test.localhost/blah.jpg")
doc = html_doc('<img src="//test.localhost/blah.jpg">')
expect(doc.at('img')['src']).to eq("https://test.localhost/blah.jpg")
end
end
@ -152,14 +152,14 @@ describe Email::Styles do
emoji = "<img src='/images/emoji/emoji_one/crying_cat_face.png'>"
style = Email::Styles.new(emoji)
style.strip_avatars_and_emojis
expect(style.to_html).to match_html(emoji)
expect(style.to_html).to match_html("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\"><html><body>#{emoji}</body></html>")
end
it "works for lonesome emoji with title" do
emoji = "<img title='cry_cry' src='/images/emoji/emoji_one/crying_cat_face.png'>"
style = Email::Styles.new(emoji)
style.strip_avatars_and_emojis
expect(style.to_html).to match_html("cry_cry")
expect(style.to_html).to match_html("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\"><html><body>cry_cry</body></html>")
end
end