diff --git a/browser/common.py b/browser/common.py
index d1d4062..9b7c3c2 100644
--- a/browser/common.py
+++ b/browser/common.py
@@ -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)
diff --git a/browser/concept.py b/browser/concept.py
index 63cb7cc..ba22b07 100644
--- a/browser/concept.py
+++ b/browser/concept.py
@@ -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):
diff --git a/browser/node.py b/browser/node.py
index c2745b5..ea1b0c6 100644
--- a/browser/node.py
+++ b/browser/node.py
@@ -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'
diff --git a/browser/node_macros.pt b/browser/node_macros.pt
index 5eebb5e..5cd8dd7 100644
--- a/browser/node_macros.pt
+++ b/browser/node_macros.pt
@@ -39,8 +39,10 @@
ondblclick python: item.openEditWindow('configure.html')">
Node Body
-
-
+
+
+
diff --git a/configure.zcml b/configure.zcml
index f46b0b9..962fd7c 100644
--- a/configure.zcml
+++ b/configure.zcml
@@ -279,6 +279,7 @@
+
diff --git a/helpers.txt b/helpers.txt
index 7b9cce4..5697b5d 100755
--- a/helpers.txt
+++ b/helpers.txt
@@ -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__
+
+
+
Controlling presentation using view properties
----------------------------------------------
diff --git a/organize/README.txt b/organize/README.txt
index 8df35df..42564b2 100644
--- a/organize/README.txt
+++ b/organize/README.txt
@@ -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
diff --git a/organize/interfaces.py b/organize/interfaces.py
index ed0ce07..7ec0324 100644
--- a/organize/interfaces.py
+++ b/organize/interfaces.py
@@ -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):
diff --git a/organize/party.py b/organize/party.py
index bfb4ae7..3a1f793 100644
--- a/organize/party.py
+++ b/organize/party.py
@@ -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)
diff --git a/query.py b/query.py
new file mode 100644
index 0000000..1efd03e
--- /dev/null
+++ b/query.py
@@ -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,)
+
diff --git a/type.py b/type.py
index 84e20ba..f5f73ad 100644
--- a/type.py
+++ b/type.py
@@ -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)
+