LoginSignup
5
5

More than 5 years have passed since last update.

[Scala]SecureSocialで、すべてのビューを独自のビューで置き換えるメモ[Play]

Posted at

はじめに

Scala+Play+SecureSocialでTwitterにログイン認証してユーザ名とアバターを表示するメモの続きですが、この記事単独でも使えるかと思います。

なお、すべてのビューを表示して確認していないので、おかしなところもあるかもしれません。

play.pluginsファイルの修正

9997番を独自ビューのクラスMyViewに変更します。

conf/play.plugins
9994:securesocial.core.DefaultAuthenticatorStore
9995:securesocial.core.DefaultIdGenerator
9996:securesocial.core.providers.utils.DefaultPasswordValidator
#9997:securesocial.controllers.DefaultTemplatesPlugin
9997:service.MyView
9998:service.MyUserService
9999:securesocial.core.providers.utils.BCryptPasswordHasher
10000:securesocial.core.providers.TwitterProvider

MyViewsクラスの追加

MyViewsクラスはDefaultTemplatesPluginクラスを継承し、
DefaultTemplatesPluginのメソッドをすべてオーバーライドしてしまいます。

app/views/MyViews.scala
package views

import play.api.mvc._
import play.api.templates._
import play.api.data._

import securesocial.core._
import securesocial.controllers._
import securesocial.controllers.Registration.RegistrationInfo
import securesocial.controllers.PasswordChange.ChangeInfo


class MyViews(application: play.api.Application) extends DefaultTemplatesPlugin(application)
{
 /**
   * Returns the html for the login page
   * @param request
   * @tparam A
   * @return
   */
  override def getLoginPage[A](implicit request: Request[A], form: Form[(String, String)],
                               msg: Option[String] = None): Html =
  {
    views.html.myLogin(form, msg)
  }

  /**
   * Returns the html for the signup page
   *
   * @param request
   * @tparam A
   * @return
   */
  override def getSignUpPage[A](implicit request: Request[A], form: Form[RegistrationInfo], token: String): Html = {
    views.html.mySignUp(form, token)
  }

  /**
   * Returns the html for the start signup page
   *
   * @param request
   * @tparam A
   * @return
   */
  override def getStartSignUpPage[A](implicit request: Request[A], form: Form[String]): Html = {
    views.html.myStartSignUp(form)
  }

  /**
   * Returns the html for the reset password page
   *
   * @param request
   * @tparam A
   * @return
   */
  override def getStartResetPasswordPage[A](implicit request: Request[A], form: Form[String]): Html = {
    views.html.myStartResetPassword(form)
  }

  /**
   * Returns the html for the start reset page
   *
   * @param request
   * @tparam A
   * @return
   */
  override def getResetPasswordPage[A](implicit request: Request[A], form: Form[(String, String)], token: String): Html = {
    views.html.myResetPasswordPage(form, token)
  }

   /**
   * Returns the html for the change password page
   *
   * @param request
   * @param form
   * @tparam A
   * @return
   */
  override def getPasswordChangePage[A](implicit request: SecuredRequest[A], form: Form[ChangeInfo]): Html = {
    views.html.myPasswordChange(form)       
  }


  /**
   * Returns the email sent when a user starts the sign up process
   *
   * @param token the token used to identify the request
   * @param request the current http request
   * @return a String with the text and/or html body for the email
   */
  override def getSignUpEmail(token: String)(implicit request: RequestHeader): (Option[Txt], Option[Html]) = {
    (None, Some(views.html.mySignUpEmail(token)))
  }

  /**
   * Returns the email sent when the user is already registered
   *
   * @param user the user
   * @param request the current request
   * @return a String with the text and/or html body for the email
   */
  override def getAlreadyRegisteredEmail(user: Identity)(implicit request: RequestHeader): (Option[Txt], Option[Html]) = {
    (None, Some(views.html.myAlreadyRegisteredEmail(user)))
  }

  /**
   * Returns the welcome email sent when the user finished the sign up process
   *
   * @param user the user
   * @param request the current request
   * @return a String with the text and/or html body for the email
   */
  override def getWelcomeEmail(user: Identity)(implicit request: RequestHeader): (Option[Txt], Option[Html]) = {
    (None, Some(views.html.myWelcomeEmail(user)))
  }

  /**
   * Returns the email sent when a user tries to reset the password but there is no account for
   * that email address in the system
   *
   * @param request the current request
   * @return a String with the text and/or html body for the email
   */
  override def getUnknownEmailNotice()(implicit request: RequestHeader): (Option[Txt], Option[Html]) = {
    (None, Some(views.html.myUnknownEmailNotice(request)))
  }

  /**
   * Returns the email sent to the user to reset the password
   *
   * @param user the user
   * @param token the token used to identify the request
   * @param request the current http request
   * @return a String with the text and/or html body for the email
   */
  override def getSendPasswordResetEmail(user: Identity, token: String)(implicit request: RequestHeader): (Option[Txt], Option[Html]) = {
    (None, Some(views.html.myPasswordResetEmail(user, token)))
  }

  /**
   * Returns the email sent as a confirmation of a password change
   *
   * @param user the user
   * @param request the current http request
   * @return a String with the text and/or html body for the email
   */
  override def getPasswordChangedNoticeEmail(user: Identity)(implicit request: RequestHeader): (Option[Txt], Option[Html]) = {
    (None, Some(views.html.myPasswordChangedNotice(user)))
  }


  /**
   * Returns the html of the Not Authorized page
   *
   * @param request the current http request
   * @return a String with the text and/or html body for the email
   */
  override def getNotAuthorizedPage[A](implicit request: Request[A]): Html = {
     views.html.myNotAuthorizedPage()
  }

}

カスタムビューの作成

SecureSocialでデフォルトで表示されるビューを、独自ビューに書き換えていきます。

main.scala.html

メインのHtmlを定義して各ビューで使い回します。

app/views/main.scala.html
@(title: String)(content: Html)(implicit lang: Lang)
@import securesocial.core.providers.utils.RoutesHelper

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>@title</title>

    <link rel="stylesheet" media="screen" href="@RoutesHelper.bootstrapCssPath">
    <link rel="shortcut icon" type="image/png" href="@RoutesHelper.faviconPath">
    <link rel="stylesheet" media="screen" href="@RoutesHelper.customCssPath.getOrElse("")">
    <script src="@RoutesHelper.jqueryPath" type="text/javascript"></script>
</head>
<body>
    <div class="navbar navbar-fixed-top">
        <div class="navbar-inner">
            <div class="container">
                <span class="brand" >MyApp</span>
            </div>
        </div>
    </div>
    <div class="container" style="padding-top:30px">
        @content
    </div>
</body>
</html>

index.scala.html

ルートページ。
(ログインしていないときには、ログインページに飛ばされる。)

app/views/index.scala.html
@(user: String, provider: String, avatarUrl: Option[String])(implicit request: RequestHeader, lang: Lang)


@import helper._
@import securesocial.core.Registry
@import securesocial.core.AuthenticationMethod._
@import securesocial.core.providers.UsernamePasswordProvider.UsernamePassword


@main("Welcome") {
  <div class="page-header">
    <h1>Welcome!</h1>
  </div>

  Welcome @(user)@@@(provider)!

  @avatarUrl match {
    case Some(url) => { <image src="@url" /> }
    case None => {}
  }

  <p>
    <a href="/logout">Logout</a>
  </p>
}

alreadyRegisteredEmail.scala.html

app/views/myAlreadyRegisteredEmail.scala.html
@(user: securesocial.core.Identity)(implicit request: RequestHeader)

@import securesocial.core.IdentityProvider

<html>
<body>
  <p>Hello @user.firstName,</p>
  <p>You tried to sign up but you already have an account with us. If you don't remember your password please go
  <a href="@securesocial.core.providers.utils.RoutesHelper.startResetPassword().absoluteURL(IdentityProvider.sslEnabled)">here</a> to reset it.</p>
</body>
</html>

login.scala.html

app/views/myLogin.scala.html
@(loginForm: Form[(String,String)], errorMsg: Option[String] = None)(implicit request: RequestHeader, lang: Lang)

@import helper._
@import securesocial.core.Registry
@import securesocial.core.AuthenticationMethod._
@import securesocial.core.providers.UsernamePasswordProvider.UsernamePassword

@main("Login") {
  <div class="page-header">
    <h1>Login</h1>
  </div>

  @errorMsg.map { msg =>
    <div class="alert alert-error">
      @Messages(msg)
    </div>
  }

  @request.flash.get("success").map { msg =>
    <div class="alert alert-info">
      @msg
    </div>
  }

  @request.flash.get("error").map { msg =>
    <div class="alert alert-error">
      @msg
    </div>
  }

  @defining( Registry.providers.all.values.filter( _.id != UsernamePassword) ) { externalProviders =>
    @if( externalProviders.size > 0 ) {
      <div class="clearfix">
        <p>@Messages("securesocial.login.instructions")</p>
        <p>
          @for(p <- externalProviders) {
            @securesocial.views.html.provider(p.id)
          }
        </p>
      </div>
    }

    @Registry.providers.get(UsernamePassword).map { up =>
      <div class="clearfix">
        @if( externalProviders.size > 0 ) {
          <p>@Messages("securesocial.login.useEmailAndPassword")</p>
        } else {
          <p>@Messages("securesocial.login.useEmailAndPasswordOnly")</p>
        }
        @securesocial.views.html.provider("userpass", Some(loginForm))
      </div>
    }
  }
}

notAuthorizedPage.scala.html

app/views/myNotAuthorizedPage.scala.html
@()(implicit request: RequestHeader, lang: Lang)

@securesocial.views.html.main(Messages("securesocial.notAuthorized.title")) {

  <div class="page-header">
    <h1>@Messages("securesocial.notAuthorized.title")</h1>
  </div>
  <div class="alert alert-error">
    @Messages("securesocial.notAuthorized.message")
  </div>
}

passwordChange.scala.html

myPasswordChange.scala.html
@(passwordChangeForm:Form[securesocial.controllers.PasswordChange.ChangeInfo])(implicit request: RequestHeader, lang: Lang)

@import securesocial.core.providers.UsernamePasswordProvider
@import securesocial.core.IdentityProvider
@import helper._
@implicitFieldConstructor = @{ FieldConstructor(securesocial.views.html.inputFieldConstructor.f) }

@securesocial.views.html.main( Messages("securesocial.passwordChange.title") ) {
  <div class="page-header">
    <h1>@Messages("securesocial.passwordChange.title")</h1>
  </div>

  @request.flash.get("error").map { msg =>
    <div class="alert alert-error">
      @msg
    </div>
  }

  @if( request.flash.get("success").isDefined ) {
    <div class="alert alert-info">
      @request.flash.get("success").get
    </div>
    <div class="form-actions">
      <a class="btn" href="@securesocial.controllers.ProviderController.toUrl(request.session)">@Messages("securesocial.passwordChange.okButton")</a>
    </div>
  } else {
    <form action="@securesocial.core.providers.utils.RoutesHelper.handlePasswordChange.absoluteURL(IdentityProvider.sslEnabled)"
      class="form-horizontal"
      autocomplete= "off"
      method="POST"
    >
      <fieldset>
        @helper.inputPassword(
          passwordChangeForm("currentPassword"),
          '_label -> Messages("securesocial.passwordChange.currentPassword"),
          'class -> "input-xlarge"
        )
        @helper.inputPassword(
          passwordChangeForm("newPassword.password1"),
          '_label -> Messages("securesocial.passwordChange.newPassword1"),
          'class -> "input-xlarge"
        )
        @helper.inputPassword(
          passwordChangeForm("newPassword.password2"),
          '_label -> Messages("securesocial.passwordChange.newPassword2"),
          '_error -> passwordChangeForm.error("newPassword"),
          'class -> "input-xlarge"
        )
        <div class="form-actions">
          <button type="submit" class="btn btn-primary">@Messages("securesocial.passwordChange.changeButton")</button>
          <a class="btn" href="@securesocial.controllers.ProviderController.toUrl(request.session)">@Messages("securesocial.signup.cancel")</a>
        </div>
      </fieldset>
    </form>
  }
}

passwordChangedNotice.scala.html

app/views/myPasswordChangedNotice.scala.html
@(user: securesocial.core.Identity)(implicit request: RequestHeader)

@import securesocial.core.IdentityProvider

<html>
<body>
  <p>Hello @user.firstName,</p>
  <p>Your password was updated. Please log in using your new password by clicking
  <a href="@securesocial.core.providers.utils.RoutesHelper.login.absoluteURL(IdentityProvider.sslEnabled)">here</a></p>
</body>
</html>

passwordResetEmail.scala.html

app/views/myPasswordResetEmail.scala.html
@(user: securesocial.core.Identity, token: String)(implicit request: RequestHeader)

@import securesocial.core.IdentityProvider

<html>
<body>
  <p>Hello @user.firstName,</p>
  <p>Please follow this
  <a href="@securesocial.core.providers.utils.RoutesHelper.resetPassword(token).absoluteURL(IdentityProvider.sslEnabled)">
  link</a> to reset your password.
  </p>
</body>
</html>

resetPasswordPage.scala.html

app/views/myResetPasswordPage.scala.html
@(resetForm:Form[(String, String)], token: String)(implicit request: RequestHeader)

@import helper._
@import securesocial.core.IdentityProvider
@implicitFieldConstructor = @{ FieldConstructor(securesocial.views.html.inputFieldConstructor.f) }

@securesocial.views.html.main( Messages("securesocial.password.title") ) {
  <div class="page-header">
    <h1>@Messages("securesocial.password.title")</h1>
  </div>

  @request.flash.get("error").map { msg =>
    <div class="alert alert-error">
      @Messages(msg)
    </div>
  }

  <form action="@securesocial.core.providers.utils.RoutesHelper.handleResetPassword(token).absoluteURL(IdentityProvider.sslEnabled)"
    class="form-horizontal"
    autocomplete="off"
    method="POST"
  >

    <fieldset>
      @helper.inputPassword(
        resetForm("password.password1"),
        '_label -> Messages("securesocial.signup.password1"),
        'class -> "input-xlarge"
      )
      @helper.inputPassword(
        resetForm("password.password2"),
        '_label -> Messages("securesocial.signup.password2"),
        '_error -> resetForm.error("password"),
        'class -> "input-xlarge"
      )
      <div class="form-actions">
        <button type="submit" class="btn btn-primary">@Messages("securesocial.password.reset")</button>
      </div>
    </fieldset>
  </form>
}

signUp.scala.html

app/views/mySignUp.scala.html
@(signUpForm:Form[securesocial.controllers.Registration.RegistrationInfo], mailToken: String)(implicit request: RequestHeader, lang: Lang)

@import securesocial.core.providers.UsernamePasswordProvider
@import securesocial.core.IdentityProvider
@import helper._
@implicitFieldConstructor = @{ FieldConstructor(securesocial.views.html.inputFieldConstructor.f) }

@securesocial.views.html.main( Messages("securesocial.signup.title") ) {
  <div class="page-header">
    <h1>SignUp</h1>
  </div>

  @request.flash.get("error").map { msg =>
    <div class="alert alert-error">
      @msg
     </div>
  }

  <form action="@securesocial.controllers.routes.Registration.handleSignUp(mailToken).absoluteURL(IdentityProvider.sslEnabled)"
    class="form-horizontal"
    autocomplete= "off"
    method="POST"
  >
    <fieldset>
      @if( UsernamePasswordProvider.withUserNameSupport ) {
        @helper.inputText(
          signUpForm("userName"),
          '_label -> Messages("securesocial.signup.username"),
          'class -> "input-xlarge"
        )
      }

      @helper.inputText(
        signUpForm("firstName"),
        '_label -> Messages("securesocial.signup.firstName"),
        'class -> "input-xlarge"
      )

      @helper.inputText(
        signUpForm("lastName"),
        '_label -> Messages("securesocial.signup.lastName"),
        'class -> "input-xlarge"
      )

      @helper.inputPassword(
        signUpForm("password.password1"),
        '_label -> Messages("securesocial.signup.password1"),
        'class -> "input-xlarge"
      )

      @helper.inputPassword(
        signUpForm("password.password2"),
        '_label -> Messages("securesocial.signup.password2"),
        '_error -> signUpForm.error("password"),
        'class -> "input-xlarge"
      )

      <div class="form-actions">
        <button type="submit" class="btn btn-primary">@Messages("securesocial.signup.createAccount")</button>
        <a class="btn" href="@views.html.mylogin">@Messages("securesocial.signup.cancel")</a>
      </div>
    </fieldset>
  </form>
}

signUpEmail.scala.html

mySignUpEmail.scala.html
@(token: String)(implicit request: RequestHeader)

@import securesocial.core.IdentityProvider
<html>
<body>
  <p>Hello,</p>
  <p>Please follow this
    <a href="@securesocial.core.providers.utils.RoutesHelper.signUp(token).absoluteURL(IdentityProvider.sslEnabled)">link</a> to complete your registration.
  </p>
</body>
</html>

startResetPassword.scala.html

app/views/myStartResetPassword.scala.html
@(startForm:Form[String])(implicit request: RequestHeader)

@import helper._
@import securesocial.core.IdentityProvider

@implicitFieldConstructor = @{ FieldConstructor(securesocial.views.html.inputFieldConstructor.f) }

@securesocial.views.html.main( Messages("securesocial.password.title") ) {
  <div class="page-header">
    <h1>@Messages("securesocial.password.title")</h1>
  </div>
  @request.flash.get("error").map { msg =>
    <div class="alert alert-error">
      @Messages(msg)
    </div>
  }

  <form action="@securesocial.core.providers.utils.RoutesHelper.handleStartResetPassword().absoluteURL(IdentityProvider.sslEnabled)"
    class="form-horizontal"
    autocomplete="off"
    method="POST"
  >
    <fieldset>
      @helper.inputText(
        startForm("email"),
        '_label -> Messages("securesocial.signup.email1"),
        'class -> "input-xlarge"
      )
      <div class="form-actions">
        <button type="submit" class="btn btn-primary">@Messages("securesocial.password.reset")</button>
        <a class="btn" href="@securesocial.core.providers.utils.RoutesHelper.login()">@Messages("securesocial.signup.cancel")</a>
      </div>
    </fieldset>
  </form>
}

startSignUp.scala.html

app/views/myStartSignUp.scala.html
@(startForm:Form[String])(implicit request: RequestHeader, lang: Lang)

@import helper._
@implicitFieldConstructor = @{ FieldConstructor(securesocial.views.html.inputFieldConstructor.f) }

@views.html.myMain( "MyApp" ) {
  <div class="page-header">
    <h1>Sign Up</h1>
  </div>

  @request.flash.get("error").map { msg =>
    <div class="alert alert-error">
      @Messages(msg)
    </div>
  }

  <form action="@views.html.mySignUp"
      class="form-horizontal"
    autocomplete="off" method="post"
  >
    <fieldset>
      @helper.inputText(
        startForm("email"),
        '_label -> Messages("securesocial.signup.email1"),
        'class -> "input-xlarge"
      )
      <div class="form-actions">
        <button type="submit" class="btn btn-primary">@Messages("securesocial.signup.createAccount")</button>
        <a class="btn" href="@views.html.mylogin">@Messages("securesocial.signup.cancel")</a>
      </div>
    </fieldset>
  </form>
}

unknownEmailNotice.scala.html

app/views/myUnknownEmailNotice.scala.html
@(implicit request: RequestHeader)

<html>
<body>
  <p>Hello,</p>
  <p>We received a request to reset a password in our system. The attempt has failed because we do not have
    a registered account with this email address. It could be that you logged in using an external account such as Twitter
    or Facebook.</p>
  <p>
    If you never created an account with us ignore this email, otherwise if you think you have an account with us contact
    tech support for further assistance.
  </p>
</body>
</html>

welcomeEmail.scala.html

app/views/myWelcomeEmail.scala.html
@(user: securesocial.core.Identity)(implicit request: RequestHeader)
@import securesocial.core.IdentityProvider

<html>
<body>
  <p>Welcome @user.firstName,</p>
  <p>
  Your new account is ready.
  Click <a href="@securesocial.core.providers.utils.RoutesHelper.login.absoluteURL(IdentityProvider.sslEnabled)">here</a> to log in</p>
</body>
</html>

カスタマイズ

このままでは、元のSecureSocialデフォルトの表示とほぼ同じですが、上記のコードの中身を自分の好きにカスタマイズしていけばいいでしょう。

5
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5