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
	
	 helmutm
						helmutm