Beispiel #1
0
    def mailPassword(self, forgotten_userid, REQUEST):
        """ Email a forgotten password to a member.

        o Raise an exception if user ID is not found.
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(forgotten_userid)

        if member is None:
            raise ValueError(_(u'The username you entered could not be '
                               u'found.'))

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.password_email
        kw = {'member': member, 'password': member.getPassword()}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        host.send(mail_text)

        return context.mail_password_response(self, REQUEST)
def _unrestrictedGetObject(self):
    parent = aq_parent(self)
    if (aq_get(parent, 'REQUEST', None) is None and _GLOBALREQUEST_INSTALLED
            and _REQUESTCONTAINER_EXISTS):
        request = getRequest()
        if request is not None:
            # path should be absolute, starting at the physical root
            parent = self.getPhysicalRoot()
            request_container = RequestContainer(REQUEST=request)
            parent = aq_base(parent).__of__(request_container)
    try:
        return parent.unrestrictedTraverse(self.getPath())
    except:
        connection = queryUtility(ISQLConnectionsUtility,
                                  name=self.portal_type,
                                  default=None)
        if connection == None and self.portal_type:
            fti = queryUtility(IDexterityFTI,
                               name=self.portal_type,
                               default=None)
            if not fti:
                return None
            updateConnectionsForFti(fti)
            connection = queryUtility(ISQLConnectionsUtility,
                                      name=self.portal_type,
                                      default=None)
        return connection.getVirtualItem(self.sql_id, context=parent)
    def getObject(self, REQUEST=None):
        """Return the object for this record

        Will return None if the object cannot be found via its cataloged path
        (i.e., it was deleted or moved without recataloging), or if the user is
        not authorized to access the object.

        This method mimicks a subset of what publisher's traversal does,
        so it allows access if the final object can be accessed even
        if intermediate objects cannot.
        """
        path = self.getPath().split('/')
        if not path:
            return None
        parent = aq_parent(self)
        if (aq_get(parent, 'REQUEST', None) is None):
            request = getRequest()
            if request is not None:
                # path should be absolute, starting at the physical root
                parent = self.getPhysicalRoot()
                request_container = RequestContainer(REQUEST=request)
                parent = aq_base(parent).__of__(request_container)
        if len(path) > 1:
            parent = parent.unrestrictedTraverse(path[:-1])

        return parent.restrictedTraverse(path[-1])
Beispiel #4
0
def render_object(obj,
                  path,
                  where,
                  append_html=True,
                  output_root='',
                  raise_errors=False):
    path = path.strip('/')
    assert '..' not in path
    outputfile = os.path.join(where, path)
    outputdir = os.path.dirname(outputfile)
    environ = {'SERVER_NAME': 'localhost', 'SERVER_PORT': '80'}
    stdin = StringIO()
    response = Response(stdout=sys.stdout, stderr=sys.stderr)
    request = Request(stdin, environ, response)
    if output_root:
        request['SERVER_URL'] = relpath(output_root, outputdir)
    setDefaultSkin(request)
    app = Application().__of__(RequestContainer(REQUEST=request))
    obj = obj.__of__(app)
    request.other['VirtualRootPhysicalPath'] = obj.getPhysicalPath()
    obj = obj.unrestrictedTraverse(path)
    if getattr(obj, 'index_html', None) is not None:
        obj = obj.index_html
    try:
        result = mapply(obj, request.args, request)
    except Exception, e:
        print >> sys.stderr, "cannot render %s: %s: %s" % (
            path, e.__class__.__name__, e)
        if raise_errors:
            raise
Beispiel #5
0
def _getContext(app):
    request = HTTPRequest(None, {
        'SERVER_NAME': 'localhost',
        'SERVER_PORT': '8080',
        'REQUEST_METHOD': 'GET'
    }, HTTPResponse(stdout=None))
    return app.__of__(RequestContainer(REQUEST=request))
    def mailPassword(self, forgotten_userid, REQUEST):
        """ Email a forgotten password to a member.

        o Raise an exception if user ID is not found.
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(forgotten_userid)

        if member is None:
            raise ValueError(
                _(u'The username you entered could not be '
                  u'found.'))

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.unrestrictedTraverse('password_email')
        kw = {'member': member, 'password': member.getPassword()}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        try:
            host.send(mail_text, immediate=True)
        except (TypeError, socket.error):
            # fallback for mail hosts that don't implement the new signature
            # fallback to queue if immediate fails
            host.send(mail_text)

        try:
            # BBB: for CMF 2.2's mail_password script
            return context.mail_password_response(self, REQUEST)
        except AttributeError:
            pass
Beispiel #7
0
def _getContext(app):
    resp = HTTPResponse(stdout=None)
    env = {
        "SERVER_NAME": "localhost",
        "SERVER_PORT": "8080",
        "REQUEST_METHOD": "GET",
    }
    req = HTTPRequest(None, env, resp)
    return app.__of__(RequestContainer(REQUEST=req))
 def getPortalObject(self):
     """ Get the portal object itself.
     """
     request_container = RequestContainer(REQUEST=getRequest())
     portal_obj = queryUtility(ISiteRoot)
     if portal_obj is None:
         # fallback for bootstrap
         portal_obj = aq_parent(aq_inner(self))
     return portal_obj.__of__(request_container)
 def getMembersFolder(self):
     """ Get the members folder object.
     """
     parent = aq_parent(aq_inner(self))
     members_folder = getattr(parent, 'Members', None)
     if members_folder is None:
         return None
     request_container = RequestContainer(REQUEST=getRequest())
     return members_folder.__of__(request_container)
    def mailPassword(self, forgotten_userid, REQUEST):
        """ Email a forgotten password to a member.

        o Raise an exception if user ID is not found.
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(forgotten_userid)

        if member is None:
            raise ValueError(_(u'The username you entered could not be '
                               u'found.'))

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.unrestrictedTraverse('password_email')
        kw = {'member': member, 'password': member.getPassword()}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        try:
            host.send(mail_text, immediate=True)
        except (TypeError, socket.error):
            # fallback for mail hosts that don't implement the new signature
            # fallback to queue if immediate fails
            host.send(mail_text)

        try:
            # BBB: for CMF 2.2's mail_password script
            return context.mail_password_response(self, REQUEST)
        except AttributeError:
            pass
    def registeredNotify(self, new_member_id, password=None, REQUEST=None):
        """ Handle mailing the registration / welcome message.
        """
        if REQUEST is None:
            raise ValueError(u"'REQUEST' argument is missing.")

        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(new_member_id)

        if member is None:
            raise ValueError(
                _(u'The username you entered could not be '
                  u'found.'))

        if password is None:
            password = member.getPassword()

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.unrestrictedTraverse('registered_email')
        kw = {'member': member, 'password': password, 'email': email}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        try:
            host.send(mail_text, immediate=True)
        except (TypeError, socket.error):
            # fallback for mail hosts that don't implement the new signature
            # fallback to queue if immediate fails
            host.send(mail_text)
Beispiel #12
0
 def _getContext(self, app):
     resp = HTTPResponse(stdout=None)
     env = {
         'SERVER_NAME': 'localhost',
         'SERVER_PORT': '8080',
         'REQUEST_METHOD': 'GET'
     }
     req = HTTPRequest(None, env, resp)
     app.__of__(RequestContainer(REQUEST=req))
     return app
Beispiel #13
0
 def getMembersFolder(self):
     """ Get the members folder object.
     """
     parent = aq_parent(aq_inner(self))
     try:
         members_folder = parent.restrictedTraverse(self.membersfolder_id)
     except (AttributeError, KeyError):
         return None
     request_container = RequestContainer(REQUEST=getRequest())
     return members_folder.__of__(request_container)
Beispiel #14
0
def addRequestContainer(app, environ=None):
    """Add the request container with a fake request to the app object's
    acquisition context and return the wrapped app object. Additional request
    environment values can be passed as a dict ``environ``.
    """

    from ZPublisher.BaseRequest import RequestContainer
    req = makeTestRequest(environ)
    requestcontainer = RequestContainer(REQUEST=req)
    return app.__of__(requestcontainer)
    def registeredNotify(self, new_member_id, password=None, REQUEST=None):
        """ Handle mailing the registration / welcome message.
        """
        if REQUEST is None:
            raise ValueError(u"'REQUEST' argument is missing.")

        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(new_member_id)

        if member is None:
            raise ValueError(_(u'The username you entered could not be '
                               u'found.'))

        if password is None:
            password = member.getPassword()

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.unrestrictedTraverse('registered_email')
        kw = {'member': member, 'password': password, 'email': email}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        try:
            host.send(mail_text, immediate=True)
        except (TypeError, socket.error):
            # fallback for mail hosts that don't implement the new signature
            # fallback to queue if immediate fails
            host.send(mail_text)
Beispiel #16
0
def makerequest(app, user, password):
    """Like Testing.makerequest but add authentication info."""
    resp = HTTPResponse(stdout=stdout)
    environ['SERVER_NAME'] = 'foo'
    environ['SERVER_PORT'] = '80'
    environ['REQUEST_METHOD'] = 'GET'
    environ['AUTHENTICATED_USER'] = user
    environ['__ac_name'] = user
    environ['__ac_password'] = password
    req = HTTPRequest(stdin, environ, resp)
    return app.__of__(RequestContainer(REQUEST=req))
Beispiel #17
0
 def install():
   from ZPublisher.BaseRequest import RequestContainer
   from Products.ERP5Type.Globals import get_request
   portal = self.getPortalObject()
   # BusinessTemplate.install needs a request
   template_tool = portal.aq_base.__of__(portal.aq_parent.__of__(
     RequestContainer(REQUEST=get_request()))).portal_templates
   if template_tool.getInstalledBusinessTemplate(bt_name) is None:
     from Products.ERP5.ERP5Site import getBootstrapBusinessTemplateUrl
     url = getBootstrapBusinessTemplateUrl(bt_name)
     template_tool.download(url).install()
def wrap_app_in_request(app):
    """
    Wrap an app in request suitable for execution of templates, and such
    that Traversable.absolute_url() works in a controlled way. Sets
    request['SERVER_URL'] and returns tuple of correctly wrapped app and
    corresponding request object.
    """
    request = test_request()
    request.setServerURL(protocol='http', hostname=BASE_HOST)
    app = app.__of__(RequestContainer(REQUEST=request))
    app.REQUEST = request
    return app, request
Beispiel #19
0
 def getContext(self, app):
     from ZPublisher.HTTPRequest import HTTPRequest
     from ZPublisher.HTTPResponse import HTTPResponse
     from ZPublisher.BaseRequest import RequestContainer
     resp = HTTPResponse(stdout=None)
     env = {
         'SERVER_NAME':'localhost',
         'SERVER_PORT':'8080',
         'REQUEST_METHOD':'GET'
         }
     req = HTTPRequest(None, env, resp)
     return app.__of__(RequestContainer(REQUEST = req))
Beispiel #20
0
    def getApplication(self, request):
        # Open the database.
        conn = self.db.open()
        cleanup = Cleanup(conn.close)
        request.hold(cleanup)  # Close the connection on request.close()

        # Get the application object.
        root = conn.root()
        app = root.get(self.root_name, None)

        app = app.__of__(RequestContainer(REQUEST=request))
        request['PARENTS'].append(app)

        return app
    def _unrestrictedGetObject(self):
        """Return the object for this record

        Same as getObject, but does not do security checks.
        """
        parent = aq_parent(self)
        if (aq_get(parent, 'REQUEST', None) is None):
            request = getRequest()
            if request is not None:
                # path should be absolute, starting at the physical root
                parent = self.getPhysicalRoot()
                request_container = RequestContainer(REQUEST=request)
                parent = aq_base(parent).__of__(request_container)
        return parent.unrestrictedTraverse(self.getPath())
Beispiel #22
0
    def mailPassword(self, forgotten_userid, REQUEST):
        """ Email a forgotten password to a member.

        o Raise an exception if user ID is not found.
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(forgotten_userid)

        if member is None:
            raise ValueError(
                _(u'The username you entered could not be '
                  u'found.'))

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.password_email
        kw = {'member': member, 'password': member.getPassword()}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        host.send(mail_text)

        return context.mail_password_response(self, REQUEST)
Beispiel #23
0
def rewrap_in_request_container(obj, context=None):
    '''Fix an object's acquisition wrapper to be able to acquire the REQUEST.'''
    request = getattr(context, 'REQUEST', None) or getRequest()
    if IAcquirer.providedBy(obj) and request is not None:
        chain = []
        parent = obj
        while 1:
            chain.append(parent)
            parent = aq_parent(aq_inner(parent))
            if parent in chain or parent is None or isinstance(
                    parent, RequestContainer):
                break
        obj = RequestContainer(REQUEST=request)
        for ob in reversed(chain):
            obj = aq_base(ob).__of__(obj)
    return obj
Beispiel #24
0
def get_dmd():
    """Retrieve the DMD object."""
    connections = transaction.get()._synchronizers.data.values()[:]
    connections.reverse()
    # Make sure we don't get the temporary connection
    for cxn in connections:
        db = getattr(cxn, '_db', None)
        if db and db.database_name != 'temporary':
            resp = HTTPResponse(stdout=None)
            env = {
                'SERVER_NAME': 'localhost',
                'SERVER_PORT': '8080',
                'REQUEST_METHOD': 'GET',
            }
            req = HTTPRequest(None, env, resp)
            app = cxn.root()['Application']
            app = app.__of__(RequestContainer(REQUEST=req))
            return app.zport.dmd
Beispiel #25
0
def makerequest(app, stdout=None, environ=None):
    """
    Adds an HTTPRequest at app.REQUEST, and returns
    app.__of__(app.REQUEST). Useful for tests that need to acquire
    REQUEST.

    Usage:
      import makerequest
      app = makerequest.makerequest(app)

    You should only wrap the object used as 'root' in your tests.

    app is commonly a Zope2.app(), but that's not strictly necessary
    and frequently may be overkill; you can wrap other objects as long
    as they support acquisition and provide enough of the features of
    Zope2.app for your tests to run.  For example, if you want to call
    getPhysicalPath() on child objects, app must provide a
    non-recursive implementation of getPhysicalPath().

    *stdout* is an optional file-like object and is used by
    REQUEST.RESPONSE. The default is sys.stdout.

    *environ* is an optional mapping to be used in the request.
    Default is a fresh dictionary. Passing os.environ is not
    recommended; tests should not pollute the real os.environ.
    """
    if stdout is None:
        stdout = BytesIO()
    if environ is None:
        environ = {}
    resp = HTTPResponse(stdout=stdout)
    environ.setdefault('SERVER_NAME', 'nohost')
    environ.setdefault('SERVER_PORT', '80')
    environ.setdefault('REQUEST_METHOD', 'GET')
    req = HTTPRequest(BytesIO(), environ, resp)
    req._steps = ['noobject']  # Fake a published object.
    req['ACTUAL_URL'] = req.get('URL')  # Zope 2.7.4

    # Set default skin so that the request is usable for view look-ups.
    from zope.publisher.browser import setDefaultSkin
    setDefaultSkin(req)

    requestcontainer = RequestContainer(REQUEST=req)
    return app.__of__(requestcontainer)
def getObject(self, REQUEST=None):
    path = self.getPath().split('/')
    if not path:
        return None
    parent = aq_parent(self)
    if (aq_get(parent, 'REQUEST', None) is None
        and _GLOBALREQUEST_INSTALLED and _REQUESTCONTAINER_EXISTS):
        request = getRequest()
        if request is not None:
            # path should be absolute, starting at the physical root
            parent = self.getPhysicalRoot()
            request_container = RequestContainer(REQUEST=request)
            parent = aq_base(parent).__of__(request_container)
    if len(path) > 1:
        parent = parent.unrestrictedTraverse(path[:-1])
    try:
        return parent.restrictedTraverse(path[-1])
    except:
        connection = getUtility(ISQLConnectionsUtility, name=self.portal_type)
        return connection.getVirtualItem(self.sql_id, context=parent)
Beispiel #27
0
def get_object(catalog, rid):
    """get an object based on the catalog rid

    Code based on ZCatalog.CatalogBrains.AbstractCatalogBrains.getObject
    """
    path = catalog._catalog.getpath(rid)
    path = path.split('/')
    if not path:
        return None

    parent = aq_parent(catalog)
    if (aq_get(parent, 'REQUEST', None) is None and _GLOBALREQUEST_INSTALLED):
        request = getRequest()
        if request is not None:
            # path should be absolute, starting at the physical root
            parent = catalog.getPhysicalRoot()
            request_container = RequestContainer(REQUEST=request)
            parent = aq_base(parent).__of__(request_container)
    if len(path) > 1:
        parent = parent.unrestrictedTraverse(path[:-1])

    return parent.restrictedTraverse(path[-1])
def getObject(self, REQUEST=None):
    path = self.getPath().split('/')
    if not path:
        return None
    parent = aq_parent(self)
    if (aq_get(parent, 'REQUEST', None) is None and _GLOBALREQUEST_INSTALLED
            and _REQUESTCONTAINER_EXISTS):
        request = getRequest()
        if request is not None:
            # path should be absolute, starting at the physical root
            parent = self.getPhysicalRoot()
            request_container = RequestContainer(REQUEST=request)
            parent = aq_base(parent).__of__(request_container)
    if len(path) > 1:
        try:
            parent = parent.unrestrictedTraverse(path[:-1])
        except:
            if path[:-2] == 'data-' + self.portal_type:
                parent = queryMultiAdapter((None, ICollectiveBehaviorSQLLayer),
                                           IBrowserView,
                                           name='data-' + name,
                                           default=None)
    try:
        return parent.restrictedTraverse(path[-1])
    except:
        connection = queryUtility(ISQLConnectionsUtility,
                                  name=self.portal_type,
                                  default=None)
        if connection == None and self.portal_type:
            fti = queryUtility(IDexterityFTI,
                               name=self.portal_type,
                               default=None)
            if not fti:
                return None
            updateConnectionsForFti(fti)
            connection = queryUtility(ISQLConnectionsUtility,
                                      name=self.portal_type,
                                      default=None)
        return connection.getVirtualItem(self.sql_id, context=parent)
Beispiel #29
0
def getObject(brain=None, path=None):
    """Return the object for brain record

    Will return None if the object cannot be found via its cataloged path
    (i.e., it was deleted or moved without recataloging).

    This method mimicks a subset of what publisher's traversal does,
    so it allows access if the final object can be accessed even
    if intermediate objects cannot.
    """
    if brain is not None:
        path = brain.getPath()

    if not path:
        return None

    # Handle portal_factory
    if 'portal_factory' in path:
        return None

    # Handle relatedItems brains
    at_reference = None
    if '/at_references/' in path:
        path, at_reference = path.split('/at_references/')

    # Handle plone.app.discussion brains
    discussion = None
    if '/++conversation++default/' in path:
        path, discussion = path.split('/++conversation++default/')

    path = path.split('/')

    # Remove empty string from begining of the path
    if not path[0]:
        path = path[1:]

    if not path:
        return None

    # Remove site id from begining of the path
    parent = getSite()
    if path[0] == parent.getId():
        path = path[1:]

    if not path:
        return parent

    # Try to add REQUEST if not present
    if (aq_get(parent, 'REQUEST', None) is None and
            _GLOBALREQUEST_INSTALLED and _REQUESTCONTAINER_EXISTS):
        request = getRequest()
        if request is not None:
            request_container = RequestContainer(REQUEST=request)
            parent = aq_base(parent).__of__(request_container)

    obj = parent.unrestrictedTraverse(path)
    if isinstance(obj, BrokenClass):
        logger.warn('BrokenClass found at %s', path)
        return None

    # Handle relatedItems
    if at_reference is not None:
        at_references = getattr(obj, 'at_references', {})
        return at_references.get(at_reference, None)

    # Handle plone.app.discussion
    if discussion is not None:
        anno = getattr(obj, '__annotations__', {})
        discussions = anno.get('plone.app.discussion:conversation', {})
        return discussions.get(discussion, None)

    return obj
Beispiel #30
0
def subrequest(url, root=None, stdout=None, exception_handler=None):
    assert url is not None, 'You must pass a url'
    if isinstance(url, six.text_type):
        url = url.encode('utf-8')
    _, _, path, query, _ = urlsplit(url)
    parent_request = getRequest()
    assert parent_request is not None, \
        'Unable to get request, perhaps zope.globalrequest is not configured.'
    parent_site = getSite()
    security_manager = getSecurityManager()
    parent_app = parent_request.PARENTS[-1]
    if path.startswith('/'):
        path = normpath(path)
        vurl_parts = parent_request.get('VIRTUAL_URL_PARTS')
        if vurl_parts is not None:
            # Use the virtual host root
            path_past_root = unquote(vurl_parts[-1])
            root_path = normpath(parent_request['PATH_INFO']).rstrip(
                '/')[:-len(path_past_root) or None]
            if root is None:
                path = root_path + path
            else:
                path = '{0}/{1}{2}'.format(root_path, root.virtual_url_path(),
                                           path)
        elif root is not None:
            path = '/{0}{1}'.format(root.virtual_url_path(), path)
    else:
        try:
            parent_url = parent_request['URL']
            if isinstance(parent_url, six.text_type):
                parent_url = parent_url.encode('utf-8')
            # extra is the hidden part of the url, e.g. a default view
            extra = unquote(parent_url[len(parent_request['ACTUAL_URL']):])
        except KeyError:
            extra = ''
        here = parent_request['PATH_INFO'] + extra
        path = urljoin(here, path)
        path = normpath(path)
    request = parent_request.clone()
    for name, parent_value in parent_request.other.items():
        if name in OTHER_IGNORE \
           or OTHER_IGNORE_RE.match(name) \
           or name.startswith('_'):
            continue
        request.other[name] = parent_value
    for key, value in parent_request.response.cookies.items():
        request.cookies[key] = value['value']
    request['PARENT_REQUEST'] = parent_request
    alsoProvides(request, ISubRequest)
    try:
        setRequest(request)
        request_container = RequestContainer(REQUEST=request)
        app = aq_base(parent_app).__of__(request_container)
        request['PARENTS'] = [app]
        response = request.response
        response.__class__ = SubResponse
        response.stderr = None  # only used on retry it seems
        if stdout is None:
            stdout = StringIO()  # It might be possible to optimize this
        response.stdout = stdout
        environ = request.environ
        environ['PATH_INFO'] = path
        environ['QUERY_STRING'] = query
        # Clean up the request.
        for header in CONDITIONAL_HEADERS:
            environ.pop(header, None)
        try:
            request.processInputs()
            traversed = request.traverse(path)
            result = mapply(traversed,
                            positional=request.args,
                            keyword=request,
                            debug=None,
                            maybe=1,
                            missing_name=missing_name,
                            handle_class=dont_publish_class,
                            context=request,
                            bind=1)
            if result is not response:
                response.setBody(result)
            for key, value in request.response.cookies.items():
                parent_request.response.cookies[key] = value
        except Exception as e:
            logger.exception('Error handling subrequest to {0}'.format(url))
            if exception_handler is not None:
                exception_handler(response, e)
            else:
                view = queryMultiAdapter((e, request), name=u'index.html')
                if view is not None:
                    v = view()
                    response.setBody(v)
                else:
                    response.exception()
        return response
    finally:
        if SAFE_WRITE_KEY in request.environ:
            # append this list of safe oids to parent request
            if SAFE_WRITE_KEY not in parent_request.environ:
                parent_request.environ[SAFE_WRITE_KEY] = []
            new_keys = (set(request.environ[SAFE_WRITE_KEY]) -
                        set(parent_request.environ[SAFE_WRITE_KEY]))
            parent_request.environ[SAFE_WRITE_KEY].extend(new_keys)
        if IDisableCSRFProtection.providedBy(request):
            alsoProvides(parent_request, IDisableCSRFProtection)
        request.clear()
        setRequest(parent_request)
        setSite(parent_site)
        setSecurityManager(security_manager)