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:
helmutm 2006-05-19 09:34:03 +00:00
parent 8edec76c1d
commit 4041b91fca
11 changed files with 194 additions and 35 deletions

View file

@ -96,6 +96,10 @@ class BaseView(object):
def url(self):
return zapi.absoluteURL(self.context, self.request)
@Lazy
def view(self):
return self
@Lazy
def token(self):
return self.loopsRoot.getLoopsUri(self.context)

View file

@ -22,6 +22,7 @@ Definition of the Task view class.
$Id$
"""
from zope import interface, component, schema
from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog
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.publisher.interfaces import BadRequest
from zope.publisher.interfaces.browser import IBrowserRequest
from zope import schema
from zope.schema.interfaces import IIterableSource
from zope.security.proxy import removeSecurityProxy
@ -78,6 +78,25 @@ class ConceptView(BaseView):
for r in self.context.getResourceRelations():
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):

View file

@ -105,7 +105,9 @@ class NodeView(BaseView):
def target(self):
obj = self.targetObject
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):
target = self.target
@ -117,6 +119,7 @@ class NodeView(BaseView):
@Lazy
def bodyMacro(self):
# ?TODO: replace by: return self.target.macroName
target = self.targetObject
if target is None or IDocument.providedBy(target):
return 'textbody'

View file

@ -39,8 +39,10 @@
ondblclick python: item.openEditWindow('configure.html')">
<span tal:content="structure body">Node Body</span>
</div>
<tal:concepts define="item item/target">
<div metal:use-macro="views/concept_macros/conceptlisting2" />
<tal:concepts define="item item/target;
macro item/macro">
<div metal:use-macro="macro" />
<!--<div metal:use-macro="views/concept_macros/conceptlisting2" />-->
</tal:concepts>
</tal:body>
</metal:body>

View file

@ -279,6 +279,7 @@
<adapter factory="loops.type.LoopsTypeManager" />
<adapter factory="loops.type.TypeConcept" />
<adapter factory="loops.query.QueryConcept" />
<adapter factory="loops.external.NodesLoader" />
<adapter factory="loops.external.NodesExporter" />

View file

@ -215,7 +215,6 @@ Type-based interfaces and adapters
A type has an optional typeInterface attribute that objects of this type
will be adaptable to. The default for this is None:
>>> cc1_type.typeInterface
>>> cc1_type.typeInterface is None
True
@ -235,7 +234,7 @@ i.e. the 'topic' concept, via an adapter:
>>> class Topic(object):
... implements(ITopic)
... def __init__(self, context): pass
>>> ztapi.provideAdapter(IConcept, ITopic, Topic)
>>> component.provideAdapter(Topic, (IConcept,), ITopic)
>>> ITypeConcept(topic).typeInterface = ITopic
>>> cc1.conceptType = topic
@ -246,6 +245,57 @@ i.e. the 'topic' concept, via an adapter:
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
----------------------------------------------

View file

@ -22,8 +22,7 @@ ZCML setup):
>>> from loops.concept import ConceptManager, Concept
>>> from loops.interfaces import IConcept, ITypeConcept
>>> site['loops'] = Loops()
>>> loopsRoot = site['loops']
>>> loopsRoot = site['loops'] = Loops()
>>> from cybertools.relation.interfaces import IRelationRegistry
>>> from cybertools.relation.registry import DummyRelationRegistry

View file

@ -54,13 +54,14 @@ class UserId(schema.TextLine):
try:
principal = auth.getPrincipal(userId)
except PrincipalLookupError:
raiseValidationError(u'User %s does not exist' % userId)
raiseValidationError(_(u'User $userId does not exist',
mapping={'userId': userId}))
pa = annotations(principal)
person = pa.get(ANNOTATION_KEY, None)
if person is not None and person != self.context:
raiseValidationError(
u'There is alread a person (%s) assigned to user %s.'
% (zapi.getName(person), userId))
_(u'There is alread a person ($person) assigned to user $userId.',
mapping={'person': zapi.getName(person), 'userId': userId}))
class IPerson(IBasePerson):

View file

@ -37,7 +37,7 @@ from cybertools.organize.party import Person as BasePerson
from cybertools.typology.interfaces import IType
from loops.interfaces import IConcept
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)
@ -45,34 +45,14 @@ from loops.type import TypeInterfaceSourceList
TypeInterfaceSourceList.typeInterfaces += (IPerson,)
class Person(BasePerson):
class Person(AdapterBase, BasePerson):
""" typeInterface adapter for concepts of type 'person'.
"""
implements(IPerson)
adapts(IConcept)
__attributes = ('context', '__parent__', 'userId',)
__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)
_attributes = ('context', '__parent__', 'userId',)
_schemas = list(IPerson) + list(IConcept)
def getUserId(self):
return getattr(self.context, '_userId', None)

70
query.py Normal file
View 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
View file

@ -215,3 +215,33 @@ class TypeInterfaceSourceList(object):
def __len__(self):
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)