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:
helmutm 2006-01-22 13:43:39 +00:00
parent f30caa3c14
commit ee7a84b6d5
11 changed files with 197 additions and 20 deletions

View file

@ -236,9 +236,10 @@ view class's target attribute:
>>> m111.target is cc2
True
Node views
Node Views
----------
>>> from loops.interfaces import INode
>>> from loops.browser.node import NodeView
>>> view = NodeView(m11, TestRequest())
@ -254,7 +255,26 @@ Node views
... print item.url, view.selected(item)
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
--------------

View file

@ -1,7 +1,7 @@
# -*- coding: UTF-8 -*-
# -*- 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
# it under the terms of the GNU General Public License as published by
@ -31,3 +31,6 @@ class Loops(Folder):
implements(ILoops)
def getLoopsRoot(self):
return self

View file

@ -218,6 +218,13 @@
permission="zope.ManageContent"
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 file

@ -46,3 +46,12 @@
font-size: 90%
}
.flow-left {
float: left;
}
div.image {
margin-top: 10px;
margin-right: 5px;
}

View file

@ -30,7 +30,6 @@ from zope.proxy import removeAllProxies
from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy
from loops.interfaces import IConcept
class NodeView(object):
@ -57,6 +56,10 @@ class NodeView(object):
d = dc.modified or dc.created
return d and d.strftime('%Y-%m-%d %H:%M') or ''
@Lazy
def target(self):
return self.context.target
@Lazy
def page(self):
page = self.context.getPage()

View file

@ -62,6 +62,9 @@ class Concept(Contained, Persistent):
def __init__(self, title=u''):
self.title = title
def getLoopsRoot(self):
return zapi.getParent(self).getLoopsRoot()
# concept relations
def getSubConcepts(self, relationships=None):
@ -122,3 +125,6 @@ class ConceptManager(BTreeContainer):
implements(IConceptManager, ILoopsContained)
def getLoopsRoot(self):
return zapi.getParent(self)

View file

@ -209,16 +209,17 @@
</content>
<!-- adapters for export/import -->
<!-- adapters -->
<adapter factory="loops.external.NodesLoader" />
<adapter factory="loops.external.NodesExporter" />
<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"
for="loops.interfaces.INode"
type="zope.publisher.interfaces.browser.IBrowserRequest"

View file

@ -33,9 +33,19 @@ from zope.app.folder.interfaces import IFolder
_ = MessageFactory('loops')
# common top-level
class ILoopsObject(Interface):
""" Common top-level interface.
"""
def getLoopsRoot():
""" Return the loops root object.
"""
# concept interfaces
class IConcept(Interface):
class IConcept(ILoopsObject):
""" The concept is the central element of the loops framework.
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.
"""
contains(IConcept)
@ -101,7 +111,7 @@ class IConceptManagerContained(Interface):
# resource interfaces
class IResource(Interface):
class IResource(ILoopsObject):
""" A resource is an atomic information element that is made
available via a view or a concept.
"""
@ -158,7 +168,7 @@ class IMediaAsset(IResource, IBaseAsset):
required=False)
class IResourceManager(IContainer):
class IResourceManager(ILoopsObject, IContainer):
""" A manager/container for resources.
"""
contains(IResource)
@ -170,7 +180,7 @@ class IResourceManagerContained(Interface):
# view interfaces
class IView(Interface):
class IView(ILoopsObject):
""" A view is a user interface component that provides access to one
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.
"""
contains(IView)
@ -271,9 +281,38 @@ class INodeContained(Interface):
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
class ILoops(IFolder):
class ILoops(ILoopsObject, IFolder):
""" The top-level object of a loops site.
"""
contains(IConceptManager, IResourceManager, IViewManager)

View file

@ -52,6 +52,9 @@ class Resource(Contained, Persistent):
def getContentType(self): return self._contentType
contentType = property(getContentType, setContentType)
def getLoopsRoot(self):
return zapi.getParent(self).getLoopsRoot()
def getClients(self, relationships=None):
rels = getRelations(second=self, relationships=relationships)
return [r.first for r in rels]
@ -99,4 +102,7 @@ class ResourceManager(BTreeContainer):
implements(IResourceManager, ILoopsContained)
def getLoopsRoot(self):
return zapi.getParent(self)

73
target.py Normal file
View 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
View file

@ -34,7 +34,7 @@ from persistent import Persistent
from cybertools.relation import DyadicRelation
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 ILoopsContained
from util import moveByDelta
@ -81,6 +81,9 @@ class View(object):
self.description = description
super(View, self).__init__()
def getLoopsRoot(self):
return zapi.getParent(self).getLoopsRoot()
class Node(View, OrderedContainer):
@ -98,9 +101,6 @@ class Node(View, OrderedContainer):
contentType = u'zope.source.rest'
def getLoopsRoot(self):
return zapi.getParent(self).getLoopsRoot()
def getParentNode(self, nodeTypes=None):
parent = zapi.getParent(self)
while INode.providedBy(parent):
@ -148,6 +148,7 @@ class TargetRelation(DyadicRelation):
""" A relation between a view and another object.
"""
# adapters
class NodeTraverser(ItemTraverser):
@ -159,3 +160,12 @@ class NodeTraverser(ItemTraverser):
return self.context.getLoopsRoot()
return super(NodeTraverser, self).publishTraverse(request, name)
class NodeConfigAdapter(object):
def __init__(self, context):
self.context = context
implements(INodeConfigSchema)
adapts(INode)