So I am trying to implement the password_reset functionality into my site using bcrypt. An issue I am having is the POST is going to my new action rather to my create action.
My View
<%= form_for password_resets_path, method: 'post' do %>
<div>
<h3>Please enter your email address</h3>
<%= text_field_tag :email, params[:email] %>
</div>
<div>
<%= submit_tag "Reset Password" %>
</div>
My Controller
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => 'Email sent with password reset instructions.'
end
end
My Routes
resources :password_resets
And I am getting this error
ActionController::RoutingError (No route matches [POST] "/password_resets/new"):
I looked at different solutions already, and since I do not have a model the #object, would not work for me. Since I am simply just trying to call to an action.
I feel like I am missing something so very simple but for the life of me I have been unable to figure it out. Many thanks in advance to whomever is the one to help me.
Problem: <%= form_for password_resets_path, method: 'post' do %>
form_for needs an object. If you don't want an object, just use the form_tag helper:
<%= form_tag password_resets_path do %>
<%= text_field_tag :email, params[:email], placeholder: "Please enter your email address" %>
<%= submit_tag "Reset Password" %>
<% end %>
This should work for you.
Related
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.
I would like to have a drop down menu with a list of all the user names in the db. From there, I would like the user to choose his/her name and be able to click login and be taken to their respective page. At this point, a password is not needed. Currently, I have the following:
controller:
def login
#user = User.new
#users = User.all
# #user = User.find_by_id(:id)
# redirect_to user_path(#user)
end
view:
<%= form_for #user, url: '/login', html: {method: 'get'} do |f| %>
<%= f.label "Name" %>
<br/>
<%= select_tag :user, options_for_select(#users) do |users| %>
<%= link_to users.name, users %>
<% end %>
<br/>
<br/>
<%= f.submit 'Login' %>
<% end %>
I cannot seem to link the user to their path and also, i want to show the users name in the drop down menu. Currently, it shows a hexidecimal pointer.
Thank you in advance.
You shouldn't be making a new User object here: you just want to load one out of the database. What you want to do in the controller is just to set current_user to be one of the existing users, right?
Also you've got the form submitting back to the action which loads the form in, which seems weird. I would make it submit to a new action, like "set_current_user" which is a POST action.
in your login template:
<%= form_tag '/set_current_user' do %>
<%= f.label "Name" %>
<br/>
<%= select_tag "user_id", options_for_select(#users.collect{|user| [user.name, user.id] } %>
<br/>
<br/>
<%= submit_tag 'Login' %>
<% end %>
in the controller (you'll need to amend routes.rb to make the '/set_current_user' go to this action) you then need to set something which will keep the user logged in. The traditional way to do this is via session[:user_id], and to have a method current_user which uses this.
def set_current_user
session[:user_id] = params[:user_id]
redirect_to "/" and return
end
Your initial approach is reminiscent of how this sort of thing is normally handled, wherein you do have a form_for, but it's for a UserSession object rather than a User object.
I'm having a trouble when I'm trying to user params.require(...).permit(...)
In my application I received the follow param dic:
{"utf8"=>"✓",
"authenticity_token"=>"Vatzcb5tgTu2+wL1t6Of+FbIK8Ibp+tM03Naai4b2OU=",
"/login"=>{"username_or_email"=>"jonatasteixeira",
"password"=>"[FILTERED]"},
"commit"=>"Save /login" }
I would like to know why the my key received the "/login" name.
My view:
<h1>Login</h1>
<%= form_for(login_path) do |f| %>
<div class="field">
<%= f.label :username_or_email %><br>
<%= f.text_field :username_or_email %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.password_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', root_path %>
In my controller
class SessionsController < ApplicationController
# GET /login
def new
#user = User.new
end
# POST /login
def create
#user = User.find_by_emai(session_params[:username_or_email]) || User.find_by_username(session_params[:username_or_email])
if #user && #user.authenticate(session_params[:password])
session[:current_user_id] = #user.id
flash[:notice] = 'You are logged in'
else
flash[:notice] = 'Invalid password, username or email'
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def session_params
logger.info :login
params.require("/login").permit(:username_or_email, :password)
end
end
I dont want to use "/login" as key, I would like to use :login. Some one knows how could I adjust it?
Thanks!!
As #Rafal pointed out, you could code your call to form_for like this to get rid of the awkward /login key in your params:
<%= form_for(:login) do |f| %>
Strong parameters are really only for scenarios where you are doing mass assignment on an object. If you were creating the user, for example, then you would probably want to pass the attributes into the new initializer method using strong parameters.
#user = User.new(session_params)
But because you're not doing mass assignment in this case, you can just pass in the values directly without a session_params method:
# POST /login
def create
#user = User.find_by(email: params[:login][:username_or_email]) || User.find_by(username: params[:login][:username_or_email])
if #user && #user.authenticate(params[:login][:password])
session[:current_user_id] = #user.id
flash[:notice] = 'You are logged in'
else
flash[:notice] = 'Invalid password, username or email'
end
end
The whole point of strong parameters is so no one can pass in extra attributes. In your /login scenario, your code is completely in control of the values being handled, so you don't need to worry about it.
Form_For
When you use form_for, Rails expects an object to be passed so it can build a variety of different elements from it:
[The form_for] helper is designed to make working with resources much easier
compared to using vanilla HTML.
The problem is you're passing a route to this method, which I'm surprised actually works.
--
form_tag
You'll be better using a symbol, as recommended by the accepted answer, or by using form_tag, which doesn't require an object:
<%= form_tag login_path do %>
<%= text_field_tag :username_or_email %>
<%= password_field_tag :password %>
<%= submit_button_tag "Go" %>
<% end %>
This will remove the references to the "login" key of your params, and will give you the ability to do this (no need for require):
params.permit(:username_or_email, :password)
Instead of
<%= form_for(login_path) do |f| %>
use
<%= form_for(:login) do |f| %>
as one of you said in my previous question, i attempted to make a post request.
Here is my login view
<%= form_for :check_validity, :method => "post", :url => {:action => "check_validity"} do |f| %>
<%= f.label :username %>
<%= f.text_field :username %><br />
<%= f.label :password %>
<%= f.password_field (:password)%><br />
<%= f.submit "Submit"%>
<% end%>
Since the check_validity is a post method, i wrote the below code in my post method
def check_validity
#USER = params[:display_command_list][:username]
#PASS = params[:display_command_list][:password]
#HOST = 'hostname'
Net::SSH.start( #HOST, #USER, :password => #PASS ) do|ssh|
#result = ssh.exec!("command")
end
if(#result =~ /somestring/)
redirect_to display_command_list_path({:username => #USER, :password => #PASS})
else
redirect_to denied_access_path
end
end
But when i enter username and password in login page and click on submit button, it goes to this post method and gives me "Missing template authorization/check_validity" error. I included post "authorization/check_validity" in my routes.rb. It seems that i'm missing something very basic here :(. Please let me know why is it not able to redirect to the paths i mentioned instead of looking for check_validity.html.erb
I remember when we scaffold, and click on new post and submit it, it redirects to create method which is a post request and then automatically redirects to show method which is 'get'. How is it able to understand that it should go to 'show' after create? I tried to replicate that here but failed. Please help.
Update: I have seen here and understood that we should not use redirect in a POST method. But how would i use this information and go to denied_acess_path or display_command_list_path from this check_validity method
i think i solved the problem.
In the login method i used the following code
def login
respond_to do |format|
format.html
end
end
When i removed the respond_to block, i was able to execute the code. But i still do not know why this is happening. Is the respond to tag looking for check_validity.html.erb instead of login.html.erb?
I decided to start a little project in rails 3 and I am a little bit stuck on a form... Where can I specified the f.submit action should go to a special controller / action ?
The code in the form is:
<%= form_for #user, :url => { :action => "login" } do |f| %>
<div class="field">
<%= f.text_field :email %><br />
<%= f.text_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
User is defined as #user = User.new in "index" method of "home_controller".
but I have the error:
No route matches {:controller=>"home", :action=>"login"}
as soon as I run http://0.0.0.0:3000
I am very sorry for this newbee question but I cannot find the routing details (I worked a little bit with rails a couple of years ago but...)
Thanks,
Luc
You don't need to specify any action for f.sumbit.
First of all, you need to make sure you put
resources :users
(for example)
in your routes.rb
then if you want to create a user
put
def new
#user = User.new
end
in your users_controller so you have a page to create new user
or you can put #user=User.new anywhere you like, remember to set
the route correctly
then
def create
#user = User.new(params[:id])
if #user.save
sign_in #user
redirect_to #user
else
render 'new'
end
end
is the part that does real work after you hit on submit
the actual part that connect your form with everything else is this line
<% form_for #user do |f| %>
you can change user to other object, and you can also edit form using update action in a controller.
Hope you got the idea
Whenever you use REST objects, the mere:
form_for #article
is enough for the form to find the proper path.
Otherwise, you can use helpers this way:
form_tag(:controller => "people", :action => "search", :method => "get", :class => "nifty_form")
More info here: http://edgeguides.rubyonrails.org/form_helpers.html