require 'action_mailer' # # Mailer Behavior for Radiant # created by: M@ McCray - elucidata.net/mattmccray.com # version: 0.2.1 # contact: darthapo@gmail.com class MailerBehavior < Behavior::Base class MailerTagError < StandardError; end register "Mailer" description %{ The Mailer behavior 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. } attr_reader :form_name, :form_conf, :form_error, :form_data, :tag_attr # Page processing. If the page has posted-back, it will try to deliver the emails # and redirect to a different page, if specified. def process(request, response) @request, @response = request, response @form_name, @form_error = nil, nil if request.post? @form_name = request.parameters[:mailer_name] @form_conf = page_config['mailers'][form_name].symbolize_keys || {} @form_data = request.parameters[:mailer] # If there are recipients defined, send email... if form_conf.has_key? :recipients if send_mail and form_conf.has_key? :redirect_to response.redirect( form_conf[:redirect_to] ) else super(request, response) end else @form_error = "Email wasn't sent because no recipients are defined" super(request, response) end else super(request, response) end end # We need to process the page everytime, so that we can send the email! def cache_page? false end # Mailer Tags: define_tags do url = request.request_uri unless request.nil? # This is just for creating the namespace. tag "mailer" do |tag| tag.expand end # The ... tag is required. # It should encompass all of the helper tags for creating form fields. tag "mailer:form" do |tag| @tag_attr = { :class=>get_class_name('form') }.update( tag.attr.symbolize_keys ) raise_error_if_name_missing 'mailer:form' # Build the html form tag... results = %Q(
) results << %Q() results << %Q(
#{form_error}
) if form_error results << tag.expand results << %Q(
) end # Build tags for all of the tags... %w(text password file submit reset checkbox radio hidden).each do |type| tag "mailer:#{type}" do |tag| @tag_attr = tag.attr.symbolize_keys raise_error_if_name_missing "mailer:#{type}" unless %(submit reset).include? type input_tag_html( type ) end end # A tag. This is compatible with the tag as well tag 'mailer:select' do |tag| @tag_attr = { :id=>tag.attr['name'], :class=>get_class_name('select'), :size=>'1' }.update( tag.attr.symbolize_keys ) raise_error_if_name_missing "mailer:select" tag.locals.parent_tag_name = tag_attr[:name] tag.locals.parent_tag_type = 'select' results = %Q(" end # A tag, of course tag 'mailer:textarea' do |tag| @tag_attr = { :id=>tag.attr['name'], :class=>get_class_name('textarea'), :rows=>'5', :cols=>'35' }.update( tag.attr.symbolize_keys ) raise_error_if_name_missing "mailer:textarea" results = %Q(" end # Special tag for radio groups. This one works with the tag tag 'mailer:radiogroup' do |tag| @tag_attr = tag.attr.symbolize_keys raise_error_if_name_missing "mailer:radiogroup" tag.locals.parent_tag_name = tag_attr[:name] tag.locals.parent_tag_type = 'radiogroup' tag.expand end # This is a custom tag that will render and | 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 => "#{tag.locals.parent_tag_name}_#{tag.globals.option_count}", :value => tag_attr[:value] || tag_attr[:name], :name => tag.locals.parent_tag_name }) result << input_tag_html( 'radio', options ) result << %Q|| end end # For use with email template parts -- retrieves the data posted by the form tag 'mailer:get' do |tag| name = tag.attr['name'] if name form_data[name] else form_data.to_hash.to_yaml.to_s end end end protected # Since several form tags use the format, let's do that work in one place def input_tag_html(type, opts=tag_attr) options = { :id => tag_attr[:name], :value => "", :class=>get_class_name(type) }.update(opts) results = %Q(" 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 << %Q(#{name.to_s}="#{value.to_s}" ) unless name == 'name' end results 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 end # Raises a 'name missing' tag error def raise_name_error(tag_name) raise MailerTagError.new( "`#{tag_name}' tag requires a `name' attribute" ) end def raise_error_if_name_missing(tag_name) raise_name_error( tag_name ) if tag_attr[:name].nil? or tag_attr[:name].empty? end # Does the work of actually sending the emails def send_mail() begin # Data defined in config part recipients = form_conf[:recipients] from = form_conf[:from] || "no-reply@#{request.host}" # Render the email templates from page parts plain_body = @page.part( :email ) ? render_page_part( :email ) : render_page_part( :email_plain ) html_body = render_page_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 = <<-EMAIL The following information was posted: #{form_data.to_hash.to_yaml} EMAIL 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. ActionMailer::Base.module_eval( <<-CODE ) unless ActionMailer::Base.respond_to? 'generic_mailer' def generic_mailer(options) @recipients = options[:recipients] @from = options[:from] || "" @cc = options[:cc] || "" @bcc = options[:bcc] || "" @subject = options[:subject] || "" @headers = options[:headers] || {} @charset = options[:charset] || "utf-8" if options.has_key? :html_body and !options[:html_body].nil? part "text/html" do |a| a.body = options[:html_body] || "" end end if options.has_key? :plain_body part "text/plain" do |p| p.body = options[:plain_body] || "" end end end CODE # Now deliver mail using our new generic_mail method ActionMailer::Base.deliver_generic_mailer( :recipients => recipients, :from => from, :subject => form_data[:subject] || "Form Mail from #{request.host}", :plain_body => plain_body, :html_body => html_body ) rescue @form_error = "Error encountered while trying to send email. #{$!}" return false end true end end