Author: dpp
Date: Sat Jun 13 00:20:38 2009
New Revision: 784317
URL: http://svn.apache.org/viewvc?rev=784317&view=rev
Log:
Pulled all the XHTML out of User... and made User not rely on anything else
Added:
incubator/esme/trunk/server/src/main/webapp/user_template/
incubator/esme/trunk/server/src/main/webapp/user_template/edit.html
incubator/esme/trunk/server/src/main/webapp/user_template/login.html
Modified:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/User.scala
incubator/esme/trunk/server/src/main/webapp/style/b-open.css
Modified:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/User.scala
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/User.scala?rev=784317&r1=784316&r2=784317&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/User.scala
(original)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/User.scala
Sat Jun 13 00:20:38 2009
@@ -1,7 +1,7 @@
package org.apache.esme.model
/**
* Copyright 2008-2009 WorldWide Conferencing, LLC
- *
+ *
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -22,6 +22,9 @@
import net.liftweb._
import mapper._
+import util.Mailer.From
+import scala.xml.{Node, Elem, NodeSeq, Text}
+
import sitemap._
import util._
import openid._
@@ -36,18 +39,13 @@
import org.openid4java.consumer._
import org.openid4java.util._
-import scala.xml.{NodeSeq, Text}
-
import org.apache.esme._
import org.apache.esme.actor._
import view._
import java.net.URL
import java.util.logging._
-object User extends User with MetaOpenIDProtoUser[User] {
- val logger: Logger = Logger.getLogger("org.apache.esme.model.User")
- logger.setLevel(Level.INFO)
-
+object User extends User with KeyedMetaMapper[Long, User] {
override def afterSave = profileChanged _ :: notifyActors _ ::
super.afterSave
private def notifyActors(in: User) {
@@ -56,23 +54,23 @@
private def profileChanged(in: User) {
if (!in.needsChange_?)
- Message.create.author(in.id).
- when(Helpers.timeNow.getTime).
- source("profile").
- setTextAndTags("User " + in.nickname + " changed profile.
Name: " + in.wholeName + ", Image: " + in.imageUrl, Nil, Empty).
- foreach{ msg =>
- if (msg.save) {
- Distributor ! Distributor.AddMessageToMailbox(in.id,
msg, ProfileReason(in.id))
- }
- }
+ Message.create.author(in.id).
+ when(Helpers.timeNow.getTime).
+ source("profile").
+ setTextAndTags("User " + in.nickname + " changed profile. Name: " +
in.wholeName + ", Image: " + in.imageUrl, Nil, Empty).
+ foreach{ msg =>
+ if (msg.save) {
+ Distributor ! Distributor.AddMessageToMailbox(in.id, msg,
ProfileReason(in.id))
+ }
+ }
}
- def findFromWeb(uid: String): Box[User] =
+ def findFromWeb(uid: String): Box[User] =
User.find(By(User.nickname, uid)) or User.find(uid)
override def dbTableName = "users" // define the DB table name
-
- override def screenWrap = S.request.flatMap(_.location) match {
+
+ def screenWrap = S.request.flatMap(_.location) match {
case Full(l) if l.name == "Login" => Full(<lift:surround with="login"
at="content">
<lift:bind /></lift:surround>)
case _ => Full(<lift:surround with="default" at="content">
@@ -82,24 +80,22 @@
/**
* The menu item for editing the user (make this "Empty" to disable)
*/
- override def editUserMenuLoc: Box[Menu] =
+ def editUserMenuLoc: Box[Menu] =
Full(Menu(Loc("EditUser", editPath, "Profile",
Template(() => wrapIt(editFunc.map(_()) openOr edit)),
testLogginIn)))
-
-
- override def signupFields: List[BaseOwnedMappedField[User]] = nickname ::
+
+
+ def signupFields: List[BaseOwnedMappedField[User]] = nickname ::
firstName :: lastName :: imageUrl :: timezone :: locale :: Nil
-
+
override def fieldOrder: List[BaseOwnedMappedField[User]] = nickname ::
firstName :: lastName :: imageUrl :: timezone :: locale :: Nil
- onLogIn = ExtSession.userDidLogin _ :: onLogIn
+ def loginXhtml = TemplateFinder.findAnyTemplate(List("user_template",
"login")) openOr NodeSeq.Empty
- onLogOut = ExtSession.userDidLogout _ :: onLogOut
-
- override def loginXhtml =
+ /*
<form id="openid_submit" class="clear" method="POST"
action={loginPath.mkString("/", "/", "")} >
<div class="b-open-l">
<p class="input"><label>Open ID</label><user:openid /></p>
@@ -116,17 +112,17 @@
else Text("")
}
</form>
-
+*/
object openIdError extends RequestVar(false)
-
- override def login = {
+
+ def login = {
if (S.post_?) {
S.param("username").
- foreach(username =>
+ foreach(username =>
ESMEOpenIDVendor.loginAndRedirect(username, logUserIn)
)
}
-
+
def logUserIn(openid: Box[Identifier], fo: Box[VerificationResult], exp:
Box[Exception]): LiftResponse = {
(openid, exp) match {
case (Full(id), _) =>
@@ -135,17 +131,17 @@
S.notice("Welcome "+user.niceName)
Message.create.author(user.id).
- when(Helpers.timeNow.getTime).
- source("login").
- setTextAndTags("User " + user.nickname + " logged
in.", Nil, Empty).
- foreach{ msg =>
- if (msg.save) {
- Distributor !
Distributor.AddMessageToMailbox(user.id, msg, LoginReason(user.id))
- }
- }
+ when(Helpers.timeNow.getTime).
+ source("login").
+ setTextAndTags("User " + user.nickname + " logged in.", Nil, Empty).
+ foreach{ msg =>
+ if (msg.save) {
+ Distributor ! Distributor.AddMessageToMailbox(user.id, msg,
LoginReason(user.id))
+ }
+ }
RedirectResponse("/", S responseCookies :_*)
-
+
case (_, Full(exp)) =>
openIdError(true)
S.error("Got an exception: "+exp.getMessage)
@@ -157,24 +153,572 @@
RedirectResponse(S.uri, S responseCookies :_*)
}
-
+
}
-
+
loginForm
}
-
- def loginForm = bind("user", loginXhtml,
- "openid" -> (FocusOnLoad(<input type="text"
name="username"/>)))
-
+
+ def loginForm =
+ bind("user", loginXhtml,
+ "openid" -> (FocusOnLoad(<input type="text" name="username"/>)))
+
def openIDVendor = ESMEOpenIDVendor
-
- override def logout = {
+
+ def logout = {
logoutCurrentUser
S.redirectTo("/static/about")
}
def followerIdsForUserId(userId: Long): List[Long] =
Relationship.findAll(By(Relationship.target, userId)).map(_.owner.is)
+
+
+ def findOrCreate(openId: String): User =
+ find(By(this.openId, openId)) match {
+ case Full(u) => u
+ case _ =>
+ this.create.openId(openId).
+ nickname("change"+Helpers.randomInt(1000000000)).firstName("Unknown").
+ lastName("Unknown").password(Helpers.randomString(15)).
+ email(Helpers.randomInt(100000000)+"[email protected]").
+ saveMe
+ }
+
+ // no need for these menu items with OpenID
+ /**
+ * The menu item for creating the user/sign up (make this "Empty" to disable)
+ */
+ def createUserMenuLoc: Box[Menu] = Empty
+
+ /**
+ * The menu item for lost password (make this "Empty" to disable)
+ */
+ def lostPasswordMenuLoc: Box[Menu] = Empty
+
+ /**
+ * The menu item for resetting the password (make this "Empty" to disable)
+ */
+ def resetPasswordMenuLoc: Box[Menu] = Empty
+
+ /**
+ * The menu item for changing password (make this "Empty" to disable)
+ */
+ def changePasswordMenuLoc: Box[Menu] = Empty
+
+ /**
+ * The menu item for validating a user (make this "Empty" to disable)
+ */
+ def validateUserMenuLoc: Box[Menu] = Empty
+
+ def findByNickname(str: String): List[User] =
+ findAll(By(nickname, str))
+
+
+ val basePath: List[String] = "user_mgt" :: Nil
+ def signUpSuffix = "sign_up"
+ lazy val signUpPath = thePath(signUpSuffix)
+ def loginSuffix = "login"
+ lazy val loginPath = thePath(loginSuffix)
+ def lostPasswordSuffix = "lost_password"
+ lazy val lostPasswordPath = thePath(lostPasswordSuffix)
+ def passwordResetSuffix = "reset_password"
+ lazy val passwordResetPath = thePath(passwordResetSuffix)
+ def changePasswordSuffix = "change_password"
+ lazy val changePasswordPath = thePath(changePasswordSuffix)
+ def logoutSuffix = "logout"
+ lazy val logoutPath = thePath(logoutSuffix)
+ def editSuffix = "edit"
+ lazy val editPath = thePath(editSuffix)
+ def validateUserSuffix = "validate_user"
+ lazy val validateUserPath = thePath(validateUserSuffix)
+
+ def homePage = "/"
+
+
+
+ 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
+ }
+ }
+
+ def thePath(end: String): List[String] = basePath ::: List(end)
+
+ /**
+ * Return the URL of the "login" page
+ */
+ def loginPageURL = loginPath.mkString("/","/", "")
+
+ def notLoggedIn_? = !loggedIn_?
+
+ lazy val testLogginIn = If(loggedIn_? _, S.??("must.be.logged.in")) ;
+
+ lazy val testSuperUser = If(superUser_? _, S.??("must.be.super.user"))
+
+ def superUser_? : Boolean = currentUser.map(_.superUser.is) openOr false
+
+ /**
+ * The menu item for login (make this "Empty" to disable)
+ */
+ def loginMenuLoc: Box[Menu] = {
+ Full(Menu(Loc("Login", loginPath, S.??("login"),
+ If(notLoggedIn_? _, S.??("already.logged.in")),
+ Template(() => wrapIt(login)))))
+ }
+
+ /**
+ * The menu item for logout (make this "Empty" to disable)
+ */
+ def logoutMenuLoc: Box[Menu] =
+ Full(Menu(Loc("Logout", logoutPath, S.??("logout"),
+ Template(() => wrapIt(logout)),
+ testLogginIn)))
+
+/*
+ /**
+ * The menu item for creating the user/sign up (make this "Empty" to disable)
+ */
+ def createUserMenuLoc: Box[Menu] =
+ Full(Menu(Loc("CreateUser", signUpPath,
+ S.??("sign.up"),
+ Template(() => wrapIt(signupFunc.map(_()) openOr signup)),
+ If(notLoggedIn_? _, S.??("logout.first")))))
+
+ /**
+ * The menu item for lost password (make this "Empty" to disable)
+ */
+ def lostPasswordMenuLoc: Box[Menu] =
+ Full(Menu(Loc("LostPassword", lostPasswordPath,
+ S.??("lost.password"),
+ Template(() => wrapIt(lostPassword)),
+ If(notLoggedIn_? _, S.??("logout.first"))))) // not logged in
+
+ /**
+ * The menu item for resetting the password (make this "Empty" to disable)
+ */
+ def resetPasswordMenuLoc: Box[Menu] =
+ Full(Menu(Loc("ResetPassword", (passwordResetPath, true),
+ S.??("reset.password"), Hidden,
+ Template(() => wrapIt(passwordReset(snarfLastItem))),
+ If(notLoggedIn_? _,
+ S.??("logout.first"))))) //not Logged in
+
+ /**
+ * The menu item for editing the user (make this "Empty" to disable)
+ */
+ def editUserMenuLoc: Box[Menu] =
+ Full(Menu(Loc("EditUser", editPath, S.??("edit.user"),
+ Template(() => wrapIt(editFunc.map(_()) openOr edit)),
+ testLogginIn)))
+*/
+
+/*
+/**
+ * The menu item for changing password (make this "Empty" to disable)
+ */
+ def changePasswordMenuLoc: Box[Menu] =
+ Full(Menu(Loc("ChangePassword", changePasswordPath,
+ S.??("change.password"),
+ Template(() => wrapIt(changePassword)),
+ testLogginIn)))
+ */
+
+/*
+ /**
+ * The menu item for validating a user (make this "Empty" to disable)
+ */
+ def validateUserMenuLoc: Box[Menu] =
+ Full(Menu(Loc("ValidateUser", (validateUserPath, true),
+ S.??("validate.user"), Hidden,
+ Template(() => wrapIt(validateUser(snarfLastItem))),
+ If(notLoggedIn_? _, S.??("logout.first")))))
+*/
+ 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}</a>))
+ }
+
+ 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))
+
+ // def requestLoans: List[LoanWrapper] = Nil // List(curUser)
+ var onLogIn: List[User => Unit] = List(ExtSession.userDidLogin _)
+
+ var onLogOut: List[Box[User] => Unit] = List(ExtSession.userDidLogout _)
+
+ def loggedIn_? : Boolean = currentUserId.isDefined
+
+ def logUserIdIn(id: String) {
+ curUser.remove()
+ curUserId(Full(id))
+ }
+ def logUserIn(who: User) {
+ curUser.remove()
+ curUserId(Full(who.id.toString))
+ onLogIn.foreach(_(who))
+ }
+
+ def logoutCurrentUser = logUserOut()
+
+ def logUserOut() {
+ onLogOut.foreach(_(curUser))
+ curUserId.remove()
+ curUser.remove()
+ S.request.foreach(_.request.getSession.invalidate)
+ }
+
+ private object curUserId extends SessionVar[Box[String]](Empty)
+
+ def currentUserId: Box[String] = curUserId.is
+
+ private object curUser extends
RequestVar[Box[User]](currentUserId.flatMap(id => getSingleton.find(id)))
+
+
+ def currentUser: Box[User] = curUser.is
+
+ /*
+ def signupXhtml(user: User) = {
+ (<form method="post" action={S.uri}><table><tr><td
+ colspan="2">Sign Up</td></tr>
+ {localForm(user, false)}
+ <tr><td> </td><td><user:submit/></td></tr>
+ </table></form>)
+ }
+
+
+ def signupMailBody(user: User, validationLink: String) = {
+ (<html>
+ <head>
+ <title>Sign Up Confirmation</title>
+ </head>
+ <body>
+ <p>Dear {user.firstName},
+ <br/>
+ <br/>
+ Click on this link to complete signup
+ <br/><a href={validationLink}>{validationLink}</a>
+ <br/>
+ <br/>
+ Thanks
+ </p>
+ </body>
+ </html>)
+ }
+
+ def signupMailSubject = S.??("sign.up.confirmation")
+
+ def sendValidationEmail(user: User) {
+ val resetLink = S.hostAndPath+"/"+validateUserPath.mkString("/")+
+ "/"+user.uniqueId
+
+ val email: String = user.email
+
+ val msgXml = signupMailBody(user, resetLink)
+
+ import net.liftweb.util._
+ import Mailer._
+
+ Mailer.sendMail(From(emailFrom),Subject(signupMailSubject),
+ (To(user.email) :: xmlToMailBodyType(msgXml) ::
+ (bccEmail.toList.map(BCC(_)))) :_* )
+ }
+
+ protected object signupFunc extends RequestVar[Box[() => NodeSeq]](Empty)
+
+ def signup = {
+ val theUser: User = create
+ val theName = signUpPath.mkString("")
+
+ def testSignup() {
+ theUser.validate match {
+ case Nil =>
+ theUser.validated(skipEmailValidation).uniqueId.reset()
+ theUser.save
+ if (!skipEmailValidation) {
+ sendValidationEmail(theUser)
+ S.notice(S.??("sign.up.message"))
+ } else {
+ S.notice(S.??("welcome"))
+ logUserIn(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 = getSingleton.find(By(uniqueId, id))
match {
+ case Full(user) if !user.validated =>
+ user.validated(true).uniqueId.reset().save
+ S.notice(S.??("account.validated"))
+ logUserIn(user)
+ S.redirectTo(homePage)
+
+ case _ => S.error(S.??("invalid.validation.link")); S.redirectTo(homePage)
+ }
+
+/*
+ def login = {
+ if (S.post_?) {
+ S.param("username").
+ flatMap(username => getSingleton.find(By(email, username))) match {
+ case Full(user) if user.validated &&
+ user.password.match_?(S.param("password").openOr("*")) =>
+ S.notice(S.??("logged.in"))
+ logUserIn(user)
+ S.redirectTo(homePage)
+
+ case Full(user) if !user.validated =>
+ S.error(S.??("account.validation.error"))
+
+ case _ => S.error(S.??("invalid.credentials"))
+ }
+ }
+
+ bind("user", loginXhtml,
+ "email" -> (FocusOnLoad(<input type="text" name="username"/>)),
+ "password" -> (<input type="password" name="password"/>),
+ "submit" -> (<input type="submit" value={S.??("log.in")}/>))
+ }
+ */
+
+/*
+ def lostPasswordXhtml = {
+ (<form method="post" action={S.uri}>
+ <table><tr><td
+ colspan="2">{S.??("enter.email")}</td></tr>
+ <tr><td>{S.??("email.address")}</td><td><user:email /></td></tr>
+ <tr><td> </td><td><user:submit /></td></tr>
+ </table>
+ </form>)
+ }
+
+ def passwordResetMailBody(user: User, resetLink: String) = {
+ (<html>
+ <head>
+ <title>{S.??("reset.password.confirmation")}</title>
+ </head>
+ <body>
+ <p>{S.??("dear")} {user.firstName},
+ <br/>
+ <br/>
+ {S.??("click.reset.link")}
+ <br/><a href={resetLink}>{resetLink}</a>
+ <br/>
+ <br/>
+ {S.??("thank.you")}
+ </p>
+ </body>
+ </html>)
+ }
+
+ def passwordResetEmailSubject = S.??("reset.password.request")
+
+ def sendPasswordReset(email: String) {
+ getSingleton.find(By(this.email, email)) match {
+ case Full(user) if user.validated =>
+ user.uniqueId.reset().save
+ val resetLink = S.hostAndPath+
+ passwordResetPath.mkString("/", "/", "/")+user.uniqueId
+
+ val email: String = user.email
+
+ import Mailer._
+
+ val msgXml = passwordResetMailBody(user, resetLink)
+ Mailer.sendMail(From(emailFrom),Subject(passwordResetEmailSubject),
+ (To(user.email) :: xmlToMailBodyType(msgXml) ::
+ (bccEmail.toList.map(BCC(_)))) :_*)
+
+ S.notice(S.??("password.reset.email.sent"))
+ S.redirectTo(homePage)
+
+ case Full(user) =>
+ sendValidationEmail(user)
+ S.notice(S.??("account.validation.resent"))
+ S.redirectTo(homePage)
+
+ case _ => S.error(S.??("email.address.not.found"))
+ }
+ }
+
+ def lostPassword = {
+ bind("user", lostPasswordXhtml,
+ "email" -> SHtml.text("", sendPasswordReset _),
+ "submit" -> <input type="Submit" value={S.??("send.it")} />)
+ }
+
+ def passwordResetXhtml = {
+ (<form method="post" action={S.uri}>
+ <table><tr><td colspan="2">{S.??("reset.your.password")}</td></tr>
+
<tr><td>{S.??("enter.your.new.password")}</td><td><user:pwd/></td></tr>
+
<tr><td>{S.??("repeat.your.new.password")}</td><td><user:pwd/></td></tr>
+ <tr><td> </td><td><user:submit/></td></tr>
+ </table>
+ </form>)
+ }
+
+ def passwordReset(id: String) =
+ getSingleton.find(By(uniqueId, id)) match {
+ case Full(user) =>
+ def finishSet() {
+ user.validate match {
+ case Nil => S.notice(S.??("password.changed"))
+ user.save
+ logUserIn(user); S.redirectTo(homePage)
+
+ case xs => S.error(xs)
+ }
+ }
+ user.uniqueId.reset().save
+
+ bind("user", passwordResetXhtml,
+ "pwd" -> SHtml.password_*("",S.LFuncHolder((p: List[String]) =>
+ user.password.setList(p))),
+ "submit" -> SHtml.submit(S.??("set.password"), finishSet _))
+ case _ => S.error(S.??("pasword.link.invalid")); S.redirectTo(homePage)
+ }
+
+ def changePasswordXhtml = {
+ (<form method="post" action={S.uri}>
+ <table><tr><td colspan="2">{S.??("change.password")}</td></tr>
+ <tr><td>{S.??("old.password")}</td><td><user:old_pwd /></td></tr>
+ <tr><td>{S.??("new.password")}</td><td><user:new_pwd /></td></tr>
+ <tr><td>{S.??("repeat.password")}</td><td><user:new_pwd /></td></tr>
+ <tr><td> </td><td><user:submit /></td></tr>
+ </table>
+ </form>)
+ }
+
+ def changePassword = {
+ val user = currentUser.open_! // we can do this because the logged in test
has happened
+ var oldPassword = ""
+ var newPassword: List[String] = Nil
+
+ def testAndSet() {
+ if (!user.password.match_?(oldPassword))
S.error(S.??("wrong.old.password"))
+ else {
+ user.password.setFromAny(newPassword)
+ user.validate match {
+ case Nil => user.save; S.notice(S.??("pasword.changed"));
S.redirectTo(homePage)
+ case xs => S.error(xs)
+ }
+ }
+ }
+
+ bind("user", changePasswordXhtml,
+ "old_pwd" -> SHtml.password("", oldPassword = _),
+ "new_pwd" -> SHtml.password_*("", S.LFuncHolder(newPassword = _)),
+ "submit" -> SHtml.submit(S.??("change"), testAndSet _))
+ }
+*/
+
+/*def editXhtml(user: User) = {
+ (<form method="post" action={S.uri}>
+ <table><tr><td colspan="2">{S.??("edit")}</td></tr>
+ {localForm(user, true)}
+ <tr><td> </td><td><user:submit/></td></tr>
+ </table>
+ </form>)
+ }*/
+
+
+ def editXhtml = <form method="post" action={S.uri}>{
+ TemplateFinder.findAnyTemplate(List("user_template", "edit")) openOr
NodeSeq.Empty
+ }</form>
+
+ object editFunc extends RequestVar[Box[() => NodeSeq]](Empty)
+
+ def edit = {
+ val theUser: User = currentUser.open_! // we know we're logged in
+ val theName = editPath.mkString("")
+
+ def testEdit() {
+ theUser.validate match {
+ case Nil =>
+ theUser.save
+ S.notice(S.??("profle.updated"))
+ S.redirectTo(homePage)
+
+ case xs => S.error(xs) ; editFunc(Full(innerEdit _))
+ }
+ }
+
+ def innerEdit = bind("user", editXhtml,
+ "user_info" -> localForm(theUser, true),
+ "submit" -> SHtml.submit(S.??("edit"), testEdit _))
+
+ innerEdit
+ }
+
+ private def localForm(user: User, ignorePassword: Boolean): NodeSeq = {
+ signupFields.
+ map(fi => getSingleton.getActualBaseField(user, fi)).
+ filter(f => !ignorePassword || (f match {
+ case f: MappedPassword[User] => false
+ case _ => true
+ })).
+ flatMap(f =>
+ f.toForm.toList.map(form =>
+ (<tr><td>{f.displayName}</td><td>{form}</td></tr>) ) )
+ }
+
+ import scala.xml.transform.{RuleTransformer, RewriteRule}
+ protected def wrapIt(in: NodeSeq): NodeSeq =
+ screenWrap.map(new RuleTransformer(new RewriteRule {
+ override def transform(n: Node) = n match {
+ case e: Elem if "bind" == e.label && "lift" == e.prefix => in
+ case _ => n
+ }
+ })) openOr in
+
+
}
object ESMEOpenIDVendor extends OpenIdVendor {
@@ -182,9 +726,9 @@
type ConsumerType = ESMEOpenIDConsumer
def logUserOut(): Unit = User.logUserOut()
-
+
def currentUser = User.currentUser
-
+
def postLogin(id: Box[Identifier],res: VerificationResult): Unit = {
id match {
case Full(id) =>
@@ -197,9 +741,9 @@
S.error("Failed to authenticate")
}
}
-
+
def displayUser(in: User): NodeSeq = Text("Welcome "+in.niceName)
-
+
def createAConsumer = new ESMEOpenIDConsumer
}
@@ -207,8 +751,8 @@
{
override val manager = {
- User.logger.info("Proxy settings: " + Props.get("http.proxyHost", "[no
host]")
- + ":" + Props.get("http.proxyPort", "[no port]"))
+ Log.info("Proxy settings: " + Props.get("http.proxyHost", "[no host]")
+ + ":" + Props.get("http.proxyPort", "[no port]"))
for (host <- Props.get("http.proxyHost")){
val proxyProps = new ProxyProperties()
@@ -223,11 +767,13 @@
/**
* An O-R mapped "User" class that includes first name, last name, password
*/
-class User extends OpenIDProtoUser[User] {
+class User extends KeyedMapper[Long, User] with UserIdAsString {//
OpenIDProtoUser[User] {
+ import S._
+
def getSingleton = User // what's the "meta" server
object imageUrl extends MappedString(this, 256)
-
+
def authTokens: List[AuthToken] =
AuthToken.findAll(By(AuthToken. user, this),
OrderBy(AuthToken.description, Ascending))
@@ -243,17 +789,17 @@
By(Relationship.target, who)) match {
case Full(x) => true
case Empty => { if (Relationship.create.owner(this).target(who).save)
- Message.create.author(who.id).
- when(Helpers.timeNow.getTime).
- source("followed").
- setTextAndTags("User " + this.nickname + " followed " +
who.nickname + ".", Nil, Empty).
- foreach { msg =>
- if (msg.save) {
- Distributor !
Distributor.AddMessageToMailbox(who.id, msg, FollowedReason(this.id))
- }
- }
- true
- }
+ Message.create.author(who.id).
+ when(Helpers.timeNow.getTime).
+ source("followed").
+ setTextAndTags("User " + this.nickname + " followed " +
who.nickname + ".", Nil, Empty).
+ foreach { msg =>
+ if (msg.save) {
+ Distributor ! Distributor.AddMessageToMailbox(who.id, msg,
FollowedReason(this.id))
+ }
+ }
+ true
+ }
case _ => false
}
}
@@ -261,20 +807,20 @@
def unfollow(who: User): Boolean = {
Relationship.findAll(By(Relationship.owner, this),
By(Relationship.target, who)).foreach{ r =>
- if (r.delete_!) Message.create.author(who.id).
- when(Helpers.timeNow.getTime).
- source("unfollowed").
- setTextAndTags("User " +
this.nickname + " unfollowed " + who.nickname + ".", Nil, Empty).
- foreach{ msg =>
- if (msg.save) {
- Distributor !
Distributor.AddMessageToMailbox(who.id, msg, UnfollowedReason(this.id))
- }
- }
- }
+ if (r.delete_!) Message.create.author(who.id).
+ when(Helpers.timeNow.getTime).
+ source("unfollowed").
+ setTextAndTags("User " + this.nickname + " unfollowed " + who.nickname +
".", Nil, Empty).
+ foreach{ msg =>
+ if (msg.save) {
+ Distributor ! Distributor.AddMessageToMailbox(who.id, msg,
UnfollowedReason(this.id))
+ }
+ }
+ }
true
}
- def following_?(who: User): Boolean =
+ def following_?(who: User): Boolean =
Relationship.find(By(Relationship.owner, this),
By(Relationship.target, who)).isDefined
@@ -283,7 +829,7 @@
def followers(): List[User] =
User.findAll(In.fk(Relationship.owner, By(Relationship.target, this)))
-
+
def wholeName: String = (firstName.is, lastName.is) match {
case (f, l) if f.length > 1 && l.length > 1 => f+" "+l
case (f, _) if f.length > 1 => f
@@ -296,16 +842,126 @@
def image: Option[NodeSeq] = tryo(Text((new
URL(imageUrl)).toString)).toOption
- def tracking: List[Tracking] =
+ def tracking: List[Tracking] =
Tracking.findAll(By(Tracking.user, this),
By(Tracking.disabled, false),
By(Tracking.removed, false),
OrderBy(Tracking.id, Ascending))
-
+
def performing: List[Action] =
Action.findAll(By(Action.user, this),
By(Action.disabled, false),
By(Action.removed, false),
OrderBy(Action.id, Ascending))
+
+ override def primaryKeyField = id
+
+ // the primary key for the database
+ object id extends MappedLongIndex(this)
+
+ def userIdAsString: String = id.is.toString
+
+ // First Name
+ object firstName extends MappedString(this, 32) {
+ override def displayName = fieldOwner.firstNameDisplayName
+ override val fieldId = Some(Text("txtFirstName"))
+ }
+
+ def firstNameDisplayName = ??("First Name")
+
+ // Last Name
+ object lastName extends MappedString(this, 32) {
+ override def displayName = fieldOwner.lastNameDisplayName
+ override val fieldId = Some(Text("txtLastName"))
+ }
+
+ def lastNameDisplayName = ??("Last Name")
+
+ // Email
+ object email extends MappedEmail(this, 48) {
+ override def dbIndexed_? = true
+ override def validations = valUnique(S.??("unique.email.address")) _ ::
super.validations
+ override def displayName = fieldOwner.emailDisplayName
+ override val fieldId = Some(Text("txtEmail"))
+ }
+
+ def emailDisplayName = ??("Email")
+ // Password
+ object password extends MappedPassword[User](this) {
+ override def displayName = fieldOwner.passwordDisplayName
+ }
+
+ def passwordDisplayName = ??("Password")
+
+ object superUser extends MappedBoolean(this) {
+ override def defaultValue = false
+ }
+
+ def shortName: String = (firstName.is, lastName.is) 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 _ => email.is
+ }
+
+ def niceNameWEmailLink = <a href={"mailto:"+email.is}>{niceName}</a>
+
+ object uniqueId extends MappedUniqueId(this, 32) {
+ override def dbIndexed_? = true
+ override def writePermission_? = true
+ }
+
+ object validated extends MappedBoolean[User](this) {
+ override def defaultValue = false
+ override val fieldId = Some(Text("txtValidated"))
+ }
+
+ object locale extends MappedLocale[User](this) {
+ override def displayName = fieldOwner.localeDisplayName
+ override val fieldId = Some(Text("txtLocale"))
+ }
+
+ object timezone extends MappedTimeZone[User](this) {
+ override def displayName = fieldOwner.timezoneDisplayName
+ override val fieldId = Some(Text("txtTimeZone"))
+ }
+
+ object openId extends MappedString(this, 512) {
+ override def dbIndexed_? = true
+ }
+
+ object nickname extends MappedPoliteString(this, 64) {
+ override def dbIndexed_? = true
+
+ def deDupUnderscore(in: String): String = in.indexOf("__") match {
+ case -1 => in
+ case pos => deDupUnderscore(in.substring(0, pos)+in.substring(pos + 1))
+ }
+
+ override def setFilter = notNull _ :: toLower _ :: trim _ ::
+ deDupUnderscore _ :: super.setFilter
+
+ private def validateNickname(str: String): List[FieldError] = {
+ val others = getSingleton.findByNickname(str).
+ // getSingleton.findAll(By(getSingleton.nickname, str)).
+ filter(_.id.is != fieldOwner.id.is)
+ others.map(u => FieldError(this, <xml:group>Duplicate nickname:
{str}</xml:group>))
+ }
+
+ private def validText(str: String): List[FieldError] =
+ if (ValidNickName(str)) Nil
+ else List(FieldError(this,
+ <xml:group>Invalid nickname. Must start with
+ a letter and contain only letters,
+ numbers or "_"</xml:group>))
+
+ override def validations = validText _ :: validateNickname _ ::
super.validations
+ }
+
+ def niceName: String = nickname
+
+ def timezoneDisplayName = ??("Time Zone")
+
+ def localeDisplayName = ??("Locale")
}
Modified: incubator/esme/trunk/server/src/main/webapp/style/b-open.css
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/style/b-open.css?rev=784317&r1=784316&r2=784317&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/style/b-open.css (original)
+++ incubator/esme/trunk/server/src/main/webapp/style/b-open.css Sat Jun 13
00:20:38 2009
@@ -1,91 +1,95 @@
/* OpenId (begin) */ /**/
- .b-open
- {
- margin-top: 60px;
- }
-
- .b-open form
- {
- padding: 20px 20px;
-
- background: #233e5e;
- }
-
- .b-open label
- {
- font-size: 110%;
- font-weight: bold;
-
- vertical-align: middle;
- }
-
- .b-open input
- {
- width: 255px;
- margin-left: 0.5em;
-
- vertical-align: middle;
- }
-
- .b-open .button
- {
- margin-top: 20px;
- }
-
- .b-open .sign-in
- {
- float: left;
-
- margin: -6px 0 0 4.4em;
- }
-
- .b-open .note
- {
- font-weight: bold;
-
- margin-left: 1em;
- }
-
- .b-open-l
- {
- float: left;
-
- margin-right: 20px;
- }
-
- .b-open-r
- {
- float: left;
-
- width: 12em;
-
- color: #fa801f;
- }
-
- .b-open h3
- {
- font-size: 110%;
- font-weight: bold;
- }
-
- .b-open .open-id
- {
- margin: 20px 0 0 6.1em;
- }
-
- .b-open .open-id p
- {
- margin-top: 0.4em;
- }
-
- .b-open .open-id a
- {
- text-decoration: none;
- }
-
- .b-open .open-id a:hover
- {
- text-decoration: underline;
- }
+.b-open
+{
+ margin-top: 60px;
+}
+
+.b-open form
+{
+ padding: 20px 20px;
+
+ background: #233e5e;
+}
+
+.b-open label
+{
+ font-size: 110%;
+ font-weight: bold;
+
+ vertical-align: middle;
+}
+
+.b-open input
+{
+ width: 255px;
+ margin-left: 0.5em;
+
+ vertical-align: middle;
+}
+
+.b-open .button
+{
+ margin-top: 20px;
+}
+
+.b-open .sign-in
+{
+ float: left;
+
+ margin: -6px 0 0 4.4em;
+}
+
+.b-open .note
+{
+ font-weight: bold;
+
+ margin-left: 1em;
+}
+
+.b-open-l
+{
+ float: left;
+
+ margin-right: 20px;
+}
+
+.b-open-l input {
+ border: 1px solid black;
+}
+
+.b-open-r
+{
+ float: left;
+
+ width: 12em;
+
+ color: #fa801f;
+}
+
+.b-open h3
+{
+ font-size: 110%;
+ font-weight: bold;
+}
+
+.b-open .open-id
+{
+ margin: 20px 0 0 6.1em;
+}
+
+.b-open .open-id p
+{
+ margin-top: 0.4em;
+}
+
+.b-open .open-id a
+{
+ text-decoration: none;
+}
+
+.b-open .open-id a:hover
+{
+ text-decoration: underline;
+}
/* OpenId (end) */ /**/
\ No newline at end of file
Added: incubator/esme/trunk/server/src/main/webapp/user_template/edit.html
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/user_template/edit.html?rev=784317&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/user_template/edit.html (added)
+++ incubator/esme/trunk/server/src/main/webapp/user_template/edit.html Sat Jun
13 00:20:38 2009
@@ -0,0 +1,4 @@
+<table><tr><td colspan="2">Edit your profile</td></tr>
+ <user:user_info/>
+ <tr><td> </td><td><user:submit/></td></tr>
+</table>
Added: incubator/esme/trunk/server/src/main/webapp/user_template/login.html
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/user_template/login.html?rev=784317&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/user_template/login.html (added)
+++ incubator/esme/trunk/server/src/main/webapp/user_template/login.html Sat
Jun 13 00:20:38 2009
@@ -0,0 +1,9 @@
+ <form id="openid_submit" class="clear" method="POST" action="/user_mgt/login"
>
+ <div class="b-open-l">
+ <p class="input"><label>Open ID</label><user:openid /></p>
+ <p class="button">
+ <img onclick="document.getElementById('openid_submit').submit()"
src="/images/sign-on.png" alt="Sign On" />
+ </p>
+ </div>
+
+ </form>