Work in progress: assign target to node using a search form
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1097 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									e86c3e331a
								
							
						
					
					
						commit
						a0948469d4
					
				
					 10 changed files with 346 additions and 149 deletions
				
			
		
							
								
								
									
										115
									
								
								README.txt
									
										
									
									
									
								
							
							
						
						
									
										115
									
								
								README.txt
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -390,113 +390,44 @@ Node Views
 | 
			
		|||
  ...     print item.url, view.selected(item)
 | 
			
		||||
  http://127.0.0.1/loops/views/m1/m11 True
 | 
			
		||||
 | 
			
		||||
Node Configuration
 | 
			
		||||
------------------
 | 
			
		||||
A Node and its Target
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
When 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.
 | 
			
		||||
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.view import NodeConfigAdapter
 | 
			
		||||
  >>> ztapi.provideAdapter(INode, INodeConfigSchema, NodeConfigAdapter)
 | 
			
		||||
  >>> nodeConfig = INodeConfigSchema(m111)
 | 
			
		||||
 | 
			
		||||
  >>> nodeConfig.title = u'New title for m111'
 | 
			
		||||
  >>> nodeConfig.title
 | 
			
		||||
  u'New title for m111'
 | 
			
		||||
  >>> m111.title
 | 
			
		||||
  u'New title for m111'
 | 
			
		||||
  >>> nodeConfig.target = doc1
 | 
			
		||||
  >>> m111.target is doc1
 | 
			
		||||
  >>> from loops.browser.node import ConfigureView
 | 
			
		||||
  >>> form = {'action': 'create', 'create.title': 'New Resource',
 | 
			
		||||
  ...         'create.type': 'loops.resource.MediaAsset',}
 | 
			
		||||
  >>> view = ConfigureView(m111, TestRequest(form = form))
 | 
			
		||||
  >>> sorted((t.token, t.title) for t in view.targetTypes())
 | 
			
		||||
  [('loops.concept.Concept', u'Concept'),
 | 
			
		||||
      ('loops.resource.Document', u'Document'),
 | 
			
		||||
      ('loops.resource.MediaAsset', u'Media Asset')]
 | 
			
		||||
  >>> view.update()
 | 
			
		||||
  True
 | 
			
		||||
  >>> m111 in doc1.getClients()
 | 
			
		||||
  True
 | 
			
		||||
 | 
			
		||||
The targetUri and targetType fields are only relevant when creating
 | 
			
		||||
a new target object:
 | 
			
		||||
 | 
			
		||||
  >>> nodeConfig.targetUri
 | 
			
		||||
  ''
 | 
			
		||||
  >>> nodeConfig.targetType
 | 
			
		||||
  'loops.resource.Document'
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
  1
 | 
			
		||||
  >>> sorted([zapi.getName(s) for s in source])
 | 
			
		||||
  [u'doc1']
 | 
			
		||||
 | 
			
		||||
The form then uses a sort of browser view providing the ITerms interface
 | 
			
		||||
based on this source list:
 | 
			
		||||
 | 
			
		||||
  >>> terms = LoopsTerms(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
 | 
			
		||||
 | 
			
		||||
There is a special edit view class that can be used to configure a node
 | 
			
		||||
in a way that allows the creation of a target object on the fly.
 | 
			
		||||
(We here use the base class providing the method for this action; the real
 | 
			
		||||
application uses a subclass that does all the other stuff for form handling.)
 | 
			
		||||
When creating a new target object you may specify a uri that determines
 | 
			
		||||
the location of the new target object and its name.
 | 
			
		||||
 | 
			
		||||
  >>> from loops.browser.node import ConfigureBaseView
 | 
			
		||||
  >>> view = ConfigureBaseView(INodeConfigSchema(m111), TestRequest())
 | 
			
		||||
  >>> view.checkCreateTarget()
 | 
			
		||||
  >>> sorted(resources.keys())
 | 
			
		||||
  [u'doc1']
 | 
			
		||||
  >>> form = {'field.createTarget': True,
 | 
			
		||||
  ...         'field.targetUri': '.loops/resources/ma07',
 | 
			
		||||
  ...         'field.targetType': 'loops.resource.MediaAsset'}
 | 
			
		||||
  >>> view = ConfigureBaseView(m111, TestRequest(form=form))
 | 
			
		||||
  >>> m111.target = view.checkCreateTarget()
 | 
			
		||||
  >>> sorted(resources.keys())
 | 
			
		||||
  [u'doc1', u'ma07']
 | 
			
		||||
  >>> isinstance(resources['ma07'], MediaAsset)
 | 
			
		||||
  True
 | 
			
		||||
  
 | 
			
		||||
  >>> form = {'field.createTarget': True,
 | 
			
		||||
  ...         'field.targetType': 'loops.resource.Document'}
 | 
			
		||||
  >>> view = ConfigureBaseView(m111, TestRequest(form=form))
 | 
			
		||||
  >>> m111.target = view.checkCreateTarget()
 | 
			
		||||
  >>> sorted(resources.keys())
 | 
			
		||||
  [u'doc1', u'm1.m11.m111', u'ma07']
 | 
			
		||||
  >>> isinstance(resources['m1.m11.m111'], Document)
 | 
			
		||||
  True
 | 
			
		||||
        
 | 
			
		||||
  [u'doc1', u'm1.m11.m111']
 | 
			
		||||
 | 
			
		||||
  >>> view.target.title, view.target.token
 | 
			
		||||
  ('New Resource', '.loops/resources/m1.m11.m111')
 | 
			
		||||
 | 
			
		||||
A node object provides the targetSchema of its target:
 | 
			
		||||
 | 
			
		||||
  >>> from loops.interfaces import IDocumentView
 | 
			
		||||
  >>> from loops.interfaces import IMediaAssetView
 | 
			
		||||
  >>> IDocumentView.providedBy(m111)
 | 
			
		||||
  True
 | 
			
		||||
  >>> IMediaAssetView.providedBy(m111)
 | 
			
		||||
  False
 | 
			
		||||
  >>> IMediaAssetView.providedBy(m111)
 | 
			
		||||
  True
 | 
			
		||||
  >>> m111.target = None
 | 
			
		||||
  >>> IDocumentView.providedBy(m111)
 | 
			
		||||
  False
 | 
			
		||||
  >>> m111.target = resources['ma07']
 | 
			
		||||
  >>> m111.target = resources['doc1']
 | 
			
		||||
  >>> IDocumentView.providedBy(m111)
 | 
			
		||||
  False
 | 
			
		||||
  >>> IMediaAssetView.providedBy(m111)
 | 
			
		||||
  True
 | 
			
		||||
  >>> IMediaAssetView.providedBy(m111)
 | 
			
		||||
  False
 | 
			
		||||
 | 
			
		||||
A node's target is rendered using the NodeView's renderTargetBody()
 | 
			
		||||
method. This makes use of a browser view registered for the target interface,
 | 
			
		||||
| 
						 | 
				
			
			@ -535,7 +466,7 @@ edit form provided by the node:
 | 
			
		|||
 | 
			
		||||
  >>> proxy = zapi.getAdapter(m111, IDocumentView)
 | 
			
		||||
  >>> proxy.title = u'Set via proxy'
 | 
			
		||||
  >>> resources['ma07'].title
 | 
			
		||||
  >>> resources['doc1'].title
 | 
			
		||||
  u'Set via proxy'
 | 
			
		||||
 | 
			
		||||
If the target object is removed from its container all references
 | 
			
		||||
| 
						 | 
				
			
			@ -549,7 +480,7 @@ cybertools.relation package.)
 | 
			
		|||
  >>> ztapi.subscribe([Interface, IObjectRemovedEvent], None,
 | 
			
		||||
  ...                 invalidateRelations)
 | 
			
		||||
 | 
			
		||||
  >>> del resources['ma07']
 | 
			
		||||
  >>> del resources['doc1']
 | 
			
		||||
  >>> m111.target
 | 
			
		||||
  >>> IMediaAssetView.providedBy(m111)
 | 
			
		||||
  False
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,9 @@ from zope.cachedescriptors.property import Lazy
 | 
			
		|||
from zope.interface import implements
 | 
			
		||||
from zope.security.proxy import removeSecurityProxy
 | 
			
		||||
 | 
			
		||||
from loops import util
 | 
			
		||||
from loops.target import getTargetTypes
 | 
			
		||||
 | 
			
		||||
class BaseView(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, context, request):
 | 
			
		||||
| 
						 | 
				
			
			@ -66,12 +69,20 @@ class BaseView(object):
 | 
			
		|||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def typeTitle(self):
 | 
			
		||||
        return self.context.conceptType.title
 | 
			
		||||
        voc = util.KeywordVocabulary(getTargetTypes())
 | 
			
		||||
        token = '.'.join((self.context.__module__,
 | 
			
		||||
                         self.context.__class__.__name__))
 | 
			
		||||
        term = voc.getTermByToken(token)
 | 
			
		||||
        return term.title
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def typeUrl(self):
 | 
			
		||||
        return zapi.absoluteURL(self.context.conceptType, self.request)
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def viewIterator(self, objs):
 | 
			
		||||
        request = self.request
 | 
			
		||||
        for o in objs:
 | 
			
		||||
            yield BaseView(o, request)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LoopsTerms(object):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,6 +134,14 @@ class ConceptView(BaseView):
 | 
			
		|||
            result = [r for r in result if r.conceptType == type]
 | 
			
		||||
        return self.viewIterator(result)
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def typeTitle(self):
 | 
			
		||||
        return self.context.conceptType.title
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def typeUrl(self):
 | 
			
		||||
        return zapi.absoluteURL(self.context.conceptType, self.request)
 | 
			
		||||
 | 
			
		||||
    def viewIterator(self, objs):
 | 
			
		||||
        request = self.request
 | 
			
		||||
        for o in objs:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,13 @@
 | 
			
		|||
      permission="zope.View"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
  <page
 | 
			
		||||
      for="*"
 | 
			
		||||
      name="target_macros"
 | 
			
		||||
      template="target_macros.pt"
 | 
			
		||||
      permission="zope.View"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
  <!-- loops top-level container -->
 | 
			
		||||
 | 
			
		||||
  <addform
 | 
			
		||||
| 
						 | 
				
			
			@ -331,10 +338,10 @@
 | 
			
		|||
  <editform
 | 
			
		||||
      label="Configure Node"
 | 
			
		||||
      name="configure.html"
 | 
			
		||||
      schema="loops.interfaces.INodeConfigSchema"
 | 
			
		||||
      fields="title description nodeType target createTarget targetUri targetType"
 | 
			
		||||
      schema="loops.interfaces.INode"
 | 
			
		||||
      fields="title description nodeType target"
 | 
			
		||||
      for="loops.interfaces.INode"
 | 
			
		||||
      template="edit.pt"
 | 
			
		||||
      template="node_target.pt"
 | 
			
		||||
      class="loops.browser.node.ConfigureView"
 | 
			
		||||
      permission="zope.ManageContent">
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,7 +64,8 @@
 | 
			
		|||
        <div class="separator"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="row">
 | 
			
		||||
      <div metal:define-macro="submit_button"
 | 
			
		||||
           class="row">
 | 
			
		||||
        <div class="controls">
 | 
			
		||||
          <input type="submit" name="UPDATE_SUBMIT" value="Change"
 | 
			
		||||
                 i18n:attributes="value submit-button;" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										140
									
								
								browser/node.py
									
										
									
									
									
								
							
							
						
						
									
										140
									
								
								browser/node.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -24,16 +24,24 @@ $Id$
 | 
			
		|||
 | 
			
		||||
from zope.cachedescriptors.property import Lazy
 | 
			
		||||
from zope.app import zapi
 | 
			
		||||
from zope.app.catalog.interfaces import ICatalog
 | 
			
		||||
from zope.app.container.browser.contents import JustContents
 | 
			
		||||
from zope.app.dublincore.interfaces import ICMFDublinCore
 | 
			
		||||
from zope.app.event.objectevent import ObjectCreatedEvent
 | 
			
		||||
#import zope.configuration.name
 | 
			
		||||
from zope.dottedname.resolve import resolve
 | 
			
		||||
from zope.event import notify
 | 
			
		||||
from zope.proxy import removeAllProxies
 | 
			
		||||
from zope.security import canAccess, canWrite
 | 
			
		||||
from zope.security.proxy import removeSecurityProxy
 | 
			
		||||
 | 
			
		||||
from loops.interfaces import IConcept, IDocument, IMediaAsset
 | 
			
		||||
from loops.resource import MediaAsset
 | 
			
		||||
from loops.target import getTargetTypes
 | 
			
		||||
from loops import util
 | 
			
		||||
from loops.browser.common import BaseView
 | 
			
		||||
from loops.browser.concept import ConceptView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NodeView(object):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -124,12 +132,13 @@ class NodeView(object):
 | 
			
		|||
        return item.context == self.context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConfigureBaseView(object):
 | 
			
		||||
    """ Helper view class for editing/configuring a node, providing the
 | 
			
		||||
        stuff needed for creating a target object.
 | 
			
		||||
class ConfigureView(BaseView):
 | 
			
		||||
    """ An editing view for configuring a node, optionally creating
 | 
			
		||||
        a target object.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, context, request):
 | 
			
		||||
        #self.context = context
 | 
			
		||||
        self.context = removeSecurityProxy(context)
 | 
			
		||||
        self.request = request
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,53 +146,84 @@ class ConfigureBaseView(object):
 | 
			
		|||
    def loopsRoot(self):
 | 
			
		||||
        return self.context.getLoopsRoot()
 | 
			
		||||
 | 
			
		||||
    def checkCreateTarget(self):
 | 
			
		||||
        form = self.request.form
 | 
			
		||||
        if form.get('field.createTarget', False):
 | 
			
		||||
            root = self.loopsRoot
 | 
			
		||||
            type = self.request.form.get('field.targetType',
 | 
			
		||||
                                         'loops.resource.MediaAsset')
 | 
			
		||||
            factory = resolve(type)
 | 
			
		||||
            uri = self.request.form.get('field.targetUri', None)
 | 
			
		||||
            if uri:
 | 
			
		||||
                path = uri.split('/')
 | 
			
		||||
                # TODO: check for .loops prefix
 | 
			
		||||
                containerName = path[-2]
 | 
			
		||||
                name = path[-1]
 | 
			
		||||
                container = root[containerName]
 | 
			
		||||
            else:
 | 
			
		||||
                container = ('.resource.' in type and root.getResourceManager()
 | 
			
		||||
                          or root.getConceptManager())
 | 
			
		||||
                viewManagerPath = zapi.getPath(root.getViewManager())
 | 
			
		||||
                name = zapi.getPath(self.context)[len(viewManagerPath)+1:]
 | 
			
		||||
                name = name.replace('/', '.')
 | 
			
		||||
            # check for duplicates:
 | 
			
		||||
            num = 1
 | 
			
		||||
            basename = name
 | 
			
		||||
            while name in container:
 | 
			
		||||
                name = '%s-%d' % (basename, num)
 | 
			
		||||
                num += 1
 | 
			
		||||
            # create target:
 | 
			
		||||
            container[name] = factory()
 | 
			
		||||
            target = container[name]
 | 
			
		||||
            # set possibly new target uri in request for further processing:
 | 
			
		||||
            targetUri = self.loopsRoot.getLoopsUri(target)
 | 
			
		||||
            form['field.target'] = targetUri
 | 
			
		||||
            return target
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConfigureView(object):
 | 
			
		||||
    """ An editing view for configuring a node, optionally creating
 | 
			
		||||
        a target object.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, context, request):
 | 
			
		||||
        super(ConfigureView, self).__init__(context, request)
 | 
			
		||||
        self.delegate = ConfigureBaseView(context, request)
 | 
			
		||||
    @property
 | 
			
		||||
    def target(self):
 | 
			
		||||
        target = self.context.target
 | 
			
		||||
        if target is not None:
 | 
			
		||||
            if IConcept.providedBy(target):
 | 
			
		||||
                return ConceptView(target, self.request)
 | 
			
		||||
            return BaseView(target, self.request)
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def update(self):
 | 
			
		||||
        if self.update_status is not None:
 | 
			
		||||
            return self.update_status
 | 
			
		||||
        self.delegate.checkCreateTarget()
 | 
			
		||||
        return super(ConfigureView, self).update()
 | 
			
		||||
        request = self.request
 | 
			
		||||
        action = request.get('action')
 | 
			
		||||
        if action is None or action == 'search':
 | 
			
		||||
            return True
 | 
			
		||||
        if action == 'create':
 | 
			
		||||
            return self.createAndAssign()
 | 
			
		||||
        if action == 'assign':
 | 
			
		||||
            token = request.get('token')
 | 
			
		||||
            if token:
 | 
			
		||||
                target = self.loopsRoot.loopsTraverse(token)
 | 
			
		||||
            else:
 | 
			
		||||
                target = None
 | 
			
		||||
            self.context.target = target
 | 
			
		||||
        # TODO: raise error
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def createAndAssign(self):
 | 
			
		||||
        form = self.request.form
 | 
			
		||||
        root = self.loopsRoot
 | 
			
		||||
        type = self.request.form.get('create.type',
 | 
			
		||||
                                     'loops.resource.MediaAsset')
 | 
			
		||||
        factory = resolve(type)
 | 
			
		||||
        if '.resource.' in type:
 | 
			
		||||
            container = root.getResourceManager()
 | 
			
		||||
        else:
 | 
			
		||||
            container = root.getConceptManager()
 | 
			
		||||
        name = form.get('create.name', '')
 | 
			
		||||
        if not name:
 | 
			
		||||
            viewManagerPath = zapi.getPath(root.getViewManager())
 | 
			
		||||
            name = zapi.getPath(self.context)[len(viewManagerPath)+1:]
 | 
			
		||||
            name = name.replace('/', '.')
 | 
			
		||||
        # check for duplicates:
 | 
			
		||||
        num = 1
 | 
			
		||||
        basename = name
 | 
			
		||||
        while name in container:
 | 
			
		||||
            name = '%s-%d' % (basename, num)
 | 
			
		||||
            num += 1
 | 
			
		||||
        container[name] = removeSecurityProxy(factory())
 | 
			
		||||
        target = container[name]
 | 
			
		||||
        target.title = form.get('create.title', u'')
 | 
			
		||||
        notify(ObjectCreatedEvent(target))
 | 
			
		||||
        self.context.target = target
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def targetTypes(self):
 | 
			
		||||
        return util.KeywordVocabulary(getTargetTypes())
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def search(self):
 | 
			
		||||
        request = self.request
 | 
			
		||||
        if request.get('action') != 'search':
 | 
			
		||||
            return []
 | 
			
		||||
        searchTerm = request.get('searchTerm', None)
 | 
			
		||||
        if searchTerm:
 | 
			
		||||
            cat = zapi.getUtility(ICatalog)
 | 
			
		||||
            result = cat.searchResults(loops_searchableText=searchTerm)
 | 
			
		||||
        else:
 | 
			
		||||
            result = (list(self.loopsRoot.getConceptManager().values())
 | 
			
		||||
                    + list(self.loopsRoot.getResourceManager().values()))
 | 
			
		||||
        return list(self.viewIterator(result))
 | 
			
		||||
 | 
			
		||||
    def viewIterator(self, objs):
 | 
			
		||||
        request = self.request
 | 
			
		||||
        for o in objs:
 | 
			
		||||
            if o == self.context.target:
 | 
			
		||||
                continue
 | 
			
		||||
            if IConcept.providedBy(o):
 | 
			
		||||
                yield ConceptView(o, request)
 | 
			
		||||
            else:
 | 
			
		||||
                yield BaseView(o, request)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										43
									
								
								browser/node_target.pt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								browser/node_target.pt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
<tal:tag condition="view/update" />
 | 
			
		||||
<html metal:use-macro="context/@@standard_macros/view"
 | 
			
		||||
      i18n:domain="zope">
 | 
			
		||||
<head></head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div metal:fill-slot="body"
 | 
			
		||||
       tal:define="target view/target">
 | 
			
		||||
 | 
			
		||||
      <h1 tal:content="context/title">Node Title</h1><br />
 | 
			
		||||
 | 
			
		||||
      <div>
 | 
			
		||||
        <span i18n:translate=""
 | 
			
		||||
              tal:condition="not:target">No target assigned</span>
 | 
			
		||||
        <tal:target condition="target">
 | 
			
		||||
          <span i18n:translate="">Currently assigned target</span>:
 | 
			
		||||
          <a href="#"
 | 
			
		||||
             tal:attributes="href string:${target/url}/@@SelectedManagementView.html"
 | 
			
		||||
             tal:content="target/title">Document xy</a>
 | 
			
		||||
        </tal:target>
 | 
			
		||||
      </div>
 | 
			
		||||
      
 | 
			
		||||
      <div style="padding-right:20px">
 | 
			
		||||
        <metal:create use-macro="views/target_macros/create" />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div   tal:define="items view/search;
 | 
			
		||||
                         action string:assign;
 | 
			
		||||
                         target nocall:context/target;
 | 
			
		||||
                         summary string:Assignment candidates;
 | 
			
		||||
                         legend string:Search;
 | 
			
		||||
                         buttonText string:Assign Target;"
 | 
			
		||||
             style="padding-right:20px">
 | 
			
		||||
        <metal:assign use-macro="views/target_macros/listing">
 | 
			
		||||
          <metal:search fill-slot="topActions">
 | 
			
		||||
            <metal:block use-macro="views/target_macros/search" />
 | 
			
		||||
          </metal:search>
 | 
			
		||||
        </metal:assign>
 | 
			
		||||
      </div>
 | 
			
		||||
      
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										146
									
								
								browser/target_macros.pt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								browser/target_macros.pt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
<html i18n:domain="loops">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<metal:assignments define-macro="listing">
 | 
			
		||||
  <fieldset>
 | 
			
		||||
    <legend tal:content="legend"
 | 
			
		||||
            i18n:translate="">Listing</legend>
 | 
			
		||||
    <metal:top define-slot="topActions" />
 | 
			
		||||
    <form metal:define-macro="listing_form"
 | 
			
		||||
          method="post" name="listing" action="."
 | 
			
		||||
          tal:attributes="action request/URL"
 | 
			
		||||
          tal:condition="items">
 | 
			
		||||
      <input type="hidden" name="action" value="assign"
 | 
			
		||||
             tal:attributes="value action" />
 | 
			
		||||
      <table class="listing" summary="Currently assigned"
 | 
			
		||||
             i18n:attributes="summary"
 | 
			
		||||
             tal:attributes="summary summary">
 | 
			
		||||
        <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th> </th>
 | 
			
		||||
            <th i18n:translate="label_title">Title</th>
 | 
			
		||||
            <th i18n:translate="label_type">Type</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody tal:define="target view/target;
 | 
			
		||||
                           targetToken python: target and target.token or None">
 | 
			
		||||
          <tal:none define="item nothing; title string:None; token nothing;
 | 
			
		||||
                            type nothing">
 | 
			
		||||
            <metal:none use-macro="views/target_macros/list_item_tr" />
 | 
			
		||||
          </tal:none>
 | 
			
		||||
          <tal:current define="item target"
 | 
			
		||||
                       condition="item">
 | 
			
		||||
            <metal:current use-macro="views/target_macros/list_item" />
 | 
			
		||||
          </tal:current>
 | 
			
		||||
          <tal:items repeat="item items">
 | 
			
		||||
            <metal:item define-macro="list_item"
 | 
			
		||||
                        tal:define="title item/title;
 | 
			
		||||
                                    token item/token;
 | 
			
		||||
                                    type item/typeTitle">
 | 
			
		||||
              <tr metal:define-macro="list_item_tr">
 | 
			
		||||
                <td class="field">
 | 
			
		||||
                  <input class="formSelection"
 | 
			
		||||
                         type="radio" name="token" id="#" value=""
 | 
			
		||||
                         tal:attributes="value token;
 | 
			
		||||
                                checked python: targetToken == token" />
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <a href="#"
 | 
			
		||||
                     tal:omit-tag="not:item"
 | 
			
		||||
                     tal:content="title"
 | 
			
		||||
                     tal:attributes="href
 | 
			
		||||
                        string:${item/url|nothing}/@@SelectedManagementView.html">
 | 
			
		||||
                    Title
 | 
			
		||||
                  </a>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <a tal:condition="type"
 | 
			
		||||
                     href="#"
 | 
			
		||||
                     tal:attributes="href
 | 
			
		||||
                        string:${item/typeUrl}/@@SelectedManagementView.html"
 | 
			
		||||
                     tal:omit-tag="not:item/typeUrl">
 | 
			
		||||
                    <span tal:replace="type">Type</span>
 | 
			
		||||
                  </a>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </metal:item>
 | 
			
		||||
          </tal:items>
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </table>
 | 
			
		||||
      <div class="formControls">
 | 
			
		||||
        <input class="context" type="submit" name="form.button.submit"
 | 
			
		||||
               value="Change assignment"
 | 
			
		||||
               i18n:attributes="value"
 | 
			
		||||
               tal:attributes="value buttonText" />
 | 
			
		||||
        <metal:buttons define-slot="specialButtons" />
 | 
			
		||||
      </div>
 | 
			
		||||
    </form>
 | 
			
		||||
  </fieldset>
 | 
			
		||||
</metal:assignments>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<metal:create define-macro="create">
 | 
			
		||||
  <fieldset>
 | 
			
		||||
    <legend i18n:translate="">Create Target</legend>
 | 
			
		||||
    <form method="post" name="listing" action="."
 | 
			
		||||
        tal:attributes="action request/URL">
 | 
			
		||||
      <input type="hidden" name="action" value="create" />
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <span i18n:translate="">Name</span>
 | 
			
		||||
        <input name="create.name" size="15"
 | 
			
		||||
               tal:attributes="value nothing" /> 
 | 
			
		||||
        <span i18n:translate="">Title</span>
 | 
			
		||||
        <input name="create.title" size="30"
 | 
			
		||||
               tal:attributes="value nothing" /> 
 | 
			
		||||
        <span i18n:translate="">Type</span>
 | 
			
		||||
        <select name="create.type">
 | 
			
		||||
          <tal:types repeat="type view/targetTypes">
 | 
			
		||||
            <option value=".loops/concepts/topic"
 | 
			
		||||
                    i18n:translate=""
 | 
			
		||||
                    tal:attributes="value type/token"
 | 
			
		||||
                    tal:content="type/title">Topic</option>
 | 
			
		||||
          </tal:types>
 | 
			
		||||
        </select>
 | 
			
		||||
      </div><br />
 | 
			
		||||
      <div class="formControls">
 | 
			
		||||
        <input class="context" type="submit" name="form.button.submit"
 | 
			
		||||
               value="Create Target"
 | 
			
		||||
               i18n:attributes="value" />
 | 
			
		||||
      </div>
 | 
			
		||||
    </form>
 | 
			
		||||
  </fieldset>
 | 
			
		||||
</metal:create>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<metal:search define-macro="search">
 | 
			
		||||
  <form method="post" name="listing" action="."
 | 
			
		||||
        tal:attributes="action request/URL">
 | 
			
		||||
      <input type="hidden" name="action" value="search" />
 | 
			
		||||
      <div class="row"
 | 
			
		||||
           tal:define="searchTerm request/searchTerm | nothing;
 | 
			
		||||
                       searchType request/searchType | nothing;">
 | 
			
		||||
        <span i18n:translate="">Search Term</span>
 | 
			
		||||
        <input name="searchTerm"
 | 
			
		||||
               tal:attributes="value searchTerm" />
 | 
			
		||||
        <span i18n:translate="">Type</span>
 | 
			
		||||
        <select name="searchType">
 | 
			
		||||
          <option value="*">Any</option>
 | 
			
		||||
          <tal:types repeat="type view/targetTypes">
 | 
			
		||||
            <option value=".loops/concepts/topic"
 | 
			
		||||
                    i18n:translate=""
 | 
			
		||||
                    tal:attributes="value type/token;
 | 
			
		||||
                                    selected python: type.token == searchType"
 | 
			
		||||
                    tal:content="type/title">Topic</option>
 | 
			
		||||
          </tal:types>
 | 
			
		||||
        </select>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="formControls">
 | 
			
		||||
        <input class="context" type="submit" name="form.button.submit"
 | 
			
		||||
               value="Search"
 | 
			
		||||
               i18n:attributes="value" />
 | 
			
		||||
      </div>
 | 
			
		||||
  </form>
 | 
			
		||||
</metal:search>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,8 @@
 | 
			
		|||
    
 | 
			
		||||
    <require
 | 
			
		||||
        permission="zope.View"
 | 
			
		||||
        attributes="getLoopsUri loopsTraverse getConceptManager" />
 | 
			
		||||
        attributes="getLoopsUri loopsTraverse getConceptManager
 | 
			
		||||
                    getResourceManager getViewManager" />
 | 
			
		||||
 | 
			
		||||
  </content>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ $Id$
 | 
			
		|||
from zope.app import zapi
 | 
			
		||||
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 zope.security.proxy import removeSecurityProxy
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +37,8 @@ from loops.interfaces import IDocumentView, IMediaAssetView
 | 
			
		|||
from loops.interfaces import IView
 | 
			
		||||
from loops.interfaces import IConcept, IConceptView
 | 
			
		||||
 | 
			
		||||
_ = MessageFactory('loops')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# proxies for accessing target objects from views/nodes
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -141,3 +144,9 @@ class QueryableTargetSource(object):
 | 
			
		|||
    def __contains__(self, value):
 | 
			
		||||
        return value in self.resources.values() or value in self.concepts.values()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getTargetTypes():
 | 
			
		||||
    return (('loops.concept.Concept', _(u'Concept')),
 | 
			
		||||
            ('loops.resource.Document', _(u'Document')),
 | 
			
		||||
            ('loops.resource.MediaAsset', _(u'Media Asset')),
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue