move stateful and process to organize; work in progress: organize.tracking
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2509 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									2c6cdafe10
								
							
						
					
					
						commit
						4d5bfed47f
					
				
					 24 changed files with 328 additions and 81 deletions
				
			
		
							
								
								
									
										22
									
								
								common.py
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								common.py
									
										
									
									
									
								
							|  | @ -58,29 +58,23 @@ def adapted(obj, langInfo=None): | |||
| 
 | ||||
| # helper functions for specifying automatic attribute handling | ||||
| 
 | ||||
| def adapterAttributes(*args): | ||||
| def collectAttributeNames(lst, name): | ||||
|     attrs = [] | ||||
|     for arg in args: | ||||
|     for arg in lst: | ||||
|         if isinstance(arg, basestring): | ||||
|             attrs.append(arg) | ||||
|         elif isinstance(arg, type): | ||||
|             attrs.extend(list(arg._adapterAttributes)) | ||||
|             attrs.extend(list(getattr(arg, name))) | ||||
|         else: | ||||
|             raise ValueError("Argument must be string or class, '%s' is '%s'." % | ||||
|                              (arg, type(arg))) | ||||
|     return tuple(attrs) | ||||
| 
 | ||||
| def adapterAttributes(*args): | ||||
|     return collectAttributeNames(args, '_adapterAttributes') | ||||
| 
 | ||||
| def contextAttributes(*args): | ||||
|     attrs = [] | ||||
|     for arg in args: | ||||
|         if isinstance(arg, basestring): | ||||
|             attrs.append(arg) | ||||
|         elif isinstance(arg, type): | ||||
|             attrs.extend(list(arg._contextAttributes)) | ||||
|         else: | ||||
|             raise ValueError("Argument must be string or class, '%s' is '%s'." % | ||||
|                              (arg, type(arg))) | ||||
|     return attrs | ||||
|     return collectAttributeNames(args, '_contextAttributes') | ||||
| 
 | ||||
| 
 | ||||
| # type interface adapters | ||||
|  | @ -133,7 +127,7 @@ class ResourceAdapterBase(AdapterBase): | |||
|     implements(IStorageInfo) | ||||
|     adapts(IResource) | ||||
| 
 | ||||
|     _adapterAttributes = ('storageName', 'storageParams', ) + AdapterBase._adapterAttributes | ||||
|     _adapterAttributes = adapterAttributes('storageName', 'storageParams', AdapterBase) | ||||
|     _contextAttributes = list(IResourceAdapter) | ||||
| 
 | ||||
|     storageName = None | ||||
|  |  | |||
|  | @ -331,6 +331,7 @@ | |||
|   <adapter factory="loops.external.NodesExporter" /> | ||||
|   <adapter factory="loops.external.NodesImporter" />--> | ||||
| 
 | ||||
|   <!-- obsolete - remove: --> | ||||
|   <adapter factory="loops.target.DocumentProxy" | ||||
|            permission="zope.ManageContent" /> | ||||
|   <adapter factory="loops.target.MediaAssetProxy" | ||||
|  | @ -405,11 +406,9 @@ | |||
|   <include package=".integrator" /> | ||||
|   <include package=".knowledge" /> | ||||
|   <include package=".organize" /> | ||||
|   <include package=".process" /> | ||||
|   <include package=".rest" /> | ||||
|   <include package=".search" /> | ||||
|   <include package=".security" /> | ||||
|   <include package=".stateful" /> | ||||
|   <include package=".versioning" /> | ||||
|   <include package=".xmlrpc" /> | ||||
| 
 | ||||
|  |  | |||
|  | @ -43,7 +43,6 @@ | |||
| 
 | ||||
|   <zope:adapter factory="loops.organize.member.MemberRegistrationManager" | ||||
|                 trusted="True" /> | ||||
| 
 | ||||
|   <zope:class class="loops.organize.member.MemberRegistrationManager"> | ||||
|     <require permission ="zope.Public" | ||||
|              interface="loops.organize.interfaces.IMemberRegistrationManager" /> | ||||
|  | @ -70,5 +69,8 @@ | |||
| 
 | ||||
|   <include package=".browser" /> | ||||
|   <include package=".personal" /> | ||||
|   <include package=".process" /> | ||||
|   <include package=".stateful" /> | ||||
|   <include package=".tracking" /> | ||||
| 
 | ||||
| </configure> | ||||
|  |  | |||
|  | @ -38,55 +38,9 @@ In order to be able to login and store favorites and other personal data | |||
| we have to prepare our environment. We need some basic adapter registrations, | ||||
| and a pluggable authentication utility with a principal folder. | ||||
| 
 | ||||
|   >>> from loops.organize.tests import setupUtilitiesAndAdapters | ||||
|   >>> setupData = setupUtilitiesAndAdapters(loopsRoot) | ||||
| 
 | ||||
|   >>> from zope.app.appsetup.bootstrap import ensureUtility | ||||
|   >>> from zope.app.authentication.authentication import PluggableAuthentication | ||||
|   >>> from zope.app.security.interfaces import IAuthentication | ||||
|   >>> ensureUtility(site, IAuthentication, '', PluggableAuthentication, | ||||
|   ...               copy_to_zlog=False, asObject=True) | ||||
|   <...PluggableAuthentication...> | ||||
|   >>> pau = component.getUtility(IAuthentication, context=site) | ||||
| 
 | ||||
|   >>> from zope.app.authentication.principalfolder import PrincipalFolder | ||||
|   >>> from zope.app.authentication.interfaces import IAuthenticatorPlugin | ||||
|   >>> pFolder = PrincipalFolder('users.') | ||||
|   >>> pau['users'] = pFolder | ||||
|   >>> pau.authenticatorPlugins = ('users',) | ||||
| 
 | ||||
| So we can now register a user ... | ||||
| 
 | ||||
|   >>> from zope.app.authentication.principalfolder import InternalPrincipal | ||||
|   >>> pFolder['john'] = InternalPrincipal('john', 'xx', u'John') | ||||
|   >>> from zope.app.authentication.principalfolder import FoundPrincipalFactory | ||||
|   >>> component.provideAdapter(FoundPrincipalFactory) | ||||
| 
 | ||||
| ... and create a corresponding person. | ||||
| 
 | ||||
|   >>> from loops.concept import Concept | ||||
|   >>> johnC = concepts['john'] = Concept(u'John') | ||||
|   >>> person = concepts['person'] | ||||
|   >>> johnC.conceptType = person | ||||
|   >>> from loops.common import adapted | ||||
|   >>> adapted(johnC).userId = 'users.john' | ||||
| 
 | ||||
| Finally, we log in as the newly created user. | ||||
| 
 | ||||
|   >>> from zope.app.authentication.principalfolder import Principal | ||||
|   >>> pJohn = Principal('users.john', 'xxx', u'John') | ||||
| 
 | ||||
|   >>> from loops.tests.auth import login | ||||
|   >>> login(pJohn) | ||||
| 
 | ||||
| One step is still missing: As we are now working with a real principal | ||||
| the security checks e.g. in views are active. So we have to provide | ||||
| our user with the necessary permissions. | ||||
| 
 | ||||
|   >>> grantPermission = setupData.rolePermissions.grantPermissionToRole | ||||
|   >>> assignRole = setupData.principalRoles.assignRoleToPrincipal | ||||
|   >>> grantPermission('zope.View', 'zope.Member') | ||||
|   >>> assignRole('zope.Member', 'users.john') | ||||
|   >>> from loops.organize.tests import setupObjectsForTesting | ||||
|   >>> setupData = setupObjectsForTesting(site, concepts) | ||||
|   >>> johnC = setupData.johnC | ||||
| 
 | ||||
| Working with the favorites storage | ||||
| ---------------------------------- | ||||
|  | @ -116,7 +70,7 @@ The adapter provides convenience methods for accessing the favorites storage. | |||
| 
 | ||||
| So we are now ready to query the favorites. | ||||
| 
 | ||||
|   >>> favs = favorites.query(userName=johnCId) | ||||
|   >>> favs = list(favorites.query(userName=johnCId)) | ||||
|   >>> favs | ||||
|   [<Favorite ['29', 1, '35', '...']: {}>] | ||||
| 
 | ||||
|  | @ -149,7 +103,7 @@ Let's now trigger the saving of a favorite. | |||
| 
 | ||||
|   >>> view.add() | ||||
| 
 | ||||
|   >>> len(favorites.query(userName=johnCId)) | ||||
|   >>> len(list(favorites.query(userName=johnCId))) | ||||
|   2 | ||||
| 
 | ||||
|   >>> d002Id = util.getUidForObject(resources['d001.txt']) | ||||
|  | @ -157,5 +111,11 @@ Let's now trigger the saving of a favorite. | |||
|   >>> view = FavoriteView(home, request) | ||||
|   >>> view.remove() | ||||
| 
 | ||||
|   >>> len(favorites.query(userName=johnCId)) | ||||
|   >>> len(list(favorites.query(userName=johnCId))) | ||||
|   1 | ||||
| 
 | ||||
| 
 | ||||
| Fin de partie | ||||
| ============= | ||||
| 
 | ||||
|   >>> placefulTearDown() | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ ZCML setup): | |||
| 
 | ||||
|   >>> from loops.interfaces import ILoops | ||||
|   >>> from loops.setup import ISetupManager | ||||
|   >>> from loops.process.setup import SetupManager | ||||
|   >>> from loops.organize.process.setup import SetupManager | ||||
|   >>> component.provideAdapter(SetupManager, (ILoops,), ISetupManager, | ||||
|   ...                           name='process') | ||||
| 
 | ||||
|  | @ -53,7 +53,7 @@ Manage processes | |||
| 
 | ||||
| The classes used in this package are just adapters to IConcept. | ||||
| 
 | ||||
|   >>> from loops.process.definition import Process | ||||
|   >>> from loops.organize.process.definition import Process | ||||
|   >>> from cybertools.process.interfaces import IProcess | ||||
|   >>> component.provideAdapter(Process, (IConcept,), IProcess) | ||||
| 
 | ||||
|  | @ -8,11 +8,10 @@ | |||
| 
 | ||||
|   <!-- process/workflow management stuff --> | ||||
| 
 | ||||
|   <zope:adapter factory="loops.process.definition.Process" | ||||
|   <zope:adapter factory="loops.organize.process.definition.Process" | ||||
|                 provides="cybertools.process.interfaces.IProcess" | ||||
|                 trusted="True" /> | ||||
| 
 | ||||
|   <zope:class class="loops.process.definition.Process"> | ||||
|   <zope:class class="loops.organize.process.definition.Process"> | ||||
|     <require permission="zope.View" | ||||
|              interface="cybertools.process.interfaces.IProcess" /> | ||||
|     <require permission="zope.ManageContent" | ||||
|  | @ -21,7 +20,7 @@ | |||
| 
 | ||||
|   <!-- setup --> | ||||
| 
 | ||||
|   <zope:adapter factory="loops.process.setup.SetupManager" | ||||
|   <zope:adapter factory="loops.organize.process.setup.SetupManager" | ||||
|                 name="process" /> | ||||
| 
 | ||||
| </configure> | ||||
|  | @ -32,7 +32,7 @@ making an object statful we'll use an adapter. | |||
|   >>> component.provideUtility(simplePublishing, IStatesDefinition, | ||||
|   ...                          name='loops.simple_publishing') | ||||
| 
 | ||||
|   >>> from loops.stateful.base import SimplePublishable | ||||
|   >>> from loops.organize.stateful.base import SimplePublishable | ||||
|   >>> component.provideAdapter(SimplePublishable, name='loops.simple_publishing') | ||||
| 
 | ||||
| We may now take a document and adapt it to IStateful so that we may | ||||
|  | @ -57,3 +57,9 @@ not just kept in the adapter. | |||
| 
 | ||||
|   >>> statefulDoc01.state | ||||
|   'published' | ||||
| 
 | ||||
| 
 | ||||
| Fin de partie | ||||
| ============= | ||||
| 
 | ||||
|   >>> placefulTearDown() | ||||
|  | @ -10,9 +10,9 @@ | |||
|         name="loops.simple_publishing" /> | ||||
| 
 | ||||
|   <zope:adapter | ||||
|         factory="loops.stateful.base.SimplePublishable" | ||||
|         factory="loops.organize.stateful.base.SimplePublishable" | ||||
|         name="loops.simple_publishing" trusted="True" /> | ||||
|   <zope:class class="loops.stateful.base.SimplePublishable"> | ||||
|   <zope:class class="loops.organize.stateful.base.SimplePublishable"> | ||||
|     <require permission="zope.View" | ||||
|              interface="cybertools.stateful.interfaces.IStateful" /> | ||||
|     <require permission="zope.ManageContent" | ||||
|  | @ -2,21 +2,32 @@ | |||
| 
 | ||||
| import unittest, doctest | ||||
| from zope.testing.doctestunit import DocFileSuite | ||||
| from zope.app.testing import ztapi | ||||
| from zope.interface.verify import verifyClass | ||||
| 
 | ||||
| from zope import component | ||||
| from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility | ||||
| from zope.app.appsetup.bootstrap import ensureUtility | ||||
| from zope.app.authentication.authentication import PluggableAuthentication | ||||
| from zope.app.authentication.interfaces import IAuthenticatorPlugin | ||||
| from zope.app.authentication.principalfolder import FoundPrincipalFactory | ||||
| from zope.app.authentication.principalfolder import InternalPrincipal | ||||
| from zope.app.authentication.principalfolder import Principal | ||||
| from zope.app.authentication.principalfolder import PrincipalFolder | ||||
| from zope.app.principalannotation import PrincipalAnnotationUtility | ||||
| from zope.app.principalannotation import annotations | ||||
| from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility | ||||
| from zope.app.security.interfaces import IAuthentication | ||||
| from zope.app.security.principalregistry import PrincipalRegistry | ||||
| from zope.app.securitypolicy.interfaces import IRolePermissionManager | ||||
| from zope.app.securitypolicy.interfaces import IPrincipalRoleManager | ||||
| 
 | ||||
| from cybertools.util.jeep import Jeep | ||||
| from loops.common import adapted | ||||
| from loops.concept import Concept | ||||
| from loops.organize.interfaces import IPerson | ||||
| from loops.organize.party import Person | ||||
| from loops.organize.task import Task | ||||
| from loops.setup import addAndConfigureObject | ||||
| from loops.tests.auth import login | ||||
| 
 | ||||
| 
 | ||||
| def setupUtilitiesAndAdapters(loopsRoot): | ||||
|  | @ -25,6 +36,8 @@ def setupUtilitiesAndAdapters(loopsRoot): | |||
|     principalAnnotations = PrincipalAnnotationUtility() | ||||
|     component.provideUtility(principalAnnotations, IPrincipalAnnotationUtility) | ||||
|     component.provideAdapter(Person, provides=IPerson) | ||||
|     component.provideAdapter(Task) | ||||
|     component.provideAdapter(FoundPrincipalFactory) | ||||
|     return Jeep(( | ||||
|             ('auth', auth), | ||||
|             ('principalAnnotations', principalAnnotations), | ||||
|  | @ -32,6 +45,30 @@ def setupUtilitiesAndAdapters(loopsRoot): | |||
|             ('principalRoles', IPrincipalRoleManager(loopsRoot)), | ||||
|     )) | ||||
| 
 | ||||
| def setupObjectsForTesting(site, concepts): | ||||
|     loopsRoot = concepts.getLoopsRoot() | ||||
|     setupData = setupUtilitiesAndAdapters(loopsRoot) | ||||
|     ensureUtility(site, IAuthentication, '', PluggableAuthentication, | ||||
|                   copy_to_zlog=False, asObject=True) | ||||
|     pau = component.getUtility(IAuthentication, context=site) | ||||
|     # create principal folder and user(s) | ||||
|     pFolder = PrincipalFolder('users.') | ||||
|     pau['users'] = pFolder | ||||
|     pau.authenticatorPlugins = ('users',) | ||||
|     pFolder['john'] = InternalPrincipal('john', 'xx', u'John') | ||||
|     # create person and log in | ||||
|     johnC = addAndConfigureObject(concepts, Concept, 'john', | ||||
|                 conceptType=concepts['person'], title=u'john', userId='users.john') | ||||
|     pJohn = Principal('users.john', 'xxx', u'John') | ||||
|     login(pJohn) | ||||
|     # grant roles and permissions | ||||
|     grantPermission = setupData.rolePermissions.grantPermissionToRole | ||||
|     assignRole = setupData.principalRoles.assignRoleToPrincipal | ||||
|     grantPermission('zope.View', 'zope.Member') | ||||
|     assignRole('zope.Member', 'users.john') | ||||
|     setupData.update(dict(johnC=johnC)) | ||||
|     return setupData | ||||
| 
 | ||||
| 
 | ||||
| class Test(unittest.TestCase): | ||||
|     "Basic tests for the organize sub-package." | ||||
|  |  | |||
							
								
								
									
										64
									
								
								organize/tracking/README.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								organize/tracking/README.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| =============================================================== | ||||
| loops - Linked Objects for Organization and Processing Services | ||||
| =============================================================== | ||||
| 
 | ||||
|   ($Id$) | ||||
| 
 | ||||
| Let's do some basic setup | ||||
| 
 | ||||
|   >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown | ||||
|   >>> site = placefulSetUp(True) | ||||
|   >>> from zope import component, interface | ||||
| 
 | ||||
| and set up a simple loops site with a concept manager and some concepts | ||||
| (with all the type machinery, what in real life is done via standard | ||||
| ZCML setup): | ||||
| 
 | ||||
|   >>> from loops.organize.setup import SetupManager | ||||
|   >>> component.provideAdapter(SetupManager, name='organize') | ||||
|   >>> from loops.organize.tracking.setup import SetupManager | ||||
|   >>> component.provideAdapter(SetupManager, name='organize.tracking') | ||||
| 
 | ||||
|   >>> from loops.tests.setup import TestSite | ||||
|   >>> t = TestSite(site) | ||||
|   >>> concepts, resources, views = t.setup() | ||||
| 
 | ||||
| 
 | ||||
| Tracking Changes and Object Access | ||||
| ================================== | ||||
| 
 | ||||
|   >>> loopsRoot = concepts.getLoopsRoot() | ||||
|   >>> records = loopsRoot.getRecordManager() | ||||
|   >>> changes = records['changes'] | ||||
| 
 | ||||
| User management setup | ||||
| --------------------- | ||||
| 
 | ||||
| In order to be able to login and store personal data | ||||
| we have to prepare our environment. We need some basic adapter registrations, | ||||
| and a pluggable authentication utility with a principal folder. | ||||
| 
 | ||||
|   >>> from loops.organize.tests import setupObjectsForTesting | ||||
|   >>> setupData = setupObjectsForTesting(site, concepts) | ||||
|   >>> johnC = setupData.johnC | ||||
| 
 | ||||
| Recording changes to objects | ||||
| ---------------------------- | ||||
| 
 | ||||
|   >>> from loops.organize.tracking.change import recordModification | ||||
|   >>> component.provideHandler(recordModification) | ||||
| 
 | ||||
|   >>> tTask = concepts['task'] | ||||
|   >>> from loops.concept import Concept | ||||
|   >>> from loops.setup import addAndConfigureObject | ||||
|   >>> t01 = addAndConfigureObject(concepts, Concept, 't01', conceptType=tTask, | ||||
|   ...                   title='Develop change tracking') | ||||
| 
 | ||||
|   >>> len(changes) | ||||
|   1 | ||||
| 
 | ||||
| 
 | ||||
| Fin de partie | ||||
| ============= | ||||
| 
 | ||||
|   >>> placefulTearDown() | ||||
							
								
								
									
										3
									
								
								organize/tracking/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								organize/tracking/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| """ | ||||
| $Id$ | ||||
| """ | ||||
							
								
								
									
										91
									
								
								organize/tracking/change.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								organize/tracking/change.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | |||
| # | ||||
| #  Copyright (c) 2008 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 | ||||
| # | ||||
| 
 | ||||
| """ | ||||
| Recording changes to loops objects. | ||||
| 
 | ||||
| $Id$ | ||||
| """ | ||||
| 
 | ||||
| from zope.app.container.interfaces import IObjectAddedEvent, IObjectRemovedEvent | ||||
| from zope.app.container.interfaces import IObjectMovedEvent | ||||
| from zope.cachedescriptors.property import Lazy | ||||
| from zope.component import adapter | ||||
| from zope.lifecycleevent.interfaces import IObjectModifiedEvent, IObjectCreatedEvent | ||||
| 
 | ||||
| from cybertools.tracking.btree import Track, getTimeStamp | ||||
| from loops.concept import ConceptManager | ||||
| from loops.resource import ResourceManager | ||||
| from loops.interfaces import IAssignmentEvent, IDeassignmentEvent | ||||
| from loops.interfaces import ILoopsObject | ||||
| from loops.organize.party import getPersonForUser | ||||
| from loops.security.common import getCurrentPrincipal | ||||
| from loops import util | ||||
| 
 | ||||
| 
 | ||||
| class ChangeManager(object): | ||||
| 
 | ||||
|     context = None | ||||
| 
 | ||||
|     def __init__(self, context): | ||||
|         if isinstance(context, (ConceptManager, ResourceManager)): | ||||
|             return | ||||
|         self.context = context | ||||
| 
 | ||||
|     @Lazy | ||||
|     def valid(self): | ||||
|         return not (self.context is None or | ||||
|                     self.storage is None or | ||||
|                     self.person is None) | ||||
| 
 | ||||
|     @Lazy | ||||
|     def loopsRoot(self): | ||||
|         return self.context.getLoopsRoot() | ||||
| 
 | ||||
|     @Lazy | ||||
|     def storage(self): | ||||
|         records = self.loopsRoot.getRecordManager() | ||||
|         if records is not None: | ||||
|             return records.get('changes') | ||||
|         return None | ||||
| 
 | ||||
|     @Lazy | ||||
|     def person(self): | ||||
|         principal = getCurrentPrincipal() | ||||
|         if principal is not None: | ||||
|             return getPersonForUser(self.context, principal=principal) | ||||
|         return None | ||||
| 
 | ||||
|     def recordModification(self, event=None): | ||||
|         if not self.valid: | ||||
|             return | ||||
|         uid = util.getUidForObject(self.context) | ||||
|         personUid = util.getUidForObject(self.person) | ||||
|         last = self.storage.getLastUserTrack(uid, 0, personUid) | ||||
|         if last is None or last.metadata['timeStamp'] < getTimeStamp() - 5: | ||||
|             self.storage.saveUserTrack(uid, 0, personUid, dict(action='modify')) | ||||
| 
 | ||||
| 
 | ||||
| class ChangeRecord(Track): | ||||
| 
 | ||||
|     typeName = 'ChangeRecord' | ||||
| 
 | ||||
| 
 | ||||
| @adapter(ILoopsObject, IObjectModifiedEvent) | ||||
| def recordModification(obj, event): | ||||
|     ChangeManager(obj).recordModification(event) | ||||
							
								
								
									
										24
									
								
								organize/tracking/configure.zcml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								organize/tracking/configure.zcml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| <!-- $Id$ --> | ||||
| 
 | ||||
| <configure | ||||
|    xmlns="http://namespaces.zope.org/zope" | ||||
|    i18n_domain="loops"> | ||||
| 
 | ||||
|   <class class="loops.organize.tracking.change.ChangeRecord"> | ||||
|     <require permission="zope.View" | ||||
|              interface="cybertools.tracking.interfaces.ITrack" /> | ||||
|     <require permission="zope.ManageContent" | ||||
|              set_schema="cybertools.tracking.interfaces.ITrack" /> | ||||
|   </class> | ||||
| 
 | ||||
|   <class class="loops.organize.tracking.change.ChangeRecord"> | ||||
|     <require permission="zope.View" | ||||
|              interface="cybertools.tracking.interfaces.ITrackingStorage" /> | ||||
|   </class> | ||||
| 
 | ||||
|   <subscriber handler="loops.organize.tracking.change.recordModification" /> | ||||
| 
 | ||||
|   <adapter factory="loops.organize.tracking.setup.SetupManager" | ||||
|            name="organize.tracking" /> | ||||
| 
 | ||||
| </configure> | ||||
							
								
								
									
										38
									
								
								organize/tracking/setup.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								organize/tracking/setup.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| # | ||||
| #  Copyright (c) 2008 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 | ||||
| # | ||||
| 
 | ||||
| """ | ||||
| Automatic setup of a loops site for the organize.tracking package. | ||||
| 
 | ||||
| $Id$ | ||||
| """ | ||||
| 
 | ||||
| from zope.component import adapts | ||||
| from zope.interface import implements, Interface | ||||
| 
 | ||||
| from cybertools.tracking.btree import TrackingStorage | ||||
| from loops.organize.tracking.change import ChangeRecord | ||||
| from loops.setup import SetupManager as BaseSetupManager | ||||
| 
 | ||||
| 
 | ||||
| class SetupManager(BaseSetupManager): | ||||
| 
 | ||||
|     def setup(self): | ||||
|         records = self.context.getRecordManager() | ||||
|         changes = self.addObject(records, TrackingStorage, 'changes', | ||||
|                                    trackFactory=ChangeRecord) | ||||
							
								
								
									
										22
									
								
								organize/tracking/tests.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								organize/tracking/tests.py
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| # $Id$ | ||||
| 
 | ||||
| import unittest, doctest | ||||
| from zope.testing.doctestunit import DocFileSuite | ||||
| 
 | ||||
| 
 | ||||
| class Test(unittest.TestCase): | ||||
|     "Basic tests for the loops.organize.tracking package." | ||||
| 
 | ||||
|     def testBasics(self): | ||||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| def test_suite(): | ||||
|     flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | ||||
|     return unittest.TestSuite(( | ||||
|                 unittest.makeSuite(Test), | ||||
|                 DocFileSuite('README.txt', optionflags=flags), | ||||
|             )) | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     unittest.main(defaultTest='test_suite') | ||||
|  | @ -1,5 +1,5 @@ | |||
| # | ||||
| #  Copyright (c) 2006 Helmut Merz helmutm@cy55.de | ||||
| #  Copyright (c) 2007 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 | ||||
|  | @ -82,3 +82,11 @@ def getInternalPrincipal(id, context=None): | |||
|     if next is not None: | ||||
|         return next.getPrincipal(pau.prefix + id) | ||||
|     raise PrincipalLookupError(id) | ||||
| 
 | ||||
| 
 | ||||
| def getTrackingStorage(obj, name): | ||||
|     records = obj.getLoopsRoot().getRecordManager() | ||||
|     if records is not None: | ||||
|         return records.get(name) | ||||
|     return None | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm