Assignment of resources to concepts and vice versa
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1108 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
fc6946be95
commit
d6b7f5bc1f
10 changed files with 410 additions and 47 deletions
53
README.txt
53
README.txt
|
@ -121,7 +121,7 @@ a special concept type.
|
||||||
Concept Views
|
Concept Views
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
>>> from loops.browser.concept import ConceptView
|
>>> from loops.browser.concept import ConceptView, ConceptConfigureView
|
||||||
>>> view = ConceptView(cc1, TestRequest())
|
>>> view = ConceptView(cc1, TestRequest())
|
||||||
|
|
||||||
>>> children = list(view.children())
|
>>> children = list(view.children())
|
||||||
|
@ -136,18 +136,19 @@ of URIs to item and the predicate of the relationship:
|
||||||
>>> [c.token for c in children]
|
>>> [c.token for c in children]
|
||||||
['.loops/concepts/cc2:.loops/concepts/standard']
|
['.loops/concepts/cc2:.loops/concepts/standard']
|
||||||
|
|
||||||
The concept view allows updating the underlying context object:
|
There is also a concept configuration view that allows updating the
|
||||||
|
underlying context object:
|
||||||
|
|
||||||
>>> cc3 = Concept(u'loops for Zope 3')
|
>>> cc3 = Concept(u'loops for Zope 3')
|
||||||
>>> concepts['cc3'] = cc3
|
>>> concepts['cc3'] = cc3
|
||||||
>>> view = ConceptView(cc1,
|
>>> view = ConceptConfigureView(cc1,
|
||||||
... TestRequest(action='assign', tokens=['.loops/concepts/cc3']))
|
... TestRequest(action='assign', tokens=['.loops/concepts/cc3']))
|
||||||
>>> view.update()
|
>>> view.update()
|
||||||
True
|
True
|
||||||
>>> sorted(c.title for c in cc1.getChildren())
|
>>> sorted(c.title for c in cc1.getChildren())
|
||||||
[u'Zope 3', u'loops for Zope 3']
|
[u'Zope 3', u'loops for Zope 3']
|
||||||
|
|
||||||
>>> view = ConceptView(cc1,
|
>>> view = ConceptConfigureView(cc1,
|
||||||
... TestRequest(action='remove', qualifier='children',
|
... TestRequest(action='remove', qualifier='children',
|
||||||
... tokens=['.loops/concepts/cc2:.loops/concepts/standard']))
|
... tokens=['.loops/concepts/cc2:.loops/concepts/standard']))
|
||||||
>>> view.update()
|
>>> view.update()
|
||||||
|
@ -159,14 +160,14 @@ We can also create a new concept and assign it:
|
||||||
|
|
||||||
>>> params = {'action': 'create', 'create.name': 'cc4',
|
>>> params = {'action': 'create', 'create.name': 'cc4',
|
||||||
... 'create.title': u'New concept'}
|
... 'create.title': u'New concept'}
|
||||||
>>> view = ConceptView(cc1, TestRequest(**params))
|
>>> view = ConceptConfigureView(cc1, TestRequest(**params))
|
||||||
>>> view.update()
|
>>> view.update()
|
||||||
True
|
True
|
||||||
>>> sorted(c.title for c in cc1.getChildren())
|
>>> sorted(c.title for c in cc1.getChildren())
|
||||||
[u'New concept', u'loops for Zope 3']
|
[u'New concept', u'loops for Zope 3']
|
||||||
|
|
||||||
The concept view provides methods for displaying concept types and
|
The concept configuration view provides methods for displaying concept
|
||||||
predicates:
|
types and predicates:
|
||||||
|
|
||||||
>>> from zope.publisher.interfaces.browser import IBrowserRequest
|
>>> from zope.publisher.interfaces.browser import IBrowserRequest
|
||||||
>>> from loops.browser.common import LoopsTerms
|
>>> from loops.browser.common import LoopsTerms
|
||||||
|
@ -257,15 +258,33 @@ We can associate a resource with a concept by assigning it to the concept:
|
||||||
>>> list(res)
|
>>> list(res)
|
||||||
[<loops.resource.Document ...>]
|
[<loops.resource.Document ...>]
|
||||||
|
|
||||||
The resource also provides access to the associated concepts (or views, see
|
The concept configuration view discussed above also manages the relations
|
||||||
below) via the getClients() method:
|
from concepts to resources:
|
||||||
|
|
||||||
>>> conc = doc1.getClients()
|
>>> len(cc1.getResources())
|
||||||
>>> len(conc)
|
|
||||||
1
|
1
|
||||||
>>> conc[0] is cc1
|
>>> form = dict(action='remove', qualifier='resources',
|
||||||
|
... tokens=['.loops/resources/doc1:.loops/concepts/standard'])
|
||||||
|
>>> view = ConceptConfigureView(cc1, TestRequest(form=form))
|
||||||
|
>>> [zapi.getName(r.context) for r in view.resources()]
|
||||||
|
[u'doc1']
|
||||||
|
>>> view.update()
|
||||||
True
|
True
|
||||||
|
>>> len(cc1.getResources())
|
||||||
|
0
|
||||||
|
>>> form = dict(action='assign', assignAs='resource',
|
||||||
|
... tokens=['.loops/resources/doc1'])
|
||||||
|
>>> view = ConceptConfigureView(cc1, TestRequest(form=form))
|
||||||
|
>>> view.update()
|
||||||
|
True
|
||||||
|
>>> len(cc1.getResources())
|
||||||
|
1
|
||||||
|
|
||||||
|
These relations may also be managed starting from a resource using
|
||||||
|
the resource configuration view:
|
||||||
|
|
||||||
|
>>> from loops.browser.resource import ResourceConfigureView
|
||||||
|
|
||||||
Index attributes adapter
|
Index attributes adapter
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
@ -393,6 +412,16 @@ out - this is usually done through ZCML.)
|
||||||
>>> m111.target is cc2
|
>>> m111.target is cc2
|
||||||
True
|
True
|
||||||
|
|
||||||
|
A resource provides access to the associated views/nodes via the
|
||||||
|
getClients() method:
|
||||||
|
|
||||||
|
>>> len(doc1.getClients())
|
||||||
|
0
|
||||||
|
>>> m112.target = doc1
|
||||||
|
>>> nodes = doc1.getClients()
|
||||||
|
>>> nodes[0] is m112
|
||||||
|
True
|
||||||
|
|
||||||
Node Views
|
Node Views
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ from zope.app.dublincore.interfaces import ICMFDublinCore
|
||||||
from zope.app.event.objectevent import ObjectCreatedEvent
|
from zope.app.event.objectevent import ObjectCreatedEvent
|
||||||
from zope.app.form.browser.interfaces import ITerms
|
from zope.app.form.browser.interfaces import ITerms
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
from zope.dottedname.resolve import resolve
|
||||||
from zope.event import notify
|
from zope.event import notify
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from zope.publisher.interfaces import BadRequest
|
from zope.publisher.interfaces import BadRequest
|
||||||
|
@ -35,7 +36,10 @@ from zope.publisher.interfaces.browser import IBrowserRequest
|
||||||
from zope import schema
|
from zope import schema
|
||||||
from zope.schema.interfaces import IIterableSource
|
from zope.schema.interfaces import IIterableSource
|
||||||
from zope.security.proxy import removeSecurityProxy
|
from zope.security.proxy import removeSecurityProxy
|
||||||
|
from loops.interfaces import IConcept
|
||||||
from loops.concept import Concept, ConceptTypeSourceList, PredicateSourceList
|
from loops.concept import Concept, ConceptTypeSourceList, PredicateSourceList
|
||||||
|
from loops.resource import getResourceTypes, getResourceTypesForSearch
|
||||||
|
from loops.target import getTargetTypes
|
||||||
from loops.browser.common import BaseView, LoopsTerms
|
from loops.browser.common import BaseView, LoopsTerms
|
||||||
from loops import util
|
from loops import util
|
||||||
|
|
||||||
|
@ -50,6 +54,13 @@ class ConceptView(BaseView):
|
||||||
for r in self.context.getParentRelations():
|
for r in self.context.getParentRelations():
|
||||||
yield ConceptRelationView(r, self.request)
|
yield ConceptRelationView(r, self.request)
|
||||||
|
|
||||||
|
def resources(self):
|
||||||
|
for r in self.context.getResourceRelations():
|
||||||
|
yield ConceptResourceRelationView(r, self.request, contextIsSecond=True)
|
||||||
|
|
||||||
|
|
||||||
|
class ConceptConfigureView(ConceptView):
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
request = self.request
|
request = self.request
|
||||||
action = request.get('action')
|
action = request.get('action')
|
||||||
|
@ -75,6 +86,8 @@ class ConceptView(BaseView):
|
||||||
self.context.assignChild(removeSecurityProxy(concept), predicate)
|
self.context.assignChild(removeSecurityProxy(concept), predicate)
|
||||||
elif assignAs == 'parent':
|
elif assignAs == 'parent':
|
||||||
self.context.assignParent(removeSecurityProxy(concept), predicate)
|
self.context.assignParent(removeSecurityProxy(concept), predicate)
|
||||||
|
elif assignAs == 'resource':
|
||||||
|
self.context.assignResource(removeSecurityProxy(concept), predicate)
|
||||||
else:
|
else:
|
||||||
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
||||||
elif action == 'remove':
|
elif action == 'remove':
|
||||||
|
@ -84,6 +97,8 @@ class ConceptView(BaseView):
|
||||||
self.context.deassignParent(concept, [predicate])
|
self.context.deassignParent(concept, [predicate])
|
||||||
elif qualifier == 'children':
|
elif qualifier == 'children':
|
||||||
self.context.deassignChild(concept, [predicate])
|
self.context.deassignChild(concept, [predicate])
|
||||||
|
elif qualifier == 'resources':
|
||||||
|
self.context.deassignResource(concept, [predicate])
|
||||||
else:
|
else:
|
||||||
raise(BadRequest, 'Illegal qualifier: %s.' % qualifier)
|
raise(BadRequest, 'Illegal qualifier: %s.' % qualifier)
|
||||||
else:
|
else:
|
||||||
|
@ -97,8 +112,13 @@ class ConceptView(BaseView):
|
||||||
raise(BadRequest, 'Empty name.')
|
raise(BadRequest, 'Empty name.')
|
||||||
title = request.get('create.title', u'')
|
title = request.get('create.title', u'')
|
||||||
conceptType = request.get('create.type')
|
conceptType = request.get('create.type')
|
||||||
concept = Concept(title)
|
if conceptType and conceptType.startswith('loops.resource.'):
|
||||||
container = self.loopsRoot.getConceptManager()
|
factory = resolve(conceptType)
|
||||||
|
concept = factory(title)
|
||||||
|
container = self.loopsRoot.getResourceManager()
|
||||||
|
else:
|
||||||
|
concept = Concept(title)
|
||||||
|
container = self.loopsRoot.getConceptManager()
|
||||||
container[name] = concept
|
container[name] = concept
|
||||||
if conceptType:
|
if conceptType:
|
||||||
ctype = self.loopsRoot.loopsTraverse(conceptType)
|
ctype = self.loopsRoot.loopsTraverse(conceptType)
|
||||||
|
@ -113,6 +133,8 @@ class ConceptView(BaseView):
|
||||||
self.context.assignChild(removeSecurityProxy(concept), predicate)
|
self.context.assignChild(removeSecurityProxy(concept), predicate)
|
||||||
elif assignAs == 'parent':
|
elif assignAs == 'parent':
|
||||||
self.context.assignParent(removeSecurityProxy(concept), predicate)
|
self.context.assignParent(removeSecurityProxy(concept), predicate)
|
||||||
|
elif assignAs == 'resource':
|
||||||
|
self.context.assignResource(removeSecurityProxy(concept), predicate)
|
||||||
else:
|
else:
|
||||||
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
||||||
|
|
||||||
|
@ -153,7 +175,10 @@ class ConceptView(BaseView):
|
||||||
def viewIterator(self, objs):
|
def viewIterator(self, objs):
|
||||||
request = self.request
|
request = self.request
|
||||||
for o in objs:
|
for o in objs:
|
||||||
yield ConceptView(o, request)
|
if IConcept.providedBy(o):
|
||||||
|
yield ConceptConfigureView(o, request)
|
||||||
|
else:
|
||||||
|
yield BaseView(o, request)
|
||||||
|
|
||||||
def conceptTypes(self):
|
def conceptTypes(self):
|
||||||
types = ConceptTypeSourceList(self.context)
|
types = ConceptTypeSourceList(self.context)
|
||||||
|
@ -171,6 +196,13 @@ class ConceptView(BaseView):
|
||||||
def getConceptTypeTokenForSearch(self, ct):
|
def getConceptTypeTokenForSearch(self, ct):
|
||||||
return ct is None and 'unknown' or zapi.getName(ct)
|
return ct is None and 'unknown' or zapi.getName(ct)
|
||||||
|
|
||||||
|
def resourceTypes(self):
|
||||||
|
return util.KeywordVocabulary(getResourceTypes())
|
||||||
|
|
||||||
|
def resourceTypesForSearch(self):
|
||||||
|
return util.KeywordVocabulary(getResourceTypesForSearch())
|
||||||
|
|
||||||
|
|
||||||
def predicates(self):
|
def predicates(self):
|
||||||
preds = PredicateSourceList(self.context)
|
preds = PredicateSourceList(self.context)
|
||||||
terms = zapi.getMultiAdapter((preds, self.request), ITerms)
|
terms = zapi.getMultiAdapter((preds, self.request), ITerms)
|
||||||
|
@ -188,7 +220,6 @@ class ConceptRelationView(object):
|
||||||
self.context = relation.first
|
self.context = relation.first
|
||||||
self.other = relation.second
|
self.other = relation.second
|
||||||
self.predicate = relation.predicate
|
self.predicate = relation.predicate
|
||||||
self.conceptType = self.context.conceptType
|
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -208,6 +239,10 @@ class ConceptRelationView(object):
|
||||||
return ':'.join((self.loopsRoot.getLoopsUri(self.context),
|
return ':'.join((self.loopsRoot.getLoopsUri(self.context),
|
||||||
self.loopsRoot.getLoopsUri(self.predicate)))
|
self.loopsRoot.getLoopsUri(self.predicate)))
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def conceptType(self):
|
||||||
|
return self.context.conceptType
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def typeTitle(self):
|
def typeTitle(self):
|
||||||
return self.conceptType.title
|
return self.conceptType.title
|
||||||
|
@ -224,3 +259,23 @@ class ConceptRelationView(object):
|
||||||
def predicateUrl(self):
|
def predicateUrl(self):
|
||||||
return zapi.absoluteURL(self.predicate, self.request)
|
return zapi.absoluteURL(self.predicate, self.request)
|
||||||
|
|
||||||
|
|
||||||
|
class ConceptResourceRelationView(ConceptRelationView):
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def conceptType(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def typeTitle(self):
|
||||||
|
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 ''
|
||||||
|
|
||||||
|
|
87
browser/concept_resources.pt
Normal file
87
browser/concept_resources.pt
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<tal:tag condition="view/update" />
|
||||||
|
<html metal:use-macro="context/@@standard_macros/view"
|
||||||
|
i18n:domain="loops">
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div metal:fill-slot="body">
|
||||||
|
|
||||||
|
<h1 tal:content="context/title">Concept Title</h1><br />
|
||||||
|
|
||||||
|
<div tal:define="items view/resources;
|
||||||
|
action string:remove;
|
||||||
|
qualifier string:parents;
|
||||||
|
summary string:Currently assigned resources;
|
||||||
|
legend string:Resources;
|
||||||
|
showPredicate string:yes;
|
||||||
|
buttonText string:Remove Resources;"
|
||||||
|
style="float:left; padding-right:20px">
|
||||||
|
<metal:parents use-macro="views/relation_macros/listing" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div tal:define="legend string:Create Resource;
|
||||||
|
buttonText string:Create Resource"
|
||||||
|
style="padding-right:20px; clear:left">
|
||||||
|
<metal:create use-macro="views/relation_macros/create">
|
||||||
|
<select name="create.type" metal:fill-slot="types">
|
||||||
|
<tal:types repeat="type view/resourceTypes">
|
||||||
|
<option value="loops.resource.Document"
|
||||||
|
i18n:translate=""
|
||||||
|
tal:attributes="value type/token"
|
||||||
|
tal:content="type/title">Document</option>
|
||||||
|
</tal:types>
|
||||||
|
</select>
|
||||||
|
<metal:control fill-slot="control">
|
||||||
|
<input class="context" type="submit" name="form.button.submit"
|
||||||
|
value="Create Object"
|
||||||
|
i18n:attributes="value"
|
||||||
|
tal:attributes="value buttonText" />
|
||||||
|
<input type="hidden" name="assignAs" value="resource" />
|
||||||
|
and assign using Predicate
|
||||||
|
<select metal:use-macro="views/relation_macros/predicates" />
|
||||||
|
</metal:control>
|
||||||
|
</metal:create>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div tal:define="items view/search;
|
||||||
|
action string:assign;
|
||||||
|
qualifier nothing;
|
||||||
|
summary string:Assignment candidates;
|
||||||
|
legend string:Search;
|
||||||
|
showPredicate nothing;
|
||||||
|
buttonText string:Assign;"
|
||||||
|
style="padding-right:20px">
|
||||||
|
<metal:assign use-macro="views/relation_macros/listing">
|
||||||
|
<metal:search fill-slot="topActions">
|
||||||
|
<metal:block use-macro="views/relation_macros/search">
|
||||||
|
<select name="searchType" metal:fill-slot="types">
|
||||||
|
<option value="loops:resource:*"
|
||||||
|
tal:attributes="selected python:
|
||||||
|
searchType == 'loops:resource:*'">Any</option>
|
||||||
|
<tal:types repeat="type view/resourceTypesForSearch">
|
||||||
|
<option value="loops:resource:Document"
|
||||||
|
i18n:translate=""
|
||||||
|
tal:attributes="value type/token;
|
||||||
|
selected python: type.token == searchType"
|
||||||
|
tal:content="type/title">Topic</option>
|
||||||
|
</tal:types>
|
||||||
|
</select>
|
||||||
|
</metal:block>
|
||||||
|
</metal:search>
|
||||||
|
<metal:special fill-slot="specialButtons">
|
||||||
|
<input type="hidden" name="assignAs" value="resource" />
|
||||||
|
Object(s) using Predicate
|
||||||
|
<select name="predicate">
|
||||||
|
<tal:types repeat="pred view/predicates">
|
||||||
|
<option value=".loops/concepts/hasType"
|
||||||
|
i18n:translate=""
|
||||||
|
tal:attributes="value pred/token"
|
||||||
|
tal:content="pred/title">Predicate</option>
|
||||||
|
</tal:types>
|
||||||
|
</select>
|
||||||
|
</metal:special>
|
||||||
|
</metal:assign>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -105,7 +105,7 @@
|
||||||
|
|
||||||
<pages
|
<pages
|
||||||
for="loops.interfaces.IConcept"
|
for="loops.interfaces.IConcept"
|
||||||
class=".concept.ConceptView"
|
class=".concept.ConceptConfigureView"
|
||||||
permission="zope.ManageContent">
|
permission="zope.ManageContent">
|
||||||
|
|
||||||
<page
|
<page
|
||||||
|
@ -114,6 +114,12 @@
|
||||||
menu="zmi_views" title="Related Concepts"
|
menu="zmi_views" title="Related Concepts"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<page
|
||||||
|
name="resources.html"
|
||||||
|
template="concept_resources.pt"
|
||||||
|
menu="zmi_views" title="Resources"
|
||||||
|
/>
|
||||||
|
|
||||||
</pages>
|
</pages>
|
||||||
|
|
||||||
<editform
|
<editform
|
||||||
|
@ -163,6 +169,21 @@
|
||||||
add="zope.ManageContent"
|
add="zope.ManageContent"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- resource in general -->
|
||||||
|
|
||||||
|
<pages
|
||||||
|
for="loops.interfaces.IResource"
|
||||||
|
class=".resource.ResourceConfigureView"
|
||||||
|
permission="zope.ManageContent">
|
||||||
|
|
||||||
|
<page
|
||||||
|
name="concepts.html"
|
||||||
|
template="resource_concepts.pt"
|
||||||
|
menu="zmi_views" title="Concepts"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</pages>
|
||||||
|
|
||||||
<!-- document -->
|
<!-- document -->
|
||||||
|
|
||||||
<!--<zope:view
|
<!--<zope:view
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<td>
|
<td>
|
||||||
<a tal:condition="item/typeTitle | nothing"
|
<a tal:condition="item/typeTitle | nothing"
|
||||||
tal:content="item/typeTitle" href="#"
|
tal:content="item/typeTitle" href="#"
|
||||||
|
tal:omit-tag="not:item/typeUrl"
|
||||||
tal:attributes="href
|
tal:attributes="href
|
||||||
string:${item/typeUrl}/@@SelectedManagementView.html">
|
string:${item/typeUrl}/@@SelectedManagementView.html">
|
||||||
Type
|
Type
|
||||||
|
@ -85,7 +86,7 @@
|
||||||
<input name="create.title" size="30"
|
<input name="create.title" size="30"
|
||||||
tal:attributes="value nothing" />
|
tal:attributes="value nothing" />
|
||||||
<span i18n:translate="">Type</span>
|
<span i18n:translate="">Type</span>
|
||||||
<select name="create.type">
|
<select name="create.type" metal:define-slot="types">
|
||||||
<tal:types repeat="type view/conceptTypes">
|
<tal:types repeat="type view/conceptTypes">
|
||||||
<option value=".loops/concepts/topic"
|
<option value=".loops/concepts/topic"
|
||||||
i18n:translate=""
|
i18n:translate=""
|
||||||
|
@ -96,24 +97,26 @@
|
||||||
</select>
|
</select>
|
||||||
</div><br />
|
</div><br />
|
||||||
<div class="formControls">
|
<div class="formControls">
|
||||||
<input class="context" type="submit" name="form.button.submit"
|
<metal:control define-slot="control">
|
||||||
value="Create Object"
|
<input class="context" type="submit" name="form.button.submit"
|
||||||
i18n:attributes="value"
|
value="Create Object"
|
||||||
tal:attributes="value buttonText" />
|
i18n:attributes="value"
|
||||||
and assign as
|
tal:attributes="value buttonText" />
|
||||||
<select name="assignAs">
|
and assign as
|
||||||
<option value="child" selected i18n:translate="">Child</option>
|
<select name="assignAs">
|
||||||
<option value="parent" i18n:translate="">Parent</option>
|
<option value="child" selected i18n:translate="">Child</option>
|
||||||
</select>
|
<option value="parent" i18n:translate="">Parent</option>
|
||||||
using Predicate
|
</select>
|
||||||
<select name="create.predicate">
|
using Predicate
|
||||||
<tal:types repeat="pred view/predicates">
|
<select name="create.predicate" metal:define-macro="predicates">
|
||||||
<option value=".loops/concepts/hasType"
|
<tal:types repeat="pred view/predicates">
|
||||||
i18n:translate=""
|
<option value=".loops/concepts/hasType"
|
||||||
tal:attributes="value pred/token"
|
i18n:translate=""
|
||||||
tal:content="pred/title">Predicate</option>
|
tal:attributes="value pred/token"
|
||||||
</tal:types>
|
tal:content="pred/title">Predicate</option>
|
||||||
</select>
|
</tal:types>
|
||||||
|
</select>
|
||||||
|
</metal:control>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -131,7 +134,7 @@
|
||||||
<input name="searchTerm"
|
<input name="searchTerm"
|
||||||
tal:attributes="value searchTerm" />
|
tal:attributes="value searchTerm" />
|
||||||
<span i18n:translate="">Type</span>
|
<span i18n:translate="">Type</span>
|
||||||
<select name="searchType">
|
<select name="searchType" metal:define-slot="types">
|
||||||
<option value="loops:concept:*"
|
<option value="loops:concept:*"
|
||||||
tal:attributes="selected python:
|
tal:attributes="selected python:
|
||||||
searchType == 'loops:concept:*'">Any</option>
|
searchType == 'loops:concept:*'">Any</option>
|
||||||
|
|
|
@ -24,12 +24,15 @@ $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.dublincore.interfaces import ICMFDublinCore
|
from zope.app.dublincore.interfaces import ICMFDublinCore
|
||||||
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 IDocument, IMediaAsset
|
from loops.interfaces import IDocument, IMediaAsset
|
||||||
|
from loops.browser.common import BaseView
|
||||||
|
from loops.browser.concept import ConceptRelationView, ConceptConfigureView
|
||||||
|
|
||||||
renderingFactories = {
|
renderingFactories = {
|
||||||
'text/plain': 'zope.source.plaintext',
|
'text/plain': 'zope.source.plaintext',
|
||||||
|
@ -40,11 +43,69 @@ renderingFactories = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DocumentView(object):
|
class ResourceView(BaseView):
|
||||||
|
|
||||||
def __init__(self, context, request):
|
def concepts(self):
|
||||||
self.context = context
|
for r in self.context.getConceptRelations():
|
||||||
self.request = request
|
yield ConceptRelationView(r, self.request)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceConfigureView(ResourceView, ConceptConfigureView):
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
request = self.request
|
||||||
|
action = request.get('action')
|
||||||
|
if action is None:
|
||||||
|
return True
|
||||||
|
if action == 'create':
|
||||||
|
self.createAndAssign()
|
||||||
|
return True
|
||||||
|
tokens = request.get('tokens', [])
|
||||||
|
for token in tokens:
|
||||||
|
parts = token.split(':')
|
||||||
|
token = parts[0]
|
||||||
|
if len(parts) > 1:
|
||||||
|
relToken = parts[1]
|
||||||
|
concept = self.loopsRoot.loopsTraverse(token)
|
||||||
|
if action == 'assign':
|
||||||
|
predicate = request.get('predicate') or None
|
||||||
|
if predicate:
|
||||||
|
predicate = removeSecurityProxy(
|
||||||
|
self.loopsRoot.loopsTraverse(predicate))
|
||||||
|
self.context.assignConcept(removeSecurityProxy(concept), predicate)
|
||||||
|
elif action == 'remove':
|
||||||
|
predicate = self.loopsRoot.loopsTraverse(relToken)
|
||||||
|
self.context.deassignConcept(concept, [predicate])
|
||||||
|
return True
|
||||||
|
|
||||||
|
def search(self):
|
||||||
|
request = self.request
|
||||||
|
if request.get('action') != 'search':
|
||||||
|
return []
|
||||||
|
searchTerm = request.get('searchTerm', None)
|
||||||
|
searchType = request.get('searchType', None)
|
||||||
|
result = []
|
||||||
|
if searchTerm or searchType != 'none':
|
||||||
|
criteria = {}
|
||||||
|
if searchTerm:
|
||||||
|
criteria['loops_title'] = searchTerm
|
||||||
|
if searchType:
|
||||||
|
if searchType.endswith('*'):
|
||||||
|
start = searchType[:-1]
|
||||||
|
end = start + '\x7f'
|
||||||
|
else:
|
||||||
|
start = end = searchType
|
||||||
|
criteria['loops_type'] = (start, end)
|
||||||
|
cat = zapi.getUtility(ICatalog)
|
||||||
|
result = cat.searchResults(**criteria)
|
||||||
|
else:
|
||||||
|
result = self.loopsRoot.getConceptManager().values()
|
||||||
|
if searchType == 'none':
|
||||||
|
result = [r for r in result if r.conceptType is None]
|
||||||
|
return self.viewIterator(result)
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentView(ResourceView):
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" Return the rendered content (data) of the context object.
|
""" Return the rendered content (data) of the context object.
|
||||||
|
|
49
browser/resource_concepts.pt
Normal file
49
browser/resource_concepts.pt
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<tal:tag condition="view/update" />
|
||||||
|
<html metal:use-macro="context/@@standard_macros/view"
|
||||||
|
i18n:domain="loops">
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div metal:fill-slot="body">
|
||||||
|
|
||||||
|
<h1 tal:content="context/title">Concept Title</h1><br />
|
||||||
|
|
||||||
|
<div tal:define="items view/concepts;
|
||||||
|
action string:remove;
|
||||||
|
qualifier string:parents;
|
||||||
|
summary string:Currently assigned objects;
|
||||||
|
legend string:Parent Concepts;
|
||||||
|
showPredicate string:yes;
|
||||||
|
buttonText string:Remove Parents;"
|
||||||
|
style="padding-right:20px">
|
||||||
|
<metal:parents use-macro="views/relation_macros/listing" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div tal:define="items view/search;
|
||||||
|
action string:assign;
|
||||||
|
qualifier nothing;
|
||||||
|
summary string:Assignment candidates;
|
||||||
|
legend string:Search;
|
||||||
|
showPredicate nothing;
|
||||||
|
buttonText string:Assign;"
|
||||||
|
style="padding-right:20px">
|
||||||
|
<metal:assign use-macro="views/relation_macros/listing">
|
||||||
|
<metal:search fill-slot="topActions">
|
||||||
|
<metal:block use-macro="views/relation_macros/search" />
|
||||||
|
</metal:search>
|
||||||
|
<metal:special fill-slot="specialButtons">
|
||||||
|
Object(s) using Predicate
|
||||||
|
<select name="predicate">
|
||||||
|
<tal:types repeat="pred view/predicates">
|
||||||
|
<option value=".loops/concepts/hasType"
|
||||||
|
i18n:translate=""
|
||||||
|
tal:attributes="value pred/token"
|
||||||
|
tal:content="pred/title">Predicate</option>
|
||||||
|
</tal:types>
|
||||||
|
</select>
|
||||||
|
</metal:special>
|
||||||
|
</metal:assign>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -173,7 +173,7 @@ class Concept(Contained, Persistent):
|
||||||
if predicate is None:
|
if predicate is None:
|
||||||
predicate = self.getConceptManager().getDefaultPredicate()
|
predicate = self.getConceptManager().getDefaultPredicate()
|
||||||
registry = zapi.getUtility(IRelationRegistry)
|
registry = zapi.getUtility(IRelationRegistry)
|
||||||
registry.register(ResourceRelation(self, resource, predicate))
|
registry.register(ResourceRelation(self, resource))
|
||||||
# TODO (?): avoid duplicates
|
# TODO (?): avoid duplicates
|
||||||
|
|
||||||
def deassignResource(self, resource, predicates=None):
|
def deassignResource(self, resource, predicates=None):
|
||||||
|
|
|
@ -205,10 +205,33 @@ class IResource(ILoopsObject, IPotentialTarget):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def getClients(relationships=None):
|
def getClients(relationships=None):
|
||||||
""" Return a sequence of objects that are clients of the resource,
|
""" Return a sequence of objects that the resource is the target of.
|
||||||
i.e. that have some relation with it.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def getConcepts(predicates=None):
|
||||||
|
""" Return a tuple of concepts related to self as parent concepts,
|
||||||
|
optionally restricted to the predicates given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def getConceptRelations(predicates=None, concepts=None):
|
||||||
|
""" Return a sequence of relations to concepts assigned to self
|
||||||
|
as parent concepts, optionally restricted to the predicates given
|
||||||
|
or to a certain concept.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def assignConcept(concept, predicate):
|
||||||
|
""" Assign an existing concept to self using the predicate given.
|
||||||
|
The assigned concept will be a parent concept of self.
|
||||||
|
|
||||||
|
The predicate defaults to the concept manager's default predicate.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def deassignConcept(concept, predicates=None):
|
||||||
|
""" Remove the concept relations to the concept given from self,
|
||||||
|
optionally restricting them to the predicates given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IDocumentSchema(IResourceSchema):
|
class IDocumentSchema(IResourceSchema):
|
||||||
|
|
||||||
|
|
41
resource.py
41
resource.py
|
@ -27,6 +27,7 @@ from zope.app.container.btree import BTreeContainer
|
||||||
from zope.app.container.contained import Contained
|
from zope.app.container.contained import Contained
|
||||||
from zope.app.file.image import Image as BaseMediaAsset
|
from zope.app.file.image import Image as BaseMediaAsset
|
||||||
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 persistent import Persistent
|
from persistent import Persistent
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
@ -39,12 +40,18 @@ from interfaces import IMediaAsset, IMediaAssetSchema, IMediaAssetView
|
||||||
from interfaces import IResourceManager, IResourceManagerContained
|
from interfaces import IResourceManager, IResourceManagerContained
|
||||||
from interfaces import ILoopsContained
|
from interfaces import ILoopsContained
|
||||||
from interfaces import IIndexAttributes
|
from interfaces import IIndexAttributes
|
||||||
|
from concept import ResourceRelation
|
||||||
|
from view import TargetRelation
|
||||||
|
|
||||||
|
_ = MessageFactory('loops')
|
||||||
|
|
||||||
|
|
||||||
class Resource(Contained, Persistent):
|
class Resource(Contained, Persistent):
|
||||||
|
|
||||||
implements(IResource, IResourceManagerContained, IRelatable)
|
implements(IResource, IResourceManagerContained, IRelatable)
|
||||||
|
|
||||||
|
_size = _width = _height = 0
|
||||||
|
|
||||||
_title = u''
|
_title = u''
|
||||||
def getTitle(self): return self._title
|
def getTitle(self): return self._title
|
||||||
def setTitle(self, title): self._title = title
|
def setTitle(self, title): self._title = title
|
||||||
|
@ -57,17 +64,34 @@ class Resource(Contained, Persistent):
|
||||||
def getContentType(self): return self._contentType
|
def getContentType(self): return self._contentType
|
||||||
contentType = property(getContentType, setContentType)
|
contentType = property(getContentType, setContentType)
|
||||||
|
|
||||||
|
def __init__(self, title=u''):
|
||||||
|
self.title = title
|
||||||
|
|
||||||
def getLoopsRoot(self):
|
def getLoopsRoot(self):
|
||||||
return zapi.getParent(self).getLoopsRoot()
|
return zapi.getParent(self).getLoopsRoot()
|
||||||
|
|
||||||
def getClients(self, relationships=None):
|
def getClients(self, relationships=None):
|
||||||
|
if relationships is None:
|
||||||
|
relationships = [TargetRelation]
|
||||||
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]
|
||||||
|
|
||||||
def __init__(self, title=u''):
|
# concept relations
|
||||||
self.title = title
|
|
||||||
|
|
||||||
_size = _width = _height = 0
|
def getConceptRelations (self, predicates=None, concept=None):
|
||||||
|
predicates = predicates is None and ['*'] or predicates
|
||||||
|
relationships = [ResourceRelation(None, self, p) for p in predicates]
|
||||||
|
# TODO: sort...
|
||||||
|
return getRelations(first=concept, second=self, relationships=relationships)
|
||||||
|
|
||||||
|
def getConcepts(self, predicates=None):
|
||||||
|
return [r.first for r in self.getConceptRelations(predicates)]
|
||||||
|
|
||||||
|
def assignConcept(self, concept, predicate=None):
|
||||||
|
concept.assignResource(self, predicate)
|
||||||
|
|
||||||
|
def deassignConcept(self, concept, predicates=None):
|
||||||
|
concept.deassignResource(self, predicates)
|
||||||
|
|
||||||
|
|
||||||
class Document(Resource):
|
class Document(Resource):
|
||||||
|
@ -138,3 +162,14 @@ class IndexAttributes(object):
|
||||||
context = self.context
|
context = self.context
|
||||||
return ':'.join(('loops:resource', context.__class__.__name__))
|
return ':'.join(('loops:resource', context.__class__.__name__))
|
||||||
|
|
||||||
|
|
||||||
|
def getResourceTypes():
|
||||||
|
return (('loops.resource.Document', _(u'Document')),
|
||||||
|
('loops.resource.MediaAsset', _(u'Media Asset')),
|
||||||
|
)
|
||||||
|
|
||||||
|
def getResourceTypesForSearch():
|
||||||
|
return (('loops:resource:Document', _(u'Document')),
|
||||||
|
('loops:resource:MediaAsset', _(u'Media Asset')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue