add a portlet showing the presence of other users; may be activated by the global option 'organize.showPresence'
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3466 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
c54b0e283c
commit
48701178bd
9 changed files with 149 additions and 2 deletions
|
@ -59,6 +59,7 @@ from loops import util
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
from loops.browser.common import BaseView
|
from loops.browser.common import BaseView
|
||||||
from loops.browser.concept import ConceptView
|
from loops.browser.concept import ConceptView
|
||||||
|
from loops.organize.interfaces import IPresence
|
||||||
from loops.organize.tracking import access
|
from loops.organize.tracking import access
|
||||||
from loops.versioning.util import getVersion
|
from loops.versioning.util import getVersion
|
||||||
|
|
||||||
|
@ -135,9 +136,21 @@ class NodeView(BaseView):
|
||||||
icon='cybertools.icons/user.png',
|
icon='cybertools.icons/user.png',
|
||||||
url=url,
|
url=url,
|
||||||
priority=10)
|
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
|
# force early portlet registrations by target by setting up target view
|
||||||
self.virtualTarget
|
self.virtualTarget
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def usersPresent(self):
|
||||||
|
presence = component.getUtility(IPresence)
|
||||||
|
presence.update(self.request.principal.id)
|
||||||
|
data = presence.getPresentUsers()
|
||||||
|
return data
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def view(self):
|
def view(self):
|
||||||
name = self.request.get('loops.viewName', '') or self.context.viewName
|
name = self.request.get('loops.viewName', '') or self.context.viewName
|
||||||
|
|
|
@ -236,6 +236,13 @@
|
||||||
</metal:actions>
|
</metal:actions>
|
||||||
|
|
||||||
|
|
||||||
|
<metal:actions define-macro="presence">
|
||||||
|
<tal:user repeat="user view/usersPresent">
|
||||||
|
<div tal:content="user" />
|
||||||
|
</tal:user>
|
||||||
|
</metal:actions>
|
||||||
|
|
||||||
|
|
||||||
<!-- inner HTML macros -->
|
<!-- inner HTML macros -->
|
||||||
|
|
||||||
<div metal:define-macro="inline_edit"
|
<div metal:define-macro="inline_edit"
|
||||||
|
|
Binary file not shown.
|
@ -3,7 +3,7 @@ msgstr ""
|
||||||
|
|
||||||
"Project-Id-Version: $Id$\n"
|
"Project-Id-Version: $Id$\n"
|
||||||
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
|
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
|
||||||
"PO-Revision-Date: 2009-07-13 12:00 CET\n"
|
"PO-Revision-Date: 2009-07-20 12:00 CET\n"
|
||||||
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
|
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
|
||||||
"Language-Team: loops developers <helmutm@cy55.de>\n"
|
"Language-Team: loops developers <helmutm@cy55.de>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
@ -158,6 +158,9 @@ msgstr "Aktuelles Objekt zu Lesezeichen hinzufügen"
|
||||||
msgid "Remove from favorites"
|
msgid "Remove from favorites"
|
||||||
msgstr "Aus Lesezeichen entfernen"
|
msgstr "Aus Lesezeichen entfernen"
|
||||||
|
|
||||||
|
msgid "Presence"
|
||||||
|
msgstr "Anwesenheit"
|
||||||
|
|
||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
msgstr "Aktionen"
|
msgstr "Aktionen"
|
||||||
|
|
||||||
|
|
|
@ -415,6 +415,13 @@ Send Email to Members
|
||||||
u'\n\nEvent #1\nhttp://127.0.0.1/loops/views/menu/.97\n\n'
|
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
|
Fin de partie
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@ from zope.app.security.interfaces import IAuthentication
|
||||||
from zope import schema
|
from zope import schema
|
||||||
from zope.traversing.api import getParent
|
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 _
|
from loops.util import _
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,6 +101,14 @@ class PersonBasedAuthenticator(Persistent, Contained):
|
||||||
return InternalPrincipal(self, login)
|
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):
|
class InternalPrincipal(object):
|
||||||
|
|
||||||
def __init__(self, auth, login):
|
def __init__(self, auth, login):
|
||||||
|
|
|
@ -50,6 +50,11 @@
|
||||||
|
|
||||||
<!-- authentication -->
|
<!-- authentication -->
|
||||||
|
|
||||||
|
<zope:utility
|
||||||
|
name="loops Session Credentials"
|
||||||
|
provides="zope.app.authentication.interfaces.ICredentialsPlugin"
|
||||||
|
factory="loops.organize.auth.LoopsSessionCredentialsPlugin" />
|
||||||
|
|
||||||
<zope:localUtility class="loops.organize.auth.PersonBasedAuthenticator">
|
<zope:localUtility class="loops.organize.auth.PersonBasedAuthenticator">
|
||||||
<require
|
<require
|
||||||
permission="zope.ManageServices"
|
permission="zope.ManageServices"
|
||||||
|
@ -72,6 +77,8 @@
|
||||||
<zope:adapter factory="loops.organize.setup.SetupManager"
|
<zope:adapter factory="loops.organize.setup.SetupManager"
|
||||||
name="organize" />
|
name="organize" />
|
||||||
|
|
||||||
|
<zope:utility factory="loops.organize.presence.Presence" />
|
||||||
|
|
||||||
<!-- include -->
|
<!-- include -->
|
||||||
|
|
||||||
<include package=".browser" />
|
<include package=".browser" />
|
||||||
|
|
|
@ -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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -177,3 +177,34 @@ class IAllocated(IConceptSchema):
|
||||||
)),
|
)),
|
||||||
default='standard',
|
default='standard',
|
||||||
required=True)
|
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.
|
||||||
|
"""
|
||||||
|
|
68
organize/presence.py
Normal file
68
organize/presence.py
Normal file
|
@ -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]
|
Loading…
Add table
Reference in a new issue