You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@esme.apache.org by dp...@apache.org on 2009/01/23 01:26:47 UTC

svn commit: r736864 [3/3] - in /incubator/esme/trunk/server: ./ src/main/scala/bootstrap/liftweb/ src/main/scala/org/ src/main/scala/org/apache/ src/main/scala/org/apache/esme/ src/main/scala/org/apache/esme/actor/ src/main/scala/org/apache/esme/api/ s...

Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,129 @@
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.esme.snippet
+
+import org.apache.esme._
+import model._
+import actor._
+
+import net.liftweb._
+import http._
+import js._
+import JsCmds._
+import JE._
+import util._
+import Helpers._
+
+import scala.xml.{NodeSeq, Text, Node}
+
+object JsonPoster extends SessionVar(S.buildJsonFunc{
+    case JsonCmd("post", _, map: Map[String, Any], _) =>
+      for (msgObj <- map.get("msg");
+           msg <- Box.isA(msgObj, classOf[String]).map(_.trim) if msg.length > 0;
+           tagObj <- map.get("tags");
+           tags <- Box.isA(tagObj, classOf[String]);
+           user <- User.currentUser) {
+        
+        val replyTo = map.get("reply-to").map(toLong) match {
+          case Some(x) if x > 0L => Full(x)
+          case _ => Empty
+        }
+        
+        Distributor ! 
+        Distributor.UserCreatedMessage(user.id, msg, 
+                                       Tag.split(tags),
+                                       millis, 
+                                       Empty,
+                                       "web",
+                                       replyTo)
+                                       
+      }
+      Noop
+
+    case _ => Noop
+  }
+)
+
+class UserSnip extends DispatchSnippet {
+  def dispatch: DispatchIt = 
+  Map("name" -> userName _,
+      "postScript" -> postScript _,
+      "followers" -> followers _,
+      "following" -> following _,
+      "loginForm" -> loginForm _,
+      "loggedIn" -> loggedInFilter _)
+
+  def loggedInFilter(in: NodeSeq): NodeSeq = {
+    val lookFor = if (User.loggedIn_?) "in" else "out"
+    
+    (<foo>{in}</foo> \ lookFor).toList.
+    filter(_.prefix == "logged").
+    map(_.child).firstOption.getOrElse(NodeSeq.Empty)
+  }
+
+  def userFmt(u: User): Node = 
+  <li><a href={"/user/"+urlEncode(u.nickname.is)}>{u.niceName}</a> {u.firstName} {u.lastName}</li>
+
+  def calcUser: Box[User] =
+  S.attr("userId").flatMap(s => User.find(toLong(s))) or User.currentUser
+
+  def followers(in: NodeSeq): NodeSeq = 
+  <ul>
+    {
+      calcUser.toList.flatMap(_.followers.map(userFmt))
+    }
+  </ul>
+
+  def following(in: NodeSeq): NodeSeq =
+  <ul>
+    {
+      calcUser.toList.flatMap(_.following.map(userFmt))
+    }
+  </ul>
+
+  def loginForm(in: NodeSeq): NodeSeq =
+    if (User.loggedIn_?) NodeSeq.Empty
+    else User.loginForm
+  
+
+  def userName(in: NodeSeq) = {
+    if (User.currentUser.map(_.needsChange_?) openOr false)
+    S.redirectTo("/user_mgt/edit")
+    
+    Text(User.currentUser.map(_.wholeName) openOr "")
+  }
+
+  def postScript(in: NodeSeq): NodeSeq =
+  <xml:group>
+    {Script(JsonPoster.is._2)}
+    {Script(Function("post_msg", List(),
+                     JsonPoster.is._1("post",
+                                      JsObj("msg" -> ValById("textdude"),
+                                            "tags" -> ValById("tagdude"),
+                                            "reply-to" -> JsVar("currentConvNumber"))) &
+                     SetValById("textdude", "") &
+                     SetValById("tagdude", "") &
+                     JsRaw("clearReplyTo();")
+        ))
+    }
+  </xml:group>
+}

Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/.keep
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/.keep?rev=736864&view=auto
==============================================================================
    (empty)

Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/ActionView.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/ActionView.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/ActionView.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/ActionView.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,174 @@
+package org.apache.esme.view
+
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import net.liftweb._
+import http._
+import js._
+import JE._
+import JsCmds._
+import SHtml._
+import util._
+import Helpers._
+import sitemap._
+import mapper._
+import Loc._
+
+import org.apache.esme._
+import model._
+
+import scala.xml.{NodeSeq}
+
+object ActionView {
+  def loggedIn_? = User.loggedIn_?
+  
+  val ifIsLoggedIn = If(loggedIn_? _, "You must be logged in")
+
+  val menuItems =
+  Menu(Loc("actionMgt", List("action_view", "index"), "Action Management", ifIsLoggedIn)) ::
+  Nil
+}
+
+class ActionView extends LiftView {
+  def dispatch = Map("index" -> index _)
+
+
+  def index() =
+  for (user <- User.currentUser) yield {
+    val spanName = "TokenSpan"
+    val theName = "theName"
+    val theAction = "theAction"
+    val theTest = "theTest"
+
+    var testText = ""
+    var nameText = ""
+
+    def redisplayActions: JsCmd = SetHtml(spanName, displayActions)
+    def saveIt(actionText: String): JsCmd = {
+
+      val toSave = Action.create.name(nameText).user(user)
+      DB.use(toSave.connectionIdentifier) {
+        ignore =>
+        val act: Box[List[FieldError]] =
+        for (a1 <- toSave.setAction(actionText);
+             a2 <- a1.setTest(testText))
+        yield a2.validate
+
+        act match {
+          case Full(Nil) =>
+            toSave.save
+            S.notice("Action added")
+            redisplayActions &
+            SetValById(theName, "") & SetValById(theAction, "") &
+            SetValById(theTest, "")
+
+          case Full(xs) => S.error(xs); Noop
+          case Failure(msg, _, _) => S.error(msg) ; Noop
+          case _ => Noop
+        }
+      }
+    }
+
+    def displayActions: NodeSeq =
+    Action.findAll(By(Action.user, user), By(Action.removed, false),
+                   OrderBy(Action.id, Ascending)) match {
+      case Nil => Nil
+      case xs =>
+        <table>
+          <tr>
+            <td>Name</td>
+            <td>Enabled</td>
+            <td>Test</td>
+            <td>Action</td>
+            <td>Remove</td>
+          </tr>
+          {
+            xs.map(at =>
+              <tr>
+                <td>{at.name}</td>
+                <td>Enabled: {ajaxCheckbox(!at.disabled, e => {at.disabled(!e).save; Noop})}</td>
+                <td><pre>{at.testText}</pre></td>
+                <td><pre>{at.actionText}</pre></td>
+                <td>
+                  {SHtml.ajaxButton("Remove", () => {at.removed(true).save ; redisplayActions})}
+                </td>
+              </tr>)
+          }
+        </table>
+    }
+
+    <lift:surround with="default" at="content">
+
+      Manage your Actions items: <br/>
+      <span id={spanName}>
+        {
+          displayActions
+        }
+      </span>
+
+      {
+        ajaxForm(
+          <xml:group>
+            <table>
+              <tr><td colspan="3">New Action</td></tr>
+              <tr>
+                <td>Name</td>
+                <td>{text("", nameText = _) % ("id" -> theName)}</td>
+              </tr>
+              <tr>
+                <td>Test</td>
+                <td>{textarea("", testText = _) % ("id" -> theTest)}</td>
+                <td>
+                  @foo -- sender is @foo<br />
+                  day = (0,1) -- sent on Sunday or Monday<br/>
+                  #moo -- contains the #moo tag<br/>
+                  50% -- success 50% of the time<br/>
+                  @foo &amp; 50% -- half the time, something sent by @foo<br/>
+                  login -- user has logged in<br/>
+                  followed -- user is being followed<br/>
+                  unfollowed -- user is being unfollowed<br/>
+                  profile -- user changed profile<br/>
+                  every N mins -- repeat action, N is an integer
+                </td>
+              </tr>
+
+              <tr>
+                <td>Action</td>
+                <td>{textarea("", saveIt) % ("id" -> theAction)}</td>
+                <td>
+                  filter -- not put in your timeline<br />
+                  resend -- sends the message to all your followers<br />
+                  mailto:foo@bar.com -- sends the message to foo@bar.com<br/>
+                  http://foo.com/message/in -- HTTP post, %s expands to message<br/>
+                  atom:http://blog.com/feed.atom -- posts new messages from Atom feed<br/>
+                  rss:http://blog.com/feed.rss -- posts new messages from RSS feed
+                  </td>
+              </tr>
+              <input type="submit" value="Add" />
+            </table>
+          </xml:group>
+        )
+      }
+
+    </lift:surround>
+  }
+}

Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Auth.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Auth.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Auth.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Auth.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,104 @@
+package org.apache.esme.view
+
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import net.liftweb._
+import http._
+import js._
+import JE._
+import JsCmds._
+import SHtml._
+import util._
+import Helpers._
+import sitemap._
+import Loc._
+
+import org.apache.esme._
+import model._
+
+import scala.xml.{NodeSeq}
+
+object Auth {
+  def loggedIn_? = User.loggedIn_?
+  
+  val ifIsLoggedIn = If(loggedIn_? _, "You must be logged in")
+
+  val menuItems =
+  Menu(Loc("authToken", List("auth", "index"), "Manage Tokens", ifIsLoggedIn)) ::
+  Nil
+}
+
+class Auth extends LiftView {
+  def dispatch = Map("index" -> index _)
+
+  def index() =
+  for (user <- User.currentUser) yield {
+    val spanName = "TokenSpan"
+    val theInput = "theInput"
+    def redisplayTokens: JsCmd = SetHtml(spanName, displayTokens)
+
+    def addAuthToken(desc: String): JsCmd = {
+      desc.trim match {
+        case x if x.length < 3 => S.error("Description too short")
+        case x => AuthToken.create.description(x).user(user).saveMe
+          S.notice("New token added")
+      }
+
+      redisplayTokens & SetValById(theInput, "")
+    }
+
+    def displayTokens: NodeSeq =
+    user.authTokens match {
+      case Nil => Nil
+      case xs =>
+        <ul>
+          {
+            xs.map(at => <li>{at.description} token: {at.uniqueId}
+                {SHtml.ajaxButton("Revoke",
+                                  () => {at.delete_!
+                                             redisplayTokens})}</li>)
+          }
+        </ul>
+    }
+
+    <lift:surround with="default" at="content">
+
+      Manage your external application authentication tokens: <br/>
+      <span id={spanName}>
+      {
+        displayTokens
+      }
+      </span>
+
+      {
+        ajaxForm(
+          <xml:group>
+            Create a new Authentication Token.<br />
+            Description: {text("", addAuthToken) % ("id" -> theInput)}
+            <input type="submit" value="Add" />
+          </xml:group>
+        )
+      }
+
+    </lift:surround>
+  }
+}

Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Track.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Track.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Track.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/Track.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,107 @@
+package org.apache.esme.view
+
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import net.liftweb._
+import http._
+import js._
+import JE._
+import JsCmds._
+import SHtml._
+import util._
+import Helpers._
+import sitemap._
+import mapper._
+import Loc._
+
+import org.apache.esme._
+import model._
+
+import scala.xml.{NodeSeq}
+
+object Track {
+  def loggedIn_? = User.loggedIn_?
+  
+  val ifIsLoggedIn = If(loggedIn_? _, "You must be logged in")
+
+  val menuItems =
+  Menu(Loc("trackMgt", List("track", "index"), "Item Tracking", ifIsLoggedIn)) ::
+  Nil
+}
+
+class Track extends LiftView {
+  def dispatch = Map("index" -> index _)
+  //{
+  //case "index" => index
+//}
+
+  def index() =
+  for (user <- User.currentUser) yield {
+    val spanName = "TokenSpan"
+    val theInput = "theInput"
+    def redisplayTracking: JsCmd = SetHtml(spanName, displayTracking)
+
+    def addTrack(what: String): JsCmd = {
+      what.trim match {
+        case x if x.length < 3 => S.error("Too short")
+        case x => Tracking.create.regex(x).user(user).saveMe
+          S.notice("Now tracking "+x)
+      }
+
+      redisplayTracking & SetValById(theInput, "")
+    }
+
+    def displayTracking: NodeSeq =
+    Tracking.findAll(By(Tracking.user, user), By(Tracking.removed, false),
+    OrderBy(Tracking.id, Ascending)) match {
+      case Nil => Nil
+      case xs =>
+        <ul>
+          {
+            xs.map(at => <li>{at.pattern} Enabled: {ajaxCheckbox(!at.disabled, e => {at.disabled(!e).save; Noop})}
+                {SHtml.ajaxButton("Remove", () => {at.removed(true).save ; redisplayTracking})}</li>)
+          }
+        </ul>
+    }
+
+    <lift:surround with="default" at="content">
+
+      Manage your tracking items: <br/>
+      <span id={spanName}>
+      {
+        displayTracking
+      }
+      </span>
+
+      {
+        ajaxForm(
+          <xml:group>
+            Track something new.<br />
+            What? {text("", addTrack) % ("id" -> theInput)}
+            <input type="submit" value="Add" />
+          </xml:group>
+        )
+      }
+
+    </lift:surround>
+  }
+}

Added: incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/UserView.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/UserView.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/UserView.scala (added)
+++ incubator/esme/trunk/server/src/main/scala/org/apache/esme/view/UserView.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,256 @@
+package org.apache.esme.view
+
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import net.liftweb._
+import http._
+import js._
+import JE._
+import JsCmds._
+import SHtml._
+import util._
+import Helpers._
+import sitemap._
+import mapper._
+import Loc._
+
+import org.apache.esme._
+import model._
+import lib._
+
+import scala.xml.{NodeSeq, Text, Elem}
+
+import java.util.logging._
+
+object UserView extends LiftView /* with MsgFormat */ {
+  val logger: Logger = Logger.getLogger("org.apache.esme.view")
+  logger.setLevel(Level.INFO)
+  def loggedIn_? = User.loggedIn_?
+  
+  val ifIsLoggedIn = If(loggedIn_? _, "You must be logged in")
+
+  val menuItems =
+  Menu(Loc("userView", List("user_view", "index"), "View User", Hidden)) ::
+  Nil
+
+  implicit def fToO(in: () => NodeSeq): Box[() => NodeSeq] = Full(in)
+  implicit def fToO2(in: Elem): Box[NodeSeq] = Full(in)
+
+  val dispatch = 
+  Map("index" -> index _,
+      "tag" -> displayTag _,
+      "search" -> search _,
+      "all" -> allUsers _,
+      "conversation" -> conversation _)
+
+  def conversation(): Box[NodeSeq] =   {
+    val cid = S.param("cid").map(toLong).openOr(-1L)
+
+    val msgs = Message.findAndPrime(By(Message.conversation, cid),
+                                    OrderBy(Message.id, Ascending))
+
+    val replyMap = msgs.foldLeft[Map[Long, List[Message]]](Map.empty){
+      case (map, msg) => msg.replyTo.can match {
+          case Full(rt) => map + (rt -> (msg :: map.getOrElse(rt, Nil)))
+          case _ => map
+        }
+    }
+    
+    val start = msgs.firstOption.toList
+
+    def show(what: List[Message]): NodeSeq = {
+      what match {
+        case Nil => Text("")
+        case _ =>
+          <ul>
+            {
+              what.zipWithIndex.map{
+                case (m, idx) =>
+                  <span id={"m_"+idx}>
+                    {Script(OnLoad(JsFunc("displayMessages", JsArray(m.asJs), "m_"+idx).cmd))
+                    }
+                  </span>
+              }
+            }
+          </ul>
+      }
+    }
+    
+    <lift:surround with="default" at="content">
+      <div>
+        <fieldset>
+          <legend>Conversation</legend>
+          {
+            show(start)
+          }
+        </fieldset>
+      </div>
+
+    </lift:surround>
+  }
+
+  def index(): Box[NodeSeq] =
+  (<lift:surround with="default" at="content">
+      {
+        val ui = for (uid <- S.param("uid");
+                      user <- User.findFromWeb(uid)) yield {
+          def updateFollow: JsCmd = SetHtml("following", followOrNot)
+
+          def followOrNot: NodeSeq = {
+            User.currentUser match {
+              case Full(u) if u != user =>
+                if (u.following_?(user))
+                ajaxButton("Unfollow", () => {u.unfollow(user); updateFollow})
+                else ajaxButton("Follow", () => {u.follow(user); updateFollow})
+
+              case _ => <xml:group />
+            }
+          }
+
+          <div>
+            <div id="tabs" class="b-view tab1">
+              <dl class="messages">
+                <dt class="caption">{user.niceName}'s Timeline 
+                  <span id="following">{followOrNot}</span></dt>
+                <dd>
+                  {
+                    val lst: List[Message] =
+                    Mailbox.mostRecentMessagesFor(user.id, 50).map(_._1)
+                    //  user.latestMessages(50) openOr Nil
+                    lst.zipWithIndex.map{
+                      case (m, idx) =>
+                        <span id={"m2_"+idx}>
+                          {
+                            Script(OnLoad(JsFunc("displayMessages", JsArray(m.asJs), "m2_"+idx).cmd))
+                          }
+                        </span>
+                    }
+                  }
+                </dd>
+              </dl>
+		  
+              <dl class="tagclouds">
+                <dt class="caption">{user.niceName}'s Messages</dt>
+               
+                <dd class="b-clouds">
+                  {
+                    val lst: List[Message] =
+                    Message.findAll(By(Message.author, user), MaxRows(50), OrderBy(Message.id, Descending))
+                    lst.zipWithIndex.map{
+                      case (m, idx) =>
+                        <span id={"m3_"+idx}>
+                          {
+                            Script(OnLoad(JsFunc("displayMessages", JsArray(m.asJs), "m3_"+idx).cmd))
+                          }
+                        </span>
+                    }
+                  }
+                </dd>
+              </dl>
+              <dl class="contacts">
+                <dt class="caption">
+                  Contacts
+                </dt>
+
+                <dd class="b-contacts" style="height: 240px; overflow: auto">
+                  Following:
+                  <lift:UserSnip.following userId={user.id.toString}/>
+                </dd>
+                <dd class="b-contacts" style="height: 240px; overflow: auto">
+                  Followers:
+                  <lift:UserSnip.followers userId={user.id.toString}/>
+                </dd>
+              </dl>
+            </div>
+          </div>
+        }
+        ui openOr (<span>User Not Found</span>)
+      }
+   </lift:surround>)
+  
+  def search(): Box[NodeSeq] =
+  for (user <- User.currentUser;
+       term <- S.param("term")) yield
+  <lift:surround with="default" at="content">
+    <div>
+      <fieldset>
+        <legend>Search: {term}</legend>
+        <ul>
+          {
+            val lst: List[Message] = Message.search(term, user.following, 50)
+            lst.zipWithIndex.map{
+              case (m, idx) =>
+                <span id={"m4_"+idx}>
+                  {
+                    Script(OnLoad(JsFunc("displayMessages", JsArray(m.asJs), "m4_"+idx).cmd))
+                  }
+                </span>
+            }
+          }
+        </ul>
+      </fieldset>
+    </div>
+  </lift:surround>
+  
+  def displayTag(): Box[NodeSeq] =
+  (<lift:surround with="default" at="content">
+      {
+        val ui = for (tagText <- S.param("tag").map(_.trim);
+                      tag <- Tag.find(By(Tag.name, tagText))) yield {
+          val lst = tag.findMessages()
+          
+          (<div>
+              <fieldset>
+                <legend>{tag.name}</legend>
+                <ul>
+                  {
+                    lst.zipWithIndex.map{
+                      case (m, idx) =>
+                        <span id={"m5_"+idx}>
+                          {
+                            Script(OnLoad(JsFunc("displayMessages", JsArray(m.asJs), "m5_"+idx).cmd))
+                          }
+                        </span>
+                    }
+                  }
+                </ul>
+              </fieldset>
+           </div>)
+        }
+        ui openOr (<span>Tag Not Found</span>)
+      }
+   </lift:surround>)
+  
+  def allUsers(): Box[NodeSeq] = 
+  <lift:surround with="default" at="content">
+    <fieldset>
+      <legend>Users</legend>
+      <ul>
+        {
+          User.findAll(OrderBy(User.nickname, Ascending)).
+          map(u => <li><a href={"/user/"+urlEncode(u.nickname.is)}>{u.niceName}</a> {u.firstName} {u.lastName}</li>)
+        }
+      </ul>
+    </fieldset>
+  </lift:surround>
+
+}

Added: incubator/esme/trunk/server/src/test/scala/org/apache/esme/AppTest.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/test/scala/org/apache/esme/AppTest.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/test/scala/org/apache/esme/AppTest.scala (added)
+++ incubator/esme/trunk/server/src/test/scala/org/apache/esme/AppTest.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,50 @@
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.esme;
+
+import _root_.junit.framework._;
+import Assert._;
+
+object AppTest {
+    def suite: Test = {
+        val suite = new TestSuite(classOf[AppTest]);
+        suite
+    }
+
+    def main(args : Array[String]) {
+        _root_.junit.textui.TestRunner.run(suite);
+    }
+}
+
+/**
+ * Unit test for simple App.
+ */
+class AppTest extends TestCase("app") {
+
+    /**
+     * Rigourous Tests :-)
+     */
+    def testOK() = assertTrue(true);
+    //def testKO() = assertTrue(false);
+
+
+}

Added: incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala
URL: http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala?rev=736864&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala (added)
+++ incubator/esme/trunk/server/src/test/scala/org/apache/esme/lib/MsgParseTest.scala Thu Jan 22 16:26:46 2009
@@ -0,0 +1,346 @@
+/**
+ * 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
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.esme.lib
+
+import org.specs._
+import org.specs.runner.JUnit3
+import org.specs.runner.ConsoleRunner
+import net.liftweb.util._
+import org.specs.matcher._
+import Helpers._
+import MsgParser._
+import net.sourceforge.jwebunit.junit.WebTester
+import org.mortbay.jetty.Server
+//import org.mortbay.jetty.servlet.Context
+import org.mortbay.jetty.servlet.ServletHolder
+import org.mortbay.jetty.webapp.WebAppContext
+import org.apache.esme._
+import model._
+
+import net.sourceforge.jwebunit.junit.WebTester
+import _root_.junit.framework.AssertionFailedError
+
+class MsgParserSpecsAsTest extends JUnit3(MsgParserSpecs)
+object MsgParserSpecsRunner extends ConsoleRunner(MsgParserSpecs)
+
+object MsgParserSpecs extends Specification {
+  JettyTestServer.start()
+  
+  type PFT = MsgParser.ParseResult[_]
+  def parseMatch(name: String, matchr: PartialFunction[PFT, Any]) = new Matcher[PFT] {
+    def apply(v: => PFT) = (matchr.isDefinedAt(v), 
+                            name+" succeeded parsing",
+                            name+" failed parsing")
+  }
+  
+  "Msg Parser Parse" should {
+    "parse top label" in {
+      val ret = MsgParser.toplabel("www")
+   
+      ret must
+      parseMatch("www", {
+          case MsgParser.Success("www", _) =>
+        })
+    }
+
+    "parse a simple URL" in {
+      MsgParser.httpUrl("http://www.google.com") must
+      parseMatch("google", {
+          case MsgParser.Success("http://www.google.com", _) =>
+        })
+    }
+    
+    "parse a complex simple URL" in {
+      val Str = "http://www.google.com:8080/Foo/bar"
+        
+      val ret = MsgParser.httpUrl(Str)
+
+      ret must
+      parseMatch("google + port", {
+          case MsgParser.Success(Str, _) =>
+        })
+    }
+    
+    "parse a complex URL with query params" in {
+      val Str = "https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/10754"
+      
+      val ret = MsgParser.httpUrl(Str)
+
+      ret must
+      parseMatch("sap thing", {
+          case MsgParser.Success(Str, _) =>
+        })
+    }
+    
+    "Fail for an empty string" in {
+      MsgParser.begin("") must
+      parseMatch("Empty",
+                 {case MsgParser.Error(_, _) =>
+          case MsgParser.Failure(_, _) =>
+        })
+					    
+    }
+    
+    "Find only text" in {
+      MsgParser.begin("This is a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) :: Nil, _) =>
+		   
+        })
+					    
+    }
+    
+    "Find only text" in {
+      MsgParser.begin("This is http://$$$ a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) :: Nil, _) =>
+		   
+        })
+					    
+    }
+
+    "Find only text" in {
+      MsgParser.begin("This is #,hello a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) :: Nil, _) =>
+		   
+        })
+					    
+    }
+
+    "Find only text" in {
+      MsgParser.begin("This is @--Moose a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) :: Nil, _) =>
+		   
+        })
+					    
+    }
+
+    "Find a hash tag" in {
+      MsgParser.begin("This is #hash a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) ::
+                                         HashTag(tag) ::
+                                         MsgText(_) :: Nil, _)
+            if tag.name.equalsIgnoreCase("hash") =>
+        }
+      )
+					    
+    }
+
+    "Find a @msg tag" in {
+      val ret = MsgParser.begin("This is @hash, a message")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) ::
+                                         AtName(_) ::
+                                         MsgText(_) :: Nil, _) =>
+        }
+      )
+					    
+    }
+    
+    "Find a url tag" in {
+      MsgParser.begin("This is http://www.moo.com a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) ::
+                                         URL(_) ::
+                                         MsgText(_) :: Nil, _) =>
+        }
+      )
+					    
+    }
+    
+    "Find a https url tag" in {
+      MsgParser.begin("This is https://www.moo.com a message") must
+      parseMatch("only test",
+                 {case MsgParser.Success(MsgText(_) ::
+                                         URL(_) ::
+                                         MsgText(_) :: Nil, _) =>
+        }
+      )
+					    
+    }
+    
+    "match 'any'" in {
+      val ret =  MsgParser._testMessage("any")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(AnyAction, _) =>
+        }
+      )
+					    
+    }
+    
+    
+    "match '54%'" in {
+      val ret =  MsgParser._testMessage(" 54% ")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(PercentAction(54), _) =>
+        }
+      )
+					    
+    }
+    
+    "match '#foo'" in {
+      val ret =  MsgParser._testMessage(" #foo ")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(HashAction(_, _), _) =>
+        }
+      )
+					    
+    }
+
+    /*
+    
+    "match '@hash'" in {
+      println("About to test @hash")
+      val ret =  MsgParser._testMessage(" @hash ")
+
+      println("hash is is "+ret)
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(AtUserAction(_), _) =>
+        }
+      )
+					    
+    }
+    */
+
+    "match '@hasher'" in {
+      val ret =  MsgParser._testMessage(" @hasher ")
+
+      ret must
+      parseMatch(" @hasher ",
+                 {case MsgParser.Failure(_ , _) =>
+        }
+      )
+					    
+    }
+    
+    "match 'day = 4'" in {
+      val ret =  MsgParser._testMessage(" day = 4 ")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(DateTestAction(DayDateType, EqOpr, List(4)), _) =>
+        }
+      )
+					    
+    }
+    
+    "match 'month = (1,2,4)'" in {
+      val ret =  MsgParser._testMessage(" month = (1, 2,     4   ) ")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(DateTestAction(MonthDateType, EqOpr, List(1, 2, 4)), _) =>
+        }
+      )
+					    
+    }
+    
+    "match 'month = (1,2,4) | 74%'" in {
+      val ret =  MsgParser._testMessage(" month = (1, 2,     4   )|74% ")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(OrAction(DateTestAction(MonthDateType, EqOpr, List(1, 2, 4)), PercentAction(74)), _) =>
+        }
+      )
+					    
+    }
+    
+    "match '( month = (1, 2,     4   )|74%) & #frog'" in {
+      val ret =  MsgParser._testMessage("( month = (1, 2,     4   )|74%) & #frog ")
+
+      ret must
+      parseMatch("only test",
+                 {case MsgParser.Success(AndAction(
+                ParenAction(OrAction(DateTestAction(MonthDateType, EqOpr, List(1, 2, 4)), PercentAction(74))),
+                  HashAction(_, _)), _) =>
+        })
+					    
+    }
+    
+  }
+
+}
+
+
+object JettyTestServer {
+  private val serverPort_ = System.getProperty("SERVLET_PORT", "8989").toInt
+  private var baseUrl_ = "http://127.0.0.1:" + serverPort_
+
+  System.setProperty("run.mode", "test")
+
+  private val server_ : Server = {
+    val server = new Server(serverPort_)
+    val context = new WebAppContext()
+    context.setServer(server)
+    context.setContextPath("/")
+    context.setWar("src/main/webapp")
+    //val context = new Context(_server, "/", Context.SESSIONS)
+    //context.addFilter(new FilterHolder(new LiftFilter()), "/");
+    server.addHandler(context)
+    server
+  }
+
+  def urlFor(path: String) = baseUrl_ + path
+
+  def start() = {
+    server_.start()
+    User.create.nickname("hash").save
+  }
+
+  def stop() = {
+    server_.stop()
+    server_.join()
+  }
+
+  def browse(startPath: String, f:(WebTester) => Unit) = {
+    val wc = new WebTester()
+    try {
+      wc.setScriptingEnabled(false)
+      wc.beginAt(JettyTestServer.urlFor(startPath))
+      f(wc)
+    } catch {
+      case exc: AssertionFailedError => {
+          System.err.println("serveur response: ", wc.getServeurResponse())
+          throw exc
+        }
+    } finally {
+      wc.closeBrowser()
+    }
+  }
+
+}
+
+