Commit 78445246 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 3ecb8c85
......@@ -263,22 +263,33 @@
<th tal:condition="python: show_select_column"\n
class="listbox-table-select-cell">\n
\n
<input class="listbox-check-all"\n
type="image"\n
name="checkAll:method" value="1"\n
alt="Check All" title="Check All"\n
tal:attributes="name string:${field_id}_checkAll:method;\n
src string:${portal_url_string}/images/checkall.png"\n
i18n:domain="ui" i18n:attributes="title" />\n
<button class="listbox-check-all"\n
type="submit"\n
title="Check All"\n
name="listbox_checkAll:method" value="listbox"\n
tal:attributes="value string:${field_id}"\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
<input class="listbox-uncheck-all"\n
type="image" \n
name="uncheckAll:method" value="1"\n
alt="Uncheck All" title="Uncheck All"\n
tal:attributes="src string:${portal_url_string}/images/decheckall.png;\n
name string:${field_id}_uncheckAll:method;"\n
i18n:domain="ui" i18n:attributes="title" /> \n
\n
<button class="listbox-uncheck-all"\n
type="submit"\n
title="Uncheck All"\n
name="listbox_uncheckAll:method" value="listbox"\n
tal:attributes="value string:${field_id}"\n
i18n:domain="ui" i18n:attributes="title">\n
<img class="listbox-uncheck-all"\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
\n
<!-- Label column row -->\n
......
......@@ -198,17 +198,17 @@
\n
<tal:block tal:condition="python: here.current_page > 0">\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
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
i18n:domain="ui" i18n:attributes="title">\n
<span class="image"/>\n
</button>\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
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
i18n:domain="ui" i18n:attributes="title">\n
<span class="image"/>\n
......@@ -223,15 +223,15 @@
\n
<tal:block tal:condition="python: here.current_page < here.total_pages - 1">\n
<button type="submit"\n
title="Next Page" name="nextPage:method" class="listbox_next_page"\n
tal:attributes="name string:${field_id}_nextPage:method;\n
title="Next Page" name="listbox_nextPage:method" value="listbox" class="listbox_next_page"\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
i18n:domain="ui" i18n:attributes="title">\n
<span class="image"/>\n
</button>\n
<button type="submit"\n
title="Last Page" name="lastPage:method" class="listbox_last_page"\n
tal:attributes="name string:${field_id}_lastPage:method;\n
title="Last Page" name="listbox_lastPage:method" value="listbox" class="listbox_last_page"\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
i18n:domain="ui" i18n:attributes="title" >\n
<span class="image"/>\n
......@@ -302,13 +302,19 @@
\n
<!-- Type in listbox 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
name="page_start" onblur="this.value=this.defaultValue"\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
value python:here.current_page + 1;\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>\n
\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
*/\n
\n
function submitAction(form, act) {\n
form.action = act;\n
form.submit();\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.submit();\n
} else {\n
form_or_submit.click();\n
}\n
}\n
\n
// This function will be called when the user click the save button. As \n
......@@ -176,17 +181,22 @@ function fixLeftRightHeightAndFocus(fix_height) {\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
// 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(event.keyCode == 13){\n
if (form == "main_form") {\n
form = document.forms[form]; // backward compatibility\n
}\n
form.action = method_name;\n
if (clear_changed_flag === true) {\n
changed = false;\n
}\n
form.submit();\n
if ($(form_or_submit).is(\'form\')) {\n
form = form_or_submit;\n
if (form == "main_form") {\n
form = document.forms[form]; // backward compatibility\n
}\n
form.action = method_name;\n
form.submit();\n
} else {\n
form_or_submit.click();\n
}\n
event.preventDefault();\n
return false;\n
}\n
......
......@@ -756,10 +756,12 @@ table.listbox input{\n
float:none;\n
}\n
\n
table.listbox input.listbox-check-all,\n
table.listbox input.listbox-uncheck-all,\n
table.listbox button.listbox-check-all,\n
table.listbox button.listbox-uncheck-all,\n
table.listbox input.listbox-select-action{\n
width:auto;\n
padding:0;\n
float:none;\n
}\n
\n
.listbox-table-data-cell > .figure{\n
......
......@@ -2355,7 +2355,7 @@ class ListBoxHTMLRendererLine(ListBoxRendererLine):
request.set('cell', brain)
html_list = []
# Generate page selection methods based on the Listbox id
# Deprecated: Generate page selection methods based on the Listbox id
createFolderMixInPageSelectionMethod(field_id)
# Check is there is a validation error at the level of the listbox
......
......@@ -1788,9 +1788,9 @@ from ZPublisher.mapply import mapply
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]
# Monkey patch FolderMixIn with SelectionTool methods
# kept here for compatibility with previous implementations
# of Listbox HTML renderer. See bellow new implementation
# Monkey patch FolderMixIn with SelectionTool methods, and wrapper methods
# ('listbox_<WRAPPED_METHOD_NAME>()') used to set ListBox properties for
# pagination
for property_id in candidate_method_id_list:
def portal_selection_wrapper(self, wrapper_property_id=property_id, *args, **kw):
"""
......@@ -1807,6 +1807,50 @@ for property_id in candidate_method_id_list:
if security_property is not None:
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):
"""
This method must be called by listbox at rendering time.
......@@ -1818,6 +1862,10 @@ def createFolderMixInPageSelectionMethod(listbox_id):
multiple multi-page listboxes in view mode. It also
opens the way towards multiple editable listboxes in the same
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
test_method_id = "%s_nextPage" % listbox_id
......@@ -1830,6 +1878,13 @@ def createFolderMixInPageSelectionMethod(listbox_id):
"""
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')
request = self.REQUEST
selection_name_property_id = "%s_list_selection_name" % listbox_id
......
......@@ -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>
</td>
<td style="white-space: nowrap; vertical-align: middle; text-align: center;">
<input tal:condition="python: here.current_page > 0"
id="listbox_previous_page" type="image" src="1leftarrowv.png"
title="Previous Page" name="previousPage:method"
<button tal:condition="python: here.current_page > 0"
id="listbox_previous_page" type="submit"
title="Previous Page" name="listbox_previousPage:method" value="listbox"
tal:attributes="id string:${field_id}_previous_page;
name string:${field_id}_previousPage:method;
src string:${portal_url_string}/images/1leftarrowv.png"
i18n:domain="ui" i18n:attributes="title" />
value string:${field_id}"
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 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"
tal:define="lines here/getMaxLineNumber"
tal:attributes="id string:${field_id}_page_selection;
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">
<option value="0"
tal:repeat="p python: range(0, here.total_pages)"
......@@ -166,13 +177,18 @@
</select>
</td>
<td style="white-space: nowrap; vertical-align: middle; text-align: center">
<input tal:condition="python: here.current_page < here.total_pages - 1"
id="listbox_next_page" type="image" src="1rightarrowv.png"
title="Next Page" name="nextPage:method"
<button tal:condition="python: here.current_page < here.total_pages - 1"
id="listbox_next_page" type="submit"
title="Next Page" name="listbox_nextPage:method" value="listbox"
tal:attributes="id string:${field_id}_next_page;
name string:${field_id}_nextPage:method;
src string:${portal_url_string}/images/1rightarrowv.png"
i18n:domain="ui" i18n:attributes="title" />
value string:${field_id}"
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>
</tr>
</table>
......@@ -194,18 +210,33 @@
</td>
<td tal:condition="show_select_column"
class="Data" style="width: 50px; text-align: center; vertical-align: middle">
<input id="listbox_check_all" type="image"
name="checkAll:method" value="1"
src="checkall.png" alt="Check All" title="Check All"
<button id="listbox_check_all" type="submit"
name="listbox_checkAll:method" value="listbox"
title="Check All"
tal:attributes="id string:${field_id}_check_all;
name string:${field_id}_checkAll:method;
src string:${portal_url_string}/images/checkall.png"
i18n:domain="ui" i18n:attributes="title" />&nbsp;<input id="listbox_uncheck_all" type="image" name="uncheckAll:method" value="1"
src="%(portal_url_string)s/images/decheckall.png" style="border: 0" alt="Uncheck All" title="Uncheck All"
value string:${field_id}"
i18n:domain="ui" i18n:attributes="title">
<img class="listbox-check-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;
name string:${field_id}_uncheckAll:method;
src string:${portal_url_string}/images/decheckall.png"
i18n:domain="ui" i18n:attributes="title" />
value string:${field_id}"
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>
<tal:block tal:repeat="value here/getLabelValueList">
<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