Topic: How to make a simple Contact Form

Has anybody ever made a Contact Form for a Rails website? There's not a single tutorial out there, neither in any of the Rails books I've got nor on the net.

I've got a view that looks like this:


<%= start_form_tag(:action => 'send_contact_form') %>
<table>
    <tr>
        <td class="label">Your Name:</td>
        <td><%= text_field_tag "name" %></td>
    </tr>
    <tr>
        <td class="label">Your E-mail:</td>
        <td><%= text_field_tag "email" %></td>
    </tr>
    <tr valign="top">
        <td class="label">Your Message:</td>
        <td><%= text_area_tag "message" %></td>
    </tr>
    <tr>
        <td></td>
        <td><%= submit_tag(" SEND ") %></td>
    </tr>   
</table>
<%= end_form_tag %>

I've also got a model called OrderMailer.rb that looks like this (and works for automatically generating and sending emails to users):


class OrderMailer < ActionMailer::Base

  def confirm(order)
    @subject        = 'UKAutoBarn.com Order Confirmation'
    @recipients     = order.email
    @from           = 'orders@ukautobarn.com'
    @body["order"]  = order
  end

  def sent(order)
    @subject        = 'UKAutoBarn.com Order Shipped'
    @recipients     = order.email
    @from           = 'orders@ukautobarn.com'
    @body["order"]  = order
  end
 
end


Can anybody help?

Last edited by PixelLover (2006-08-20 07:16:39)

Re: How to make a simple Contact Form

The ActionMailer::Base documentation explains how to set up mailers pretty well. Just adapt their examples to a contact form; it shouldn't be too difficult.

vinnie - rails forum admin

Re: How to make a simple Contact Form

PixelLover:
If you get this working why don't you post your own tutorial on using ActionMailer?

Re: How to make a simple Contact Form

something along the lines of...


#1. In your OrderMailer model: -

def message_from_visitor(email = 'anon@example.net', message)
  @subject = "Email sent via your website"
  @body['message'] = message
  @recipients = "youraddress@example.com"
  @from = email
  @sent_on = Time.now
  @headers = {}
end

.

#2. In your controller: -

def send_contact_form
  if request.post?
    OrderMailer.deliver_message_from_visitor(params[:email], params[:message])
    flash[:notice] = "Message sent OK!" # notify successful send
    redirect_to :action => 'index' # or wherever you wanna go
  end
end

.

#3. In view/order_mailer/message_from_visitor.rhtml: -

<%= h(@message) %>

.

Hopefully all the code is ok, I've cut-n-pasted it from my working copy, but I've cut out the stuff that sends to different recipients based on user id, so can't verify 100% it's working - let me know if you run into trouble and I'll see what I can do


Edit: apologies for the bad formatting, the instruction for where the code goes is ABOVE the code block, even though the next instruction is closer - have numbered the instructions and put dots in the break up the post, hopefully will help - can't wait for a nice RoR forum big_smile

Last edited by Baltar (2006-08-20 18:25:08)

Re: How to make a simple Contact Form

Hi Baltar,

Thanks, your code works great. I've implemented it and made only a few changes to it. The

email = 'anon@example.net'

bit of your Model code won't work here, so I just left it out. I also added a 'name' variable because I want to know the names of my visitors!

It's a shame there aren't any validation methods for ActionMailer (like in ActiveRecord), so I'll have to come up with my own validation methods. Do you have any in your contact form? I wonder how you would deal with visitors entering a non-valid email address and things like that...

Re: How to make a simple Contact Form

Glad it works, my one was a bit different, I had a select box filled with the user names of the authors on the site and then the visitor could choose which author to send an email to, enter their own email address (which wasn't checked) and then send the message. Hence the default 'anon@example.net' was there in case they didn't bother filling out the field. In my case I didn't care about getting back in touch with the visitor, I just wanted an easy, anonymous way for the authors to get feedback.

I suppose you could do a regex pattern match within the controller, something like

if request.post? and params[:email] ~= /emailregex/

but it's not exactly fool proof(!) - I'm like you and would love to know the 'proper' way to do validations on ActionMailer - anyone out there know??

Last edited by Baltar (2006-08-21 05:51:36)

Re: How to make a simple Contact Form

I haven't been on the net for a week so I'm a bit late. But this is how I did mine. It is a bit more verbose but the benefit is that it can be used to send HTML email as well as normal plain text email.

First I generate a model called Notifier with

ruby script/generate mailer Notifier multipart_alternative

Next I create an action for mailing in my controller

def my_mailer

from_name = params[:comment][:name]
from_email = params[:comment][:email]
the_subject = params[:comment][:subject]
message = params[:comment][:message]

begin
#First check if the senders email is valid
if from_email =~ /^[a-zA-Z0-9._%-]+@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,4}$/
#put all the contents of my form in a hash
mail_info = {"from_name" => from_name, "from_email" => from_email, "message" => message, "the_subject" => the_subject}
#Call the Notifier class and send the email
Notifier.deliver_multipart_alternative(mail_info)
#Display a message notifying the sender that his email was delivered.
flash[:notice] = 'Your message was successfully delivered.'
#Then redirect to index or any page you want with the message
redirect_to(:action => 'index')

else
#if the senders email address is not valid
#display a warning and redirect to any action you want
flash[:warning] = 'Your email address appears to be invalid.'
redirect_to(:action => 'index')
end

rescue
#if everything fails, display a warning and the exception
#Maybe not always advisable if your app is public
#But good for debugging, especially if action mailer is setup wrong
flash[:warning] = "Your message could not be delivered at this time. #$!. Please try again later"
redirect_to(:action => 'index')
end

end


Next I add the following to the file, notifier.rb in app/models

  #this is the method that actually sends the email
  def multipart_alternative(mail_info)

    @from = mail_info["from_email"]
    @recipients = "youremail.com"
    @subject = mail_info["subject"]
    @mail_info = mail_info
    @body["mail_info"] = mail_info
    @sent_on = Time.now
   
part :content_type => "text/plain",
:body => render_message("multipart_alternative_plain", "mail_info" => mail_info)

part :content_type => "text/html",
:body => render_message("multipart_alternative", "mail_info" => mail_info)   

end


Next you create the views for your email. You will need to create two views, one is multipart and the other is plain. This is done under app/views/notifier

So the first file, multipart_alternative_plain.rhtml could be:

Hello Baltar

You have recieved a message from <%= @mail_info["from_name"] %> about <%= @mail_info["the_subject"] %>

His message reads thus:

<%= @mail_info["message"] %>

Please consider it and reply to <%= @mail_info["from_email"] %>
Best Wishes,
Your Ruby Slave.


Then for the other file multipart_alternative.rhtml you could write:

<h3>Hello Baltar</h3>

<p>
You have recieved a message from <%= @mail_info["from_name"] %> about <%= @mail_info["the_subject"] %>
</p>

<br/>
His message reads thus:
<br/>
<p>
<%= @mail_info["message"] %>
<p>
Please consider it and reply to <%= @mail_info["from_email"] %><br/>
Best Wishes,<br/>
Your Ruby Slave.<br/>


Finally the view for sending emails in your application. If added some javascript to verify if the contents of the form are filled in so you don't get blank emails. Also the input type being used is a button so that it can verify before submitting the form.

<script>
function checkForm(){
var _1=document.getElementById("comment_email").value;
var _2=document.getElementById("comment_name").value;
var _3=document.getElementById("comment_message").value;
var _4=document.getElementById("comment_subject").value;

if(_1.length==0||_2.length==0||_3.length==0||_4.length==0){
alert("You must fill every part of this form to continue");
}else{
document.getElementById("contact_form").submit();
}
}
</script>

<% if flash[:notice] %><div class="green flash"><b>Success:</b> <%= flash[:notice] %></div><br/><% end -%>
<% if flash[:warning] %><div class="red flash"><b>Error:</b> <%= flash[:warning] %></div><br/><% end -%>

<form action="/my_mailer" method="post" id="contact_form">

<p>Your name <br/>
<%= text_field 'comment', 'name' , :size => 36 %></p>

<!-- You might want to make subject a drop down select box -->
<p>Your inquiry type <br/>
<%= text_field 'comment', 'subject' , :size => 36 %></p>

<p>Your Email (is validated)<br/>
<%= text_field 'comment', 'email' , :size => 36 %></p>

<p>Your Message <br/>
<%= text_area 'comment', 'message', :cols => 42, :rows => 9  %></p>
<br/><br/>
<!-- Before you submit, check if the values are there -->
<%= submit_tag 'Report Error', :onclick => 'checkForm();' , :type => 'button' %>

</form>


Hope it wasn't too verbose. Of course you have to configure actionMailer and whatnot in environment.rb.

Last edited by daibatzu (2006-08-21 14:37:31)

Re: How to make a simple Contact Form

nice write up daibatzu!

never really used html emails much but can see the attraction now in having my ruby minions deliver pretty emails!

I think your method of inline javascript is probably the easiest way of making sure the form is filled corectly, does seem a pity that Notifier can't have some validates_ rules in it, but I'm sure DHH and the team have some reasons

This post has turned into quite the mini-tutorial now, awesome! big_smile

Last edited by Baltar (2006-08-21 08:15:10)

Re: How to make a simple Contact Form

Apparently Javascript can also validate regular expressions so it may be possible to remove the email validation check from the controller and place it in the javascript. But I'll have to look into that.

Re: How to make a simple Contact Form

Keep in mind that moving validations over to the client side means users can bypass the validations.

Railscasts - Free Ruby on Rails Screencasts

Re: How to make a simple Contact Form

Oops. Silly me.

12

Re: How to make a simple Contact Form

Thanks, Daibatzu. I followed the instructions in your code but am getting the following error when I try to pull up the contact page (my_mailer):

NoMethodError (undefined method `[]' for nil:NilClass):
    /app/controllers/front_controller.rb:285:in `contact_us'
    /vendor/rails/actionpack/lib/action_controller/base.rb:941:in `perform_action_without_filters'
    /vendor/rails/actionpack/lib/action_controller/filters.rb:368:in `perform_action_without_benchmark'
    /vendor/rails/actionpack/lib/action_controller/benchmarking.rb:69:in `perform_action_without_rescue'
    /usr/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
    /vendor/rails/actionpack/lib/action_controller/benchmarking.rb:69:in `perform_action_without_rescue'
    /vendor/rails/actionpack/lib/action_controller/rescue.rb:82:in `perform_action'
    /vendor/rails/actionpack/lib/action_controller/base.rb:408:in `process_without_filters'
    /vendor/rails/actionpack/lib/action_controller/filters.rb:377:in `process_without_session_management_support'
    /vendor/rails/actionpack/lib/action_controller/session_management.rb:117:in `process'
    /vendor/rails/railties/lib/dispatcher.rb:38:in `dispatch'
    /vendor/rails/railties/lib/fcgi_handler.rb:150:in `process_request'
    /vendor/rails/railties/lib/fcgi_handler.rb:54:in `process!'
    /usr/local/lib/ruby/gems/1.8/gems/fcgi-0.8.6.1/./fcgi.rb:600:in `each_cgi'
    /usr/local/lib/ruby/gems/1.8/gems/fcgi-0.8.6.1/./fcgi.rb:597:in `each_cgi'
    /vendor/rails/railties/lib/fcgi_handler.rb:53:in `process!'
    /vendor/rails/railties/lib/fcgi_handler.rb:23:in `process!'
    /home/garrand/rails/garr/public/dispatch.fcgi:24

Any thoughts you might have will be greatly appreciated. Thanks again.

Last edited by ipg (2006-09-12 19:31:17)

Re: How to make a simple Contact Form

Hi, why not post your code. I'm sure you've made a mistake somewhere while copying the example above. I've just tried it again and it works perfectly. If you post it the my_mailer action in your controller, your view, notifier.rb model and both multipart_alternative and multipart_plain I'll see what I can do. Good Luck.

14

Re: How to make a simple Contact Form

Thanks, Daibatzu. Here is the code:

in controller front_controller.rb:

  # Displays contact page.
  def my_mailer
    from_name = params[:comment][:name]
    from_email = params[:comment][:email]
    the_subject = params[:comment][:subject]
    message = params[:comment][:message]
    begin
      #First check if the senders email is valid
      if from_email =~ /^[a-zA-Z0-9._%-]+@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,4}$/
        #put all the contents of my form in a hash
        mail_info = {"from_name" => from_name, "from_email" => from_email, "message" => message, "the_subject" => the_subject}
        #Call the Notifier class and send the email
        Notifier.deliver_multipart_alternative(mail_info)
        #Display a message notifying the sender that his email was delivered.
        flash[:notice] = 'Your message was successfully delivered.'
        #Then redirect to index or any page you want with the message
        redirect_to(:action => 'index')
      else
        #if the senders email address is not valid
        #display a warning and redirect to any action you want
        flash[:warning] = 'Your email address appears to be invalid.'
        redirect_to(:action => 'index')
    end
    rescue
      #if everything fails, display a warning and the exception
      #Maybe not always advisable if your app is public
      #But good for debugging, especially if action mailer is setup wrong
      flash[:warning] = "Your message could not be delivered at this time. #$!. Please try again later"
      redirect_to(:action => 'index')
    end
  end

notifier.rb:

class Notifier < ActionMailer::Base

  #this is the method that actually sends the email
  def multipart_alternative(mail_info)
    @from = mail_info["from_email"]
    @recipients = "mymail@mydomain.com"
    @subject = mail_info["subject"]
    @mail_info = mail_info
    @body["mail_info"] = mail_info
    @sent_on = Time.now

    part :content_type => "text/plain", :body => render_message("multipart_alternative_plain", "mail_info" => mail_info)
    part :content_type => "text/html", :body => render_message("multipart_alternative", "mail_info" => mail_info)
  end
end


views/notifier/multipart_alternative_plain.rhtml

Hello, Person, 

You have recieved a message from <%= @mail_info["from_name"] %> about <%= @mail_info["the_subject"] %>

His message reads thus:
<%= @mail_info["message"] %>

Please consider it and reply to <%= @mail_info["from_email"] %>

Best Wishes,
Your Ruby Slave.


views/notifier/multipart_alternative.rhtml

<h3>Hello Baltar</h3> 
<p> You have recieved a message from <%= @mail_info["from_name"] %> about <%= @mail_info["the_subject"] %> </p>

<br/> His message reads thus: <br/>

<p> <%= @mail_info["message"] %>
   
<p> Please consider it and reply to <%= @mail_info["from_email"] %>

<br/> Best Wishes,
<br/> Your Ruby Slave.</p>


and last but not least, the view in views/front/my_mailer.rhtml

<h2>Contact Us</h2>


<script>
function checkForm(){
    var _1=document.getElementById("comment_email").value;
    var _2=document.getElementById("comment_name").value;
    var _3=document.getElementById("comment_message").value;
    var _4=document.getElementById("comment_subject").value;
    if(_1.length==0||_2.length==0||_3.length==0||_4.length==0){
        alert("You must fill every part of this form to continue");
        }else{
        document.getElementById("contact_form").submit();
    }
}
</script>
<% if flash[:notice] %>
<div class="green flash"><b>Success:</b> <%= flash[:notice] %></div><br/>
<% end -%>

<% if flash[:warning] %><div class="red flash"><b>Error:</b> <%= flash[:warning] %></div><br/>
<% end -%>
   
<form action="/my_mailer" method="post" id="contact_form">
<p>Your name <br/> <%= text_field 'comment', 'name' , :size => 36 %></p> <!-- You might want to make subject a drop down select box -->
<p>Your inquiry type <br/> <%= text_field 'comment', 'subject' , :size => 36 %></p>
<p>Your Email (is validated)<br/> <%= text_field 'comment', 'email' , :size => 36 %></p>
<p>Your Message <br/> <%= text_area 'comment', 'message', :cols => 42, :rows => 9 %></p>
<br/><br/> <!-- Before you submit, check if the values are there -->
<%= submit_tag 'Report Error', :onclick => 'checkForm();' , :type => 'button' %> </form>


Thanks, again.

Re: How to make a simple Contact Form

Hello, I've noticed one mistake and think I know what your problem is.

Firstly, your contact form should not be my_mailer.rhtml. That is a seperate action on it's own and it doesn't have a view associated with it. You can define a new action in your front_controller, maybe call it contact. So you would have:

def contact
#doesn't really have to do anything.
end

Now create a view called contact.rhtml and put everything you've put in my_mailer.rhtml into this file (i.e the contents of my_mailer.rhtml into contact.rhtml). After this you MUST delete my_mailer.rhtml because it isn't necessary and is conflicting with your application.

This should fix your problem. I'm not sure but I think this is the problem you are having.

If you want to call my_mailer from a seperate controller remember that your form action should have the controller name prefixed. So you would have:

<form action="/front/my_mailer" method="post" id="contact_form">

instead of just

<form action="/my_mailer" method="post" id-"contact_form">

16

Re: How to make a simple Contact Form

Worked! Thanks, Daibatzu.

Re: How to make a simple Contact Form

Thanks a bunch for this!

Just a few notes to anyone else trying to use this.

I used daibatzu's code word for word.

The subject was not getting sent in the email.

In the Notifier.rb i changed

@subject = mail_info["subject"]

to
@subject = mail_info["the_subject"]

also not mentioned here is the environment.rb stuff

in environment.rb i added these lines at the very bottom

ActionMailer::Base.server_settings = {
  :address  => "mail.site.com",
  :port  => 25,
  :domain  => 'www.site.com',
  :user_name  => "mail_login_username",
  :password  => 'password',
  :authentication  => :login
    }

You may have to comment out this line in environment.rb (it was commented out for me by default)

# config.frameworks -= [ :action_web_service, :action_mailer ]

then restart your server.

that stuff is from the wiki

The other helpful tip is the routing(front_controller stuff) which Daibatzu mentions earlier.

I just want to say thanks to Daibatzu posting this.

Re: How to make a simple Contact Form

I found this example. I haven't gotten it to work though.

http://www.bphogan.com/learn/pdf/tablel … t_form.pdf

Re: How to make a simple Contact Form

Hi Dawie,

Looks very promising. Have you tried to implement it? No success?

Re: How to make a simple Contact Form

It works and its exactly what I use:

Tableless.rb Model

class Tableless < ActiveRecord::Base
 
  def self.columns()
    @columns ||= [];
  end
 
  def self.column(name, sql_type = nil, default = nil, null = true)
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  end
 
  # override the save method to prevent exceptions.
 
  def save(validate = true)
    validate ? valid? : true
  end
 
end

Contact.rb Model
class Contact < Tableless
  column :name, :string
  column :company, :string
  column :phone, :string
  column :email_address, :string
  column :message, :text
  validates_presence_of :name, :email_address, :message
end

Controller
def submit_contact
    @contact = Contact.new(params['contact'])
    if @contact.save
      begin
        Mailer::deliver_contact_message(@contact)
        redirect_to_index("Thank you for contacting us, we will endevour to reply ASAP.")
      rescue
        redirect_to_index("Sorry, an error occured in sending your message")
      end
    else
      @title = "Contact Us"
      render :action=> "contact"
    end
  end

View
<fieldset>
<% form_for :contact, :url => { :action => :submit_contact } do |form| %>

<table class="stripedlist" cellpadding="5" cellspacing="0">

<tr>
<th colspan="2">Please Enter your details:</th>
</tr>

<tr class="<%= cycle('odd', 'even') %>">
<td>Name:</td>
<td><%= form.text_field :name, :size => 20 %></td>
</tr>

<tr class="<%= cycle('odd', 'even') %>">
<td>Company:</td>
<td><%= form.text_field :company, :size => 20 %></td>
</tr>

<tr class="<%= cycle('odd', 'even') %>">
<td>Phone Number:</td>
<td><%= form.text_field :phone, :size => 20 %></td>
</tr>

<tr class="<%= cycle('odd', 'even') %>">
<td>Email:</td>
<td><%= form.text_field :email_address, :size => 20 %> </td>
</tr>

<tr class="<%= cycle('odd', 'even') %>">
<td>Message:</td>
<td><%= form.text_area :message %></td>
</tr>

<tr class="<%= cycle('odd', 'even') %>">
<td colspan="2"><div><%= submit_tag "Submit", :class => "submit" %></div></td>
</tr>

</table>
<% end %>
</fieldset>


Enviroment.rb
  c = YAML::load(File.open("#{RAILS_ROOT}/config/config.yml"))
  ActionMailer::Base.server_settings = {
    :address => c[RAILS_ENV]['email']['server'],
    :port => c[RAILS_ENV]['email']['port'],
    :domain => c[RAILS_ENV]['email']['domain'],
    :authentication => c[RAILS_ENV]['email']['authentication'],
    :user_name => c[RAILS_ENV]['email']['username'],
    :password => c[RAILS_ENV]['email']['password']
  }
  CONTACT_RECIPIENT = c[RAILS_ENV]['email']['contact_recipient']

Config.yml
development:
  email:
    server: mail.domain.com
    port: 25
    domain: domain.com
    authentication: login
    username:
    password:
    contact_recipient:

production:
  email:
    server: mail.domain.com
    port: 25
    domain: domain.com
    authentication: login
    username:
    password:
    contact_recipient:

Last edited by artpop (2006-09-30 18:39:03)