[gen] It is now possible to define, via method 'getDynamicSearches', dynamic searches for a class.
This commit is contained in:
parent
4872e5d8b8
commit
5269b278f7
|
@ -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…
Reference in a new issue