Skip to content

Commit

Permalink
Merge branch 'master' into issue_562
Browse files Browse the repository at this point in the history
  • Loading branch information
dataflake committed Apr 27, 2019
2 parents cf7d219 + 2f1362d commit 2a002ce
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 58 deletions.
84 changes: 41 additions & 43 deletions docs/zdgbook/ObjectPublishing.rst
Expand Up @@ -832,97 +832,95 @@ Similarly, you might want to have multiple submit buttons which invoke
a different method for each button.

The publisher provides a way to select methods using form variables
through use of the *method* argument type. The method type allows the
request 'PATH_INFO' to be augmented using information from a form item
name or value.

If the name of a form field is ':method', then the value of the field
is added to 'PATH_INFO'. For example, if the original 'PATH_INFO' is
'foo/bar' and the value of a ':method' field is 'x/y', then
'PATH_INFO' is transformed to 'foo/bar/x/y'. This is useful when
through the use of the ``method`` argument type. The method type allows
the request variable ``PATH_INFO`` to be augmented using information
from a form item's name or value.

If the name of a form field is ``:method``, then the value of the field
is added to ``PATH_INFO``. For example, if the original ``PATH_INFO``
is ``foo/bar`` and the value of a ``:method`` field is ``x/y``, then
``PATH_INFO`` is transformed to ``foo/bar/x/y``. This is useful when
presenting a select list. Method names can be placed in the select
option values.

If the name of a form field ends in ':method' then the part of the
name before ':method' is added to 'PATH_INFO'. For example, if the
original 'PATH_INFO' is 'foo/bar' and there is a 'x/y:method' field,
then 'PATH_INFO' is transformed to 'foo/bar/x/y'. In this case, the
form value is ignored. This is useful for mapping submit buttons to
methods, since submit button values are displayed and should,
therefore, not contain method names.
If the name of a form field **ends** in ``:method`` then the part of
the name before ``:method`` is added to ``PATH_INFO``. For example, if
the original ``PATH_INFO`` is ``foo/bar`` and there is a ``x/y:method``
field, then ``PATH_INFO`` is transformed to ``foo/bar/x/y``. In this
case, the form value is ignored. This is useful for mapping submit
buttons to methods, since submit button values are displayed and
should therefore not contain method names.

Only one method field should be provided. If more than one method
field is included in the request, the behavior is undefined.

Record Arguments
----------------

Sometimes you may wish to consolidate form data into a structure
rather than pass arguments individually. Record arguments allow you
rather than pass arguments individually. **Record arguments** allow you
to do this.

The 'record' type converter allows you to combine multiple form
The ``record`` type converter allows you to combine multiple form
variables into a single input variable. For example::

<input name="date.year:record:int">
<input name="date.month:record:int">
<input name="date.day:record:int">

This form will result in a single variable, 'date', with
attributes 'year', 'month', and 'day'.
This form will result in a single variable, ``date``, with the
attributes ``year``, ``month``, and ``day``.

You can skip empty record elements with the 'ignore_empty' converter.
You can skip empty record elements with the ``ignore_empty`` converter.
For example::

<input type="text" name="person.email:record:ignore_empty">

When the email form field is left blank the publisher skips over the
variable rather than returning a null string as its value. When the
record 'person' is returned it will not have an 'email' attribute if
the user did not enter one.
variable rather than returning an empty string as its value. When the
record ``person`` is returned it will not have an ``email`` attribute
if the user did not enter one.

You can also provide default values for record elements with the
'default' converter. For example::
``default`` converter. For example::

<input type="hidden"
name="pizza.toppings:record:list:default"
value="All">
<select multiple name="pizza.toppings:record:list:ignore_empty">
<option>Cheese</option>
<option>Onions</option>
<option>Anchovies</option>
<option>Olives</option>
<option>Garlic<option>
<option>Cheese</option>
<option>Onions</option>
<option>Anchovies</option>
<option>Olives</option>
<option>Garlic<option>
</select>

The 'default' type allows a specified value to be inserted when the
The ``default`` type allows a specified value to be inserted when the
form field is left blank. In the above example, if the user does not
select values from the list of toppings, the default value will be
used. The record 'pizza' will have the attribute 'toppings' and its
used. The record ``pizza`` will have the attribute ``toppings`` and its
value will be the list containing the word "All" (if the field is
empty) or a list containing the selected toppings.

You can even marshal large amounts of form data into multiple records
with the 'records' type converter. Here's an example::
with the ``records`` type converter. Here's an example::

<h2>Member One</h2>
Name:
<input type="text" name="members.name:records"><BR>
<input type="text" name="members.name:records"><br>
Email:
<input type="text" name="members.email:records"><BR>
<input type="text" name="members.email:records"><br>
Age:
<input type="text" name="members.age:int:records"><BR>
<input type="text" name="members.age:int:records"><br>

<H2>Member Two</H2>
<h2>Member Two</h2>
Name:
<input type="text" name="members.name:records"><BR>
<input type="text" name="members.name:records"><br>
Email:
<input type="text" name="members.email:records"><BR>
<input type="text" name="members.email:records"><br>
Age:
<input type="text" name="members.age:int:records"><BR>
<input type="text" name="members.age:int:records"><br>

This form data will be marshaled into a list of records named
'members'. Each record will have a 'name', 'email', and 'age'
This form data will be marshalled into a list of records named
``members``. Each record will have a ``name``, ``email``, and ``age``
attribute.

Record marshalling provides you with the ability to create complex
Expand Down
20 changes: 11 additions & 9 deletions src/App/dtml/manage_page_footer.dtml
@@ -1,11 +1,13 @@
<dtml-unless "REQUEST.get('zmi_dialog','window')=='modal'"
><dtml-unless "'/manage_menu' in REQUEST.URL">
<script>
// Helpers for Menu Handling <dtml-var "REQUEST.URL">
var manage_menu = typeof window.parent.frames!="undefined"&&typeof window.parent.frames.manage_menu!="undefined";
window.parent.history.replaceState('','Main','<dtml-var URL>')
</script>
</dtml-unless>
</body>
<dtml-unless "REQUEST.get('zmi_dialog','window')=='modal'">
<dtml-unless "'/manage_menu' in REQUEST.URL">
<script>
// Helpers for Menu Handling <dtml-var "REQUEST.URL">
var manage_menu = typeof window.parent.frames!="undefined"&&typeof window.parent.frames.manage_menu!="undefined";
<dtml-if "REQUEST.get('method','') == 'GET'">
window.parent.history.replaceState('','Main','<dtml-var URL>')
</dtml-if>
</script>
</dtml-unless>
</body>
</html>
</dtml-unless>
7 changes: 4 additions & 3 deletions src/OFS/DTMLMethod.py
Expand Up @@ -69,7 +69,6 @@ class DTMLMethod(
"""
meta_type = 'DTML Method'
zmi_icon = 'far fa-file-alt'
encoding = default_encoding
_proxy_roles = ()
index_html = None # Prevent accidental acquisition
_cache_namespace_keys = ()
Expand Down Expand Up @@ -118,6 +117,7 @@ def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
o If supplied, use the REQUEST mapping, Response, and key word
arguments.
"""
self.encoding = default_encoding
if not self._cache_namespace_keys:
data = self.ZCacheable_get(default=_marker)
if data is not _marker:
Expand Down Expand Up @@ -181,8 +181,9 @@ def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
else:
if PY2 and not isinstance(r, text_type):
# Prevent double-encoding edge cases under Python 2
r = r.decode('utf-8')
c, e = guess_content_type(self.getId(), r.encode('utf-8'))
r = r.decode(self.encoding)
c, e = guess_content_type(self.getId(),
r.encode(self.encoding))
RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
Expand Down
2 changes: 1 addition & 1 deletion src/Products/PageTemplates/tests/testZopePageTemplate.py
Expand Up @@ -48,7 +48,7 @@
<html>
<head>
<META http-equiv="content-type" content="text/html; charset=%s">
</hed>
</head>
<body>
test üöäÜÖÄß
</body>
Expand Down
7 changes: 6 additions & 1 deletion src/Products/PageTemplates/tests/test_persistenttemplate.py
@@ -1,7 +1,9 @@
import io
import re
import unittest

from Products.PageTemplates.ZopePageTemplate import manage_addPageTemplate
from Testing.utils import capture_stdout
from Testing.ZopeTestCase import ZopeTestCase


Expand Down Expand Up @@ -211,7 +213,10 @@ def test_filename_attribute(self):
self.assertEqual(template().strip(), u'012')

def test_edit_with_errors(self):
template = self._makeOne('foo', simple_error)
# Prevent error output to the console
with capture_stdout(io.StringIO()):
template = self._makeOne('foo', simple_error)

# this should not raise:
editable_text = get_editable_content(template)
# and the errors should be in an xml comment at the start of
Expand Down
23 changes: 23 additions & 0 deletions src/Testing/utils.py
@@ -0,0 +1,23 @@
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" Some utility functiions for unit tests
"""
import contextlib
import sys

@contextlib.contextmanager
def capture_stdout(file):
old_out = sys.stdout
sys.stdout = file
yield
sys.stdout = old_out
2 changes: 1 addition & 1 deletion src/ZPublisher/HTTPRequest.py
Expand Up @@ -1179,7 +1179,7 @@ def processInputs(
path = path[:-1]
else:
path = ''
other['PATH_INFO'] = path = "%s/%s" % (path, meth)
other['PATH_INFO'] = "%s/%s" % (path, meth)
self._hacked_path = 1

def postProcessInputs(self):
Expand Down

0 comments on commit 2a002ce

Please sign in to comment.