Ready for an 'advanced search' functionality.
This commit is contained in:
parent
2ff08258bc
commit
f8baeee4f7
|
@ -90,47 +90,52 @@ class Generator(AbstractGenerator):
|
||||||
# Some global i18n messages
|
# Some global i18n messages
|
||||||
poMsg = msg(app, '', app); poMsg.produceNiceDefault()
|
poMsg = msg(app, '', app); poMsg.produceNiceDefault()
|
||||||
self.labels += [poMsg,
|
self.labels += [poMsg,
|
||||||
msg('workflow_state', '', msg.WORKFLOW_STATE),
|
msg('workflow_state', '', msg.WORKFLOW_STATE),
|
||||||
msg('data_change', '', msg.DATA_CHANGE),
|
msg('data_change', '', msg.DATA_CHANGE),
|
||||||
msg('modified_field', '', msg.MODIFIED_FIELD),
|
msg('modified_field', '', msg.MODIFIED_FIELD),
|
||||||
msg('previous_value', '', msg.PREVIOUS_VALUE),
|
msg('previous_value', '', msg.PREVIOUS_VALUE),
|
||||||
msg('phase', '', msg.PHASE),
|
msg('phase', '', msg.PHASE),
|
||||||
msg('root_type', '', msg.ROOT_TYPE),
|
msg('root_type', '', msg.ROOT_TYPE),
|
||||||
msg('workflow_comment', '', msg.WORKFLOW_COMMENT),
|
msg('workflow_comment', '', msg.WORKFLOW_COMMENT),
|
||||||
msg('choose_a_value', '', msg.CHOOSE_A_VALUE),
|
msg('choose_a_value', '', msg.CHOOSE_A_VALUE),
|
||||||
msg('choose_a_doc', '', msg.CHOOSE_A_DOC),
|
msg('choose_a_doc', '', msg.CHOOSE_A_DOC),
|
||||||
msg('min_ref_violated', '', msg.MIN_REF_VIOLATED),
|
msg('min_ref_violated', '', msg.MIN_REF_VIOLATED),
|
||||||
msg('max_ref_violated', '', msg.MAX_REF_VIOLATED),
|
msg('max_ref_violated', '', msg.MAX_REF_VIOLATED),
|
||||||
msg('no_ref', '', msg.REF_NO),
|
msg('no_ref', '', msg.REF_NO),
|
||||||
msg('add_ref', '', msg.REF_ADD),
|
msg('add_ref', '', msg.REF_ADD),
|
||||||
msg('ref_name', '', msg.REF_NAME),
|
msg('ref_name', '', msg.REF_NAME),
|
||||||
msg('ref_actions', '', msg.REF_ACTIONS),
|
msg('ref_actions', '', msg.REF_ACTIONS),
|
||||||
msg('move_up', '', msg.REF_MOVE_UP),
|
msg('move_up', '', msg.REF_MOVE_UP),
|
||||||
msg('move_down', '', msg.REF_MOVE_DOWN),
|
msg('move_down', '', msg.REF_MOVE_DOWN),
|
||||||
msg('query_create', '', msg.QUERY_CREATE),
|
msg('query_create', '', msg.QUERY_CREATE),
|
||||||
msg('query_import', '', msg.QUERY_IMPORT),
|
msg('query_import', '', msg.QUERY_IMPORT),
|
||||||
msg('query_no_result', '', msg.QUERY_NO_RESULT),
|
msg('query_no_result', '', msg.QUERY_NO_RESULT),
|
||||||
msg('query_consult_all', '', msg.QUERY_CONSULT_ALL),
|
msg('query_consult_all', '', msg.QUERY_CONSULT_ALL),
|
||||||
msg('import_title', '', msg.IMPORT_TITLE),
|
msg('import_title', '', msg.IMPORT_TITLE),
|
||||||
msg('import_show_hide', '', msg.IMPORT_SHOW_HIDE),
|
msg('import_show_hide', '', msg.IMPORT_SHOW_HIDE),
|
||||||
msg('import_already', '', msg.IMPORT_ALREADY),
|
msg('import_already', '', msg.IMPORT_ALREADY),
|
||||||
msg('import_many', '', msg.IMPORT_MANY),
|
msg('import_many', '', msg.IMPORT_MANY),
|
||||||
msg('import_done', '', msg.IMPORT_DONE),
|
msg('import_done', '', msg.IMPORT_DONE),
|
||||||
msg('ref_invalid_index', '', msg.REF_INVALID_INDEX),
|
msg('search_title', '', msg.SEARCH_TITLE),
|
||||||
msg('bad_int', '', msg.BAD_INT),
|
msg('search_button', '', msg.SEARCH_BUTTON),
|
||||||
msg('bad_float', '', msg.BAD_FLOAT),
|
msg('search_objects', '', msg.SEARCH_OBJECTS),
|
||||||
msg('bad_email', '', msg.BAD_EMAIL),
|
msg('search_results', '', msg.SEARCH_RESULTS),
|
||||||
msg('bad_url', '', msg.BAD_URL),
|
msg('search_results_descr', '', ' '),
|
||||||
msg('bad_alphanumeric', '', msg.BAD_ALPHANUMERIC),
|
msg('ref_invalid_index', '', msg.REF_INVALID_INDEX),
|
||||||
msg('select_delesect', '', msg.SELECT_DESELECT),
|
msg('bad_int', '', msg.BAD_INT),
|
||||||
msg('no_elem_selected', '', msg.NO_SELECTION),
|
msg('bad_float', '', msg.BAD_FLOAT),
|
||||||
msg('delete_confirm', '', msg.DELETE_CONFIRM),
|
msg('bad_email', '', msg.BAD_EMAIL),
|
||||||
msg('delete_done', '', msg.DELETE_DONE),
|
msg('bad_url', '', msg.BAD_URL),
|
||||||
msg('goto_first', '', msg.GOTO_FIRST),
|
msg('bad_alphanumeric', '', msg.BAD_ALPHANUMERIC),
|
||||||
msg('goto_previous', '', msg.GOTO_PREVIOUS),
|
msg('select_delesect', '', msg.SELECT_DESELECT),
|
||||||
msg('goto_next', '', msg.GOTO_NEXT),
|
msg('no_elem_selected', '', msg.NO_SELECTION),
|
||||||
msg('goto_last', '', msg.GOTO_LAST),
|
msg('delete_confirm', '', msg.DELETE_CONFIRM),
|
||||||
msg('goto_source', '', msg.GOTO_SOURCE),
|
msg('delete_done', '', msg.DELETE_DONE),
|
||||||
|
msg('goto_first', '', msg.GOTO_FIRST),
|
||||||
|
msg('goto_previous', '', msg.GOTO_PREVIOUS),
|
||||||
|
msg('goto_next', '', msg.GOTO_NEXT),
|
||||||
|
msg('goto_last', '', msg.GOTO_LAST),
|
||||||
|
msg('goto_source', '', msg.GOTO_SOURCE),
|
||||||
]
|
]
|
||||||
# Create basic files (config.py, Install.py, etc)
|
# Create basic files (config.py, Install.py, etc)
|
||||||
self.generateTool()
|
self.generateTool()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import re, os, os.path, Cookie
|
import re, os, os.path, Cookie
|
||||||
|
from appy.gen import Type
|
||||||
from appy.gen.utils import FieldDescr, SomeObjects
|
from appy.gen.utils import FieldDescr, SomeObjects
|
||||||
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
|
||||||
|
@ -111,12 +112,19 @@ class ToolMixin(AbstractMixin):
|
||||||
'''Executes a query on a given p_contentType (or several, separated
|
'''Executes a query on a given p_contentType (or several, separated
|
||||||
with commas) in Plone's portal_catalog. Portal types are from the
|
with commas) in Plone's portal_catalog. Portal types are from the
|
||||||
flavour numbered p_flavourNumber. If p_searchName is specified, it
|
flavour numbered p_flavourNumber. If p_searchName is specified, it
|
||||||
corresponds to a search defined on p_contentType: additional search
|
corresponds to:
|
||||||
criteria will be added to the query. We will retrieve objects from
|
1) a search defined on p_contentType: additional search criteria
|
||||||
p_startNumber. If p_search is defined, it corresponds to a custom
|
will be added to the query, or;
|
||||||
Search instance (instead of a predefined named search like in
|
2) "_advanced": in this case, additional search criteria will also
|
||||||
p_searchName). If both p_searchName and p_search are given, p_search
|
be added to the query, but those criteria come from the session
|
||||||
is ignored. This method returns a list of objects in the form of the
|
and were created from search.pt.
|
||||||
|
|
||||||
|
We will retrieve objects from p_startNumber. If p_search is defined,
|
||||||
|
it corresponds to a custom Search instance (instead of a predefined
|
||||||
|
named search like in p_searchName). If both p_searchName and p_search
|
||||||
|
are given, p_search is ignored.
|
||||||
|
|
||||||
|
This method returns a list of objects in the form of the
|
||||||
__dict__ attribute of an instance of SomeObjects (see in
|
__dict__ attribute of an instance of SomeObjects (see in
|
||||||
appy.gen.utils). We return the __dict__ attribute instead of real
|
appy.gen.utils). We return the __dict__ attribute instead of real
|
||||||
instance: that way, it can be used in ZPTs without security problems.
|
instance: that way, it can be used in ZPTs without security problems.
|
||||||
|
@ -143,8 +151,9 @@ class ToolMixin(AbstractMixin):
|
||||||
# In this case, contentType must contain a single content type.
|
# In this case, contentType must contain a single content type.
|
||||||
appyClass = self.getAppyClass(contentType)
|
appyClass = self.getAppyClass(contentType)
|
||||||
if searchName:
|
if searchName:
|
||||||
search = ArchetypesClassDescriptor.getSearch(
|
if searchName != '_advanced':
|
||||||
appyClass, searchName)
|
search = ArchetypesClassDescriptor.getSearch(
|
||||||
|
appyClass, searchName)
|
||||||
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():
|
||||||
|
@ -362,6 +371,26 @@ class ToolMixin(AbstractMixin):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def getSearchableFields(self, contentType):
|
||||||
|
'''Returns the list of fields that may be searched on objects on type
|
||||||
|
p_contentType (=indexed fields).'''
|
||||||
|
appyClass = self.getAppyClass(contentType)
|
||||||
|
res = []
|
||||||
|
for attrName in dir(appyClass):
|
||||||
|
attr = getattr(appyClass, attrName)
|
||||||
|
if isinstance(attr, Type) and attr.indexed:
|
||||||
|
dAttr = self._appy_getTypeAsDict(attrName, attr, appyClass)
|
||||||
|
res.append((attrName, dAttr))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def onSearchObjects(self):
|
||||||
|
'''This method is called when the user triggers a search from
|
||||||
|
search.pt.'''
|
||||||
|
rq = self.REQUEST
|
||||||
|
backUrl = '%s/query?type_name=%s&flavourNumber=%d&search=_advanced' % \
|
||||||
|
(os.path.dirname(rq['URL']), rq['type_name'], rq['flavourNumber'])
|
||||||
|
return self.goto(backUrl)
|
||||||
|
|
||||||
def getJavascriptMessages(self):
|
def getJavascriptMessages(self):
|
||||||
'''Returns the translated version of messages that must be shown in
|
'''Returns the translated version of messages that must be shown in
|
||||||
Javascript popups.'''
|
Javascript popups.'''
|
||||||
|
|
|
@ -639,7 +639,7 @@
|
||||||
startNumber request/startNumber|python:'0';
|
startNumber request/startNumber|python:'0';
|
||||||
startNumber python: int(startNumber);
|
startNumber python: int(startNumber);
|
||||||
searchName request/search;
|
searchName request/search;
|
||||||
searchLabel python: '%s_search_%s' % (contentType, searchName);
|
searchLabel python: test(searchName=='_advanced', 'search_results', '%s_search_%s' % (contentType, searchName));
|
||||||
searchDescr python: '%s_descr' % searchLabel;
|
searchDescr python: '%s_descr' % searchLabel;
|
||||||
severalTypes python: contentType and (contentType.find(',') != -1);
|
severalTypes python: contentType and (contentType.find(',') != -1);
|
||||||
queryResult python: tool.executeQuery(contentType, flavourNumber, searchName, startNumber, remember=True);
|
queryResult python: tool.executeQuery(contentType, flavourNumber, searchName, startNumber, remember=True);
|
||||||
|
@ -909,6 +909,11 @@
|
||||||
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/import?type_name=%s\'' % (appFolder.absolute_url(), rootClass);
|
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/import?type_name=%s\'' % (appFolder.absolute_url(), rootClass);
|
||||||
src string: $portal_url/skyn/import.png;
|
src string: $portal_url/skyn/import.png;
|
||||||
title python: tool.translate('query_import')"/>
|
title python: tool.translate('query_import')"/>
|
||||||
|
<tal:comment replace="nothing">Search objects of this type (todo: update flavourNumber)</tal:comment>
|
||||||
|
<img style="cursor:pointer"
|
||||||
|
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/search?type_name=%s&flavourNumber=1\'' % (appFolder.absolute_url(), rootClass);
|
||||||
|
src string: $portal_url/skyn/search.gif;
|
||||||
|
title python: tool.translate('search_objects')"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
BIN
gen/plone25/skin/search.gif
Executable file
BIN
gen/plone25/skin/search.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 433 B |
44
gen/plone25/skin/search.pt
Normal file
44
gen/plone25/skin/search.pt
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
|
||||||
|
lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"
|
||||||
|
xmlns:metal="http://xml.zope.org/namespaces/metal"
|
||||||
|
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
||||||
|
metal:use-macro="here/main_template/macros/master">
|
||||||
|
|
||||||
|
<tal:comment replace="nothing">Disable standard Plone green tabs</tal:comment>
|
||||||
|
<div metal:fill-slot="top_slot">
|
||||||
|
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
||||||
|
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
|
||||||
|
<body>
|
||||||
|
<metal:fill fill-slot="main"
|
||||||
|
tal:define="appFolder context/getParentNode;
|
||||||
|
contentType request/type_name;
|
||||||
|
tool python: portal.get('portal_%s' % appFolder.id.lower());
|
||||||
|
searchableFields python: tool.getSearchableFields(contentType)">
|
||||||
|
|
||||||
|
<tal:comment replace="nothing">Search title</tal:comment>
|
||||||
|
<h1 tal:content="python: tool.translate('search_title')"></h1><br/>
|
||||||
|
|
||||||
|
<tal:comment replace="nothing">Form for searching objects of request/type_name.</tal:comment>
|
||||||
|
<form name="search" tal:attributes="action python: appFolder.absolute_url()+'/skyn/do'" method="post">
|
||||||
|
<input type="hidden" name="action" value="SearchObjects"/>
|
||||||
|
<input type="hidden" name="type_name" tal:attributes="value contentType"/>
|
||||||
|
<input type="hidden" name="flavourNumber:int" tal:attributes="value request/flavourNumber"/>
|
||||||
|
|
||||||
|
<tal:searchFields repeat="searchField searchableFields">
|
||||||
|
<tal:showSearchField define="fieldName python:searchField[0];
|
||||||
|
appyType python:searchField[1]">
|
||||||
|
<metal:searchField use-macro="python: appFolder.skyn.widgets.macros.get('search%s' % searchField[1]['type'])"/>
|
||||||
|
</tal:showSearchField>
|
||||||
|
</tal:searchFields>
|
||||||
|
|
||||||
|
<tal:comment replace="nothing">Submit button</tal:comment>
|
||||||
|
<p align="right"><br/>
|
||||||
|
<input type="submit" tal:attributes="value python:tool.translate('search_button')"/>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</metal:fill>
|
||||||
|
</body>
|
||||||
|
</html>
|
39
gen/plone25/skin/widgets.pt
Normal file
39
gen/plone25/skin/widgets.pt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<metal:searchInteger define-macro="searchInteger">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchInteger>
|
||||||
|
|
||||||
|
<metal:searchFloat define-macro="searchFloat">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchFloat>
|
||||||
|
|
||||||
|
<metal:searchString define-macro="searchString">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchString>
|
||||||
|
|
||||||
|
<metal:searchBoolean define-macro="searchBoolean">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchBoolean>
|
||||||
|
|
||||||
|
<metal:searchDate define-macro="searchDate">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchDate>
|
||||||
|
|
||||||
|
<metal:searchFile define-macro="searchFile">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchFile>
|
||||||
|
|
||||||
|
<metal:searchRef define-macro="searchRef">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchRef>
|
||||||
|
|
||||||
|
<metal:searchComputed define-macro="searchComputed">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchComputed>
|
||||||
|
|
||||||
|
<metal:searchAction define-macro="searchAction">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchAction>
|
||||||
|
|
||||||
|
<metal:searchInfo define-macro="searchInfo">
|
||||||
|
<p tal:content="fieldName">Hello</p>
|
||||||
|
</metal:searchInfo>
|
|
@ -57,6 +57,10 @@ class PoMessage:
|
||||||
IMPORT_ALREADY = 'Already imported.'
|
IMPORT_ALREADY = 'Already imported.'
|
||||||
IMPORT_MANY = 'Import selected elements'
|
IMPORT_MANY = 'Import selected elements'
|
||||||
IMPORT_DONE = 'Import terminated successfully.'
|
IMPORT_DONE = 'Import terminated successfully.'
|
||||||
|
SEARCH_TITLE = 'Advanced search'
|
||||||
|
SEARCH_BUTTON = 'Search'
|
||||||
|
SEARCH_OBJECTS = 'Search objects of this type.'
|
||||||
|
SEARCH_RESULTS = 'Search results'
|
||||||
WORKFLOW_COMMENT = 'Optional comment'
|
WORKFLOW_COMMENT = 'Optional comment'
|
||||||
WORKFLOW_STATE = 'state'
|
WORKFLOW_STATE = 'state'
|
||||||
DATA_CHANGE = 'Data change'
|
DATA_CHANGE = 'Data change'
|
||||||
|
|
2
version.py
Normal file
2
version.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
short='dev'
|
||||||
|
verbose='dev'
|
Loading…
Reference in a new issue