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