Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serve MessageDialogs and exception views as text/html (4.x) #1082

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
4.8.6 (unreleased)
------------------

- Explicitly serve ``App.Dialogs.MessageDialog`` and exception views as HTML
due to the changed default content type from `#1075
<https://github.com/zopefoundation/Zope/pull/1075>`_.


4.8.5 (2022-12-17)
------------------
15 changes: 14 additions & 1 deletion src/App/Dialogs.py
Original file line number Diff line number Diff line change
@@ -34,7 +34,20 @@
from App.special_dtml import HTML


MessageDialog = HTML("""
class MessageDialogHTML(HTML):
"""A special version of HTML which is always published as text/html
"""
def __call__(self, *args, **kw):
class _HTMLString(str):
"""A special string that will be published as text/html
"""
def asHTML(self):
return self
return _HTMLString(
super(MessageDialogHTML, self).__call__(*args, **kw))


MessageDialog = MessageDialogHTML("""
<HTML>
<HEAD>
<TITLE>&dtml-title;</TITLE>
19 changes: 19 additions & 0 deletions src/App/tests/test_Dialogs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Testing.ZopeTestCase


class TestMessageDialog(Testing.ZopeTestCase.ZopeTestCase):

def test_publish_set_content_type(self):
from App.Dialogs import MessageDialog

md = MessageDialog(
title='dialog title',
message='dialog message',
action='action'
)
self.assertIn('dialog title', md)
self.assertIn('dialog message', md)
self.assertIn('action', md)
req = self.app.REQUEST
req.RESPONSE.setBody(md)
self.assertIn('text/html', req.RESPONSE.getHeader('Content-Type'))
5 changes: 5 additions & 0 deletions src/ZPublisher/WSGIPublisher.py
Original file line number Diff line number Diff line change
@@ -144,6 +144,11 @@ def _exc_view_created_response(exc, request, response):
for key, value in exc.headers.items():
response.setHeader(key, value)

# Explicitly set the content type header if it's not there yet so
# the response doesn't get served with the text/plain default
if not response.getHeader('Content-Type'):
response.setHeader('Content-Type', 'text/html')

# Set the response body to the result of calling the view.
response.setBody(view())
return True
24 changes: 22 additions & 2 deletions src/ZPublisher/tests/test_WSGIPublisher.py
Original file line number Diff line number Diff line change
@@ -670,6 +670,10 @@ def _request_factory(stdin, environ, response):
self.assertTrue(app_iter[1].startswith(
'Exception View: ConflictError'))
self.assertEqual(_request.retry_count, _request.retry_max_count)

# The Content-Type response header should be set to text/html
self.assertIn('text/html', _request.response.getHeader('Content-Type'))

unregisterExceptionView(Exception)

def testCustomExceptionViewUnauthorized(self):
@@ -871,10 +875,10 @@ def _callFUT(self, exc):
from zope.interface import directlyProvides
from zope.publisher.browser import IDefaultBrowserLayer
from ZPublisher.WSGIPublisher import _exc_view_created_response
req = DummyRequest()
req = self.app.REQUEST
req['PARENTS'] = [self.app]
directlyProvides(req, IDefaultBrowserLayer)
return _exc_view_created_response(exc, req, DummyResponse())
return _exc_view_created_response(exc, req, req.RESPONSE)

def _registerStandardErrorView(self):
from OFS.browser import StandardErrorMessageView
@@ -901,14 +905,22 @@ def testWithStandardErrorMessage(self):
from OFS.DTMLMethod import addDTMLMethod
from zExceptions import NotFound
self._registerStandardErrorView()
response = self.app.REQUEST.RESPONSE

addDTMLMethod(self.app, 'standard_error_message', file='OOPS')

# The response content-type header is not set before rendering
# the standard error template
self.assertFalse(response.getHeader('Content-Type'))

try:
self.assertTrue(self._callFUT(NotFound))
finally:
self._unregisterStandardErrorView()

# After rendering the response content-type header is set
self.assertIn('text/html', response.getHeader('Content-Type'))


class WSGIPublisherTests(FunctionalTestCase):

@@ -1063,6 +1075,14 @@ def setStatus(self, status, reason=None, lock=None):

status = property(lambda self: self._status, setStatus)

def getHeader(self, header):
return dict(self._headers).get(header, None)

def setHeader(self, header, value):
headers = dict(self._headers)
headers[header] = value
self._headers = tuple(headers.items())


class DummyCallable(object):
_called_with = _raise = _result = None