email_build.rb 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. module Channel::EmailBuild
  3. =begin
  4. mail = Channel::EmailBuild.build(
  5. from: 'sender@example.com',
  6. to: 'recipient@example.com',
  7. body: 'somebody with some text',
  8. content_type: 'text/plain',
  9. )
  10. =end
  11. def self.build(attr, notification = false)
  12. mail = Mail.new
  13. # set organization
  14. organization = Setting.get('organization')
  15. if organization
  16. mail['Organization'] = organization.to_s
  17. end
  18. # notification
  19. if notification
  20. attr['X-Loop'] = 'yes'
  21. attr['Precedence'] = 'bulk'
  22. attr['Auto-Submitted'] = 'auto-generated'
  23. attr['X-Auto-Response-Suppress'] = 'All'
  24. end
  25. attr['X-Powered-By'] = 'Zammad - Helpdesk/Support (https://zammad.org/)'
  26. attr['X-Mailer'] = 'Zammad Mail Service'
  27. # set headers
  28. attr.each do |key, value|
  29. next if key.to_s == 'attachments'
  30. next if key.to_s == 'body'
  31. next if key.to_s == 'content_type'
  32. mail[key.to_s] = if value && value.class != Array
  33. value.to_s
  34. else
  35. value
  36. end
  37. end
  38. # add html part
  39. if attr[:content_type] && attr[:content_type] == 'text/html'
  40. html_alternative = Mail::Part.new do
  41. content_type 'text/html; charset=UTF-8'
  42. # complete check
  43. html_document = Channel::EmailBuild.html_complete_check(attr[:body])
  44. body html_document
  45. end
  46. # generate plain part
  47. attr[:body] = attr[:body].html2text
  48. end
  49. # add plain text part
  50. text_alternative = Mail::Part.new do
  51. content_type 'text/plain; charset=UTF-8'
  52. body attr[:body]
  53. end
  54. # build email without any attachments
  55. if !html_alternative && attr[:attachments].blank?
  56. mail.content_type 'text/plain; charset=UTF-8'
  57. mail.body attr[:body]
  58. return mail
  59. end
  60. # build email with attachments
  61. alternative_bodies = Mail::Part.new { content_type 'multipart/alternative' }
  62. alternative_bodies.add_part text_alternative
  63. if html_alternative
  64. html_container = Mail::Part.new { content_type 'multipart/related' }
  65. html_container.add_part html_alternative
  66. # place to add inline attachments related to html alternative
  67. attr[:attachments]&.each do |attachment|
  68. next if attachment.class == Hash
  69. next if attachment.preferences['Content-ID'].blank?
  70. attachment = Mail::Part.new do
  71. content_type attachment.preferences['Content-Type']
  72. content_id "<#{attachment.preferences['Content-ID']}>"
  73. content_disposition attachment.preferences['Content-Disposition'] || 'inline'
  74. content_transfer_encoding 'binary'
  75. body attachment.content.force_encoding('BINARY')
  76. end
  77. html_container.add_part attachment
  78. end
  79. alternative_bodies.add_part html_container
  80. end
  81. mail.add_part alternative_bodies
  82. # add attachments
  83. attr[:attachments]&.each do |attachment|
  84. if attachment.class == Hash
  85. attachment['content-id'] = nil
  86. mail.attachments[attachment[:filename]] = attachment
  87. else
  88. next if attachment.preferences['Content-ID'].present?
  89. filename = attachment.filename
  90. encoded_filename = Mail::Encodings.decode_encode filename, :encode
  91. disposition = attachment.preferences['Content-Disposition'] || 'attachment'
  92. content_type = attachment.preferences['Content-Type'] || attachment.preferences['Mime-Type'] || 'application/octet-stream'
  93. mail.attachments[attachment.filename] = {
  94. content_disposition: "#{disposition}; filename=\"#{encoded_filename}\"",
  95. content_type: "#{content_type}; filename=\"#{encoded_filename}\"",
  96. content: attachment.content
  97. }
  98. end
  99. end
  100. mail
  101. end
  102. =begin
  103. quoted_in_one_line = Channel::EmailBuild.recipient_line('Somebody @ "Company"', 'some.body@example.com')
  104. returns
  105. '"Somebody @ \"Company\"" <some.body@example.com>'
  106. =end
  107. def self.recipient_line(realname, email)
  108. return "#{realname} <#{email}>" if realname.match?(/^[A-z]+$/i)
  109. "\"#{realname.gsub('"', '\"')}\" <#{email}>"
  110. end
  111. =begin
  112. Check if string is a complete html document. If not, add head and css styles.
  113. full_html_document_string = Channel::EmailBuild.html_complete_check(html_string)
  114. =end
  115. def self.html_complete_check(html)
  116. # apply mail client fixes
  117. html = Channel::EmailBuild.html_mail_client_fixes(html)
  118. return html if html.match?(/<html>/i)
  119. html_email_body = File.read(Rails.root.join('app', 'views', 'mailer', 'application_wrapper.html.erb').to_s)
  120. html_email_body.gsub!('###html_email_css_font###', Setting.get('html_email_css_font'))
  121. # use block form because variable html could contain backslashes and e. g. '\1' that
  122. # must not be handled as back-references for regular expressions
  123. html_email_body.sub('###html###') { html }
  124. end
  125. =begin
  126. Add/change markup to display html in any mail client nice.
  127. html_string_with_fixes = Channel::EmailBuild.html_mail_client_fixes(html_string)
  128. =end
  129. def self.html_mail_client_fixes(html)
  130. # https://github.com/martini/zammad/issues/165
  131. new_html = html.gsub('<blockquote type="cite">', '<blockquote type="cite" style="border-left: 2px solid blue; margin: 0 0 16px; padding: 8px 12px 8px 12px;">')
  132. new_html.gsub!(/<p>/mxi, '<p style="margin: 0;">')
  133. new_html.gsub!(%r{</?hr>}mxi, '<hr style="margin-top: 6px; margin-bottom: 6px; border: 0; border-top: 1px solid #dfdfdf;">')
  134. new_html
  135. end
  136. end