Merge branch 'search_actions'
This commit is contained in:
		
						commit
						b0c54ddd1b
					
				
					 6 changed files with 186 additions and 58 deletions
				
			
		|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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,23 +60,24 @@ | ||||||
|               <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"> | ||||||
|  |     <input type="hidden" name="form.action" | ||||||
|  |            tal:attributes="value item/form_action" /> | ||||||
|     <fieldset class="box"> |     <fieldset class="box"> | ||||||
|       <h2 i18n:translate="">Search results</h2> |       <h2 i18n:translate="">Search results</h2> | ||||||
|       <metal:results define-macro="results"> |       <metal:results define-macro="results"> | ||||||
|  | @ -80,6 +85,12 @@ | ||||||
|                i18n:attributes="summary"> |                i18n:attributes="summary"> | ||||||
|           <thead> |           <thead> | ||||||
|             <tr> |             <tr> | ||||||
|  |               <th tal:condition="item/showActions" | ||||||
|  |                   class="checkbox"> | ||||||
|  |                 <input type="checkbox" | ||||||
|  |                        onchange="checked = this.checked; | ||||||
|  |                            dojo.query('.select_item').forEach(function(n) { | ||||||
|  |                                 n.checked = checked;});" /></th> | ||||||
|               <th i18n:translate="">Title</th> |               <th i18n:translate="">Title</th> | ||||||
|               <th i18n:translate="">Type</th> |               <th i18n:translate="">Type</th> | ||||||
|               <th i18n:translate="" |               <th i18n:translate="" | ||||||
|  | @ -95,8 +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" | ||||||
|  |                       tal:define="uid row/uniqueId" | ||||||
|  |                       class="checkbox"> | ||||||
|  |                   <input type="checkbox" name="selection:list" class="select_item" | ||||||
|  |                          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"> | ||||||
|  | @ -134,6 +153,7 @@ | ||||||
|           </tbody> |           </tbody> | ||||||
|         </table> |         </table> | ||||||
|       </metal:results> |       </metal:results> | ||||||
|  |       <metal:actions use-macro="item/search_macros/actions" /> | ||||||
|     </fieldset> |     </fieldset> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
|  | @ -374,4 +394,49 @@ | ||||||
|                     param not in ['type', 'text', 'concept', 'state']" />  |                     param not in ['type', 'text', 'concept', 'state']" />  | ||||||
| </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> | ||||||
|  | @ -28,6 +28,7 @@ from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.traversing.api import getName, getParent | from zope.traversing.api import getName, getParent | ||||||
| 
 | 
 | ||||||
|  | from cybertools.browser.form import FormController | ||||||
| from cybertools.stateful.interfaces import IStateful, IStatesDefinition | from cybertools.stateful.interfaces import IStateful, IStatesDefinition | ||||||
| from loops.browser.common import BaseView | from loops.browser.common import BaseView | ||||||
| from loops.browser.node import NodeView | from loops.browser.node import NodeView | ||||||
|  | @ -35,6 +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, checkPermission | ||||||
| from loops import util | from loops import util | ||||||
| from loops.util import _ | from loops.util import _ | ||||||
| 
 | 
 | ||||||
|  | @ -45,6 +47,8 @@ search_template = ViewPageTemplateFile('search.pt') | ||||||
| class QuickSearchResults(NodeView): | class QuickSearchResults(NodeView): | ||||||
|     """ Provides results listing """ |     """ Provides results listing """ | ||||||
| 
 | 
 | ||||||
|  |     showActions = False | ||||||
|  | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def search_macros(self): |     def search_macros(self): | ||||||
|         return self.controller.getTemplateMacros('search', search_template) |         return self.controller.getTemplateMacros('search', search_template) | ||||||
|  | @ -72,6 +76,7 @@ class QuickSearchResults(NodeView): | ||||||
| 
 | 
 | ||||||
| class Search(BaseView): | class Search(BaseView): | ||||||
| 
 | 
 | ||||||
|  |     form_action = 'execute_search_action' | ||||||
|     maxRowNum = 0 |     maxRowNum = 0 | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|  | @ -82,6 +87,11 @@ class Search(BaseView): | ||||||
|     def macro(self): |     def macro(self): | ||||||
|         return self.search_macros['search'] |         return self.search_macros['search'] | ||||||
| 
 | 
 | ||||||
|  |     @Lazy | ||||||
|  |     def showActions(self): | ||||||
|  |         return checkPermission('loops.ManageSite', self.context) | ||||||
|  |         #return canWriteObject(self.context) | ||||||
|  | 
 | ||||||
|     @property |     @property | ||||||
|     def rowNum(self): |     def rowNum(self): | ||||||
|         """ Return the rowNum to be used for identifying the current search |         """ Return the rowNum to be used for identifying the current search | ||||||
|  | @ -261,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): | ||||||
|  | @ -315,3 +324,46 @@ class SearchResults(NodeView): | ||||||
|         result = sorted(result, key=lambda x: x.title.lower()) |         result = sorted(result, key=lambda x: x.title.lower()) | ||||||
|         return self.viewIterator(result) |         return self.viewIterator(result) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class ActionExecutor(FormController): | ||||||
|  | 
 | ||||||
|  |     def update(self): | ||||||
|  |         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', []) | ||||||
|  |             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,12 +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 factory="loops.expert.setup.SetupManager" |   <adapter factory="loops.expert.setup.SetupManager" | ||||||
|            name="expert" /> |            name="expert" /> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue