|
Here is a short list of links related to this Lift Framework ProtoUser.scala source code file:
Lift Framework example source code file (ProtoUser.scala)
The Lift Framework ProtoUser.scala source code/* * Copyright 2006-2011 WorldWide Conferencing, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.liftweb package proto import net.liftweb.http._ import js._ import JsCmds._ import scala.xml.{NodeSeq, Node, Text, Elem} import scala.xml.transform._ import net.liftweb.sitemap._ import net.liftweb.sitemap.Loc._ import net.liftweb.util.Helpers._ import net.liftweb.util._ import net.liftweb.common._ import net.liftweb.util.Mailer._ import S._ /** * A prototypical user class with abstractions to the underlying storage */ trait ProtoUser { /** * The underlying record for the User */ type TheUserType /** * Bridges from TheUserType to methods used in this class */ protected trait UserBridge { /** * Convert the user's primary key to a String */ def userIdAsString: String /** * Return the user's first name */ def getFirstName: String /** * Return the user's last name */ def getLastName: String /** * Get the user's email */ def getEmail: String /** * Is the user a superuser */ def superUser_? : Boolean /** * Has the user been validated? */ def validated_? : Boolean /** * Does the supplied password match the actual password? */ def testPassword(toTest: Box[String]): Boolean /** * Set the validation flag on the user and return the user */ def setValidated(validation: Boolean): TheUserType /** * Set the unique ID for this user to a new value */ def resetUniqueId(): TheUserType /** * Return the unique ID for the user */ def getUniqueId(): String /** * Validate the user */ def validate: List[FieldError] /** * Given a list of string, set the password */ def setPasswordFromListString(in: List[String]): TheUserType /** * Save the user to backing store */ def save: Boolean /** * Get a nice name for the user */ def niceName: String = (getFirstName, getLastName, getEmail) match { case (f, l, e) if f.length > 1 && l.length > 1 => f+" "+l+" ("+e+")" case (f, _, e) if f.length > 1 => f+" ("+e+")" case (_, l, e) if l.length > 1 => l+" ("+e+")" case (_, _, e) => e } /** * Get a short name for the user */ def shortName: String = (getFirstName, getLastName) match { case (f, l) if f.length > 1 && l.length > 1 => f+" "+l case (f, _) if f.length > 1 => f case (_, l) if l.length > 1 => l case _ => getEmail } /** * Get an email link */ def niceNameWEmailLink = <a href={"mailto:"+urlEncode(getEmail)}>{niceName} } /** * Convert an instance of TheUserType to the Bridge trait */ protected implicit def typeToBridge(in: TheUserType): UserBridge /** * Get a nice name for the user */ def niceName(inst: TheUserType): String = inst.niceName /** * Get a nice name for the user */ def shortName(inst: TheUserType): String = inst.shortName /** * Get an email link for the user */ def niceNameWEmailLink(inst: TheUserType): Elem = inst.niceNameWEmailLink /** * A generic representation of a field. For example, this represents the * abstract "name" field and is used along with an instance of TheCrudType * to compute the BaseField that is the "name" field on the specific instance * of TheCrudType */ type FieldPointerType /** * Based on a FieldPointer, build a FieldPointerBridge */ protected implicit def buildFieldBridge(from: FieldPointerType): FieldPointerBridge protected trait FieldPointerBridge { /** * What is the display name of this field? */ def displayHtml: NodeSeq /** * Does this represent a pointer to a Password field */ def isPasswordField_? : Boolean } /** * The list of fields presented to the user at sign-up */ def signupFields: List[FieldPointerType] /** * The list of fields presented to the user for editing */ def editFields: List[FieldPointerType] /** * What template are you going to wrap the various nodes in */ def screenWrap: Box[Node] = Empty /** * The base path for the user related URLs. Override this * method to change the base path */ def basePath: List[String] = "user_mgt" :: Nil /** * The path suffix for the sign up screen */ def signUpSuffix: String = "sign_up" /** * The computed path for the sign up screen */ lazy val signUpPath = thePath(signUpSuffix) /** * The path suffix for the login screen */ def loginSuffix = "login" /** * The computed path for the login screen */ lazy val loginPath = thePath(loginSuffix) /** * The path suffix for the lost password screen */ def lostPasswordSuffix = "lost_password" /** * The computed path for the lost password screen */ lazy val lostPasswordPath = thePath(lostPasswordSuffix) /** * The path suffix for the reset password screen */ def passwordResetSuffix = "reset_password" /** * The computed path for the reset password screen */ lazy val passwordResetPath = thePath(passwordResetSuffix) /** * The path suffix for the change password screen */ def changePasswordSuffix = "change_password" /** * The computed path for change password screen */ lazy val changePasswordPath = thePath(changePasswordSuffix) /** * The path suffix for the logout screen */ def logoutSuffix = "logout" /** * The computed pat for logout */ lazy val logoutPath = thePath(logoutSuffix) /** * The path suffix for the edit screen */ def editSuffix = "edit" /** * The computed path for the edit screen */ lazy val editPath = thePath(editSuffix) /** * The path suffix for the validate user screen */ def validateUserSuffix = "validate_user" /** * The calculated path to the user validation screen */ lazy val validateUserPath = thePath(validateUserSuffix) /** * The application's home page */ def homePage = "/" /** * If you want to redirect a user to a different page after login, * put the page here */ object loginRedirect extends SessionVar[Box[String]](Empty) { override lazy val __nameSalt = Helpers.nextFuncName } /** * A helper class that holds menu items for the path */ case class MenuItem(name: String, path: List[String], loggedIn: Boolean) { lazy val endOfPath = path.last lazy val pathStr: String = path.mkString("/", "/", "") lazy val display = name match { case null | "" => false case _ => true } } /** * Calculate the path given a suffix by prepending the basePath to the suffix */ protected def thePath(end: String): List[String] = basePath ::: List(end) /** * Return the URL of the "login" page */ def loginPageURL = loginPath.mkString("/","/", "") /** * Inverted loggedIn_? */ def notLoggedIn_? = !loggedIn_? /** * A Menu.LocParam to test if the user is logged in */ lazy val testLogginIn = If(loggedIn_? _, S.??("must.be.logged.in")) ; /** * A Menu.LocParam to test if the user is a super user */ lazy val testSuperUser = If(superUser_? _, S.??("must.be.super.user")) /** * A Menu.LocParam for testing if the user is logged in and if they're not, * redirect them to the login page */ def loginFirst = If( loggedIn_? _, () => { import net.liftweb.http.{RedirectWithState, RedirectState} val uri = S.uriAndQueryString RedirectWithState( loginPageURL, RedirectState( ()=>{loginRedirect.set(uri)}) ) } ) /** * Is there a user logged in and are they a superUser? */ def superUser_? : Boolean = currentUser.map(_.superUser_?) openOr false /** * The menu item for login (make this "Empty" to disable) */ def loginMenuLoc: Box[Menu] = Full(Menu(Loc("Login" + menuNameSuffix, loginPath, S.??("login"), loginMenuLocParams ::: globalUserLocParams))) /** * If you want to include a LocParam (e.g. LocGroup) on all the * User menus, add them here */ protected def globalUserLocParams: List[LocParam[Unit]] = Nil /** * The LocParams for the menu item for login. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def loginMenuLocParams: List[LocParam[Unit]] = If(notLoggedIn_? _, S.??("already.logged.in")) :: Template(() => wrapIt(login)) :: Nil /** * If you have more than 1 ProtoUser in your application, you'll need to distinguish the menu names. * Do so by changing the menu name suffix so that there are no name clashes */ protected def menuNameSuffix: String = "" /** * The menu item for logout (make this "Empty" to disable) */ def logoutMenuLoc: Box[Menu] = Full(Menu(Loc("Logout" + menuNameSuffix, logoutPath, S.??("logout"), logoutMenuLocParams ::: globalUserLocParams))) /** * The LocParams for the menu item for logout. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def logoutMenuLocParams: List[LocParam[Unit]] = Template(() => wrapIt(logout)) :: testLogginIn :: Nil /** * The menu item for creating the user/sign up (make this "Empty" to disable) */ def createUserMenuLoc: Box[Menu] = Full(Menu(Loc("CreateUser" + menuNameSuffix, signUpPath, S.??("sign.up"), createUserMenuLocParams ::: globalUserLocParams))) /** * The LocParams for the menu item for creating the user/sign up. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def createUserMenuLocParams: List[LocParam[Unit]] = Template(() => wrapIt(signupFunc.map(_()) openOr signup)) :: If(notLoggedIn_? _, S.??("logout.first")) :: Nil /** * The menu item for lost password (make this "Empty" to disable) */ def lostPasswordMenuLoc: Box[Menu] = Full(Menu(Loc("LostPassword" + menuNameSuffix, lostPasswordPath, S.??("lost.password"), lostPasswordMenuLocParams ::: globalUserLocParams))) // not logged in /** * The LocParams for the menu item for lost password. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def lostPasswordMenuLocParams: List[LocParam[Unit]] = Template(() => wrapIt(lostPassword)) :: If(notLoggedIn_? _, S.??("logout.first")) :: Nil /** * The menu item for resetting the password (make this "Empty" to disable) */ def resetPasswordMenuLoc: Box[Menu] = Full(Menu(Loc("ResetPassword" + menuNameSuffix, (passwordResetPath, true), S.??("reset.password"), resetPasswordMenuLocParams ::: globalUserLocParams))) //not Logged in /** * The LocParams for the menu item for resetting the password. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def resetPasswordMenuLocParams: List[LocParam[Unit]] = Hidden :: Template(() => wrapIt(passwordReset(snarfLastItem))) :: If(notLoggedIn_? _, S.??("logout.first")) :: Nil /** * The menu item for editing the user (make this "Empty" to disable) */ def editUserMenuLoc: Box[Menu] = Full(Menu(Loc("EditUser" + menuNameSuffix, editPath, S.??("edit.user"), editUserMenuLocParams ::: globalUserLocParams))) /** * The LocParams for the menu item for editing the user. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def editUserMenuLocParams: List[LocParam[Unit]] = Template(() => wrapIt(editFunc.map(_()) openOr edit)) :: testLogginIn :: Nil /** * The menu item for changing password (make this "Empty" to disable) */ def changePasswordMenuLoc: Box[Menu] = Full(Menu(Loc("ChangePassword" + menuNameSuffix, changePasswordPath, S.??("change.password"), changePasswordMenuLocParams ::: globalUserLocParams))) /** * The LocParams for the menu item for changing password. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def changePasswordMenuLocParams: List[LocParam[Unit]] = Template(() => wrapIt(changePassword)) :: testLogginIn :: Nil /** * The menu item for validating a user (make this "Empty" to disable) */ def validateUserMenuLoc: Box[Menu] = Full(Menu(Loc("ValidateUser" + menuNameSuffix, (validateUserPath, true), S.??("validate.user"), validateUserMenuLocParams ::: globalUserLocParams))) /** * The LocParams for the menu item for validating a user. * Overwrite in order to add custom LocParams. Attention: Not calling super will change the default behavior! */ protected def validateUserMenuLocParams: List[LocParam[Unit]] = Hidden :: Template(() => wrapIt(validateUser(snarfLastItem))) :: If(notLoggedIn_? _, S.??("logout.first")) :: Nil /** * An alias for the sitemap property */ def menus: List[Menu] = sitemap // issue 182 /** * Insert this LocParam into your menu if you want the * User's menu items to be inserted at the same level * and after the item */ final case object AddUserMenusAfter extends Loc.LocParam[Any] /** * replace the menu that has this LocParam with the User's menu * items */ final case object AddUserMenusHere extends Loc.LocParam[Any] /** * Insert this LocParam into your menu if you want the * User's menu items to be children of that menu */ final case object AddUserMenusUnder extends Loc.LocParam[Any] private lazy val AfterUnapply = SiteMap.buildMenuMatcher(_ == AddUserMenusAfter) private lazy val HereUnapply = SiteMap.buildMenuMatcher(_ == AddUserMenusHere) private lazy val UnderUnapply = SiteMap.buildMenuMatcher(_ == AddUserMenusUnder) /** * The SiteMap mutator function */ def sitemapMutator: SiteMap => SiteMap = SiteMap.sitemapMutator { case AfterUnapply(menu) => menu :: sitemap case HereUnapply(_) => sitemap case UnderUnapply(menu) => List(menu.rebuild(_ ::: sitemap)) }(SiteMap.addMenusAtEndMutator(sitemap)) lazy val sitemap: List[Menu] = List(loginMenuLoc, logoutMenuLoc, createUserMenuLoc, lostPasswordMenuLoc, resetPasswordMenuLoc, editUserMenuLoc, changePasswordMenuLoc, validateUserMenuLoc).flatten(a => a) def skipEmailValidation = false def userMenu: List[Node] = { val li = loggedIn_? ItemList. filter(i => i.display && i.loggedIn == li). map(i => (<a href={i.pathStr}>{i.name})) } protected def snarfLastItem: String = (for (r <- S.request) yield r.path.wholePath.last) openOr "" lazy val ItemList: List[MenuItem] = List(MenuItem(S.??("sign.up"), signUpPath, false), MenuItem(S.??("log.in"), loginPath, false), MenuItem(S.??("lost.password"), lostPasswordPath, false), MenuItem("", passwordResetPath, false), MenuItem(S.??("change.password"), changePasswordPath, true), MenuItem(S.??("log.out"), logoutPath, true), MenuItem(S.??("edit.profile"), editPath, true), MenuItem("", validateUserPath, false)) var onLogIn: List[TheUserType => Unit] = Nil var onLogOut: List[Box[TheUserType] => Unit] = Nil /** * This function is given a chance to log in a user * programmatically when needed */ var autologinFunc: Box[()=>Unit] = Empty def loggedIn_? = { if(!currentUserId.isDefined) for(f <- autologinFunc) f() currentUserId.isDefined } def logUserIdIn(id: String) { curUser.remove() curUserId(Full(id)) } def logUserIn(who: TheUserType, postLogin: () => Nothing): Nothing = { if (destroySessionOnLogin) { S.session.open_!.destroySessionAndContinueInNewSession(() => { logUserIn(who) postLogin() }) } else { logUserIn(who) postLogin() } } def logUserIn(who: TheUserType) { curUserId.remove() curUser.remove() curUserId(Full(who.userIdAsString)) curUser(Full(who)) onLogIn.foreach(_(who)) } def logoutCurrentUser = logUserOut() def logUserOut() { onLogOut.foreach(_(curUser)) curUserId.remove() curUser.remove() S.session.foreach(_.destroySession()) } private object curUserId extends SessionVar[Box[String]](Empty) { override lazy val __nameSalt = Helpers.nextFuncName } def currentUserId: Box[String] = curUserId.is private object curUser extends RequestVar[Box[TheUserType]](currentUserId.flatMap(userFromStringId)) with CleanRequestVarOnSessionTransition { override lazy val __nameSalt = Helpers.nextFuncName } /** * Given a String representing the User ID, find the user */ protected def userFromStringId(id: String): Box[TheUserType] def currentUser: Box[TheUserType] = curUser.is def signupXhtml(user: TheUserType) = { (<form method="post" action={S.uri}> {localForm(user, false, signupFields)} <tr> </table>) } def signupMailBody(user: TheUserType, validationLink: String): Elem = { (<html> <head> <title>{S.??("sign.up.confirmation")} </head> <body> <p>{S.??("dear")} {user.getFirstName}, <br/> <br/> {S.??("sign.up.validation.link")} <br/>{validationLink} <br/> <br/> {S.??("thank.you")} </p> </body> </html>) } def signupMailSubject = S.??("sign.up.confirmation") /** * Send validation email to the user. The XHTML version of the mail * body is generated by calling signupMailBody. You can customize the * mail sent to users by override generateValidationEmailBodies to * send non-HTML mail or alternative mail bodies. */ def sendValidationEmail(user: TheUserType) { val resetLink = S.hostAndPath+"/"+validateUserPath.mkString("/")+ "/"+urlEncode(user.getUniqueId()) val email: String = user.getEmail val msgXml = signupMailBody(user, resetLink) Mailer.sendMail(From(emailFrom),Subject(signupMailSubject), (To(user.getEmail) :: generateValidationEmailBodies(user, resetLink) ::: (bccEmail.toList.map(BCC(_)))) :_* ) } /** * Generate the mail bodies to send with the valdiation link. * By default, just an HTML mail body is generated by calling signupMailBody * but you can send additional or alternative mail by override this method. */ protected def generateValidationEmailBodies(user: TheUserType, resetLink: String): List[MailBodyType] = List(xmlToMailBodyType(signupMailBody(user, resetLink))) protected object signupFunc extends RequestVar[Box[() => NodeSeq]](Empty) { override lazy val __nameSalt = Helpers.nextFuncName } /** * Override this method to do something else after the user signs up */ protected def actionsAfterSignup(theUser: TheUserType, func: () => Nothing): Nothing = { theUser.setValidated(skipEmailValidation).resetUniqueId() theUser.save if (!skipEmailValidation) { sendValidationEmail(theUser) S.notice(S.??("sign.up.message")) func() } else { logUserIn(theUser, () => { S.notice(S.??("welcome")) func() }) } } /** * Override this method to validate the user signup (eg by adding captcha verification) */ def validateSignup(user: TheUserType): List[FieldError] = user.validate /** * Create a new instance of the User */ protected def createNewUserInstance(): TheUserType /** * If there's any mutation to do to the user on creation for * signup, override this method and mutate the user. This can * be used to pull query parameters from the request and assign * certain fields. . Issue #722 * * @param user the user to mutate * @return the mutated user */ protected def mutateUserOnSignup(user: TheUserType): TheUserType = user def signup = { val theUser: TheUserType = mutateUserOnSignup(createNewUserInstance()) val theName = signUpPath.mkString("") def testSignup() { validateSignup(theUser) match { case Nil => actionsAfterSignup(theUser, () => S.redirectTo(homePage)) case xs => S.error(xs) ; signupFunc(Full(innerSignup _)) } } def innerSignup = bind("user", signupXhtml(theUser), "submit" -> SHtml.submit(S.??("sign.up"), testSignup _)) innerSignup } def emailFrom = "noreply@"+S.hostName def bccEmail: Box[String] = Empty def testLoggedIn(page: String): Boolean = ItemList.filter(_.endOfPath == page) match { case x :: xs if x.loggedIn == loggedIn_? => true case _ => false } def validateUser(id: String): NodeSeq = findUserByUniqueId(id) match { case Full(user) if !user.validated_? => user.setValidated(true).resetUniqueId().save logUserIn(user, () => { S.notice(S.??("account.validated")) S.redirectTo(homePage) }) case _ => S.error(S.??("invalid.validation.link")); S.redirectTo(homePage) } /** * How do we prompt the user for the username. By default, * it's S.??("email.address"), you can can change it to something else */ def userNameFieldString: String = S.??("email.address") /** * The string that's generated when the user name is not found. By * default: S.??("email.address.not.found") */ def userNameNotFoundString: String = S.??("email.address.not.found") def loginXhtml = { (<form method="post" action={S.uri}>
| |||||||||||||
{userNameFieldString} | |||||||||||||
{S.??("reset.your.password")} | |||||||||||||
{S.??("enter.your.new.password")} | |||||||||||||
{S.??("repeat.your.new.password")} | |||||||||||||
{S.??("change.password")} | |||||||||||||
{S.??("old.password")} | |||||||||||||
{S.??("new.password")} | |||||||||||||
{S.??("repeat.password")} | |||||||||||||
{S.??("edit")} | |||||||||||||
{field.displayName} | {form} |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.