Update information of different user with extension of Devise - ruby-on-rails

I setup a platform that uses Devise to create and manage users and CanCanCan to manage permissions. Users have a role (admin or client) that allows them access to certain areas. Admin users have the ability to can :manage, :all
I extended Devise so that I have a users/registrations and users/sessions controller.
Admin users have access to views in a ManagerController dashboard that allows them to manage other users. I've figured out how to show the details of a different user, but I can't figure out how to save the information. I get errors that indicate it either can't find the User or that it requires a POST route.
I'm fairly new to Rails, so it could be something simple. I was able to find solutions that included the regular Devise installation, but not the extension. I feel like the problem may be in my routes.
I also can't create new users from here either.
Routes.rb
get 'users' => 'manager#users'
get 'users/edit' => 'manager#edit'
post 'users/edit' => 'manager#edit'
get 'users/new' => 'manager#new'
devise_for :users, :controllers => { registrations: 'users/registrations', sessions: 'users/sessions' }
devise_scope :user do
authenticated :user do
root 'users/registrations#edit', as: :authenticated_root
end
unauthenticated do
root 'users/sessions#new', as: :unauthenticated_root
end
end
Manager Controller
class ManagerController < ApplicationController
authorize_resource :class => false
include ManagerHelper
def users
#users = User.where.not(:id => current_user.id)
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def edit
#user = User.find(params[:id])
#companies = Company.all
end
def update
#user = User.find(params[:id])
params[:user][:id].delete(:password) if params[:user][:password].blank?
params[:user][:id].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update(user_params)
flash[:notice] = "Successfully updated User."
redirect_to root_path
else
render :action => 'edit'
end
end
end
private
def user_params
params.require(:user).permit(:email, :first_name, :last_name, :company_id, :role, :password, :password_confirmation)
end
end
ManagerHelper
module ManagerHelper
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
Manager/Edit.html.erb
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, :as => #user, :url => edit_user_registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= f.hidden_field :id %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :first_name %><br />
<%= f.text_field :first_name%>
</div>
<div class="field">
<%= f.label :last_name %><br />
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :company %><br />
<%= f.collection_select :company_id, #companies, :id, :name, prompt: true %>
</div>
<div class="field">
<%= f.label :role %><br />
<%= f.text_field :role %>
</div>
<div class="field">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "new-password" %>
<% if #minimum_password_length %>
<br />
<em><%= #minimum_password_length %> characters minimum</em>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
I would like the updated information to be saved to the User database.
Currently, I get this error when I click update after changing the user info.
ActiveRecord::RecordNotFound in ManagerController#edit
Couldn't find User without an ID
Weirdly, when I see the User info, the url is: http://localhost:3000/users/edit?id=2
But when I update (and get the error), the url changes to: http://localhost:3000/users/edit.user
I would also like to be able to create New Users from this screen.
Currently, the user form populates with the information of the user who is logged in. (To be fair, I have been trying to get the edit user correct before tackling this issue.)

I think You want to update existing user info.
If that is the case:
I find your url path is wrong
:url => edit_user_registration_path(resource_name)
you donot have to user registration here .
If the admin going to update info
in routes file . some thing like this
namespace :admins do
resources :users
end
then use the path creatd from this

Many tries later, I've found a solution. There were a lot of errors, so bear with me.
Routes.rb -- Was using the wrong URL in the form submission, so needed to add the routes for the Update function in the Manager controller.
get 'users/edit' => 'manager#edit'
post 'users/edit' => 'manager#edit'
get 'users/update' => 'manager#update'
post 'users/update' => 'manager#update'
Manager Controller -- I had to change the password rules so that I could update without a password. Similar concept, slightly different execution.
def update
#user = User.find(params[:user][:id])
#companies = Company.all
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
if #user.update(user_params)
flash[:notice] = "Successfully updated User."
redirect_to root_path
else
render :action => 'edit'
end
end
Rendered Form.html.erb - Updated with newly created URL path.
<%= form_for(resource, as: #user, url: users_update_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>

Related

Getting First argument in form cannot contain nil or be empty while trying to allow users to edit their password

I am trying to add a solution to allow users to change their password. I am using devise. And I followed up the third solution from this tutorial.
However after I do everything:
class UsersController < ApplicationController
before_action :authenticate_user!
def edit
#user = current_user
end
def update_password
#user = current_user
if #user.update_with_password(user_params)
# Sign in the user by passing validation in case their password changed
bypass_sign_in(#user)
redirect_to root_path
else
render "edit"
end
end
private
def user_params
# NOTE: Using `strong_parameters` gem
params.require(:user).permit(:password, :password_confirmation, :current_password)
end
end
in routes.rb
resource :user, only: [:edit] do
collection do
patch 'update_password'
end
end
and in the view:
<%= form_for(#user, :url => { :action => "update_password" } ) do |f| %>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
</div>
<div class="field">
<%= f.label :password, "Password" %><br />
<%= f.password_field :password, :autocomplete => "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="action_container">
<%= f.submit %>
</div>
<% end %>
but I get this error First argument in form cannot contain nil or be empty in
= form_for(#user, :url => { :action => 'update_password' } ) do |f|
is there something I have missed? Thanks!

Issues with redirecting after signup with Devise. What to do?

I added roles to my users and after sign up devise automatically send me back to the homepage. I tried many things from this site and past questions but nothing seems to work. Maybe I'm over thinking it, I'm new here. Essentially I want people who sign up to choose their role, if they're a teacher they go to create a new class ('classrooms#new') and students go to chose their class ('classrooms#index'). I've created a separate registrations controller as suggested and that hasn't worked.
Sign up form (new.html.erb)
<div class="authform">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<h2>Sign Up</h2>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_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 %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div>
<%= f.radio_button :role, 'student' %>
<%= label :role_student, 'Student' %>
<%= f.radio_button :role, 'teacher' %>
<%= label :role_teacher, 'teacher' %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %>
</div>
Here is my registrations controller:
class Devise::RegistrationsController < DeviseController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
end
Here is my users controller
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
before_filter :check_role
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
end
def check_role
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def create_user_type
end
private
def secure_params
params.require(:user).permit(:role)
end
end
Edit: Here is my routes page
Rails.application.routes.draw do
get 'students/new'
get 'classrooms/new'
get 'teachers/new'
devise_for :users
resources :users
get "edit_profile" => "devise/registrations#edit"
resources :posts
resources :teachers
post 'users/create_user_type' => 'users/create_user_type'
root to: 'visitors#index'
end
I know there is repetitive code with checking roles, I've just been merging answers together (which isn't good I know)
use pry gem with binding.pry to check your code & variable content.
Anyway, I suppose that role is an attribute of user so why aren't you checking
user.teacher?
user.student?
instead of
params['']['']
resource attribute is persistent instead of params content
I think your registrations controller isn't correct.
If you create your own registrations controller, you want it to override the registrations controller from devise:
class RegistrationsController < Devise::RegistrationsController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
end
Then in your routes you must make sure that it also uses your own registrations controller:
devise_for :users, controllers: {registrations: 'registrations'}
Hope this will help you.

sign up and create new object at the same time

i'm new in Ruby on Rails.
I was trying to register a new user with devise and at the same time, create a new Company object. The association between them : User belongs to Company. Company has many users. I tried to make it based on this link : http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast, but it didnt work. it said : "undefined method for Company", which is Company doesnt have email attribute.
and in sign up form, i only put email attribute for user
<div class="title"><%= t('.signup') %></div>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="control-group"><%= f.label :email, t('.email') %>
<%= f.email_field :email %></div>
<div class="control-group"><%= f.label :password, t('.password') %>
<%= f.password_field :password %></div>
<div class="control-group"><%= f.label :password_confirmation, t('.password_confirmation') %>
<%= f.password_field :password_confirmation %></div>
<%= f.fields_for :company_attributes do |f_company| %>
<div class="control-group"><%= f_company.label :name, t('.company_name') %>
<%= f_company.text_field :name %></div>
<% end %>
<div class="buttons"><%= f.submit t('.signup'), class:"btn btn-primary" %><br>
<%= render "links" %></div>
<% end %>
updated
Company controller :
class CompaniesController < Devise::RegistrationsController
def new
#company = Company.new
#user = #company.users.build
end
def create
#company = Company.new(params[:company])
#user = User.create(params[:user].merge(company_id:company.id))
if #company.save
redirect_to "/"
else
render 'users/sign_up'
end
end
end
User Controller :
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render 'users/new'
end
end
I would really appreciate any idea and any help. Thanks in advance
In your case, since you are only interested in receiving the company_name I'd simplify it and submit the company_name via a text_field_tag.
In other words, I would replace:
<%= f.fields_for :company_attributes do |f_company| %>
<div class="control-group"><%= f_company.label :name, t('.company_name') %>
<%= f_company.text_field :name %></div>
<% end %>
With this
<%= text_field_tag 'company_name', {placeholder:"Enter here the name of your company",class:"form-control"} %>
This would submit the name to the UsersController, and you could access it with params[:company_name] so the controller would look like this:
Users controller:
def new
#user = User.new
end
def create
#user = User.create(params[:user])
#Create a company via 'user<->company' association using 'company_name'
#company= #user.company.create(name: params[:company_name])
if #user.save
redirect_to users_path
else
render 'users/new'
end
end

Couldn't find User with id=update_password with Devise

I'm using devise on a Rails application and for various reasons I needed to add a custom password change, using solution 3 from here: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password
I've looked through various other questions and tried augmenting my routes in a number of ways but the url always comes up /users/update_password (without a parameter passed), or /users/update_password.254 (with a parameter passed). I need it to be /users/254/update_password.
Here is the Users Controller I'm working with (relevant methods are update_password and user_params).
class UsersController < ApplicationController
inherit_resources
load_and_authorize_resource
before_filter :authenticate_user!
def destroy
name = #user.to_s
destroy!(notice: "User #{#user.to_s} was deleted.")
end
def edit
#user = current_user
end
def update_password
#user = User.find(current_user.id)
if #user.update_with_password(user_params)
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
private
def build_resource_params
[params.fetch(:user, {}).permit(:name, :email, :phone_number, :password, :password_confirmation).tap do |p|
p[:institution_pid] = build_institution_pid if params[:user][:institution_pid]
p[:role_ids] = build_role_ids if params[:user][:role_ids]
end]
end
def build_institution_pid
institution = Institution.find(params[:user][:institution_pid])
authorize!(:add_user, institution)
institution.id
end
def build_role_ids
[].tap do |role_ids|
roles = Role.find(params[:user][:role_ids].reject &:blank?)
roles.each do |role|
authorize!(:add_user, role)
role_ids << role.id
end
end
end
def user_params
params.required(:user).permit(:password, :password_confirmation, :current_password)
end
end
This is the relevant portion of routes.rb
devise_for :users
resources :users do
collection do
get 'password'
put 'update_password'
end
end
This is the view I created to go with it:
<div class="page-header">
<h1>Change Password</h1>
</div>
<%= form_for(#user, :url => { :action => "update_password" } ) do |f| %>
<div class="field">
<%= f.label :password, "Password" %><br />
<%= f.password_field :password, :autocomplete => "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
</div>
<div class="action_container">
<%= f.submit %>
</div>
And this is the link: (currently with a parameter but I've tried without as well)
<li><%= link_to "Change Password", update_password_users_path(current_user) %></li>
When I click that link I get routed to a page that says ActiveRecord::RecordNotFound in UsersController#show, Couldn't find User with id=update_password.
I tried to follow the instructions pretty closely and I've tried to fiddle with the routes a couple times to get it to work and I'm pretty lost at this point. Has anyone seen this before and can help? Thanks!
To achieve what you need, you will have the following routes:
resources :users do
patch 'update_password', on: :collection
get 'edit_password', on: :member
end
Then you'll add a link that would lead user to update password form. The link should look like as follows:
<li><%= link_to "Change Password", edit_password_user_path(current_user) %></li>
The link would lead to users/edit_password.html.erb:
<div class="page-header">
<h1>Change Password</h1>
</div>
<%= form_for(#user, :url => { :action => "update_password" } ) do |f| %>
<div class="field">
<%= f.label :password, "Password" %><br />
<%= f.password_field :password, :autocomplete => "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
</div>
<div class="action_container">
<%= f.submit %>
</div>
<% end %>
When form is submitted, that would trigger update_password in UsersController. Here is the code for controller:
class UsersController < ApplicationController
def edit_password
#user = current_user
end
def update_password
#user = User.find(current_user.id)
if #user.update_with_password(user_params)
sign_in #user, :bypass => true
redirect_to root_url
else
render "edit_password"
end
end
private
def user_params
params.required(:user).permit(:password, :password_confirmation, :current_password)
end
end

Sign up/Login Routing error

Sorry for the novice question but I am trying to get to grips with RoR. I have a very basic sign up and login process in place but am having some difficulty getting the routing correct. I am also unsure whether I am actually being logged out successfully when I push my logout button because it isn't then displaying the login button as it should.
My setup is as follows on Rails 3.1:
Sessions Controller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password!"
render "signup"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged Out!"
end
end
User Controller
class UserController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new (params[:user])
if #user.save
redirect_to root_url, :notice => "Signed Up!"
else
render "user/new"
end
end
end
User Model
class User < ActiveRecord::Base
has_secure_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email, :on => :create
end
Sessions/new.html.erb
<h1>Log In</h1>
<%= form_tag login_path do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class ="field">
<%= label_tag :password %>
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Log in" %></div>
<%end%>
User/new.html.erb
<% if session[:user_id] %>
<!-- user is logged in -->
<%= link_to logout_path %>
<% else %>
<!-- user is not logged in -->
<%= link_to login_path %>
<% end %>
<h1>Sign Up</h1>
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class = "field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class = "field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class = "field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit %></div>
<% end %>
Finally my Routes file
MadeByV2::Application.routes.draw do
controller :user do
get "signup" => "user#new"
end
resources :users
controller :sessions do
get "login" => "sessions#new"
post "login" => "sessions#create"
delete "logout" => "sessions#destroy"
end
root :to => "user#new"
end
Sorry for the extensive use of code in this post but I figure it's best to give a well rounded view of everything so people can see where I am going wrong.
Any help you can offer really would be much appreciated because I don't seem to be getting it myself
Thanks,
Tom
Your code looks fine, but your routes look a little strange.
I'd try something like this:
resources :users
resources :sessions
match 'login' => 'sessions#new', :as => :login
match 'logout' => 'sessions#destroy', :as => :logout
Then I think everything you've currently got should work.

Resources