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
111
README.txt
111
README.txt
|
@ -390,113 +390,44 @@ 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 Configuration
|
A Node and its Target
|
||||||
------------------
|
---------------------
|
||||||
|
|
||||||
When configuring a node you may specify what you want to do with respect
|
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.
|
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.browser.node import ConfigureView
|
||||||
>>> from loops.view import NodeConfigAdapter
|
>>> form = {'action': 'create', 'create.title': 'New Resource',
|
||||||
>>> ztapi.provideAdapter(INode, INodeConfigSchema, NodeConfigAdapter)
|
... 'create.type': 'loops.resource.MediaAsset',}
|
||||||
>>> nodeConfig = INodeConfigSchema(m111)
|
>>> view = ConfigureView(m111, TestRequest(form = form))
|
||||||
|
>>> sorted((t.token, t.title) for t in view.targetTypes())
|
||||||
>>> nodeConfig.title = u'New title for m111'
|
[('loops.concept.Concept', u'Concept'),
|
||||||
>>> nodeConfig.title
|
('loops.resource.Document', u'Document'),
|
||||||
u'New title for m111'
|
('loops.resource.MediaAsset', u'Media Asset')]
|
||||||
>>> m111.title
|
>>> view.update()
|
||||||
u'New title for m111'
|
|
||||||
>>> nodeConfig.target = 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
|
|
||||||
'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())
|
>>> sorted(resources.keys())
|
||||||
[u'doc1']
|
[u'doc1', u'm1.m11.m111']
|
||||||
>>> 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,
|
>>> view.target.title, view.target.token
|
||||||
... 'field.targetType': 'loops.resource.Document'}
|
('New Resource', '.loops/resources/m1.m11.m111')
|
||||||
>>> 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
|
|
||||||
|
|
||||||
A node object provides the targetSchema of its target:
|
A node object provides the targetSchema of its target:
|
||||||
|
|
||||||
>>> from loops.interfaces import IDocumentView
|
>>> from loops.interfaces import IDocumentView
|
||||||
>>> from loops.interfaces import IMediaAssetView
|
>>> from loops.interfaces import IMediaAssetView
|
||||||
>>> IDocumentView.providedBy(m111)
|
>>> IDocumentView.providedBy(m111)
|
||||||
True
|
|
||||||
>>> IMediaAssetView.providedBy(m111)
|
|
||||||
False
|
False
|
||||||
|
>>> IMediaAssetView.providedBy(m111)
|
||||||
|
True
|
||||||
>>> m111.target = None
|
>>> m111.target = None
|
||||||
>>> IDocumentView.providedBy(m111)
|
>>> IDocumentView.providedBy(m111)
|
||||||
False
|
False
|
||||||
>>> m111.target = resources['ma07']
|
>>> m111.target = resources['doc1']
|
||||||
>>> IDocumentView.providedBy(m111)
|
>>> IDocumentView.providedBy(m111)
|
||||||
False
|
|
||||||
>>> IMediaAssetView.providedBy(m111)
|
|
||||||
True
|
True
|
||||||
|
>>> IMediaAssetView.providedBy(m111)
|
||||||
|
False
|
||||||
|
|
||||||
A node's target is rendered using the NodeView's renderTargetBody()
|
A node's target is rendered using the NodeView's renderTargetBody()
|
||||||
method. This makes use of a browser view registered for the target interface,
|
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 = zapi.getAdapter(m111, IDocumentView)
|
||||||
>>> proxy.title = u'Set via proxy'
|
>>> proxy.title = u'Set via proxy'
|
||||||
>>> resources['ma07'].title
|
>>> resources['doc1'].title
|
||||||
u'Set via proxy'
|
u'Set via proxy'
|
||||||
|
|
||||||
If the target object is removed from its container all references
|
If the target object is removed from its container all references
|
||||||
|
@ -549,7 +480,7 @@ cybertools.relation package.)
|
||||||
>>> ztapi.subscribe([Interface, IObjectRemovedEvent], None,
|
>>> ztapi.subscribe([Interface, IObjectRemovedEvent], None,
|
||||||
... invalidateRelations)
|
... invalidateRelations)
|
||||||
|
|
||||||
>>> del resources['ma07']
|
>>> del resources['doc1']
|
||||||
>>> m111.target
|
>>> m111.target
|
||||||
>>> IMediaAssetView.providedBy(m111)
|
>>> IMediaAssetView.providedBy(m111)
|
||||||
False
|
False
|
||||||
|
|
|
@ -29,6 +29,9 @@ from zope.cachedescriptors.property import Lazy
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from zope.security.proxy import removeSecurityProxy
|
from zope.security.proxy import removeSecurityProxy
|
||||||
|
|
||||||
|
from loops import util
|
||||||
|
from loops.target import getTargetTypes
|
||||||
|
|
||||||
class BaseView(object):
|
class BaseView(object):
|
||||||
|
|
||||||
def __init__(self, context, request):
|
def __init__(self, context, request):
|
||||||
|
@ -66,12 +69,20 @@ class BaseView(object):
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def typeTitle(self):
|
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
|
@Lazy
|
||||||
def typeUrl(self):
|
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):
|
class LoopsTerms(object):
|
||||||
|
|
|
@ -134,6 +134,14 @@ class ConceptView(BaseView):
|
||||||
result = [r for r in result if r.conceptType == type]
|
result = [r for r in result if r.conceptType == type]
|
||||||
return self.viewIterator(result)
|
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):
|
def viewIterator(self, objs):
|
||||||
request = self.request
|
request = self.request
|
||||||
for o in objs:
|
for o in objs:
|
||||||
|
|
|
@ -35,6 +35,13 @@
|
||||||
permission="zope.View"
|
permission="zope.View"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<page
|
||||||
|
for="*"
|
||||||
|
name="target_macros"
|
||||||
|
template="target_macros.pt"
|
||||||
|
permission="zope.View"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- loops top-level container -->
|
<!-- loops top-level container -->
|
||||||
|
|
||||||
<addform
|
<addform
|
||||||
|
@ -331,10 +338,10 @@
|
||||||
<editform
|
<editform
|
||||||
label="Configure Node"
|
label="Configure Node"
|
||||||
name="configure.html"
|
name="configure.html"
|
||||||
schema="loops.interfaces.INodeConfigSchema"
|
schema="loops.interfaces.INode"
|
||||||
fields="title description nodeType target createTarget targetUri targetType"
|
fields="title description nodeType target"
|
||||||
for="loops.interfaces.INode"
|
for="loops.interfaces.INode"
|
||||||
template="edit.pt"
|
template="node_target.pt"
|
||||||
class="loops.browser.node.ConfigureView"
|
class="loops.browser.node.ConfigureView"
|
||||||
permission="zope.ManageContent">
|
permission="zope.ManageContent">
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,8 @@
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div metal:define-macro="submit_button"
|
||||||
|
class="row">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="submit" name="UPDATE_SUBMIT" value="Change"
|
<input type="submit" name="UPDATE_SUBMIT" value="Change"
|
||||||
i18n:attributes="value submit-button;" />
|
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.cachedescriptors.property import Lazy
|
||||||
from zope.app import zapi
|
from zope.app import zapi
|
||||||
|
from zope.app.catalog.interfaces import ICatalog
|
||||||
from zope.app.container.browser.contents import JustContents
|
from zope.app.container.browser.contents import JustContents
|
||||||
from zope.app.dublincore.interfaces import ICMFDublinCore
|
from zope.app.dublincore.interfaces import ICMFDublinCore
|
||||||
|
from zope.app.event.objectevent import ObjectCreatedEvent
|
||||||
#import zope.configuration.name
|
#import zope.configuration.name
|
||||||
from zope.dottedname.resolve import resolve
|
from zope.dottedname.resolve import resolve
|
||||||
|
from zope.event import notify
|
||||||
from zope.proxy import removeAllProxies
|
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, IDocument, IMediaAsset
|
from loops.interfaces import IConcept, IDocument, IMediaAsset
|
||||||
from loops.resource import MediaAsset
|
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):
|
class NodeView(object):
|
||||||
|
|
||||||
|
@ -124,12 +132,13 @@ class NodeView(object):
|
||||||
return item.context == self.context
|
return item.context == self.context
|
||||||
|
|
||||||
|
|
||||||
class ConfigureBaseView(object):
|
class ConfigureView(BaseView):
|
||||||
""" Helper view class for editing/configuring a node, providing the
|
""" An editing view for configuring a node, optionally creating
|
||||||
stuff needed for creating a target object.
|
a target object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, context, request):
|
def __init__(self, context, request):
|
||||||
|
#self.context = context
|
||||||
self.context = removeSecurityProxy(context)
|
self.context = removeSecurityProxy(context)
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
|
@ -137,53 +146,84 @@ class ConfigureBaseView(object):
|
||||||
def loopsRoot(self):
|
def loopsRoot(self):
|
||||||
return self.context.getLoopsRoot()
|
return self.context.getLoopsRoot()
|
||||||
|
|
||||||
def checkCreateTarget(self):
|
@property
|
||||||
form = self.request.form
|
def target(self):
|
||||||
if form.get('field.createTarget', False):
|
target = self.context.target
|
||||||
root = self.loopsRoot
|
if target is not None:
|
||||||
type = self.request.form.get('field.targetType',
|
if IConcept.providedBy(target):
|
||||||
'loops.resource.MediaAsset')
|
return ConceptView(target, self.request)
|
||||||
factory = resolve(type)
|
return BaseView(target, self.request)
|
||||||
uri = self.request.form.get('field.targetUri', None)
|
return 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)
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
if self.update_status is not None:
|
request = self.request
|
||||||
return self.update_status
|
action = request.get('action')
|
||||||
self.delegate.checkCreateTarget()
|
if action is None or action == 'search':
|
||||||
return super(ConfigureView, self).update()
|
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
|
<require
|
||||||
permission="zope.View"
|
permission="zope.View"
|
||||||
attributes="getLoopsUri loopsTraverse getConceptManager" />
|
attributes="getLoopsUri loopsTraverse getConceptManager
|
||||||
|
getResourceManager getViewManager" />
|
||||||
|
|
||||||
</content>
|
</content>
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ $Id$
|
||||||
from zope.app import zapi
|
from zope.app import zapi
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope.component import adapts
|
from zope.component import adapts
|
||||||
|
from zope.i18nmessageid import MessageFactory
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from zope import schema
|
from zope import schema
|
||||||
from zope.security.proxy import removeSecurityProxy
|
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 IView
|
||||||
from loops.interfaces import IConcept, IConceptView
|
from loops.interfaces import IConcept, IConceptView
|
||||||
|
|
||||||
|
_ = MessageFactory('loops')
|
||||||
|
|
||||||
|
|
||||||
# proxies for accessing target objects from views/nodes
|
# proxies for accessing target objects from views/nodes
|
||||||
|
|
||||||
|
@ -141,3 +144,9 @@ class QueryableTargetSource(object):
|
||||||
def __contains__(self, value):
|
def __contains__(self, value):
|
||||||
return value in self.resources.values() or value in self.concepts.values()
|
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