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