2008年12月7日日曜日

とりあえず、パスワードを忘れた時の処理を追加(?)

とりあえず、Restful Authentication with all the bells and whistlesを、順番に追っかけてみましょうかね。

app/models/user.mailer.rbに、
  • forgot_password
  • reset_password
の、2つのメソッドを追加。
こんな感じ。
  def forgot_password(user)
    setup_email(user)
    @subject    += 'You have requested to change your password'
    @body[:url]  = "http://localhost:3000/reset_password/#{user.password_reset_code}"
  end

  def reset_password(user)
    setup_email(user)
    @subject    += 'Your password has been reset.'
  end
はい。
次に、app/models/user_observer.rbに追記。
UserMailer.deliver_forgot_password(user) if user.recently_forgot_password?
UserMailer.deliver_reset_password(user) if user.recently_reset_password?
で、次に、って、アレ?
permission云々って、書いてあるな...

んー、飛ばすwww

で、app/models/user.rbに
  • forgot_password
  • reset_password
  • recently_forgot_password?
  • recently_reset_password?
  • make_password_reset_code (protected)
を追加。
以下、コード。 
 # Forgot password
  def forgot_password
    @forgotten_password = true
    self.make_password_reset_code
  end
  # Reset password
  def reset_password
    # First update the password_reset_code before setting the
    # reset_password flag to avoid duplicate email notifications.
    update_attribute(:password_reset_code, nil)
    @reset_password = true
  end

  #used in user_observer
  def recently_forgot_password?
    @forgotten_password
  end
 
  def recently_reset_password?
    @reset_password
  end
(略)
protected
(略)
    def make_password_reset_code
        self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end
こんな感じですか。
次。lib/authenticated_system.rbを書き換えてるようですが、permission絡みみたいなんで、飛ばす。

次は、app/controller/users_controller.rb。
とりあえず、indexとeditとupdateのメソッドの追加。
  def index
    @users = User.find(:all)
  end

  # render edit.html.erb
  def edit
    @user = current_user
  end

  def update
    @user = User.find(current_user)
    if @user.update_attributes(params[:user])
      flash[:notice] = "User updated"
      redirect_to :action => 'show', :id => current_user
    else
      render :action => 'edit'
    end
  end
ほい。
updateは、viewが無いんだね。

そりゃそうだwww

次は、app/controllers/sessions_controller.rb。
...なんかいるか?
...飛ばすwww

次。
ん?
passwords_controller.rb?
accounts_controller.rb?
roles_controller.rb?

ん?
飛ばすwww

んで?
application.html.erbも、飛ばす。
なんか、role絡みのコードが入ってるし。後だね、後。

次は、app/views/passwords/edit.html.erb。
あれ?
app/views/passwords/new.html.erbとapp/views/accounts/edit.html.erbも、とりあえず、飛ばそう。

あ、そうか!
これ、password変更のロジックだな。

おk。後回しwww

んで、...
なんか、role関係のviewが続くんで、まとめて飛ばし。

次。
やっとだよwww
app/views/sesisons/new.html.erb。
まあ、別にどうでも良いんだけど、とりあえず、users_controllerのnewアクションへのリンクを足す。

new_user_pathなんて云う書き方、今、はじめて気が付いたwww
"アクション名_コントローラ名(単数系)_path"で、そのurlが生成できるんだな。
イヤ、便利っぽくない?www
後で書き換えよう:-)

app/views/user_mailer/activation.erb
<%= h @user.real_name %>, your account has been activated.  Welcome aboard!

  <%=h @url %>
@user.loginは、loginを削除してしまっているんで、real_nameメソッドを使って、本名を使うようにした。
後述のforgot_password.erbとreset_password.erbも一緒。

app/views/user_mailer/forgot_password.erb
<%= h @user.real_name %>, your account has been activated.  To visit the site, follow the link below:
<%= @url %> 
app/views/user_mailer/reset_password.erb
<%= h @user.real_name %>, Your password has been reset

こんな感じ?

app/views/user_mailer/signup_notification.html.erbは、real_nameとemailを使って、passwordは送らないようにしましょうか。再発行できるんだから、ね?
Your account has been created.

  Real Name: <%= h @user.real_name %>
  Email: <%= h @user.email %>
  Password: <%= h @user.password %>

Visit this url to activate your account:

  <%= h @url %>

って、あれ?
だとすると、さっき飛ばしたロジック、入れとかないと、マズいよね?
じゃあ、入れましょうか。
$ script/generate controller Passwords
$ script/generate controller Accounts
んで、
app/controllers/passwords_controller.rb
class PasswordsController <>
   skip_before_filter :login_required :only => [:new, :creat]
  :not_logged_in_required, :only => [:new, :create]
  
  # Enter email address to recover password 
  def new
  end
 
  # Forgot password action
  def create    
    return unless request.post?
  if @user = User.find_for_forget(params[:email])
      @user.forgot_password
      @user.save      
      flash[:notice] = "A password reset link has been sent to your email address."
      redirect_to login_path
    else
      flash[:notice] = "Could not find a user with that email address."
      render :action => 'new'
    end  
  end
  
  # Action triggered by clicking on the /reset_password/:id link recieved via email
  # Makes sure the id code is included
  # Checks that the id code matches a user in the database
  # Then if everything checks out, shows the password reset fields
  def edit
    if params[:id].nil?
      render :action => 'new'
      return
    end
    @user = User.find_by_password_reset_code(params[:id]) if params[:id]
    raise if @user.nil?
  rescue
    logger.error "Invalid Reset Code entered."
    flash[:notice] = "Sorry - That is an invalid password reset code. Please check your code and try again. (Perhaps your email client inserted a carriage return?)"
    #redirect_back_or_default('/')
    redirect_to new_user_path
  end
    
  # Reset password action /reset_password/:id
  # Checks once again that an id is included and makes sure that the password field isn't blank
  def update
    if params[:id].nil?
      render :action => 'new'
      return
    end
    if params[:password].blank?
      flash[:notice] = "Password field cannot be blank."
      render :action => 'edit', :id => params[:id]
      return
    end
    @user = User.find_by_password_reset_code(params[:id]) if params[:id]
    raise if @user.nil?
    return if @user unless params[:password]
      if (params[:password] == params[:password_confirmation])
      #Uncomment and comment lines with @user to have the user logged in after reset - not recommended
        #self.current_user = @user #for the next two lines to work
        #current_user.password_confirmation = params[:password_confirmation]
        #current_user.password = params[:password]
        #@user.reset_password
    #flash[:notice] = current_user.save ? "Password reset" : "Password not reset"
    @user.password_confirmation = params[:password_confirmation]
    @user.password = params[:password]
    @user.reset_password        
    flash[:notice] = @user.save ? "Password reset." : "Password not reset."
      else
        flash[:notice] = "Password mismatch."
        render :action => 'edit', :id => params[:id]
      return
      end  
      redirect_to login_path
  rescue
    logger.error "Invalid Reset Code entered"
    flash[:notice] = "Sorry - That is an invalid password reset code. Please check your code and try again. (Perhaps your email client inserted a carriage return?)"
    redirect_to new_user_path
  end
    
end
ほぼベタ張りwww
しっかし、長えな、これwww
ロジックがどう動いているのか、サッパリ理解してないぞwww

続いて、app/controllers/accounts_controller.rb。
class AccountsController <>
  skip_before_filter :login_required :only => [:show]
  before_filter :not_logged_in_required, :only => :show
 
  # Activate action
  def show
    # Uncomment and change paths to have user logged in after activation - not recommended
    #self.current_user = User.find_and_activate!(params[:id])
  User.find_and_activate!(params[:id])
    flash[:notice] = "Your account has been activated! You can now login."
    redirect_to login_path
  rescue User::ArgumentError
    flash[:notice] = 'Activation code not found. Please try creating a new account.'
    redirect_to new_user_path 
  rescue User::ActivationCodeNotFound
    flash[:notice] = 'Activation code not found. Please try creating a new account.'
    redirect_to new_user_path
  rescue User::AlreadyActivated
    flash[:notice] = 'Your account has already been activated. You can log in below.'
    redirect_to login_path
  end
 
  def edit
  end
  
  # Change password action  
  def update
  return unless request.post?
    if User.authenticate(current_user.email, params[:old_password])
      if ((params[:password] == params[:password_confirmation]) && !params[:password_confirmation].blank?)
        current_user.password_confirmation = params[:password_confirmation]
        current_user.password = params[:password]        
    if current_user.save
          flash[:notice] = "Password successfully updated."
          redirect_to root_path #profile_url(current_user.login)
        else
          flash[:error] = "An error occured, your password was not changed."
          render :action => 'edit'
        end
      else
        flash[:error] = "New password does not match the password confirmation."
        @old_password = params[:old_password]
        render :action => 'edit'      
      end
    else
      flash[:error] = "Your old password is incorrect."
      render :action => 'edit'
    end 
  end
  
end
つうか、ね?
newアクション、いるのか?www
あと、User.authenticateでcurrent_user.loginを放り込んでるから、current_user.emailに変更ね。

んじゃあ、viewも作りましょうか。
app/views/passwords/edit.html.erb
<% form_tag url_for(:action => "update", :id => params[:id]) do %>
    Password:<br />
    <%= password_field_tag :password %><br />
    Confirm Password:<br />
    <%= password_field_tag :password_confirmation %><br />
    <%= submit_tag "Reset Your Password" %>
<% end %>

app/views/passwords/new.html.erb
<h2>Forgot Password</h2>
<% form_tag url_for(:action => 'create') do %>
    What is the email address used to create your account?<br />
    <%= text_field_tag :email, "", :size => 50 %><br />
    <%= submit_tag 'Reset Password' %>
<% end %>

app/views/accounts/edit.html.erb
<% form_tag url_for(:action => "update") do %>
  <p><label for="old_password" class="block">Old Password</label><br />
  <%= password_field_tag 'old_password', @old_password, :size => 45 %></p>
 
  <p><label for="password" class="block">New Password</label><br />
  <%= password_field_tag 'password', {}, :size => 45 %><br />
  <small>Between 4 and 40 characters</small></p>
 
  <p><label for="password_confirmation"  class="block">Confirm new password</label><br />
  <%= password_field_tag 'password_confirmation', {}, :size => 45 %></p>
 
  <%= submit_tag 'Change password' %>
 
<% end %>
な感じで。
何も変えてないぞwwww

んで、config/routes.rbに以下を追記。
map.forgot_password '/forgot_password', :controller => 'passwords', :action => 'new'
map.reset_password '/reset_password/:id', :controller => 'passwords', :action => 'edit'
map.change_password '/change_password', :controller => 'accounts', :action => 'edit'

map.resources :users, :member => { :enable => :put } do |users|
 users.resource :account
 users.resources :roles
end

map.resource :password
こんな感じで、いいんじゃね?
ね?

0 件のコメント: