[gen] It is now possible to define, via method 'getDynamicSearches', dynamic searches for a class.
This commit is contained in:
		
							parent
							
								
									4872e5d8b8
								
							
						
					
					
						commit
						5269b278f7
					
				
					 6 changed files with 99 additions and 52 deletions
				
			
		|  | @ -106,7 +106,8 @@ class Group: | |||
|                  hasLabel=True, hasDescr=False, hasHelp=False, | ||||
|                  hasHeaders=False, group=None, colspan=1, align='center', | ||||
|                  valign='top', css_class='', master=None, masterValue=None, | ||||
|                  cellpadding=1, cellspacing=1, cellgap='0.6em', label=None): | ||||
|                  cellpadding=1, cellspacing=1, cellgap='0.6em', label=None, | ||||
|                  translated=None): | ||||
|         self.name = name | ||||
|         # In its simpler form, field "columns" below can hold a list or tuple | ||||
|         # of column widths expressed as strings, that will be given as is in | ||||
|  | @ -168,6 +169,9 @@ class Group: | |||
|         self.masterValue = initMasterValue(masterValue) | ||||
|         if master: master.slaves.append(self) | ||||
|         self.label = label # See similar attr of Type class. | ||||
|         # If a translated name is already given here, we will use it instead of | ||||
|         # trying to translate the group label. | ||||
|         self.translated = translated | ||||
| 
 | ||||
|     def _setColumns(self): | ||||
|         '''Standardizes field "columns" as a list of Column instances. Indeed, | ||||
|  | @ -293,7 +297,8 @@ 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, colspan=1, **fields): | ||||
|                  default=False, colspan=1, translated=None, | ||||
|                  translatedDescr=None, **fields): | ||||
|         self.name = name | ||||
|         # Searches may be visually grouped in the portlet. | ||||
|         self.group = Group.get(group) | ||||
|  | @ -304,6 +309,10 @@ class Search: | |||
|         # on main link. | ||||
|         self.default = default | ||||
|         self.colspan = colspan | ||||
|         # If a translated name or description is already given here, we will | ||||
|         # use it instead of trying to translate from labels. | ||||
|         self.translated = translated | ||||
|         self.translatedDescr = translatedDescr | ||||
|         # In the dict below, keys are indexed field names and values are | ||||
|         # search values. | ||||
|         self.fields = fields | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ 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, Page | ||||
| from appy.gen.utils import SomeObjects, getClassName, GroupDescr | ||||
| from appy.gen.utils import SomeObjects, getClassName, GroupDescr, SearchDescr | ||||
| from appy.gen.mixins import BaseMixin | ||||
| from appy.gen.wrappers import AbstractWrapper | ||||
| from appy.gen.descriptors import ClassDescriptor | ||||
|  | @ -288,9 +288,8 @@ class ToolMixin(BaseMixin): | |||
|                      maxResults=None, noSecurity=False, sortBy=None, | ||||
|                      sortOrder='asc', filterKey=None, filterValue=None, | ||||
|                      refObject=None, refField=None): | ||||
|         '''Executes a query on instances of a given p_className (or several, | ||||
|            separated with commas) in the catalog. If p_searchName is specified, | ||||
|            it corresponds to: | ||||
|         '''Executes a query on instances of a given p_className in the catalog. | ||||
|            If p_searchName is specified, it corresponds to: | ||||
|              1) a search defined on p_className: additional search criteria | ||||
|                 will be added to the query, or; | ||||
|              2) "_advanced": in this case, additional search criteria will also | ||||
|  | @ -328,22 +327,10 @@ class ToolMixin(BaseMixin): | |||
| 
 | ||||
|            If p_refObject and p_refField are given, the query is limited to the | ||||
|            objects that are referenced from p_refObject through p_refField.''' | ||||
|         # Is there one or several content types ? | ||||
|         if className.find(',') != -1: | ||||
|             classNames = className.split(',') | ||||
|         else: | ||||
|             classNames = className | ||||
|         params = {'ClassName': classNames} | ||||
|         params = {'ClassName': className} | ||||
|         if not brainsOnly: params['batch'] = True | ||||
|         # Manage additional criteria from a search when relevant | ||||
|         if searchName: | ||||
|             # In this case, className must contain a single content type. | ||||
|             appyClass = self.getAppyClass(className) | ||||
|             if searchName != '_advanced': | ||||
|                 search = ClassDescriptor.getSearch(appyClass, searchName) | ||||
|             else: | ||||
|                 fields = self.REQUEST.SESSION['searchCriteria'] | ||||
|                 search = Search('customSearch', **fields) | ||||
|         if searchName: search = self.getSearch(className, searchName) | ||||
|         if search: | ||||
|             # Add additional search criteria | ||||
|             for fieldName, fieldValue in search.fields.iteritems(): | ||||
|  | @ -672,36 +659,61 @@ class ToolMixin(BaseMixin): | |||
|         obj = self.getObject(objectUid) | ||||
|         return obj, fieldName | ||||
| 
 | ||||
|     def getGroupedSearches(self, contentType): | ||||
|     def getGroupedSearches(self, className): | ||||
|         '''Returns an object with 2 attributes: | ||||
|            * "searches" stores the searches that are defined for p_contentType; | ||||
|            * "searches" stores the searches that are defined for p_className; | ||||
|            * "default" stores the search defined as the default one. | ||||
|            Every item representing a search is a dict containing info about a | ||||
|            search or about a group of searches. | ||||
|         ''' | ||||
|         appyClass = self.getAppyClass(contentType) | ||||
|         appyClass = self.getAppyClass(className) | ||||
|         res = [] | ||||
|         default = None # Also retrieve the default one here. | ||||
|         groups = {} # The already encountered groups | ||||
|         page = Page('main') # A dummy page required by class GroupDescr | ||||
|         for search in ClassDescriptor.getSearches(appyClass): | ||||
|             # Compute the dict representing this search | ||||
|             searchLabel = '%s_search_%s' % (contentType, search.name) | ||||
|             dSearch = {'name': search.name, 'type': 'search', | ||||
|                        'colspan': search.colspan, | ||||
|                        'label': self.translate(searchLabel), | ||||
|                        'descr': self.translate('%s_descr' % searchLabel)} | ||||
|         searches = ClassDescriptor.getSearches(appyClass) | ||||
|         # Add dynamic searches if defined on p_appyClass | ||||
|         if hasattr(appyClass, 'getDynamicSearches'): | ||||
|             searches += appyClass.getDynamicSearches(self.appy()) | ||||
|         for search in searches: | ||||
|             # Create the search descriptor | ||||
|             searchDescr = SearchDescr(search, className, self).get() | ||||
|             if not search.group: | ||||
|                 # Insert the search at the highest level, not in any group. | ||||
|                 res.append(dSearch) | ||||
|                 res.append(searchDescr) | ||||
|             else: | ||||
|                 groupDescr = search.group.insertInto(res, groups, page, | ||||
|                                                     contentType, forSearch=True) | ||||
|                 GroupDescr.addWidget(groupDescr, dSearch) | ||||
|                                                      className, forSearch=True) | ||||
|                 GroupDescr.addWidget(groupDescr, searchDescr) | ||||
|             # Is this search the default search? | ||||
|             if search.default: default = dSearch | ||||
|             if search.default: default = searchDescr | ||||
|         return Object(searches=res, default=default).__dict__ | ||||
| 
 | ||||
|     def getSearch(self, className, name, descr=False): | ||||
|         '''Gets the Search instance (or a SearchDescr instance if p_descr is | ||||
|            True) corresponding to the search named p_name, on class | ||||
|            p_className.''' | ||||
|         if name == '_advanced': | ||||
|             # It is an advanced search whose parameters are in the session. | ||||
|             fields = self.REQUEST.SESSION['searchCriteria'] | ||||
|             res = Search('customSearch', **fields) | ||||
|         elif name: | ||||
|             appyClass = self.getAppyClass(className) | ||||
|             # Search among static searches | ||||
|             res = ClassDescriptor.getSearch(appyClass, name) | ||||
|             if not res and hasattr(appyClass, 'getDynamicSearches'): | ||||
|                 # Search among dynamic searches | ||||
|                 for search in appyClass.getDynamicSearches(self.appy()): | ||||
|                     if search.name == name: | ||||
|                         res = search | ||||
|                         break | ||||
|         else: | ||||
|             # It is the search for every instance of p_className | ||||
|             res = Search('allSearch') | ||||
|         # Return a SearchDescr if required. | ||||
|         if descr: res = SearchDescr(res, className, self).get() | ||||
|         return res | ||||
| 
 | ||||
|     def getQueryUrl(self, contentType, searchName, startNumber=None): | ||||
|         '''This method creates the URL that allows to perform a (non-Ajax) | ||||
|            request for getting queried objects from a search named p_searchName | ||||
|  | @ -753,11 +765,13 @@ class ToolMixin(BaseMixin): | |||
|                 # This is a named, predefined search. | ||||
|                 label = '%s_search_%s' % (d1.split(':')[0], searchName) | ||||
|             res['backText'] = self.translate(label) | ||||
|             # If it is a dynamic search this label does not exist. | ||||
|             if ('_' in res['backText']): res['backText'] = '' | ||||
|         else: | ||||
|             fieldName, pageName = d2.split(':') | ||||
|             sourceObj = self.getObject(d1) | ||||
|             label = '%s_%s' % (sourceObj.meta_type, fieldName) | ||||
|             res['backText'] = '%s : %s' % (sourceObj.Title(), | ||||
|             res['backText'] = '%s - %s' % (sourceObj.Title(), | ||||
|                                            self.translate(label)) | ||||
|         newNav = '%s.%s.%s.%%d.%s' % (t, d1, d2, totalNumber) | ||||
|         # Among, first, previous, next and last, which one do I need? | ||||
|  |  | |||
|  | @ -64,8 +64,9 @@ | |||
|     <tr valign="middle"> | ||||
|       <tal:comment replace="nothing">Go to the source URL (search or referred object)</tal:comment> | ||||
|       <td tal:condition="sourceUrl"><a tal:attributes="href sourceUrl"><img | ||||
|           tal:attributes="src string: $appUrl/ui/gotoSource.png; | ||||
|                           title python: backText + ' : ' + _('goto_source')"/></a></td> | ||||
|           tal:define="gotoSource python: _('goto_source'); | ||||
|                       goBack python: backText and ('%s - %s' % (backText, gotoSource)) or gotoSource" | ||||
|           tal:attributes="src string: $appUrl/ui/gotoSource.png; title goBack"/></a></td> | ||||
|       <tal:comment replace="nothing">Go to the first page</tal:comment> | ||||
|       <td tal:condition="firstUrl"><a tal:attributes="href firstUrl"><img | ||||
|           tal:attributes="src string: $appUrl/ui/arrowLeftDouble.png; | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| <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; | ||||
|                      title search/translatedDescr; | ||||
|                      class python: test(search['name'] == currentSearch, 'portletCurrent', '');" | ||||
|      tal:content="structure search/label"></a> | ||||
|      tal:content="search/translated"> | ||||
|   </a> | ||||
| </div> | ||||
| 
 | ||||
| <tal:comment replace="nothing">Macro for displaying a group of searches</tal:comment> | ||||
|  | @ -16,7 +17,8 @@ | |||
|                         src python:test(expanded, 'ui/collapse.gif', 'ui/expand.gif'); | ||||
|                         onClick python:'toggleCookie(\'%s\')' % widget['labelId']; | ||||
|                         align dleft"/> | ||||
|    <span tal:replace="python: _(widget['labelId'])"/> | ||||
|    <span tal:condition="not: widget/translated" tal:replace="python: _(widget['labelId'])"/> | ||||
|    <span tal:condition="widget/translated" tal:replace="widget/translated"/> | ||||
|  </div> | ||||
|  <tal:comment replace="nothing">Group content</tal:comment> | ||||
|  <div tal:define="display python:test(expanded, 'display:block', 'display:none')" | ||||
|  |  | |||
|  | @ -7,15 +7,13 @@ | |||
|                refUrlPart    python: refObject and ('&ref=%s:%s' % (refObject.UID(), refField))  or ''; | ||||
|                startNumber   request/startNumber|python:'0'; | ||||
|                startNumber   python: int(startNumber); | ||||
|                searchName    request/search; | ||||
|                labelId       python: searchName and ('%s_search_%s' % (className, searchName)) or ''; | ||||
|                labelId       python: (searchName == '_advanced') and 'search_results' or labelId; | ||||
|                searchLabel   python: labelId and _(labelId) or ''; | ||||
|                searchName    request/search|python:''; | ||||
|                searchDescr   python: tool.getSearch(className, searchName, descr=True); | ||||
|                sortKey       request/sortKey| python:''; | ||||
|                sortOrder     request/sortOrder| python:'asc'; | ||||
|                filterKey     request/filterKey| python:''; | ||||
|                filterValue   request/filterValue | python:''; | ||||
|                queryResult   python: tool.executeQuery(className, searchName, startNumber, remember=True, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, refObject=refObject, refField=refField); | ||||
|                queryResult   python: tool.executeQuery(className, search=searchDescr['search'], startNumber=startNumber, remember=True, sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey, filterValue=filterValue, refObject=refObject, refField=refField); | ||||
|                objs          queryResult/objects; | ||||
|                totalNumber   queryResult/totalNumber; | ||||
|                batchSize     queryResult/batchSize; | ||||
|  | @ -39,7 +37,7 @@ | |||
| 
 | ||||
|   <tal:comment replace="nothing">The title of the search.</tal:comment> | ||||
|   <p> | ||||
|    <span tal:replace="structure python: test(searchName, searchLabel, _('%s_plural' % className))"/> | ||||
|    <span tal:replace="searchDescr/translated"/> | ||||
|     (<span tal:replace="totalNumber"/>) | ||||
|       <tal:newSearch condition="python: searchName == '_advanced'"> | ||||
|          — <i><a tal:attributes="href newSearchUrl" | ||||
|  | @ -48,19 +46,16 @@ | |||
|   </p> | ||||
|   <table width="100%"> | ||||
|    <tr> | ||||
|     <tal:descr condition="searchName"> | ||||
|     <td tal:define="descr python: _('%s_descr' % labelId)" | ||||
|         tal:condition="descr/strip"> | ||||
|      <span class="discreet" tal:content="descr"></span><br/> | ||||
|     <tal:comment replace="nothing">Search description</tal:comment> | ||||
|     <td tal:condition="searchDescr/translatedDescr"> | ||||
|      <span class="discreet" tal:content="searchDescr/translatedDescr"></span><br/> | ||||
|     </td> | ||||
|     </tal:descr> | ||||
|     <td tal:attributes="align dright" width="25%"> | ||||
|      <tal:comment replace="nothing">Appy (top) navigation</tal:comment> | ||||
|      <metal:nav use-macro="context/ui/navigate/macros/appyNavigate"/> | ||||
|     </td> | ||||
|    </tr> | ||||
|   </table> | ||||
| 
 | ||||
|   <table tal:define="columnLayouts python: tool.getResultColumnsLayouts(className, refInfo); | ||||
|                      columns python: objs[0].getColumnsSpecifiers(columnLayouts, dir)" | ||||
|          class="list" width="100%"> | ||||
|  |  | |||
							
								
								
									
										26
									
								
								gen/utils.py
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								gen/utils.py
									
										
									
									
									
								
							|  | @ -185,6 +185,32 @@ class PhaseDescr(Descr): | |||
|                     self.nextPhase = allPhases[i+1] | ||||
|         self.phaseStatus = res | ||||
| 
 | ||||
| class SearchDescr(Descr): | ||||
|     '''Describes a Search.''' | ||||
|     def __init__(self, search, className, tool): | ||||
|         self.search = search | ||||
|         self.name = search.name | ||||
|         self.type = 'search' | ||||
|         self.colspan = search.colspan | ||||
|         if search.translated: | ||||
|             self.translated = search.translated | ||||
|             self.translatedDescr = search.translatedDescr | ||||
|         else: | ||||
|             # The label may be specific in some special cases. | ||||
|             labelDescr = '' | ||||
|             if search.name == 'allSearch': | ||||
|                 label = '%s_plural' % className | ||||
|             elif search.name == 'customSearch': | ||||
|                 label = 'search_results' | ||||
|             else: | ||||
|                 label = '%s_search_%s' % (className, search.name) | ||||
|                 labelDescr = label + '_descr' | ||||
|             self.translated = tool.translate(label) | ||||
|             if labelDescr: | ||||
|                 self.translatedDescr = tool.translate(labelDescr) | ||||
|             else: | ||||
|                 self.translatedDescr = '' | ||||
| 
 | ||||
| # ------------------------------------------------------------------------------ | ||||
| upperLetter = re.compile('[A-Z]') | ||||
| def produceNiceMessage(msg): | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay