(Using Rails 3) I have authlogic set up to manage my users and user sessions (with a user controller and user_sessions controller and associated views, models, etc). I have two levels of user: an admin and a client. The admin can create users and edit users, and users are only able to look at documents on the website (no user editing or viewing privileges at all).
Then I implemented another controller (reset_password) which allows clients to change their password on the website. To do this I allowed clients to edit clients (so they can edit themselves). But The form that allows clients to change their password is much more limited than the form that allows admins to make clients.
The problem I am having is that when a client goes to the password editing form, and enters an invalid password or password confirmation, the page refreshes to the full admin form that lets clients change their privileges, etc. What I need is a way to force a failed form entry to return the client to the same form that they were just on rather than the admin form.
The problem, I am pretty sure, is because both the admin form and the client form use 'form_for @user do |f|' to define the form. I believe that rails cannot tell the two apart.
Here are the relevant files:
My admin form (users#edit with rendered form):
= stylesheet_link_tag :universal
= form_for @user do |f|
- if @user.errors.any?
#error_explanation
%span.boldHeader= "#{pluralize(@user.errors.count, "error")} prohibited this user from being saved:"
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.fieldContainer
.field
= f.label :username
= f.text_field :username
.field
= f.label :role
= f.collection_select :role, User::ROLES, :to_s, :humanize, { :selected => :client }
.fieldContainer
.field
= f.label :password
= f.password_field :password
.field
= f.label :password_confirmation
= f.password_field :password_confirmation
.fieldContainer
.field
= f.label :first_name
= f.text_field :first_name
.field
= f.label :last_name
= f.text_field :last_name
.field
= f.label :firm_name
= f.text_field :firm_name
.fieldContainer
.field
= f.label :email
= f.text_field :email
.field
= f.label :phone
= f.text_field :phone
.actions
= f.submit 'Save'
%a.button{ :href => "/users" }Cancel
My client form (reset_password#edit with rendered form):
= stylesheet_link_tag :universal
= form_for @user do |f|
- if @user.errors.any?
#error_explanation
%span.boldHeader= "#{pluralize(@user.errors.count, "error")} prohibited this user from being saved:"
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.fieldContainer
.field
= f.label :password
= f.password_field :password
.field
= f.label :password_confirmation
= f.password_field :password_confirmation
.actions
= f.submit 'Save'
%a.button{ :href => "/users" }Cancel
Client form update method:
def update
@user = current_user
if @user.update_attributes(params[:user])
format.html { redirect_to @user, notice: 'Password successfully changed.' }
format.json { head :no_content }
else
format.json { render json: @user.errors, status: :unprocessable_entity }
format.html { redirect_to "/settings" }
end
end
Admin form update method:
def update
respond_to do |format|
if @user.update_attributes(params[:user])
format.html { redirect_to @user, notice: 'User was successfully updated' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
Note: /settings routes to reset_passwords#edit which has a partial render in it which is how I display the client form (see above). I also have users, user_sessions, and reset_passwords as resources in my routes file.
Finally, here is what I am seeing in the production log. You can see that I start in reset_password/_form (the client form) but then I get rolled back to users/_form (the admin form).
Started GET "/settings" for 127.0.0.1 at 2013-07-02 16:26:52 -0400
Processing by ResetPasswordsController#edit as HTML
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Rendered reset_passwords/_form.html.haml (4.0ms)
Rendered reset_passwords/edit.html.haml within layouts/application (14.0ms)
Rendered shared/_header.html.haml (1.0ms)
Completed 200 OK in 57ms (Views: 54.0ms | ActiveRecord: 0.0ms)
[2013-07-02 16:26:52] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
Started GET "/assets/global.css?body=1" for 127.0.0.1 at 2013-07-02 16:26:52 -0400
Served asset /global.css - 304 Not Modified (0ms)
[2013-07-02 16:26:53] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
Started PUT "/users/1" for 127.0.0.1 at 2013-07-02 16:26:58 -0400
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9KeHrOySL3FRhSnfYtVvKl2rCz9EPBSyGmtGGasDxnA=", "user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Save", "id"=>"1"}
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "1"]]
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.0ms) begin transaction
User Exists (0.0ms) SELECT 1 AS one FROM "users" WHERE ("users"."persistence_token" = '5d1007bf94b34a7de44707e4ca432b18338f1c9ef6c5f99bf04845096a6b880057b5b43c917b9d38f790521ef62147eec3d9302471da73967048b48b4a399b18' AND "users"."id" != 1) LIMIT 1
(1.0ms) rollback transaction
Rendered users/_form.html.haml (9.0ms)
Rendered users/edit.html.haml within layouts/application (19.0ms)
Rendered shared/_header.html.haml (0.0ms)
Completed 200 OK in 80ms (Views: 61.0ms | ActiveRecord: 1.0ms)