reorganize resource stuff - dynamic typing, first type is 'file'
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1289 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
89ae9aeab0
commit
e860cf5a07
16 changed files with 349 additions and 298 deletions
46
README.txt
46
README.txt
|
@ -17,12 +17,12 @@ with lower-level aspects like type or state management.
|
|||
|
||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||
>>> site = placefulSetUp(True)
|
||||
|
||||
|
||||
>>> from zope.app import zapi
|
||||
>>> from zope.app.tests import ztapi
|
||||
>>> from zope.interface import Interface
|
||||
>>> from zope.publisher.browser import TestRequest
|
||||
|
||||
|
||||
|
||||
Concepts and Relations
|
||||
======================
|
||||
|
@ -73,7 +73,7 @@ also need a default predicate concept; the default name for this is
|
|||
|
||||
Now we can assign the concept c2 as a child to c1 (using the standard
|
||||
ConceptRelation):
|
||||
|
||||
|
||||
>>> cc1.assignChild(cc2)
|
||||
|
||||
We can now ask our concepts for their related child and parent concepts:
|
||||
|
@ -163,7 +163,7 @@ The token attribute provided with the items returned by the children() and
|
|||
parents() methods identifies identifies not only the item itself but
|
||||
also the relationship to the context object using a combination
|
||||
of URIs to item and the predicate of the relationship:
|
||||
|
||||
|
||||
>>> [c.token for c in children]
|
||||
['.loops/concepts/cc2:.loops/concepts/standard']
|
||||
|
||||
|
@ -207,11 +207,11 @@ types and predicates.
|
|||
>>> from zope.schema.interfaces import IIterableSource
|
||||
>>> ztapi.provideAdapter(IIterableSource, ITerms, LoopsTerms,
|
||||
... with=(IBrowserRequest,))
|
||||
|
||||
|
||||
>>> sorted((t.title, t.token) for t in view.conceptTypes())
|
||||
[(u'Topic', '.loops/concepts/topic'), (u'Type', '.loops/concepts/type'),
|
||||
(u'Unknown Type', '.loops/concepts/unknown')]
|
||||
|
||||
|
||||
>>> sorted((t.title, t.token) for t in view.predicates())
|
||||
[(u'subconcept', '.loops/concepts/standard')]
|
||||
|
||||
|
@ -222,7 +222,7 @@ Index attributes adapter
|
|||
>>> idx = IndexAttributes(cc2)
|
||||
>>> idx.text()
|
||||
u'cc2 Zope 3'
|
||||
|
||||
|
||||
>>> idx.title()
|
||||
u'cc2 Zope 3'
|
||||
|
||||
|
@ -230,16 +230,16 @@ Index attributes adapter
|
|||
Resources and what they have to do with Concepts
|
||||
================================================
|
||||
|
||||
>>> from loops.interfaces import IDocument, IMediaAsset
|
||||
>>> from loops.interfaces import IResource, IDocument, IMediaAsset
|
||||
|
||||
We first need a resource manager:
|
||||
|
||||
|
||||
>>> from loops.resource import ResourceManager
|
||||
>>> loopsRoot['resources'] = ResourceManager()
|
||||
>>> resources = loopsRoot['resources']
|
||||
|
||||
A common type of resource is a document:
|
||||
|
||||
|
||||
>>> from loops.interfaces import IDocument
|
||||
>>> from loops.resource import Document
|
||||
>>> doc1 = Document(u'Zope Info')
|
||||
|
@ -247,9 +247,9 @@ A common type of resource is a document:
|
|||
>>> doc1.title
|
||||
u'Zope Info'
|
||||
>>> doc1.data
|
||||
u''
|
||||
>>> doc1.contentType
|
||||
''
|
||||
>>> doc1.contentType
|
||||
u''
|
||||
|
||||
Another one is a media asset:
|
||||
|
||||
|
@ -258,7 +258,7 @@ Another one is a media asset:
|
|||
>>> img = MediaAsset(u'A png Image')
|
||||
|
||||
For testing we use some simple files from the tests directory:
|
||||
|
||||
|
||||
>>> from loops import tests
|
||||
>>> import os
|
||||
>>> path = os.path.join(*tests.__path__)
|
||||
|
@ -312,7 +312,7 @@ These relations may also be managed starting from a resource using
|
|||
the resource configuration view:
|
||||
|
||||
>>> from loops.browser.resource import ResourceConfigureView
|
||||
|
||||
|
||||
Index attributes adapter
|
||||
------------------------
|
||||
|
||||
|
@ -320,7 +320,7 @@ Index attributes adapter
|
|||
>>> idx = IndexAttributes(doc1)
|
||||
>>> idx.text()
|
||||
u'doc1 Zope Info'
|
||||
|
||||
|
||||
>>> idx.title()
|
||||
u'doc1 Zope Info'
|
||||
|
||||
|
@ -340,7 +340,7 @@ the views or nodes, however, present informations coming from the concepts
|
|||
or resources they are related to.
|
||||
|
||||
We first need a view manager:
|
||||
|
||||
|
||||
>>> from loops.view import ViewManager, Node
|
||||
>>> from zope.security.checker import NamesChecker, defineChecker
|
||||
>>> nodeChecker = NamesChecker(('body',))
|
||||
|
@ -351,7 +351,7 @@ We first need a view manager:
|
|||
|
||||
The view space is typically built up with nodes; a node may be a top-level
|
||||
menu that may contain other nodes as menu or content items:
|
||||
|
||||
|
||||
>>> m1 = Node(u'Menu')
|
||||
>>> views['m1'] = m1
|
||||
>>> m11 = Node(u'Zope')
|
||||
|
@ -426,7 +426,7 @@ out - this is usually done through ZCML.)
|
|||
>>> from cybertools.relation.interfaces import IRelationInvalidatedEvent
|
||||
>>> ztapi.subscribe([ITargetRelation, IRelationInvalidatedEvent], None,
|
||||
... removeTargetRelation)
|
||||
|
||||
|
||||
>>> m111.target = cc1
|
||||
>>> m111.target is cc1
|
||||
True
|
||||
|
@ -487,10 +487,10 @@ accessing a target via a node view it is usually wrapped in a corresponding
|
|||
view; these views we have to provide as multi-adapters:
|
||||
|
||||
>>> from loops.browser.node import ConfigureView
|
||||
>>> from loops.browser.resource import DocumentView, MediaAssetView
|
||||
>>> from loops.browser.resource import DocumentView, ResourceView
|
||||
>>> ztapi.provideAdapter(IDocument, Interface, DocumentView,
|
||||
... with=(IBrowserRequest,))
|
||||
>>> ztapi.provideAdapter(IMediaAsset, Interface, MediaAssetView,
|
||||
>>> ztapi.provideAdapter(IResource, Interface, ResourceView,
|
||||
... with=(IBrowserRequest,))
|
||||
|
||||
>>> form = {'action': 'create', 'create.title': 'New Resource',
|
||||
|
@ -592,10 +592,10 @@ Let's add some more nodes and reorder them:
|
|||
>>> m11['m114'] = m114
|
||||
>>> m11.keys()
|
||||
['m111', 'm112', 'm113', 'm114']
|
||||
|
||||
|
||||
A special management view provides methods for moving objects down, up,
|
||||
to the bottom, and to the top.
|
||||
|
||||
|
||||
>>> from cybertools.container.ordered import OrderedContainerView
|
||||
>>> view = OrderedContainerView(m11, TestRequest())
|
||||
>>> view.move_bottom(('m113',))
|
||||
|
@ -641,7 +641,7 @@ instance to another.
|
|||
>>> exporter.dumpData()
|
||||
|
||||
Load them again from the exported file:
|
||||
|
||||
|
||||
>>> importer = NodesImporter(views)
|
||||
>>> importer.filename = dumpname
|
||||
>>> imported = importer.getData()
|
||||
|
|
|
@ -117,7 +117,7 @@ class BaseView(object):
|
|||
@Lazy
|
||||
def loopsRoot(self):
|
||||
return self.context.getLoopsRoot()
|
||||
|
||||
|
||||
@Lazy
|
||||
def url(self):
|
||||
return zapi.absoluteURL(self.context, self.request)
|
||||
|
@ -199,9 +199,10 @@ class LoopsTerms(object):
|
|||
@Lazy
|
||||
def loopsRoot(self):
|
||||
return self.context.getLoopsRoot()
|
||||
|
||||
|
||||
def getTerm(self, value):
|
||||
#return BaseView(value, self.request)
|
||||
#if value is None:
|
||||
# return SimpleTerm(None, '', u'not assigned')
|
||||
title = value.title or zapi.getName(value)
|
||||
token = self.loopsRoot.getLoopsUri(value)
|
||||
return SimpleTerm(value, token, title)
|
||||
|
|
|
@ -66,14 +66,14 @@ class ConceptEditForm(EditForm):
|
|||
class ConceptView(BaseView):
|
||||
|
||||
template = NamedTemplate('loops.concept_macros')
|
||||
|
||||
|
||||
@Lazy
|
||||
def macro(self):
|
||||
return self.template.macros['conceptdata']
|
||||
|
||||
def fieldData(self):
|
||||
ti = IType(self.context).typeInterface
|
||||
if not ti: return
|
||||
if not ti: return
|
||||
adapter = ti(self.context)
|
||||
for n, f in schema.getFieldsInOrder(ti):
|
||||
value = getattr(adapter, n, '')
|
||||
|
|
|
@ -233,6 +233,21 @@
|
|||
|
||||
<!-- resource in general -->
|
||||
|
||||
<page
|
||||
for="loops.interfaces.IResource"
|
||||
name="index.html"
|
||||
permission="zope.View"
|
||||
class=".resource.ResourceView"
|
||||
attribute="show" />
|
||||
|
||||
<zope:adapter
|
||||
for="loops.interfaces.IResource
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.browser.resource.ResourceView"
|
||||
permission="zope.View"
|
||||
/>
|
||||
|
||||
<pages
|
||||
for="loops.interfaces.IResource"
|
||||
class=".resource.ResourceConfigureView"
|
||||
|
@ -246,12 +261,19 @@
|
|||
|
||||
</pages>
|
||||
|
||||
<zope:adapter
|
||||
for="loops.interfaces.IResource
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.browser.resource.ResourceView"
|
||||
permission="zope.View"
|
||||
<page
|
||||
name="edit.html"
|
||||
for="loops.interfaces.IResource"
|
||||
class="loops.browser.resource.ResourceEditForm"
|
||||
permission="zope.ManageContent"
|
||||
menu="zmi_views" title="Edit"
|
||||
/>
|
||||
|
||||
<!-- suppress the upload menu item: -->
|
||||
<menuItem
|
||||
for="loops.interfaces.IResource"
|
||||
menu="zmi_views" action="upload.html" title="Upload"
|
||||
filter="nothing"
|
||||
/>
|
||||
|
||||
<!-- document -->
|
||||
|
@ -263,33 +285,6 @@
|
|||
class=".resource.DocumentView"
|
||||
attribute="show" />
|
||||
|
||||
<addform
|
||||
label="Add Document"
|
||||
name="AddLoopsDocument.html"
|
||||
schema="loops.interfaces.IDocumentSchema"
|
||||
fields="title data contentType"
|
||||
content_factory="loops.resource.Document"
|
||||
template="add.pt"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<addMenuItem
|
||||
class="loops.resource.Document"
|
||||
title="Document"
|
||||
description="A document is an editable information unit"
|
||||
permission="zope.ManageContent"
|
||||
view="AddLoopsDocument.html"
|
||||
/>
|
||||
|
||||
<!-- <editform
|
||||
label="Edit Document"
|
||||
name="edit.html"
|
||||
schema="loops.interfaces.IDocumentSchema"
|
||||
fields="title data contentType"
|
||||
for="loops.interfaces.IDocument"
|
||||
template="edit.pt"
|
||||
permission="zope.ManageContent"
|
||||
menu="zmi_views" title="Edit" />-->
|
||||
|
||||
<page
|
||||
name="edit.html"
|
||||
for="loops.interfaces.IDocument"
|
||||
|
@ -322,25 +317,7 @@
|
|||
|
||||
<!-- media asset -->
|
||||
|
||||
<addform
|
||||
label="Add Media Asset"
|
||||
name="AddLoopsMediaAsset.html"
|
||||
schema="loops.interfaces.IMediaAssetSchema"
|
||||
fields="title data contentType"
|
||||
content_factory="loops.resource.MediaAsset"
|
||||
template="add.pt"
|
||||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<addMenuItem
|
||||
class="loops.resource.MediaAsset"
|
||||
title="Media Asset"
|
||||
description="A media asset is a binary file, image, video or audio file"
|
||||
permission="zope.ManageContent"
|
||||
view="AddLoopsMediaAsset.html"
|
||||
/>
|
||||
|
||||
<editform
|
||||
<!--<editform
|
||||
label="Edit Media Asset"
|
||||
name="edit.html"
|
||||
schema="loops.interfaces.IMediaAssetSchema"
|
||||
|
@ -349,7 +326,7 @@
|
|||
template="edit.pt"
|
||||
permission="zope.ManageContent"
|
||||
menu="zmi_views" title="Edit Media Asset"
|
||||
/>
|
||||
/>-->
|
||||
|
||||
<!--<page
|
||||
name="edit.html"
|
||||
|
@ -359,21 +336,6 @@
|
|||
menu="zmi_views" title="Edit"
|
||||
/>-->
|
||||
|
||||
<!-- suppress the upload menu item: -->
|
||||
<menuItem
|
||||
for="loops.interfaces.IMediaAsset"
|
||||
menu="zmi_views" action="upload.html" title="Upload"
|
||||
filter="nothing"
|
||||
/>
|
||||
|
||||
<zope:adapter
|
||||
for="loops.interfaces.IMediaAsset
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.browser.resource.MediaAssetView"
|
||||
permission="zope.View"
|
||||
/>
|
||||
|
||||
<!-- view manager -->
|
||||
|
||||
<addform
|
||||
|
@ -584,6 +546,10 @@
|
|||
for="loops.type.TypeInterfaceSourceList
|
||||
zope.publisher.interfaces.browser.IBrowserRequest" />
|
||||
|
||||
<zope:adapter factory="loops.browser.common.LoopsTerms"
|
||||
for="loops.resource.ResourceTypeSourceList
|
||||
zope.publisher.interfaces.browser.IBrowserRequest" />
|
||||
|
||||
<zope:view factory="loops.view.NodeTraverser"
|
||||
for="loops.interfaces.INode"
|
||||
type="zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
|
|
|
@ -204,7 +204,7 @@ class NodeView(BaseView):
|
|||
|
||||
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')
|
||||
if target is None:
|
||||
|
@ -250,7 +250,7 @@ class ConfigureView(NodeView):
|
|||
obj = self.targetObject
|
||||
if obj is not None:
|
||||
return zapi.getMultiAdapter((obj, self.request))
|
||||
|
||||
|
||||
def update(self):
|
||||
request = self.request
|
||||
action = request.get('action')
|
||||
|
@ -291,6 +291,8 @@ class ConfigureView(NodeView):
|
|||
target.title = form.get('create.title', u'')
|
||||
if IConcept.providedBy(target):
|
||||
target.conceptType = type.typeProvider
|
||||
elif IResource.providedBy(target):
|
||||
target.resourceType = type.typeProvider
|
||||
notify(ObjectCreatedEvent(target))
|
||||
notify(ObjectModifiedEvent(target))
|
||||
self.context.target = target
|
||||
|
|
|
@ -33,8 +33,8 @@ from zope.proxy import removeAllProxies
|
|||
from zope.security import canAccess, canWrite
|
||||
from zope.security.proxy import removeSecurityProxy
|
||||
|
||||
from loops.interfaces import IDocument, IMediaAsset
|
||||
from loops.interfaces import IFileSystemResource, IControlledResource
|
||||
from cybertools.typology.interfaces import IType
|
||||
from loops.interfaces import IBaseResource, IDocument, IMediaAsset
|
||||
from loops.browser.common import EditForm, BaseView
|
||||
from loops.browser.concept import ConceptRelationView, ConceptConfigureView
|
||||
from loops.browser.node import NodeView
|
||||
|
@ -48,12 +48,27 @@ renderingFactories = {
|
|||
}
|
||||
|
||||
|
||||
class ResourceEditForm(EditForm):
|
||||
|
||||
@Lazy
|
||||
def typeInterface(self):
|
||||
return IType(self.context).typeInterface
|
||||
|
||||
@property
|
||||
def form_fields(self):
|
||||
fields = FormFields(IBaseResource)
|
||||
typeInterface = self.typeInterface
|
||||
if typeInterface is not None:
|
||||
fields = FormFields(fields, typeInterface)
|
||||
return fields
|
||||
|
||||
|
||||
class DocumentEditForm(EditForm):
|
||||
#form_fields = FormFields(IDocument, IFileSystemResource, IControlledResource)
|
||||
form_fields = FormFields(IDocument)
|
||||
for f in form_fields:
|
||||
f.render_context |= DISPLAY_UNWRITEABLE
|
||||
|
||||
|
||||
class MediaAssetEditForm(EditForm):
|
||||
form_fields = FormFields(IMediaAsset)
|
||||
|
||||
|
@ -62,6 +77,20 @@ class ResourceView(BaseView):
|
|||
|
||||
template = ViewPageTemplateFile('resource_macros.pt')
|
||||
|
||||
@property
|
||||
def macro(self):
|
||||
if 'image/' in self.context.contentType:
|
||||
return self.template.macros['image']
|
||||
else:
|
||||
return self.template.macros['download']
|
||||
|
||||
def show(self):
|
||||
data = self.context.data
|
||||
response = self.request.response
|
||||
response.setHeader('Content-Type', self.context.contentType)
|
||||
response.setHeader('Content-Length', len(data))
|
||||
return data
|
||||
|
||||
def concepts(self):
|
||||
for r in self.context.getConceptRelations():
|
||||
yield ConceptRelationView(r, self.request)
|
||||
|
@ -130,8 +159,10 @@ class ResourceConfigureView(ResourceView, ConceptConfigureView):
|
|||
|
||||
class DocumentView(ResourceView):
|
||||
|
||||
macro = ResourceView.template.macros['render']
|
||||
|
||||
@property
|
||||
def macro(self):
|
||||
return ResourceView.template.macros['render']
|
||||
|
||||
def render(self):
|
||||
""" Return the rendered content (data) of the context object.
|
||||
"""
|
||||
|
@ -143,19 +174,3 @@ class DocumentView(ResourceView):
|
|||
view = zapi.getMultiAdapter((removeAllProxies(source), self.request))
|
||||
return view.render()
|
||||
|
||||
def show(self):
|
||||
data = self.context.data
|
||||
response = self.request.response
|
||||
response.setHeader('Content-Type', self.context.contentType)
|
||||
response.setHeader('Content-Length', len(data))
|
||||
return data
|
||||
|
||||
|
||||
class MediaAssetView(ResourceView):
|
||||
|
||||
@property
|
||||
def macro(self):
|
||||
if 'image/' in self.context.contentType:
|
||||
return self.template.macros['image']
|
||||
else:
|
||||
return self.template.macros['download']
|
||||
|
|
47
common.py
47
common.py
|
@ -29,9 +29,54 @@ from zope.app.dublincore.zopedublincore import ScalarProperty
|
|||
from zope.component import adapts
|
||||
from zope.interface import implements
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from loops.interfaces import ILoopsObject
|
||||
from loops.interfaces import ILoopsObject, IConcept, IResource
|
||||
from loops.interfaces import IResourceAdapter
|
||||
|
||||
|
||||
# type interface adapters
|
||||
|
||||
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 = 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 __eq__(self, other):
|
||||
return self.context == other.context
|
||||
|
||||
|
||||
class ResourceAdapterBase(AdapterBase):
|
||||
|
||||
adapts(IResource)
|
||||
|
||||
_schemas = list(IResourceAdapter)
|
||||
|
||||
|
||||
# other adapters
|
||||
|
||||
class LoopsDCAdapter(ZDCAnnotatableAdapter):
|
||||
|
||||
implements(IZopeDublinCore)
|
||||
|
|
|
@ -125,6 +125,30 @@
|
|||
|
||||
</content>
|
||||
|
||||
<interface
|
||||
interface=".interfaces.IResource"
|
||||
type="zope.app.content.interfaces.IContentType" />
|
||||
|
||||
<class class=".resource.Resource">
|
||||
|
||||
<implements
|
||||
interface="zope.app.annotation.interfaces.IAttributeAnnotatable" />
|
||||
|
||||
<factory
|
||||
id="loops.Resource"
|
||||
description="Document" />
|
||||
|
||||
<require
|
||||
permission="zope.View"
|
||||
interface=".interfaces.IBaseResource
|
||||
zope.app.size.interfaces.ISized" />
|
||||
|
||||
<require
|
||||
permission="zope.ManageContent"
|
||||
set_schema=".interfaces.IBaseResource" />
|
||||
|
||||
</class>
|
||||
|
||||
<interface
|
||||
interface=".interfaces.IDocument"
|
||||
type="zope.app.content.interfaces.IContentType" />
|
||||
|
@ -141,19 +165,12 @@
|
|||
<require
|
||||
permission="zope.View"
|
||||
interface=".interfaces.IDocument
|
||||
.interfaces.IFileSystemResource
|
||||
.interfaces.IControlledResource
|
||||
zope.app.size.interfaces.ISized" />
|
||||
|
||||
<require
|
||||
permission="zope.ManageContent"
|
||||
set_schema=".interfaces.IDocument" />
|
||||
|
||||
<require
|
||||
permission="zope.ManageApplication"
|
||||
set_schema=".interfaces.IFileSystemResource
|
||||
.interfaces.IControlledResource" />
|
||||
|
||||
</content>
|
||||
|
||||
<interface
|
||||
|
@ -171,11 +188,11 @@
|
|||
|
||||
<require
|
||||
permission="zope.View"
|
||||
interface=".interfaces.IMediaAsset" />
|
||||
interface=".interfaces.IBaseResource" />
|
||||
|
||||
<require
|
||||
permission="zope.ManageContent"
|
||||
set_schema=".interfaces.IMediaAsset" />
|
||||
set_schema=".interfaces.IBaseResource" />
|
||||
|
||||
</content>
|
||||
|
||||
|
@ -249,12 +266,7 @@
|
|||
trusted="True" />
|
||||
|
||||
<adapter factory="loops.common.LoopsDCAdapter"
|
||||
for="loops.interfaces.IDocument"
|
||||
provides="zope.app.dublincore.interfaces.IZopeDublinCore"
|
||||
trusted="True" />
|
||||
|
||||
<adapter factory="loops.common.LoopsDCAdapter"
|
||||
for="loops.interfaces.IMediaAsset"
|
||||
for="loops.interfaces.IResource"
|
||||
provides="zope.app.dublincore.interfaces.IZopeDublinCore"
|
||||
trusted="True" />
|
||||
|
||||
|
@ -264,16 +276,30 @@
|
|||
|
||||
<adapter factory="loops.concept.IndexAttributes" />
|
||||
<adapter factory="loops.resource.IndexAttributes" />
|
||||
<adapter factory="loops.resource.IndexableResource" />
|
||||
<adapter factory="loops.resource.IndexableResource" trusted="True" />
|
||||
<class class="loops.resource.IndexableResource">
|
||||
<require permission="zope.View"
|
||||
interface="loops.interfaces.IBaseResourceSchema" />
|
||||
</class>
|
||||
|
||||
<adapter factory="loops.resource.DocumentReadFileAdapter" />
|
||||
<adapter factory="loops.resource.DocumentWriteFileAdapter" />
|
||||
|
||||
<adapter factory="loops.type.LoopsType" />
|
||||
<adapter factory="loops.type.ConceptType" />
|
||||
<adapter factory="loops.type.ResourceType" />
|
||||
<adapter factory="loops.type.ResourceType"
|
||||
for="loops.interfaces.IDocument" />
|
||||
<adapter factory="loops.type.ResourceType"
|
||||
for="loops.interfaces.IMediaAsset" />
|
||||
<adapter factory="loops.type.LoopsTypeManager" />
|
||||
|
||||
<adapter factory="loops.type.TypeConcept" />
|
||||
<adapter factory="loops.type.TypeConcept" trusted="True" />
|
||||
<class class="loops.type.TypeConcept">
|
||||
<require permission="zope.View"
|
||||
interface="loops.interfaces.ITypeConcept" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.interfaces.ITypeConcept" />
|
||||
</class>
|
||||
|
||||
<adapter factory="loops.query.QueryConcept" trusted="True" />
|
||||
<class class="loops.query.QueryConcept">
|
||||
|
@ -283,6 +309,14 @@
|
|||
set_schema="loops.query.IQueryConcept" />
|
||||
</class>
|
||||
|
||||
<adapter factory="loops.resource.FileAdapter" trusted="True" />
|
||||
<class class="loops.resource.FileAdapter">
|
||||
<require permission="zope.View"
|
||||
interface="loops.interfaces.IFile" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.interfaces.IFile" />
|
||||
</class>
|
||||
|
||||
|
||||
<adapter factory="loops.setup.SetupManager" />
|
||||
<adapter factory="loops.external.NodesLoader" />
|
||||
|
@ -302,7 +336,7 @@
|
|||
/>
|
||||
|
||||
<vocabulary
|
||||
factory="loops.type.ResourceTypeSourceList"
|
||||
factory="loops.resource.ResourceTypeSourceList"
|
||||
name="loops.resourceTypeSource"
|
||||
/>
|
||||
|
||||
|
|
100
interfaces.py
100
interfaces.py
|
@ -29,6 +29,7 @@ from zope.app.container.constraints import contains, containers
|
|||
from zope.app.container.interfaces import IContainer, IOrderedContainer
|
||||
from zope.app.file.interfaces import IImage as IBaseAsset
|
||||
from zope.app.folder.interfaces import IFolder
|
||||
from zope.app.size.interfaces import ISized
|
||||
from cybertools.relation.interfaces import IRelation
|
||||
|
||||
import util
|
||||
|
@ -62,7 +63,7 @@ class IPotentialTarget(Interface):
|
|||
|
||||
class IConcept(ILoopsObject, IPotentialTarget):
|
||||
""" The concept is the central element of the loops framework.
|
||||
|
||||
|
||||
A concept is related to other concepts, may have resources
|
||||
associated with it and may be referenced by views.
|
||||
"""
|
||||
|
@ -130,7 +131,7 @@ class IConcept(ILoopsObject, IPotentialTarget):
|
|||
def getResources(predicates=None):
|
||||
""" Return a sequence of resources assigned to self,
|
||||
optionally restricted to the predicates given.
|
||||
"""
|
||||
"""
|
||||
|
||||
def getResourceRelations(predicates=None, resource=None):
|
||||
""" Return a sequence of relations to resources assigned to self,
|
||||
|
@ -143,12 +144,12 @@ class IConcept(ILoopsObject, IPotentialTarget):
|
|||
|
||||
The relationship defaults to ConceptResourceRelation.
|
||||
"""
|
||||
|
||||
|
||||
def deassignResource(resource, predicates=None):
|
||||
""" Remove the relations to the resource given from self, optionally
|
||||
restricting them to the predicates given.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class IConceptView(Interface):
|
||||
""" Used for accessing a concept via a node's target attribute"""
|
||||
|
@ -178,12 +179,12 @@ class IConceptManagerContained(Interface):
|
|||
# resource interfaces
|
||||
|
||||
|
||||
class IBaseResource(Interface):
|
||||
class IBaseResource(ILoopsObject):
|
||||
""" 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'),
|
||||
|
@ -199,6 +200,26 @@ class IBaseResource(Interface):
|
|||
source="loops.resourceTypeSource",
|
||||
required=False)
|
||||
|
||||
data = schema.Bytes(
|
||||
title=_(u'Data'),
|
||||
description=_(u'Resource raw data'),
|
||||
default='',
|
||||
missing_value='',
|
||||
required=False)
|
||||
|
||||
contentType = schema.BytesLine(
|
||||
title=_(u'Content Type'),
|
||||
description=_(u'Content type (format) of the data field'),
|
||||
default='',
|
||||
missing_value='',
|
||||
required=False)
|
||||
|
||||
|
||||
class IBaseResourceSchema(Interface):
|
||||
""" New schema for resources; to be used by sub-interfaces that will
|
||||
be implemented by type adapters.
|
||||
"""
|
||||
|
||||
|
||||
class IResourceSchema(Interface):
|
||||
|
||||
|
@ -224,29 +245,6 @@ class IResourceSchema(Interface):
|
|||
required=False)
|
||||
|
||||
|
||||
# the next two interfaces are probably obsolete:
|
||||
|
||||
class IFileSystemResource(Interface):
|
||||
|
||||
fsPath = schema.BytesLine(
|
||||
title=_(u'Filesystem Path'),
|
||||
description=_(u'Optional path to a file in the filesystem '
|
||||
'to be used for storing the resource'),
|
||||
default='',
|
||||
missing_value='',
|
||||
required=False)
|
||||
|
||||
|
||||
class IControlledResource(Interface):
|
||||
|
||||
readOnly = schema.Bool(
|
||||
title=_(u'Read only'),
|
||||
description=_(u'Check this if resource may not be modified '
|
||||
'after being first filled with non-empty content'),
|
||||
default=False,
|
||||
required=False)
|
||||
|
||||
|
||||
class IResource(ILoopsObject, IPotentialTarget):
|
||||
""" A resource is an atomic information element that is made
|
||||
available via a view or a concept.
|
||||
|
@ -323,10 +321,10 @@ class IMediaAssetView(IMediaAssetSchema):
|
|||
|
||||
class IMediaAsset(IMediaAssetSchema, IResource, IBaseAsset):
|
||||
""" A resource containing a (typically binary) file-like content
|
||||
or an image.
|
||||
or an image.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
class IResourceManager(ILoopsObject, IContainer):
|
||||
""" A manager/container for resources.
|
||||
"""
|
||||
|
@ -475,7 +473,7 @@ class ILoops(ILoopsObject):
|
|||
""" Retrieve object specified by the loops uri (starting with
|
||||
'.loops/') given.
|
||||
"""
|
||||
|
||||
|
||||
def getConceptManager():
|
||||
""" Return the (default) concept manager.
|
||||
"""
|
||||
|
@ -545,7 +543,7 @@ class ITypeConcept(Interface):
|
|||
# storage = schema.Choice()
|
||||
|
||||
|
||||
class IResourceAdapter(Interface):
|
||||
class IResourceAdapter(IBaseResourceSchema):
|
||||
""" Base interface for adapters for resources. This is the base interface
|
||||
of the interfaces to be used as typeInterface attribute on type concepts
|
||||
specifying resource types.
|
||||
|
@ -556,15 +554,15 @@ class IFile(IResourceAdapter):
|
|||
""" A media asset that is not shown on a (web) page like an image but
|
||||
may be downloaded instead.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
class IImage(IResourceAdapter):
|
||||
""" A media asset that may be embedded in a (web) page as an image.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
class ITextDocument(IResourceAdapter):
|
||||
""" A resource containing some sort of plain text that may be rendered and
|
||||
""" A resource containing some sort of plain text that may be rendered and
|
||||
edited without necessarily involving a special external application
|
||||
(like e.g. OpenOffice); typical content types are text/html, text/xml,
|
||||
text/restructured, etc.
|
||||
|
@ -581,3 +579,27 @@ class IViewConfiguratorSchema(Interface):
|
|||
default=u'',
|
||||
required=False)
|
||||
|
||||
|
||||
# the next two interfaces are obsolete, they will be replaced by IResourceStorage:
|
||||
|
||||
class IFileSystemResource(Interface):
|
||||
|
||||
fsPath = schema.BytesLine(
|
||||
title=_(u'Filesystem Path'),
|
||||
description=_(u'Optional path to a file in the filesystem '
|
||||
'to be used for storing the resource'),
|
||||
default='',
|
||||
missing_value='',
|
||||
required=False)
|
||||
|
||||
|
||||
class IControlledResource(Interface):
|
||||
|
||||
readOnly = schema.Bool(
|
||||
title=_(u'Read only'),
|
||||
description=_(u'Check this if resource may not be modified '
|
||||
'after being first filled with non-empty content'),
|
||||
default=False,
|
||||
required=False)
|
||||
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ from loops.interfaces import IConcept, IResource
|
|||
from loops.knowledge.interfaces import IPerson, ITask
|
||||
from loops.organize.party import Person as BasePerson
|
||||
from loops.organize.task import Task as BaseTask
|
||||
from loops.type import TypeInterfaceSourceList, AdapterBase
|
||||
from loops.common import AdapterBase
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
# register type interfaces - (TODO: use a function for this)
|
||||
|
|
|
@ -41,7 +41,8 @@ from cybertools.typology.interfaces import IType
|
|||
from loops.concept import Concept
|
||||
from loops.interfaces import IConcept
|
||||
from loops.organize.interfaces import IPerson, ANNOTATION_KEY
|
||||
from loops.type import TypeInterfaceSourceList, AdapterBase
|
||||
from loops.common import AdapterBase
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
# register type interfaces - (TODO: use a function for this)
|
||||
|
|
|
@ -26,7 +26,8 @@ from zope.interface import implements
|
|||
|
||||
from cybertools.organize.interfaces import ITask
|
||||
from loops.interfaces import IConcept
|
||||
from loops.type import TypeInterfaceSourceList, AdapterBase
|
||||
from loops.common import AdapterBase
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (ITask,)
|
||||
|
|
|
@ -33,7 +33,8 @@ from cybertools.typology.interfaces import IType
|
|||
from cybertools.process.interfaces import IProcess
|
||||
from cybertools.process.definition import Process as BaseProcess
|
||||
from loops.interfaces import IConcept
|
||||
from loops.type import TypeInterfaceSourceList, AdapterBase
|
||||
from loops.common import AdapterBase
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
|
||||
# register type interfaces - (TODO: use a function for this)
|
||||
|
|
3
query.py
3
query.py
|
@ -32,7 +32,8 @@ from zope.security.proxy import removeSecurityProxy
|
|||
|
||||
from cybertools.typology.type import BaseType, TypeManager
|
||||
from loops.interfaces import IConcept
|
||||
from loops.type import AdapterBase, TypeInterfaceSourceList
|
||||
from loops.common import AdapterBase
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
|
||||
_ = MessageFactory('loops')
|
||||
|
||||
|
|
127
resource.py
127
resource.py
|
@ -25,13 +25,15 @@ $Id$
|
|||
from zope.app import zapi
|
||||
from zope.app.container.btree import BTreeContainer
|
||||
from zope.app.container.contained import Contained
|
||||
from zope.app.file.image import Image as BaseMediaAsset
|
||||
from zope.app.file.image import Image
|
||||
from zope.app.file.interfaces import IFile
|
||||
from zope.app.filerepresentation.interfaces import IReadFile, IWriteFile
|
||||
from zope.app.size.interfaces import ISized
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.component import adapts
|
||||
from zope.i18nmessageid import MessageFactory
|
||||
from zope.interface import implements
|
||||
from zope import schema
|
||||
from persistent import Persistent
|
||||
from cStringIO import StringIO
|
||||
|
||||
|
@ -39,36 +41,42 @@ from textindexng.interfaces import IIndexableContent
|
|||
from textindexng.content import IndexContentCollector
|
||||
from cybertools.relation.registry import getRelations
|
||||
from cybertools.relation.interfaces import IRelatable
|
||||
from cybertools.typology.interfaces import ITypeManager
|
||||
|
||||
from interfaces import IBaseResource, IResource
|
||||
from interfaces import IFile
|
||||
from interfaces import IDocument, IDocumentSchema, IDocumentView
|
||||
from interfaces import IMediaAsset, IMediaAssetSchema, IMediaAssetView
|
||||
from interfaces import IFileSystemResource, IControlledResource
|
||||
from interfaces import IResourceManager, IResourceManagerContained
|
||||
from interfaces import ILoopsContained
|
||||
from interfaces import IIndexAttributes
|
||||
from concept import ResourceRelation
|
||||
from common import ResourceAdapterBase
|
||||
from view import TargetRelation
|
||||
|
||||
_ = MessageFactory('loops')
|
||||
|
||||
|
||||
class Resource(Contained, Persistent):
|
||||
class Resource(Image, Contained):
|
||||
|
||||
implements(IBaseResource, IResource, IResourceManagerContained, IRelatable, ISized)
|
||||
|
||||
implements(IBaseResource, IResource, IFileSystemResource, IControlledResource,
|
||||
IResourceManagerContained, IRelatable)
|
||||
|
||||
proxyInterface = IMediaAssetView
|
||||
|
||||
_size = _width = _height = 0
|
||||
|
||||
def __init__(self, title=u''):
|
||||
super(Resource, self).__init__()
|
||||
self.title = title
|
||||
|
||||
def getResourceType(self):
|
||||
typePred = self.getLoopsRoot().getConceptManager().getTypePredicate()
|
||||
cm = self.getLoopsRoot().getConceptManager()
|
||||
typePred = cm.getTypePredicate()
|
||||
if typePred is None:
|
||||
return None
|
||||
concepts = self.getConcepts([typePred])
|
||||
# TODO (?): check for multiple types (->Error)
|
||||
return concepts and concepts[0] or None
|
||||
return concepts and concepts[0] or cm.get('file', None)
|
||||
def setResourceType(self, concept):
|
||||
current = self.getResourceType()
|
||||
if current != concept:
|
||||
|
@ -80,32 +88,32 @@ class Resource(Contained, Persistent):
|
|||
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
|
||||
title = property(getTitle, setTitle)
|
||||
|
||||
_contentType = ''
|
||||
def _setData(self, data):
|
||||
dataFile = StringIO(data) # let File tear it into pieces
|
||||
super(Resource, self)._setData(dataFile)
|
||||
if not self.contentType:
|
||||
self.guessContentType(data)
|
||||
data = property(Image._getData, _setData)
|
||||
|
||||
def guessContentType(self, data):
|
||||
if not isinstance(data, str): # seems to be a file object
|
||||
data = data.read(20)
|
||||
if data.startswith('%PDF'):
|
||||
self.contentType = 'application/pdf'
|
||||
|
||||
_contentType = u''
|
||||
def setContentType(self, contentType):
|
||||
if contentType:
|
||||
self._contentType = contentType
|
||||
def getContentType(self): return self._contentType
|
||||
contentType = property(getContentType, setContentType)
|
||||
|
||||
_fsPath = ''
|
||||
def setFsPath(self, fsPath): self._fsPath = fsPath
|
||||
def getFsPath(self): return self._fsPath
|
||||
fsPath = property(getFsPath, setFsPath)
|
||||
|
||||
_readOnly = ''
|
||||
def setReadOnly(self, readOnly): self._readOnly = readOnly
|
||||
def getReadOnly(self): return self._readOnly
|
||||
readOnly = property(getReadOnly, setReadOnly)
|
||||
|
||||
def __init__(self, title=u''):
|
||||
self.title = title
|
||||
|
||||
def getLoopsRoot(self):
|
||||
return zapi.getParent(self).getLoopsRoot()
|
||||
|
||||
|
@ -122,7 +130,7 @@ class Resource(Contained, Persistent):
|
|||
relationships = [ResourceRelation(None, self, p) for p in predicates]
|
||||
# TODO: sort...
|
||||
return getRelations(first=concept, second=self, relationships=relationships)
|
||||
|
||||
|
||||
def getConcepts(self, predicates=None):
|
||||
return [r.first for r in self.getConceptRelations(predicates)]
|
||||
|
||||
|
@ -132,17 +140,7 @@ class Resource(Contained, Persistent):
|
|||
def deassignConcept(self, concept, predicates=None):
|
||||
concept.deassignResource(self, predicates)
|
||||
|
||||
|
||||
class Document(Resource):
|
||||
|
||||
implements(IDocument, ISized)
|
||||
|
||||
proxyInterface = IDocumentView
|
||||
|
||||
_data = u''
|
||||
def setData(self, data): self._data = data
|
||||
def getData(self): return self._data
|
||||
data = property(getData, setData)
|
||||
# ISized interface
|
||||
|
||||
def getSize(self):
|
||||
return len(self.data)
|
||||
|
@ -154,29 +152,24 @@ class Document(Resource):
|
|||
return '%i Bytes' % self.getSize()
|
||||
|
||||
|
||||
class MediaAsset(Resource, BaseMediaAsset):
|
||||
class Document(Resource):
|
||||
|
||||
implements(IMediaAsset)
|
||||
implements(IDocument)
|
||||
|
||||
proxyInterface = IMediaAssetView
|
||||
proxyInterface = IDocumentView
|
||||
|
||||
def __init__(self, title=u''):
|
||||
super(MediaAsset, self).__init__()
|
||||
self.title = title
|
||||
|
||||
def _setData(self, data):
|
||||
dataFile = StringIO(data) # let File tear it into pieces
|
||||
super(MediaAsset, self)._setData(dataFile)
|
||||
if not self.contentType:
|
||||
self.guessContentType(data)
|
||||
_data = ''
|
||||
def setData(self, data): self._data = data
|
||||
def getData(self): return self._data
|
||||
data = property(getData, setData)
|
||||
|
||||
data = property(BaseMediaAsset._getData, _setData)
|
||||
|
||||
def guessContentType(self, data):
|
||||
if not isinstance(data, str): # seems to be a file object
|
||||
data = data.read(20)
|
||||
if data.startswith('%PDF'):
|
||||
self.contentType = 'application/pdf'
|
||||
class MediaAsset(Resource):
|
||||
|
||||
implements(IMediaAsset)
|
||||
|
||||
|
||||
class ResourceManager(BTreeContainer):
|
||||
|
@ -188,17 +181,19 @@ class ResourceManager(BTreeContainer):
|
|||
|
||||
def getViewManager(self):
|
||||
return self.getLoopsRoot().getViewManager()
|
||||
|
||||
|
||||
|
||||
|
||||
# adapters and similar stuff
|
||||
|
||||
|
||||
class FileAdapter(object):
|
||||
class FileAdapter(ResourceAdapterBase):
|
||||
""" A type adapter for providing file functionality for resources.
|
||||
"""
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
implements(IFile)
|
||||
|
||||
# TODO: provide specialized access to data attribute analog to zope.app.file;
|
||||
# automatically set contentType...
|
||||
|
||||
|
||||
class DocumentWriteFileAdapter(object):
|
||||
|
@ -259,3 +254,23 @@ class IndexableResource(object):
|
|||
icc.addBinary(fields[0], context.data, context.contentType, language='de')
|
||||
return icc
|
||||
|
||||
|
||||
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 if t.typeProvider is not None]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.resourceTypes)
|
||||
|
||||
|
||||
|
|
64
type.py
64
type.py
|
@ -36,6 +36,7 @@ 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
|
||||
from loops.common import AdapterBase
|
||||
|
||||
|
||||
class LoopsType(BaseType):
|
||||
|
@ -65,7 +66,7 @@ class LoopsType(BaseType):
|
|||
def typeInterface(self):
|
||||
adapter = zapi.queryAdapter(self.typeProvider, ITypeConcept)
|
||||
if adapter is not None:
|
||||
return adapter.typeInterface
|
||||
return removeSecurityProxy(adapter.typeInterface)
|
||||
else:
|
||||
conceptType = self.typeProvider
|
||||
typeConcept = self.root.getConceptManager().getTypeConcept()
|
||||
|
@ -130,7 +131,7 @@ class ResourceType(LoopsType):
|
|||
type concepts as is already the case for concepts.
|
||||
"""
|
||||
|
||||
adapts(IResource)
|
||||
#adapts(IResource)
|
||||
|
||||
typeTitles = {'MediaAsset': u'Media Asset'}
|
||||
|
||||
|
@ -214,15 +215,13 @@ class LoopsTypeManager(TypeManager):
|
|||
for cls in (Document, MediaAsset)])
|
||||
|
||||
|
||||
class TypeConcept(object):
|
||||
class TypeConcept(AdapterBase):
|
||||
""" typeInterface adapter for concepts of type 'type'.
|
||||
"""
|
||||
|
||||
implements(ITypeConcept)
|
||||
adapts(IConcept)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = removeSecurityProxy(context)
|
||||
_schemas = list(ITypeConcept) + list(IConcept)
|
||||
|
||||
def getTypeInterface(self):
|
||||
ti = getattr(self.context, '_typeInterface', None)
|
||||
|
@ -251,56 +250,3 @@ class TypeInterfaceSourceList(object):
|
|||
def __len__(self):
|
||||
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.
|
||||
"""
|
||||
|
||||
adapts(IConcept)
|
||||
|
||||
_attributes = ('context', '__parent__', )
|
||||
_schemas = 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 __eq__(self, other):
|
||||
return self.context == other.context
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue