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