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;
}
.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 {

View file

@ -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 {

View file

@ -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>

View file

@ -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']" />&nbsp;
</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>

View file

@ -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)

View file

@ -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" />