From 7622600c99fabd12512065fafe931cc3c9711cd6 Mon Sep 17 00:00:00 2001 From: helmutm Date: Sun, 19 Mar 2006 09:49:47 +0000 Subject: [PATCH] Make typeProvider attribute available for concepts git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1130 fd906abe-77d9-0310-91a1-e0d9ade77398 --- browser/node.py | 22 +++++++++++++++++----- concept.py | 3 ++- configure.zcml | 2 ++ helpers.txt | 37 +++++++++++++++++++++++++++++++++++++ interfaces.py | 14 ++++++++++++++ type.py | 30 ++++++++++++++++++++++++++++++ view.py | 20 +++++++++++++------- 7 files changed, 115 insertions(+), 13 deletions(-) diff --git a/browser/node.py b/browser/node.py index 0bc72ec..de67d4e 100644 --- a/browser/node.py +++ b/browser/node.py @@ -125,12 +125,23 @@ class NodeView(BaseView): return [NodeView(child, self.request) for child in self.context.getMenuItems()] + @Lazy + def parents(self): + return zapi.getParents(self.context) + + @Lazy + def nearestMenuItem(self): + if self.context.isMenuItem(): + return self.context + for p in self.parents: + if p.isMenuItem(): + return p + def selected(self, item): - if item.context == self.context: - return True - if item.context in zapi.getParents(self.context) and not item.menuItems: - return True - return False + return item.context == self.nearestMenuItem + + def active(self, item): + return item.context == self.context or item.context in self.parents def targetDefaultView(self): target = self.request.annotations.get('loops.view', {}).get('target') @@ -149,6 +160,7 @@ class NodeView(BaseView): if target is not None: return zapi.getUtility(IIntIds).getId(target) + class ConfigureView(NodeView): """ An editing view for configuring a node, optionally creating a target object. diff --git a/concept.py b/concept.py index f26a99c..2556599 100644 --- a/concept.py +++ b/concept.py @@ -28,6 +28,7 @@ from zope.app.container.contained import Contained from zope.cachedescriptors.property import Lazy from zope.component import adapts from zope.interface import implements +from zope.interface import alsoProvides, directlyProvides, directlyProvidedBy from zope import schema from zope.security.proxy import removeSecurityProxy from persistent import Persistent @@ -36,7 +37,7 @@ from cybertools.relation import DyadicRelation from cybertools.relation.registry import getRelations from cybertools.relation.registry import getRelationSingle, setRelationSingle from cybertools.relation.interfaces import IRelationRegistry, IRelatable -from cybertools.typology.interfaces import ITypeManager +from cybertools.typology.interfaces import IType, ITypeManager from interfaces import IConcept, IConceptRelation, IConceptView from interfaces import IConceptManager, IConceptManagerContained diff --git a/configure.zcml b/configure.zcml index 0052a1a..ff295aa 100644 --- a/configure.zcml +++ b/configure.zcml @@ -240,6 +240,8 @@ + + diff --git a/helpers.txt b/helpers.txt index cb37bec..d800eeb 100755 --- a/helpers.txt +++ b/helpers.txt @@ -190,3 +190,40 @@ condition: >>> sorted(t.token for t in types) ['loops.resource.Document', 'loops.resource.MediaAsset'] +Type-based interfaces +--------------------- + +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 is None + True + +For concept objects that provide types (type providers) the value of +the typeInterface attribute is the ITypeConcept interface: + + >>> from loops.interfaces import ITypeConcept + >>> topic_type.typeInterface is ITypeConcept + True + +We now want to have a topic (i.e. a concept that has topic_type as its type) +to provide the interface ITopic. This is done by assigning this interface +to topic_type.typeProvider, i.e. the 'topic' concept, via an adapter: + + >>> from loops.type import TypeConcept + >>> ztapi.provideAdapter(IConcept, ITypeConcept, TypeConcept) + + >>> class ITopic(Interface): pass + >>> from zope.interface import implements + >>> class Topic(object): + ... implements(ITopic) + ... def __init__(self, context): pass + >>> ztapi.provideAdapter(IConcept, ITopic, Topic) + + >>> ITypeConcept(topic).typeInterface = ITopic + >>> cc1.conceptType = topic + + >>> cc1_type = IType(cc1) + >>> cc1Adapter = cc1_type.typeInterface(cc1) + >>> ITopic.providedBy(cc1Adapter) + True diff --git a/interfaces.py b/interfaces.py index ea8c52b..bfcb5d6 100644 --- a/interfaces.py +++ b/interfaces.py @@ -384,6 +384,10 @@ class INode(IView, IBaseNode): a menu). """ + def isMenuItem(): + """ Return True if this object is a menu item. + """ + def getTextItems(): """ Return the text items belonging to this object. """ @@ -465,3 +469,13 @@ class IIndexAttributes(Interface): """ Return a string that identifies the type of the object. """ + +# types stuff + +class ITypeConcept(Interface): + """ This interface should be provided by concepts of type 'type'. + """ + + typeInterface = Attribute('The interface provided by objects of this type') + + diff --git a/type.py b/type.py index 6812874..2673534 100644 --- a/type.py +++ b/type.py @@ -24,10 +24,12 @@ $Id$ from zope.app import zapi from zope.component import adapts +from zope.interface import implements from zope.cachedescriptors.property import Lazy from zope.dottedname.resolve import resolve from cybertools.typology.type import BaseType, TypeManager from loops.interfaces import ILoopsObject, IConcept, IResource +from loops.interfaces import ITypeConcept from loops.concept import Concept from loops.resource import Document, MediaAsset @@ -72,6 +74,17 @@ class ConceptType(LoopsType): def defaultContainer(self): return self.root.getConceptManager() + @Lazy + def typeInterface(self): + adapter = zapi.queryAdapter(self.typeProvider, ITypeConcept) + if adapter is not None: + return adapter.typeInterface + else: + conceptType = self.context.conceptType + if conceptType is conceptType.getConceptManager().getTypeConcept(): + return ITypeConcept + return None + class ConceptTypeInfo(ConceptType): @@ -161,3 +174,20 @@ class LoopsTypeManager(TypeManager): def resourceTypes(self): return tuple([ResourceTypeInfo(self.context, cls) for cls in (Document, MediaAsset)]) + + +class TypeConcept(object): + + implements(ITypeConcept) + adapts(IConcept) + + def __init__(self, context): + self.context = context + + def getTypeInterface(self): + return getattr(self.context, '_typeInterface', None) + def setTypeInterface(self, ifc): + self.context._typeInterface = ifc + typeInterface = property(getTypeInterface, setTypeInterface) + + diff --git a/view.py b/view.py index 53a7755..8ae4b64 100644 --- a/view.py +++ b/view.py @@ -128,7 +128,9 @@ class Node(View, OrderedContainer): continue def getMenu(self): - return self.nodeType == 'menu' and self or self.getParentNode(['menu']) + if self.nodeType == 'menu': + return self + return self.getParentNode(['menu']) def getPage(self): pageTypes = ['page', 'menu', 'info'] @@ -142,6 +144,9 @@ class Node(View, OrderedContainer): def getTextItems(self): return self.getChildNodes(['text']) + def isMenuItem(self): + return self.nodeType in ('page', 'menu') + class ViewManager(OrderedContainer): @@ -171,13 +176,14 @@ class NodeTraverser(ItemTraverser): return self.context.getLoopsRoot() if name.startswith('.target'): if len(name) > len('.target'): - idx = int(name[len('.target'):]) - target = zapi.getUtility(IIntIds).getObject(idx) + uid = int(name[len('.target'):]) + target = zapi.getUtility(IIntIds).getObject(uid) else: target = self.context.target - viewAnnotations = request.annotations.get('loops.view', {}) - viewAnnotations['target'] = target - request.annotations['loops.view'] = viewAnnotations - return self.context + if target is not None: + viewAnnotations = request.annotations.get('loops.view', {}) + viewAnnotations['target'] = target + request.annotations['loops.view'] = viewAnnotations + return self.context return super(NodeTraverser, self).publishTraverse(request, name)