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
|
||||
-------------
|
||||
|
||||
>>> from loops.browser.concept import ConceptView
|
||||
>>> from loops.browser.concept import ConceptView, ConceptConfigureView
|
||||
>>> view = ConceptView(cc1, TestRequest())
|
||||
|
||||
>>> children = list(view.children())
|
||||
|
@ -136,18 +136,19 @@ of URIs to item and the predicate of the relationship:
|
|||
>>> [c.token for c in children]
|
||||
['.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')
|
||||
>>> concepts['cc3'] = cc3
|
||||
>>> view = ConceptView(cc1,
|
||||
>>> view = ConceptConfigureView(cc1,
|
||||
... TestRequest(action='assign', tokens=['.loops/concepts/cc3']))
|
||||
>>> view.update()
|
||||
True
|
||||
>>> sorted(c.title for c in cc1.getChildren())
|
||||
[u'Zope 3', u'loops for Zope 3']
|
||||
|
||||
>>> view = ConceptView(cc1,
|
||||
>>> view = ConceptConfigureView(cc1,
|
||||
... TestRequest(action='remove', qualifier='children',
|
||||
... tokens=['.loops/concepts/cc2:.loops/concepts/standard']))
|
||||
>>> view.update()
|
||||
|
@ -159,14 +160,14 @@ We can also create a new concept and assign it:
|
|||
|
||||
>>> params = {'action': 'create', 'create.name': 'cc4',
|
||||
... 'create.title': u'New concept'}
|
||||
>>> view = ConceptView(cc1, TestRequest(**params))
|
||||
>>> view = ConceptConfigureView(cc1, TestRequest(**params))
|
||||
>>> view.update()
|
||||
True
|
||||
>>> sorted(c.title for c in cc1.getChildren())
|
||||
[u'New concept', u'loops for Zope 3']
|
||||
|
||||
The concept view provides methods for displaying concept types and
|
||||
predicates:
|
||||
The concept configuration view provides methods for displaying concept
|
||||
types and predicates:
|
||||
|
||||
>>> from zope.publisher.interfaces.browser import IBrowserRequest
|
||||
>>> 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)
|
||||
[<loops.resource.Document ...>]
|
||||
|
||||
The resource also provides access to the associated concepts (or views, see
|
||||
below) via the getClients() method:
|
||||
The concept configuration view discussed above also manages the relations
|
||||
from concepts to resources:
|
||||
|
||||
>>> conc = doc1.getClients()
|
||||
>>> len(conc)
|
||||
>>> len(cc1.getResources())
|
||||
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
|
||||
>>> 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
|
||||
------------------------
|
||||
|
||||
|
@ -393,6 +412,16 @@ out - this is usually done through ZCML.)
|
|||
>>> m111.target is cc2
|
||||
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
|
||||
----------
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ from zope.app.dublincore.interfaces import ICMFDublinCore
|
|||
from zope.app.event.objectevent import ObjectCreatedEvent
|
||||
from zope.app.form.browser.interfaces import ITerms
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.dottedname.resolve import resolve
|
||||
from zope.event import notify
|
||||
from zope.interface import implements
|
||||
from zope.publisher.interfaces import BadRequest
|
||||
|
@ -35,7 +36,10 @@ from zope.publisher.interfaces.browser import IBrowserRequest
|
|||
from zope import schema
|
||||
from zope.schema.interfaces import IIterableSource
|
||||
from zope.security.proxy import removeSecurityProxy
|
||||
from loops.interfaces import IConcept
|
||||
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 import util
|
||||
|
||||
|
@ -50,6 +54,13 @@ class ConceptView(BaseView):
|
|||
for r in self.context.getParentRelations():
|
||||
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):
|
||||
request = self.request
|
||||
action = request.get('action')
|
||||
|
@ -75,6 +86,8 @@ class ConceptView(BaseView):
|
|||
self.context.assignChild(removeSecurityProxy(concept), predicate)
|
||||
elif assignAs == 'parent':
|
||||
self.context.assignParent(removeSecurityProxy(concept), predicate)
|
||||
elif assignAs == 'resource':
|
||||
self.context.assignResource(removeSecurityProxy(concept), predicate)
|
||||
else:
|
||||
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
||||
elif action == 'remove':
|
||||
|
@ -84,6 +97,8 @@ class ConceptView(BaseView):
|
|||
self.context.deassignParent(concept, [predicate])
|
||||
elif qualifier == 'children':
|
||||
self.context.deassignChild(concept, [predicate])
|
||||
elif qualifier == 'resources':
|
||||
self.context.deassignResource(concept, [predicate])
|
||||
else:
|
||||
raise(BadRequest, 'Illegal qualifier: %s.' % qualifier)
|
||||
else:
|
||||
|
@ -97,8 +112,13 @@ class ConceptView(BaseView):
|
|||
raise(BadRequest, 'Empty name.')
|
||||
title = request.get('create.title', u'')
|
||||
conceptType = request.get('create.type')
|
||||
concept = Concept(title)
|
||||
container = self.loopsRoot.getConceptManager()
|
||||
if conceptType and conceptType.startswith('loops.resource.'):
|
||||
factory = resolve(conceptType)
|
||||
concept = factory(title)
|
||||
container = self.loopsRoot.getResourceManager()
|
||||
else:
|
||||
concept = Concept(title)
|
||||
container = self.loopsRoot.getConceptManager()
|
||||
container[name] = concept
|
||||
if conceptType:
|
||||
ctype = self.loopsRoot.loopsTraverse(conceptType)
|
||||
|
@ -113,6 +133,8 @@ class ConceptView(BaseView):
|
|||
self.context.assignChild(removeSecurityProxy(concept), predicate)
|
||||
elif assignAs == 'parent':
|
||||
self.context.assignParent(removeSecurityProxy(concept), predicate)
|
||||
elif assignAs == 'resource':
|
||||
self.context.assignResource(removeSecurityProxy(concept), predicate)
|
||||
else:
|
||||
raise(BadRequest, 'Illegal assignAs parameter: %s.' % assignAs)
|
||||
|
||||
|
@ -153,7 +175,10 @@ class ConceptView(BaseView):
|
|||
def viewIterator(self, objs):
|
||||
request = self.request
|
||||
for o in objs:
|
||||
yield ConceptView(o, request)
|
||||
if IConcept.providedBy(o):
|
||||
yield ConceptConfigureView(o, request)
|
||||
else:
|
||||
yield BaseView(o, request)
|
||||
|
||||
def conceptTypes(self):
|
||||
types = ConceptTypeSourceList(self.context)
|
||||
|
@ -171,6 +196,13 @@ class ConceptView(BaseView):
|
|||
def getConceptTypeTokenForSearch(self, 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):
|
||||
preds = PredicateSourceList(self.context)
|
||||
terms = zapi.getMultiAdapter((preds, self.request), ITerms)
|
||||
|
@ -188,7 +220,6 @@ class ConceptRelationView(object):
|
|||
self.context = relation.first
|
||||
self.other = relation.second
|
||||
self.predicate = relation.predicate
|
||||
self.conceptType = self.context.conceptType
|
||||
self.request = request
|
||||
|
||||
@Lazy
|
||||
|
@ -208,6 +239,10 @@ class ConceptRelationView(object):
|
|||
return ':'.join((self.loopsRoot.getLoopsUri(self.context),
|
||||
self.loopsRoot.getLoopsUri(self.predicate)))
|
||||
|
||||
@Lazy
|
||||
def conceptType(self):
|
||||
return self.context.conceptType
|
||||
|
||||
@Lazy
|
||||
def typeTitle(self):
|
||||
return self.conceptType.title
|
||||
|
@ -224,3 +259,23 @@ class ConceptRelationView(object):
|
|||
def predicateUrl(self):
|
||||
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
|
||||
for="loops.interfaces.IConcept"
|
||||
class=".concept.ConceptView"
|
||||
class=".concept.ConceptConfigureView"
|
||||
permission="zope.ManageContent">
|
||||
|
||||
<page
|
||||
|
@ -114,6 +114,12 @@
|
|||
menu="zmi_views" title="Related Concepts"
|
||||
/>
|
||||
|
||||
<page
|
||||
name="resources.html"
|
||||
template="concept_resources.pt"
|
||||
menu="zmi_views" title="Resources"
|
||||
/>
|
||||
|
||||
</pages>
|
||||
|
||||
<editform
|
||||
|
@ -163,6 +169,21 @@
|
|||
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 -->
|
||||
|
||||
<!--<zope:view
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<td>
|
||||
<a tal:condition="item/typeTitle | nothing"
|
||||
tal:content="item/typeTitle" href="#"
|
||||
tal:omit-tag="not:item/typeUrl"
|
||||
tal:attributes="href
|
||||
string:${item/typeUrl}/@@SelectedManagementView.html">
|
||||
Type
|
||||
|
@ -85,7 +86,7 @@
|
|||
<input name="create.title" size="30"
|
||||
tal:attributes="value nothing" />
|
||||
<span i18n:translate="">Type</span>
|
||||
<select name="create.type">
|
||||
<select name="create.type" metal:define-slot="types">
|
||||
<tal:types repeat="type view/conceptTypes">
|
||||
<option value=".loops/concepts/topic"
|
||||
i18n:translate=""
|
||||
|
@ -96,24 +97,26 @@
|
|||
</select>
|
||||
</div><br />
|
||||
<div class="formControls">
|
||||
<input class="context" type="submit" name="form.button.submit"
|
||||
value="Create Object"
|
||||
i18n:attributes="value"
|
||||
tal:attributes="value buttonText" />
|
||||
and assign as
|
||||
<select name="assignAs">
|
||||
<option value="child" selected i18n:translate="">Child</option>
|
||||
<option value="parent" i18n:translate="">Parent</option>
|
||||
</select>
|
||||
using Predicate
|
||||
<select name="create.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:control define-slot="control">
|
||||
<input class="context" type="submit" name="form.button.submit"
|
||||
value="Create Object"
|
||||
i18n:attributes="value"
|
||||
tal:attributes="value buttonText" />
|
||||
and assign as
|
||||
<select name="assignAs">
|
||||
<option value="child" selected i18n:translate="">Child</option>
|
||||
<option value="parent" i18n:translate="">Parent</option>
|
||||
</select>
|
||||
using Predicate
|
||||
<select name="create.predicate" metal:define-macro="predicates">
|
||||
<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:control>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
@ -131,7 +134,7 @@
|
|||
<input name="searchTerm"
|
||||
tal:attributes="value searchTerm" />
|
||||
<span i18n:translate="">Type</span>
|
||||
<select name="searchType">
|
||||
<select name="searchType" metal:define-slot="types">
|
||||
<option value="loops:concept:*"
|
||||
tal:attributes="selected python:
|
||||
searchType == 'loops:concept:*'">Any</option>
|
||||
|
|
|
@ -24,12 +24,15 @@ $Id$
|
|||
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.app import zapi
|
||||
from zope.app.catalog.interfaces import ICatalog
|
||||
from zope.app.dublincore.interfaces import ICMFDublinCore
|
||||
from zope.proxy import removeAllProxies
|
||||
from zope.security import canAccess, canWrite
|
||||
from zope.security.proxy import removeSecurityProxy
|
||||
|
||||
from loops.interfaces import IDocument, IMediaAsset
|
||||
from loops.browser.common import BaseView
|
||||
from loops.browser.concept import ConceptRelationView, ConceptConfigureView
|
||||
|
||||
renderingFactories = {
|
||||
'text/plain': 'zope.source.plaintext',
|
||||
|
@ -40,11 +43,69 @@ renderingFactories = {
|
|||
}
|
||||
|
||||
|
||||
class DocumentView(object):
|
||||
class ResourceView(BaseView):
|
||||
|
||||
def __init__(self, context, request):
|
||||
self.context = context
|
||||
self.request = request
|
||||
def concepts(self):
|
||||
for r in self.context.getConceptRelations():
|
||||
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):
|
||||
""" 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:
|
||||
predicate = self.getConceptManager().getDefaultPredicate()
|
||||
registry = zapi.getUtility(IRelationRegistry)
|
||||
registry.register(ResourceRelation(self, resource, predicate))
|
||||
registry.register(ResourceRelation(self, resource))
|
||||
# TODO (?): avoid duplicates
|
||||
|
||||
def deassignResource(self, resource, predicates=None):
|
||||
|
|
|
@ -205,10 +205,33 @@ class IResource(ILoopsObject, IPotentialTarget):
|
|||
"""
|
||||
|
||||
def getClients(relationships=None):
|
||||
""" Return a sequence of objects that are clients of the resource,
|
||||
i.e. that have some relation with it.
|
||||
""" Return a sequence of objects that the resource is the target of.
|
||||
"""
|
||||
|
||||
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):
|
||||
|
||||
|
|
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.file.image import Image as BaseMediaAsset
|
||||
from zope.component import adapts
|
||||
from zope.i18nmessageid import MessageFactory
|
||||
from zope.interface import implements
|
||||
from persistent import Persistent
|
||||
from cStringIO import StringIO
|
||||
|
@ -39,12 +40,18 @@ from interfaces import IMediaAsset, IMediaAssetSchema, IMediaAssetView
|
|||
from interfaces import IResourceManager, IResourceManagerContained
|
||||
from interfaces import ILoopsContained
|
||||
from interfaces import IIndexAttributes
|
||||
from concept import ResourceRelation
|
||||
from view import TargetRelation
|
||||
|
||||
_ = MessageFactory('loops')
|
||||
|
||||
|
||||
class Resource(Contained, Persistent):
|
||||
|
||||
implements(IResource, IResourceManagerContained, IRelatable)
|
||||
|
||||
_size = _width = _height = 0
|
||||
|
||||
_title = u''
|
||||
def getTitle(self): return self._title
|
||||
def setTitle(self, title): self._title = title
|
||||
|
@ -57,17 +64,34 @@ class Resource(Contained, Persistent):
|
|||
def getContentType(self): return self._contentType
|
||||
contentType = property(getContentType, setContentType)
|
||||
|
||||
def __init__(self, title=u''):
|
||||
self.title = title
|
||||
|
||||
def getLoopsRoot(self):
|
||||
return zapi.getParent(self).getLoopsRoot()
|
||||
|
||||
def getClients(self, relationships=None):
|
||||
if relationships is None:
|
||||
relationships = [TargetRelation]
|
||||
rels = getRelations(second=self, relationships=relationships)
|
||||
return [r.first for r in rels]
|
||||
|
||||
def __init__(self, title=u''):
|
||||
self.title = title
|
||||
# concept relations
|
||||
|
||||
_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):
|
||||
|
@ -138,3 +162,14 @@ class IndexAttributes(object):
|
|||
context = self.context
|
||||
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