So I followed this wonderfully flawed tutorial:
http://matharvard.ca/posts/2011/aug/22/contact-form-in-rails-3/
...on making contact forms. It works great. The only problem is that it ONLY sends the subject. I think maybe the problem is in the notifications mailer:
notifications_mailer.rb
class NotificationsMailer < ActionMailer::Base
default :from => "noreply#youdomain.dev"
default :to => "you#youremail.dev"
def new_message(message)
#message = message
mail(:subject => "[YourWebsite.tld] #{message.subject}")
end
end
I would, of course, like it to send ALL the info the user submitted... (name, email address, subject, and body.
Also I was wondering how I could do a simple version of this with just the body where the subject is set to a default. (I want to have a small comment box that would send an email to me with the comment.) Would I have to make a whole new controller and model for that, or could this handle both?
UPDATE
Notifications Mailer View / new.html.erb
Name: <%= #message.name %>
Email: <%= #message.email %>
Subject: <%= #message.subject %>
Body: <%= #message.body %>
contact controller
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
flash[:success] = "Message was successfully sent."
redirect_to(root_path)
else
flash[:error] = "Please fill all fields."
render :new
end
end
end
message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
Basically it works... but it only sends the subject. I also got it to send a complete mail once with everything BUT the subject... but I can't remember how I did it.
Should I just smash this computer into a million pieces and go on a rampage?
Sigh...
UPDATE AGAIN
This is what the emails say with the above settings:
Subject: [liquid.radio] Whatever The Subject is. Body: Completely
blank
This is what they said after whatever the hell I did two weeks ago.
Subject: Message from liquid.radio
Body:
A contact enquiry was made by Richard Pryor at 2013-06-17 23:36.
Reply-To: richard#pryor.com
Subject: Scared for no reason Body: Oh
no... Oh God no! What is that?!
All I did was mess around with the notifications controller. Although I don't remember... for the life of me... what I did. But, as you can see... it send the complete message as it should... but a completely different subject.
Really kinda need help here.
First, this project places production settings in config/application.rb. Move the GMail ActionMailer settings to config/environments/production.rb. Add the letter_opener gem to your Gemfile's development group.
# Gemfile
group :development do
gem 'letter_opener'
end
Add the letter_opener settings to your development environment.
# config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
Add active_attr gem
# Gemfile
gem 'active_attr'
Make sure you run bundle install
Replace your messages model with robust ActiveAttr implementation.
# app/models/message.rb
class Message
include ActiveAttr::Model
attribute :name
attribute :email
attribute :subject
attribute :body
validates_presence_of :name, :email, :subject, :body
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
end
Improve the routes.
# config/routes.rb
get 'contact' => 'contact#new', :as => 'contact'
post 'contact' => 'contact#create', :as => 'contact'
Make sure your email template is correct
# app/views/notifications_mailer/new_message.text.erb
Name: <%= #message.name %>
Email: <%= #message.email %>
Subject: <%= #message.subject %>
Body: <%= #message.body %>
Update: December 12, 2013
I've created a Rails 3.2 project on GitHub to assist anyone searching for a modern solution to creating contact forms in Rails. Merry Christmas!
https://github.com/scarver2/contact_form_app
Ultimately here's what I did... I realize this isn't a true solution. But I still don't have a different one.
notifications_mailer.rb
class NotificationsMailer < ActionMailer::Base
default :from => "example#gmail.com"
default :to => "example#gmail.com"
def new_message(message)
#message = message
mail(:subject => "[liquid.radio] #{message.subject}", :body => "
From: #{message.name}
Reply to: #{message.email}
Subject: #{message.subject}
Message: #{message.body}")
end
end
Not how the mailer is supposed to work at all... but at least it sends a complete message.
If you're in a time crunch... like I was... this will get you there. I will accept a real answer (probably scarver2's) once I stop getting blank emails any other way.
Related
I'm trying to make a form that you can only access once before a certain boolean is set to true, and along with it you input some other info, the problem is that I don't know how to set it up properly, so far the form loads no problem, but it doesn't update the information of the user.
my main problem is that I don't know how to set up the routes and the form, since most of the time I just use resources: to make them automatically
here is my form :
<%= form_for #user, url: organizer_user_path, method: :put do |f| %>
.
.
.
<%end%>
my routes, which I don't think put is correct at all
get 'organizer/user', to: "users#organizer_form"
put 'organizer/user', to: "users#organizer_registration"
and my controller
def organizer_form
#user = User.find(current_user[:id])
end
def organizer_registration
#user = User.find(current_user[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
render 'organizer_form'
end
end
I'm sure the problem lies in how I define my routes and how they are called onto the form, any help?
here are the params that are submitted:
--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: "✓"
_method: put
authenticity_token: w9zTUQLK+4kZtbvRCfcYfZgn8ma4xSRdbTMy+fktpoHlGADxHSB6u9QBh3K3zv72V/Ys2b3Q1MiKC0Gr+OqNJw==
user: !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
organizer: 'true'
contact_name: Name
last_name: Last name
contact_email: mail1#mail.com
cellphone: '1234567'
company: ''
office_phone: ''
website: ''
permitted: false
commit: continuar
controller: users
action: organizer_registration
permitted: false
This is my user_params
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :organizer, :contact_name, :last_name, :contact_email, :cellphone, :company, :office_phone, :website)
end
Finally found the answer! I had
attr_accessor :organizer, :contact_name, :last_name, :contact_email, :cellphone, :company, :office_phone, :website
which somehow stopped the update from happening, so removing that made the updates save to the database.
I have made the following addition to my active admin interface:
action_item :only => :show do
link_to('Approve this article', approve_admin_article_path(article)) if article.approved.nil?
end
member_action :approve, :method => :get do
# do approval
redirect_to :action => :show, :notice => "Approved!"
end
This throws the following error:
undefined method `approved' for
:Arbre::HTML::Article
What I think is happening is Active Admin thinks I'm passing an article tag in, not an article class?
Does anyone know of a work around for this? perhaps aliasing?
Thanks!
class Article < ActiveRecord::Base
attr_accessible :body
# Relations:
belongs_to :articleable, polymorphic: true, :counter_cache => true
has_many :comments, as: :commentable, order: 'created_at DESC', dependent: :destroy
# Validations
validates_presence_of :body
validates_length_of :body, maximum: 15000
end
Found a workaround
There is something fishy when you name your class as 'Article', ActiveAdmin relate to it when rendering as <article> HTML tag - The problem is somewhere in the controller of course because this is where the article object is being generated
So, I override the controller
ActiveAdmin.register Article do
controller do
def show
# grabbing my desired Article and not the <article> tag into some global variable
##myarticle = Article.find(params[:id])
end
end
sidebar :article_details , :only => :show do
ul do
# using the ##myarticle which I know should be initialized
# (you can put .nil? checking here if you want)
li link_to 'Article Images (' + ##myarticle.images.count.to_s + ')' , admin_article_article_images_path(##myarticle)
li link_to 'Article Clips ('+##myarticle.clips.count.to_s + ')' , admin_article_article_clips_path(##myarticle)
end
end
end
Enjoy
Assuming you're having the issue in the 'show' block, you could change the show block to the following:
show do |object|
end
Then you can call object.some_method without the clash. This way you don't need to override the controller.
I have the following models, which basically are trying to mean that a professor has knowledge of many subjects for a particular level. The subjects are fixed, so there will be no new subjects created, there will be just "related" to a professor through the knowledge join table.
class Subject < ActiveRecord::Base
# Self Associations
has_many :subcategories, :class_name => "Subject"
belongs_to :category, :class_name => "Subject",:foreign_key => "parent_id"
# Associations
has_many :knowledges
has_many :professors, :through => :knowledges
end
class Professor < ActiveRecord::Base
# Associations
has_many :knowledges
has_many :subjects, :through => :knowledges
...
end
class Knowledge < ActiveRecord::Base
# Associations
belongs_to :professor
belongs_to :subject
has_one :level
attr_accessible :subject_id, :professor_id
validates :subject_id, :uniqueness => { :scope => :professor_id }
end
I want to have a form that will let a professor to add a subject to his account, and I decided to have a form for a knowledge (as I want to be able to insert a level too).
It looks like this:
<%= simple_form_for #knowledge,:url => professor_knowledges_path, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group select optional">
<%= label_tag "Subject Type", nil, :class => "select optional control-label"%>
<div class="controls">
<%= select_tag "Parent Subject", options_from_collection_for_select(#parent_subjects, "id", "name"), :id => "knowledge_parent_subject" %>
</div>
</div>
<%= f.input :subject_id, :collection => #subjects, :label => "Subject" %>
<%= f.input :level %>
<%= f.button :submit, t('add_form'),:class => 'btn-primary' %>
<% end %>
And in the create action of the Knowledges controller I have this:
def create
#knowledge = Knowledge.create(:professor_id => current_professor.id, :subject_id => params[:knowledge][:subject_id])
end
I would like/expect to get an ActiveRecord saying that this knowledge can't be inserted because there is a uniqueness violation, but nops, I just see a 500 in the logs and a rollback, but it seems the execution goes on. So my question is: What am I doing wrong, or how I could improve this modeling situation? I believe the form needs to be related to the join model as I want to have fields of that model on it...But maybe I am wrong, and I could do in an easy/cleaner way.
EDIT:
As asked in one of the comments, here is the log of the submission of the form and the 500 error right after the rollback:
Started POST "/professors/1/knowledges" for 127.0.0.1 at 2012-07-01 00:45:39 -0700
Processing by KnowledgesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4JVyxWnIh37kyBwLwLGTHk/znsI1c5wrJvaWjKKT5tM=", "Parent Subject"=>"1", "knowledge"=>{"subject_id"=>"1"}, "commit"=>"Añadir", "professor_id"=>"1"}
Professor Load (0.4ms) SELECT `professors`.* FROM `professors` WHERE `professors`.`id` = 1 LIMIT 1
Completed 500 Internal Server Error in 4ms
I added some conditions in the create action, like this:
def create
#knowledge = Knowledge.new(:professor_id => current_professor.id, :subject_id => params[:knowledge][:subject_id])
if #knowledge.save
flash[:notice] = "Success..."
redirect_to professor_path(current_professor)
else
render :action => 'new'
end
end
And this actually shows the following right after the 500:
Completed 500 Internal Server Error in 6ms
ActiveRecord::RecordInvalid (Validation failed: Subject has already been taken):
I wonder why the exception is raised instead of just adding the errors into the object and let me manage that situation. Isn't what the following line should be doing?
validates :subject_id, :uniqueness => { :scope => :professor_id }
That error means you are trying to insert duplicate subject_id / professor_id pairs on that table. Most often happens when either the subject_id or professor_id is null.
Are you sure the controller is getting the correct parameters? I would check the logs to make sure the inserts are what you would expect.
I don't have enough reputation to comment...my answer is more some things to try than a definitive answer, sorry.
It looks like the save is failing due to validation errors. You can try to handle those in your 'else' block. The following will give you a description of all validation errors (useful for debugging).
#knowledge.errors.full_messages
You haven't shown what is happening in the 'new' action. I suspect this is where the errors are occurring.
Does the same issue occur (i.e. the validation problem) in the console? If so, try cleaning out your databases (beware - the following will erase & rebuild all your databases).
rake db:drop:all db:create:all db:migrate db:test:prepare
Also, if you haven't already, add an index to your migration for Knowledge to prevent duplicates being added to the db. e.g.
add_index :knowledges, [ :professor_id, :subject_id ], unique: true
In my application I have a polymorphic Idea model. I want to use it with different entities like project, opportunity etc.
In the idea table it is differentiated using ideable_id and ideable_type.
So as an example, for a Project ideable_id is the project_id and ideable_type is Project.
For Opportunity ideable_id is the opportunity_id and ideable_type is Opportunity
I want to create an idea for a model called ideabank. ideabank is a virtual model, it's not an entity like project or opportunity. How should I create such a model without a database representation which will give me ideable_id and ideable_type?
Or should I leave the fields for ideable_id and ideable_type free while adding idea to ideabank?
If you are using rails 3, see this
http://railscasts.com/episodes/219-active-model
models/message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :content
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
messages_controller.rb
def create
#message = Message.new(params[:message])
if #message.valid?
# TODO send message here
flash[:notice] = "Message sent! Thank you for contacting us."
redirect_to root_url
else
render :action => 'new'
end
end
I've made a simple form for sending emails with attachments. The problem is that I don't know how to make it work in the mailer. All tutorials that I've found so far are covering the scenario when the file for the attachment is already somewhere one the server and I wasn't able to find anything about validating email contents.
So, I've got 2 questions for you:
How can I let users send emails with attachments uploaded by them?
How can I validate user's inputs and extensions of attachments?
My email form...
<div id="form_wrapper">
<%= form_for(:kontakt, :url => {:action => 'kontakt'}, :html => { :multipart => true }, :remote=>true) do |f| %>
<ul>
<li>
<%= f.label(:email) %>
<%= f.text_field(:email) %>
</li>
<li>
<%= f.label(:content) %>
<%= f.text_area(:content, :size => "42x7") %>
</li>
<li>
<%= f.label(:preview, :class=>:preview )%>
<%= f.file_field :preview %>
</li>
</ul>
<%= image_submit_tag("blank.gif",:id=>"send_email", :class=>"action submit") %>
<%= link_to("Reset", {:controller=>'frontend',:action => 'index'},:remote => true, :class => 'action reset') %>
<% end %>
</div>
<%= javascript_include_tag 'jquery.form.js','jquery.rails','jquery.remotipart' %>
and my mailer...
class Contact < ActionMailer::Base
default :from => "xxxxx#gmail.com"
def wiadomosc(email)
#content=email[:content]
file=email[:preview]
attachments[file.original_filename] =File.read(file.path)
mail(
:subject=>"www.XXXXXX.pl - you've got a new message",
:reply_to =>"xxxxx#gmail.com",
:to => email[:email]
)
end
end
I came up with attachments[file.original_filename] =File.read(file.path) and it's adding attachments but the files are coruppted and cannot be opened...
Any thoughts or links would be greatly appreciated.
I've found that if you want to send email with attachment in rails, you need to attach it that way...
attachments[file.original_filename] = File.open(file.path, 'rb'){|f| f.read}
So the whole mailer method might look like this...
def message(email)
#content=email[:content]
unless email[:email_attachment].nil?
file=email[:email_attachment]
attachments[file.original_filename] = File.open(file.path, 'rb'){|f| f.read}
end
mail(
:subject=>"www.XXXXXX.pl - you've got a new message",
:reply_to =>"xxxxx#gmail.com",
:to => email[:email]
)
end
As for the validations, there are two methods that I've found. First one is quite obvious. You have to create a table in your database and validate form's inputs in model just like you always do. If you do it that way than everything will be saved in your database. It has some advantages, for exmple: you can use it as an archive or for some kind of statistics about your clients in your app or even put it through some sql triggers. However, if you don't want to save anything than you can create "tableless model" (Railscasts #193 and original Codetunes). All you have to do is to place this code at the begining of your model:
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
Than you have to list your columns...
column :name, :string
column :company, :string
column :email, :string
column :content, :text
column :type_of_question, :string
column :email_attachment, :string
And after that you can place your model's code...
has_attached_file :email_attachment
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
TYPES = ['application/zip', 'multipart/x-zip', 'application/x-zip-compressed']
validates :name, :presence => {:message => "Blah blah blah"}
validates :email, :presence => {:message => "Blah blah blah"},
:format=>{:unless=> Proc.new{|s| s.email.nil?|| s.email.empty? },
:with => EMAIL_REGEX, :message => "Blah blah blah"}
validates :content, :presence => {:message => "Blah blah blah"}
validates_inclusion_of :type_of_question, :in => ["Blah1", "Blah2", "Blah3"],
:message => "%{value} is not on the list of types"
validates_attachment_content_type :email_attachment, :content_type => TYPES,
:message => "The attachment has wrong extension..."
Right now, I'm using Gmail for sending emails but it's quite slooow. It takes about 2 minutes to send a short message with 2Kb test attachment. Have you got any suggestions about how to speed up this process? Maybe you can recomend another provider or solution?
PS. Don't forget to check out 'client side validations' Railscasts #263