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