Add a contact form to Hartl's tutorial - ruby-on-rails

I'm learning Rails by following Hartl's tutorial and making my own adjustments to it. Now, I would like to extent it and add a contact form that sends an email message. Such is not included in the tutorial, but by the end of chapter 10 we're learned to use the mailer method and we've configured SendGrid on Heroku.
I already have the view set up in the routes and think it would require the following additional steps:
1) Terminal. rails generate mailer ContactForm
2) In app/mailers/contactform.rb:
def send_contactform_email(visitor)
#visitor = visitor
mail( :to => myemail#example.com, :from => visitor.email, :subject => 'Contact form' )
end
3) app/views/contactform_mailer/ (the view for the mail message) for example:
<h1>Website contact form</h1>
<p>On <$= ... %> <%= "#{#visitor.name} (#{#visitor.email} sent the following message:" %></p>
<p><%= #visitor.message %></p>
4) app\controllers\static_pages_controller (or another location?)
# Sends contact form email.
def send_contact_form_email
ContactFormMailer.send_contactform_email(visitor).deliver_now
redirect_to contact_path, notice: 'Message sent'
end
5) app\views\static_pages\contact.html.erb (I'm not sure about the first line, should I also do something in the routes.rb? My guess is this first line will have to tell to execute the method in step 4, which is not going to work the way it is now.)
<%= form_for(:static_pages, url: contactform_path) do |f| %>
<i class="pt-row-icon glyphicon glyphicon-user"></i> <%= f.label :name %>
<%= f.text_field :name, placeholder: 'Name', class: 'form-control' %>
<i class="pt-row-icon glyphicon glyphicon-envelope"></i> <%= f.label :email %>
<%= f.email_field :email, placeholder: 'Email', class: 'form-control' %>
<i class="pt-row-icon glyphicon glyphicon-envelope"></i> <%= f.label :message %>
<%= f.text_area :message, placeholder: 'Your messageā€¦', class: 'form-control' %>
<%= f.submit "Send", class: "btn btn-primary" %>
<% end %>
I don't think this is 100% correct yet, particularly the bold sections. What are your thoughts?
UPDATE, VERSION 2: I've tried to make the updates as suggested by Ven and now have the code below. The idea as I understand it is that
the controller in def contact sets the #message variable.
the form_for knows it should fill this variable with params[:message].
the controller adopts the values from the form_for and passes them to the mailer.
the mailer uses the mailer view to design the message to be sent.
the mailer sends it back to the controller that send the message.
1) App/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
before_action :valid_email?, only: [:send_message_email]
# Shows the contact form page
def contact
#message = message(message_params)
end
# Sends the message.
def send_message_email
#message = message(message_params)
if #message.valid?
MessageMailer.new_message(#message).deliver_now
redirect_to contact_path, notice: "Your messages has been sent."
else
flash[:alert] = "An error occurred while delivering this message."
render :new
end
end
private
def message_params
params.require(:message).require(:name, :email, :content)
end
def valid_email?(email)
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
email.present? && (email =~ VALID_EMAIL_REGEX)
end
end
2) Contact form in app\views\static_pages\contact.html.erb:
<%= form_for(message: params[:message], url: contact_path) do |f| %>
<%= f.label :name %> <%= f.text_field :name, placeholder: 'Name', class: 'form-control' %>
<%= f.label :email %> <%= f.email_field :email, placeholder: 'Email', class: 'form-control' %>
<%= f.label :content %> <%= f.text_area :content, placeholder: 'Your messageā€¦', class: 'form-control' %>
<%= f.submit "Send", class: "btn btn-primary" %>
<% end %>
3) Routes.rb
get 'contact' => 'static_pages#contact', as: 'contact'
post 'contact' => 'static_pages#send_message_email'
4) App/views/message_mailer.text.erb (and html.erb)
<%= #message[:name] %> <%= #message[:email] %> wrote:
<%= #message[:content] %>
5) App/mailers/message_mailer.rb
class MessageMailer < ApplicationMailer
default to: "myemail#example.com>"
def new_message(message)
#message = message
mail to: "myemail#example.com"
mail from: #message[:email]
mail subject: "Message from #{message[:name]}"
end
end
Now when I try to visit the contact form on the server, I get the error message: param is missing or the value is empty: message. It refers to the params.require(:message).require(:name, :email, :content) line. Not sure what I'm doing wrong. Changing it to params.require(:message).permit(:name, :email, :content) makes no difference.

4) app\controllers\static_pages_controller (or another location?)
This seems to be correct, if this is the github repo for said app.
def send_contact_form_email
You controller has an issue: this action will try to send the email, not matter if it's used in POST or GET. You should use two different actions, one for displaying the view (using GET), and one for sending the email (using the mailer class you created). (at this point, you might want to create another controller)
ContactFormMailer.send_contactform_email(visitor).deliver_now
Then, moving on: what you pass to your mailer is "visitor". There's no such variable.
You probably want to access something out of the params hash (which contains parameters for GET and POST requests), and use the same key as your form (form_for(:visitor ... => params[:visitor] (so you want to change that :static_pages)).
<p>On <$= ... %> <%= "#{#visitor.name} (#{#visitor.email} sent the following message:" %></p>
As this returns an object, and not a hash, #visitor.email needs to be #visitor[:email] inside the mailer.
One last thing: simply using params[:visitor] will mean people could leave the field blanks. You might want to look into strong parameters, that were added in Rails 4 (the book seems somewhat outdated?).
And lastly, you need to add routes to be able to reach these actions (one for the GET request - display the view - and one for the POST request - to submit the form).
PS:
mail( :to => myemail#example.com, :from => visitor.email, :subject => 'Contact form' )
Warning: here, you forgot to quote the email address. Also, you swapped the to/from parameters. You want to send TO your visitor email, not from it.
EDIT
params.require(:message).require(:name, :email, :content)
This will require said keys, but AFAIK on the same "level" as :message - the top one. You want to use permit:
params.require(:message) # require "namespace"
.permit(:name, :email, :content) # permit keys
#message = message(message_params)
Where is the message function defined?
mail to: "myemail#example.com"
mail from: #message[:email]
mail subject: "Message from #{message[:name]}"
This sends 3 different emails, since you called the mail function 3 times.

Related

Email form data in rails without model

I have a form that I've created to capture simple contact information from a user:
views/whitepapers/index.html.erb
<%= form_tag({action: "download"}, id: "whitepaper-form-#{w.id}") do %>
<%= label_tag 'name' %>
<%= text_field_tag "contact[name]", nil, class: "form-control" %>
<br/>
<%= label_tag 'email' %>
<%= email_field_tag "contact[email]", nil, class: "form-control" %>
<br/>
<%= label_tag 'phone' %>
<%= text_field_tag "contact[phone]", nil, class: "form-control" %>
<%= hidden_field_tag 'id', w.id %>
<%= hidden_field_tag 'whitepaper-name', w.title %>
<%= submit_tag "Download Now", class: "btn btn-success", id: "whitepaper-# {w.id}-submit" %>
<% end %>
Now, Once the user clicks the "Download" button, the file downloads, so I have that part taken care of. Now I'd like to email the form data without saving anything to the DB.
I've created the mailer: mailers/whitepaper_download_mailer.rb
class WhitepaperDownloadMailer < ApplicationMailer
def email_lead(contact)
#contact = contact
mail to: "admin#example.co", subject: "A Whitepaper Download!"
end
end
And I've started working on implementing in the controller, but all the examples I've run across have to do with data including the model. This is what I have so far, but it's not working in my controller:
controllers/whitepapers.rb
def download
#whitepaper = Whitepaper.find(params[:id])
#contact.name = params[:contact_name]
#contact.email = params[:contact_email]
#contact.phone = params[:contact_phone]
#contact.whitepaper_name = params[:whitepaper_name]
file_path = File.join(Rails.root, "public", #whitepaper.whitepaper_url)
send_file file_path
WhitepaperDownloadMailer.email_lead(#contact).deliver_now
end
models/whitepaper.rb
class Whitepaper < ActiveRecord::Base
mount_uploader :whitepaper, WhitepaperUploader
validates :title, presence: true
validates :abstract, presence: true
validates :whitepaper, presence: true
end
Obviously, I know this isn't going to work since I'm passing #contact to the mailer, but pulling form params into a structure (i.e. #contact.name). Should I be passing each of the parameter variables into the mailer:
WhitepaperDownloadMailer.email_lead(#contact.name, #contact.email, #contact.phone).deliver_now
Or is there some other way that I haven't found yet to make this mailer work?
I figured this out with help from #kevinthompson and Openstruct. So, directly from the form, in my controller controllers/whitepapers.rb:
def contact
#whitepaper = Whitepaper.find(params[:contact][:whitepaper_id])
file_path = File.join(Rails.root, "public", #whitepaper.whitepaper_url)
send_file file_path
if request.post?
#contact = OpenStruct.new(params[:contact])
WhitepaperDownloadMailer.email_lead(#contact).deliver_now
end
end
I also ended up changing the form_tag action in the view to coincide:
<%= form_tag({action: "contact"}, id: "whitepaper-form-#{w.id}") do %>

Mailboxer with rails 4 (undefined local variable or method `root_url')

Update
The error was that rails cant find the root_url
Visit <%= link_to root_url, root_url %> and go to your inbox for more info.
for a quick fix and I dont need to sent the user to the root_url just a notification for the user to go to the app. I change the code to this: on the mailbox email views
Visit **messages** and go to your inbox for more info.
Question
I got devise set with my rails 4 app. Im following the example mailboxer-app when I sent the message I get a error:
`error undefined local variable or method `root_url' for #<#<Class:0x007ffe0b881678>:0x007ffe0b068298>`
Stuff I have fix to get it working
Got the form sending message to user with email
user can sent and reply
mark as delete
view inbox,sentbox and trash
this are my steps
install gem -v 0.12.1
rails g mailboxer:install
run migration
use the code from the example app(controller,view,routes)
add to my user.rb acts_as_messageable and
Conversations Controller
before_filter :authenticate_user!
helper_method :mailbox, :conversation
def index
#inbox ||= current_user.mailbox.inbox.paginate(:page => params[:inbox], :per_page => 5 )
#sentbox ||= current_user.mailbox.sentbox.paginate(:page => params[:sentbox], :per_page => 5 )
#trash ||= current_user.mailbox.trash.paginate(:page => params[:trash], :per_page => 5 )
end
def create
recipient_emails = conversation_params(:recipients).split(',')
recipients = User.where(email: recipient_emails).all
conversation = current_user.
send_message(recipients, *conversation_params(:body, :subject)).conversation
redirect_to :conversations
end
form
<%= bootstrap_form_for :conversation, url: :conversations do |f| %>
<%= f.text_field :recipients%>
<%= f.text_field :subject%>
<%= f.text_field :body %>
<div class="form-actions">
<%= f.primary "send" %>
<%= submit_tag 'Cancel', type: :reset, class: 'btn btn-danger' %>
</div>
<% end %>
View
<% #inbox.each do |conversation| %>
<%= conversation.originator.username%>
<%= link_to raw(truncate(strip_tags(conversation.subject), :length => 15)), conversation_path(conversation) %>
<% end %>
Ok got the fix to this problem.
what happen is that the mailboxer mailer was looking for root_url. Rails 4.1 wont generate the views for that just copy the files from the source code and works greate.
and just change that part of the code here.
view/mailboxer/all of this files
message_mailer
notification_mailer
change this
Visit <%= link_to root_url, root_url %> and go to your inbox for more info.
to this
Visit **messages** and go to your inbox for more info.
Thanx to this guy supremebeing7. on the mailboxer issue page

ActionMailer delivery when following parameters met

Forgive me for asking what i believe is quite an in depth challenge (well for me at the moment anyway)
I have a small app that allows users to check in, check out and hopefully receive emails when a book has been checked back in by registering their interest via a remind me button
So far I have setup actionmailer (basic setup)
class ReminderMailer < ActionMailer::Base
default from: "email address"
def remind_email(book)
#book = book
#url = "http://localhost:3000"
mail(:to => #book.user.email, :subject => "Book Reminder")
end
I have all the config in place to send the emails as I am already doing that through devise.
I have also created the mailer templates. It is the logic I am stuck with.
So when a User checks a book out i pass this back to the model
<%= form_for #book do |f| %>
<%= f.label :checked_out, "Check Book Out?" %>
<%= f.check_box :checked_out, {}, true %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.hidden_field :checked_out, :value => true %>
<%= f.submit 'Checkout' %>
<% end %>
Check In
<%= form_for #book do |f| %>
<%= f.label :checked_out, "Check Book Back In" %>
<%= f.check_box :checked_out, {checked: false}, false %>
<%= f.hidden_field :user_id, :value => nil %>
<%= f.hidden_field :checked_out, :value => false %>
<%= f.submit 'Check In' %>
<% end %>
Register Interest
<%= form_for #book do |f| %>
<%= f.label :remind_me, "let Me know when book back in" %>
<%= f.check_box :remind_me, {checked: false}, false %>
<%= f.hidden_field :remind_me, :value => current_user.id %>
<%= f.submit 'Remind Me' %>
<% end %>
So my thinking is that when you register your interest your user id gets placed into the remind_me column, and what i want to achieve is that when the checked_out field is false and book.user_id is back to nil I would like the email to send the the user whos user_id is in the remind_me column
Am i thinking about this in the correct way?
if anyone can help it would be appreciated so that i can learn from this and then keep practicing it until I understand what is going on
There are 2 ways to answer:
The first one, don't use a form to check in a book and just call a method. For example: You replace your form with a link which call a new method in your controller:
<%= link_to "check in", check_in_book_path(#book.id) %>
In your books_controller you call a model method which check in the book:
def check_in
#book = Book.find params[:id]
#book.check_in!
redirect_to book_path(#book)
end
In your book model:
def check_in!
self.user = nil
self.checked_out = false
if self.save
RemindMailer.remind_mail(self).deliver
end
end
Don't forget to add the route for your new controller method.
The second way, if you keep your form, is shorter but more complicated. You need to add a callback to your model which will verify if the data changed. For example, in you book model:
after_save :send_mail_if_check_in
def send_mail_if_check_in
if !self.checked_out && self.changes[:user_id] && self.user.nil?
RemindMailer.remind_mail(self).deliver
end
end
I prefer the first solution because it seems to be a state machine which is more maintenable.
I hope this help

Attaching an e-mail form to a controller

I would like to change the below link into an form that posts params of the form to my controller to send an email... the current link works and sends an email...
<%= button_to 'Hello', contact_pages_path, :method => :put %>
In My controller I have:
def contact
Contact.contact_form.deliver
end
My Mailer:
class Contact < ActionMailer::Base
default from: "****"
default to: "****"
def contact_form
mail(:subject => "Registered")
end
end
and in my routes I have:
resources :pages do
put :contact, :on => :collection
end
I realise that I need to create a body in the mailer - but I am not sure how to create a form to do this and pass it all on. I did think about creating a model to do this, but I thought having an entire model for just sending an email from a form would be slight over kill?
You can create custom forms using form_tag and then use a text_area_tag to take in the body. As long as you give it a name, it will be sent in the params. Example (using HAML):
= form_tag contact_pages_path, :method => :put
= text_area_tag "body"
= submit_tag "Send"
And then in your controller you can access the text in the body with params[:body].
Look here for more information about the text_area_tag (takes in many options you may want to use) and you can also read up more on the form_tag.
This also doesn't require you to make an extra model.
<%= form_tag(contact_pages_path, :method => "post") do %>
<%= text_field_tag "article", "firstname" %>
<%= submit_tag("Search") %>
<% end -%>
When you submit it will go to contact_pages_path and in your controller try params[:article], so its value should be "first name".
try this
In erb file
<%= form_tag(contact_pages_path, :method => "post") do %>
From : <%= text_field_tag "from_email", "" %> <br/>
To : <%= text_field_tag "to_email", "" %> <br/>
Message:<br/>
<%= = text_area_tag "message" %>
<%= submit_tag "send" %>
<% end %>
in action
def contact
from_email = params[:from_email]
to_email = params[:to_email]
message = params[:message]
// do operation to send the mail
end

Rails: 'Email has already been taken' for every email I enter

Update:
Checking the console, a lot of the users are actually going into the database, even when this error message appears!
Even weirder, you will notice that I have a redirect to root_path on a successful save to the database. it never goes there. This is the log. If it saves, it sometimes goes to the show action but there's actually a moment when the browser appears to get stuck, everything blanks it and I have to click in the address field to push it through to show.index.html.
Redirected to http://localhost:3000root_path
Completed 302 Found in 34ms
[2012-01-08 18:08:27] ERROR URI::InvalidURIError: the scheme http does not accept registry part: localhost:3000root_path (or bad hostname?)
/Users/mel/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:746:in `rescue in merge'
/Users/me/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/uri/generic.rb:743:in `merge'
/Users/me/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/webrick/httpresponse.rb:163:in `setup_header'
/Users/me/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/webrick/httpresponse.rb:101:in `send_response'
/Users/me/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/webrick/httpserver.rb:86:in `run'
/Users/me/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/webrick/server.rb:183:in `block in start_thread'
Original Question:
I'm creating a form for users to sign up to win an ipad. It's got two fields, email and name.
When I submit the form, I keep getting the same error message no matter what email i enter
1 error prohibited this from being saved:
There were problems with the following fields:
Email has already been taken
Strangely, when I debug params, it shows whatever new email address I enter.
utf8: "\xE2\x9C\x93"
authenticity_token: UgkHFLBfKYM2tZ11u6ItWvPS5XeTIMhFuJDwvhgqe30=
ipad: !map:ActiveSupport::HashWithIndifferentAccess
name: Amy
email: franz#gmail.com
commit: Sign up
action: create
controller: ipads
Anyone have any idea? I'm a bit of a noob...
I'm just adding this code to an Enki blog I'm making on Rails...
I have this validation on my ipad.rb model (note, I had a column for a Twitter address in the migration that I'm just ignoring in the form)
attr_accessible :name, :email, :twitter
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:uniqueness => true
This is my ipads_controller.rb
class IpadsController < ApplicationController
def new
#ipad = Ipad.new
#title = "iPad Contest"
end
def create
#ipad = Ipad.new(params[:ipad])
if #ipad.save
redirect_to 'root_path'
else
#title = "Poo"
render 'new'
end
end
end
This is my new.html.erb
<h1>Win an iPad</h1>
<%= #title %>
<h1>Sign up for iPad</h1>
<%= form_for(#ipad) do |f| %>
<%= render 'shared/errror_messages' %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= debug params %>
Using redirect_to root_path instead of redirect_to 'root_path' fixed it. Noob error.

Resources