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:
Helmut Merz 2011-05-26 10:26:50 +02:00
parent 5d63df8e70
commit e18392ac0f
6 changed files with 157 additions and 82 deletions

View file

@ -416,6 +416,13 @@ img.notselected {
font-weight: bold; font-weight: bold;
} }
.message {
font-weight: bold;
background-color: #c3d9ff;
padding: 4px;
margin-bottom: 4px;
}
/* comments */ /* comments */
div.comment { div.comment {
@ -440,10 +447,6 @@ div.comment {
padding: 2px; padding: 2px;
} }
.searchForm input.submit {
font-weight: bold;
}
/* blog */ /* blog */
.blog .description { .blog .description {

View file

@ -417,6 +417,13 @@ img.notselected {
font-weight: bold; font-weight: bold;
} }
.message {
font-weight: bold;
background-color: #c3d9ff;
padding: 4px;
margin-bottom: 4px;
}
/* lobo layout-specific classes */ /* lobo layout-specific classes */
.legend { .legend {
@ -447,10 +454,6 @@ div.comment {
padding: 2px; padding: 2px;
} }
.searchForm input.submit {
font-weight: bold;
}
/* blog */ /* blog */
.blog .description { .blog .description {

View file

@ -25,22 +25,33 @@
zope.publisher.interfaces.browser.IBrowserRequest" zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface" provides="zope.interface.Interface"
factory="loops.expert.browser.search.Search" factory="loops.expert.browser.search.Search"
permission="zope.View" permission="zope.View" />
/>
<browser:page <browser:page
name="listConceptsForComboBox.js" name="listConceptsForComboBox.js"
for="loops.interfaces.ILoopsObject" for="loops.interfaces.ILoopsObject"
class="loops.expert.browser.search.Search" class="loops.expert.browser.search.Search"
attribute="listConcepts" attribute="listConcepts"
permission="zope.View" permission="zope.View" />
/>
<browser:page <browser:page
name="searchresults.html" name="searchresults.html"
for="loops.interfaces.ILoopsObject" for="loops.interfaces.ILoopsObject"
class="loops.expert.browser.search.SearchResults" class="loops.expert.browser.search.SearchResults"
permission="zope.View" 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> </configure>

View file

@ -33,11 +33,15 @@
tal:content="item/title"> tal:content="item/title">
Search Search
</h1> </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" <form action="." method="post" id="1.search.form"
tal:attributes="id formId"> 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.submitted" value="yes" />
<input type="hidden" name="search.resultsId" value="1.results" <input type="hidden" name="search.resultsId" value="1.results"
tal:attributes="value resultsId" /> tal:attributes="value resultsId" />
@ -56,25 +60,22 @@
<td></td> <td></td>
<td colspan="3"><br /> <td colspan="3"><br />
<input type="submit" name="button.search" value="Search" class="submit" <input type="submit" name="button.search" value="Search" class="submit"
i18n:attributes="value" i18n:attributes="value" />
tal:attributes="xx_onclick python:
item.submitReplacing(resultsId, formId, view)" />
</td> </td>
</tr> </tr>
</table> </table>
</form>
</fieldset> </fieldset>
</div> </div>
<tal:results condition="request/search.submitted|nothing"> <tal:results condition="request/search.submitted|nothing">
<metal:results use-macro="item/search_macros/search_results" /> <metal:results use-macro="item/search_macros/search_results" />
</tal:results> </tal:results>
</form>
</div> </div>
</metal:search> </metal:search>
<div metal:define-macro="search_results" id="search.results" <div metal:define-macro="search_results" id="search.results"
tal:define="item nocall:item|nocall:view"> tal:define="item nocall:item|nocall:view">
<form method="post">
<input type="hidden" name="form.action" <input type="hidden" name="form.action"
tal:attributes="value item/form_action" /> tal:attributes="value item/form_action" />
<fieldset class="box"> <fieldset class="box">
@ -105,13 +106,16 @@
<tal:items tal:repeat="row item/results"> <tal:items tal:repeat="row item/results">
<tal:item define="class python: repeat['row'].odd() and 'even' or 'odd'; <tal:item define="class python: repeat['row'].odd() and 'even' or 'odd';
description row/description; description row/description;
targetUrl python:view.getUrlForTarget(row)"> targetUrl python:view.getUrlForTarget(row);
selected_uids request/selection|python:[]">
<tr tal:attributes="class class"> <tr tal:attributes="class class">
<td tal:condition="item/showActions|nothing" <td tal:condition="item/showActions|nothing"
tal:define="uid row/uniqueId" tal:define="uid row/uniqueId"
class="checkbox"> class="checkbox">
<input type="checkbox" name="selection:list" class="select_item" <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> <td>
<a tal:attributes="href string:$targetUrl?version=this; <a tal:attributes="href string:$targetUrl?version=this;
title description"> title description">
@ -149,13 +153,8 @@
</tbody> </tbody>
</table> </table>
</metal:results> </metal:results>
<div class="buttons"> <metal:actions use-macro="item/search_macros/actions" />
<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>
</fieldset> </fieldset>
</form>
</div> </div>
@ -395,4 +394,49 @@
param not in ['type', 'text', 'concept', 'state']" />&nbsp; param not in ['type', 'text', 'concept', 'state']" />&nbsp;
</td> </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> </html>

View file

@ -36,7 +36,7 @@ from loops.common import adapted, AdapterBase
from loops.expert.concept import ConceptQuery, FullQuery from loops.expert.concept import ConceptQuery, FullQuery
from loops.interfaces import IResource from loops.interfaces import IResource
from loops.organize.personal.browser.filter import FilterView 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 import util
from loops.util import _ from loops.util import _
@ -89,7 +89,8 @@ class Search(BaseView):
@Lazy @Lazy
def showActions(self): def showActions(self):
return canWriteObject(self.context) return checkPermission('loops.ManageSite', self.context)
#return canWriteObject(self.context)
@property @property
def rowNum(self): def rowNum(self):
@ -270,9 +271,8 @@ class Search(BaseView):
return True return True
#class SearchResults(BaseView):
class SearchResults(NodeView): class SearchResults(NodeView):
""" Provides results as inner HTML """ """ Provides results as inner HTML - not used any more (?)"""
@Lazy @Lazy
def search_macros(self): def search_macros(self):
@ -331,13 +331,39 @@ class ActionExecutor(FormController):
form = self.request.form form = self.request.form
actions = [k for k in form.keys() if k.startswith('action.')] actions = [k for k in form.keys() if k.startswith('action.')]
if actions: if actions:
action = actions[0].split('.', 1)[1]
uids = form.get('selection', []) uids = form.get('selection', [])
action = actions[0] if uids:
if action == 'action.delete': method = self.actions.get(action)
print '*** delete', uids if method:
method(self, uids)
return True return True
def delete(self, uids):
self.request.form['message'] = _(
u'The objects selected have been deleted.')
for uid in uids: for uid in uids:
obj = util.getObjectForUid(uid) obj = util.getObjectForUid(uid)
if not canWriteObject(obj):
continue
parent = getParent(obj) parent = getParent(obj)
del parent[getName(obj)] del parent[getName(obj)]
return True
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)

View file

@ -13,18 +13,6 @@
set_schema="loops.expert.concept.IQueryConcept" /> set_schema="loops.expert.concept.IQueryConcept" />
</class> </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" <adapter factory="loops.expert.setup.SetupManager"
name="expert" /> name="expert" />