Commit da78761d authored by Arnaud Fontaine's avatar Arnaud Fontaine

Backport 6fd21826 ListBox fix from master branch (#KMS-819).

ListBox: Navigation methods should not be created when rendering ListBox (#20161014-741678).

These methods were generated when rendering the ListBox and with the id
of the ListBox in their name. However, a customer reported the following
problem on Accounting Periods ListBox:

  1. Display Accounting Period ListBox on ZEO-1.
       => The ListBox will be rendered and 'listbox_period_list_*' methods will be generated on ZEO-1.
  2.  Click on 'Next Page' button and the user is redirect to ZEO-2 where the ListBox has never been generated.
       => The method does not exist yet and thus a 404 error is raised.

Instead of having one method per ListBox ID, only one is now created (eg
listbox_setPage() for SelectionTool.setPage()) at Zope startup and the
ListBox ID previously defined in the method name is now defined in the
value attributes of the buttons.
parent 0528f9c6
...@@ -264,22 +264,33 @@ ...@@ -264,22 +264,33 @@
<th tal:condition="python: show_select_column"\n <th tal:condition="python: show_select_column"\n
class="listbox-table-select-cell">\n class="listbox-table-select-cell">\n
\n \n
<input class="listbox-check-all"\n <button class="listbox-check-all"\n
type="image"\n type="submit"\n
name="checkAll:method" value="1"\n title="Check All"\n
alt="Check All" title="Check All"\n name="listbox_checkAll:method" value="listbox"\n
tal:attributes="name string:${field_id}_checkAll:method;\n tal:attributes="value string:${field_id}"\n
src string:${portal_url_string}/images/checkall.png"\n i18n:domain="ui" i18n:attributes="title">\n
i18n:domain="ui" i18n:attributes="title" />\n <img class="listbox-check-all"\n
src="images/checkall.png"\n
alt="Check All"\n
title="Check All"\n
tal:attributes="src string:${portal_url_string}/images/checkall.png"\n
i18n:domain="ui" i18n:attributes="title;alt" />\n
</button>\n
&nbsp;\n &nbsp;\n
<input class="listbox-uncheck-all"\n <button class="listbox-uncheck-all"\n
type="image" \n type="submit"\n
name="uncheckAll:method" value="1"\n title="Uncheck All"\n
alt="Uncheck All" title="Uncheck All"\n name="listbox_uncheckAll:method" value="listbox"\n
tal:attributes="src string:${portal_url_string}/images/decheckall.png;\n tal:attributes="value string:${field_id}"\n
name string:${field_id}_uncheckAll:method;"\n i18n:domain="ui" i18n:attributes="title">\n
i18n:domain="ui" i18n:attributes="title" /> \n <img class="listbox-uncheck-all"\n
\n src="images/decheckall.png"\n
alt="Uncheck All"\n
title="Uncheck All"\n
tal:attributes="src string:${portal_url_string}/images/decheckall.png;"\n
i18n:domain="ui" i18n:attributes="title;alt" />\n
</button>\n
</th>\n </th>\n
\n \n
<!-- Label column row -->\n <!-- Label column row -->\n
......
...@@ -199,17 +199,17 @@ ...@@ -199,17 +199,17 @@
\n \n
<tal:block tal:condition="python: here.current_page > 0">\n <tal:block tal:condition="python: here.current_page > 0">\n
<button type="submit"\n <button type="submit"\n
title="First Page" name="firstPage:method"\n title="First Page" name="listbox_firstPage:method" value="listbox"\n
class="listbox_first_page"\n class="listbox_first_page"\n
tal:attributes="name string:${field_id}_firstPage:method;\n tal:attributes="value string:${field_id};\n
class python: test(is_default_listbox_field, \'listbox_first_page\', \'listbox_first_page %s_first_page\' %field_id)"\n class python: test(is_default_listbox_field, \'listbox_first_page\', \'listbox_first_page %s_first_page\' %field_id)"\n
i18n:domain="ui" i18n:attributes="title">\n i18n:domain="ui" i18n:attributes="title">\n
<span class="image"/>\n <span class="image"/>\n
</button>\n </button>\n
<button type="submit"\n <button type="submit"\n
title="Previous Page" name="previousPage:method"\n title="Previous Page" name="listbox_previousPage:method" value="listbox"\n
class="listbox_previous_page"\n class="listbox_previous_page"\n
tal:attributes="name string:${field_id}_previousPage:method;\n tal:attributes="value string:${field_id};\n
class python: test(is_default_listbox_field, \'listbox_previous_page\', \'listbox_previous_page %s_previous_page\' %field_id)"\n class python: test(is_default_listbox_field, \'listbox_previous_page\', \'listbox_previous_page %s_previous_page\' %field_id)"\n
i18n:domain="ui" i18n:attributes="title">\n i18n:domain="ui" i18n:attributes="title">\n
<span class="image"/>\n <span class="image"/>\n
...@@ -224,15 +224,15 @@ ...@@ -224,15 +224,15 @@
\n \n
<tal:block tal:condition="python: here.current_page < here.total_pages - 1">\n <tal:block tal:condition="python: here.current_page < here.total_pages - 1">\n
<button type="submit"\n <button type="submit"\n
title="Next Page" name="nextPage:method" class="listbox_next_page"\n title="Next Page" name="listbox_nextPage:method" value="listbox" class="listbox_next_page"\n
tal:attributes="name string:${field_id}_nextPage:method;\n tal:attributes="value string:${field_id};\n
class python: test(is_default_listbox_field, \'listbox_next_page\', \'listbox_next_page %s_next_page\' %field_id)"\n class python: test(is_default_listbox_field, \'listbox_next_page\', \'listbox_next_page %s_next_page\' %field_id)"\n
i18n:domain="ui" i18n:attributes="title">\n i18n:domain="ui" i18n:attributes="title">\n
<span class="image"/>\n <span class="image"/>\n
</button>\n </button>\n
<button type="submit"\n <button type="submit"\n
title="Last Page" name="lastPage:method" class="listbox_last_page"\n title="Last Page" name="listbox_lastPage:method" value="listbox" class="listbox_last_page"\n
tal:attributes="name string:${field_id}_lastPage:method;\n tal:attributes="value string:${field_id};\n
class python: test(is_default_listbox_field, \'listbox_last_page\', \'listbox_last_page %s_last_page\' %field_id)"\n class python: test(is_default_listbox_field, \'listbox_last_page\', \'listbox_last_page %s_last_page\' %field_id)"\n
i18n:domain="ui" i18n:attributes="title" >\n i18n:domain="ui" i18n:attributes="title" >\n
<span class="image"/>\n <span class="image"/>\n
...@@ -303,13 +303,19 @@ ...@@ -303,13 +303,19 @@
\n \n
<!-- Type in listbox navigation --> \n <!-- Type in listbox navigation --> \n
<tal:block metal:define-macro="type_in_page_navigation">\n <tal:block metal:define-macro="type_in_page_navigation">\n
<input type="submit"\n
id="listbox_setPage"\n
class="hidden_button"\n
name="listbox_setPage:method" value="listbox"\n
tal:attributes="value string:${field_id};\n
id string:${field_id}_setPage" />\n
<input class="listbox_set_page" \n <input class="listbox_set_page" \n
name="page_start" onblur="this.value=this.defaultValue"\n name="page_start" onblur="this.value=this.defaultValue"\n
tal:attributes="name string:${field_id}_page_start;\n tal:attributes="name string:${field_id}_page_start;\n
class python: test(is_default_listbox_field, \'listbox_set_page\', \'listbox_set_page %s_set_page\' %field_id);\n class python: test(is_default_listbox_field, \'listbox_set_page\', \'listbox_set_page %s_set_page\' %field_id);\n
value python:here.current_page + 1;\n value python:here.current_page + 1;\n
size python:len(str(here.total_pages));\n size python:len(str(here.total_pages));\n
onkeypress string:submitFormOnEnter(event, this.form, \'${field_id}_setPage\')" />\n onkeypress string:submitFormOnEnter(event, $$(\'#${field_id}_setPage\'))" />\n
/ <tal:block content="here/total_pages" />\n / <tal:block content="here/total_pages" />\n
</tal:block>\n </tal:block>\n
\n \n
......
...@@ -48,9 +48,14 @@ along with this program; if not, write to the Free Software\n ...@@ -48,9 +48,14 @@ along with this program; if not, write to the Free Software\n
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n
*/\n */\n
\n \n
function submitAction(form, act) {\n function submitAction(form_or_submit, act) {\n
if ($(form_or_submit).is(\'form\')) {\n
form = form_or_submit;\n
form.action = act;\n form.action = act;\n
form.submit();\n form.submit();\n
} else {\n
form_or_submit.click();\n
}\n
}\n }\n
\n \n
// This function will be called when the user click the save button. As \n // This function will be called when the user click the save button. As \n
...@@ -176,17 +181,22 @@ function fixLeftRightHeightAndFocus(fix_height) {\n ...@@ -176,17 +181,22 @@ function fixLeftRightHeightAndFocus(fix_height) {\n
// and modify respective main form action\n // and modify respective main form action\n
// if clear_changed_flag is set to true, changed will be set to false, so no\n // if clear_changed_flag is set to true, changed will be set to false, so no\n
// warning message about unsaved changes will be displayed\n // warning message about unsaved changes will be displayed\n
function submitFormOnEnter(event, form, method_name, clear_changed_flag, element){\n function submitFormOnEnter(event, form_or_submit, method_name, clear_changed_flag, element){\n
if (clear_changed_flag === null){ clear_changed_flag = false; }\n if (clear_changed_flag === null){ clear_changed_flag = false; }\n
if(event.keyCode == 13){\n if(event.keyCode == 13){\n
if (clear_changed_flag === true) {\n
changed = false;\n
}\n
if ($(form_or_submit).is(\'form\')) {\n
form = form_or_submit;\n
if (form == "main_form") {\n if (form == "main_form") {\n
form = document.forms[form]; // backward compatibility\n form = document.forms[form]; // backward compatibility\n
}\n }\n
form.action = method_name;\n form.action = method_name;\n
if (clear_changed_flag === true) {\n
changed = false;\n
}\n
form.submit();\n form.submit();\n
} else {\n
form_or_submit.click();\n
}\n
event.preventDefault();\n event.preventDefault();\n
return false;\n return false;\n
}\n }\n
......
...@@ -756,10 +756,12 @@ table.listbox input{\n ...@@ -756,10 +756,12 @@ table.listbox input{\n
float:none;\n float:none;\n
}\n }\n
\n \n
table.listbox input.listbox-check-all,\n table.listbox button.listbox-check-all,\n
table.listbox input.listbox-uncheck-all,\n table.listbox button.listbox-uncheck-all,\n
table.listbox input.listbox-select-action{\n table.listbox input.listbox-select-action{\n
width:auto;\n width:auto;\n
padding:0;\n
float:none;\n
}\n }\n
\n \n
.listbox-table-data-cell > .figure{\n .listbox-table-data-cell > .figure{\n
......
...@@ -2366,7 +2366,7 @@ class ListBoxHTMLRendererLine(ListBoxRendererLine): ...@@ -2366,7 +2366,7 @@ class ListBoxHTMLRendererLine(ListBoxRendererLine):
request.set('cell', brain) request.set('cell', brain)
html_list = [] html_list = []
# Generate page selection methods based on the Listbox id # Deprecated: Generate page selection methods based on the Listbox id
createFolderMixInPageSelectionMethod(field_id) createFolderMixInPageSelectionMethod(field_id)
for (original_value, processed_value), (sql, title), alias \ for (original_value, processed_value), (sql, title), alias \
......
...@@ -1787,9 +1787,9 @@ from ZPublisher.mapply import mapply ...@@ -1787,9 +1787,9 @@ from ZPublisher.mapply import mapply
method_id_filter_list = [x for x in FolderMixIn.__dict__ if callable(getattr(FolderMixIn, x))] method_id_filter_list = [x for x in FolderMixIn.__dict__ if callable(getattr(FolderMixIn, x))]
candidate_method_id_list = [x for x in SelectionTool.__dict__ if callable(getattr(SelectionTool, x)) and not x.startswith('_') and not x.endswith('__roles__') and x not in method_id_filter_list] candidate_method_id_list = [x for x in SelectionTool.__dict__ if callable(getattr(SelectionTool, x)) and not x.startswith('_') and not x.endswith('__roles__') and x not in method_id_filter_list]
# Monkey patch FolderMixIn with SelectionTool methods # Monkey patch FolderMixIn with SelectionTool methods, and wrapper methods
# kept here for compatibility with previous implementations # ('listbox_<WRAPPED_METHOD_NAME>()') used to set ListBox properties for
# of Listbox HTML renderer. See bellow new implementation # pagination
for property_id in candidate_method_id_list: for property_id in candidate_method_id_list:
def portal_selection_wrapper(self, wrapper_property_id=property_id, *args, **kw): def portal_selection_wrapper(self, wrapper_property_id=property_id, *args, **kw):
""" """
...@@ -1806,6 +1806,50 @@ for property_id in candidate_method_id_list: ...@@ -1806,6 +1806,50 @@ for property_id in candidate_method_id_list:
if security_property is not None: if security_property is not None:
setattr(FolderMixIn, security_property_id, security_property) setattr(FolderMixIn, security_property_id, security_property)
def portal_selection_wrapper(self, wrapper_property_id=property_id, *args, **kw):
"""
Wrapper method for SelectionTool.
"""
portal_selection = getToolByName(self, 'portal_selections')
request = self.REQUEST
try:
listbox_id = request.form['listbox_%s' % wrapper_property_id]
except KeyError:
# Backward-compatibility: Should be removed as soon as
# createFolderMixInPageSelectionMethod has been removed
warnings.warn(
"DEPRECATED: listbox_%s: 'value' attribute of the submit button "
"should be set to the ListBox ID and the method name 'listbox_%s" %
(wrapper_property_id, wrapper_property_id),
DeprecationWarning)
listbox_id = 'listbox'
selection_name_property_id = "%s_list_selection_name" % listbox_id
listbox_uid_property_id = "%s_uid" % listbox_id
list_start_property_id = "%s_list_start" % listbox_id
page_start_property_id = "%s_page_start" % listbox_id
# Rename request parameters
if request.has_key(selection_name_property_id):
request.form['list_selection_name'] = request[selection_name_property_id]
if request.has_key(listbox_uid_property_id):
request.form['listbox_uid'] = request[listbox_uid_property_id]
if request.has_key(list_start_property_id):
request.form['list_start'] = request[list_start_property_id]
if request.has_key(page_start_property_id):
request.form['page_start'] = request[page_start_property_id]
# Call the wrapper
method = getattr(portal_selection, wrapper_property_id)
return mapply(method, positional=args, keyword=request,
context=self, bind=1)
new_property_id = "listbox_%s" % property_id
setattr(FolderMixIn, new_property_id, portal_selection_wrapper)
security_property_id = '%s__roles__' % (property_id, )
security_property = getattr(SelectionTool, security_property_id, None)
if security_property is not None:
new_security_property_id = '%s__roles__' % (new_property_id, )
setattr(FolderMixIn, new_security_property_id, security_property)
def createFolderMixInPageSelectionMethod(listbox_id): def createFolderMixInPageSelectionMethod(listbox_id):
""" """
This method must be called by listbox at rendering time. This method must be called by listbox at rendering time.
...@@ -1817,6 +1861,10 @@ def createFolderMixInPageSelectionMethod(listbox_id): ...@@ -1817,6 +1861,10 @@ def createFolderMixInPageSelectionMethod(listbox_id):
multiple multi-page listboxes in view mode. It also multiple multi-page listboxes in view mode. It also
opens the way towards multiple editable listboxes in the same opens the way towards multiple editable listboxes in the same
page although this is something which we can not recommend. page although this is something which we can not recommend.
Deprecated because these methods are generated when rendering the
ListBox. Therefore, they are only available on the ZEO client
where it has been rendered but not the other ZEO clients.
""" """
# Immediately return in the method already exists # Immediately return in the method already exists
test_method_id = "%s_nextPage" % listbox_id test_method_id = "%s_nextPage" % listbox_id
...@@ -1829,6 +1877,13 @@ def createFolderMixInPageSelectionMethod(listbox_id): ...@@ -1829,6 +1877,13 @@ def createFolderMixInPageSelectionMethod(listbox_id):
""" """
Wrapper method for SelectionTool. Wrapper method for SelectionTool.
""" """
warnings.warn(
"DEPRECATED: %s_%s: The ListBox ID must not be contained anymore in the "
"method name, but instead be in the 'value' attribute of the submit "
"button and the method name should be 'listbox_%s'" %
(wrapper_listbox_id, wrapper_property_id, wrapper_method_id),
DeprecationWarning)
portal_selection = getToolByName(self, 'portal_selections') portal_selection = getToolByName(self, 'portal_selections')
request = self.REQUEST request = self.REQUEST
selection_name_property_id = "%s_list_selection_name" % listbox_id selection_name_property_id = "%s_list_selection_name" % listbox_id
......
...@@ -143,20 +143,31 @@ ...@@ -143,20 +143,31 @@
i18n:translate="" i18n:domain="ui"> - <tal:block tal:replace="python: len(here.getCheckedUidList())" i18n:name="number">0</tal:block> item(s) selected</span> i18n:translate="" i18n:domain="ui"> - <tal:block tal:replace="python: len(here.getCheckedUidList())" i18n:name="number">0</tal:block> item(s) selected</span>
</td> </td>
<td style="white-space: nowrap; vertical-align: middle; text-align: center;"> <td style="white-space: nowrap; vertical-align: middle; text-align: center;">
<input tal:condition="python: here.current_page > 0" <button tal:condition="python: here.current_page > 0"
id="listbox_previous_page" type="image" src="1leftarrowv.png" id="listbox_previous_page" type="submit"
title="Previous Page" name="previousPage:method" title="Previous Page" name="listbox_previousPage:method" value="listbox"
tal:attributes="id string:${field_id}_previous_page; tal:attributes="id string:${field_id}_previous_page;
name string:${field_id}_previousPage:method; value string:${field_id}"
src string:${portal_url_string}/images/1leftarrowv.png" i18n:domain="ui" i18n:attributes="title">
i18n:domain="ui" i18n:attributes="title" /> <img src="1leftarrowv.png"
alt="Previous Page"
title="Previous Page"
tal:attributes="src string:${portal_url_string}/images/1leftarrowv.png"
i18n:domain="ui" i18n:attributes="title;alt" />
</button>
</td> </td>
<td style="white-space: nowrap; vertical-align: middle; text-align: center"> <td style="white-space: nowrap; vertical-align: middle; text-align: center">
<input type="submit"
id="listbox_setPage"
class="hidden_button"
name="listbox_setPage:method" value="listbox"
tal:attributes="value string:${field_id};
id string:${field_id}_setPage" />
<select id="listbox_page_selection" name="list_start" title="Change Page" size="1" <select id="listbox_page_selection" name="list_start" title="Change Page" size="1"
tal:define="lines here/getMaxLineNumber" tal:define="lines here/getMaxLineNumber"
tal:attributes="id string:${field_id}_page_selection; tal:attributes="id string:${field_id}_page_selection;
name string:${field_id}_list_start; name string:${field_id}_list_start;
onchange string:submitAction(this.form, '${context_url}/${field_id}_setPage')" onchange string:submitAction($$('#${field_id}_setPage'))"
i18n:domain="ui" i18n:attributes="title"> i18n:domain="ui" i18n:attributes="title">
<option value="0" <option value="0"
tal:repeat="p python: range(0, here.total_pages)" tal:repeat="p python: range(0, here.total_pages)"
...@@ -166,13 +177,18 @@ ...@@ -166,13 +177,18 @@
</select> </select>
</td> </td>
<td style="white-space: nowrap; vertical-align: middle; text-align: center"> <td style="white-space: nowrap; vertical-align: middle; text-align: center">
<input tal:condition="python: here.current_page < here.total_pages - 1" <button tal:condition="python: here.current_page < here.total_pages - 1"
id="listbox_next_page" type="image" src="1rightarrowv.png" id="listbox_next_page" type="submit"
title="Next Page" name="nextPage:method" title="Next Page" name="listbox_nextPage:method" value="listbox"
tal:attributes="id string:${field_id}_next_page; tal:attributes="id string:${field_id}_next_page;
name string:${field_id}_nextPage:method; value string:${field_id}"
src string:${portal_url_string}/images/1rightarrowv.png" i18n:domain="ui" i18n:attributes="title">
i18n:domain="ui" i18n:attributes="title" /> <img src="1rightarrowv.png"
alt="Next Page"
title="Next Page"
tal:attributes="src string:${portal_url_string}/images/1rightarrowv.png"
i18n:domain="ui" i18n:attributes="title;alt" />
</button>
</td> </td>
</tr> </tr>
</table> </table>
...@@ -194,18 +210,33 @@ ...@@ -194,18 +210,33 @@
</td> </td>
<td tal:condition="show_select_column" <td tal:condition="show_select_column"
class="Data" style="width: 50px; text-align: center; vertical-align: middle"> class="Data" style="width: 50px; text-align: center; vertical-align: middle">
<input id="listbox_check_all" type="image" <button id="listbox_check_all" type="submit"
name="checkAll:method" value="1" name="listbox_checkAll:method" value="listbox"
src="checkall.png" alt="Check All" title="Check All" title="Check All"
tal:attributes="id string:${field_id}_check_all; tal:attributes="id string:${field_id}_check_all;
name string:${field_id}_checkAll:method; value string:${field_id}"
src string:${portal_url_string}/images/checkall.png" i18n:domain="ui" i18n:attributes="title">
i18n:domain="ui" i18n:attributes="title" />&nbsp;<input id="listbox_uncheck_all" type="image" name="uncheckAll:method" value="1" <img class="listbox-check-all"
src="%(portal_url_string)s/images/decheckall.png" style="border: 0" alt="Uncheck All" title="Uncheck All" src="images/checkall.png"
alt="Check All"
title="Check All"
tal:attributes="src string:${portal_url_string}/images/checkall.png"
i18n:domain="ui" i18n:attributes="title;alt" />
</button>
&nbsp;
<button id="listbox_uncheck_all" type="submit"
name="listbox_uncheckAll:method" value="listbox"
title="Uncheck All"
tal:attributes="id string:${field_id}_uncheck_all; tal:attributes="id string:${field_id}_uncheck_all;
name string:${field_id}_uncheckAll:method; value string:${field_id}"
src string:${portal_url_string}/images/decheckall.png" i18n:domain="ui" i18n:attributes="title">
i18n:domain="ui" i18n:attributes="title" /> <img class="listbox-uncheck-all"
src="images/decheckall.png"
alt="Uncheck All"
title="Uncheck All"
tal:attributes="src string:${portal_url_string}/images/decheckall.png;"
i18n:domain="ui" i18n:attributes="title;alt" />
</button>
</td> </td>
<tal:block tal:repeat="value here/getLabelValueList"> <tal:block tal:repeat="value here/getLabelValueList">
<tal:block tal:define="sql python: value[0]; <tal:block tal:define="sql python: value[0];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment