Author: vdichev
Date: Fri Jun 19 22:47:29 2009
New Revision: 786699

URL: http://svn.apache.org/viewvc?rev=786699&view=rev
Log:
Merge access pools from branch

Added:
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/AccessPoolMgr.scala
   (with props)
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AccessPool.scala
   (with props)
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Privilege.scala
   (with props)
    incubator/esme/trunk/server/src/main/webapp/pools_view/
    incubator/esme/trunk/server/src/main/webapp/pools_view/index.html   (with 
props)
Modified:
    incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
    incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Action.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala
    
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
    incubator/esme/trunk/server/src/main/webapp/templates-hidden/default.html
    incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html

Modified: 
incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala 
(original)
+++ incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala Fri 
Jun 19 22:47:29 2009
@@ -62,13 +62,15 @@
                                   Message, Mailbox, Tag,
                                   Group, Relationship, MessageTag,
                                   AuthToken, UrlStore, Tracking,
-                                  Action, DidPerform)
+                                  Action, DidPerform, AccessPool,
+                                  Privilege)
     }
 
     Schemifier.schemify(true, Log.infoF _, User, ExtSession, Message,
                         Mailbox, Tag,
                         Group, Relationship, MessageTag, AuthToken,
-                        UrlStore, Tracking, Action, DidPerform)
+                        UrlStore, Tracking, Action, DidPerform,
+                        AccessPool, Privilege)
 
     LiftRules.statelessDispatchTable.append {
       case r @ Req("api" :: "send_msg" :: Nil, "", PostRequest)
@@ -106,12 +108,14 @@
       Loc.Snippet("user_info", TagDisplay.userInfo))) ::
     Menu(Loc("conv", List("user_view", "conversation"), "Conversation", 
Hidden)) ::
     Menu(Loc("about", List("static", "about"), "About", Hidden)) ::
+    Menu(Loc("openid", List("static", "openid"), "OpenID", Hidden)) ::
     Menu(Loc("tag", List("info_view", "tag"), "Tag", Hidden, 
Loc.Snippet("tag_display", TagDisplay.display))) ::
     Menu(Loc("search", List("user_view", "search"), "Search", Hidden)) ::
     User.sitemap :::
     TrackMgr.menuItems :::
     ActionMgr.menuItems :::
-    AuthMgr.menuItems
+    AuthMgr.menuItems :::
+    AccessPoolMgr.menuItems
 
     LiftRules.setSiteMap(SiteMap(entries:_*))
 

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala
 (original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/Distributor.scala
 Fri Jun 19 22:47:29 2009
@@ -54,10 +54,11 @@
       case UserCreatedMessage(user, text, tags, when, 
                               metaData,
                               source,
-                              inReplyTo) =>
+                              inReplyTo,
+                              pool) =>
         val toact = findOrCreateUser(user)
         toact ! UserActor.CreateMessage(text, tags,
-                                        when, metaData, source, inReplyTo)
+                                        when, metaData, source, inReplyTo, 
pool)
         toact ! text
 
       case AddMessageToMailbox(user, message, reason) =>
@@ -89,6 +90,9 @@
 
       case PublicTimelineUnlisteners(who) =>
         listeners = listeners.filter(_ ne who)
+        
+      case AllowUserInPool(userId, poolId) =>
+        findOrCreateUser(userId) ! UserActor.AllowPool(poolId)
 
       case _ =>
     }
@@ -100,7 +104,8 @@
                                 when: Long,
                                 metaData: Box[Elem],
                                 source: String,
-                                replyTo: Box[Long])
+                                replyTo: Box[Long],
+                                pool: Box[Long])
   case class AddMessageToMailbox(user: Long, message: Message, reason: 
MailboxReason)
   case class Listen(user: Long, who: Actor)
   case class Unlisten(user: Long, who: Actor)
@@ -110,6 +115,7 @@
   case class UserUpdated(userId: Long)
   case class PublicTimelineListeners(who: Actor)
   case class PublicTimelineUnlisteners(who: Actor)
+  case class AllowUserInPool(userId: Long, poolId: Long)
   sealed trait TrackingType
   case object PerformTrackingType extends TrackingType
   case object TrackTrackingType extends TrackingType

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
 (original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
 Fri Jun 19 22:47:29 2009
@@ -42,13 +42,15 @@
   private[actor] case class CreateMessage(text: String, tags: List[String],
                                           when: Long, metaData: Box[Elem],
                                           source: String,
-                                          replyTo: Box[Long])
+                                          replyTo: Box[Long],
+                                          pool: Box[Long])
   private[actor] case class AddToMailbox(msg: Message, reason: MailboxReason)
   private[actor] case class Listen(who: Actor)
   private[actor] case class Unlisten(who: Actor)
   private[actor] case class LatestMessages(cnt: Int)
   private[actor] case class TestForTracking(msg: Message)
   private[actor] case class UpdateTracking(ttype: Distributor.TrackingType)
+  private[actor] case class AllowPool(poolId: Long)
   
   case class MessageReceived(msg: Message, reason: MailboxReason)
   
@@ -70,8 +72,12 @@
   private var perform: List[PerformMatcher] = Nil
   
   private var _mailbox: Array[Long] = Array()
+  
+  private var pools: Set[Long] = Set()
 
   private def followers: List[Long] = User.followerIdsForUserId(userId)
+  
+  private def canReadPool_?(poolId: Long) = pools contains poolId
 
   private case class RunFunc(f: () => Unit)
 
@@ -93,6 +99,8 @@
 
         _mailbox = Mailbox.mostRecentMessagesFor(userId, 500).
         map(_._1.id.is).toArray
+        
+        pools = Privilege.findViewablePools(userId)
 
         this ! UpdateTracking(Distributor.TrackTrackingType)
         this ! UpdateTracking(Distributor.PerformTrackingType)
@@ -100,17 +108,30 @@
       case RunFunc(f) =>
         f()
         
-      case CreateMessage(text, tags, when, metaData, source, replyTo) =>
-        val tagLst = tags.removeDuplicates.map(Tag.findOrCreate)
+      case CreateMessage(text, tags, when, metaData, source, replyTo, pool) =>
+        val tagLst = if (pool == None) 
+                       tags.removeDuplicates.map(Tag.findOrCreate)
+                     else Nil
 
         Message.create.author(userId).when(when).
         source(source).
-        setTextAndTags(text, tagLst, metaData).map{msg =>
+        setTextAndTags(text, tagLst, metaData).filter{ m =>
+          pool match {
+            case Full(p) => 
+              m.pool(p)
+              Privilege.hasPermission(userId, p, Permission.Write)
+            case _ => true
+          }
+        }.map{msg =>
           // do some security... only reply to messages
           // that are in our mailbox
           for (rt <- replyTo;
                mb <- Mailbox.find(By(Mailbox.message, rt),
-                                  By(Mailbox.user, userId))) msg.replyTo(rt)
+                                  By(Mailbox.user, userId));
+               rtm <- Message.find(mb.message.is)) 
+                 if (rtm.pool == msg.pool) msg.replyTo(rt)
+                 // workaround for compiler bug:
+                 else null
 
           msg.saveMe
 
@@ -135,12 +156,14 @@
         }
 
       case AddToMailbox(msg, reason) =>
-        addToMailbox(msg, reason)
+        if (!msg.pool.defined_? || canReadPool_?(msg.pool.is))
+          addToMailbox(msg, reason)
         
         
       case TestForTracking(msg) =>
-        for (t <- tracking.find(_.doesMatch_?(msg)))
-        this ! AddToMailbox(msg, TrackReason(t.trackId))
+        if (!msg.pool.defined_? || canReadPool_?(msg.pool.is))
+          for (t <- tracking.find(_.doesMatch_?(msg)))
+          this ! AddToMailbox(msg, TrackReason(t.trackId))
          
           
       case UpdateTracking(ttype) =>
@@ -164,6 +187,8 @@
     
       case LatestMessages(cnt) =>
         reply(_mailbox.take(cnt).toList)
+      
+      case AllowPool(poolId) => pools += poolId
     }
   }
 

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala 
(original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/api/RestAPI.scala 
Fri Jun 19 22:47:29 2009
@@ -97,6 +97,12 @@
         
     case Req("api" :: "delete_action" :: Nil, "", PostRequest) =>
       deleteAction _
+
+    case Req("api" :: "add_pool" :: poolName :: Nil, "", PostRequest) =>
+      () => addPool(poolName)
+
+    case Req("api" :: "add_user_pool" :: Nil, "", PostRequest) =>
+      addUserToPool
   }
 
   def findAction: Box[Action] =
@@ -287,6 +293,10 @@
          msg <- params.param("message") ?~ "Message not included")
     yield {
       val from: String = params.param("via") openOr "api"
+      val pool = for (poolName <- params.param("pool");
+                      p <- AccessPool.findPool(poolName,
+                        params.param("realm") openOr "Native")
+                      ) yield p.id.is
 
       val xml: Box[Elem] = params.param("metadata").flatMap(md =>
         tryo(XML.loadString(md)))
@@ -298,7 +308,8 @@
                                      millis,
                                      xml,
                                      from,
-                                     params.param("replyto").map(toLong))
+                                     params.param("replyto").map(toLong),
+                                     pool)
       true
     }
     r
@@ -340,6 +351,36 @@
     r
   }
 
+  def addPool(poolName: String): LiftResponse = {
+    val r: Box[Boolean] =
+    for (user <- User.currentUser;
+         pool <- AccessPool.create.realm("Native").setName(poolName);
+         privilegeSaved = Privilege.create.pool(pool.saveMe).user(user).
+           permission(Permission.Admin).save
+    ) yield privilegeSaved
+    
+    r
+  }
+  
+  def addUserToPool(): LiftResponse = {
+    val r: Box[Boolean] = 
+    for (adminUser <- User.currentUser;
+         poolName <- S.param("pool") ?~ "Pool not specified";
+         realm <- (S.param("realm") or Full("Native"));
+         pool <- AccessPool.findPool(poolName, realm) ?~ "Pool not found";
+         userName <- S.param("user") ?~ "User to add to pool not specified";
+         user <- User.findFromWeb(userName) ?~ "User not found";
+         permissionName <- (S.param("permission") or Full("Write"));
+         permission <- Box(Permission.valueOf(permissionName)) ?~ "Unknown 
permission type"
+    ) yield if(Privilege.hasPermission(adminUser.id.is, pool.id.is, 
Permission.Admin)) {
+      val result = 
Privilege.create.user(user).pool(pool).permission(permission).save
+      if (result) Distributor ! Distributor.AllowUserInPool(user.id.is, 
pool.id.is)
+      result
+    } else false // "User has no permission to administer pool"
+    
+    r
+  }
+  
   def createTag(in: NodeSeq) = <esme_api>{in}</esme_api>
 
   

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala
 (original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/comet/PublicTimeline.scala
 Fri Jun 19 22:47:29 2009
@@ -45,7 +45,8 @@
   override def localSetup() {
     super.localSetup()
     Distributor ! Distributor.PublicTimelineListeners(this) 
-    messages = Message.findAll(OrderBy(Message.id, Descending), 
+    messages = Message.findAll(By(Message.pool, Empty),
+        OrderBy(Message.id, Descending), 
                                MaxRows(40)).map(_.id.is)
   }
   
@@ -71,7 +72,8 @@
       reRender(false)
 
     case Distributor.NewMessage(msg) =>
-      messages = (msg.id.is :: messages).take(40)
+      if (!msg.pool.defined_?)
+        messages = (msg.id.is :: messages).take(40)
 
       if ((millis - lastRender) < 30000L) {
         if (!scheduled) {

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala 
(original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/Feed.scala 
Fri Jun 19 22:47:29 2009
@@ -52,7 +52,8 @@
           getDate(node),
           Empty,
           source,
-          Empty
+          Empty,
+          None
         )
     ).toList
   }

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala
 (original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/external/TwitterFeed.scala
 Fri Jun 19 22:47:29 2009
@@ -53,7 +53,8 @@
       tf.parse(node \ "created_at" text).getTime,
       Empty,
       "twiiter",
-      Empty
+      Empty,
+      None
     )).toList
   }
   

Added: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/AccessPoolMgr.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/AccessPoolMgr.scala?rev=786699&view=auto
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/AccessPoolMgr.scala
 (added)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/AccessPoolMgr.scala
 Fri Jun 19 22:47:29 2009
@@ -0,0 +1,165 @@
+/**
+ * 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 net.liftweb._
+import http._
+import SHtml._
+import js._
+import JsCmds._
+import JE._
+
+import sitemap._
+import Loc._
+
+import mapper._
+
+import util._
+import Helpers._
+
+import model._
+import org.apache.esme.actor.Distributor
+
+import scala.xml._
+
+/**
+ * Manage the sitemap and related snippets for Access Pools
+ */
+object AccessPoolMgr {
+  def loggedIn_? = User.loggedIn_?
+
+  val ifIsLoggedIn = If(loggedIn_? _, strFuncToFailMsg(() => S.?("You must be 
logged in")))
+
+  val menuItems =
+  Menu(Loc("accessPools", List("pools_view", "index"), "Manage Access Pools", 
ifIsLoggedIn,
+           Loc.Snippet("addPool", addPool),
+           Loc.Snippet("editPool", editPool),
+           Loc.Snippet("poolUsers", displayPoolUsers))) ::
+  Nil
+
+  object updatePool extends RequestVar[() => JsCmd](() => Noop)
+  object poolId extends RequestVar[Long](0)
+
+  def addPool(in: NodeSeq): NodeSeq = {
+    val theInput = "new_pool"
+    val user = User.currentUser
+    
+    def addNewPool(name: String) = {
+      name.trim match {
+        case x if x.length < 3 => S.error("Name too short")
+        case x => {
+          val pool = AccessPool.create.realm("Native").setName(name)
+          pool match {
+            case Failure(_,_,_) => S.error("Duplicate pool name!")
+            case Full(p: AccessPool) => val privilegeSaved =
+              
Privilege.create.pool(p.saveMe).user(user).permission(Permission.Admin).save
+              if(privilegeSaved) 
+                S.notice("New pool added")
+              else
+                S.error("Could not add pool!")
+            case _ => S.error("Could not add pool!")
+          }
+        }
+      }
+
+    }
+
+    bind("add", in,
+         "poolName" -> text("", addNewPool, "id" -> theInput)
+    )
+    
+  }
+
+  def editPool(in: NodeSeq): NodeSeq = {
+    val redisplayPool = updatePool.is
+    
+    var pool = ""
+    var username = ""
+    val editPoolName = "edit_pool"
+    val editUsername = "edit_username"
+    val editPermission = "edit_permission"
+    val adminUser = User.currentUser
+    
+    val adminPools = ("0", "--choose pool--") ::
+    (adminUser match {
+      case Full(u)=> Privilege.findAdminPools(u.id).map(
+        p => (p.toString, AccessPool.find(p).get.getName)).toList
+      case _ => Nil
+    })
+      
+    val permissions = Permission.map(perm => (perm.id.toString, 
perm.toString)).collect
+    
+    def addPoolUser(permission: String): JsCmd = {
+      val r: Box[Boolean] = 
+      for (admin <- adminUser;
+           p <- AccessPool.find(pool) ?~ "Pool not found";
+           user <- User.findFromWeb(username) ?~ "User not found"
+      ) yield if(Privilege.hasPermission(admin.id.is, p.id.is, 
Permission.Admin)) {
+        val result = 
Privilege.create.user(user).pool(p).permission(Permission(permission.toInt)).save
+        if (result) Distributor ! Distributor.AllowUserInPool(user.id.is, 
p.id.is)
+        result
+      } else false // "User has no permission to administer pool"
+      r match {
+        case Failure(m,_,_) => S.error(m)
+        case Full(true) => S.notice("Successfully set user privileges in pool")
+        case _ => S.error("Could not set user privileges in pool")
+      }
+      
+      poolId.set(pool.toLong)
+      redisplayPool() & SetValById(editUsername, "")
+    }
+
+    bind("edit", in,
+         "pool" -> ajaxSelect(adminPools, Empty, p => {pool = p;
+                                                       poolId.set(p.toLong);
+                                                       redisplayPool()},
+                                                 "id" -> editPoolName),
+         "username" -> text(username, username = _, "id" -> editUsername),
+         "permission" -> select(permissions, Empty, addPoolUser, "id" -> 
editPermission)
+    )
+    
+  }
+  
+  def displayPoolUsers(in: NodeSeq): NodeSeq = {
+    // get the span name to update
+    val spanName = S.attr("the_id") openOr "PoolSpan"
+    // get the current user
+    val user = User.currentUser
+
+    def doRender(): NodeSeq =
+    Privilege.findAll(By(Privilege.pool, poolId.is)) match {
+      case Nil => NodeSeq.Empty
+      case xs => bind("pool", in,
+                      "user" -> 
+                      (lst => xs.flatMap(i => bind("user", lst,
+                                                   "name" -> 
User.find(i.user).get.nickname.is,
+                                                   "privilege" -> 
i.permission.is.toString
+                      ))))
+    }
+    
+
+    def updateSpan(): JsCmd = SetHtml(spanName, doRender())
+
+    updatePool.set(updateSpan)
+    doRender
+ }
+}

Propchange: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/lib/AccessPoolMgr.scala
------------------------------------------------------------------------------
    svn:executable = *

Added: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AccessPool.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AccessPool.scala?rev=786699&view=auto
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AccessPool.scala
 (added)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AccessPool.scala
 Fri Jun 19 22:47:29 2009
@@ -0,0 +1,70 @@
+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
+ * 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 mapper._
+import http._
+import util._
+
+import scala.xml.Text
+
+object AccessPool extends AccessPool with LongKeyedMetaMapper[AccessPool] {
+
+  def findPool(name: String, realm: String): Box[AccessPool] = 
+    AccessPool.find(By(AccessPool.name,  name),
+                    By(AccessPool.realm, realm))
+
+}
+
+class AccessPool extends LongKeyedMapper[AccessPool] {
+  def getSingleton = AccessPool
+  def primaryKeyField = id
+
+  object id extends MappedLongIndex(this)
+
+  // is it worth having foreign key to another table?
+  object realm extends MappedString(this, 256)
+
+  private[model] object name extends MappedString(this, 256) {
+    
+    override def validations = checkDuplicate _ :: super.validations
+    
+    def checkDuplicate(in: String): List[FieldError] = 
+      sameName(in).map(p =>
+        FieldError(this, Text("Duplicate pool: " + in + " in realm " + 
p.realm.is ))
+      )
+    
+  }
+  
+  def setName(in: String) = sameName(in) match {
+    case Nil => Full(this.name(in))
+    case List(_,_*) => Failure("Duplicate access pool name!")
+  }
+  
+  def getName() = name.is
+
+  private def sameName(name: String) = 
+    AccessPool.findAll(By(AccessPool.name, name)).
+      filter(_.realm.is.equalsIgnoreCase(this.realm.is))
+  
+}

Propchange: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/AccessPool.scala
------------------------------------------------------------------------------
    svn:executable = *

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Action.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Action.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Action.scala 
(original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Action.scala 
Fri Jun 19 22:47:29 2009
@@ -155,7 +155,7 @@
             val lastMsg = if (msgList.isEmpty) None 
               else {
                 val m = msgList.first
-                Some(Distributor.UserCreatedMessage(user, m.getText, m.tags, 
m.when, Empty, m.source, Full(m.replyTo)))
+                Some(Distributor.UserCreatedMessage(user, m.getText, m.tags, 
m.when, Empty, m.source, Full(m.replyTo), None))
               }
 
             val feed = a match {

Modified: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala 
(original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Message.scala 
Fri Jun 19 22:47:29 2009
@@ -192,6 +192,8 @@
 
   object conversation extends MappedLongForeignKey(this, Message)
 
+  object pool extends MappedLongForeignKey(this, AccessPool)
+  
   private[model] def preload(users: Map[Long, User]) {
     author.can.foreach{
       id =>

Added: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Privilege.scala
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Privilege.scala?rev=786699&view=auto
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Privilege.scala
 (added)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Privilege.scala
 Fri Jun 19 22:47:29 2009
@@ -0,0 +1,74 @@
+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
+ * 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 mapper._
+import util._
+
+object Privilege extends Privilege with LongKeyedMetaMapper[Privilege] {
+  
+  override def beforeSave = deleteExisting _ :: super.beforeSave
+
+  private def deleteExisting(in: Privilege) {
+    findAll(By(pool, in.pool),
+            By(user, in.user)).
+    foreach(_.delete_!)
+  }
+  
+  def findViewablePools(userId: Long): Set[Long] =
+  Set(Privilege.findMap(
+    By(Privilege.user, userId)
+  )(p => Full(p.pool.is)) :_*)
+
+  def findWritablePools(userId: Long): Set[Long] = Set(Privilege.findMap(
+    By(Privilege.user, userId),
+    NotBy(Privilege.permission, Permission.Read)
+  )(p => Full(p.pool.is)) :_*)
+
+  def findAdminPools(userId: Long): Set[Long] = Set(Privilege.findMap(
+    By(Privilege.user, userId),
+    By(Privilege.permission, Permission.Admin)
+  )(p => Full(p.pool.is)) :_*)
+}
+
+class Privilege extends LongKeyedMapper[Privilege] {
+  def getSingleton = Privilege // what's the "meta" server
+  def primaryKeyField = id
+
+  object id extends MappedLongIndex(this)
+  object pool extends MappedLongForeignKey(this, AccessPool)
+  object user extends MappedLongForeignKey(this, User)
+  object permission extends MappedEnum(this, Permission)
+  
+  def hasPermission(userId: Long, poolId: Long, permission: Permission.Value) 
= Privilege.find(
+    By(user, userId),
+    By(pool, poolId)
+  ).map(_.permission.is >= permission).getOrElse(false)
+}
+
+object Permission extends Enumeration {
+  val Read = Value(0, "Read")
+  val Write = Value(1, "Write")
+  val Admin = Value(2, "Admin")
+}
+

Propchange: 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/model/Privilege.scala
------------------------------------------------------------------------------
    svn:executable = *

Modified: 
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=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
 (original)
+++ 
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
 Fri Jun 19 22:47:29 2009
@@ -46,13 +46,19 @@
 
         val replyTo: Box[Long] = map.get("reply-to").map(toLong)
 
+        val pool = map.get("access_pool").map(toLong) match {
+          case Some(x) if x > 0L => Some(x)
+          case _ => None
+        }
+        
         Distributor ! 
         Distributor.UserCreatedMessage(user.id, msg, 
                                        Tag.split(tags),
                                        millis, 
                                        Empty,
                                        "web",
-                                       replyTo)
+                                       replyTo,
+                                       pool)
       }
       Noop
 
@@ -67,7 +73,8 @@
       "followers" -> followers _,
       "following" -> following _,
       "loginForm" -> loginForm _,
-      "loggedIn" -> loggedInFilter _)
+      "loggedIn" -> loggedInFilter _,
+      "accessPools" -> accessPools _)
 
   def loggedInFilter(in: NodeSeq): NodeSeq = {
     val lookFor = if (User.loggedIn_?) "in" else "out"
@@ -109,6 +116,15 @@
     Text(User.currentUser.map(_.wholeName) openOr "")
   }
 
+  def accessPools(in: NodeSeq): NodeSeq = {
+    for(user <- User.currentUser.toSeq;
+        p    <- Privilege.findWritablePools(user.id))
+        // slow?
+      yield <option value={p}>
+              {AccessPool.find(p).get.getName}
+            </option>
+  }
+  
   def postScript(in: NodeSeq): NodeSeq =
   <xml:group>
     {Script(JsonPoster.is._2)}
@@ -116,9 +132,11 @@
                      JsonPoster.is._1("post",
                                       JsObj("msg" -> ValById("textdude"),
                                             "tags" -> ValById("tagdude"),
+                                            "access_pool" -> 
ValById("access_pool"),
                                             "reply-to" -> 
JsVar("currentConvNumber"))) &
                      SetValById("textdude", "") &
                      SetValById("tagdude", "") &
+                     SetValById("access_pool", "0") &
                      JsRaw("clearReplyTo();")
         ))
     }

Added: incubator/esme/trunk/server/src/main/webapp/pools_view/index.html
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/pools_view/index.html?rev=786699&view=auto
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/pools_view/index.html (added)
+++ incubator/esme/trunk/server/src/main/webapp/pools_view/index.html Fri Jun 
19 22:47:29 2009
@@ -0,0 +1,43 @@
+<lift:surround with="default" at="content">
+
+  Manage access pools: <br/>
+
+  <lift:form>
+    <lift:addPool>
+      Add pool with name: <add:poolName/>
+      <input type="submit" value="Add" />
+    </lift:addPool>
+  </lift:form>
+  
+  <span id="PoolSpan">
+    <lift:ignore>
+      <!--
+      The poolUsers snippet *MUST* appear on the page before
+      the editPool snippet
+      -->
+    </lift:ignore>
+    <lift:poolUsers the_id="PoolSpan">
+      <table>
+        <thead>
+          <tr> <th>User</th>  <th>Privilege</th> </tr>
+        </thead>
+        
+        <tbody>
+          <pool:user>
+            <tr> <td><user:name/></td>  <td><user:privilege/></td> </tr>
+          </pool:user>
+        </tbody>
+      </table>
+    </lift:poolUsers>
+  </span>
+
+
+  <lift:form>
+    <lift:editPool>
+      Edit user permissions within pool: <edit:pool/> <br/>
+      User name: <edit:username/> <edit:permission/> <br/>
+      <input type="submit" value="Edit" />
+    </lift:editPool>
+  </lift:form>
+  
+</lift:surround>

Propchange: incubator/esme/trunk/server/src/main/webapp/pools_view/index.html
------------------------------------------------------------------------------
    svn:executable = *

Modified: 
incubator/esme/trunk/server/src/main/webapp/templates-hidden/default.html
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/templates-hidden/default.html?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/templates-hidden/default.html 
(original)
+++ incubator/esme/trunk/server/src/main/webapp/templates-hidden/default.html 
Fri Jun 19 22:47:29 2009
@@ -24,6 +24,7 @@
           <li><lift:Menu.item name="trackMgt"/></li>
           <li><lift:Menu.item name="actionMgt"/></li>
           <li><lift:Menu.item name="authToken"/></li>
+          <li><lift:Menu.item name="accessPools"/></li>
           <li><lift:Menu.item name="Logout"/></li>
         </ul>
       </div>

Modified: 
incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html
URL: 
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html?rev=786699&r1=786698&r2=786699&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html 
(original)
+++ incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html 
Fri Jun 19 22:47:29 2009
@@ -101,6 +101,7 @@
           <li><lift:Menu.item name="trackMgt"/></li>
           <li><lift:Menu.item name="actionMgt"/></li>
           <li><lift:Menu.item name="authToken"/></li>
+          <li><lift:Menu.item name="accessPools"/></li>
           <li><lift:Menu.item name="Logout"/></li>
         </ul>
       </div>
@@ -243,6 +244,14 @@
                     </div>
                   </div>
 
+                  <div class="row clear">
+                    <label>Access pool</label>
+                    <select id="access_pool">
+                      <option id="0">--public--</option>
+                      <lift:UserSnip.accessPools />
+                    </select>
+                  </div>
+
                   <div class="row clear"><button class="btn" 
onclick="javascript:post_msg();">Update<!--<img src="/images/send-message.png" 
alt="Send Message" />--></button></div>
 
                   <script>


Reply via email to