Skip to content

Commit

Permalink
Merge 6808176 into 55a2183
Browse files Browse the repository at this point in the history
  • Loading branch information
dataflake committed Mar 8, 2019
2 parents 55a2183 + 6808176 commit 3aa58e4
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -59,6 +59,9 @@ Other changes
- Provide additional links on PyPI with ``project_urls`` in ``setup.py``
(`#434 <https://github.com/zopefoundation/Zope/issues/434>`_)

- Resurrect automatic support for ``standard_error_message`` DTML Method
(`#238 <https://github.com/zopefoundation/Zope/issues/238>`_)


4.0b9 (2019-02-09)
------------------
Expand Down
27 changes: 27 additions & 0 deletions src/OFS/browser/__init__.py
@@ -0,0 +1,27 @@
##############################################################################
#
# 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
#
##############################################################################

from Products.Five import BrowserView


class StandardErrorMessageView(BrowserView):
""" View rendered on SiteError.
Requires a DTML Method named ``standard_error_message``
"""

def __call__(self):
root = self.request['PARENTS'][-1]
return root.standard_error_message(
error_type=self.context.__class__.__name__,
error_value=str(self.context))
12 changes: 12 additions & 0 deletions src/OFS/browser/configure.zcml
@@ -0,0 +1,12 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">

<browser:page
for="Exception"
name="standard_error_message"
class=".StandardErrorMessageView"
permission="zope.Public"
/>

</configure>
2 changes: 2 additions & 0 deletions src/OFS/configure.zcml
Expand Up @@ -4,4 +4,6 @@
<include file="deprecated.zcml"/>
<include file="event.zcml"/>

<include package=".browser"/>

</configure>
17 changes: 16 additions & 1 deletion src/ZPublisher/WSGIPublisher.py
Expand Up @@ -19,6 +19,7 @@

from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager
from Acquisition import aq_acquire
from six import PY3
from six import reraise
from six.moves._thread import allocate_lock
Expand Down Expand Up @@ -96,9 +97,22 @@ def get_module_info(module_name='Zope2'):

def _exc_view_created_response(exc, request, response):
view = queryMultiAdapter((exc, request), name=u'index.html')
parents = request.get('PARENTS')

if view is None and parents:
# Try a fallback based on the old standard_error_message
# DTML Method in the ZODB
view = queryMultiAdapter((exc, request),
name=u'standard_error_message')
root_parent = parents[0]
try:
standard_error_message = aq_acquire(root_parent,
'standard_error_message')
except (AttributeError, KeyError):
view = None

if view is not None:
# Wrap the view in the context in which the exception happened.
parents = request.get('PARENTS')
if parents:
view.__parent__ = parents[0]

Expand All @@ -113,6 +127,7 @@ def _exc_view_created_response(exc, request, response):
# Set the response body to the result of calling the view.
response.setBody(view())
return True

return False


Expand Down
65 changes: 58 additions & 7 deletions src/ZPublisher/tests/test_WSGIPublisher.py
Expand Up @@ -491,7 +491,7 @@ def testCustomExceptionViewConflictErrorHandling(self):
directlyProvides(_request, IDefaultBrowserLayer)
_request.response = DummyResponse()
_request.retry_count = 0
_request.retry_max_count = 3
_request.retry_max_count = 2
_request.environ = {}

def _close():
Expand Down Expand Up @@ -519,6 +519,7 @@ def _request_factory(stdin, environ, response):
# have been retried up to retry_max_count times
self.assertTrue(app_iter[1].startswith('Exception View: ConflictError'))
self.assertEqual(_request.retry_count, _request.retry_max_count)
unregisterExceptionView(Exception)

def testCustomExceptionViewUnauthorized(self):
from AccessControl import Unauthorized
Expand All @@ -531,6 +532,7 @@ def testCustomExceptionViewUnauthorized(self):
body = b''.join(app_iter)
self.assertEqual(start_response._called_with[0][0], '401 Unauthorized')
self.assertTrue(b'Exception View: Unauthorized' in body)
unregisterExceptionView(IUnauthorized)

def testCustomExceptionViewForbidden(self):
from zExceptions import Forbidden
Expand All @@ -543,6 +545,7 @@ def testCustomExceptionViewForbidden(self):
body = b''.join(app_iter)
self.assertEqual(start_response._called_with[0][0], '403 Forbidden')
self.assertTrue(b'Exception View: Forbidden' in body)
unregisterExceptionView(IForbidden)

def testCustomExceptionViewNotFound(self):
from zExceptions import NotFound
Expand All @@ -555,6 +558,7 @@ def testCustomExceptionViewNotFound(self):
body = b''.join(app_iter)
self.assertEqual(start_response._called_with[0][0], '404 Not Found')
self.assertTrue(b'Exception View: NotFound' in body)
unregisterExceptionView(INotFound)

def testCustomExceptionViewZTKNotFound(self):
from zope.publisher.interfaces import NotFound as ZTK_NotFound
Expand All @@ -567,6 +571,7 @@ def testCustomExceptionViewZTKNotFound(self):
body = b''.join(app_iter)
self.assertEqual(start_response._called_with[0][0], '404 Not Found')
self.assertTrue(b'Exception View: NotFound' in body)
unregisterExceptionView(INotFound)

def testCustomExceptionViewBadRequest(self):
from zExceptions import BadRequest
Expand Down Expand Up @@ -623,6 +628,51 @@ def testHandleErrorsFalseBypassesExceptionResponse(self):
self._callFUT(environ, start_response, _publish)


class ExcViewCreatedTests(ZopeTestCase):

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['PARENTS'] = [self.app]
directlyProvides(req, IDefaultBrowserLayer)
return _exc_view_created_response(exc, req, DummyResponse())

def _registerStandardErrorView(self):
from OFS.browser import StandardErrorMessageView
from zope.interface import Interface
registerExceptionView(Interface, factory=StandardErrorMessageView,
name=u'standard_error_message')

def _unregisterStandardErrorView(self):
from OFS.browser import StandardErrorMessageView
from zope.interface import Interface
unregisterExceptionView(Interface, factory=StandardErrorMessageView,
name=u'standard_error_message')

def testNoStandardErrorMessage(self):
from zExceptions import NotFound
self._registerStandardErrorView()

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

def testWithStandardErrorMessage(self):
from OFS.DTMLMethod import addDTMLMethod
from zExceptions import NotFound
self._registerStandardErrorView()

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

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


class WSGIPublisherTests(FunctionalTestCase):

def test_can_handle_non_ascii_URLs(self):
Expand Down Expand Up @@ -708,28 +758,29 @@ def __call__(self):
self.__parent__.__class__.__name__))


def registerExceptionView(for_):
def registerExceptionView(for_, factory=CustomExceptionView, name=u'index.html'):
from zope.interface import Interface
from zope.component import getGlobalSiteManager
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
gsm = getGlobalSiteManager()
gsm.registerAdapter(
CustomExceptionView,
factory,
required=(for_, IDefaultBrowserLayer),
provided=Interface,
name=u'index.html',
name=name,
)

def unregisterExceptionView(for_):
def unregisterExceptionView(for_, factory=CustomExceptionView,
name=u'index.html'):
from zope.interface import Interface
from zope.component import getGlobalSiteManager
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
gsm = getGlobalSiteManager()
gsm.unregisterAdapter(
CustomExceptionView,
factory,
required=(for_, IDefaultBrowserLayer),
provided=Interface,
name=u'index.html',
name=name,
)


Expand Down
1 change: 1 addition & 0 deletions src/Zope2/App/meta.zcml
Expand Up @@ -4,6 +4,7 @@

<include package="AccessControl" file="meta.zcml" />
<include package="OFS" file="meta.zcml" />
<include package="Products.Five" file="meta.zcml" />
<include package="zope.component" file="meta.zcml" />
<include package="zope.i18n" file="meta.zcml" />

Expand Down

0 comments on commit 3aa58e4

Please sign in to comment.