This has been asked before to some degree but I couldn't find an actual answer: How can I allow my users to log in via the same form they signup? This is a client request/demand by the way, I realise this is far from ideal...
User
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
render :json => #user.to_json(:only => [:id])
else
render :json => #user.errors, :status => ""
end
end
end
Session
class SessionsController < ApplicationController
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:email])
sign_in user
render :json => #user.to_json(:only => [:id])
else
render :json => #user.errors, :status => ""
end
end
end
Form
<%= form_for(#user, :html => { :class => "access"}) do |f| %>
<%= f.label :firstname, "Firstname" %>
<%= f.text_field :firstname, :placeholder => "Your firstname" %>
<%= f.label :surname, "Surname" %>
<%= f.text_field :surname, :placeholder => "Your surname" %>
<%= f.label :email, "Email Address" %>
<%= f.text_field :email, :placeholder => "Your email address" %>
<%= f.submit "Verify" %>
<% end %>
Emails are validated as unique, could that be an entry point? Eg. If the email is not unique check the name and email or something.
I have no idea how to go about this.
My suggestion is to make another controller which is something like AccessController. It should use a name that means both login and register, Access was just all I could think of. That controller should be able to do a check on the login credentials and either login or register.
Related
I've created a sign up form on rails, and upon clicking my submit button to save my user details, I get the following error:
ActiveModel::ForbiddenAttributesError
for this line: #user = User.new(params[:user])in my users controller.
See code below:
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
new.html.erb
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
</br> <%= f.text_field :firstname, placeholder: 'First Name' %>
</br> <%= f.text_field :lastname, placeholder: 'Last Name' %>
</br> <%= f.text_field :email, placeholder: 'Email' %>
</br> <%= f.password_field :password, placeholder: 'Password' %>
</br> <%= f.password_field :password_confirmation, placeholder: 'Confirm Password' %>
<%= f.submit :Register %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
routes.rb
Rails.application.routes.draw do
get 'users/new'
get 'pages/home'
get 'pages/howitworks'
get 'pages/about'
get 'pages/contact'
get 'pages/becomeauser'
get 'signup' => 'users#new'
resources :users
The params[:user] hash might have attributes in it that isn't a part of the User model. In your case, i'm guessing its password_confirmation.
Use strong params the next time - here
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
private:
def user_params
params.require(:user).permit(:first_name, :last_name, :email, : password)
end
This means that you have Strong Parameters enabled. I would encourage you to read at least a part of this Rails Guide. Essentially, instead of using params[] to update or create records, you should use an intermediate method to ensure that users aren't sneaking information into your requests. This is much safer. For instance, someone could submit a "created_at" attribute with your form, and then be modifying fields which are not supposed to be edited. Your approach should instead be as follows:
Create a private user_params method in the controller which permits specified attributes
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
Replace your User.create(), User.new(), and User.update() references to params[:user] with user_params
# Do this instead of #user = User.new(params[:user])
#user = User.new(user_params)
I am stuck with this issue, and i am not able to identify the problem. I am trying to add a user from admin section. Hence I have created a custom user controller, which calls the devise model. And I am trying to insert and update values using custom forms.
Controller
class Admin::UsersController < ApplicationController
load_and_authorize_resource
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user!
layout 'admin-layout'
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit({ roles: [] }, :email, :password, :password_confirmation) }
end
def index
#user = User.all
end
def new
#user = User.new
end
def create
#user = User.new
if #user.save
redirect_to :action => "index"
else
render :action => "new"
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes
redirect_to :action => "index"
else
render :Action => "update"
end
end
def destroy
User.find(params[:id]).destroy
redirect_to ::Action => "index"
end
end
View File
<div class="container">
<div class="col-md-6 col-md-offset-2">
<h2>Create New User</h2>
<%= form_for #user, url: {action: "create"} do |f| %>
<%= f.text_field :email, :class=>"form-control", :placeholder => "Enter Email", :required => "true" %>
<%= f.text_field :password, :class=>"form-control", :placeholder => "Enter Password", :required => "true" %>
<%= f.text_field :password_confirmation, :class=>"form-control", :placeholder => "Enter Password Again", :required => "true" %>
<p>Roles:</p>
<% for r in Role.all %>
<%= check_box_tag "user[role_ids][]", r.id %>
<%= r.name %>
<% end %><br/>
<%= f.submit "Create", :class=>"btn btn-primary" %>
<% end %>
</div>
<div>
Routes
admin_users GET /admin/users(.:format) admin/users#index
POST /admin/users(.:format) admin/users#create
Can any1 please point me out where am I making a mistake.
You defined new, create etc. methods as protected, they need to be public if you want them to be recognized as actions.
BTW, in Ruby, if you have collection Role.all, it's much more readable to iterate over this with each instead of for (which is rarely used here):
<% Role.all.each do |r| %>
Bassicaly my problem what to do if i have 3 forms and one submit button.
I want to create a form which sends email to each recipient and then create new record in free_registration_coupons table.
I need validation of email for this form.
Model FreeRegistrationCoupon: recipient_email, token, sender_id
For now i have this:
class FreeRegistrationCouponsController < ApplicationController
def send_invitations
emails = [params[:recipient_email_1], params[:recipient_email_2], params[:recipient_email_3]]
emails.reject!{ |e| e.eql?("") }
if emails.present?
emails.each do |e|
FreeRegistrationCoupon.create(:recipient_email => e, :sender_id => current_user.id)
#MAILER
end
redirect_to root_path, :notice => "You just send #{emails.size} invitations!"
else
redirect_to(:back)
end
end
end
class FreeRegistrationCoupon < ActiveRecord::Base
before_save :generate_token
attr_accessor :recipient_email, :sender_id
validates :recipient_email, :presence => true, :email => true
def generate_token
self.token = SecureRandom.hex
end
end
This is form which is in other controller CarsController#confirm:
<%= form_tag :controller => 'free_registration_coupons', :action => "send_invitations" do %>
<!-- errors -->
<%= label_tag :recipient_email_1 %>
<%= text_field_tag :recipient_email_1 %>
<%= label_tag :recipient_email_2 %>
<%= text_field_tag :recipient_email_2 %>
<%= label_tag :recipient_email_3 %>
<%= text_field_tag :recipient_email_3 %>
<%= submit_tag %>
<% end %>
I think you should have defined your form using:
<%= form_tag :controller => 'free_registration_coupons', :action => "send_invitations" do %>
<%= #error_message %>
<%= label_tag "recipient_email[1]" %>
<%= text_field_tag "recipient_email[1]" %>
<%= label_tag "recipient_email[2]" %>
<%= text_field_tag "recipient_email[2]" %>
<%= label_tag "recipient_email[3]" %>
<%= text_field_tag "recipient_email[3]" %>
<%= submit_tag %>
<% end %>
This way it will be easier to treat all email address on your controller and you can track those errors to display them afterwards:
class FreeRegistrationCouponsController < ApplicationController
def send_invitations
emails = params[:recipient_email]
emails.reject!{ |param, value| value.eql?("") }
errors = []
if emails.any?
emails.each do |param, value|
validation_result = FreeRegistrationCoupon.save(:recipient_email => value, :sender_id => current_user.id)
#MAILER
end
redirect_to root_path, :notice => "You just send #{emails.size} invitations!"
else
#error_message = "You have to include, at least, one e-mail address!"
render :name_of_the_action_that_called_send_invitations
end
end
end
I didnt test this code. Hope it helps!
I'm working on an Ruby on Rails application (2.3.x) and i want to make a form that lets the user login or register. I want to do this in the same form. I have a JS function that replaces the form elements like this:
Login form:
<% form_for #user do |f| %>
<div id="form">
<%= f.label :email, "E-mail" %>
<%= f.text_field :email %>
<%= f.label :password, "Password" %>
<%= f.password_field :password %>
<%= link_to "I don't have an account, "#", :id => "changeForm"%>
<%= f.submit "Login" %>
</div>
<% end %>
The id "changeForm" triggers a JS function that changes the form elements. So if you press the url the html looks like this:
<% form_for #user do |f| %>
<div id="form">
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :email, "E-mail" %>
<%= f.text_field :email %>
<%= f.label :password, "Password" %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Password confirmation" %>
<%= f.password_field :password_confirmation %>
<%= link_to "I do have an account, "#", :id => "changeForm"%>
<%= f.submit "Register" %>
</div>
<% end %>
I added the neccesary validations to my user model:
class User < ActiveRecord::Base
attr_reader :password
validates_presence_of :name, :email, :password
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates_confirmation_of :password
But what happens when you fill in the email / password you get the errors that the name is missing and that the password fields aren't confirmed. So i could do some nasty programming in my user model like this:
#if password_conf or the name are present the user has tried to register...
if params[:user][:password_confirmation].present? || params[:user][:name].present?
#so we'll try to save the user
if #user.save
#if the user is saved authenticate the user
current_session.user = User.authenticate(params[:user])
#if the user is logged in?
if current_session.user.present?
flash[:notice] = "succesvully logged
redirect_to some_routes_path
else
#not logged in...
flash[:notice] = "Not logged in"
render :action => "new"
end
else
#user not saved
render :action => "new"
end
else
#So if the params[:user][:password_confirmation] or [:user][:name] weren't present we geuss the user wants to login...
current_session.user = User.authenticate(params[:user])
#are we logged_in?
if current_session.user.present?
flash[:notice] = "Succesvully logged in"
redirect_to some_routes_path
else
#errors toevoegen
#user.errors.add(:email, "The combination of email/password isn't valid")
#user.errors.add(:password," ")
render :action => "new"
end
end
end
Without validations this (imho dirty code and should not be in the controller) works. But i want to use the validates_presence_of methods and i don't want to slap the "conventions over configurations" in the face.
So another thing i have tried is adding a hidden field to the form:
#login form
<%= f.hidden_field :login, :value => true %>
# and ofcourse set it to false if we want to register.
And then i wanted to use the method: before_validation
before_validation_on_create do |user|
if params[:user].login == true #throws an error i know...
validates_presence_of :email, :password
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
else
validates_presence_of :name, :email, :password
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates_confirmation_of :password
end
end
But this doesn't work because i can't access the params. And login isn't a attribute for the user object. But i thought that in this way i could validate the email and password params if the user wants to login. And all the other attrs if the user want to register.
So all i could think of doesn't work how i want it to work. So my main goal is this:
1 form for login/register with the use of the validation methods in the user model. So if we want to login but don't fill in any information => give validation errors. And if the user wants to login but the email/password combination doens't match give the "#user.errors.add(:email, "the combination wasn't found in the db...")". And the same goes for user register...
Thanks in advance!
You should point each form to the appropriate controller action:
# Registration form
<% form_for #user, :url => users_path do |f| %>
...
<% end %>
# Login form, non-RESTful action
<% form_for #user, :url => login_users_path do |f| %>
...
<% end %>
This assumes you have a login action in your UsersController, but if you'd like to have a dedicated controller manage your sessions:
# Use SessionsController (Sessions#Create) to create a new user session
<% form_for #user, :url => sessions_path do |f| %>
...
<% end %>
Ok so I am having a problem with doing multiple forms in rails. here is the code below
Models
class Profile < ActiveRecord::Base
belongs_to :user
has_attached_file :avatar, :styles => { :medium => "134x137>", :thumb => "111x111>", :tiny => "32x38>" }
validates_attachment_content_type :avatar, :content_type => ['image/pjpeg','image/jpeg', 'image/x-png', 'image/png', 'image/gif']
class User < ActiveRecord::Base
has_one :profile, :dependent => :destroy
Profile Controller
def edit
#user = User.find(params[:id])
#profile = #user.profile
end
Profiles Edit View
<% form_for #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<input id="send_update" name="send" type="submit" value="Update" />
<% end %>
<% form_for #profile , :html => { :multipart => true } do |f| %>
<%= render :partial => 'form', :locals => {:f => f, :profile => #profile, :title => 'Edit Profile'} %>
<%= submit_tag 'Update', :style => 'text_align:right'%>
<% end %>
Profile _form partial
<label>Upload Avatar</label>
<tr><%= f.file_field :avatar %></tr>
So basically I have two forms in the edit view and when i click on the second Update to update the avatar I go to the users update and i get this flash error "Sorry, something went wrong"
def update
#user = User.find(params[:id])
current_email = #user.email
if #user.update_attributes(params[:user])
UserMailer.deliver_email_changed (#user) if email_changed?(current_email, #user.email)
flash[:notice] = "<h1>Account updated!</h1>"
redirect_to edit_user_path(#user)
else
flash.now[:error] = "Sorry, something went wrong"
render :action => :edit
end
end
My questions are this
Is there a better way to structure this so maybe i have one form?
Why is it not saving now and whats causing the issue?
The method you are using to update is wrong, it is syntactically invalid (you're missing an end). It should be:
def update
#user = User.find(params[:id])
current_email = #user.email
if #user.update_attributes(params[:user])
UserMailer.deliver_email_changed (#user) if email_changed?(current_email, #user.email)
flash[:notice] = "<h1>Account updated!</h1>"
redirect_to edit_user_path(#user)
else
flash.now[:error] = "Sorry, something went wrong"
render :action => :edit
end
end
It should be two forms indeed, as I'm guessing you don't want any of the values of one form to submit if the user performs the other action.
Now, organize your controllers. You are calling #user = User.find(params[:id]) on your ProfilesController, but the id you're passing is an user's one. Either this should be on the user's controller, and update the associated profile from there, or you should receive the id of the profile object instead.
I'd go with the first one. You can update the Profile object of a user using accepts_nested_attributes_for, and your forms would be like:
<% form_for #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<%= f.submit, :id => ... %>
<% end %>
<% form_for #user, :html => { :multipart => true } do |f| %>
<% f.fields_for :profile do |profile_form| %>
<%= render :partial => 'form', :locals => {:f => profile_form, :title => 'Edit Profile'} %>
<%= submit_tag 'Update', :style => 'text_align:right'%>
<% end %>
<% end %
If the error is that the password cannot be blank, may be due to a validates_presence_of :password, :password_confirmation. You should use a conditional validation there
Troubleshooting would help:
Remove the if/else and keep the #user.update_attributes(params[:user]). Rails would give you a more detailed error message.
Check the form structure (html source) especially for field naming.
Check the log files for DB statements
HTH