2013-06-10 15:33:37 -04:00
#
# HTML emails don't support CSS, so we can use nokogiri to inline attributes based on
# matchers.
#
module Email
class Styles
2014-08-21 16:24:05 +05:30
@@plugin_callbacks = [ ]
2013-06-10 15:33:37 -04:00
def initialize ( html )
@html = html
2013-06-13 12:15:05 -04:00
@fragment = Nokogiri :: HTML . fragment ( @html )
2013-06-10 15:33:37 -04:00
end
2014-08-21 16:24:05 +05:30
def self . register_plugin_style ( & block )
@@plugin_callbacks . push ( block )
end
2014-05-09 14:39:09 -04:00
def add_styles ( node , new_styles )
existing = node [ 'style' ]
if existing . present?
node [ 'style' ] = " #{ existing } ; #{ new_styles } "
else
node [ 'style' ] = new_styles
end
end
2013-06-13 12:15:05 -04:00
def format_basic
2014-06-13 17:11:04 -04:00
uri = URI ( Discourse . base_url )
2013-06-13 12:15:05 -04:00
@fragment . css ( 'img' ) . each do | img |
2013-06-10 15:33:37 -04:00
2013-11-28 17:20:56 -05:00
next if img [ 'class' ] == 'site-logo'
if img [ 'class' ] == " emoji " || img [ 'src' ] =~ / plugins \/ emoji /
2013-07-26 17:27:46 +10:00
img [ 'width' ] = 20
img [ 'height' ] = 20
2013-06-13 12:15:05 -04:00
else
2014-05-14 16:40:54 -04:00
add_styles ( img , 'max-width: 694px;' ) if img [ 'style' ] !~ / max-width /
2013-06-13 12:15:05 -04:00
end
2013-08-27 00:08:38 +02:00
# ensure all urls are absolute
2013-08-14 11:32:17 -04:00
if img [ 'src' ] =~ / ^ \/ [^ \/ ] /
2013-06-13 12:15:05 -04:00
img [ 'src' ] = " #{ Discourse . base_url } #{ img [ 'src' ] } "
end
2013-08-27 00:08:38 +02:00
# ensure no schemaless urls
2013-12-03 10:10:53 -05:00
if img [ 'src' ] && img [ 'src' ] . starts_with? ( " // " )
2014-06-13 17:11:04 -04:00
img [ 'src' ] = " #{ uri . scheme } : #{ img [ 'src' ] } "
2013-08-27 00:08:38 +02:00
end
2013-07-22 15:06:37 -04:00
end
2013-07-26 17:27:46 +10:00
end
2013-07-22 15:06:37 -04:00
2013-07-26 17:27:46 +10:00
def format_notification
style ( '.previous-discussion' , 'font-size: 17px; color: #444;' )
2014-05-14 16:40:54 -04:00
style ( '.notification-date' , " text-align:right;color: # 999999;padding-right:5px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;font-size:11px " )
2013-07-26 17:27:46 +10:00
style ( '.username' , " font-size:13px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;color: # 3b5998;text-decoration:none;font-weight:bold " )
style ( '.post-wrapper' , " margin-bottom:25px;max-width:761px " )
style ( '.user-avatar' , 'vertical-align:top;width:55px;' )
style ( '.user-avatar img' , nil , width : '45' , height : '45' )
style ( 'hr' , 'background-color: #ddd; height: 1px; border: 1px;' )
2014-08-27 14:38:03 +03:00
style ( '.rtl' , 'direction: rtl;' )
2013-07-26 17:27:46 +10:00
# we can do this but it does not look right
# style('#main', 'font-family:"Helvetica Neue", Helvetica, Arial, sans-serif')
2013-07-27 08:08:58 +10:00
style ( 'td.body' , 'padding-top:5px;' , colspan : " 2 " )
correct_first_body_margin
2013-07-26 17:27:46 +10:00
correct_footer_style
reset_tables
2014-05-09 14:39:09 -04:00
onebox_styles
2014-08-21 16:24:05 +05:30
plugin_styles
2014-05-09 14:39:09 -04:00
end
def onebox_styles
# Links to other topics
2014-05-13 14:44:40 -04:00
style ( 'aside.quote' , 'border-left: 5px solid #bebebe; background-color: #f1f1f1; padding: 12px 25px 2px 12px; margin-bottom: 10px;' )
2014-05-09 14:39:09 -04:00
style ( 'aside.quote blockquote' , 'border: 0px; padding: 0; margin: 7px 0' )
style ( 'aside.quote div.info-line' , 'color: #666; margin: 10px 0' )
style ( 'aside.quote .avatar' , 'margin-right: 5px' )
# Oneboxes
2014-05-13 14:44:40 -04:00
style ( 'aside.onebox' , " padding: 12px 25px 2px 12px; border-left: 5px solid # bebebe; background: # eee; margin-bottom: 10px; " )
2014-05-14 16:40:54 -04:00
style ( 'aside.onebox img' , " max-height: 80%; max-width: 25%; height: auto; float: left; margin-right: 10px; margin-bottom: 10px " )
2014-05-09 14:39:09 -04:00
style ( 'aside.onebox h3' , " border-bottom: 0 " )
style ( 'aside.onebox .source' , " margin-bottom: 8px " )
style ( 'aside.onebox .source a[href]' , " color: # 333; font-weight: normal " )
style ( 'aside.clearfix' , " clear: both " )
2014-05-13 14:44:40 -04:00
# Finally, convert all `aside` tags to `div`s
2014-05-14 16:40:54 -04:00
@fragment . css ( 'aside, article, header' ) . each do | n |
2014-05-13 14:44:40 -04:00
n . name = " div "
end
2014-07-14 16:41:05 -04:00
# iframes can't go in emails, so replace them with clickable links
@fragment . css ( 'iframe' ) . each do | i |
begin
src_uri = URI ( i [ 'src' ] )
# If an iframe is protocol relative, use SSL when displaying it
display_src = " #{ src_uri . scheme || 'https://' } #{ src_uri . host } #{ src_uri . path } "
i . replace " <p><a href=' #{ src_uri . to_s } '> #{ display_src } </a><p> "
rescue URI :: InvalidURIError
# If the URL is weird, remove it
i . remove
end
end
2013-06-13 12:15:05 -04:00
end
def format_html
2014-05-13 14:44:40 -04:00
style ( 'h3' , 'margin: 15px 0 20px 0;' )
2013-07-26 17:27:46 +10:00
style ( 'hr' , 'background-color: #ddd; height: 1px; border: 1px;' )
2014-04-17 14:40:30 -04:00
style ( 'a' , 'text-decoration: none; font-weight: bold; color: #006699;' )
2013-07-26 17:27:46 +10:00
style ( 'ul' , 'margin: 0 0 0 10px; padding: 0 0 0 20px;' )
style ( 'li' , 'padding-bottom: 10px' )
style ( 'div.digest-post' , 'margin-left: 15px; margin-top: 20px; max-width: 694px;' )
2013-08-09 14:43:02 -04:00
style ( 'div.digest-post h1' , 'font-size: 20px;' )
2013-08-16 12:18:58 -04:00
style ( 'span.footer-notice' , 'color:#666; font-size:80%' )
2013-11-29 13:00:10 -05:00
style ( 'span.post-count' , 'margin: 0 5px; color: #777;' )
2013-12-16 14:41:59 -05:00
style ( 'pre' , 'word-wrap: break-word; max-width: 694px;' )
2013-12-02 10:04:18 -05:00
style ( 'code' , 'background-color: #f1f1ff; padding: 2px 5px;' )
2013-12-16 14:41:59 -05:00
style ( 'pre code' , 'display: block; background-color: #f1f1ff; padding: 5px;' )
2014-04-17 14:40:30 -04:00
style ( '.featured-topic a' , 'text-decoration: none; font-weight: bold; color: #006699; margin-right: 5px' )
2014-01-22 15:30:30 -05:00
2014-05-09 14:39:09 -04:00
onebox_styles
2014-08-21 16:24:05 +05:30
plugin_styles
end
# this method is reserved for styles specific to plugin
def plugin_styles
@@plugin_callbacks . each { | block | block . call ( @fragment ) }
2013-07-26 17:27:46 +10:00
end
2013-06-10 15:33:37 -04:00
2013-07-26 17:27:46 +10:00
def to_html
strip_classes_and_ids
2014-06-13 17:11:04 -04:00
replace_relative_urls
2013-07-26 17:27:46 +10:00
@fragment . to_html . tap do | result |
result . gsub! ( / \ [email-indent \ ] / , " <div style='margin-left: 15px'> " )
result . gsub! ( / \ [ \/ email-indent \ ] / , " </div> " )
2013-06-10 15:33:37 -04:00
end
2013-07-26 17:27:46 +10:00
end
2013-06-10 15:33:37 -04:00
2013-07-26 17:27:46 +10:00
private
2013-06-10 15:33:37 -04:00
2014-06-13 17:11:04 -04:00
def replace_relative_urls
forum_uri = URI ( Discourse . base_url )
host = forum_uri . host
scheme = forum_uri . scheme
@fragment . css ( '[href]' ) . each do | element |
href = element [ 'href' ]
if href =~ / ^ \/ \/ #{ host } /
element [ 'href' ] = " #{ scheme } : #{ href } "
end
end
end
2013-07-27 08:08:58 +10:00
def correct_first_body_margin
@fragment . css ( '.body p' ) . each do | element |
2014-06-09 15:28:03 -04:00
element [ 'style' ] = " margin-top:0; border: 0; "
2013-07-27 08:08:58 +10:00
end
end
2013-07-26 17:27:46 +10:00
def correct_footer_style
@fragment . css ( '.footer' ) . each do | element |
2013-07-28 23:00:02 -07:00
element [ 'style' ] = " color: # 666; "
2013-07-26 17:27:46 +10:00
element . css ( 'a' ) . each do | inner |
2013-07-27 08:08:58 +10:00
inner [ 'style' ] = " color: # 666; "
2013-07-26 17:27:46 +10:00
end
2013-06-10 15:33:37 -04:00
end
2013-07-26 17:27:46 +10:00
end
2013-06-10 15:33:37 -04:00
2013-07-26 17:27:46 +10:00
def strip_classes_and_ids
@fragment . css ( '*' ) . each do | element |
element . delete ( 'class' )
element . delete ( 'id' )
2013-06-11 12:27:11 -04:00
end
2013-06-13 12:15:05 -04:00
end
2013-06-11 12:27:11 -04:00
2013-07-26 17:27:46 +10:00
def reset_tables
style ( 'table' , nil , cellspacing : '0' , cellpadding : '0' , border : '0' )
2013-06-10 15:33:37 -04:00
end
2013-07-26 17:27:46 +10:00
def style ( selector , style , attribs = { } )
@fragment . css ( selector ) . each do | element |
2014-05-09 14:39:09 -04:00
add_styles ( element , style ) if style
2013-07-26 17:27:46 +10:00
attribs . each do | k , v |
element [ k ] = v
end
end
end
2013-06-10 15:33:37 -04:00
end
2013-07-26 17:27:46 +10:00
end