Changing the user login/logout code to return to the last page we were on.

July 23, 2007

Back again after a weekend of DIY and sun (at last!).

At the end of the last session i realised that the login info needs to be on the show screen as well, so i’m going to make it into a partial: _login.rhtml now has this –

<% user = session[:user] %>
<% if user %>
<%= “You are logged in as “ %> <strong> <%= user.login %> </strong> <span> </span>
<%= link_to ‘Log out’, :controller => “user”, :action => “logout” %> <br/>
<% else %>
<%= link_to ‘Log in’, :controller => “user”, :action => “login” %>
<% end %><br />

Which i call on the new, list and show pages with

<%= render :partial => ‘login’ %>

Nice and easy, no problems. Like i was saying the other day, i’d still like to move all of the login functionality onto the top of each page (instead of a separate login page) – i’m going to have a look at doing that now, after all. I think the way to go about it is to make a new partial called _login_manager, which looks at whether someone’s logged in and then renders either the _login (if no-one’s logged in) or the _logged_in (if someone is logged in) partials.

OK, done that. Looks good, maybe a little amateurish still but i think that’s just down to the general lack of stylesheet usage. Here’s my code for the log_in partial: it uses a table with the form components in the cells.

<%= start_form_tag :controller => “user”, :action=> “login” %>
<table width=”70%” border=”0″ cellspacing=”1″ cellpadding=”1″>
<tr>
<td><label for=”user_login”>Login name:</label></td>
<td> <label for=”user_password”>Password:</label></td>
<td> </td>
<td><%= link_to ‘Sign up’, :action => ‘signup’ %></td>
</tr>
<tr>
<td><%= text_field “user”, “login”, :size => 20 %></td>
<td><%= password_field “user”, “password”, :size => 20 %></td>
<td><%= submit_tag “Log in” %></td>
<td><%= link_to ‘Forgotten your username/password?’, :action => ‘forgot_password’ %></td>
</tr>
</table>
<%= end_form_tag %>

That seems to work ok.

Ah- one problem – when you log out it takes you to the old login screen, and when you change password it doesn’t take you back to the list afterwards. Let’s see…better have a look at UserController.

Changed the logout action so that instead of returning you to the login screen it calls the return_to_stored action in ApplicationController. However, this isn’t working.

Changed the login action to call return_to_stored when it finishes. However, return_to_stored isn’t working that well – or more likely isn’t being called properly. It looks ok – it either returns the user to whatever’s held in session[:return_to], or if this is empty then takes them to the list page. The only time something gets saved into session[:return_to] is in the login_required method, which if you recall, gets checked whenever the user tries to do something they need to be logged in for:

def login_required
if session[:user]
return true
end
#otherwise, the user is forced to log in
flash[:warning] = ‘Please login to continue’
#store the page to return to, ie the page this was called from.
#This is used by redirect_to_stored

session[:return_to] = request.request_uri
return false
end

So, i can log in and out happily on the list page. What about the other stuff – sign up, forgot password, change password?

Forgot password – fixed.

Sign up – “No action responds to sign up”. Probably got the displayed text and the name of the action mixed up in log_in.rhtml…Actually it was because i forgot to set :controller to “user” so it was looking in the newspipe controller, which of course doesn’t hold the signup action. Working now.

Change password – Hmm, something bad’s happened. I seem to be stuck on the ‘change password’ screen without it able to say who’s signed in. At one point i put in the new password and it asked my which user i wanted to change the password for, from a drop down list of several users! This is really bad. Someone could change someone else’s password with this, not good at all.

Fixed it up a bit by making sure that redirect_to_stored is called after changing the password. However, it won’t show who’s signed in – i’m calling session[:user] and just getting a #….wait, that means ‘an object’ – i need to call session[:user].login! Fool. Ok, fixed that. Still have two problems though – a) it’s filling in the password fields with some random crap. I have no idea what – i’ve set the value to “” and “nil” and still just get some random stuff. I don’t even know what it’s printing because it’s starred out. b) If the passwords are different, and i hit submit, it does that thing of asking me who’s password i want to change.

I looked up validates_confirmation_of, which is a rails method that i’m calling from the User model. That all seems ok – it’s recommendations for the form fields are exactly what i’m doing.

Tried something other than using this automatic method – changed the change_password method to take both the text field values and compare them itself – if they match then it changes the password, saves the user object, and returns to the list page. Otherwise, it flashes an error messge and goes back to the change password screen:

#allow user to change the password
def change_password
@user = session[:user]
if request.post?
#old check – commented out in favour of a manual test
#@user.update_attributes(:password => params[:user][:password],
#:password_confirmation => params[:user][:password_confirmation])

if params[:user_password] == params[:user_password_confirmation]
@user.password = params[:user_password]
if @user.save
flash[:message] = “Password changed”
redirect_to_stored
else
flash[:warning] = “System error: couldn’t save new password.”
redirect_to :controller => “newspipe”, :action => ‘list’
end
else
flash[:warning] = “Password and confirmation do not match”
redirect_to :action => ‘change_password’
end
end
end

And now – NOTHING WORKS! agggghhhh

I’m just getting this on every screen, even list:

Status: 500 Internal Server Error Content-Type: text/html

We’re sorry, but something went wrong.

We’ve been notified about this issue and we’ll take a look at it shortly.

what the…It’s happening on evey screen. Maybe it’s to do with the default layout? Mucked about with that…no change. OK, step retracing time. Changed controller back to how it was – still broken. Changed the model back – still broken. Hmmmm. Wish i had subversion installed. (note to self – install subversion once you get out of this mess).

Think i found the problem i’ve deleted most of my controller files! God knows how. As luck would have it i just backed them up but this was obviously a bad bad error. OK, definitely going to get subversion installed soon!

Right, working again, so now i need to redo the stuff i undid while looking for the problem. What a mess.

Made changes – “System error: couldn’t save new password.”. Ah yeah, that’s cos i’m just trying to write the password into the db, but of course it’s an encrypted version that gets saved so i need to call the proper encrypted password method. Still can’t work out how to get this to work. Leaving it as it was and carrying on.

Next – adding the partial to the show screen. Done this and it looks ok, but whenever you do anything (such as log out) you go back to the list screen. I think this is because redirect_to_stored has list as it’s default page to go to. It’s obviously not being used properly. Do I need to save the variable :return_to into session before i call the actions?

Agh, everything just seems to be getting more broken the more i try to fix it at the moment.

OK – let’s look at this step by step.

I’m on the show page, logged in, and i hit logout. I’m calling this code from the _logged_in.hrtml partial:

<%= link_to ‘Log out’, :controller => “user”, :action => “logout” %>

That calls the action ‘logout’, from UserController. That action code looks like this –

def logout
session[:user] = nil
flash[:message] = ‘Logged out’
#this should take the user back to the page they were at
redirect_to_stored
end

redirect_to_stored in turn looks like this:

def redirect_to_stored
if return_to = session[:return_to]
session[:return_to] = nil
redirect_to_url(return_to)
else
flash[:message] = “session[:return_to] was empty, returning to list page”
redirect_to :controller => ‘newspipe’, :action => ‘list’
end
end

So we’ve got the problem stll that there’s just nothing in session[:return_to] when redirect_to_stored is called. The only time that something gets saved into here is in the login_required method, which seems wrong.

I’ve asked on some forums about this, i think i’m going to have to leave it for now. Currently, apart from always getting sent back to the list page, all the login stuff is working fine.

….lots of experimentation and mucking about (still no forum answers) …

Progress! It now remembers what page you were on when you log in and out! I did this (and i’m sure this is a dumb way to do it) by making another partial called _save_page that writes the current page into session[:return_to]. Then i call that partial only from pages that i’m interested in returning to, such as show and list. Now it seems to be ok – the :return_to value is being saved and retrieved ok.

So, i can log in and log out without getting kicked back to the list page now. I also changed login so that redirect_to_stored is called whether the login was successful or not. Fixed up the other login methods to redirect_to_stored as well (the method still defaults to returning to the list page if nothing is held in session[:return_to].

So, probably an unneccessarily convoluted way of doing things, but it works. I’m really pleased, i thought today was going to be a write off. I seem to have a knack of fixing stuff just before stopping time. :) OK, 7 o clock now so time to stop.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: