<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>app/models/mailer_tags.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -8,6 +8,11 @@ Ported to 'mental' by: Sean Cribbs - seancribbs.com
   Version: 0.1
   Contact: seancribbs@gmail.com
 
+File upload facility and submit placeholder
+  by: Tobin Richard, Tricycle Developments - tricycledevelopments.com
+    Version: 0.2
+    Contact: tobin.richard@gmail.com
+
 The Mailer extension enables form mail on a page. You can define email
 templates using pages parts (email, and/or email_html). You configure
 the recipients and other Mailer settings in a config part. Following
@@ -36,6 +41,9 @@ The following tags are available to help you build the form:
     &lt;r:mailer:option name=&quot;&quot; /&gt;
     &lt;r:mailer:submit /&gt;
     &lt;r:mailer:reset /&gt;
+    &lt;r:mailer:submit_placeholder /&gt;
+
+... and the following to help you build the email or email_html templates.
     &lt;r:mailer:get name=&quot;&quot; /&gt;
 
 Simple example of a form:
@@ -49,9 +57,47 @@ Simple example of a form:
  &lt;r:mailer:submit value=&quot;Send&quot; /&gt;
 &lt;/r:mailer:form&gt;
 
+Forms with file attachments:
+
+In many cases it is desirable to limit the maximum size of a file that may be uploaded.
+This is set as the max_filesize attribute for mailers in the config page part. Any file
+included in the form will have the limit imposed. Following is a simple example config
+part that includes a file size limit of 100,000 bytes:
+    mailers:
+      contact:
+        subject: From the website of Whatever
+        from: noreply@mydomain.com
+        redirect_to: /contact/thank-you
+        max_filesize: 100000
+        recipients:
+          - one@one.com
+          - two@two.com
+
+The following is a simple form that might be used to submit a file for the above
+configuration:
+    &lt;r:mailer:form name=&quot;contact&quot;&gt;
+        Type your message: &lt;r:mailer:text name=&quot;themessage&quot; /&gt; &lt;br/&gt;
+        Select a file: &lt;r:mailer:file name=&quot;thefile&quot; /&gt; &lt;br/&gt;
+        &lt;r:mailer:submit value=&quot;submit&quot;/&gt;
+    &lt;/r:mailer:form&gt;
+
+If a user does not select a file the other form contents will still be e-mailed. 
+The &lt;r:mailer:get name=&quot;foo&quot; /&gt; (with &lt;r:mailer:file name=&quot;foo&quot; /&gt;) will provide the
+uploaded file name.
+
+If you are using email or email_html parts then the &lt;r:mailer:get name=&quot;&quot; /&gt; tag can be
+used to retrieve the name of the uploaded file. If no file was uploaded &quot;&quot; will be
+returned.
+
+If you wish to show show that activity is taking place during submission you may use
+the &lt;r:mailer:submit_placeholder /&gt; tag in your form. This will insert a hidden div with
+the contents of the submit_placeholder page part. The div will be displayed when the
+user clicks any submit button.
 
 Todo:
  * Abstract tag naming, doesn't need to mimick HTML input types
- * Support for file attachments to emails
  * Validate recipient emails addresses -- at least the format
- * Better Error handling
\ No newline at end of file
+ * Better Error handling
+ * Status indicator during file uploads
+ * Check _all_ file attachment sizes before displaying any file size errors rather
+   than stopping at the first.</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,7 @@
 require 'action_mailer'
 class MailerPage &lt; Page
+  include MailerTags
+
   TLDS = %w{com org net edu info mil gov biz ws}
 
   class MailerTagError &lt; StandardError; end
@@ -25,9 +27,9 @@ class MailerPage &lt; Page
       @form_data = request.parameters[:mailer]
       @form_conf = config['mailers'][form_name].symbolize_keys || {}
       # If there are recipients defined, send email...
-      if form_conf.has_key? :recipients
+      if recipients
         if send_mail and form_conf.has_key? :redirect_to
-          response.redirect(form_conf[:redirect_to], :status =&gt; 302)
+          response.redirect( form_conf[:redirect_to], &quot;302 Found&quot; )
         else
           super(request, response)
         end
@@ -45,205 +47,86 @@ class MailerPage &lt; Page
     false
   end
 
-  # Mailer Tags:
+  protected
 
-    desc %{ All mailer-related tags live inside this one. }
-    tag &quot;mailer&quot; do |tag|
-      tag.expand
-    end
-
-    desc %{
-    Generates a form for submitting email.  The 'name' attribute is required
-    and should correspond with configuration given in the 'config' part/tab.
-
-    Usage:
-    &lt;pre&gt;&lt;code&gt;  &lt;r:mailer:form name=&quot;contact&quot;&gt;...&lt;/r:mailer:form&gt;&lt;/code&gt;&lt;/pre&gt;}
-    tag &quot;mailer:form&quot; do |tag|
-      @tag_attr = { :class=&gt;get_class_name('form') }.update( tag.attr.symbolize_keys )
-      raise_error_if_name_missing 'mailer:form'
-      # Build the html form tag...
-      results =  %Q(&lt;form action=&quot;#{ url }&quot; method=&quot;post&quot; class=&quot;#{ tag_attr[:class] }&quot; enctype=&quot;multipart/form-data&quot;&gt;)
-      results &lt;&lt; %Q(&lt;div&gt;&lt;input type=&quot;hidden&quot; name=&quot;mailer_name&quot; value=&quot;#{ tag_attr[:name] }&quot; /&gt;&lt;/div&gt;)
-      results &lt;&lt; %Q(&lt;div class=&quot;mailer-error&quot;&gt;#{form_error}&lt;/div&gt;) if form_error
-      results &lt;&lt; tag.expand
-      results &lt;&lt; %Q(&lt;/form&gt;)
-    end
-
-    # Build tags for all of the &lt;input /&gt; tags...
-    %w(text password file submit reset checkbox radio hidden).each do |type|
-      desc %{
-      Renders a #{type} form control for a mailer form. #{&quot;The 'name' attribute is required.&quot; unless %(submit reset).include? type}
-      All unused attributes will be added as HTML attributes on the resulting tag.}
-      tag &quot;mailer:#{type}&quot; do |tag|
-        @tag_attr = tag.attr.symbolize_keys
-        raise_error_if_name_missing &quot;mailer:#{type}&quot; unless %(submit reset).include? type
-        input_tag_html( type )
-      end
-    end
-
-    desc %{
-    Renders a @&lt;select&gt;...&lt;/select&gt;@ tag for a mailer form.  The 'name' attribute is required.  @&lt;r:option /&gt;@ tags may be nested
-    inside the tag to automatically generate options.
-    }
-    tag 'mailer:select' do |tag|
-      @tag_attr = { :id=&gt;tag.attr['name'], :class=&gt;get_class_name('select'), :size=&gt;'1' }.update( tag.attr.symbolize_keys )
-      raise_error_if_name_missing &quot;mailer:select&quot;
-      tag.locals.parent_tag_name = tag_attr[:name]
-      tag.locals.parent_tag_type = 'select'
-      results =  %Q(&lt;select name=&quot;mailer[#{tag_attr[:name]}]&quot; #{add_attrs_to(&quot;&quot;)}&gt;)
-      results &lt;&lt; tag.expand
-      results &lt;&lt; &quot;&lt;/select&gt;&quot;
-    end
-
-    desc %{
-    Renders a &lt;textarea&gt;...&lt;/textarea&gt; tag for a mailer form. The `name' attribute is required. }
-    tag 'mailer:textarea' do |tag|
-      @tag_attr = { :id=&gt;tag.attr['name'], :class=&gt;get_class_name('textarea'), :rows=&gt;'5', :cols=&gt;'35' }.update( tag.attr.symbolize_keys )
-      raise_error_if_name_missing &quot;mailer:textarea&quot;
-      results =  %Q(&lt;textarea name=&quot;mailer[#{tag_attr[:name]}]&quot; #{add_attrs_to(&quot;&quot;)}&gt;)
-      results &lt;&lt; tag.expand
-      results &lt;&lt; &quot;&lt;/textarea&gt;&quot;
-    end
-
-    desc %{
-    Renders a series of @&lt;input type=&quot;radio&quot; .../&gt;@ tags for a mailer form.  The 'name' attribute is required.
-    Nested @&lt;r:option /&gt;@ tags will generate individual radio buttons with corresponding values. }
-    tag 'mailer:radiogroup' do |tag|
-      @tag_attr = tag.attr.symbolize_keys
-      raise_error_if_name_missing &quot;mailer:radiogroup&quot;
-      tag.locals.parent_tag_name = tag_attr[:name]
-      tag.locals.parent_tag_type = 'radiogroup'
-      tag.expand
-    end
-
-    desc %{ Renders an @&lt;option/&gt;@ tag if the parent is a
-    @&lt;r:mailer:select&gt;...&lt;/r:mailer:select&gt;@ tag, an @&lt;input type=&quot;radio&quot;/&gt;@ tag if
-    the parent is a @&lt;r:mailer:radiogroup&gt;...&lt;/r:mailer:radiogroup&gt;@ }
-    tag 'mailer:option' do |tag|
-      @tag_attr = tag.attr.symbolize_keys
-      raise_error_if_name_missing &quot;mailer:option&quot;
-      result = &quot;&quot;
-      if tag.locals.parent_tag_type == 'select'
-        result &lt;&lt; %Q|&lt;option value=&quot;#{tag_attr.delete(:value) || tag_attr[:name]}&quot; #{add_attrs_to(&quot;&quot;)}&gt;#{tag_attr[:name]}&lt;/option&gt;|
-      elsif tag.locals.parent_tag_type == 'radiogroup'
-        tag.globals.option_count = tag.globals.option_count.nil? ? 1 : tag.globals.option_count += 1
-        options = tag_attr.clone.update({
-          :id =&gt; &quot;#{tag.locals.parent_tag_name}_#{tag.globals.option_count}&quot;,
-          :value =&gt; tag_attr.delete(:value) || tag_attr[:name],
-          :name =&gt; tag.locals.parent_tag_name
-        })
-        result &lt;&lt; %Q|&lt;label for=&quot;#{options[:id]}&quot;&gt;|
-        result &lt;&lt; input_tag_html( 'radio', options )
-        result &lt;&lt; %Q|&lt;span&gt;#{tag_attr[:name]}&lt;/span&gt;&lt;/label&gt;|
-      end
-    end
-
-    desc %{
-    Renders an obfuscated email address @&lt;option /&gt;@ tag
-    using the email.js file. Use nested @&lt;r:address&gt;...&lt;/r:address&gt;@ to specify the email
-    address and @&lt;r:label&gt;...&lt;/r:label&gt;@ to specify what the content of the tag should be. }
-    tag 'mailer:email_option' do |tag|
-      hash = tag.locals.params = {}
-      contents = tag.expand
-      address = hash['address'].blank? ? contents : hash['address']
-      label = hash['label']
-      if address =~ /([\w.%-]+)@([\w.-]+)\.([A-z]{2,4})/
-        user, domain, tld = $1, $2, $3
-        tld_num = TLDS.index(tld)
-        unless label.blank?
-        %{&lt;script type=&quot;text/javascript&quot;&gt;
-              // &lt;![CDATA[
-              mail4('#{user}', '#{domain}', #{tld_num}, &quot;#{label}&quot;);
-              // ]]&gt;
-              &lt;/script&gt;
-        }
+  def recipients
+    chosen_recipient = form_data[:recipient_choice]
+    if form_conf[:recipient_list] &amp;&amp; chosen_address = form_conf[:recipient_list][chosen_recipient]
+      [chosen_address]
         else
-        %{&lt;script type=&quot;text/javascript&quot;&gt;
-              // &lt;![CDATA[
-              mail4('#{user}', '#{domain}', #{tld_num}, '#{user}');
-              // ]]&gt;
-              &lt;/script&gt;
-        }
-        end
+      form_conf[:recipients]
       end
     end
 
-    tag &quot;mailer:email_option:label&quot; do |tag|
-      tag.locals.params['label'] = tag.expand.strip
+  def from
+    form_data[form_conf[:from_field]] || form_conf[:from] || &quot;no-reply@#{request.host}&quot;
     end
 
-    tag &quot;mailer:email_option:address&quot; do |tag|
-      tag.locals.params['address'] = tag.expand.strip
+  def cc
+    form_data[form_conf[:cc_field]] || form_conf[:cc] || &quot;&quot;
     end
 
-
-    desc %{
-    Renders the value of a datum submitted via a mailer form.  Used in the 'email', 'email_html', and
-    'config' parts to generate the resulting email. }
-    tag 'mailer:get' do |tag|
-      name = tag.attr['name']
-      if name
-        form_data[name].is_a?(Array) ? form_data[name].to_sentence : form_data[name]
-      else
-        form_data.to_hash.to_yaml.to_s
+  def reply_to
+    form_data[form_conf[:reply_to_field]] || form_conf[:reply_to] || from
       end
+
+  def subject
+    form_data[:subject] || form_conf[:subject] || &quot;Form Mail from #{request.host}&quot;
     end
 
-protected
+  def html_body
+    html_body = render_part( :email_html ) || nil
+  end
 
-  # Since several form tags use the &lt;input type=&quot;X&quot; /&gt; format, let's do that work in one place
-  def input_tag_html(type, opts=tag_attr)
-    options = { :id =&gt; tag_attr[:name], :value =&gt; &quot;&quot;, :class=&gt;get_class_name(type) }.update(opts)
-    results =  %Q(&lt;input type=&quot;#{type}&quot; )
-    results &lt;&lt; %Q(name=&quot;mailer[#{options[:name]}]&quot; ) if tag_attr[:name]
-    results &lt;&lt; &quot;#{add_attrs_to(&quot;&quot;, options)}/&gt;&quot;
+  def plain_body
+    part( :email ) ? render_part( :email ) : render_part( :email_plain )
   end
 
-  def add_attrs_to(results, tag_attrs=tag_attr)
-    # Well, turns out I stringify the keys so I can sort them so I can test the tag output
-    tag_attrs.stringify_keys.sort.each do |name, value|
-      results &lt;&lt; %Q(#{name.to_s}=&quot;#{value.to_s}&quot; ) unless name == 'name'
+  def plain_or_default_body
+    (plain_body.nil? || plain_body.empty?) ? default_body : plain_body
     end
-    results
+  
+  def default_body
+    &quot;The following information was posted:\n#{form_data.to_hash.to_yaml}&quot;
   end
 
-  # Get the default css class based on type
-  def get_class_name(type, class_name=nil)
-    class_name = 'mailer-form' if class_name.nil? and %(form).include? type
-    class_name = 'mailer-field' if class_name.nil? and %(text password file select textarea).include? type
-    class_name = 'mailer-button' if class_name.nil? and %(submit reset).include? type
-    class_name = 'mailer-option' if class_name.nil? and %(checkbox radio).include? type
-    class_name
+  def max_filesize
+    form_conf[:max_filesize] || 0
   end
 
-  # Raises a 'name missing' tag error
-  def raise_name_error(tag_name)
-    raise MailerTagError.new( &quot;`#{tag_name}' tag requires a `name' attribute&quot; )
+  def attached_files
+    files = []
+    form_data.each_value do |d|
+      files &lt;&lt; d if d.is_a?(Tempfile) || d.class == StringIO
   end
-  def raise_error_if_name_missing(tag_name)
-    raise_name_error( tag_name ) if tag_attr[:name].nil? or tag_attr[:name].empty?
+    files
   end
 
+  
   # Does the work of actually sending the emails
-  def send_mail()
-    begin
-      # Data defined in config part
-      recipients = form_conf[:recipients]
-      from = form_data[form_conf[:from_field]] || form_conf[:from] || &quot;no-reply@#{request.host}&quot;
-      reply_to = form_data[form_conf[:reply_to_field]] || form_conf[:reply_to] || from
-      # Render the email templates from page parts
-      plain_body = part( :email ) ? render_part( :email ) : render_part( :email_plain )
-      html_body = render_part( :email_html ) || nil
-      # If we haven't defined any kind of mail template, use a default text one.
-      if (plain_body.nil? or plain_body.empty?) and (html_body.nil? or html_body.empty?)
-        plain_body = &lt;&lt;-EMAIL
-The following information was posted:
-#{form_data.to_hash.to_yaml}
-        EMAIL
+  def send_mail
+    create_actionmailer_deliver_method
+    ActionMailer::Base.deliver_generic_mailer(
+      :recipients =&gt; recipients,
+      :from =&gt; from,
+      :subject =&gt; subject,
+      :plain_body =&gt; plain_or_default_body,
+      :html_body =&gt; html_body,
+      :cc =&gt; cc,
+      :headers =&gt; { 'Reply-To' =&gt; reply_to },
+      :files =&gt; attached_files,
+      :filesize_limit =&gt; max_filesize
+    )
+    true
+  rescue
+    @form_error = &quot;Error encountered while trying to send email. #{$!}&quot;
+    false
       end
+
       # Since we can't have a subclass of ActionMailer in our behavior file,
       # We add a generic mailer method to the ActionMailer::Base clase.
       # Is this a hack? Yes. Does it work? Yes.
+  def create_actionmailer_deliver_method
       ActionMailer::Base.module_eval( &lt;&lt;-CODE ) unless ActionMailer::Base.respond_to? 'generic_mailer'
           def generic_mailer(options)
             @recipients = options[:recipients]
@@ -253,31 +136,25 @@ The following information was posted:
             @subject = options[:subject] || &quot;&quot;
             @headers = options[:headers] || {}
             @charset = options[:charset] || &quot;utf-8&quot;
-            @content_type = &quot;multipart/alternative&quot;
+        @content_type = &quot;multipart/mixed&quot;            
               if options.has_key? :plain_body
                 part :content_type =&gt; &quot;text/plain&quot;, :body =&gt; (options[:plain_body] || &quot;&quot;)
               end
               if options.has_key? :html_body and !options[:html_body].blank?
                 part :content_type =&gt; &quot;text/html&quot;, :body =&gt; (options[:html_body] || &quot;&quot;)
               end
+        # attchments
+        options[:files].each do |a|
+          # only attach files that are below the filesize limit
+          if (options[:filesize_limit] == 0 || a.size &lt;= options[:filesize_limit]) then
+            attachment( :content_type =&gt; &quot;application/octet-stream&quot;,
+                        :body =&gt; a.read,
+                        :filename =&gt; a.original_filename )
+          else
+            raise &quot;The file &quot; + a.original_filename + &quot; is too large. The maximum size allowed is &quot; + options[:filesize_limit].to_s + &quot; bytes.&quot; 
           end
-      CODE
-      # Now deliver mail using our new generic_mail method
-      ActionMailer::Base.deliver_generic_mailer(
-        :recipients =&gt; recipients,
-        :from =&gt; from,
-        :subject =&gt; form_data[:subject] || form_conf[:subject] || &quot;Form Mail from #{request.host}&quot;,
-        :plain_body =&gt; plain_body,
-        :html_body =&gt; html_body,
-        :cc =&gt; form_data[form_conf[:cc_field]] || form_conf[:cc] || &quot;&quot;,
-        :headers =&gt; { 'Reply-To' =&gt; reply_to }
-      )
-    rescue
-      @form_error = &quot;Error encountered while trying to send email. #{$!}&quot;
-      return false
     end
-    true
   end
-
+    CODE
+  end
 end
-</diff>
      <filename>app/models/mailer_page.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 class MailerExtension &lt; Radiant::Extension
-  version &quot;0.1&quot;
+  version &quot;0.2&quot;
   description &quot;Provides a page type for email forms and generic mailing functionality. Based on Matt McCray's behavior.&quot;
   url &quot;http://dev.radiantcms.org/svn/radiant/branches/mental/extensions/mailer/&quot;
 </diff>
      <filename>mailer_extension.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,13 +2,8 @@ require File.dirname(__FILE__) + '/../test_helper'
 
 class MailerExtensionTest &lt; Test::Unit::TestCase
   
-  # Replace this with your real tests.
-  def test_this_extension
-    flunk
-  end
-  
   def test_initialization
-    assert_equal RADIANT_ROOT + '/vendor/extensions/mailer', MailerExtension.root
+    assert MailerExtension.root.match(%r{/vendor/extensions/mailer$})
     assert_equal 'Mailer', MailerExtension.extension_name
   end
   </diff>
      <filename>test/functional/mailer_extension_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>a6dd0d045501d8210e7e558419634c6aa0115eda</id>
    </parent>
  </parents>
  <author>
    <name>Glenn Murray</name>
    <email>glenn@trike.com.au</email>
  </author>
  <url>http://github.com/tricycle/radiant-mailer-extension/commit/04ee071195a3a0eddb3392a4d1117ba8d2700605</url>
  <id>04ee071195a3a0eddb3392a4d1117ba8d2700605</id>
  <committed-date>2008-07-30T17:37:13-07:00</committed-date>
  <authored-date>2008-07-29T22:21:30-07:00</authored-date>
  <message> File upload facility and submit placeholder [Tobin Richard (tobin.richard@gmail.com) Tricycle Developments - tricycledevelopments.com]</message>
  <tree>b22fb83bbd297584a3d3a0de32827223f147dfc6</tree>
  <committer>
    <name>Glenn Murray</name>
    <email>glenn@trike.com.au</email>
  </committer>
</commit>
