more on the organize sub-package; set up query machinery for dynamically providing views
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1216 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									8edec76c1d
								
							
						
					
					
						commit
						4041b91fca
					
				
					 11 changed files with 194 additions and 35 deletions
				
			
		|  | @ -96,6 +96,10 @@ class BaseView(object): | ||||||
|     def url(self): |     def url(self): | ||||||
|         return zapi.absoluteURL(self.context, self.request) |         return zapi.absoluteURL(self.context, self.request) | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def view(self): | ||||||
|  |         return self | ||||||
|  | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def token(self): |     def token(self): | ||||||
|         return self.loopsRoot.getLoopsUri(self.context) |         return self.loopsRoot.getLoopsUri(self.context) | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ Definition of the Task view class. | ||||||
| $Id$ | $Id$ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | from zope import interface, component, schema | ||||||
| from zope.app import zapi | from zope.app import zapi | ||||||
| from zope.app.catalog.interfaces import ICatalog | from zope.app.catalog.interfaces import ICatalog | ||||||
| from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent | from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent | ||||||
|  | @ -35,7 +36,6 @@ from zope.formlib.form import EditForm, FormFields | ||||||
| from zope.interface import implements | from zope.interface import implements | ||||||
| from zope.publisher.interfaces import BadRequest | from zope.publisher.interfaces import BadRequest | ||||||
| from zope.publisher.interfaces.browser import IBrowserRequest | from zope.publisher.interfaces.browser import IBrowserRequest | ||||||
| from zope import schema |  | ||||||
| from zope.schema.interfaces import IIterableSource | from zope.schema.interfaces import IIterableSource | ||||||
| from zope.security.proxy import removeSecurityProxy | from zope.security.proxy import removeSecurityProxy | ||||||
| 
 | 
 | ||||||
|  | @ -78,6 +78,25 @@ class ConceptView(BaseView): | ||||||
|         for r in self.context.getResourceRelations(): |         for r in self.context.getResourceRelations(): | ||||||
|             yield ConceptRelationView(r, self.request, contextIsSecond=True) |             yield ConceptRelationView(r, self.request, contextIsSecond=True) | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def view(self): | ||||||
|  |         ti = IType(self.context).typeInterface | ||||||
|  |         # TODO: check the interface (maybe for a base interface IViewProvider) | ||||||
|  |         #       instead of the viewName attribute: | ||||||
|  |         if ti and 'viewName' in ti: | ||||||
|  |             typeAdapter = ti(self.context) | ||||||
|  |             viewName = typeAdapter.viewName | ||||||
|  |             # ??? Would it make sense to use a somehow restricted interface | ||||||
|  |             #     that should be provided by the view like IQuery? | ||||||
|  |             #viewInterface = getattr(typeAdapter, 'viewInterface', None) or IQuery | ||||||
|  |             if viewName: | ||||||
|  |                 adapter = component.queryMultiAdapter((self.context, self.request), | ||||||
|  |                                          interface.Interface, name=viewName) | ||||||
|  |                 if adapter is not None: | ||||||
|  |                     return adapter | ||||||
|  |         #elif type provides view: use this | ||||||
|  |         return self | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class ConceptConfigureView(ConceptView): | class ConceptConfigureView(ConceptView): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -105,7 +105,9 @@ class NodeView(BaseView): | ||||||
|     def target(self): |     def target(self): | ||||||
|         obj = self.targetObject |         obj = self.targetObject | ||||||
|         if obj is not None: |         if obj is not None: | ||||||
|             return zapi.getMultiAdapter((obj, self.request)) |             basicView = zapi.getMultiAdapter((obj, self.request)) | ||||||
|  |             return basicView.view | ||||||
|  |             #return zapi.getMultiAdapter((obj, self.request)) | ||||||
| 
 | 
 | ||||||
|     def renderTarget(self): |     def renderTarget(self): | ||||||
|         target = self.target |         target = self.target | ||||||
|  | @ -117,6 +119,7 @@ class NodeView(BaseView): | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def bodyMacro(self): |     def bodyMacro(self): | ||||||
|  |         # ?TODO: replace by: return self.target.macroName | ||||||
|         target = self.targetObject |         target = self.targetObject | ||||||
|         if target is None or IDocument.providedBy(target): |         if target is None or IDocument.providedBy(target): | ||||||
|             return 'textbody' |             return 'textbody' | ||||||
|  |  | ||||||
|  | @ -39,8 +39,10 @@ | ||||||
|                          ondblclick python: item.openEditWindow('configure.html')"> |                          ondblclick python: item.openEditWindow('configure.html')"> | ||||||
|       <span tal:content="structure body">Node Body</span> |       <span tal:content="structure body">Node Body</span> | ||||||
|     </div> |     </div> | ||||||
|     <tal:concepts define="item item/target"> |     <tal:concepts define="item item/target; | ||||||
|       <div metal:use-macro="views/concept_macros/conceptlisting2" /> |                           macro item/macro"> | ||||||
|  |       <div metal:use-macro="macro" /> | ||||||
|  |       <!--<div metal:use-macro="views/concept_macros/conceptlisting2" />--> | ||||||
|     </tal:concepts> |     </tal:concepts> | ||||||
|   </tal:body> |   </tal:body> | ||||||
| </metal:body> | </metal:body> | ||||||
|  |  | ||||||
|  | @ -279,6 +279,7 @@ | ||||||
|   <adapter factory="loops.type.LoopsTypeManager" /> |   <adapter factory="loops.type.LoopsTypeManager" /> | ||||||
|    |    | ||||||
|   <adapter factory="loops.type.TypeConcept" /> |   <adapter factory="loops.type.TypeConcept" /> | ||||||
|  |   <adapter factory="loops.query.QueryConcept" /> | ||||||
|    |    | ||||||
|   <adapter factory="loops.external.NodesLoader" /> |   <adapter factory="loops.external.NodesLoader" /> | ||||||
|   <adapter factory="loops.external.NodesExporter" /> |   <adapter factory="loops.external.NodesExporter" /> | ||||||
|  |  | ||||||
							
								
								
									
										54
									
								
								helpers.txt
									
										
									
									
									
								
							
							
						
						
									
										54
									
								
								helpers.txt
									
										
									
									
									
								
							|  | @ -215,7 +215,6 @@ Type-based interfaces and adapters | ||||||
| A type has an optional typeInterface attribute that objects of this type | A type has an optional typeInterface attribute that objects of this type | ||||||
| will be adaptable to. The default for this is None: | will be adaptable to. The default for this is None: | ||||||
| 
 | 
 | ||||||
|   >>> cc1_type.typeInterface |  | ||||||
|   >>> cc1_type.typeInterface is None |   >>> cc1_type.typeInterface is None | ||||||
|   True |   True | ||||||
| 
 | 
 | ||||||
|  | @ -235,7 +234,7 @@ i.e. the 'topic' concept, via an adapter: | ||||||
|   >>> class Topic(object): |   >>> class Topic(object): | ||||||
|   ...     implements(ITopic) |   ...     implements(ITopic) | ||||||
|   ...     def __init__(self, context): pass |   ...     def __init__(self, context): pass | ||||||
|   >>> ztapi.provideAdapter(IConcept, ITopic, Topic) |   >>> component.provideAdapter(Topic, (IConcept,), ITopic) | ||||||
|    |    | ||||||
|   >>> ITypeConcept(topic).typeInterface = ITopic |   >>> ITypeConcept(topic).typeInterface = ITopic | ||||||
|   >>> cc1.conceptType = topic |   >>> cc1.conceptType = topic | ||||||
|  | @ -246,6 +245,57 @@ i.e. the 'topic' concept, via an adapter: | ||||||
|   True |   True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Concepts as queries | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  | We first have to set up the query type, i.e. a type concept associated | ||||||
|  | with the IQueryConcept interface: | ||||||
|  | 
 | ||||||
|  |   >>> from loops.query import IQueryConcept, QueryConcept | ||||||
|  |   >>> component.provideAdapter(QueryConcept, (IConcept,), IQueryConcept) | ||||||
|  | 
 | ||||||
|  |   >>> query = concepts['query'] = Concept(u'Query') | ||||||
|  |   >>> query.conceptType = typeObject | ||||||
|  |   >>> ITypeConcept(query).typeInterface = IQueryConcept | ||||||
|  | 
 | ||||||
|  | Next we need a concept of this type: | ||||||
|  | 
 | ||||||
|  |   >>> simpleQuery = concepts['simpleQuery'] = Concept(u'Simple query') | ||||||
|  |   >>> simpleQuery.conceptType = query | ||||||
|  |   >>> sq_type = IType(simpleQuery) | ||||||
|  |   >>> sq_adapter = sq_type.typeInterface(simpleQuery) | ||||||
|  |   >>> sq_adapter.viewName = 'simpleview.html' | ||||||
|  |   >>> simpleQuery._viewName | ||||||
|  |   'simpleview.html' | ||||||
|  | 
 | ||||||
|  | This viewName attribute of the query will be automatically used by | ||||||
|  | a concept view when asked for the view that should be used for rendering | ||||||
|  | the concept... | ||||||
|  | 
 | ||||||
|  |   >>> from loops.browser.concept import ConceptView | ||||||
|  |   >>> from zope.publisher.browser import TestRequest | ||||||
|  |   >>> sq_baseView = ConceptView(simpleQuery, TestRequest()) | ||||||
|  |   >>> sq_view = sq_baseView.view | ||||||
|  | 
 | ||||||
|  | ...but only when the view exists, i.e. there is a class registered as a | ||||||
|  | view/multi-adapter with this name: | ||||||
|  |    | ||||||
|  |   >>> sq_view is sq_baseView | ||||||
|  |   True | ||||||
|  | 
 | ||||||
|  |   >>> class SimpleView(object): | ||||||
|  |   ...     def __init__(self, context, request): pass | ||||||
|  |   >>> from zope.publisher.interfaces.browser import IBrowserRequest | ||||||
|  |   >>> component.provideAdapter(SimpleView, (IConcept, IBrowserRequest), Interface, | ||||||
|  |   ...                          name='simpleview.html') | ||||||
|  |   >>> sq_baseView = ConceptView(simpleQuery, TestRequest()) | ||||||
|  |   >>> sq_view = sq_baseView.view | ||||||
|  |   >>> sq_view is sq_baseView | ||||||
|  |   False | ||||||
|  |   >>> sq_view.__class__ | ||||||
|  |   <class 'SimpleView'> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Controlling presentation using view properties | Controlling presentation using view properties | ||||||
| ---------------------------------------------- | ---------------------------------------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,8 +22,7 @@ ZCML setup): | ||||||
|   >>> from loops.concept import ConceptManager, Concept |   >>> from loops.concept import ConceptManager, Concept | ||||||
|   >>> from loops.interfaces import IConcept, ITypeConcept |   >>> from loops.interfaces import IConcept, ITypeConcept | ||||||
| 
 | 
 | ||||||
|   >>> site['loops'] = Loops() |   >>> loopsRoot = site['loops'] = Loops() | ||||||
|   >>> loopsRoot = site['loops'] |  | ||||||
| 
 | 
 | ||||||
|   >>> from cybertools.relation.interfaces import IRelationRegistry |   >>> from cybertools.relation.interfaces import IRelationRegistry | ||||||
|   >>> from cybertools.relation.registry import DummyRelationRegistry |   >>> from cybertools.relation.registry import DummyRelationRegistry | ||||||
|  |  | ||||||
|  | @ -54,13 +54,14 @@ class UserId(schema.TextLine): | ||||||
|         try: |         try: | ||||||
|             principal = auth.getPrincipal(userId) |             principal = auth.getPrincipal(userId) | ||||||
|         except PrincipalLookupError: |         except PrincipalLookupError: | ||||||
|             raiseValidationError(u'User %s does not exist' % userId) |             raiseValidationError(_(u'User $userId does not exist', | ||||||
|  |                                    mapping={'userId': userId})) | ||||||
|         pa = annotations(principal) |         pa = annotations(principal) | ||||||
|         person = pa.get(ANNOTATION_KEY, None) |         person = pa.get(ANNOTATION_KEY, None) | ||||||
|         if person is not None and person != self.context: |         if person is not None and person != self.context: | ||||||
|             raiseValidationError( |             raiseValidationError( | ||||||
|                 u'There is alread a person (%s) assigned to user %s.' |                 _(u'There is alread a person ($person) assigned to user $userId.', | ||||||
|                 % (zapi.getName(person), userId)) |                  mapping={'person': zapi.getName(person), 'userId': userId})) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IPerson(IBasePerson): | class IPerson(IBasePerson): | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ from cybertools.organize.party import Person as BasePerson | ||||||
| from cybertools.typology.interfaces import IType | from cybertools.typology.interfaces import IType | ||||||
| from loops.interfaces import IConcept | from loops.interfaces import IConcept | ||||||
| from loops.organize.interfaces import IPerson, ANNOTATION_KEY | from loops.organize.interfaces import IPerson, ANNOTATION_KEY | ||||||
| from loops.type import TypeInterfaceSourceList | from loops.type import TypeInterfaceSourceList, AdapterBase | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # register IPerson as a type interface - (TODO: use a function for this) | # register IPerson as a type interface - (TODO: use a function for this) | ||||||
|  | @ -45,34 +45,14 @@ from loops.type import TypeInterfaceSourceList | ||||||
| TypeInterfaceSourceList.typeInterfaces += (IPerson,) | TypeInterfaceSourceList.typeInterfaces += (IPerson,) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Person(BasePerson): | class Person(AdapterBase, BasePerson): | ||||||
|     """ typeInterface adapter for concepts of type 'person'. |     """ typeInterface adapter for concepts of type 'person'. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     implements(IPerson) |     implements(IPerson) | ||||||
|     adapts(IConcept) |  | ||||||
| 
 | 
 | ||||||
|     __attributes = ('context', '__parent__', 'userId',) |     _attributes = ('context', '__parent__', 'userId',) | ||||||
|     __schemas = list(IPerson) + list(IConcept) |     _schemas = list(IPerson) + list(IConcept) | ||||||
| 
 |  | ||||||
|     def __init__(self, context): |  | ||||||
|         self.context = context # to get the permission stuff right |  | ||||||
|         self.__parent__ = context |  | ||||||
| 
 |  | ||||||
|     def __getattr__(self, attr): |  | ||||||
|         self.checkAttr(attr) |  | ||||||
|         return getattr(self.context, '_' + attr, None) |  | ||||||
| 
 |  | ||||||
|     def __setattr__(self, attr, value): |  | ||||||
|         if attr in self.__attributes: |  | ||||||
|             object.__setattr__(self, attr, value) |  | ||||||
|         else: |  | ||||||
|             self.checkAttr(attr) |  | ||||||
|             setattr(self.context, '_' + attr, value) |  | ||||||
| 
 |  | ||||||
|     def checkAttr(self, attr): |  | ||||||
|         if attr not in self.__schemas: |  | ||||||
|             raise AttributeError(attr) |  | ||||||
| 
 | 
 | ||||||
|     def getUserId(self): |     def getUserId(self): | ||||||
|         return getattr(self.context, '_userId', None) |         return getattr(self.context, '_userId', None) | ||||||
|  |  | ||||||
							
								
								
									
										70
									
								
								query.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								query.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | ||||||
|  | # | ||||||
|  | #  Copyright (c) 2006 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 | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  | Query management stuff. | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | from zope.app import zapi | ||||||
|  | from zope.component import adapts | ||||||
|  | from zope.interface import Interface, Attribute, implements | ||||||
|  | from zope.i18nmessageid import MessageFactory | ||||||
|  | from zope.cachedescriptors.property import Lazy | ||||||
|  | from zope import schema | ||||||
|  | from zope.security.proxy import removeSecurityProxy | ||||||
|  | 
 | ||||||
|  | from cybertools.typology.type import BaseType, TypeManager | ||||||
|  | from loops.interfaces import IConcept | ||||||
|  | from loops.type import AdapterBase, TypeInterfaceSourceList | ||||||
|  | 
 | ||||||
|  | _ = MessageFactory('loops') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class IQuery(Interface): | ||||||
|  |     """ The basic query interface. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     def query(self, **kw): | ||||||
|  |         """ Execute the query and return a sequence of objects. | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class IQueryConcept(Interface): | ||||||
|  |     """ The schema for the query type. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     viewName = schema.TextLine( | ||||||
|  |         title=_(u'Adapter/View name'), | ||||||
|  |         description=_(u'The name of the (mulit-) adapter (typically a view) ' | ||||||
|  |                        'to be used for the query and for presenting ' | ||||||
|  |                        'the results'), | ||||||
|  |         default=u'', | ||||||
|  |         required=True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QueryConcept(AdapterBase): | ||||||
|  | 
 | ||||||
|  |     implements(IQueryConcept) | ||||||
|  | 
 | ||||||
|  |     _schemas = list(IQueryConcept) + list(IConcept) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,) | ||||||
|  | 
 | ||||||
							
								
								
									
										30
									
								
								type.py
									
										
									
									
									
								
							
							
						
						
									
										30
									
								
								type.py
									
										
									
									
									
								
							|  | @ -215,3 +215,33 @@ class TypeInterfaceSourceList(object): | ||||||
|     def __len__(self): |     def __len__(self): | ||||||
|         return len(self.typeInterfaces) |         return len(self.typeInterfaces) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class AdapterBase(object): | ||||||
|  |     """ (Mix-in) Class for concept adapters that provide editing of fields | ||||||
|  |         defined by the type interface. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     adapts(IConcept) | ||||||
|  |      | ||||||
|  |     _attributes = ('context', '__parent__', ) | ||||||
|  |     _schemas = (IConcept,) | ||||||
|  |      | ||||||
|  |     def __init__(self, context): | ||||||
|  |         self.context = context # to get the permission stuff right | ||||||
|  |         self.__parent__ = context | ||||||
|  | 
 | ||||||
|  |     def __getattr__(self, attr): | ||||||
|  |         self.checkAttr(attr) | ||||||
|  |         return getattr(self.context, '_' + attr, None) | ||||||
|  | 
 | ||||||
|  |     def __setattr__(self, attr, value): | ||||||
|  |         if attr in self._attributes: | ||||||
|  |             object.__setattr__(self, attr, value) | ||||||
|  |         else: | ||||||
|  |             self.checkAttr(attr) | ||||||
|  |             setattr(self.context, '_' + attr, value) | ||||||
|  | 
 | ||||||
|  |     def checkAttr(self, attr): | ||||||
|  |         if attr not in self._schemas: | ||||||
|  |             raise AttributeError(attr) | ||||||
|  | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm