More on assigning targets to views/nodes via a vocabulary, including doc tests
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1055 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
4bbf9d4df9
commit
48f580ca31
10 changed files with 89 additions and 57 deletions
50
README.txt
50
README.txt
|
@ -271,20 +271,20 @@ 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
|
Node Configuration
|
||||||
--------------------
|
------------------
|
||||||
|
|
||||||
When configuring a node you may
|
When configuring a node you may specify what you want to do with respect
|
||||||
specify what you want to do with respect to the node's target: associate
|
to the node's target: associate an existing one or create a new one.
|
||||||
an existing one or create a new one.
|
These options are provided via the INodeConfigSchema that is provided
|
||||||
|
by a NodeConfigAdapter; in addition the attributes of the node (like the
|
||||||
|
title) may be changed via the NodeConfigAdapter.
|
||||||
|
|
||||||
>>> from loops.interfaces import INodeConfigSchema
|
>>> from loops.interfaces import INodeConfigSchema
|
||||||
>>> from loops.view import NodeConfigAdapter
|
>>> from loops.view import NodeConfigAdapter
|
||||||
>>> ztapi.provideAdapter(INode, INodeConfigSchema, NodeConfigAdapter)
|
>>> ztapi.provideAdapter(INode, INodeConfigSchema, NodeConfigAdapter)
|
||||||
>>> nodeConfig = INodeConfigSchema(m111)
|
>>> nodeConfig = INodeConfigSchema(m111)
|
||||||
|
|
||||||
>>> nodeConfig.targetUri
|
|
||||||
'.loops/concepts/cc2'
|
|
||||||
>>> nodeConfig.title = u'New title for m111'
|
>>> nodeConfig.title = u'New title for m111'
|
||||||
>>> nodeConfig.title
|
>>> nodeConfig.title
|
||||||
u'New title for m111'
|
u'New title for m111'
|
||||||
|
@ -293,9 +293,43 @@ an existing one or create a new one.
|
||||||
>>> nodeConfig.target = doc1
|
>>> nodeConfig.target = doc1
|
||||||
>>> m111.target is doc1
|
>>> m111.target is doc1
|
||||||
True
|
True
|
||||||
|
>>> m111 in doc1.getClients()
|
||||||
|
True
|
||||||
|
|
||||||
|
The targetUri and targetType fields are only relevant when creating
|
||||||
|
a new target object:
|
||||||
|
|
||||||
|
>>> nodeConfig.targetUri
|
||||||
|
''
|
||||||
>>> nodeConfig.targetType
|
>>> nodeConfig.targetType
|
||||||
'loops.resource.Document'
|
'loops.resource.Document'
|
||||||
>>> m111 in doc1.getClients()
|
|
||||||
|
The node configuration form provides a target assignment field using
|
||||||
|
a vocabulary (source) for selecting the target. (In a future version this form
|
||||||
|
will be extended by a widget that lets you search for potential target
|
||||||
|
objects.) The source is basically a source list:
|
||||||
|
|
||||||
|
>>> from loops.target import TargetSourceList
|
||||||
|
>>> source = TargetSourceList(m111)
|
||||||
|
>>> len(source)
|
||||||
|
3
|
||||||
|
>>> sorted([zapi.getName(s) for s in source])
|
||||||
|
[u'cc1', u'cc2', u'doc1']
|
||||||
|
|
||||||
|
The form then uses a sort of browser view providing the ITerms interface
|
||||||
|
based on this source list:
|
||||||
|
|
||||||
|
>>> from loops.browser.target import TargetTerms
|
||||||
|
>>> terms = TargetTerms(source, TestRequest())
|
||||||
|
>>> term = terms.getTerm(doc1)
|
||||||
|
>>> term.token, term.title, term.value
|
||||||
|
('.loops/resources/doc1', u'Zope Info', <loops.resource.Document...>)
|
||||||
|
|
||||||
|
>>> term = terms.getTerm(cc1)
|
||||||
|
>>> term.token, term.title, term.value
|
||||||
|
('.loops/concepts/cc1', u'cc1', <loops.concept.Concept...>)
|
||||||
|
|
||||||
|
>>> terms.getValue('.loops/concepts/cc1') is cc1
|
||||||
True
|
True
|
||||||
|
|
||||||
There is a special edit view class that can be used to configure a node
|
There is a special edit view class that can be used to configure a node
|
||||||
|
|
|
@ -37,6 +37,12 @@ class Loops(Folder):
|
||||||
def getLoopsRoot(self):
|
def getLoopsRoot(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def getConceptManager(self):
|
||||||
|
return self['concepts']
|
||||||
|
|
||||||
|
def getResourceManager(self):
|
||||||
|
return self['resources']
|
||||||
|
|
||||||
def getViewManager(self):
|
def getViewManager(self):
|
||||||
return self['views']
|
return self['views']
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ 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 IDocument, IMediaAsset
|
from loops.interfaces import IConcept, IDocument, IMediaAsset
|
||||||
from loops.resource import MediaAsset
|
from loops.resource import MediaAsset
|
||||||
|
|
||||||
class NodeView(object):
|
class NodeView(object):
|
||||||
|
@ -97,7 +97,9 @@ class NodeView(object):
|
||||||
target = self.target
|
target = self.target
|
||||||
if target is None or IDocument.providedBy(target):
|
if target is None or IDocument.providedBy(target):
|
||||||
return 'textbody'
|
return 'textbody'
|
||||||
if target.contentType.startswith('image/'):
|
if IConcept.providedBy(target): # TODO...
|
||||||
|
return 'filebody'
|
||||||
|
if IMediaAsset.providedBy(target) and target.contentType.startswith('image/'):
|
||||||
return 'imagebody'
|
return 'imagebody'
|
||||||
return 'filebody'
|
return 'filebody'
|
||||||
|
|
||||||
|
@ -138,20 +140,23 @@ class ConfigureBaseView(object):
|
||||||
def checkCreateTarget(self):
|
def checkCreateTarget(self):
|
||||||
form = self.request.form
|
form = self.request.form
|
||||||
if form.get('field.createTarget', False):
|
if form.get('field.createTarget', False):
|
||||||
|
root = self.loopsRoot
|
||||||
type = self.request.form.get('field.targetType',
|
type = self.request.form.get('field.targetType',
|
||||||
'loops.resource.MediaAsset')
|
'loops.resource.MediaAsset')
|
||||||
factory = resolve(type)
|
factory = resolve(type)
|
||||||
uri = self.request.form.get('field.targetUri', None)
|
uri = self.request.form.get('field.targetUri', None)
|
||||||
if uri:
|
if uri:
|
||||||
path = uri.split('/')
|
path = uri.split('/')
|
||||||
|
# TODO: check for .loops prefix
|
||||||
containerName = path[-2]
|
containerName = path[-2]
|
||||||
name = path[-1]
|
name = path[-1]
|
||||||
|
container = root[containerName]
|
||||||
else:
|
else:
|
||||||
containerName = 'resource' in type and 'resources' or 'concepts'
|
container = ('.resource.' in type and root.getResourceManager()
|
||||||
viewManagerPath = zapi.getPath(self.context.getViewManager())
|
or root.getConceptManager())
|
||||||
|
viewManagerPath = zapi.getPath(root.getViewManager())
|
||||||
name = zapi.getPath(self.context)[len(viewManagerPath)+1:]
|
name = zapi.getPath(self.context)[len(viewManagerPath)+1:]
|
||||||
name = name.replace('/', '.')
|
name = name.replace('/', '.')
|
||||||
container = self.loopsRoot[containerName]
|
|
||||||
# check for duplicates:
|
# check for duplicates:
|
||||||
num = 1
|
num = 1
|
||||||
basename = name
|
basename = name
|
||||||
|
@ -161,9 +166,8 @@ class ConfigureBaseView(object):
|
||||||
# create target:
|
# create target:
|
||||||
container[name] = factory()
|
container[name] = factory()
|
||||||
target = container[name]
|
target = container[name]
|
||||||
# set possibly new targetUri in request for further processing:
|
# set possibly new target uri in request for further processing:
|
||||||
targetUri = self.loopsRoot.getLoopsUri(target)
|
targetUri = self.loopsRoot.getLoopsUri(target)
|
||||||
#form['field.targetUri'] = targetUri
|
|
||||||
form['field.target'] = targetUri
|
form['field.target'] = targetUri
|
||||||
return target
|
return target
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,12 @@
|
||||||
The body
|
The body
|
||||||
</div>
|
</div>
|
||||||
<div class="content-1"
|
<div class="content-1"
|
||||||
tal:define="target view/target;
|
tal:define="target item/target;
|
||||||
onclick string:openEditWindow('${item/url}/@@edit_target.html')"
|
onclick string:openEditWindow('${item/url}/@@edit_target.html')"
|
||||||
tal:condition="target"
|
tal:condition="target"
|
||||||
tal:attributes="class string:content-$level;
|
tal:attributes="class string:content-$level;
|
||||||
ondblclick python: item.editable and onclick or ''"
|
ondblclick python: item.editable and onclick or ''"
|
||||||
tal:content="structure view/renderTargetBody">
|
tal:content="structure item/renderTargetBody">
|
||||||
The body
|
The body
|
||||||
</div>
|
</div>
|
||||||
</tal:body>
|
</tal:body>
|
||||||
|
|
|
@ -45,7 +45,8 @@ class TargetTerms(object):
|
||||||
|
|
||||||
def getTerm(self, value):
|
def getTerm(self, value):
|
||||||
token = self.loopsRoot.getLoopsUri(value)
|
token = self.loopsRoot.getLoopsUri(value)
|
||||||
return SimpleTerm(value, token, value.title)
|
title = value.title or zapi.getName(value)
|
||||||
|
return SimpleTerm(value, token, title)
|
||||||
|
|
||||||
def getValue(self, token):
|
def getValue(self, token):
|
||||||
return self.loopsRoot.loopsTraverse(token)
|
return self.loopsRoot.loopsTraverse(token)
|
||||||
|
|
|
@ -67,9 +67,6 @@ class Concept(Contained, Persistent):
|
||||||
def getLoopsRoot(self):
|
def getLoopsRoot(self):
|
||||||
return zapi.getParent(self).getLoopsRoot()
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
def getViewManager(self):
|
|
||||||
return self.getLoopsRoot().getViewManager()
|
|
||||||
|
|
||||||
# concept relations
|
# concept relations
|
||||||
|
|
||||||
def getSubConcepts(self, relationships=None):
|
def getSubConcepts(self, relationships=None):
|
||||||
|
|
|
@ -46,13 +46,9 @@ class ILoopsObject(Interface):
|
||||||
""" Return the loops root object.
|
""" Return the loops root object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getViewManager():
|
|
||||||
""" Return the (default) view manager.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class IPotentialTarget(Interface):
|
class IPotentialTarget(Interface):
|
||||||
""" For objects that may be used as target objects for view objects.
|
""" For objects that may be used as target objects for views/nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proxyInterface = Attribute('An interface allowing an object to be '
|
proxyInterface = Attribute('An interface allowing an object to be '
|
||||||
|
@ -381,7 +377,20 @@ class ILoops(ILoopsObject, IFolder):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def loopsTraverse(uri):
|
def loopsTraverse(uri):
|
||||||
""" Retrieve object specified by the loops uri given.
|
""" Retrieve object specified by the loops uri (starting with
|
||||||
|
'.loops/') given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getConceptManager():
|
||||||
|
""" Return the (default) concept manager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getResourceManager():
|
||||||
|
""" Return the (default) resource manager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getViewManager():
|
||||||
|
""" Return the (default) view manager.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,6 @@ class Resource(Contained, Persistent):
|
||||||
def getLoopsRoot(self):
|
def getLoopsRoot(self):
|
||||||
return zapi.getParent(self).getLoopsRoot()
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
def getViewManager(self):
|
|
||||||
return self.getLoopsRoot().getViewManager()
|
|
||||||
|
|
||||||
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]
|
||||||
|
|
11
target.py
11
target.py
|
@ -109,12 +109,15 @@ class TargetSourceList(object):
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = removeSecurityProxy(context)
|
self.context = removeSecurityProxy(context)
|
||||||
self.resources = self.context.getLoopsRoot()['resources']
|
root = self.context.getLoopsRoot()
|
||||||
|
self.resources = root.getResourceManager()
|
||||||
|
self.concepts = root.getConceptManager()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.resources.values())
|
return iter(list(self.resources.values()) + list(self.concepts.values()))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.resources) + len(self.concepts)
|
||||||
|
|
||||||
def __len__():
|
|
||||||
return len(self.resources)
|
|
||||||
|
|
||||||
|
|
25
view.py
25
view.py
|
@ -91,9 +91,6 @@ class View(object):
|
||||||
def getLoopsRoot(self):
|
def getLoopsRoot(self):
|
||||||
return zapi.getParent(self).getLoopsRoot()
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
def getViewManager(self):
|
|
||||||
return zapi.getParent(self).getViewManager()
|
|
||||||
|
|
||||||
|
|
||||||
class Node(View, OrderedContainer):
|
class Node(View, OrderedContainer):
|
||||||
|
|
||||||
|
@ -204,23 +201,8 @@ class NodeConfigAdapter(object):
|
||||||
@Lazy
|
@Lazy
|
||||||
def loopsRoot(self): return self.context.getLoopsRoot()
|
def loopsRoot(self): return self.context.getLoopsRoot()
|
||||||
|
|
||||||
def getTargetUri(self):
|
def getTargetUri(self):return ''
|
||||||
target = self.target
|
def setTargetUri(self, uri): pass
|
||||||
if target is not None:
|
|
||||||
return self.loopsRoot.getLoopsUri(target)
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def setTargetUri(self, uri):
|
|
||||||
return # ignore - only relevant for target creation
|
|
||||||
if uri:
|
|
||||||
names = uri.split('/')
|
|
||||||
if names[0] == '.loops':
|
|
||||||
path = '/'.join(names[1:])
|
|
||||||
self.context.target = zapi.traverse(self.loopsRoot, path)
|
|
||||||
else:
|
|
||||||
self.context.target = None
|
|
||||||
|
|
||||||
targetUri = property(getTargetUri, setTargetUri)
|
targetUri = property(getTargetUri, setTargetUri)
|
||||||
|
|
||||||
def getTargetType(self):
|
def getTargetType(self):
|
||||||
|
@ -228,8 +210,7 @@ class NodeConfigAdapter(object):
|
||||||
if target:
|
if target:
|
||||||
return '%s.%s' % (target.__module__, target.__class__.__name__)
|
return '%s.%s' % (target.__module__, target.__class__.__name__)
|
||||||
return None
|
return None
|
||||||
def setTargetType(self, tt):
|
def setTargetType(self, tt): pass
|
||||||
pass # only used whe a new target object is created
|
|
||||||
targetType = property(getTargetType, setTargetType)
|
targetType = property(getTargetType, setTargetType)
|
||||||
|
|
||||||
def getCreateTarget(self): return False
|
def getCreateTarget(self): return False
|
||||||
|
|
Loading…
Add table
Reference in a new issue