Improved advanced search screen.
This commit is contained in:
parent
d6607d7815
commit
24d0370892
|
@ -199,7 +199,12 @@ class String(Type):
|
||||||
if complement:
|
if complement:
|
||||||
return (97 - (number % 97)) == checkNumber
|
return (97 - (number % 97)) == checkNumber
|
||||||
else:
|
else:
|
||||||
return (number % 97) == checkNumber
|
# The check number can't be 0. In this case, we force it to be 97.
|
||||||
|
# This is the way Belgian bank account numbers work. I hope this
|
||||||
|
# behaviour is general enough to be implemented here.
|
||||||
|
mod97 = (number % 97)
|
||||||
|
if mod97 == 0: return checkNumber == 97
|
||||||
|
else: return checkNumber == mod97
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def MODULO_97(obj, value): return String._MODULO_97(obj, value)
|
def MODULO_97(obj, value): return String._MODULO_97(obj, value)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -122,6 +122,8 @@ class Generator(AbstractGenerator):
|
||||||
msg('search_results', '', msg.SEARCH_RESULTS),
|
msg('search_results', '', msg.SEARCH_RESULTS),
|
||||||
msg('search_results_descr', '', ' '),
|
msg('search_results_descr', '', ' '),
|
||||||
msg('search_new', '', msg.SEARCH_NEW),
|
msg('search_new', '', msg.SEARCH_NEW),
|
||||||
|
msg('search_from', '', msg.SEARCH_FROM),
|
||||||
|
msg('search_to', '', msg.SEARCH_TO),
|
||||||
msg('ref_invalid_index', '', msg.REF_INVALID_INDEX),
|
msg('ref_invalid_index', '', msg.REF_INVALID_INDEX),
|
||||||
msg('bad_int', '', msg.BAD_INT),
|
msg('bad_int', '', msg.BAD_INT),
|
||||||
msg('bad_float', '', msg.BAD_FLOAT),
|
msg('bad_float', '', msg.BAD_FLOAT),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import re, os, os.path, Cookie
|
import re, os, os.path, Cookie
|
||||||
from appy.gen import Type, Search
|
from appy.gen import Type, Search
|
||||||
from appy.gen.utils import FieldDescr, SomeObjects
|
from appy.gen.utils import FieldDescr, SomeObjects, sequenceTypes
|
||||||
from appy.gen.plone25.mixins import AbstractMixin
|
from appy.gen.plone25.mixins import AbstractMixin
|
||||||
from appy.gen.plone25.mixins.FlavourMixin import FlavourMixin
|
from appy.gen.plone25.mixins.FlavourMixin import FlavourMixin
|
||||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||||
|
@ -160,15 +160,30 @@ class ToolMixin(AbstractMixin):
|
||||||
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():
|
||||||
|
# Make the correspondance between the name of the field and the
|
||||||
|
# name of the corresponding index.
|
||||||
attrName = fieldName
|
attrName = fieldName
|
||||||
if attrName == 'title': attrName = 'Title'
|
if attrName == 'title': attrName = 'Title'
|
||||||
elif attrName == 'description': attrName = 'Description'
|
elif attrName == 'description': attrName = 'Description'
|
||||||
elif attrName == 'state': attrName = 'review_state'
|
elif attrName == 'state': attrName = 'review_state'
|
||||||
else: attrName = 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
|
else: attrName = 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
|
||||||
|
# Express the field value in the way needed by the index
|
||||||
if isinstance(fieldValue, basestring) and \
|
if isinstance(fieldValue, basestring) and \
|
||||||
fieldValue.endswith('*'):
|
fieldValue.endswith('*'):
|
||||||
v = fieldValue[:-1]
|
v = fieldValue[:-1]
|
||||||
params[attrName] = {'query':[v,v+'Z'], 'range':'minmax'}
|
params[attrName] = {'query':(v,v+'Z'), 'range':'minmax'}
|
||||||
|
elif type(fieldValue) in sequenceTypes:
|
||||||
|
# We have a range of values instead of a single value
|
||||||
|
minv, maxv = fieldValue
|
||||||
|
rangev = 'minmax'
|
||||||
|
queryv = fieldValue
|
||||||
|
if minv == None:
|
||||||
|
rangev = 'max'
|
||||||
|
queryv = maxv
|
||||||
|
elif maxv == None:
|
||||||
|
rangev = 'min'
|
||||||
|
queryv = minv
|
||||||
|
params[attrName] = {'query':queryv, 'range':rangev}
|
||||||
else:
|
else:
|
||||||
params[attrName] = fieldValue
|
params[attrName] = fieldValue
|
||||||
# Add a sort order if specified
|
# Add a sort order if specified
|
||||||
|
@ -380,6 +395,48 @@ class ToolMixin(AbstractMixin):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _searchValueIsEmpty(self, key):
|
||||||
|
'''Returns True if request value in key p_key can be considered as
|
||||||
|
empty.'''
|
||||||
|
rq = self.REQUEST.form
|
||||||
|
if key.endswith('*int'):
|
||||||
|
# We return True if "from" AND "to" values are empty.
|
||||||
|
toKey = '%s_to' % key[2:-4]
|
||||||
|
return not rq[key].strip() and not rq[toKey].strip()
|
||||||
|
elif key.endswith('*date'):
|
||||||
|
# We return True if "from" AND "to" values are empty. A value is
|
||||||
|
# considered as not empty if at least the year is specified.
|
||||||
|
toKey = '%s_to_year' % key[2:-5]
|
||||||
|
return not rq[key] and not rq[toKey]
|
||||||
|
else:
|
||||||
|
return not rq[key]
|
||||||
|
|
||||||
|
def _getDateTime(self, year, month, day, setMin):
|
||||||
|
'''Gets a valid DateTime instance from date information coming from the
|
||||||
|
request as strings in p_year, p_month and p_day. Returns None if
|
||||||
|
p_year is empty. If p_setMin is True, when some
|
||||||
|
information is missing (month or day), we will replace it with the
|
||||||
|
minimum value (=1). Else, we will replace it with the maximum value
|
||||||
|
(=12, =31).'''
|
||||||
|
if not year: return None
|
||||||
|
if not month:
|
||||||
|
if setMin: month = 1
|
||||||
|
else: month = 12
|
||||||
|
if not day:
|
||||||
|
if setMin: day = 1
|
||||||
|
else: day = 31
|
||||||
|
DateTime = self.getProductConfig().DateTime
|
||||||
|
# We loop until we find a valid date. For example, we could loop from
|
||||||
|
# 2009/02/31 to 2009/02/28.
|
||||||
|
dateIsWrong = True
|
||||||
|
while dateIsWrong:
|
||||||
|
try:
|
||||||
|
res = DateTime('%s/%s/%s' % (year, month, day))
|
||||||
|
dateIsWrong = False
|
||||||
|
except:
|
||||||
|
day = int(day)-1
|
||||||
|
return res
|
||||||
|
|
||||||
def onSearchObjects(self):
|
def onSearchObjects(self):
|
||||||
'''This method is called when the user triggers a search from
|
'''This method is called when the user triggers a search from
|
||||||
search.pt.'''
|
search.pt.'''
|
||||||
|
@ -387,16 +444,40 @@ class ToolMixin(AbstractMixin):
|
||||||
# Store the search criteria in the session
|
# Store the search criteria in the session
|
||||||
criteria = {}
|
criteria = {}
|
||||||
for attrName in rq.form.keys():
|
for attrName in rq.form.keys():
|
||||||
if attrName.startswith('w_'):
|
if attrName.startswith('w_') and \
|
||||||
|
not self._searchValueIsEmpty(attrName):
|
||||||
|
# We have a(n interval of) value(s) that is not empty for a
|
||||||
|
# given field.
|
||||||
attrValue = rq.form[attrName]
|
attrValue = rq.form[attrName]
|
||||||
if attrValue:
|
if attrName.find('*') != -1:
|
||||||
if attrName.find('*') != -1:
|
attrName, attrType = attrName.split('*')
|
||||||
attrName, attrType = attrName.split('*')
|
if attrType == 'bool':
|
||||||
if attrType == 'bool':
|
exec 'attrValue = %s' % attrValue
|
||||||
exec 'attrValue = %s' % attrValue
|
elif attrType == 'int':
|
||||||
if isinstance(attrValue, list):
|
# Get the "from" value
|
||||||
attrValue = ' OR '.join(attrValue)
|
if not attrValue.strip(): attrValue = None
|
||||||
criteria[attrName[2:]] = attrValue
|
else: attrValue = int(attrValue)
|
||||||
|
# Get the "to" value
|
||||||
|
toValue = rq.form['%s_to' % attrName[2:]].strip()
|
||||||
|
if not toValue: toValue = None
|
||||||
|
else: toValue = int(toValue)
|
||||||
|
attrValue = (attrValue, toValue)
|
||||||
|
elif attrType == 'date':
|
||||||
|
prefix = attrName[2:]
|
||||||
|
# Get the "from" value
|
||||||
|
year = attrValue
|
||||||
|
month = rq.form['%s_from_month' % prefix]
|
||||||
|
day = rq.form['%s_from_day' % prefix]
|
||||||
|
fromDate = self._getDateTime(year, month, day, True)
|
||||||
|
# Get the "to" value"
|
||||||
|
year = rq.form['%s_to_year' % prefix]
|
||||||
|
month = rq.form['%s_to_month' % prefix]
|
||||||
|
day = rq.form['%s_to_day' % prefix]
|
||||||
|
toDate = self._getDateTime(year, month, day, False)
|
||||||
|
attrValue = (fromDate, toDate)
|
||||||
|
if isinstance(attrValue, list):
|
||||||
|
attrValue = ' OR '.join(attrValue)
|
||||||
|
criteria[attrName[2:]] = attrValue
|
||||||
rq.SESSION['searchCriteria'] = criteria
|
rq.SESSION['searchCriteria'] = criteria
|
||||||
# Goto the screen that displays search results
|
# Goto the screen that displays search results
|
||||||
backUrl = '%s/query?type_name=%s&flavourNumber=%d&search=_advanced' % \
|
backUrl = '%s/query?type_name=%s&flavourNumber=%d&search=_advanced' % \
|
||||||
|
@ -594,4 +675,12 @@ class ToolMixin(AbstractMixin):
|
||||||
'''Truncates string p_value to p_numberOfChars.'''
|
'''Truncates string p_value to p_numberOfChars.'''
|
||||||
if len(value) > numberOfChars: return value[:numberOfChars] + '...'
|
if len(value) > numberOfChars: return value[:numberOfChars] + '...'
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
monthsIds = {
|
||||||
|
1: 'month_jan', 2: 'month_feb', 3: 'month_mar', 4: 'month_apr',
|
||||||
|
5: 'month_may', 6: 'month_jun', 7: 'month_jul', 8: 'month_aug',
|
||||||
|
9: 'month_sep', 10: 'month_oct', 11: 'month_nov', 12: 'month_dec'}
|
||||||
|
def getMonthName(self, monthNumber):
|
||||||
|
'''Gets the translated month name of month numbered p_monthNumber.'''
|
||||||
|
return self.translate(self.monthsIds[int(monthNumber)], domain='plone')
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -31,15 +31,15 @@
|
||||||
|
|
||||||
<table class="no-style-table" cellpadding="0" cellspacing="0" width="100%"
|
<table class="no-style-table" cellpadding="0" cellspacing="0" width="100%"
|
||||||
tal:define="numberOfColumns python: flavour.getAttr('numberOfSearchColumnsFor%s' % contentType)">
|
tal:define="numberOfColumns python: flavour.getAttr('numberOfSearchColumnsFor%s' % contentType)">
|
||||||
<tr tal:repeat="searchRow python: tool.tabularize(searchableFields, numberOfColumns)" valign="top" class="appySearchRow">
|
<tr tal:repeat="searchRow python: tool.tabularize(searchableFields, numberOfColumns)" valign="top">
|
||||||
<td tal:repeat="searchField searchRow">
|
<td tal:repeat="searchField searchRow" tal:attributes="width python:'%d%%' % (100/numberOfColumns)">
|
||||||
<tal:field condition="searchField">
|
<tal:field condition="searchField">
|
||||||
<tal:showSearchField define="fieldName python:searchField[0];
|
<tal:showSearchField define="fieldName python:searchField[0];
|
||||||
appyType python:searchField[1];
|
appyType python:searchField[1];
|
||||||
widgetName python: 'w_%s' % fieldName">
|
widgetName python: 'w_%s' % fieldName">
|
||||||
<metal:searchField use-macro="python: appFolder.skyn.widgets.macros.get('search%s' % searchField[1]['type'])"/>
|
<metal:searchField use-macro="python: appFolder.skyn.widgets.macros.get('search%s' % searchField[1]['type'])"/>
|
||||||
</tal:showSearchField>
|
</tal:showSearchField>
|
||||||
</tal:field><br/><br class="discreet"/>
|
</tal:field><br class="discreet"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<metal:searchInteger define-macro="searchInteger">
|
<metal:searchInteger define-macro="searchInteger">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<label tal:content="python: tool.translate(appyType['label'])"></label><br>
|
||||||
|
<tal:from define="fromName python: '%s*int' % widgetName">
|
||||||
|
<label tal:attributes="for fromName" tal:content="python: tool.translate('search_from')"></label>
|
||||||
|
<input type="text" tal:attributes="name fromName" size="4"/>
|
||||||
|
</tal:from>
|
||||||
|
<tal:to define="toName python: '%s_to' % fieldName">
|
||||||
|
<label tal:attributes="for toName" tal:content="python: tool.translate('search_to')"></label>
|
||||||
|
<input type="text" tal:attributes="name toName" size="4"/>
|
||||||
|
</tal:to><br/>
|
||||||
</metal:searchInteger>
|
</metal:searchInteger>
|
||||||
|
|
||||||
<metal:searchFloat define-macro="searchFloat">
|
<metal:searchFloat define-macro="searchFloat">
|
||||||
|
@ -19,10 +27,11 @@
|
||||||
tal:content="python: tool.truncate(tool.translate('%s_list_%s' % (appyType['label'], v)), 70)">
|
tal:content="python: tool.truncate(tool.translate('%s_list_%s' % (appyType['label'], v)), 70)">
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</tal:selectSearch>
|
</tal:selectSearch><br/>
|
||||||
</metal:searchString>
|
</metal:searchString>
|
||||||
|
|
||||||
<metal:searchBoolean define-macro="searchBoolean" tal:define="typedWidget python:'%s*bool' % widgetName">
|
<metal:searchBoolean define-macro="searchBoolean"
|
||||||
|
tal:define="typedWidget python:'%s*bool' % widgetName">
|
||||||
<label tal:attributes="for widgetName" tal:content="python: tool.translate(appyType['label'])"></label><br>
|
<label tal:attributes="for widgetName" tal:content="python: tool.translate(appyType['label'])"></label><br>
|
||||||
<tal:yes define="valueId python:'%s_yes' % fieldName">
|
<tal:yes define="valueId python:'%s_yes' % fieldName">
|
||||||
<input type="radio" class="noborder" value="True" tal:attributes="name typedWidget; id valueId"/>
|
<input type="radio" class="noborder" value="True" tal:attributes="name typedWidget; id valueId"/>
|
||||||
|
@ -35,29 +44,79 @@
|
||||||
<tal:whatever define="valueId python:'%s_whatever' % fieldName">
|
<tal:whatever define="valueId python:'%s_whatever' % fieldName">
|
||||||
<input type="radio" class="noborder" value="" tal:attributes="name typedWidget; id valueId" checked="checked"/>
|
<input type="radio" class="noborder" value="" tal:attributes="name typedWidget; id valueId" checked="checked"/>
|
||||||
<label tal:attributes="for valueId" tal:content="python: tool.translate('whatever')"></label>
|
<label tal:attributes="for valueId" tal:content="python: tool.translate('whatever')"></label>
|
||||||
</tal:whatever>
|
</tal:whatever><br/>
|
||||||
</metal:searchBoolean>
|
</metal:searchBoolean>
|
||||||
|
|
||||||
<metal:searchDate define-macro="searchDate">
|
<metal:searchDate define-macro="searchDate">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<label tal:content="python: tool.translate(appyType['label'])"></label>
|
||||||
|
<table cellpadding="0" cellspacing="0">
|
||||||
|
<tal:comment replace="nothing">From</tal:comment>
|
||||||
|
<tr tal:define="fromName python: '%s*date' % widgetName">
|
||||||
|
<td width="10px"> </td>
|
||||||
|
<td>
|
||||||
|
<label tal:content="python: tool.translate('search_from')"></label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select tal:attributes="name fromName">
|
||||||
|
<option value="">--</option>
|
||||||
|
<option tal:repeat="value python:range(appyType['startYear'], appyType['endYear']+1)"
|
||||||
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
|
</select> /
|
||||||
|
<select tal:attributes="name python: '%s_from_month' % fieldName">
|
||||||
|
<option value="">--</option>
|
||||||
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 13)]"
|
||||||
|
tal:content="python:tool.getMonthName(value)" tal:attributes="value value"></option>
|
||||||
|
</select> /
|
||||||
|
<select tal:attributes="name python: '%s_from_day' % fieldName">
|
||||||
|
<option value="">--</option>
|
||||||
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 32)]"
|
||||||
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tal:comment replace="nothing">To</tal:comment>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<label tal:content="python: tool.translate('search_to')"></label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select tal:attributes="name python: '%s_to_year' % fieldName">
|
||||||
|
<option value="">--</option>
|
||||||
|
<option tal:repeat="value python:range(appyType['startYear'], appyType['endYear']+1)"
|
||||||
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
|
</select> /
|
||||||
|
<select tal:attributes="name python: '%s_to_month' % fieldName">
|
||||||
|
<option value="">--</option>
|
||||||
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 13)]"
|
||||||
|
tal:content="python:tool.getMonthName(value)" tal:attributes="value value"></option>
|
||||||
|
</select> /
|
||||||
|
<select tal:attributes="name python: '%s_to_day' % fieldName">
|
||||||
|
<option value="">--</option>
|
||||||
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 32)]"
|
||||||
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</metal:searchDate>
|
</metal:searchDate>
|
||||||
|
|
||||||
<metal:searchFile define-macro="searchFile">
|
<metal:searchFile define-macro="searchFile">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<p tal:content="fieldName"></p>
|
||||||
</metal:searchFile>
|
</metal:searchFile>
|
||||||
|
|
||||||
<metal:searchRef define-macro="searchRef">
|
<metal:searchRef define-macro="searchRef">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<p tal:content="fieldName"></p>
|
||||||
</metal:searchRef>
|
</metal:searchRef>
|
||||||
|
|
||||||
<metal:searchComputed define-macro="searchComputed">
|
<metal:searchComputed define-macro="searchComputed">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<p tal:content="fieldName"></p>
|
||||||
</metal:searchComputed>
|
</metal:searchComputed>
|
||||||
|
|
||||||
<metal:searchAction define-macro="searchAction">
|
<metal:searchAction define-macro="searchAction">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<p tal:content="fieldName"></p>
|
||||||
</metal:searchAction>
|
</metal:searchAction>
|
||||||
|
|
||||||
<metal:searchInfo define-macro="searchInfo">
|
<metal:searchInfo define-macro="searchInfo">
|
||||||
<p tal:content="fieldName">Hello</p>
|
<p tal:content="fieldName"></p>
|
||||||
</metal:searchInfo>
|
</metal:searchInfo>
|
||||||
|
|
|
@ -62,6 +62,8 @@ class PoMessage:
|
||||||
SEARCH_OBJECTS = 'Search objects of this type.'
|
SEARCH_OBJECTS = 'Search objects of this type.'
|
||||||
SEARCH_RESULTS = 'Search results'
|
SEARCH_RESULTS = 'Search results'
|
||||||
SEARCH_NEW = 'New search'
|
SEARCH_NEW = 'New search'
|
||||||
|
SEARCH_FROM = 'From'
|
||||||
|
SEARCH_TO = 'to'
|
||||||
WORKFLOW_COMMENT = 'Optional comment'
|
WORKFLOW_COMMENT = 'Optional comment'
|
||||||
WORKFLOW_STATE = 'state'
|
WORKFLOW_STATE = 'state'
|
||||||
DATA_CHANGE = 'Data change'
|
DATA_CHANGE = 'Data change'
|
||||||
|
|
Loading…
Reference in a new issue