[gen] Searches can now be grouped into groups of groups of..., creating a tree structure in the portlet.
This commit is contained in:
		
							parent
							
								
									6cf29778b6
								
							
						
					
					
						commit
						4872e5d8b8
					
				
					 6 changed files with 92 additions and 78 deletions
				
			
		
										
											Binary file not shown.
										
									
								
							|  | @ -233,24 +233,26 @@ class Group: | |||
|         if self.group and (self.group not in walkedGroups) and \ | ||||
|            not self.group.label: | ||||
|             # We remember walked groups for avoiding infinite recursion. | ||||
|             self.group.generateLabels(messages, classDescr, walkedGroups) | ||||
|             self.group.generateLabels(messages, classDescr, walkedGroups, | ||||
|                                       forSearch=forSearch) | ||||
| 
 | ||||
|     def insertInto(self, widgets, groupDescrs, page, metaType): | ||||
|     def insertInto(self, widgets, groupDescrs, page, metaType, forSearch=False): | ||||
|         '''Inserts the GroupDescr instance corresponding to this Group instance | ||||
|            into p_widgets, the recursive structure used for displaying all | ||||
|            widgets in a given p_page, and returns this GroupDescr instance.''' | ||||
|            widgets in a given p_page (or all searches), and returns this | ||||
|            GroupDescr instance.''' | ||||
|         # First, create the corresponding GroupDescr if not already in | ||||
|         # p_groupDescrs. | ||||
|         if self.name not in groupDescrs: | ||||
|             groupDescr = groupDescrs[self.name] = GroupDescr(self, page, | ||||
|                                                              metaType).get() | ||||
|             groupDescr = groupDescrs[self.name] = \ | ||||
|                 GroupDescr(self, page, metaType, forSearch=forSearch).get() | ||||
|             # Insert the group at the higher level (ie, directly in p_widgets) | ||||
|             # if the group is not itself in a group. | ||||
|             if not self.group: | ||||
|                 widgets.append(groupDescr) | ||||
|             else: | ||||
|                 outerGroupDescr = self.group.insertInto(widgets, groupDescrs, | ||||
|                                                         page, metaType) | ||||
|                                             page, metaType, forSearch=forSearch) | ||||
|                 GroupDescr.addWidget(outerGroupDescr, groupDescr) | ||||
|         else: | ||||
|             groupDescr = groupDescrs[self.name] | ||||
|  | @ -291,7 +293,7 @@ class Import: | |||
| class Search: | ||||
|     '''Used for specifying a search for a given type.''' | ||||
|     def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None, | ||||
|                  default=False, **fields): | ||||
|                  default=False, colspan=1, **fields): | ||||
|         self.name = name | ||||
|         # Searches may be visually grouped in the portlet. | ||||
|         self.group = Group.get(group) | ||||
|  | @ -301,6 +303,7 @@ class Search: | |||
|         # If this search is the default one, it will be triggered by clicking | ||||
|         # on main link. | ||||
|         self.default = default | ||||
|         self.colspan = colspan | ||||
|         # In the dict below, keys are indexed field names and values are | ||||
|         # search values. | ||||
|         self.fields = fields | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ | |||
| import os, os.path, sys, re, time, random, types, base64, urllib | ||||
| from appy import Object | ||||
| import appy.gen | ||||
| from appy.gen import Type, Search, Selection, String | ||||
| from appy.gen.utils import SomeObjects, getClassName | ||||
| from appy.gen import Type, Search, Selection, String, Page | ||||
| from appy.gen.utils import SomeObjects, getClassName, GroupDescr | ||||
| from appy.gen.mixins import BaseMixin | ||||
| from appy.gen.wrappers import AbstractWrapper | ||||
| from appy.gen.descriptors import ClassDescriptor | ||||
|  | @ -672,7 +672,7 @@ class ToolMixin(BaseMixin): | |||
|         obj = self.getObject(objectUid) | ||||
|         return obj, fieldName | ||||
| 
 | ||||
|     def getSearches(self, contentType): | ||||
|     def getGroupedSearches(self, contentType): | ||||
|         '''Returns an object with 2 attributes: | ||||
|            * "searches" stores the searches that are defined for p_contentType; | ||||
|            * "default" stores the search defined as the default one. | ||||
|  | @ -680,37 +680,27 @@ class ToolMixin(BaseMixin): | |||
|            search or about a group of searches. | ||||
|         ''' | ||||
|         appyClass = self.getAppyClass(contentType) | ||||
|         searches = [] | ||||
|         res = [] | ||||
|         default = None # Also retrieve the default one here. | ||||
|         visitedGroups = {} # Names of already visited search groups | ||||
|         groups = {} # The already encountered groups | ||||
|         page = Page('main') # A dummy page required by class GroupDescr | ||||
|         for search in ClassDescriptor.getSearches(appyClass): | ||||
|             # Determine first group label, we will need it. | ||||
|             groupLabel = None | ||||
|             groupName = None | ||||
|             if search.group: | ||||
|                 groupName = search.group.name | ||||
|                 groupLabel = '%s_searchgroup_%s' % (contentType, groupName) | ||||
|             # Add an item representing the search group if relevant | ||||
|             if groupName and (groupName not in visitedGroups): | ||||
|                 group = {'name': groupName, 'isGroup': True, | ||||
|                          'labelId': groupLabel, 'searches': [], | ||||
|                          'label': self.translate(groupLabel), | ||||
|                          'descr': self.translate('%s_descr' % groupLabel), | ||||
|                 } | ||||
|                 searches.append(group) | ||||
|                 visitedGroups[groupName] = group | ||||
|             # Add the search itself | ||||
|             # Compute the dict representing this search | ||||
|             searchLabel = '%s_search_%s' % (contentType, search.name) | ||||
|             dSearch = {'name': search.name, 'isGroup': False, | ||||
|             dSearch = {'name': search.name, 'type': 'search', | ||||
|                        'colspan': search.colspan, | ||||
|                        'label': self.translate(searchLabel), | ||||
|                        'descr': self.translate('%s_descr' % searchLabel)} | ||||
|             if groupName: | ||||
|                 visitedGroups[groupName]['searches'].append(dSearch) | ||||
|             if not search.group: | ||||
|                 # Insert the search at the highest level, not in any group. | ||||
|                 res.append(dSearch) | ||||
|             else: | ||||
|                 searches.append(dSearch) | ||||
|             if search.default: | ||||
|                 default = dSearch | ||||
|         return Object(searches=searches, default=default).__dict__ | ||||
|                 groupDescr = search.group.insertInto(res, groups, page, | ||||
|                                                     contentType, forSearch=True) | ||||
|                 GroupDescr.addWidget(groupDescr, dSearch) | ||||
|             # Is this search the default search? | ||||
|             if search.default: default = dSearch | ||||
|         return Object(searches=res, default=default).__dict__ | ||||
| 
 | ||||
|     def getQueryUrl(self, contentType, searchName, startNumber=None): | ||||
|         '''This method creates the URL that allows to perform a (non-Ajax) | ||||
|  |  | |||
|  | @ -80,7 +80,7 @@ img { border: 0; vertical-align: middle} | |||
| .portletPage { font-style: italic; } | ||||
| .portletGroup { font-variant: small-caps; font-weight: bold; font-style: normal; | ||||
|                 margin-top: 0.1em } | ||||
| .portletSearch { font-size: 90%; font-style: italic; padding-left: 1em} | ||||
| .portletSearch { font-size: 90%; font-style: italic } | ||||
| .phase { border-style: dashed; border-width: thin; padding: 4px 0.6em 5px 1em;} | ||||
| .phaseSelected { background-color: #F4F5F6; } | ||||
| .content { padding: 14px 14px 9px 15px; background-color: #f1f1f1 } | ||||
|  |  | |||
|  | @ -1,9 +1,51 @@ | |||
| <tal:comment replace="nothing"> | ||||
|     This macro displays the content of the application portlet. | ||||
| </tal:comment> | ||||
| <tal:comment replace="nothing">Macro for displaying a search</tal:comment> | ||||
| <div metal:define-macro="search" class="portletSearch"> | ||||
|   <a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']); | ||||
|                      title search/descr; | ||||
|                      class python: test(search['name'] == currentSearch, 'portletCurrent', '');" | ||||
|      tal:content="structure search/label"></a> | ||||
| </div> | ||||
| 
 | ||||
| <tal:comment replace="nothing">Macro for displaying a group of searches</tal:comment> | ||||
| <metal:group define-macro="group" | ||||
|              tal:define="expanded python: request.get(widget['labelId'], 'collapsed') == 'expanded'"> | ||||
|  <tal:comment replace="nothing">Group name</tal:comment> | ||||
|  <div class="portletGroup"> | ||||
|    <img style="cursor:pointer; margin-right: 3px" | ||||
|         tal:attributes="id python: '%s_img' % widget['labelId']; | ||||
|                         src python:test(expanded, 'ui/collapse.gif', 'ui/expand.gif'); | ||||
|                         onClick python:'toggleCookie(\'%s\')' % widget['labelId']; | ||||
|                         align dleft"/> | ||||
|    <span tal:replace="python: _(widget['labelId'])"/> | ||||
|  </div> | ||||
|  <tal:comment replace="nothing">Group content</tal:comment> | ||||
|  <div tal:define="display python:test(expanded, 'display:block', 'display:none')" | ||||
|       tal:attributes="id widget/labelId; | ||||
|                       style python: 'padding-left: 10px;; %s' % display"> | ||||
|   <tal:searchRows repeat="searches widget/widgets"> | ||||
|    <tal:searchElem repeat="searchElem searches"> | ||||
|     <tal:comment replace="nothing">An inner group within this group</tal:comment> | ||||
|     <tal:group condition="python: searchElem['type'] == 'group'"> | ||||
|      <tal:g define="widget searchElem"> | ||||
|       <metal:s use-macro="app/ui/portlet/macros/group"/> | ||||
|      </tal:g>  | ||||
|     </tal:group> | ||||
|     <tal:search condition="python: searchElem['type'] != 'group'"> | ||||
|      <tal:s define="search searchElem"> | ||||
|       <metal:s use-macro="app/ui/portlet/macros/search"/> | ||||
|      </tal:s> | ||||
|     </tal:search> | ||||
|    </tal:searchElem> | ||||
|   </tal:searchRows> | ||||
|  </div> | ||||
| </metal:group> | ||||
| 
 | ||||
| <tal:comment replace="nothing">Macro displaying the whole portlet</tal:comment> | ||||
| <metal:portlet define-macro="portlet" | ||||
|    tal:define="queryUrl           python: '%s/ui/query' % tool.absolute_url(); | ||||
|                toolUrl            tool/absolute_url; | ||||
|                app                tool/getApp; | ||||
|                appUrl             app/absolute_url; | ||||
|                currentSearch      req/search|nothing; | ||||
|                currentClass       req/className|nothing; | ||||
|                contextObj         tool/getPublishedObject; | ||||
|  | @ -16,7 +58,7 @@ | |||
|        <img tal:attributes="src string: $appUrl/ui/gotoSource.png"/> | ||||
|      </a> | ||||
|   </div> | ||||
|   <metal:phases use-macro="here/ui/portlet/macros/phases"/> | ||||
|   <metal:phases use-macro="app/ui/portlet/macros/phases"/> | ||||
|  </div> | ||||
| 
 | ||||
|  <tal:comment replace="nothing">One section for every searchable root class.</tal:comment> | ||||
|  | @ -26,7 +68,7 @@ | |||
|   <div class="portletSep" tal:define="nb repeat/rootClass/number" | ||||
|                           tal:condition="python: (nb == 1 and contextObj) or (nb != 1)"></div>   | ||||
| 
 | ||||
|   <div class="portletContent" tal:define="searchInfo python: tool.getSearches(rootClass)"> | ||||
|   <div class="portletContent" tal:define="searchInfo python: tool.getGroupedSearches(rootClass)"> | ||||
|    <tal:comment replace="nothing">Section title (link triggers the default search), with action icons</tal:comment> | ||||
|    <a tal:define="queryParam python: searchInfo['default'] and ('&search=%s' % searchInfo['default']['name']) or ''" | ||||
|       tal:attributes="href python: '%s?className=%s%s' % (queryUrl, rootClass, queryParam); | ||||
|  | @ -56,40 +98,17 @@ | |||
|      <img tal:attributes="src string: $appUrl/ui/search.gif"/> | ||||
|     </a> | ||||
|    </span> | ||||
|    <tal:comment replace="nothing">Searches for this content type.</tal:comment> | ||||
|    <tal:searchOrGroup repeat="searchOrGroup searchInfo/searches"> | ||||
|      <tal:group condition="searchOrGroup/isGroup"> | ||||
|      <tal:expanded define="group searchOrGroup; | ||||
|                            expanded python: request.get(group['labelId'], 'collapsed') == 'expanded'"> | ||||
|        <tal:comment replace="nothing">Group name</tal:comment> | ||||
|        <div class="portletGroup"> | ||||
|          <img style="cursor:pointer; margin-right: 3px" | ||||
|               tal:attributes="id python: '%s_img' % group['labelId']; | ||||
|                               src python:test(expanded, 'ui/collapse.gif', 'ui/expand.gif'); | ||||
|                               onClick python:'toggleCookie(\'%s\')' % group['labelId']; | ||||
|                               align dleft"/> | ||||
|          <span tal:replace="group/label"/> | ||||
|        </div> | ||||
|        <tal:comment replace="nothing">Group searches</tal:comment> | ||||
|        <div tal:attributes="id group/labelId; | ||||
|                              style python:test(expanded, 'display:block', 'display:none')"> | ||||
|          <div class="portletSearch" tal:repeat="search group/searches"> | ||||
|            <a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']); | ||||
|                               title search/descr; | ||||
|                               class python: test(search['name'] == currentSearch, 'portletCurrent', '');" | ||||
|               tal:content="structure search/label"></a> | ||||
|          </div> | ||||
|        </div> | ||||
|      </tal:expanded> | ||||
|      </tal:group> | ||||
|      <dt tal:define="search searchOrGroup" tal:condition="not: searchOrGroup/isGroup" | ||||
|          class="portletAppyItem portletSearch"> | ||||
|        <a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']); | ||||
|                     title search/descr; | ||||
|                     class python: test(search['name'] == currentSearch, 'portletCurrent', '');" | ||||
|           tal:content="structure search/label"></a> | ||||
|      </dt> | ||||
|    </tal:searchOrGroup> | ||||
|    <tal:comment replace="nothing">Searches for this content type</tal:comment> | ||||
|    <tal:widget repeat="widget searchInfo/searches"> | ||||
|     <tal:group condition="python: widget['type'] == 'group'"> | ||||
|      <metal:s use-macro="app/ui/portlet/macros/group"/> | ||||
|     </tal:group> | ||||
|     <tal:search condition="python: widget['type'] != 'group'"> | ||||
|      <tal:s define="search widget"> | ||||
|       <metal:s use-macro="app/ui/portlet/macros/search"/> | ||||
|      </tal:s> | ||||
|     </tal:search> | ||||
|    </tal:widget> | ||||
|    </div> | ||||
|  </tal:section> | ||||
| </metal:portlet> | ||||
|  |  | |||
|  | @ -59,8 +59,8 @@ class Descr: | |||
|     def get(self): return self.__dict__ | ||||
| 
 | ||||
| class GroupDescr(Descr): | ||||
|     def __init__(self, group, page, metaType): | ||||
|         '''Creates the data structure manipulated in ZPTs from p_group, the | ||||
|     def __init__(self, group, page, metaType, forSearch=False): | ||||
|         '''Creates the data structure manipulated in ZPTs for p_group, the | ||||
|            Group instance used in the field definition.''' | ||||
|         self.type = 'group' | ||||
|         # All p_group attributes become self attributes. | ||||
|  | @ -77,7 +77,9 @@ class GroupDescr(Descr): | |||
|             else: # It is a tuple (metaType, name) | ||||
|                 if group.label[1]: labelName = group.label[1] | ||||
|                 if group.label[0]: prefix = group.label[0] | ||||
|         self.labelId = '%s_group_%s' % (prefix, labelName) | ||||
|         if forSearch: gp = 'searchgroup' | ||||
|         else:         gp = 'group' | ||||
|         self.labelId = '%s_%s_%s' % (prefix, gp, labelName) | ||||
|         self.descrId = self.labelId + '_descr' | ||||
|         self.helpId  = self.labelId + '_help' | ||||
|         # The name of the page where the group lies | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay