Skip to content
This repository
tree: dd02090d76
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 248 lines (189 sloc) 8.154 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/blank'

module ActionMailer
  module OldApi #:nodoc:
    extend ActiveSupport::Concern

    included do
      extend ActionMailer::AdvAttrAccessor

      @@protected_instance_variables = %w(@parts)
      cattr_reader :protected_instance_variables

      # Specify the BCC addresses for the message
      adv_attr_accessor :bcc

      # Specify the CC addresses for the message.
      adv_attr_accessor :cc

      # Specify the charset to use for the message. This defaults to the
      # +default_charset+ specified for ActionMailer::Base.
      adv_attr_accessor :charset

      # Specify the content type for the message. This defaults to <tt>text/plain</tt>
      # in most cases, but can be automatically set in some situations.
      adv_attr_accessor :content_type

      # Specify the from address for the message.
      adv_attr_accessor :from

      # Specify the address (if different than the "from" address) to direct
      # replies to this message.
      adv_attr_accessor :reply_to

      # Specify the order in which parts should be sorted, based on content-type.
      # This defaults to the value for the +default_implicit_parts_order+.
      adv_attr_accessor :implicit_parts_order

      # Defaults to "1.0", but may be explicitly given if needed.
      adv_attr_accessor :mime_version

      # The recipient addresses for the message, either as a string (for a single
      # address) or an array (for multiple addresses).
      adv_attr_accessor :recipients

      # The date on which the message was sent. If not set (the default), the
      # header will be set by the delivery agent.
      adv_attr_accessor :sent_on

      # Specify the subject of the message.
      adv_attr_accessor :subject

      # Specify the template name to use for current message. This is the "base"
      # template name, without the extension or directory, and may be used to
      # have multiple mailer methods share the same template.
      adv_attr_accessor :template

      # Override the mailer name, which defaults to an inflected version of the
      # mailer's class name. If you want to use a template in a non-standard
      # location, you can use this to specify that location.
      adv_attr_accessor :mailer_name

      # Define the body of the message. This is either a Hash (in which case it
      # specifies the variables to pass to the template when it is rendered),
      # or a string, in which case it specifies the actual text of the message.
      adv_attr_accessor :body

      # Alias controller_path to mailer_name so render :partial in views work.
      alias :controller_path :mailer_name
    end

    def process(method_name, *args)
      initialize_defaults(method_name)
      super
      unless @mail_was_called
        create_parts
        create_mail
      end
      @_message
    end

    # Add a part to a multipart message, with the given content-type. The
    # part itself is yielded to the block so that other properties (charset,
    # body, headers, etc.) can be set on it.
    def part(params)
      params = {:content_type => params} if String === params

      if custom_headers = params.delete(:headers)
        params.merge!(custom_headers)
      end

      part = Mail::Part.new(params)

      yield part if block_given?
      @parts << part
    end

    # Add an attachment to a multipart message. This is simply a part with the
    # content-disposition set to "attachment".
    def attachment(params, &block)
      params = { :content_type => params } if String === params

      params[:content] ||= params.delete(:data) || params.delete(:body)

      if params[:filename]
        params = normalize_file_hash(params)
      else
        params = normalize_nonfile_hash(params)
      end

      part(params, &block)
    end

  protected

    def normalize_nonfile_hash(params)
      content_disposition = "attachment;"
      
      mime_type = params.delete(:mime_type)
      
      if content_type = params.delete(:content_type)
        content_type = "#{mime_type || content_type};"
      end

      params[:body] = params.delete(:data) if params[:data]
      
      { :content_type => content_type,
        :content_disposition => content_disposition }.merge(params)
    end
    
    def normalize_file_hash(params)
      filename = File.basename(params.delete(:filename))
      content_disposition = "attachment; filename=\"#{File.basename(filename)}\""
      
      mime_type = params.delete(:mime_type)
      
      if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/)
        content_type = "#{mime_type || content_type}; filename=\"#{filename}\""
      end
      
      params[:body] = params.delete(:data) if params[:data]
      
      { :content_type => content_type,
        :content_disposition => content_disposition }.merge(params)
    end
    
    def create_mail
      m = @_message

      set_fields!({:subject => subject, :to => recipients, :from => from,
                   :bcc => bcc, :cc => cc, :reply_to => reply_to}, charset)

      m.mime_version = mime_version unless mime_version.nil?
      m.date = sent_on.to_time rescue sent_on if sent_on

      @headers.each { |k, v| m[k] = v }

      real_content_type, ctype_attrs = parse_content_type
      main_type, sub_type = split_content_type(real_content_type)

      if @parts.size == 1 && @parts.first.parts.empty?
        m.content_type([main_type, sub_type, ctype_attrs])
        m.body = @parts.first.body.encoded
      else
        @parts.each do |p|
          m.add_part(p)
        end

        m.body.set_sort_order(@implicit_parts_order)
        m.body.sort_parts!

        if real_content_type =~ /multipart/
          ctype_attrs.delete "charset"
          m.content_type([main_type, sub_type, ctype_attrs])
        end
      end

      wrap_delivery_behavior!
      m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii?
      
      @_message
    end
    
    # Set up the default values for the various instance variables of this
    # mailer. Subclasses may override this method to provide different
    # defaults.
    def initialize_defaults(method_name)
      @charset ||= self.class.default[:charset].try(:dup)
      @content_type ||= self.class.default[:content_type].try(:dup)
      @implicit_parts_order ||= self.class.default[:parts_order].try(:dup)
      @mime_version ||= self.class.default[:mime_version].try(:dup)

      @mailer_name ||= self.class.mailer_name.dup
      @template ||= method_name
      @mail_was_called = false

      @parts ||= []
      @headers ||= {}
      @sent_on ||= Time.now
      @body ||= {}
    end

    def create_parts
      if String === @body
        @parts.unshift create_inline_part(@body)
      elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
        lookup_context.find_all(@template, @mailer_name).each do |template|
          self.formats = template.formats
          @parts << create_inline_part(render(:template => template), template.mime_type)
        end

        if @parts.size > 1
          @content_type = "multipart/alternative" if @content_type !~ /^multipart/
        end

        # If this is a multipart e-mail add the mime_version if it is not
        # already set.
        @mime_version ||= "1.0" if !@parts.empty?
      end
    end

    def create_inline_part(body, mime_type=nil)
      ct = mime_type || "text/plain"
      main_type, sub_type = split_content_type(ct.to_s)

      Mail::Part.new(
        :content_type => [main_type, sub_type, {:charset => charset}],
        :content_disposition => "inline",
        :body => body
      )
    end

    def split_content_type(ct)
      ct.to_s.split("/")
    end

    def parse_content_type(defaults=nil)
      if @content_type.blank?
        [ nil, {} ]
      else
        ctype, *attrs = @content_type.split(/;\s*/)
        attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
        [ctype, {"charset" => @charset}.merge(attrs)]
      end
    end
  end
end
Something went wrong with that request. Please try again.