Basic setup for node schema adapters (+ some minor improvements)
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1022 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
f30caa3c14
commit
ee7a84b6d5
11 changed files with 197 additions and 20 deletions
24
README.txt
24
README.txt
|
@ -236,9 +236,10 @@ view class's target attribute:
|
||||||
>>> m111.target is cc2
|
>>> m111.target is cc2
|
||||||
True
|
True
|
||||||
|
|
||||||
Node views
|
Node Views
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
>>> from loops.interfaces import INode
|
||||||
>>> from loops.browser.node import NodeView
|
>>> from loops.browser.node import NodeView
|
||||||
>>> view = NodeView(m11, TestRequest())
|
>>> view = NodeView(m11, TestRequest())
|
||||||
|
|
||||||
|
@ -254,7 +255,26 @@ Node views
|
||||||
... print item.url, view.selected(item)
|
... print item.url, view.selected(item)
|
||||||
http://127.0.0.1/loops/views/m1/m11 True
|
http://127.0.0.1/loops/views/m1/m11 True
|
||||||
|
|
||||||
|
Node Schema Adapters
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
When creating or editing (more precisely: configuring) a node you may
|
||||||
|
specify what you want to do with respect to the node's target: associate
|
||||||
|
an existing one or create a new one (with specifying the target's type),
|
||||||
|
and give an URI that will be used to identify the target. (Internally
|
||||||
|
the reference to the target will be stored as a relation so that the
|
||||||
|
target may be moved or renamed without any problems.)
|
||||||
|
|
||||||
|
>>> from loops.interfaces import INodeConfigSchema
|
||||||
|
>>> from loops.view import NodeConfigAdapter
|
||||||
|
>>> ztapi.provideAdapter(INode, INodeConfigSchema, NodeConfigAdapter)
|
||||||
|
>>> nodeConfig = INodeConfigSchema(m111)
|
||||||
|
|
||||||
|
It is also possible to edit a target's attributes directly in an
|
||||||
|
edit form provided by the node:
|
||||||
|
|
||||||
|
>>> from loops.target import DocumentProxy, MediaAssetProxy
|
||||||
|
|
||||||
Ordering Nodes
|
Ordering Nodes
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: UTF-8 -*-
|
# -*- coding: UTF-8 -*-
|
||||||
# -*- Mode: Python; py-indent-offset: 4 -*-
|
# -*- Mode: Python; py-indent-offset: 4 -*-
|
||||||
#
|
#
|
||||||
# Copyright (c) 2005 Helmut Merz helmutm@cy55.de
|
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -31,3 +31,6 @@ class Loops(Folder):
|
||||||
|
|
||||||
implements(ILoops)
|
implements(ILoops)
|
||||||
|
|
||||||
|
def getLoopsRoot(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,13 @@
|
||||||
permission="zope.ManageContent"
|
permission="zope.ManageContent"
|
||||||
menu="zmi_views" title="Edit"
|
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"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- view manager -->
|
<!-- view manager -->
|
||||||
|
|
||||||
|
|
|
@ -46,3 +46,12 @@
|
||||||
font-size: 90%
|
font-size: 90%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flow-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.image {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ from zope.proxy import removeAllProxies
|
||||||
from zope.security import canAccess, canWrite
|
from zope.security import canAccess, canWrite
|
||||||
from zope.security.proxy import removeSecurityProxy
|
from zope.security.proxy import removeSecurityProxy
|
||||||
|
|
||||||
from loops.interfaces import IConcept
|
|
||||||
|
|
||||||
class NodeView(object):
|
class NodeView(object):
|
||||||
|
|
||||||
|
@ -57,6 +56,10 @@ class NodeView(object):
|
||||||
d = dc.modified or dc.created
|
d = dc.modified or dc.created
|
||||||
return d and d.strftime('%Y-%m-%d %H:%M') or ''
|
return d and d.strftime('%Y-%m-%d %H:%M') or ''
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def target(self):
|
||||||
|
return self.context.target
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def page(self):
|
def page(self):
|
||||||
page = self.context.getPage()
|
page = self.context.getPage()
|
||||||
|
|
|
@ -62,6 +62,9 @@ class Concept(Contained, Persistent):
|
||||||
def __init__(self, title=u''):
|
def __init__(self, title=u''):
|
||||||
self.title = title
|
self.title = title
|
||||||
|
|
||||||
|
def getLoopsRoot(self):
|
||||||
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
# concept relations
|
# concept relations
|
||||||
|
|
||||||
def getSubConcepts(self, relationships=None):
|
def getSubConcepts(self, relationships=None):
|
||||||
|
@ -122,3 +125,6 @@ class ConceptManager(BTreeContainer):
|
||||||
|
|
||||||
implements(IConceptManager, ILoopsContained)
|
implements(IConceptManager, ILoopsContained)
|
||||||
|
|
||||||
|
def getLoopsRoot(self):
|
||||||
|
return zapi.getParent(self)
|
||||||
|
|
||||||
|
|
|
@ -209,16 +209,17 @@
|
||||||
|
|
||||||
</content>
|
</content>
|
||||||
|
|
||||||
<!-- adapters for export/import -->
|
<!-- adapters -->
|
||||||
|
|
||||||
<adapter factory="loops.external.NodesLoader" />
|
<adapter factory="loops.external.NodesLoader" />
|
||||||
|
|
||||||
<adapter factory="loops.external.NodesExporter" />
|
<adapter factory="loops.external.NodesExporter" />
|
||||||
|
|
||||||
<adapter factory="loops.external.NodesImporter" />
|
<adapter factory="loops.external.NodesImporter" />
|
||||||
|
|
||||||
<!-- traversal adapter -->
|
<adapter factory="loops.view.NodeConfigAdapter" />
|
||||||
|
|
||||||
|
<adapter factory="loops.target.DocumentProxy" />
|
||||||
|
<adapter factory="loops.target.MediaAssetProxy" />
|
||||||
|
|
||||||
<view factory="loops.view.NodeTraverser"
|
<view factory="loops.view.NodeTraverser"
|
||||||
for="loops.interfaces.INode"
|
for="loops.interfaces.INode"
|
||||||
type="zope.publisher.interfaces.browser.IBrowserRequest"
|
type="zope.publisher.interfaces.browser.IBrowserRequest"
|
||||||
|
|
|
@ -33,9 +33,19 @@ from zope.app.folder.interfaces import IFolder
|
||||||
_ = MessageFactory('loops')
|
_ = MessageFactory('loops')
|
||||||
|
|
||||||
|
|
||||||
|
# common top-level
|
||||||
|
|
||||||
|
class ILoopsObject(Interface):
|
||||||
|
""" Common top-level interface.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getLoopsRoot():
|
||||||
|
""" Return the loops root object.
|
||||||
|
"""
|
||||||
|
|
||||||
# concept interfaces
|
# concept interfaces
|
||||||
|
|
||||||
class IConcept(Interface):
|
class IConcept(ILoopsObject):
|
||||||
""" The concept is the central element of the loops framework.
|
""" The concept is the central element of the loops framework.
|
||||||
|
|
||||||
A concept is related to other concepts, may have resources
|
A concept is related to other concepts, may have resources
|
||||||
|
@ -89,7 +99,7 @@ class IConcept(Interface):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class IConceptManager(IContainer):
|
class IConceptManager(ILoopsObject, IContainer):
|
||||||
""" A manager/container for concepts.
|
""" A manager/container for concepts.
|
||||||
"""
|
"""
|
||||||
contains(IConcept)
|
contains(IConcept)
|
||||||
|
@ -101,7 +111,7 @@ class IConceptManagerContained(Interface):
|
||||||
|
|
||||||
# resource interfaces
|
# resource interfaces
|
||||||
|
|
||||||
class IResource(Interface):
|
class IResource(ILoopsObject):
|
||||||
""" A resource is an atomic information element that is made
|
""" A resource is an atomic information element that is made
|
||||||
available via a view or a concept.
|
available via a view or a concept.
|
||||||
"""
|
"""
|
||||||
|
@ -158,7 +168,7 @@ class IMediaAsset(IResource, IBaseAsset):
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
|
|
||||||
class IResourceManager(IContainer):
|
class IResourceManager(ILoopsObject, IContainer):
|
||||||
""" A manager/container for resources.
|
""" A manager/container for resources.
|
||||||
"""
|
"""
|
||||||
contains(IResource)
|
contains(IResource)
|
||||||
|
@ -170,7 +180,7 @@ class IResourceManagerContained(Interface):
|
||||||
|
|
||||||
# view interfaces
|
# view interfaces
|
||||||
|
|
||||||
class IView(Interface):
|
class IView(ILoopsObject):
|
||||||
""" A view is a user interface component that provides access to one
|
""" A view is a user interface component that provides access to one
|
||||||
or more concepts, resources, or other views.
|
or more concepts, resources, or other views.
|
||||||
"""
|
"""
|
||||||
|
@ -261,7 +271,7 @@ class INode(IView, IBaseNode):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class IViewManager(IBaseNode):
|
class IViewManager(ILoopsObject, IBaseNode):
|
||||||
""" A manager/container for views.
|
""" A manager/container for views.
|
||||||
"""
|
"""
|
||||||
contains(IView)
|
contains(IView)
|
||||||
|
@ -271,9 +281,38 @@ class INodeContained(Interface):
|
||||||
containers(INode, IViewManager)
|
containers(INode, IViewManager)
|
||||||
|
|
||||||
|
|
||||||
|
# schemas to be used by forms on view/node objects
|
||||||
|
|
||||||
|
class ITargetProperties(Interface):
|
||||||
|
""" Fields used for specifying a view's or node's target.
|
||||||
|
"""
|
||||||
|
|
||||||
|
targetType = schema.Choice(
|
||||||
|
title=_(u'Target Type'),
|
||||||
|
description=_(u'Type of the target'),
|
||||||
|
values=('', 'resource.Document', 'resource.MediaAsset'),
|
||||||
|
default='',
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
targetUri = schema.TextLine(
|
||||||
|
title=_(u'Target URI'),
|
||||||
|
description=_(u'An URI being a unique reference to the target'),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class INodeConfigSchema(INode, ITargetProperties):
|
||||||
|
""" All fields that may be shown in the node add form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
createTarget = schema.Bool(
|
||||||
|
title=_(u'Create Target'),
|
||||||
|
description=_(u'Should a new target object be created?'),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
|
||||||
# the loops top-level container
|
# the loops top-level container
|
||||||
|
|
||||||
class ILoops(IFolder):
|
class ILoops(ILoopsObject, IFolder):
|
||||||
""" The top-level object of a loops site.
|
""" The top-level object of a loops site.
|
||||||
"""
|
"""
|
||||||
contains(IConceptManager, IResourceManager, IViewManager)
|
contains(IConceptManager, IResourceManager, IViewManager)
|
||||||
|
|
|
@ -52,6 +52,9 @@ class Resource(Contained, Persistent):
|
||||||
def getContentType(self): return self._contentType
|
def getContentType(self): return self._contentType
|
||||||
contentType = property(getContentType, setContentType)
|
contentType = property(getContentType, setContentType)
|
||||||
|
|
||||||
|
def getLoopsRoot(self):
|
||||||
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
def getClients(self, relationships=None):
|
def getClients(self, relationships=None):
|
||||||
rels = getRelations(second=self, relationships=relationships)
|
rels = getRelations(second=self, relationships=relationships)
|
||||||
return [r.first for r in rels]
|
return [r.first for r in rels]
|
||||||
|
@ -99,4 +102,7 @@ class ResourceManager(BTreeContainer):
|
||||||
|
|
||||||
implements(IResourceManager, ILoopsContained)
|
implements(IResourceManager, ILoopsContained)
|
||||||
|
|
||||||
|
def getLoopsRoot(self):
|
||||||
|
return zapi.getParent(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
73
target.py
Normal file
73
target.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Adapter classes (proxies, in fact), for providing access to concepts and
|
||||||
|
resources e.g. from forms that are called on view/node objects.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.app import zapi
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
from zope.component import adapts
|
||||||
|
from zope.interface import implements
|
||||||
|
|
||||||
|
from loops.interfaces import IResource, IDocument, IMediaAsset
|
||||||
|
from loops.interfaces import IView
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceProxy(object):
|
||||||
|
|
||||||
|
adapts(IView)
|
||||||
|
|
||||||
|
def getTitle(self): return self.target.title
|
||||||
|
def setTitle(self, title): self.title = title
|
||||||
|
title = property(getTitle, setTitle)
|
||||||
|
|
||||||
|
def setContentType(self, contentType):
|
||||||
|
self.target._contentType = contentType
|
||||||
|
def getContentType(self): return self.target.contentType
|
||||||
|
contentType = property(getContentType, setContentType)
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def target(self):
|
||||||
|
return self.context.target
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentProxy(ResourceProxy):
|
||||||
|
|
||||||
|
implements(IDocument)
|
||||||
|
|
||||||
|
def setData(self, data): self.target.data = data
|
||||||
|
def getData(self): return self.target.data
|
||||||
|
data = property(getData, setData)
|
||||||
|
|
||||||
|
|
||||||
|
class MediaAssetProxy(ResourceProxy):
|
||||||
|
|
||||||
|
implements(IMediaAsset)
|
||||||
|
|
||||||
|
def setData(self, data): self.target.data = data
|
||||||
|
def getData(self): return self.target.data
|
||||||
|
data = property(getData, setData)
|
||||||
|
|
||||||
|
|
18
view.py
18
view.py
|
@ -34,7 +34,7 @@ from persistent import Persistent
|
||||||
from cybertools.relation import DyadicRelation
|
from cybertools.relation import DyadicRelation
|
||||||
from cybertools.relation.registry import IRelationsRegistry, getRelations
|
from cybertools.relation.registry import IRelationsRegistry, getRelations
|
||||||
|
|
||||||
from interfaces import IView, INode
|
from interfaces import IView, INode, INodeConfigSchema
|
||||||
from interfaces import IViewManager, INodeContained
|
from interfaces import IViewManager, INodeContained
|
||||||
from interfaces import ILoopsContained
|
from interfaces import ILoopsContained
|
||||||
from util import moveByDelta
|
from util import moveByDelta
|
||||||
|
@ -81,6 +81,9 @@ class View(object):
|
||||||
self.description = description
|
self.description = description
|
||||||
super(View, self).__init__()
|
super(View, self).__init__()
|
||||||
|
|
||||||
|
def getLoopsRoot(self):
|
||||||
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
|
|
||||||
class Node(View, OrderedContainer):
|
class Node(View, OrderedContainer):
|
||||||
|
|
||||||
|
@ -98,9 +101,6 @@ class Node(View, OrderedContainer):
|
||||||
|
|
||||||
contentType = u'zope.source.rest'
|
contentType = u'zope.source.rest'
|
||||||
|
|
||||||
def getLoopsRoot(self):
|
|
||||||
return zapi.getParent(self).getLoopsRoot()
|
|
||||||
|
|
||||||
def getParentNode(self, nodeTypes=None):
|
def getParentNode(self, nodeTypes=None):
|
||||||
parent = zapi.getParent(self)
|
parent = zapi.getParent(self)
|
||||||
while INode.providedBy(parent):
|
while INode.providedBy(parent):
|
||||||
|
@ -148,6 +148,7 @@ class TargetRelation(DyadicRelation):
|
||||||
""" A relation between a view and another object.
|
""" A relation between a view and another object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# adapters
|
||||||
|
|
||||||
class NodeTraverser(ItemTraverser):
|
class NodeTraverser(ItemTraverser):
|
||||||
|
|
||||||
|
@ -159,3 +160,12 @@ class NodeTraverser(ItemTraverser):
|
||||||
return self.context.getLoopsRoot()
|
return self.context.getLoopsRoot()
|
||||||
return super(NodeTraverser, self).publishTraverse(request, name)
|
return super(NodeTraverser, self).publishTraverse(request, name)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeConfigAdapter(object):
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
implements(INodeConfigSchema)
|
||||||
|
adapts(INode)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue