I'm trying to set up a simple Roles model for my site. Users can have any number of roles. Right now I'm setting it up to be a hidden field on the signup form. The problem I'm running into is that in my nested form the role type isn't being saved. An entry with the uid is being created in the roles table but that's about it. The relevant code is below. Any help would be appreciated.
== Schema Information
Table name: roles
id :integer not null, primary key
user_id :integer
role :string(255)
created_at :datetime not null
updated_at :datetime not null
Heres the create method from my users controller...
def create
#user = User.new(params[:user])
if #user.save
#role = #user.roles.new(user_id:#user.id, role:params[:role])
flash[:success] = "Thanks for singing up for the Auditions App, any audition invitations will be visible on this page"
#need to add the role guest to user roles
redirect_to #user
else
render 'new'
end
end
Heres the form....
<div class="span10 offset1 ajax-form">
<%= form_for(#user, :html => {:class => 'well'}) do |f| %>
<h3>Guest Account Signup</h3>
<%= render 'shared/error_messages' %>
<div class="pull-left form-field"><%= f.label :first_name %>
<%= f.text_field :first_name, :class => 'span4' %></div>
<div class="pull-left form-field"><%= f.label :last_name %>
<%= f.text_field :last_name, :class => 'span4' %></div>
<div class="pull-left form-field"><%= f.label :email %>
<%= f.text_field :email, :class => 'span4' %></div>
<div class="pull-left form-field span4"></div>
<div class='clear'></div><!--close .clear-->
<div class="pull-left form-field"><%= f.label :password %>
<%= f.password_field :password, :class => 'span4' %></div>
<div class="pull-left form-field"><%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, :class => 'span4' %></div>
<div class="form_row pull-left form-field">
</div>
<%= f.hidden_field :role, :value => 'guest' %>
<%= f.submit "Create my account", :class => "btn btn-large btn-primary pull-left form-field" %>
<div class='clear'></div><!--close .clear-->
<% end %>
As #Arpit comments - get rid of the hidden :user_id tag. It's only confounding the attribute setter.
And I don't think building multiple roles using roles.new() (is that the same as roles.build()?) is going to cut it. You'll need to break out the separate roles' parameters and add them one at a time. Or you can grab them in one go like this:
selected_roles = Role.find_all_by_id(params[:roles])
#user.roles = selected_roles
This would assume that you are using role ids in your form, but you aren't for some reason...
Related
Preamble
As a little bit of a setup to the problem, we have an authenticatable user model that is separate from the user model that contains non-authentication related data.
Problem
As the title suggests, adding custom fields to the built in configure_sign_up_params method does not actually permit the fields in the create action. I've generated the relevant controller and views with rails generate devise:controllers user and rails generate devise:views user, and made modifications to both. In the view:
<h2>Custom User Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= fields_for(:extra_user_attributes) do |pa| %>
<div class="field">
<%= pa.label :first_name %><br />
<%= pa.text_field :first_name %>
</div>
<div class="field">
<%= pa.label :last_name %><br />
<%= pa.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "user/shared/links" %>
We have a nested :extra_user_attributes field that we need permitted, so in the devise generated controller, I uncommented a couple of lines and added the relevant logic:
class CustomUser::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# POST /resource
def create
ap sign_up_params
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:extra_user_attributes => [:first_name, :last_name]])
end
end
This is where I got stuck. I've tried several permutations in the keys array, but everything I've tried thus far yields the same result. For my own sanity, I verified the before_action actually does trigger.
As for the endpoint, I'm simply printing the output to the console just to see what the permitted signup parameters are.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5MqzDUsbrdMdE0Z7/Vw70zJddaKR+0eLYdnum3wEyTShNjB9o3Nb4ZsnE0dZEPlgs4SheZtQad0VpuIGtCeSmw==", "extra_user_attributes"=>{"first_name"=>"", "last_name"=>""}, "user"=>{"email"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
{
"email" => "",
"password" => "",
"password_confirmation" => ""
}
Am I missing something here?
Workaround
A workaround would be to:
def other_signup_params
params.require(:extra_user_attributes).permit(:first_name, :last_name)
end
Which works just fine, but it just doesn't feel right. I feel like I should be able to use the built in configure_sign_up_parameters method that Devise provides but I'm somehow doing something wrong. Appreciate the help in advance.
The problem was not with devise, but a problem with the nested parameters I set up.
<%= fields_for(:extra_user_attributes) do |pa| %>
<div class="field">
<%= pa.label :first_name %><br />
<%= pa.text_field :first_name %>
</div>
<div class="field">
<%= pa.label :last_name %><br />
<%= pa.text_field :last_name %>
</div>
<% end %>
The fields_for did not get associated with the parent form, and got orphaned. It should have read:
<%= f.fields_for(:extra_user_attributes) do |pa| %>
Then, the sanitizer properly permits the fields:
{
"email" => "foo#example.com",
"password" => "hello!",
"password_confirmation" => "world!",
"extra_user_attributes" => {
"first_name" => "Foo",
"last_name" => "Bar"
}
}
I'm trying to put Forgot password field with the sign in page, but if user is not registered (and not in apps database), then it redirects to the original devise Forgot password page with errors (http://localhost:3000/users/password). How do I make the errors appear in the same page as the sign in page (http://localhost:3000/users/sign_in)?
In app/views/devise/sessions/new.html.erb file
<%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= f.email_field :email, required: false, autofocus: true, placeholder: "Username" %>
<%= f.password_field :password, required: false, placeholder: "Password" %>
<%= f.button :submit, "Sign In", class: "btn btn-success btn-sm" %>
<div class="remember-forgot">
<div class="row">
<div class="col-md-6">
<%= f.input :remember_me, as: :boolean if devise_mapping.rememberable? %>
</div>
<div class="col-md-6 forgot-pass-content">
Forgot Password
</div>
</div>
</div>
<% end %>
<!-- where reset is -->
<div class="pass-reset">
<%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), namespace: 'forgot', html: { method: :post }) do |f| %>
<%= f.error_notification %>
<label>Enter the email you signed up with</label>
<%= f.email_field :email, required: true, autofocus: true, placeholder: "Email" %>
<%= f.submit "Submit", class: "pass-reset-submit btn btn-success btn-sm" %>
<% end %>
</div>
So there's a javascript link where an input field will show up if a user forgets their sign in credentials.
apparently two forms for same object having same fields should not be one page, one should stay with the new page. but still i have tried your question, and following things needs to be done
1) I have to override the passwords controller for devise under user scope.
class Users::PasswordsController < Devise::PasswordsController
# GET /resource/password/new
def new
super
end
# POST /resource/password
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
flash[:notice] = "sent password"
redirect_to :root
else
render "devise/sessions/new"
end
end
# GET /resource/password/edit?reset_password_token=abcdef
def edit
super
end
# PUT /resource/password
def update
super
end
protected
def after_resetting_password_path_for(resource)
super(resource)
end
# The path used after sending reset password instructions
def after_sending_reset_password_instructions_path_for(resource_name)
super(resource_name)
end
end
then my devise/sessions/new page will look like this(you can add the logic of showing form only when one clicks he forget password button. that should be simple. just add hide class and on click remove hide class.)
Log in
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
#assuming that devise_mapping has recoverable? option. you can also keep the below form in if condition
<h2>Forgot your password?</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), namespace: "forget", html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="actions">
<%= f.submit "Send me reset password instructions" %>
</div>
<% end %>
need to tell the routes to use my my passwords controller.
devise_for :users, controllers: {
passwords: 'users/passwords'
}
These things will result in showing the errors under the user sign in form but the path will remain http://localhost:3000/users/password. why because we are rendering the page and not redirecting. render just show the views without going to the controller action. now even if one tries to send the errors messages to the sessions controller somehow(after overriding that controller as well) somehow like this
redirect_to new_user_session_path, :messages => resource.errors
still that wont help, why because in session#new we are re initializing the resource as it is new action and all the errors would be gone.
I'm not sure if this is satisfactory to you or if this is not even close to your requirements. i tried to cover all things. i would be glad if some credible or official sources will provide even better response. that would definitely increase my knowledge as well.
I'm making a Rails app and am trying to have the user's role displayed on a view. The code I am using for this is:
<%= current_user.role %>
Which does not evaluate to anything.
I am using devise for user registrations and my registration view is as follows:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="login-field">
<%= f.email_field :email, :placeholder => "Email" %>
</div>
<div class="login-field">
<%= f.password_field :password, autocomplete: "off", :placeholder => "Password (8 character min)" %>
</div>
<div class="login-field">
<%= f.text_field :firstname, :placeholder => "First Name" %>
</div>
<div class="login-field">
<%= f.text_field :lastname, :placeholder => "Last Name" %>
</div>
<div class="login-field">
<%= f.text_field :phone, :placeholder => "Phone Number" %>
</div>
<%= f.select(:role, ['Role 1', 'Role 2', 'Both']) %>
<br>
<div><%= f.submit "Signup" , :class => "btn btn-success btn-lg" %></div>
<% end %>
<%= render "devise/shared/links" %>
I have a feeling that I am doing something wrong in the form submission but cannot figure out what it is. Any help is appreciated
You can put this at the top of your create action:
return render text: params.inspect # get what the form sended
and or
return render text: create_params # get what the values you permitted
This way you will get what the data your from sended and if it is what you expected.
You should override the sign_up_params in the Devise registration. Once you've created your RegistrationsController that inherits from Devise::RegistrationsController, you can override the sign_up_params:
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :email, :phone, :role ... any other attributes)
end
end
You would also need to change you routes.rb to point to the newly created controller:
devise_for :users, controllers: {registrations: 'registrations'}
I am creating an application through which a user will be able to create an account. When they create an account, in the same form they will be able to create an organization that will then be tied to their user. Once that user has created their account (and an organization) other users will be able to create an account and use an "access code" to join that organization as well. Looking at the code may explain it better.
The reason i'm posting on SO is because i have a feeling there is a better / more efficient way to do it than what i am currently doing. I'm using nested_forms (maybe not correctly) and i don't think i'm doing the associations the right way because, for example, i haven't been able to get the edit form to fill out the organization fields.
I am using sorcery for the authentication as well.
users_controller.rb
def new
#user = User.new
end
def create
#user = User.new(user_params)
if params[:user][:organization][:name].blank?
flash.now[:error] = "You must specify an organization name."
render :new
else
if params[:user][:organization][:access_code].blank?
# create new organization
#access_code = "#{SecureRandom.urlsafe_base64(16)}#{Time.now.to_i}"
#organization = Organization.create(:name => params[:user][:organization][:name], :access_code => #access_code)
#user.organization_id = #organization.id
#user.is_admin = true
else
# try and add someone to an organization
#organization = Organization.find(:all, conditions: ["name = ? AND access_code = ?", params[:user][:organization][:name], params[:user][:organization][:access_code]])
if #organization.empty?
flash.now[:error] = "No organization has been found with that name and access code."
render :new
return
else
#user.organization_id = #organization.first.id
end
end
if #user.save
user = login(#user.email, params[:user][:password])
if user
flash[:success] = "Your account has been successfully created!"
redirect_to admin_dashboard_path
end
else
flash.now[:error] = "Something went wrong! Please try again."
render :new
end
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.is_admin?
if params[:user][:organization][:name].blank? && params[:user][:organization][:name] != #user.organization.name
params[:user][:organization][:name] = #user.organization.name
end
if params[:user][:organization][:access_code].blank? && params[:user][:organization][:access_code] != #user.organization.access_code
params[:user][:organization][:access_code] = #user.organization.access_code
end
#organization = Organization.find(params[:user][:organization_id])
#organization.name = params[:user][:organization][:name]
#organization.access_code = params[:user][:organization][:access_code]
#organization.save
end
if #user.update(user_params)
flash[:success] = "Your settings have been updated!"
redirect_to edit_admin_user_path(#user.id)
else
flash.now[:error] = "Something went wrong! Please try again."
render :edit
end
end
private
def user_params
params.require(:user).permit(:organization_id, :email, :password, :password_confirmation, :full_name, :remember_me, {:organization_attributes => [:name, :website, :description, :access_code]})
end
users.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
belongs_to :organization
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :full_name
validates_presence_of :email
validates_uniqueness_of :email, :on => :create
validates_format_of :email, :with => VALID_EMAIL_REGEX, :on => :create
validates_presence_of :password, :on => :create
validates_confirmation_of :password
end
organization.rb
class Organization < ActiveRecord::Base
authenticates_with_sorcery!
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users
validates_presence_of :name
end
new.html.erb
<% provide(:title, 'Create a User') %>
<h1>Create a User</h1>
<p>Use the form below to create an account.</p>
<%= nested_form_for([:admin, #user], html: {role: "form"}) do |f| %>
<%= render "shared/error_messages", obj: #user %>
<fieldset>
<legend>User Information</legend>
<div class="form-group">
<%= f.label :full_name, "Full Name" %>
<span class="help-block">How should others see you?</span>
<%= f.text_field :full_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :email %>
<span class="help-block">Your email address is used as your login.</span>
<%= f.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</fieldset>
<%= f.fields_for :organization do |o| %>
<fieldset>
<legend>Associated Organization</legend>
<div class="form-group">
<%= o.label :name, "Organization Name" %>
<span class="help-block">This is the name of the organization you are a part of.</span>
<%= o.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= o.label :access_code, "Organization Access Code" %>
<span class="help-block">Leaving this field blank will setup a new organization.</span>
<%= o.text_field :access_code, class: "form-control" %>
</div>
</fieldset>
<% end %>
<div class="form-actions">
<%= f.submit "Create Account", class: "btn btn-primary" %>
<%= link_to "Cancel", :back, class: "text-btn" %>
</div>
<% end %>
edit.html.erb
<% provide(:title, "Edit User: #{#user.full_name} (#{#user.organization.name})") %>
<h1>Edit User: <%= #user.full_name %> (<%= #user.organization.name %>)</h1>
<p>Use the form below to manage your account.</p>
<%= nested_form_for([:admin, #user], html: {role: "form"}) do |f| %>
<%= render "shared/error_messages", obj: #user %>
<fieldset>
<legend>User Information</legend>
<div class="form-group">
<%= f.label :full_name, "Full Name" %>
<span class="help-block">How should others see you?</span>
<%= f.text_field :full_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :email %>
<span class="help-block">Your email address is used as your login.</span>
<%= f.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, placeholder: "leave blank to keep password unchanged", class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</fieldset>
<% if #user.is_admin? %>
<%= f.fields_for :organization do |o| %>
<fieldset>
<legend>Associated Organization</legend>
<div class="form-group">
<%= o.label :name, "Organization Name" %>
<span class="help-block">This is the name of the organization you are a part of.</span>
<%= o.text_field :name, class: "form-control", value: #user.organization.name %>
</div>
<div class="form-group">
<%= o.label :access_code, "Organization Access Code" %>
<span class="help-block">Leaving this field blank will setup a new organization.</span>
<%= o.text_field :access_code, class: "form-control", value: #user.organization.access_code %>
</div>
</fieldset>
<% end %>
<%= f.hidden_field :organization_id %>
<% end %>
<div class="form-actions">
<%= f.submit "Update User", class: "btn btn-primary" %>
<%= link_to "Cancel", :back, class: "text-btn" %>
</div>
<% end %>
Ok, those are all the files making it happen. Now, i have the application doing almost everything i need it to do but this doesn't feel like production-level code to me.
One issue i know i am having is that if a user types something in the organization field and nothing else the controller will create and save the organization and then render the form back with the user validation errors. I don't want it to save the organization if there are validation errors in the user model.
I'm really just asking for advice if there is a better way of doing what i am trying to do. If you can't tell exactly what i'm trying to do with this code or have any questions please let me know!
Take a look at this post: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Of particular interest will be the section on "3. Extract Form Objects".
I have a rails app using devise for registrations and rolify for roles. I would like to have an index page that has edit links for each of the users that can be accessed by an admin. This edit page should also work without having to use a password. Right now the edit_user_path goes to the edit page of the current user, which is not what i want.
What is the best way to implement this sort of sitation? i've read a few of the posts on here about this but none seem to give me what i want.
Please point me in the right direction!
EDITED
I'm attempting to do it this way, still running into "Current password can't be blank"
From Users_controller:
def update
#user = User.find(params[:id])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
if #user.update_attributes(user_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
And in my views i have an edit.html.erb file that is rendering the following form:
<div class="panel-body">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: "form-control", :autofocus => true %>
</div>
<div class="form-group">
<%= f.label :username %>
<%= f.text_field :username, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :firstname %>
<%= f.text_field :firstname, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :lastname %>
<%= f.text_field :lastname, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :city %>
<%= f.text_field :city, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :zip %>
<%= f.text_field :zip, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :state %>
<%= f.text_field :state, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :country %>
<%= f.text_field :country, class: "form-control" %>
</div>
<div class="form-group">
<%= f.submit "Update", class: "btn btn-primary" %>
</div>
<% end %>
</div>
and finally in my routes.rb file i have this line to render the edit page. I can get the edit page to show up but entering info and then hitting update just shoots me to /users with the error "Current Password can't be blank"
get 'pressroom/accounts/:id/edit' => 'users#edit', :as => :admin_edit_user
Devise doesn't come with any sort of Admin interface. If you are the only administrator and don't mind a little crudeness - there is always the console and/or scaffolding. You could create a UserController which inherits from ApplicationController and execute basic view, edit methods in the same controller. By placing the appropriate new.html.erb, edit.html.erb etc files in the User Views folder, adding/editing/deleting Users should work no differently as any other CRUD, as Devise's User is another model like any. Use a scaffold on the user and you could get what you are looking for.
There are also a lot of good gems that make setting up admin interfaces a cinch: https://github.com/gregbell/active_admin Active Admin, https://github.com/sferik/rails_admin Rails Admin and I'm sure there are a bunch more out there.
It looks like i got it working by adding:
<div class="panel-body">
<% #user = User.find(params[:id]) %>
<%= form_for(#user) do |f| %>
to the top of my _form.html.erb file
Thanks for the help everyone!
If the only thing you need is for the admin to EDIT an existing user, you can have the edit, show and update actions in a separate UsersController (and leave new and create actions up to devise). That way you can move that #user = User.find(params[:id] logic out of your form, into the controller, as #Saurabh Lodha mentioned.
I just thought one thing was missing from the answers though: Make sure to also edit your routes.rb. Use a path prefix so your routing doesn't get confusing, kind of like this:
devise_for :users, :path_prefix => 'my'
resources :users
this means that when you call edit on a current_user, it will go to my/users/edit, and when you call edit on any selected user from your user list in the admin panel, it will take you to users/user_id/edit.
I hope that clarified it a bit more! good luck! :)