merge 19889:20181 of rails_port into the openID branch
authorKai Krueger <kakrueger@gmail.com>
Sat, 27 Feb 2010 11:21:15 +0000 (11:21 +0000)
committerKai Krueger <kakrueger@gmail.com>
Sat, 27 Feb 2010 11:21:15 +0000 (11:21 +0000)
merge conflicts with the remember_me functionality

1  2 
app/controllers/user_controller.rb
app/models/user.rb
app/views/user/account.html.erb
app/views/user/login.html.erb
config/locales/en.yml

index f9bbd5dd5be7c9fbeeb97c5b73bd03e2c06e7185,c181da305862f81bdd721fb9098d154f98e46b90..97e184b5943cef9269668e13520893f6867b9375
@@@ -4,9 -4,9 +4,9 @@@ class UserController < ApplicationContr
    before_filter :authorize, :only => [:api_details, :api_gpx_files]
    before_filter :authorize_web, :except => [:api_details, :api_gpx_files]
    before_filter :set_locale, :except => [:api_details, :api_gpx_files]
-   before_filter :require_user, :only => [:set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image]
+   before_filter :require_user, :only => [:account, :go_public, :make_friend, :remove_friend]
    before_filter :check_database_readable, :except => [:api_details, :api_gpx_files]
-   before_filter :check_database_writable, :only => [:login, :new, :set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image]
+   before_filter :check_database_writable, :only => [:login, :new, :account, :go_public, :make_friend, :remove_friend]
    before_filter :check_api_readable, :only => [:api_details, :api_gpx_files]
    before_filter :require_allow_read_prefs, :only => [:api_details]
    before_filter :require_allow_read_gpx, :only => [:api_gpx_files]
@@@ -15,7 -15,7 +15,7 @@@
    before_filter :lookup_this_user, :only => [:activate, :deactivate, :hide, :unhide, :delete]
  
    filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation
-   
    cache_sweeper :user_sweeper, :only => [:account, :hide, :unhide, :delete]
  
    def save
      if Acl.find_by_address(request.remote_ip, :conditions => {:k => "no_account_creation"})
        render :action => 'new'
      else
-         #The redirect from the OpenID provider reenters here again
-         #and we need to pass the parameters through to the 
-         #open_id_authentication function a second time
-         if params[:open_id_complete]
-               openid_verify('', true)
-               #We have set the user.openid_url to nil beforehand. If it hasn't
-               #been set to a new valid openid_url, it means the openid couldn't be validated
-               if @user.nil? or @user.openid_url.nil?
-                 render :action => 'new'
-                 return
-               end  
-         else
-               @user = User.new(params[:user])
-               
-               @user.visible = true
-               @user.data_public = true
-               @user.description = "" if @user.description.nil?
-               @user.creation_ip = request.remote_ip
-               @user.languages = request.user_preferred_languages
-               #Set the openid_url to nil as for one it is used
-               #to check if the openid could be validated and secondly
-               #to not get dupplicate conflicts for an empty openid 
-               @user.openid_url = nil
-               if (!params[:user][:openid_url].nil? and params[:user][:openid_url].length > 0)
++        #The redirect from the OpenID provider reenters here again 
++      #and we need to pass the parameters through to the  
++      #open_id_authentication function a second time 
++      if params[:open_id_complete] 
++        openid_verify('', true) 
++        #We have set the user.openid_url to nil beforehand. If it hasn't 
++        #been set to a new valid openid_url, it means the openid couldn't be validated 
++        if @user.nil? or @user.openid_url.nil? 
++          render :action => 'new' 
++          return 
++        end   
++      else
+       @user = User.new(params[:user])
+       @user.visible = true
+       @user.data_public = true
+       @user.description = "" if @user.description.nil?
+       @user.creation_ip = request.remote_ip
+       @user.languages = request.user_preferred_languages
++        #Set the openid_url to nil as for one it is used 
++        #to check if the openid could be validated and secondly 
++        #to not get dupplicate conflicts for an empty openid  
++        @user.openid_url = nil
++
++if (!params[:user][:openid_url].nil? and params[:user][:openid_url].length > 0)
 +                if @user.pass_crypt.length == 0 
 +            #if the password is empty, but we have a openid 
 +            #then generate a random passowrd to disable 
 +            #loging in via password 
 +            @user.pass_crypt = ActiveSupport::SecureRandom.base64(16) 
 +            @user.pass_crypt_confirmation = @user.pass_crypt 
 +          end
 +                #Validate all of the other fields before
 +                #redirecting to the openid provider
 +                if !@user.valid?
 +                      render :action => 'new'
 +                else            
 +                      #TODO: Is it a problem to store the user variable with respect to password safty in the session variables?
 +                      #Store the user variable in the session for it to be accessible when redirecting back from the openid provider
 +                      session[:new_usr] = @user
 +                      begin
 +                        @norm_openid_url = OpenIdAuthentication.normalize_identifier(params[:user][:openid_url])
 +                      rescue
 +                        flash.now[:error] = t 'user.login.openid invalid'
 +                        render :action => 'new'
 +                        return
 +                      end
 +                      #Verify that the openid provided is valid and that the user is the owner of the id
 +                      openid_verify(@norm_openid_url, true)
 +                      #openid_verify can return in two ways:
 +                      #Either it returns with a redirect to the openid provider who then freshly
 +                      #redirects back to this url if the openid is valid, or if the openid is not plausible
 +                      #and no provider for it could be found it just returns
 +                      #we want to just let the redirect through
 +                      if response.headers["Location"].nil?
 +                        render :action => 'new'
 +                      end
 +                end
 +                #At this point there was either an error and the page has been rendered,
 +                #or there is a redirect to the openid provider and the rest of the method
 +                #gets executed whenn this method gets reentered after redirecting back
 +                #from the openid provider
 +                return
 +              end
 +        end
-         if @user.save
-               flash[:notice] = t 'user.new.flash create success message'
-               Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => params[:referer]))
-               redirect_to :action => 'login'
-         else
-               render :action => 'new'
-         end
+       if @user.save
+         flash[:notice] = t 'user.new.flash create success message'
+         Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => params[:referer]))
+         redirect_to :action => 'login'
+       else
+         render :action => 'new'
+       end
      end
    end
  
      @title = t 'user.account.title'
      @tokens = @user.oauth_tokens.find :all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null'
  
-     #The redirect from the OpenID provider reenters here again
++      #The redirect from the OpenID provider reenters here again
 +    #and we need to pass the parameters through to the 
 +    #open_id_authentication function
 +    if params[:open_id_complete]
 +      openid_verify('', false)
 +        @user.save
 +      return
 +    end
 +
      if params[:user] and params[:user][:display_name] and params[:user][:description]
-       if params[:user][:email] != @user.email
-         @user.new_email = params[:user][:email]
-       end
        @user.display_name = params[:user][:display_name]
+       @user.new_email = params[:user][:new_email]
  
        if params[:user][:pass_crypt].length > 0 or params[:user][:pass_crypt_confirmation].length > 0
          @user.pass_crypt = params[:user][:pass_crypt]
          @user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
        end
-       if (params[:user][:openid_url].length == 0)
-         #Clearing doesn't need OpenID validation, so we can set it here.
-         @user.openid_url = nil
-       end
  
        @user.description = params[:user][:description]
        @user.languages = params[:user][:languages].split(",")
+       case params[:image_action]
+         when "new" then @user.image = params[:user][:image]
+         when "delete" then @user.image = nil
+       end
        @user.home_lat = params[:user][:home_lat]
        @user.home_lon = params[:user][:home_lon]
  
        if @user.save
          set_locale
  
-         if params[:user][:email] == @user.new_email
-           flash.now[:notice] = t 'user.account.flash update success confirm needed'
-           Notifier.deliver_email_confirm(@user, @user.tokens.create)
-         else
+         if @user.new_email.nil? or @user.new_email.empty?
            flash.now[:notice] = t 'user.account.flash update success'
+         else
+           flash.now[:notice] = t 'user.account.flash update success confirm needed'
+           begin
+             Notifier.deliver_email_confirm(@user, @user.tokens.create)
+           rescue
+             # Ignore errors sending email
+           end
          end
        end
-       if (params[:user][:openid_url].length > 0)
 +
++        if (params[:user][:openid_url].length > 0)
 +              begin
 +                @norm_openid_url = OpenIdAuthentication.normalize_identifier(params[:user][:openid_url])
 +                if (@norm_openid_url != @user.openid_url)
 +                      #If the OpenID has changed, we want to check that it is a valid OpenID and one
 +                      #the user has control over before saving the openID as a password equivalent for
 +                      #the user.
 +                      openid_verify(@norm_openid_url, false)
 +                end
 +              rescue
 +                flash.now[:error] = t 'user.login.openid invalid'
 +              end
 +      end
 +
+     else
+       if flash[:errors]
+         flash[:errors].each do |attr,msg|
+           attr = "new_email" if attr == "email"
+           @user.errors.add(attr,msg)
+         end
+       end
      end
    end
  
-   def set_home
-     if params[:user][:home_lat] and params[:user][:home_lon]
-       @user.home_lat = params[:user][:home_lat].to_f
-       @user.home_lon = params[:user][:home_lon].to_f
-       if @user.save
-         flash[:notice] = t 'user.set_home.flash success'
-         redirect_to :controller => 'user', :action => 'account'
 +  def openid_specialcase_mapping(openid_url)
 +    #Special case gmail.com, as it is pontentially a popular OpenID provider and unlike
 +    #yahoo.com, where it works automatically, Google have hidden their OpenID endpoint
 +    #somewhere obscure making it less userfriendly.
 +    if (openid_url.match(/(.*)gmail.com(\/?)$/) or openid_url.match(/(.*)googlemail.com(\/?)$/) )
 +      return 'https://www.google.com/accounts/o8/id'
 +    end
 +
 +    return nil
 +  end  
 +
 +  def openid_verify(openid_url,account_create)
 +    authenticate_with_open_id(openid_url) do |result, identity_url|
 +      if result.successful?
 +        #We need to use the openid url passed back from the OpenID provider
 +        #rather than the one supplied by the user, as these can be different.
 +        #e.g. one can simply enter yahoo.com in the login box, i.e. no user specific url
 +        #only once it comes back from the OpenID provider do we know the unique address for
 +        #the user.
 +              @user = session[:new_usr] unless @user #this is used for account creation when the user is not yet in the database
 +        @user.openid_url = identity_url
 +        elsif result.missing?
 +              mapped_id = openid_specialcase_mapping(openid_url)
 +              if mapped_id
 +                openid_verify(mapped_id, account_create)
 +              else
 +                flash.now[:error] = t 'user.login.openid missing provider'
 +              end
 +        elsif result.invalid?
 +              flash.now[:error] = t 'user.login.openid invalid'
 +        else
 +              flash.now[:error] = t 'user.login.auth failure'
 +        end
 +    end
 +  end
 +
++  def open_id_authentication(openid_url)
++    #TODO: only ask for nickname and email, if we don't already have a user for that openID, in which case
++    #email and nickname are already filled out. I don't know how to do that with ruby syntax though, as we
++    #don't want to duplicate the do block
++    #On the other hand it also doesn't matter too much if we ask every time, as the OpenID provider should
++    #remember these results, and shouldn't repromt the user for these data each time.
++    authenticate_with_open_id(openid_url, :return_to => request.protocol + request.host_with_port + '/login?referer=' + params[:referer], :optional => [:nickname, :email]) do |result, identity_url, registration|
++      if result.successful?
++        #We need to use the openid url passed back from the OpenID provider
++        #rather than the one supplied by the user, as these can be different.
++        #e.g. one can simply enter yahoo.com in the login box, i.e. no user specific url
++        #only once it comes back from the OpenID provider do we know the unique address for
++        #the user.
++        user = User.find_by_openid_url(identity_url)
++        if user
++          if user.visible? and user.active?
++            session[:user] = user.id
++                      session_expires_after 1.month if session[:remember]
++          else
++            user = nil
++            flash.now[:error] = t 'user.login.account not active'
++          end
++        else
++          #We don't have a user registered to this OpenID. Redirect to the create account page
++          #with username and email filled in if they have been given by the OpenID provider through
++          #the simple registration protocol
++          redirect_to :controller => 'user', :action => 'new', :nickname => registration['nickname'], :email => registration['email'], :openid => identity_url
++        end
++      else if result.missing?
++             #Try and apply some heuristics to make common cases more userfriendly
++             mapped_id = openid_specialcase_mapping(openid_url)
++             if mapped_id
++               open_id_authentication(mapped_id)
++             else
++               flash.now[:error] = t 'user.login.openid missing provider'
++             end
++           else if result.invalid?
++                  flash.now[:error] = t 'user.login.openid invalid'
++                else
++                  flash.now[:error] = t 'user.login.auth failure'
++                end
++           end
 +      end
 +    end
 +  end
 +
    def go_public
      @user.data_public = true
      @user.save
        if user
          token = user.tokens.create
          Notifier.deliver_lost_password(user, token)
-         flash.now[:notice] = t 'user.lost_password.notice email on way'
+         flash[:notice] = t 'user.lost_password.notice email on way'
+         redirect_to :action => 'login'
        else
          flash.now[:error] = t 'user.lost_password.notice email cannot find'
        end
      # The user is logged in already, so don't show them the signup page, instead
      # send them to the home page
      redirect_to :controller => 'site', :action => 'index' if session[:user]
-     @nickname = params['nickname']
 +
++      @nickname = params['nickname']
 +    @email = params['email']
 +      @openID = params['openid']
    end
  
    def login
-     #The redirect from the OpenID provider reenters here again
 +
-     
++      #The redirect from the OpenID provider reenters here again
 +    #and we need to pass the parameters through to the 
 +    # open_id_authentication function
 +    if params[:open_id_complete]
 +      open_id_authentication('')
 +    end
 +
      if params[:user] and session[:user].nil?
-       if !params[:user][:openid_url].nil? and !params[:user][:openid_url].empty?
 -      email_or_display_name = params[:user][:email]
 -      pass = params[:user][:password]
 -      user = User.authenticate(:username => email_or_display_name, :password => pass)
 -      if user
 -        session[:user] = user.id
 -        session_expires_after 1.month if params[:remember_me]
 -      elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true)
 -        flash.now[:error] = t 'user.login.account not active'
++        if !params[:user][:openid_url].nil? and !params[:user][:openid_url].empty?
++              session[:remember] = params[:remember_me]
 +        open_id_authentication(params[:user][:openid_url])
        else
-         email_or_display_name = params[:user][:email]
-         pass = params[:user][:password]
-         user = User.authenticate(:username => email_or_display_name, :password => pass)
-         if user
-           session[:user] = user.id
-         elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true)
-           flash.now[:error] = t 'user.login.account not active'
-         else
-           flash.now[:error] = t 'user.login.auth failure'
-         end
 -        flash.now[:error] = t 'user.login.auth failure'
--      end
++              email_or_display_name = params[:user][:email]
++              pass = params[:user][:password]
++              user = User.authenticate(:username => email_or_display_name, :password => pass)
++              if user
++                session[:user] = user.id
++                session_expires_after 1.month if params[:remember_me]
++              elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true)
++                flash.now[:error] = t 'user.login.account not active'
++              else
++                flash.now[:error] = t 'user.login.auth failure'
++              end
++        end
      end
-   
      if session[:user]
        # The user is logged in, if the referer param exists, redirect them to that
        # unless they've also got a block on them, in which case redirect them to
      @title = t 'user.login.title'
    end
  
-   def open_id_authentication(openid_url)
-     #TODO: only ask for nickname and email, if we don't already have a user for that openID, in which case
-     #email and nickname are already filled out. I don't know how to do that with ruby syntax though, as we
-     #don't want to duplicate the do block
-     #On the other hand it also doesn't matter too much if we ask every time, as the OpenID provider should
-     #remember these results, and shouldn't repromt the user for these data each time.
-     authenticate_with_open_id(openid_url, :return_to => request.protocol + request.host_with_port + '/login?referer=' + params[:referer], :optional => [:nickname, :email]) do |result, identity_url, registration|
-       if result.successful?
-         #We need to use the openid url passed back from the OpenID provider
-         #rather than the one supplied by the user, as these can be different.
-         #e.g. one can simply enter yahoo.com in the login box, i.e. no user specific url
-         #only once it comes back from the OpenID provider do we know the unique address for
-         #the user.
-         user = User.find_by_openid_url(identity_url)
-         if user
-           if user.visible? and user.active?
-             session[:user] = user.id
-           else
-             user = nil
-             flash.now[:error] = t 'user.login.account not active'
-           end
-         else
-           #We don't have a user registered to this OpenID. Redirect to the create account page
-           #with username and email filled in if they have been given by the OpenID provider through
-           #the simple registration protocol
-           redirect_to :controller => 'user', :action => 'new', :nickname => registration['nickname'], :email => registration['email'], :openid => identity_url
-         end
-       else if result.missing?
-              #Try and apply some heuristics to make common cases more userfriendly
-              mapped_id = openid_specialcase_mapping(openid_url)
-              if mapped_id
-                open_id_authentication(mapped_id)
-              else
-                flash.now[:error] = t 'user.login.openid missing provider'
-              end
-            else if result.invalid?
-                   flash.now[:error] = t 'user.login.openid invalid'
-                 else
-                   flash.now[:error] = t 'user.login.auth failure'
-                 end
-            end
-       end
-     end
-   end
    def logout
      if session[:token]
        token = UserToken.find_by_token(session[:token])
        session[:token] = nil
      end
      session[:user] = nil
+     session_expires_automatically
      if params[:referer]
        redirect_to params[:referer]
      else
          @user.new_email = nil
          @user.active = true
          @user.email_valid = true
-         @user.save!
+         if @user.save
+           flash[:notice] = t 'user.confirm_email.success'
+         else
+           flash[:errors] = @user.errors
+         end
          token.destroy
-         flash[:notice] = t 'user.confirm_email.success'
          session[:user] = @user.id
          redirect_to :action => 'account', :display_name => @user.display_name
        else
      end
    end
  
-   def upload_image
-     @user.image = params[:user][:image]
-     @user.save!
-     redirect_to :controller => 'user', :action => 'view', :display_name => @user.display_name
-   end
-   def delete_image
-     @user.image = nil
-     @user.save!
-     redirect_to :controller => 'user', :action => 'view', :display_name => @user.display_name
-   end
    def api_gpx_files
      doc = OSM::API.new.get_xml_doc
      @user.traces.each do |trace|
diff --combined app/models/user.rb
index eb69dc03148107bc4bd8092382db1b5634957135,0b8b512ee3bab5becf3bc350fb916026bd51b5b0..1a476954440f8b308be08c63c0ed8072dd7c5e03
@@@ -22,11 -22,10 +22,11 @@@ class User < ActiveRecord::Bas
    validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password'
    validates_uniqueness_of :display_name, :allow_nil => true
    validates_uniqueness_of :email
 +  validates_uniqueness_of :openid_url, :allow_nil => true
    validates_length_of :pass_crypt, :within => 8..255
    validates_length_of :display_name, :within => 3..255, :allow_nil => true
-   validates_length_of :email, :within => 6..255
-   validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
+   validates_email_format_of :email
+   validates_email_format_of :new_email, :allow_blank => true
    validates_format_of :display_name, :with => /^[^\/;.,?]*$/
    validates_numericality_of :home_lat, :allow_nil => true
    validates_numericality_of :home_lon, :allow_nil => true
index 99d0f216e9fe1a675b414fcda1ed584ac2c184f4,881750c36504a2264c475258adf9770fd5bf9dcf..c20f3526c69103aa9da24c5a0f525c1885c6ac91
  <h2><%= t 'user.account.my settings' %></h2>
  <%= error_messages_for 'user' %>
- <% form_for :user, @user do |f| %>
+ <% form_for :user, :html => { :multipart => true } do |f| %>
  <table id="accountForm">
-   <tr><td class="fieldName"><%= t 'user.new.display name' %></td><td><%= f.text_field :display_name %></td></tr>
-   <tr><td class="fieldName"><%= t 'user.new.email address' %></td><td><%= f.text_field :email, {:size => 50, :maxlength => 255} %> <span class="minorNote"><%= t 'user.account.email never displayed publicly' %></span></td></tr>
-   <tr><td class="fieldName" style="padding-bottom:0px;"><%= t 'user.new.password' %></td><td style="padding-bottom:0px;"><%= f.password_field :pass_crypt, {:value => '', :size => 30, :maxlength => 255} %></td></tr>
-   <tr><td class="fieldName" style="padding-bottom:0px;"><%= t 'user.new.confirm password' %></td><td><%= f.password_field :pass_crypt_confirmation, {:value => '', :size => 30, :maxlength => 255} %></td></tr>
-   <tr><td class="fieldName" ><%= t 'user.account.openid.openid' %></td><td style="padding-bottom:0px;"><%= f.text_field :openid_url %> (<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>) </td></tr>
-   <tr>
-   <td class="fieldName" valign="top"><%= t 'user.account.public editing.heading' %></td>
-   <td>
- <% if @user.data_public? %>
-   <%= t 'user.account.public editing.enabled' %> <span class="minorNote">(<a href="<%= t 'user.account.public editing.enabled link' %>" target="_new"><%= t 'user.account.public editing.enabled link text' %></a>)</span>
- <% else %>
-   <%= t 'user.account.public editing.disabled' %><span class="minorNote">(<a href="#public"><%= t 'user.account.public editing.disabled link text' %></a>)</span>
- <% end %>
-   </td>
+   <tr>
+     <td class="fieldName"><%= t 'user.new.display name' %></td>
+     <td><%= f.text_field :display_name %></td>
+   </tr>
 -
+   <tr>
+     <td class="fieldName" style="padding-bottom:0px;"><%= t 'user.account.current email address' %></td>
+     <td style="padding-bottom:0px;"><%= @user.email %> <span class="minorNote"><%= t 'user.account.email never displayed publicly' %></span></td>
+   </tr>
+   <tr>
+     <td class="fieldName"><%= t 'user.account.new email address' %></td>
+     <td><%= f.text_field :new_email, {:size => 50, :maxlength => 255} %> <span class="minorNote"><%= t 'user.account.email never displayed publicly' %></span></td>
+   </tr>
+   <tr>
+     <td class="fieldName" style="padding-bottom:0px;"><%= t 'user.new.password' %></td>
+     <td style="padding-bottom:0px;"><%= f.password_field :pass_crypt, {:value => '', :size => 30, :maxlength => 255} %></td>
+   </tr>
+   <tr>
+     <td class="fieldName"><%= t 'user.new.confirm password' %></td>
+     <td><%= f.password_field :pass_crypt_confirmation, {:value => '', :size => 30, :maxlength => 255} %></td>
+   </tr>
++  <tr>
++    <td class="fieldName" ><%= t 'user.account.openid.openid' %></td>
++    <td style="padding-bottom:0px;"><%= f.text_field :openid_url %> (<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>) </td>
++</tr>
+   <tr>
+     <td class="fieldName" valign="top"><%= t 'user.account.public editing.heading' %></td>
+     <td>
+       <% if @user.data_public? %>
+         <%= t 'user.account.public editing.enabled' %> <span class="minorNote">(<a href="<%= t 'user.account.public editing.enabled link' %>" target="_new"><%= t 'user.account.public editing.enabled link text' %></a>)</span>
+       <% else %>
+         <%= t 'user.account.public editing.disabled' %> <span class="minorNote">(<a href="#public"><%= t 'user.account.public editing.disabled link text' %></a>)</span>
+       <% end %>
+     </td>
+   </tr>
+   <tr>
+     <td class="fieldName" valign="top"><%= t 'user.account.profile description' %></td>
+     <td><%= f.text_area :description, :rows => '5', :cols => '60' %></td>
    </tr>
  
-   <tr><td class="fieldName" valign="top"><%= t 'user.account.profile description' %></td><td><%= f.text_area :description, :rows => '5', :cols => '60' %></td></tr>
+   <tr>
+     <td class="fieldName" valign="top"><%= t 'user.account.preferred languages' %></td>
+     <td><%= f.text_field :languages %></td>
+   </tr>
  
-   <tr><td class="fieldName" valign="top"><%= t 'user.account.preferred languages' %></td><td><%= f.text_field :languages %></td></tr>
+   <tr>
+     <td class="fieldName" valign="top">
+       <%= t 'user.account.image' %>
+     </td>
+     <td valign="top">
+       <% if @user.image.nil? %>
+         <%= hidden_field_tag "image_action", "new" %>
+         <%= t 'user.account.new image' %><br /><%= file_column_field "user", "image" %>
+       <% else %>
+         <table>
+           <tr>
+             <td rowspan="3" valign="top"><%= image_tag url_for_file_column(@user, "image") %></td>
+             <td><%= radio_button_tag "image_action", "keep", true %></td>
+             <td><%= t 'user.account.keep image' %></td>
+           </tr>
+           <tr>
+             <td><%= radio_button_tag "image_action", "delete" %></td>
+             <td><%= t 'user.account.delete image' %></td>
+           </tr>
+           <tr>
+             <td><%= radio_button_tag "image_action", "new" %></td>
+             <td><%= t 'user.account.replace image' %><br /><%= file_column_field "user", "image", :onchange => "$('image_action_new').checked = true" %></td>
+           </tr>
+         </table>
+       <% end %>
+     </td>
+   </tr>
  
-   <tr id="homerow" <% unless @user.home_lat and @user.home_lon %> class="nohome" <%end%> ><td class="fieldName"><%= t 'user.account.home location' %></td><td><em class="message"><%= t 'user.account.no home location' %></em><span class="location"><%= t 'user.account.latitude' %> <%= f.text_field :home_lat, :size => 20, :id => "home_lat" %><%= t 'user.account.longitude' %><%= f.text_field :home_lon, :size => 20, :id => "home_lon" %></span></td></tr>
+   <tr id="homerow" <% unless @user.home_lat and @user.home_lon %> class="nohome" <%end%> >
+     <td class="fieldName"><%= t 'user.account.home location' %></td>
+     <td><em class="message"><%= t 'user.account.no home location' %></em><span class="location"><%= t 'user.account.latitude' %> <%= f.text_field :home_lat, :size => 20, :id => "home_lat" %><%= t 'user.account.longitude' %><%= f.text_field :home_lon, :size => 20, :id => "home_lon" %></span></td>
+   </tr>
  
-   <tr><td></td><td>
-   <p><%= t 'user.account.update home location on click' %> <input type="checkbox" value="1" <% unless @user.home_lat and @user.home_lon %> checked="checked" <% end %> id="updatehome" /> </p>
-   <div id="map" style="border:1px solid black; position:relative; width:500px; height:400px;"></div>
-   </td></tr>
+   <tr>
+     <td></td>
+     <td>
+       <p><%= t 'user.account.update home location on click' %> <input type="checkbox" value="1" <% unless @user.home_lat and @user.home_lon %> checked="checked" <% end %> id="updatehome" /> </p>
+       <div id="map" style="border:1px solid black; position:relative; width:500px; height:400px;"></div>
+     </td>
+   </tr>
    
-   <tr><td></td><td align=right><br/><%= submit_tag t('user.account.save changes button') %></td></tr>
+   <tr>
+     <td></td>
+     <td align=right><br/><%= submit_tag t('user.account.save changes button') %></td>
+   </tr>
  </table>
- <br/>
  <% end %>
  
  <%= render :partial => 'friend_map' %>
  <% unless @user.data_public? %>
  <a name="public"></a>
  <h2><%= t 'user.account.public editing note.heading' %></h2>
index 16d820b69c48144421900d37806b0143f35371cf,9ff10007905d26348d9397f2c0ae3af6c9689a3f..269bbc0cc6aeff99512132836d8603ddb16701b6
@@@ -1,4 -1,4 +1,4 @@@
 -<h1><%= t 'user.login.heading' %></h1>
 +      <h1><%= t 'user.login.heading' %></h1>
  
  <p><%= t 'user.login.please login', :create_user_link => link_to(t('user.login.create_account'), :controller => 'user', :action => 'new', :referer => params[:referer]) %></p>
  
@@@ -6,16 -6,9 +6,12 @@@
  <%= hidden_field_tag('referer', h(params[:referer])) %>
  <table id="loginForm">
    <tr><td class="fieldName"><%= t 'user.login.email or username' %></td><td><%= text_field('user', 'email',{:size => 28, :maxlength => 255, :tabindex => 1}) %></td></tr>
--  <tr><td class="fieldName"><%= t 'user.login.password' %></td><td><%= password_field('user', 'password',{:size => 28, :maxlength => 255, :tabindex => 2}) %> <span class="minorNote">(<%= link_to t('user.login.lost password link'), :controller => 'user', :action => 'lost_password' %>)</span></td></tr>
- <tr><td colspan="2">&nbsp;<!--vertical spacer--></td></tr>
- <tr><td></td><td align="right"><%= submit_tag t('user.login.login_button'), :tabindex => 3 %></td></tr>
- <tr><td colspan = "3"><h4><%= t 'user.login.alternatively' %></h4></td></tr>
 -  <tr><td class="fieldName"><label for="remember_me">Remember me:</label></td><td><%= check_box_tag "remember_me", "yes", false, :tabindex => 3 %></td></tr> 
++  <tr><td class="fieldName"><%= t 'user.login.password' %></td><td><%= password_field('user', 'password',{:size => 28, :maxlength => 255, :tabindex => 2}) %></td><td> <span class="minorNote">(<%= link_to t('user.login.lost password link'), :controller => 'user', :action => 'lost_password' %>)</span></td></tr>
++<tr><td colspan = "3"><h4><I><%= t 'user.login.alternatively' %></I></h4></td></tr>
++<tr><td class="fieldName"><%= t 'user.login.openid' %></td><td><%= text_field('user', 'openid_url',{:size => 28, :maxlength => 255, :tabindex => 3}) %></td><td> <span class="minorNote">(<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>)</span></td></tr>
 +
- <tr><td colspan="2"><%= t 'user.login.openid description' %> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>)</td></tr>
- <tr><td class="fieldName"><%= t 'user.login.openid' %></td><td><%= text_field('user', 'openid_url',{:size => 28, :maxlength => 255, :tabindex => 3}) %></td></tr>
- <tr><td colspan="2">&nbsp;<!--vertical spacer--></td></tr>
- <tr><td></td><td align="right"><%= submit_tag t('user.login.login_button'), :tabindex => 3 %></td></tr>
++  <tr><td colspan="2">&nbsp;<!--vertical spacer--></td></tr>
+   <tr><td colspan="2">&nbsp;<!--vertical spacer--></td></tr>
 -  <tr><td></td><td align="right"><%= submit_tag t('user.login.login_button'), :tabindex => 3 %></td></tr>
++  <tr><td class="fieldName"><label for="remember_me">Remember me:</label></td><td><%= check_box_tag "remember_me", "yes", false, :tabindex => 3 %></td><td align=right><%= submit_tag t('user.login.login_button'), :tabindex => 3 %></td></tr>
  </table>
  <% end %>
diff --combined config/locales/en.yml
index 58fca9498a6c6fd6b2be1b18dbb8c4faeaad40d4,e75cc22069d0680ec9ec7246cf01c838e3196ce5..b358cf8d3b9d1b684f4673f8d266e60d17119632
@@@ -313,7 -313,7 +313,7 @@@ en
        save_button: "Save"
        marker_text: Diary entry location
      view:
-       title: "Users' diaries | {{user}}"
+       title: "{{user}}'s diary | {{title}}"
        user_title: "{{user}}'s diary"
        leave_a_comment: "Leave a comment"
        login_to_leave_a_comment: "{{login_link}} to leave a comment"
        embeddable_html: "Embeddable HTML"
        licence: "Licence"
        export_details: 'OpenStreetMap data is licensed under the <a href="http://creativecommons.org/licenses/by-sa/2.0/">Creative Commons Attribution-ShareAlike 2.0 license</a>.'
+       too_large:
+         heading: "Area Too Large"
+         body: "This area is too large to be exported as OpenStreetMap XML Data. Please zoom in or select a smaller area."
        options: "Options"
        format: "Format"
        scale: "Scale"
      sign_up: sign up
      sign_up_tooltip: Create an account for editing
      view: View
-     view_tooltip: View maps
+     view_tooltip: View the map
      edit: Edit
-     edit_tooltip: Edit maps
      history: History
-     history_tooltip: Changeset history
      export: Export
      export_tooltip: Export map data
      gps_traces: GPS Traces
-     gps_traces_tooltip: Manage traces
+     gps_traces_tooltip: Manage GPS traces
      user_diaries: User Diaries
      user_diaries_tooltip: View user diaries
      tag_line: The Free Wiki World Map
        create_account: "create an account"
        email or username: "Email Address or Username:"
        password: "Password:"
 +      openid: "OpenID:"
 +      openid description: "Use your OpenID to login"
 +      alternatively: "Alternatively"
        lost password link: "Lost your password?"
        login_button: "Login"
        account not active: "Sorry, your account is not active yet.<br />Please click on the link in the account confirmation email to activate your account."
        auth failure: "Sorry, could not log in with those details."
 +      openid missing provider: "Sorry, could not contact your OpenID provider"
 +      openid invalid: "Sorry, your OpenID seems misformed"
      lost_password:
        title: "Lost password"
        heading: "Forgotten Password?"
        display name description: "Your publicly displayed username. You can change this later in the preferences."
        password: "Password:"
        confirm password: "Confirm Password:"
 +      openID: "OpenID:"
 +      openID description: '(Optional) If you have an <a href="http://wiki.openstreetmap.org/wiki/openID">OpenID</a> you can associate it with this account to login'
        signup: Signup
        flash create success message: "User was successfully created. Check your email for a confirmation note, and you will be mapping in no time :-)<br /><br />Please note that you will not be able to login until you've received and confirmed your email address.<br /><br />If you use an antispam system which sends confirmation requests then please make sure you whitelist webmaster@openstreetmap.org as we are unable to reply to any confirmation requests."
      no_such_user:
        ago: "({{time_in_words_ago}} ago)"
        email address: "Email address:"
        created from: "Created from:"
-       user image heading: User image
-       delete image: Delete Image
-       upload an image: Upload an image
-       add image: Add Image
        description: Description
        user location: User location
        no home location: "No home location has been set."
        m away: "{{count}}m away"
        nearby users: "Nearby users:"
        no nearby users: "There are no users who admit to mapping nearby yet."
-       change your settings: change your settings
        my_oauth_details: "View my OAuth details"
        role:
          administrator: "This user is an administrator"
      account:
        title: "Edit account"
        my settings: My settings
+       current email address: "Current Email Address:"
+       new email address: "New Email Address:"
        email never displayed publicly: "(never displayed publicly)"
 +      openid:
 +        openid: "OpenID:"
 +        link: "http://wiki.openstreetmap.org/wiki/OpenID"
 +        link text: "what is this?"
        public editing:
          heading: "Public editing:"
          enabled: "Enabled. Not anonymous and can edit data."
          text: "Currently your edits are anonymous and people cannot send you messages or see your location. To show what you edited and allow people to contact you through the website, click the button below. <b>Since the 0.6 API changeover, only public users can edit map data</b>. (<a href=\"http://wiki.openstreetmap.org/wiki/Anonymous_edits\">find out why</a>).<ul><li>Your email address will not be revealed by becoming public.</li><li>This action cannot be reversed and all new users are now public by default.</li></ul>"
        profile description: "Profile Description:"
        preferred languages: "Preferred Languages:"
+       image: "Image:"
+       new image: "Add an image"
+       keep image: "Keep the current image"
+       delete image: "Remove the current image"
+       replace image: "Replace the current image"
        home location: "Home Location:"
        no home location: "You have not entered your home location."
        latitude: "Latitude:"
        overlays:
          maplint: Maplint
      site:
+       edit_tooltip: Edit the map
+       edit_disabled_tooltip: Zoom in to edit the map
        edit_zoom_alert: You must zoom in to edit the map
-       history_zoom_alert: You must zoom in to see the editing history
+       history_tooltip: View edits for this area
+       history_disabled_tooltip: Zoom in to view edits for this area
+       history_zoom_alert: You must zoom in to view edits for this area