[Zope3-checkins] CVS: Zope3/src/zope/app/publication - __init__.py:1.1.2.1 browser.py:1.1.2.1 configure.zcml:1.1.2.1 http.py:1.1.2.1 publicationtraverse.py:1.1.2.1 traversers.py:1.1.2.1 vfs.py:1.1.2.1 xmlrpc.py:1.1.2.1 zopepublication.py:1.1.2.1

Jim Fulton jim@zope.com
Mon, 23 Dec 2002 14:32:02 -0500


Update of /cvs-repository/Zope3/src/zope/app/publication
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/app/publication

Added Files:
      Tag: NameGeddon-branch
	__init__.py browser.py configure.zcml http.py 
	publicationtraverse.py traversers.py vfs.py xmlrpc.py 
	zopepublication.py 
Log Message:
Initial renaming before debugging

=== Added File Zope3/src/zope/app/publication/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/app/publication/browser.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""XXX short summary goes here.

XXX longer description goes here.

$Id: browser.py,v 1.1.2.1 2002/12/23 19:31:59 jim Exp $
"""
__metaclass__ = type

from zope.app.publication.publicationtraverse \
     import PublicationTraverser as PublicationTraverser_
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.component import queryAdapter

class PublicationTraverser(PublicationTraverser_):    

    def traverseRelativeURL(self, request, ob, path):

        ob = self.traversePath(request, ob, path)

        while 1:
            adapter = queryAdapter(ob, IBrowserPublisher)
            if adapter is None:
                return ob
            ob, path = adapter.browserDefault(request)
            if not path:
                return ob
                        
            ob = self.traversePath(request, ob, path)


"""

Revision information:
$Id: browser.py,v 1.1.2.1 2002/12/23 19:31:59 jim Exp $
"""

from zope.app.publication.http import ZopeHTTPPublication
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.component import queryView
from zope.proxy.context.context import ContextWrapper
from zope.proxy.introspection import removeAllProxies

class BrowserPublication(ZopeHTTPPublication):
    """Web browser publication handling."""
        
    def getDefaultTraversal(self, request, ob):

        r = ()

        if IBrowserPublisher.isImplementedBy(removeAllProxies(ob)):
            r = ob.browserDefault(request)
        else:
            adapter = queryView(ob, '_traverse', request , None)
            if adapter is not None:
                r = adapter.browserDefault(request)
            else:
                return (ob, None)

        if r[0] is ob: return r
        
        wrapped = ContextWrapper(r[0], ob, name=None)
        return (wrapped, r[1])

# For now, have a factory that returns a singleton
class PublicationFactory:

    def __init__(self, db):
        self.__pub = BrowserPublication(db)

    def __call__(self):
        return self.__pub



=== Added File Zope3/src/zope/app/publication/configure.zcml ===
<zopeConfigure
   xmlns='http://namespaces.zope.org/zope'
   xmlns:browser='http://namespaces.zope.org/browser'
   xmlns:xmlrpc='http://namespaces.zope.org/xmlrpc'
>

<browser:view name="_traverse" 
 for="zope.interface.Interface"
 factory="zope.app.publication.traversers.SimpleComponentTraverser" />

<browser:view name="_traverse" 
 for="zope.app.interfaces.content.file.IFileContent"
 factory="zope.app.publication.traversers.FileContentTraverser" />

  <xmlrpc:view name="_traverse" 
   for="zope.interface.Interface"
   factory="zope.app.publication.traversers.SimpleComponentTraverser" />

<include package=".TraversalViews" />

</zopeConfigure>

<zopeConfigure
   xmlns='http://namespaces.zope.org/zope'
>


</zopeConfigure>


=== Added File Zope3/src/zope/app/publication/http.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""

$Id: http.py,v 1.1.2.1 2002/12/23 19:31:59 jim Exp $
"""

from zope.app.publication.zopepublication import ZopePublication

class ZopeHTTPPublication(ZopePublication):
    "HTTP-specific support"
    # XXX do we need this?


=== Added File Zope3/src/zope/app/publication/publicationtraverse.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""

$Id: publicationtraverse.py,v 1.1.2.1 2002/12/23 19:31:59 jim Exp $
"""

from zope.component import queryView, getService
from zope.interfaces.publisher import NotFound
from types import StringTypes
from zope.proxy.context.context import ContextWrapper 

from zope.app.interfaces.container import IWriteContainer
from zope.proxy.introspection import removeAllProxies
from zope.app.traversing.namespaces import namespaceLookup
from zope.app.traversing.parameterparsing import parameterizedNameParse
from zope.interfaces.publisher import IPublishTraverse

class DuplicateNamespaces(Exception):
    """More than one namespace was specified in a request"""
    
class UnknownNamespace(Exception):
    """A parameter specified an unknown namespace"""

class PublicationTraverse:

    def traverseName(self, request, ob, name):
        nm = name # the name to look up the object with

        if name and name[:1] in '@+':
            # Process URI segment parameters. 
            ns, nm, parms = parameterizedNameParse(name)

            unknown_parms = ()
            for pname, pval in parms:
                pset = getattr(self, "_parameterSet%s" % pname, self) # marker
                if pset is self:
                    # We don't know about this one
                    unknown_parms += ((pname, pval),)
                else:
                    pset(pname, pval, request)

            if ns:
                ob2 = namespaceLookup(name, ns, nm, unknown_parms, ob, request)
                return ob2

            if unknown_parms:
                nm = "%s;%s" % (
                    nm,
                    ';'.join(["%s=%s" % (parm[0], parm[1])
                              for parm in unknown_parms])
                    )
                
            if not nm:
                # Just set params, so skip
                return ob

        if nm == '.':
            return ob
                
        if IPublishTraverse.isImplementedBy(removeAllProxies(ob)):
            ob2 = ob.publishTraverse(request, nm)
        else:
            adapter = queryView(ob, '_traverse', request, self) # marker
            if adapter is not self:
                ob2 = adapter.publishTraverse(request, nm)
            else:
                raise NotFound(ob, name, request)

        return ContextWrapper(ob2, ob, name=name)

class PublicationTraverser(PublicationTraverse):    

    def traversePath(self, request, ob, path):

        if isinstance(path, StringTypes):
            path = path.split('/')
            if len(path) > 1 and not path[-1]:
                # Remove trailing slash
                path.pop()
        else:
            path = list(path)

        # Remove dingle dots
        path = [x for x in path if x != '.']

        path.reverse()

        # Remove double dots
        while '..' in path:
            l = path.index('..')
            if l < 0 or l+2 > len(path):
                break
            del path[l:l+2]
                     
        pop = path.pop

        while path:
            name = pop()
            ob = self.traverseName(request, ob, name)

        return ob


=== Added File Zope3/src/zope/app/publication/traversers.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"$Id"

__metaclass__ = type


from zope.interfaces.publisher import Unauthorized, NotFound, DebugError
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher
from zope.component \
     import queryView, getView, getDefaultViewName
from zope.component.exceptions import ComponentLookupError

class SimpleComponentTraverser:
    """Browser traverser for simple components that can only traverse to views
    """
    __implements__ = IBrowserPublisher, IXMLRPCPublisher

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def browserDefault(self, request):
        ob = self.context
        
        view_name = getDefaultViewName(ob, request)

        return ob, (view_name,)

    def publishTraverse(self, request, name):
        ob = self.context
        from zope.component.view import viewService
        view = queryView(ob, name, request)
        if view is None:
            raise NotFound(ob, name)
        return view

class FileContentTraverser(SimpleComponentTraverser):
    """Browser traverser for file content.

    The default view for file content has effective URLs that don't end in
    /.  In particular, if the content inclused HTML, relative links in
    the HTML are relative to the container the content is in.

    """

    def browserDefault(self, request):
        ob = self.context
        
        view_name = getDefaultViewName(ob, request)
        view = self.publishTraverse(request, view_name)
        if hasattr(view, 'browserDefault'):
            view, path = view.browserDefault(request)
            if len(path) == 1:
                view = view.publishTraverse(request, path[0])
                path = ()
        else:
            path = ()
                
        return view, path

class TestTraverser:
    "Bobo-style traverser, mostly useful for testing" 

    __implements__ = IBrowserPublisher

    def __init__(self, context, request):
        self.context = context

    def browserDefault(self, request):
        ob = self.context

        if hasattr(ob, '__implements__'):
        
            view_name = getDefaultViewName(ob, request)

            return ob, (("@@%s" % view_name),)
        
        return ob, ()

    def publishTraverse(self, request, name):
        ob = self.context
        if name.startswith('@@'):
            return getView(ob, name[6:], request)
            
        if name.startswith('_'):
            raise Unauthorized("Name %s begins with an underscore" % `name`)

        subob = getattr(ob, name, self) # self is marker here
        if subob is self:
            # no attribute
            try:
                subob = ob[name]
            except (KeyError, IndexError,
                    TypeError, AttributeError):
                raise NotFound(ob, name, request)

        return subob


=== Added File Zope3/src/zope/app/publication/vfs.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
# 
##############################################################################
"""

$Id: vfs.py,v 1.1.2.1 2002/12/23 19:31:59 jim Exp $
"""

from zope.app.publication.zopepublication import ZopePublication

from zope.component import queryView
from zope.interfaces.publisher import NotFound
from zope.publisher.publish import mapply


class VFSPublication(ZopePublication):
    """The Publication will do all the work for the VFS"""


    def callObject(self, request, ob):

        view = queryView(ob, 'vfs', request, self) 
        #view = ob

        if view is not self:
            method = getattr(view, request.method)
        else:
            raise NotFound(ob, 'vfs', request)

        return mapply(method, request.getPositionalArguments(), request)



=== Added File Zope3/src/zope/app/publication/xmlrpc.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""

$Id: xmlrpc.py,v 1.1.2.1 2002/12/23 19:31:59 jim Exp $
"""

from zope.proxy.introspection import removeAllProxies
from zope.app.publication.http import ZopeHTTPPublication

class XMLRPCPublication(ZopeHTTPPublication):
    """XML-RPC publication handling.

       There is nothing special here right now.
    """

    def traverseName(self, request, ob, name):

        naked_ob = removeAllProxies(ob)
        if hasattr(ob, name):
            return getattr(ob, name)
        else:
            return super(XMLRPCPublication, self).traverseName(request,
                                                               ob, name)


# For now, have a factory that returns a singleton
class XMLRPCPublicationFactory:

    def __init__(self, db):
        self.__pub = XMLRPCPublication(db)

    def __call__(self):
        return self.__pub


=== Added File Zope3/src/zope/app/publication/zopepublication.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
import sys
import logging

from zope.component import getService
from zope.component.exceptions import ComponentLookupError
from zodb.interfaces import ConflictError

from zope.publisher.base import DefaultPublication
from zope.publisher.publish import mapply
from zope.interfaces.publisher import Retry

from zope.security.securitymanagement import getSecurityManager
from zope.security.securitymanagement import newSecurityManager
from zope.security.checker import ProxyFactory

from zope.proxy.introspection import removeAllProxies

from zope.app.interfaces.services.service \
     import IServiceManagerContainer

from zope.exceptions import Unauthorized

from zope.app.applicationcontrol.applicationcontrol \
     import applicationControllerRoot

from zope.app.security.registries.principalregistry \
     import principalRegistry as prin_reg

from zope.app.interfaces.security \
     import IUnauthenticatedPrincipal

from zope.app.publication.publicationtraverse import PublicationTraverse

from zope.proxy.context.context import ContextWrapper

# XXX Should this be imported here?
from transaction import get_transaction

class Cleanup:
    def __init__(self, f):
        self.__del__ = f


class ZopePublication(object, PublicationTraverse, DefaultPublication):
    """Base Zope publication specification."""

    version_cookie = 'Zope-Version'
    root_name = 'Application'

    def __init__(self, db):
        # db is a ZODB.DB.DB object.
        self.db = db

    def beforeTraversal(self, request):

        # Try to authenticate against the default global registry.
        p = prin_reg.authenticate(request)
        if p is None:
            p = prin_reg.unauthenticatedPrincipal()
            if p is None:
                raise Unauthorized # If there's no default principal

        newSecurityManager(p.getId())
        request.user = p
        get_transaction().begin()

    def _maybePlacefullyAuthenticate(self, request, ob):
        if not IUnauthenticatedPrincipal.isImplementedBy(request.user):
            # We've already got an authenticated user. There's nothing to do.
            # Note that beforeTraversal guarentees that user is not None.
            return

        if not IServiceManagerContainer.isImplementedBy(ob):
            # We won't find an authentication service here, so give up.
            return

        sm = removeAllProxies(ob).queryServiceManager()
        if sm is None:
            # No service manager here, and thus no auth service
            return

        sm = ContextWrapper(sm, ob, name="++etc++Services")
        
        auth_service = sm.get('Authentication')
        if auth_service is None:
            # No auth service here
            return

        # Try to authenticate against the auth service
        principal = auth_service.authenticate(request)
        if principal is None:
            principal = auth_service.unauthenticatedPrincipal()
            if principal is None:
                # nothing to do here
                return

        newSecurityManager(principal.getId())
        request.user = principal
        

    def callTraversalHooks(self, request, ob):
        # Call __before_publishing_traverse__ hooks

        # This is also a handy place to try and authenticate.
        self._maybePlacefullyAuthenticate(request, ob)

    def afterTraversal(self, request, ob):
        #recordMetaData(object, request)
        self._maybePlacefullyAuthenticate(request, ob)
            

    def openedConnection(self, conn):
        # Hook for auto-refresh
        pass

    def getApplication(self, request):

        # If the first name is '++etc++ApplicationControl', then we should
        # get it rather than look in the database!
        stack = request.getTraversalStack()

        if '++etc++ApplicationController' in stack:
            return applicationControllerRoot
        
        # Open the database.
        version = request.get(self.version_cookie, '')
        conn = self.db.open(version)

        cleanup = Cleanup(conn.close)
        request.hold(cleanup)  # Close the connection on request.close()

        self.openedConnection(conn)
##        conn.setDebugInfo(getattr(request, 'environ', None), request.other)

        root = conn.root()
        app = root.get(self.root_name, None)
        
        if app is None:
            raise SystemError, "Zope Application Not Found"

        return ProxyFactory(app)

    def getDefaultTraversal(self, request, ob):
        return ob, None

    def callObject(self, request, ob):
        return mapply(ob, request.getPositionalArguments(), request)

    def afterCall(self, request):
        get_transaction().commit()

    def handleException(self, object, request, exc_info, retry_allowed=1):
        try:
            # Abort the transaction.
            get_transaction().abort()

            try:
                errService = getService(object,'ErrorReportingService')
            except ComponentLookupError:
                pass
            else:
                try:
                    errService.raising(exc_info, request)
                # It is important that an error in errService.raising
                # does not propagate outside of here. Otherwise, nothing
                # meaningful will be returned to the user.
                #
                # The error reporting service should not be doing database
                # stuff, so we shouldn't get a conflict error.
                # Even if we do, it is more important that we log this
                # error, and proceed with the normal course of events.
                # We should probably (somehow!) append to the standard
                # error handling that this error occurred while using
                # the ErrorReportingService, and that it will be in
                # the zope log.
                except:
                    logging.getLogger('SiteError').exception(
                        'Error while reporting an error to the '
                        'ErrorReportingService')

            # Delegate Unauthorized errors to the authentication service
            # XXX Is this the right way to handle Unauthorized?  We need
            # to understand this better.
            if isinstance(exc_info[1], Unauthorized):
                sm = getSecurityManager()
                id = sm.getPrincipal()
                prin_reg.unauthorized(id, request) # May issue challenge
                request.response.handleException(exc_info)
                return

            # XXX This is wrong. Should use getRequstView:
            # 
            # 
            # # Look for a component to handle the exception.
            # traversed = request.traversed
            # if traversed:
            #     context = traversed[-1]
            #     #handler = getExceptionHandler(context, t, IBrowserPublisher)
            #     handler = None  # no getExceptionHandler() exists yet.
            #     if handler is not None:
            #         handler(request, exc_info)
            #         return

            # Convert ConflictErrors to Retry exceptions.
            if retry_allowed and isinstance(exc_info[1], ConflictError):
                logger.getLogger('ZopePublication').warn(
                    'Competing writes/reads at %s',
                    request.get('PATH_INFO', '???'),
                    exc_info=True)
                raise Retry

            # Let the response handle it as best it can.
            # XXX Is this what we want in the long term?
            response = request.response
            response.handleException(exc_info)
            return
        finally:
            # Avoid leaking
            exc_info = 0


    def _parameterSetskin(self, pname, pval, request):
        request.setViewSkin(pval)
        
class DebugPublication(object):

    class call_wrapper:

        def __init__(self, ob):
            self.__ob = ob

        def __getattr__(self, name):
            return getattr(self.__ob, name)

        def __call__(self, *args, **kw):
            self.__ob(*args, **kw)

    def callObject(self, request, ob):
        return mapply(self.call_wrapper(ob),
                      request.getPositionalArguments(), request)