Provide actions on selected objects of a search result.
- delete and change state actions - show/hide actions - display search results again after execution of action - show info message - permission checks
This commit is contained in:
parent
5d63df8e70
commit
e18392ac0f
6 changed files with 157 additions and 82 deletions
|
@ -416,6 +416,13 @@ img.notselected {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-weight: bold;
|
||||
background-color: #c3d9ff;
|
||||
padding: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* comments */
|
||||
|
||||
div.comment {
|
||||
|
@ -440,10 +447,6 @@ div.comment {
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
.searchForm input.submit {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* blog */
|
||||
|
||||
.blog .description {
|
||||
|
|
|
@ -417,6 +417,13 @@ img.notselected {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-weight: bold;
|
||||
background-color: #c3d9ff;
|
||||
padding: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* lobo layout-specific classes */
|
||||
|
||||
.legend {
|
||||
|
@ -447,10 +454,6 @@ div.comment {
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
.searchForm input.submit {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* blog */
|
||||
|
||||
.blog .description {
|
||||
|
|
|
@ -6,41 +6,52 @@
|
|||
i18n_domain="loops">
|
||||
|
||||
<zope:adapter
|
||||
name="action_query.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.expert.browser.base.BaseQueryView"
|
||||
permission="zope.View" />
|
||||
name="action_query.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.expert.browser.base.BaseQueryView"
|
||||
permission="zope.View" />
|
||||
|
||||
<browser:page
|
||||
name="search.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="loops.expert.browser.search.QuickSearchResults"
|
||||
permission="zope.View" />
|
||||
name="search.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="loops.expert.browser.search.QuickSearchResults"
|
||||
permission="zope.View" />
|
||||
|
||||
<zope:adapter
|
||||
name="search"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.expert.browser.search.Search"
|
||||
permission="zope.View"
|
||||
/>
|
||||
name="search"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.expert.browser.search.Search"
|
||||
permission="zope.View" />
|
||||
|
||||
<browser:page
|
||||
name="listConceptsForComboBox.js"
|
||||
for="loops.interfaces.ILoopsObject"
|
||||
class="loops.expert.browser.search.Search"
|
||||
attribute="listConcepts"
|
||||
permission="zope.View"
|
||||
/>
|
||||
name="listConceptsForComboBox.js"
|
||||
for="loops.interfaces.ILoopsObject"
|
||||
class="loops.expert.browser.search.Search"
|
||||
attribute="listConcepts"
|
||||
permission="zope.View" />
|
||||
|
||||
<browser:page
|
||||
name="searchresults.html"
|
||||
for="loops.interfaces.ILoopsObject"
|
||||
class="loops.expert.browser.search.SearchResults"
|
||||
permission="zope.View"
|
||||
/>
|
||||
name="searchresults.html"
|
||||
for="loops.interfaces.ILoopsObject"
|
||||
class="loops.expert.browser.search.SearchResults"
|
||||
permission="zope.View" />
|
||||
|
||||
<zope:adapter
|
||||
name="execute_query_action"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="loops.expert.browser.base.ActionExecutor"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<zope:adapter
|
||||
name="execute_search_action"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="loops.expert.browser.search.ActionExecutor"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
</configure>
|
||||
|
|
|
@ -33,11 +33,15 @@
|
|||
tal:content="item/title">
|
||||
Search
|
||||
</h1>
|
||||
<div class="message"
|
||||
tal:define="message request/message|nothing"
|
||||
tal:condition="message"
|
||||
tal:content="message" />
|
||||
|
||||
<div metal:define-macro="search_form" class="searchForm">
|
||||
<fieldset class="box">
|
||||
<form action="." method="post" id="1.search.form"
|
||||
tal:attributes="id formId">
|
||||
<form action="." method="post" id="1.search.form"
|
||||
tal:attributes="id formId">
|
||||
<div metal:define-macro="search_form" class="searchForm">
|
||||
<fieldset class="box">
|
||||
<input type="hidden" name="search.submitted" value="yes" />
|
||||
<input type="hidden" name="search.resultsId" value="1.results"
|
||||
tal:attributes="value resultsId" />
|
||||
|
@ -56,25 +60,22 @@
|
|||
<td></td>
|
||||
<td colspan="3"><br />
|
||||
<input type="submit" name="button.search" value="Search" class="submit"
|
||||
i18n:attributes="value"
|
||||
tal:attributes="xx_onclick python:
|
||||
item.submitReplacing(resultsId, formId, view)" />
|
||||
i18n:attributes="value" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
<tal:results condition="request/search.submitted|nothing">
|
||||
<metal:results use-macro="item/search_macros/search_results" />
|
||||
</tal:results>
|
||||
</fieldset>
|
||||
</div>
|
||||
<tal:results condition="request/search.submitted|nothing">
|
||||
<metal:results use-macro="item/search_macros/search_results" />
|
||||
</tal:results>
|
||||
</form>
|
||||
</div>
|
||||
</metal:search>
|
||||
|
||||
|
||||
<div metal:define-macro="search_results" id="search.results"
|
||||
tal:define="item nocall:item|nocall:view">
|
||||
<form method="post">
|
||||
<input type="hidden" name="form.action"
|
||||
tal:attributes="value item/form_action" />
|
||||
<fieldset class="box">
|
||||
|
@ -105,13 +106,16 @@
|
|||
<tal:items tal:repeat="row item/results">
|
||||
<tal:item define="class python: repeat['row'].odd() and 'even' or 'odd';
|
||||
description row/description;
|
||||
targetUrl python:view.getUrlForTarget(row)">
|
||||
targetUrl python:view.getUrlForTarget(row);
|
||||
selected_uids request/selection|python:[]">
|
||||
<tr tal:attributes="class class">
|
||||
<td tal:condition="item/showActions|nothing"
|
||||
tal:define="uid row/uniqueId"
|
||||
class="checkbox">
|
||||
<input type="checkbox" name="selection:list" class="select_item"
|
||||
tal:attributes="value uid;" /></td>
|
||||
tal:attributes="value uid;
|
||||
checked python:
|
||||
uid in selected_uids" /></td>
|
||||
<td>
|
||||
<a tal:attributes="href string:$targetUrl?version=this;
|
||||
title description">
|
||||
|
@ -149,13 +153,8 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</metal:results>
|
||||
<div class="buttons">
|
||||
<input type="submit" name="action.delete"
|
||||
value="Delete objects" class="submit"
|
||||
onClick="confirm('Do you really want to delete the selected objects?')"
|
||||
i18n:attributes="value; onclick" /></div>
|
||||
<metal:actions use-macro="item/search_macros/actions" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -395,4 +394,49 @@
|
|||
param not in ['type', 'text', 'concept', 'state']" />
|
||||
</td>
|
||||
|
||||
|
||||
<div metal:define-macro="actions"
|
||||
tal:condition="item/showActions">
|
||||
<div id="search_actions_plus"
|
||||
onclick="dojo.query('#search_actions').style('display', 'block');
|
||||
dojo.query('#search_actions_plus').style('display', 'none')" >
|
||||
<img src="/++resource++cybertools.icons/plus.gif" />Show Actions</div>
|
||||
<div id="search_actions" style="display: none">
|
||||
<h4 onclick="dojo.query('#search_actions_plus').style('display', 'block');
|
||||
dojo.query('#search_actions').style('display', 'none')">
|
||||
<img src="/++resource++cybertools.icons/minus.gif" />Actions for Selected Objects</h4>
|
||||
<div class="buttons">
|
||||
<div tal:define="stateDefs item/statesDefinitions;"
|
||||
tal:condition="stateDefs">
|
||||
<table>
|
||||
<tr tal:repeat="def stateDefs">
|
||||
<td>
|
||||
<input type="submit" name="action.change_state"
|
||||
value="Change state" class="submit"
|
||||
tal:condition="repeat/def/start"
|
||||
i18n:attributes="value" /></td>
|
||||
<td valign="top"
|
||||
tal:content="def/name"
|
||||
i18n:translate=""></td>
|
||||
<td tal:define="transitions def/transitions">
|
||||
<select tal:attributes="name string:trans.${def/name}">
|
||||
<option i18n:translate="" value="-">No change</option>
|
||||
<option tal:repeat="trans transitions"
|
||||
tal:attributes="value trans/name"
|
||||
tal:content="trans/title">publish</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table><br />
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" name="action.delete"
|
||||
value="Delete objects" class="submit"
|
||||
onClick="confirm('Do you really want to ... the selected objects?')"
|
||||
i18n:attributes="value; onclick" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</html>
|
|
@ -36,7 +36,7 @@ from loops.common import adapted, AdapterBase
|
|||
from loops.expert.concept import ConceptQuery, FullQuery
|
||||
from loops.interfaces import IResource
|
||||
from loops.organize.personal.browser.filter import FilterView
|
||||
from loops.security.common import canWriteObject
|
||||
from loops.security.common import canWriteObject, checkPermission
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
||||
|
@ -89,7 +89,8 @@ class Search(BaseView):
|
|||
|
||||
@Lazy
|
||||
def showActions(self):
|
||||
return canWriteObject(self.context)
|
||||
return checkPermission('loops.ManageSite', self.context)
|
||||
#return canWriteObject(self.context)
|
||||
|
||||
@property
|
||||
def rowNum(self):
|
||||
|
@ -270,9 +271,8 @@ class Search(BaseView):
|
|||
return True
|
||||
|
||||
|
||||
#class SearchResults(BaseView):
|
||||
class SearchResults(NodeView):
|
||||
""" Provides results as inner HTML """
|
||||
""" Provides results as inner HTML - not used any more (?)"""
|
||||
|
||||
@Lazy
|
||||
def search_macros(self):
|
||||
|
@ -331,13 +331,39 @@ class ActionExecutor(FormController):
|
|||
form = self.request.form
|
||||
actions = [k for k in form.keys() if k.startswith('action.')]
|
||||
if actions:
|
||||
action = actions[0].split('.', 1)[1]
|
||||
uids = form.get('selection', [])
|
||||
action = actions[0]
|
||||
if action == 'action.delete':
|
||||
print '*** delete', uids
|
||||
return True
|
||||
for uid in uids:
|
||||
obj = util.getObjectForUid(uid)
|
||||
parent = getParent(obj)
|
||||
del parent[getName(obj)]
|
||||
if uids:
|
||||
method = self.actions.get(action)
|
||||
if method:
|
||||
method(self, uids)
|
||||
return True
|
||||
|
||||
def delete(self, uids):
|
||||
self.request.form['message'] = _(
|
||||
u'The objects selected have been deleted.')
|
||||
for uid in uids:
|
||||
obj = util.getObjectForUid(uid)
|
||||
if not canWriteObject(obj):
|
||||
continue
|
||||
parent = getParent(obj)
|
||||
del parent[getName(obj)]
|
||||
|
||||
def change_state(self, uids):
|
||||
stdefs = dict([(k.split('.', 1)[1], v)
|
||||
for k, v in self.request.form.items()
|
||||
if k.startswith('trans.') and self.request.form[k] != '-'])
|
||||
if not stdefs:
|
||||
return
|
||||
for uid in uids:
|
||||
obj = util.getObjectForUid(uid)
|
||||
if not canWriteObject(obj):
|
||||
continue
|
||||
for stdef, trans in stdefs.items():
|
||||
stf = component.getAdapter(obj, IStateful, name=stdef)
|
||||
if trans in [t.name for t in stf.getAvailableTransitions()]:
|
||||
stf.doTransition(trans)
|
||||
self.request.form['message'] = _(
|
||||
u'The state of the objects selected has been changed.')
|
||||
|
||||
actions = dict(delete=delete, change_state=change_state)
|
||||
|
|
|
@ -13,18 +13,6 @@
|
|||
set_schema="loops.expert.concept.IQueryConcept" />
|
||||
</class>
|
||||
|
||||
<adapter name="execute_query_action"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="loops.expert.browser.base.ActionExecutor"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<adapter name="execute_search_action"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="loops.expert.browser.search.ActionExecutor"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<adapter factory="loops.expert.setup.SetupManager"
|
||||
name="expert" />
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue