LocomotiveCMS + Devise を使って作ったログインページのデザインをCMS側でできるようにする(2)


[前回](http://qiita.com/hal_sk/items/50c45f764cb95615dd59)の続きです。

前回の記事では、Devise のコントローラーを以下のように拡張しましたが、new メソッドしか実装していませんでした。その為 /users/sign_up からフォームを送信すると、また味気ない画面に戻ってしまいます。

```ruby:app/controllers/users/registration_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  include Locomotive::Routing::SiteDispatcher
  include Locomotive::Render
  include Locomotive::ActionController::LocaleHelpers
  before_filter :require_site
  def new
    build_resource({})
    @page ||= self.locomotive_page('/userregistration')
    respond_to do |format|
      format.html {
         render :inline => @page.render(self.locomotive_context({ 'user' => resource}))
      }
    end
  end
  def create
    super # ←こっちを実装しなくてはならない
  end
end
```

#createメソッドの実装

いきなり結論ですが、Devise 側の処理と LocomotiveCMS の処理を組み合わせた所、以下のようなメソッドの追加が必要になりました。

(2014/04/08追記)もしかしたら、create 部分の実装をいじる必要はなくて、単純に flash メッセージと after_sign_up_path_for を工夫すれば良い気もしてきました。どうもこの辺は気持ち悪かったんですよね。実装方法変えたらあとで更新します。

```ruby:/app/controllers/users/registration_controller.rb
  def create
    build_resource(params.fetch(resource_name, {})) #パラメータから resource クラスを作成（Userクラスが入っている）

    resource_saved = resource.save #保存
    yield resource if block_given?
    if resource_saved   #ユーザ登録成功
      if resource.active_for_authentication?
        # メールによる確認なし
        sign_up(resource_name, resource)
        respond_with resource, location: after_sign_up_path_for(resource)
      else
        # メールによる確認あり
        @page = self.locomotive_page('/registrationconfirm')
        expire_data_after_sign_in!
        respond_to do |format|
          format.html {
             render :inline => @page.render(self.locomotive_context({}))
          }
        end
      end
    else
      # バリデーションエラー（再度入力フォームを呼び出す）
      @page ||= self.locomotive_page('/userregistration') 
      clean_up_passwords resource
      respond_to do |format|
        format.html {
           render :inline => @page.render(self.locomotive_context({ 'user' => resource, 'error' => devise_error_messages! }))
        }
      end
    end
  end
  # エラーメッセージを作って返す
  def devise_error_messages!
    return "" if resource.errors.empty?

    messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
    html = <<-HTML
    <div id="error_explanation">
      <h2>エラーが発生しました</h2>
      <ul>#{messages}</ul>
    </div>
    HTML

    html.html_safe
  end
  # セッションキーの削除
  def expire_data_after_sign_in!
    # session.keys will return an empty array if the session is not yet loaded.
    # This is a bug in both Rack and Rails.
    # A call to #empty? forces the session to be loaded.
    session.empty?
    session.keys.grep(/^devise\./).each { |k| session.delete(k) }
  end
  # サインアップ後のリダイレクト先
  def after_sign_up_path_for(resource)
    '/registrationfinished'
  end
```

# 入力フォームのテンプレート修正

createメソッド中の `render :inline => @page.render(self.locomotive_context({ 'user' => resource, 'error' => devise_error_messages! }))` という行が重要で、この画面で、ユーザーモデルとエラーメッセージをセットしてテンプレートのレンダリングをしています。
前回作った registration.liquid.haml でエラーメッセージの表示と、ユーザーIDを入力画面に表示するようにしておきましょう。

```haml:/app/views/pages/userregistration.liquid.haml
---
title: ユーザー登録
slug: userregistration
listed: false
published: true
cache_strategy: none
response_type: text/html
position: 4
---
{% extends 'parent' %}
{% block main %}
.row
  .large-8.columns
    %h1 ユーザー登録ページ
    .error {{error}}
    %form#new_user.new_user{"accept-charset" => "UTF-8", :action => "/users", :method => "post"}
      .hidden
        {% csrf_param %}
        %input{:name => "utf8", :type => "hidden", :value => "✓"}/
      %div
        %label{:for => "user_email"} Email
        %br/
        %input#user_email{:autofocus => "autofocus", :keyev => "true", :mouseev => "true", :name => "user[email]", :size => "30", :type => "email", :value => "{{user.email}}"}/
      %div
        %label{:for => "user_password"} Password
        %br/
        %input#user_password{:keyev => "true", :mouseev => "true", :name => "user[password]", :size => "30", :type => "password"}/
      %div
        %label{:for => "user_password_confirmation"} Password confirmation
        %br/
        %input#user_password_confirmation{:keyev => "true", :mouseev => "true", :name => "user[password_confirmation]", :size => "30", :type => "password"}/
      %div
        %input{:name => "commit", :type => "submit", :value => "Sign up"}/
{% endblock %}
```

`.error {{error}}` と、%input#user_email の`:value => "{{user.email}}"` という部分が追加した部分です。それぞれエラーメッセージと入力されたメールアドレスが入ります。

しかし、このままだと {{user.email}} をパースする際に Undefined method "to_liquid" と言われてエラーになります。 liquid テンプレートから呼べるようにする為に、User クラスに Liquid::Drop を継承させます。

```ruby:app/models/user.rb
class User < Liquid::Drop
  include Mongoid::Document
…
end
```

これで、入力時のエラーが表示され、入力した内容も引き継がれるようになりました。
![user_registration2.png](https://qiita-image-store.s3.amazonaws.com/0/1399/74b7c441-4000-6036-24cd-4844138964a5.png "user_registration2.png")

#メールアドレス確認画面

あとは、仮登録でのメールアドレス確認画面を作ります。


```HAML:/app/views/pages/registrationconfirm.liquid.haml
---
title: ユーザー登録メール送信済
slug: registrationconfirm
listed: false
published: true
cache_strategy: none
response_type: text/html
---
{% extends 'parent' %}
{% block main %}
.row
  .large-8.columns
    %h1 仮登録完了
    入力されたメールアドレスに確認のメールをお送りしました。メール本文に書かれているURLをクリックして登録を完了させてください。
{% endblock %}
```

これで、データ登録後、以下の画面が表示されるようになりました。

![registrationconfirm.png](https://qiita-image-store.s3.amazonaws.com/0/1399/bd79d0c6-86ed-e484-0a4b-679735de8e66.png "registrationconfirm.png")

#本登録完了画面

さて、あと一息です。メールで送られた本登録URLをクリックした際にも、ちゃんとしたページを表示するようにします。
本登録時に使われるコントローラーは、ConfirmationsController ですので、以下のファイルを作ります。

```ruby:app/controllers/users/confirmations_controller.rb
class Users::ConfirmationsController < Devise::ConfirmationsController
  protected
  # The path used after confirmation.
  def after_confirmation_path_for(resource_name, resource)
    if signed_in?
      '/registrationfinished'
    else
      new_session_path(resource_name)
    end
  end
end

```

after_confirmation_path_forをオーバーライドすることで、リダイレクト先を設定することが可能です。

確認時にこのコントローラーが呼ばれるように、routes ファイルを変更します。

```config/routes.rb
  devise_for :users, :controllers => {
    :registrations => 'users/registrations',
    :confirmations => 'users/confirmations'
  }
```

リダイレクト先となる、本登録完了画面を作成します。

```HAML:/app/views/pages/registrationfinished.liquid.haml
---
title: ユーザー登録完了
slug: registrationfinished
listed: false
published: true
cache_strategy: none
response_type: text/html
---
{% extends 'parent' %}
{% block main %}
.row
  .large-8.columns
    %h1 登録完了
    ユーザー登録が完了し、ログイン済です。
{% endblock %}
```

![registrationfinished.png](https://qiita-image-store.s3.amazonaws.com/0/1399/b2b6024a-1a76-ec43-df64-ba5868915adc.png "registrationfinished.png")

以上で、完了です！
次は、ログイン画面を修正しましょう。

[LocomotiveCMS + Devise を使って作ったログインページのデザインをCMS側でできるようにする(3)](http://qiita.com/hal_sk/items/92d653f47ddadb6d879c)

---
Locomotive 関連の記事一覧：
* [Locomotive Engine を Heroku で動かすまで](http://qiita.com/hal_sk/items/0512a4a9cb215fb016f5)
* [LocomotiveCMS でニュース記事一覧機能を作る](http://qiita.com/hal_sk/items/b72db4a288566e92096c)
* [LocomotiveCMS のページにユーザー認証機能を組み込む（Engineの拡張）](http://qiita.com/hal_sk/items/c6d4f5582d23bc4dd415)
* [LocomotiveCMS + Devise を使って作ったログインページのデザインをCMS側でできるようにする(1)](http://qiita.com/hal_sk/items/50c45f764cb95615dd59)
* [LocomotiveCMS + Devise を使って作ったログインページのデザインをCMS側でできるようにする(2)](http://qiita.com/hal_sk/items/c0f56736398f1154ddca)
* [LocomotiveCMS + Devise を使って作ったログインページのデザインをCMS側でできるようにする(3)](http://qiita.com/hal_sk/items/92d653f47ddadb6d879c)
* [LocomotiveCMS で、複数の Heroku インスタンスを使う](http://qiita.com/hal_sk/items/aba8d324638d205b2ce1)