前回はLiftの基本的な使い方を紹介し、簡単なウェブアルバムサービスを作りました。今回はこのウェブアルバムサービスを拡張しながら、より発展的なLiftの機能やテクニックを紹介していきます。

 今回紹介することは次の通りです。

  • ユーザーアカウントの扱い方
  • Boxモナドを使ったテクニック
  • Ajaxの実装方法

 前回作ったウェブアルバムサービスではすべてのアルバムが公開されました。しかし、実際は家族や友人だけにみてほしい場合もありますよね。例えば結婚式やオフ会での写真を参加者だけで共有したり、子供の成長の記録を家族や親戚で共有できたらとても便利です。

 今回は前回のウェブアルバムサービスを拡張し、いろいろな人に使ってもらえるようアカウントを導入し、アルバムごとの権限を導入し、ユーザーが設定できるようにします。

1. アカウントの導入

 まずはユーザーアカウントを導入します。Liftにはユーザーアカウントを管理するためのとても便利なMetaMegaProtoUserというトレイトが用意されています。このトレイトはアカウントを保持するマッパーを作るだけでなく、アカウント管理に必要なウェブインターフェイスまでも自動で生成します。

1.1 MetaMegaProtoUserトレイトでアカウントクラスUserを作成

 実際に使ってみましょう。使い方は基本的には前回アルバムや写真のマッパーを作ったLongKeyedMapperトレイトと同じです。

リスト1●Userクラス
  package jp.co.itpl.model
  
  import net.liftweb.mapper._
  import net.liftweb.common.{Box, Full, Empty, Failure}
  
  class User extends MegaProtoUser[User] {
    override def getSingleton = User
  
    //(1) ユーザー名を表すnameというフィールドを追加
    object name extends MappedString(this, 256)
  }
  
  object User extends User with MetaMegaProtoUser[User] {
    //(2) アカウントに関するウェブインターフェイスの共通テンプレート
    override def screenWrap = Full {
      <lift:surround with="default" at="content"><lift:bind /></lift:surround>
    }
  
    //(3) 新規アカウント作成時にメールによるバリデーションを省略する
    override def skipEmailValidation = true
  
    //(4) パスワードを忘れたときの画面は不要とする
    override def lostPasswordMenuLoc = Empty
  
    //(5) アカウントを新規作成するときに必要な情報
    override def signupFields = List(name, email, password)
  }

 MetaMegaProtoUserトレイトを継承すると姓、名、メールアドレス、国、パスワードなどのフィールドがデフォルトで用意されます。もしフィールドを追加したければ(1)のように追加できます。また、必要ないフィールドがあれば(5)のようにフィールドのリストを指定することもできます。今回はnameというフィールドを追加し、名前、メールアドレス、パスワードを使うものとします。

 また、今回は簡単のためアカウント作成時のメールによるチェックやパスワードを忘れたときのための画面は省略します。(3)、(4)

1.2 ログイン済みユーザーだけがアルバムサービスを利用できるように設定

 今まではユーザーアカウントというものはなく、誰でもサービスを利用できましたが、今後はログインしているユーザーだけがこのサービスを利用するようにしましょう。これはBootクラスを少し書き換えるだけで実現できます。次のようにBootクラスを書き換えてください。

リスト2●Bootクラス(Boot.scala)
 ...
  import jp.co.itpl.model.User
 ...
 class Boot {
   def boot {
 
      ...
      val entries = List(
        Menu("ホーム") / "index"
          //(1) ログイン時のみ見せるサイトはUser.loginFirstという認証を登録
        , Menu("アルバム一覧") / "list" >> User.loginFirst
        , Menu("photolist") / "photo" / "list" >> Hidden >> User.loginFirst
        , Menu("アップロード") / "photo" / "upload" >> Hidden >> User.loginFirst
      ) ++ User.sitemap //(2) ユーザー管理のためのウェブ画面を追加
 
      ...
      //(3) マッパーを定義したときはスキーマ登録する。
      Schemifier.schemify(true, Log.infoF _, Album, Photo, User)
   }
 }

 サイトマップの各ページを表すMenuクラスは >> メソッドで付加情報となるLocParam型の値を付けることができます。ここではアルバム一覧、写真一覧、アップロードの画面に対してUser.loginFirstを付け加えました。これでログインしていないユーザーからはアクセスできないようになりました。

 また、ユーザー管理のためのウェブ画面のsitemapはUser.sitemapで参照できます。結合しておきましょう。(2)

 前回同様、新しくマッパーを定義したときは(3)のようにスキーマ登録しておきましょう。

 ここまでを保存し、ビルドして下さい。そしてローカルで起動して見ましょう。

 $ mvn package
 $ mvn jetty:run

 ブラウザでlocalhost:8080/にアクセスしてみてください。

図1●(1)home画面
図1●(2)ログイン画面
図1●(3)サインアップ画面
図1●(1)home画面、(2)ログイン画面、(3)サインアップ画面
[画像のクリックで拡大表示]

 メニューに今まであった「アルバム一覧」という項目がなくなり、代わりに「ログイン」と「新規ユーザー作成」というメニューが追加されています(図1)。驚くべきことにそれぞれに対応するページがもうできています!あなたはユーザーマッパーを作っただけですがアカウントに関するページももうできてしまいました。

(注): Lift用の日本語のプロパティファイルが設定されていない場合は項目名が英語表記で表示されるかもしれません。日本語設定用のプロパティファイルlift-core_ja.propertiesをresource/i18n/に配置することで日本語対応をすることができます。プロパティファイルの作成についてはここでは割愛します。

 さて、早速ログインをしたいところですが、まだユーザーアカウントは一つも存在しないため、新規ユーザーを作ることにしましょう。名前、メールアドレス、パスワードを決めて新規ユーザー作成画面から作成しましょう。ここでは名前が今井宜洋、メールアドレスがy.imai@ocaml.jpとして新規ユーザーを作成します。

 するとログインした状態となり、メニューには「アルバム一覧」の他に「ログアウト」「ユーザー情報の変更」「パスワードの変更」といった項目が現れます(図2)。とても簡単にユーザー認証を導入できてしまいました。

図2●(1)home画面
図2●(2)ユーザー情報の変更画面
図2●(3)パスワードの変更画面
図2●(1)home画面、(2)ユーザー情報の変更画面、(3)パスワードの変更画面
[画像のクリックで拡大表示]