diff --git a/browser/configure.zcml b/browser/configure.zcml index 03533cc..a9842ea 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -237,7 +237,7 @@ for="loops.interfaces.IResource" class=".resource.ResourceConfigureView" permission="zope.ManageContent"> - + + + + + >> from cybertools.relation.registry import DummyRelationRegistry >>> from zope.app.testing import ztapi >>> ztapi.provideUtility(IRelationRegistry, DummyRelationRegistry()) + +and care for some type adapter machinery: + + >>> from loops.interfaces import IConcept + >>> from loops.interfaces import ITypeConcept + >>> from loops.type import TypeConcept + >>> component.provideAdapter(TypeConcept, (IConcept,), ITypeConcept) Now we can setup a simple loops site with its manager objects, using a loops setup manager: @@ -52,6 +59,12 @@ We also add some example concepts, u'Zope 3' resources, + + >>> from loops.resource import Resource + >>> file1 = resources['file1'] = Resource(u'A file') + >>> file1.resourceType = concepts['file'] + +(the use of Document and MediaAsset may get deprecated soon:) >>> from loops.resource import Document, MediaAsset >>> doc1 = Document(u'Zope Info') @@ -82,7 +95,6 @@ interface. The loops framework provides an adapter (LoopsType) for this purpose: >>> from cybertools.typology.interfaces import IType - >>> from loops.interfaces import IConcept >>> from loops.type import ConceptType >>> ztapi.provideAdapter(IConcept, IType, ConceptType) >>> cc1_type = IType(cc1) @@ -148,6 +160,25 @@ lazy properties, one should always get a new adapter: Now let's have a look at resources. + + >>> from loops.interfaces import IResource + >>> from loops.type import LoopsType + >>> component.provideAdapter(LoopsType, (IResource,), IType) + >>> file1_type = IType(file1) + >>> file1_type.title + u'File' + >>> file1_type.token + '.loops/concepts/file' + >>> file1_type.tokenForSearch + 'loops:resource:file' + >>> file1_type.qualifiers + ('resource',) + >>> file1_type.defaultContainer + + >>> file1_type.factory + + +(The use of Document and MediaAsset will be deprecated soon...) >>> from loops.interfaces import IResource >>> from loops.type import ResourceType @@ -185,10 +216,11 @@ get a type manager from all loops objects, always with the same context: True >>> types = typeManager.types - >>> sorted((t.token) for t in types) - ['.loops/concepts/predicate', '.loops/concepts/topic', - '.loops/concepts/type', 'loops.resource.Document', - 'loops.resource.MediaAsset'] + >>> sorted(t.token for t in types) + ['.loops/concepts/file', '.loops/concepts/image', '.loops/concepts/predicate', + '.loops/concepts/textdocument', '.loops/concepts/topic', + '.loops/concepts/type', + 'loops.resource.Document', 'loops.resource.MediaAsset'] >>> typeManager.getType('.loops/concepts/topic') == cc1_type True @@ -201,15 +233,14 @@ condition: ['.loops/concepts/predicate', '.loops/concepts/topic', '.loops/concepts/type'] >>> types = typeManager.listTypes(exclude=('concept',)) >>> sorted(t.token for t in types) - ['loops.resource.Document', 'loops.resource.MediaAsset'] + ['.loops/concepts/file', '.loops/concepts/image', + '.loops/concepts/textdocument', 'loops.resource.Document', + 'loops.resource.MediaAsset'] + Type-based interfaces and adapters ---------------------------------- - >>> from loops.interfaces import ITypeConcept - >>> from loops.type import TypeConcept - >>> ztapi.provideAdapter(IConcept, ITypeConcept, TypeConcept) - A type has an optional typeInterface attribute that objects of this type will be adaptable to. The default for this is None: diff --git a/interfaces.py b/interfaces.py index ac54a1c..9ab4bfa 100644 --- a/interfaces.py +++ b/interfaces.py @@ -177,6 +177,29 @@ class IConceptManagerContained(Interface): # resource interfaces + +class IBaseResource(Interface): + """ New base interface for resources. Functionality beyond this simple + interface is provided by adapters that are chosen via the + resource type's typeInterface. + """ + + title = schema.TextLine( + title=_(u'Title'), + description=_(u'Title of the resource'), + default=u'', + missing_value=u'', + required=False) + + resourceType = schema.Choice( + title=_(u'Resource Type'), + description=_(u"The type of the resource, specified by a relation to " + "a concept of type 'type'."), + default=None, + source="loops.resourceTypeSource", + required=False) + + class IResourceSchema(Interface): title = schema.TextLine( @@ -201,6 +224,8 @@ class IResourceSchema(Interface): required=False) +# the next two interfaces are probably obsolete: + class IFileSystemResource(Interface): fsPath = schema.BytesLine( @@ -517,6 +542,7 @@ class ITypeConcept(Interface): required=False) # viewName = schema.TextLine() + # storage = schema.Choice() class IResourceAdapter(Interface): diff --git a/resource.py b/resource.py index 31500b0..4ef1ef3 100644 --- a/resource.py +++ b/resource.py @@ -40,7 +40,7 @@ from textindexng.content import IndexContentCollector from cybertools.relation.registry import getRelations from cybertools.relation.interfaces import IRelatable -from interfaces import IResource +from interfaces import IBaseResource, IResource from interfaces import IDocument, IDocumentSchema, IDocumentView from interfaces import IMediaAsset, IMediaAssetSchema, IMediaAssetView from interfaces import IFileSystemResource, IControlledResource @@ -55,11 +55,32 @@ _ = MessageFactory('loops') class Resource(Contained, Persistent): - implements(IResource, IFileSystemResource, IControlledResource, + implements(IBaseResource, IResource, IFileSystemResource, IControlledResource, IResourceManagerContained, IRelatable) + + proxyInterface = IMediaAssetView _size = _width = _height = 0 + def getResourceType(self): + typePred = self.getLoopsRoot().getConceptManager().getTypePredicate() + if typePred is None: + return None + concepts = self.getConcepts([typePred]) + # TODO (?): check for multiple types (->Error) + return concepts and concepts[0] or None + def setResourceType(self, concept): + current = self.getResourceType() + if current != concept: + typePred = self.getLoopsRoot().getConceptManager().getTypePredicate() + if typePred is None: + raise ValueError('No type predicate found for ' + + zapi.getName(self)) + if current is not None: + self.deassignConcept(current, [typePred]) + self.assignConcept(concept, typePred) + resourceType = property(getResourceType, setResourceType) + _title = u'' def getTitle(self): return self._title def setTitle(self, title): self._title = title @@ -167,6 +188,17 @@ class ResourceManager(BTreeContainer): def getViewManager(self): return self.getLoopsRoot().getViewManager() + + +# adapters and similar stuff + + +class FileAdapter(object): + """ A type adapter for providing file functionality for resources. + """ + + def __init__(self, context): + self.context = context class DocumentWriteFileAdapter(object): diff --git a/setup.py b/setup.py index 6d5af56..50fdf24 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ from zope import component from zope.component import adapts from zope.interface import implements, Interface -from loops.interfaces import ILoops +from loops.interfaces import ILoops, ITypeConcept, IFile, IImage, ITextDocument from loops.concept import ConceptManager, Concept from loops.resource import ResourceManager from loops.view import ViewManager, Node @@ -73,8 +73,16 @@ class SetupManager(object): hasType = self.addObject(conceptManager, Concept, 'hasType', title=u'has Type') predicate = self.addObject(conceptManager, Concept, 'predicate', title=u'Predicate') standard = self.addObject(conceptManager, Concept, 'standard', title=u'subobject') - typeConcept.conceptType = typeConcept - predicate.conceptType = typeConcept + file = self.addObject(conceptManager, Concept, 'file', title=u'File') + image = self.addObject(conceptManager, Concept, 'image', title=u'Image') + textdocument = self.addObject(conceptManager, Concept, + 'textdocument', title=u'Text Document') + for c in (typeConcept, file, image, textdocument, predicate): + c.conceptType = typeConcept + ITypeConcept(typeConcept).typeInterface = ITypeConcept + ITypeConcept(file).typeInterface = IFile + ITypeConcept(image).typeInterface = IImage + ITypeConcept(textdocument).typeInterface = ITextDocument hasType.conceptType = predicate standard.conceptType = predicate diff --git a/type.py b/type.py index 9e2dd69..d6ca785 100644 --- a/type.py +++ b/type.py @@ -30,8 +30,10 @@ from zope.dottedname.resolve import resolve from zope import schema from zope.security.proxy import removeSecurityProxy from cybertools.typology.type import BaseType, TypeManager +from cybertools.typology.interfaces import ITypeManager from loops.interfaces import ILoopsObject, IConcept, IResource -from loops.interfaces import ITypeConcept, IResourceAdapter, IFile, IImage +from loops.interfaces import ITypeConcept +from loops.interfaces import IResourceAdapter, IFile, IImage, ITextDocument from loops.concept import Concept from loops.resource import Resource, Document, MediaAsset @@ -92,7 +94,8 @@ class LoopsType(BaseType): @Lazy def typeProvider(self): - return self.context.conceptType + # TODO: unify this type attribute naming... + return self.context.resourceType class LoopsTypeInfo(LoopsType): @@ -110,6 +113,10 @@ class ConceptType(LoopsType): adapts(IConcept) + @Lazy + def typeProvider(self): + return self.context.conceptType + class ConceptTypeInfo(LoopsTypeInfo): """ The type info class used by the type manager for listing types. @@ -233,8 +240,7 @@ class TypeInterfaceSourceList(object): implements(schema.interfaces.IIterableSource) - #typeInterfaces = (ITypeConcept, IFile, IImage,) - typeInterfaces = (ITypeConcept,) + typeInterfaces = (ITypeConcept, IFile, ITextDocument) def __init__(self, context): self.context = context @@ -246,6 +252,25 @@ class TypeInterfaceSourceList(object): return len(self.typeInterfaces) +class ResourceTypeSourceList(object): + + implements(schema.interfaces.IIterableSource) + + def __init__(self, context): + self.context = context + + def __iter__(self): + return iter(self.resourceTypes) + + @Lazy + def resourceTypes(self): + types = ITypeManager(self.context).listTypes(include=('resource',)) + return [t.typeProvider for t in types] + + def __len__(self): + return len(self.resourceTypes) + + class AdapterBase(object): """ (Mix-in) Class for concept adapters that provide editing of fields defined by the type interface.