diff --git a/browser/node.py b/browser/node.py
index f04e12a..b32c1ee 100644
--- a/browser/node.py
+++ b/browser/node.py
@@ -59,6 +59,7 @@ from loops import util
from loops.util import _
from loops.browser.common import BaseView
from loops.browser.concept import ConceptView
+from loops.organize.interfaces import IPresence
from loops.organize.tracking import access
from loops.versioning.util import getVersion
@@ -135,9 +136,21 @@ class NodeView(BaseView):
icon='cybertools.icons/user.png',
url=url,
priority=10)
+ if self.globalOptions('organize.showPresence'):
+ cm.register('portlet_right', 'presence', title=_(u'Presence'),
+ subMacro=node_macros.macros['presence'],
+ icon='cybertools.icons/group.png',
+ priority=11)
# force early portlet registrations by target by setting up target view
self.virtualTarget
+ @Lazy
+ def usersPresent(self):
+ presence = component.getUtility(IPresence)
+ presence.update(self.request.principal.id)
+ data = presence.getPresentUsers()
+ return data
+
@Lazy
def view(self):
name = self.request.get('loops.viewName', '') or self.context.viewName
diff --git a/browser/node_macros.pt b/browser/node_macros.pt
index 8944c32..0fc3916 100644
--- a/browser/node_macros.pt
+++ b/browser/node_macros.pt
@@ -236,6 +236,13 @@
+
+
+
+
+
+
+
\n"
"Language-Team: loops developers \n"
"MIME-Version: 1.0\n"
@@ -158,6 +158,9 @@ msgstr "Aktuelles Objekt zu Lesezeichen hinzufügen"
msgid "Remove from favorites"
msgstr "Aus Lesezeichen entfernen"
+msgid "Presence"
+msgstr "Anwesenheit"
+
msgid "Actions"
msgstr "Aktionen"
diff --git a/organize/README.txt b/organize/README.txt
index 99b15fc..af84133 100644
--- a/organize/README.txt
+++ b/organize/README.txt
@@ -415,6 +415,13 @@ Send Email to Members
u'\n\nEvent #1\nhttp://127.0.0.1/loops/views/menu/.97\n\n'
+Show Presence of Other Users
+============================
+
+ >>> from loops.organize.presence import Presence
+ >>> component.provideUtility(Presence())
+
+
Fin de partie
=============
diff --git a/organize/auth.py b/organize/auth.py
index b1819df..24690a1 100644
--- a/organize/auth.py
+++ b/organize/auth.py
@@ -34,6 +34,9 @@ from zope.app.security.interfaces import IAuthentication
from zope import schema
from zope.traversing.api import getParent
+from cybertools.browser.loops.auth import LoopsSessionCredentialsPlugin \
+ as BaseSessionCredentialsPlugin
+from loops.organize.interfaces import IPresence
from loops.util import _
@@ -98,6 +101,14 @@ class PersonBasedAuthenticator(Persistent, Contained):
return InternalPrincipal(self, login)
+class LoopsSessionCredentialsPlugin(BaseSessionCredentialsPlugin):
+
+ def logout(self, request):
+ presence = component.getUtility(IPresence)
+ presence.removePresentUser(request.principal.id)
+ super(LoopsSessionCredentialsPlugin, self).logout(request)
+
+
class InternalPrincipal(object):
def __init__(self, auth, login):
diff --git a/organize/configure.zcml b/organize/configure.zcml
index 2a6c454..7e075ab 100644
--- a/organize/configure.zcml
+++ b/organize/configure.zcml
@@ -50,6 +50,11 @@
+
+
+
+
diff --git a/organize/interfaces.py b/organize/interfaces.py
index 7b59a0b..6e35ea0 100644
--- a/organize/interfaces.py
+++ b/organize/interfaces.py
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -177,3 +177,34 @@ class IAllocated(IConceptSchema):
)),
default='standard',
required=True)
+
+
+# presence
+
+class IPresence(Interface):
+ """ Utility for getting information about active principals,
+ mapping principal.id to timestamp of last activity.
+ """
+
+ def update(self, principalId):
+ """ Update Dictionary of active users, by calling addPresentUser();
+ automaticly check for inactive users by calling
+ removeInactiveUsers().
+ """
+
+ def addPresentUser(self, principalId):
+ """Add a user to dictionary of active users.
+ """
+
+ def removeInactiveUsers(self):
+ """ Remove a user from dictionary of active users if user
+ didn't interact for last min_until_logout minutes.
+ """
+
+ def getPresentUsers(self):
+ """ Return list of titles of active users.
+ """
+
+ def removePresentUser(self, principalId):
+ """ Remove user from dictionary of active users, e.g. when user logs out.
+ """
diff --git a/organize/presence.py b/organize/presence.py
new file mode 100644
index 0000000..b1b951f
--- /dev/null
+++ b/organize/presence.py
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+Utility for collecting information about logged-in/active users.
+
+Author: Hannes Plattner.
+
+$Id$
+"""
+
+from zope.interface import implements
+from zope.cachedescriptors.property import Lazy
+
+from cybertools.meta.interfaces import IOptions
+from cybertools.util.date import getTimeStamp
+from loops.organize.interfaces import IPresence
+from loops.organize import util
+
+
+class Presence(object):
+
+ implements(IPresence)
+
+ def __init__(self, min_until_logout=10, presentUsers=None):
+ self.min_until_logout = min_until_logout
+ self.presentUsers = presentUsers or {}
+
+ def update(self, principalId):
+ self.addPresentUser(principalId)
+ self.removeInactiveUsers()
+
+ def addPresentUser(self, principalId):
+ self.presentUsers[principalId] = getTimeStamp()
+
+ def removeInactiveUsers(self):
+ toDelete = []
+ for id, timeStamp in self.presentUsers.iteritems():
+ if (getTimeStamp() - timeStamp) > (self.min_until_logout*60):
+ toDelete.append(id)
+ for id in toDelete:
+ if id in self.presentUsers:
+ del self.presentUsers[id]
+
+ def getPresentUsers(self):
+ ret = []
+ for id, timeStamp in self.presentUsers.iteritems():
+ ret.append(util.getPrincipalForUserId(id).title)
+ return ret
+
+ def removePresentUser(self, principalId):
+ if principalId in self.presentUsers:
+ del self.presentUsers[principalId]