=========
Skinnable
=========

Request can provide skins. But what's exactly a skin. At the code level, a skin
is just an interface which a request provides. Why do we need skins? We can use
skins for register different adapter.

That's a little bit much use of the word skin. Let's explain it mor detailed.
A skin is an interface which provides a type interface. This type interface 
is called ISkinType. The zope.publisher right now provides only one specific
skin type interface used in the IBrowserRequest implementation. This interface
is called BrowserSkinType.

Since the zope server provides request factories for biuld a request, each
such request type could provide it's own skin type interface. This ensures that
we can register a skin type for each request.

Now a more high level point of view. A skin is a concept which we can use for
provide different kind of views, templates or other adapter adapting a request.
This skins are the key component for provide different kind of application
layers. Then a skin makes it possible that an application can act very
different with each skin. Of corse that's only the case at the interaction
level where the request is involved. But that's moste the time the case since
we have an web application server. 

Another part of the skinnable concept is that a skin can define a default skin.
This is done within the IDefaultSkin interface. Such a default skin get defined
at the level request implementation level. Such a default skin can get overriden
in a custom setup. Overriding a skin can be done by using the defaultSkin
directive offeren from zope.app.publication.zcml.

Why does a request need a default skin. If a request needs to provide some
pluggable concepts which requires that a default adapter is registered for
a request, this adapter could be registered for the default skin. If a project
likes to use another pattern and needs to register another request adapter, the
project could register it's own skin and register the custom adapter for this
new project based skin. This is very handy and allows to skip a complete
default skin based setup for a given request.

In general this means a request interface and the request class wich implements
the request interface does only provide the basic API but no adapters if the
request needs to delegate things to an adapter. For such a request a default
skin can get defined. This default skin can provide all adatpers which the
request implementation needs to have. This gives us to option to replace the
default skin within an own skin and provide custom adapters.

Our exmple will define a full request and all it's component from scratch.
it doesn't depend on IBrowserRequest. We'll use a JSON-RPC as sample like
the z3c.jsonrpc package provides.

Layers and Skins
----------------

We also use the term layer if we talk about skins. A layer or skin layer is an
interface registered as a ISkinType without a name. Zope provides a traversal
pattern which allows to traverse a skin within a skin namespace called
``skin``. This allows to traverse to traverse to a method called applySkin
which will aplly a registered named skin. this means if we register a ISkinType
as within an optional name argument, we will register a skin. if we register a
ISkinType without a name just we register a layer. This means, layers are not
traversable ISkinType interfaces.

Let's start define a request:

  >>> from zope.publisher.interfaces import IRequest
  >>> class IJSONRequest(IRequest):
  ...     """JSON request."""

And we define a skin type:

  >>> from zope.publisher.interfaces import ISkinType
  >>> class IJSONSkinType(ISkinType):
  ...     """JSON skin type."""

A request would implement the IJSONRequest interface but not the request type
interface:

  >>> import zope.interface
  >>> from zope.publisher.base import BaseRequest
  >>> class JSONRequest(BaseRequest):
  ...     """JSON request implementation."""
  ...     zope.interface.implements(IJSONRequest)

Now our request provides IJSONRequest because it implement that interface:

  >>> from StringIO import StringIO
  >>> request = JSONRequest(StringIO(''), {})
  >>> IJSONRequest.providedBy(request)
  True


setDefaultSkin
--------------

The default skin is a marker interface that can be registered as an
adapter that provides IDefaultSkin for the request type. A default skin
interface like any other skin must also provide ISkinType. This is
important since applySkin will lookup for skins based on this type.

Note: Any interfaces that are directly provided by the request coming into
this method are replaced by the applied layer/skin interface. This is very
important since the retry pattern can use a clean request without any
directly provided interface after a retry get started.

If a default skin is not available, the fallback default skin get applied
if available for the given request type. The default fallback skin is
implemented as an named adapter factory providing IDefaultSkin and
using ``default`` as name. 

Important to know is that some skin adapters get registered as interfaces
and the fallback skins as adapters. See the defaultSkin directive in 
zope.app.publication.zcml for more information which registers plain
interfaces as adapters which are not adaptable. (issue?)

Each request can only have one (unnamed) default skin and will fallback to
the named (default) fallback skin if available.

Only the IBrowserRequest provides such a default fallback adapter. This
adapter will apply the IDefaultBrowserLayer if no explicit default skin
is registered for IBrowserRequest.

Our test setup requires a custom default layer which we will apply to our
request. Let's define a custm layer:

  >>> class IJSONDefaultLayer(zope.interface.Interface):
  ...     """JSON default layyer."""

To illustrate, we'll first use setDefaultSkin without a registered
IDefaultSkin adapter:

  >>> IJSONDefaultLayer.providedBy(request)
  False

If we try to set a default skin and no one exist we will not fail but
nothing happens

  >>> from zope.publisher. handy andult = ProxyFactory(fault)
  >>> stripped_fault = premarshal(proxied_fault)
  >>> type(stripped_fault) is Proxy
  False

Standard python datetime objects are also handled:

  >>> import datetime
  >>> sample = datetime.datetime(2006,06,17,21,41,00)
  >>> stripped_date = premarshal(sample)
  >>> isinstance(stripped_date, datetime.datetime)
  False
  >>> isinstance(stripped_date, xmlrpclib.DateTime)
  True

We can also use premarshal to strip proxies off of Binary objects.
We have to make a security declaration first though:

  >>> import xmlrpclib
  >>> binary = xmlrpclib.Binary('foobar')
  >>> proxied_binary = ProxyFactory(binary)
  >>> stripped_binary = premarshal(proxied_binary)
  >>> type(stripped_binary) is Proxy
  False
                                                                                                                                                                                                                                           ./usr/lib/zope2.12/lib/python/zope.publisher-3.8.0.egg/zope/publisher/xmlrpc.py                     0000644 0000000 0000000 00000017134 12115616160 025653  0                                                                                                    ustar   root                            root                                                                                                                                                                                                                   ##############################################################################
#
# 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.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.
#
##############################################################################
"""XML-RPC Publisher

This module contains the XMLRPCRequest and XMLRPCResponse

$Id: xmlrpc.py 82493 2007-12-28 14:12:27Z ctheune $
"""
__docformat__ = 'restructuredtext'

import sys
import xmlrpclib
import datetime
from StringIO import StringIO

import zope.component
import zope.interface
from zope.interface import implements
from zope.publisher.interfaces.xmlrpc import \
        IXMLRPCPublisher, IXMLRPCRequest, IXMLRPCPremarshaller

from zope.publisher.http import HTTPRequest, HTTPResponse, DirectResult
from zope.security.proxy import isinstance

class XMLRPCRequest(HTTPRequest):
    implements(IXMLRPCRequest)

    _args = ()

    def _createResponse(self):
        """Create a specific XML-RPC response object."""
        return XMLRPCResponse()

    def processInputs(self):
        'See IPublisherRequest'
        # Parse the request XML structure

        # XXX using readlines() instead of lines()
        # as twisted's BufferedStream sends back
        # an empty stream here for read() (bug)
        lines = ''.join(self._body_instream.readlines())
        self._args, function = xmlrpclib.loads(lines)

        # Translate '.' to '/' in function to represent object traversal.
        function = function.split('.')

        if function:
            self.setPathSuffix(function)


class TestRequest(XMLRPCRequest):

    def __init__(self, body_instream=None, environ=None, response=None, **kw):

        _testEnv =  {
            'SERVER_URL':         'http://127.0.0.1',
            'HTTP_HOST':          '127.0.0.1',
            'CONTENT_LENGTH':     '0',
            'GATEWAY_INTERFACE':  'TestFooInterface/1.0',
            }

        if environ:
            _testEnv.update(environ)
        if kw:
            _testEnv.update(kw)
        if body_instream is None:
            body_instream = StringIO('')

        super(TestRequest, self).__init__(body_instream, _testEnv, response)


class XMLRPCResponse(HTTPResponse):
    """XMLRPC response.

    This object is responsible for converting all output to valid XML-RPC.
    """

    def setResult(self, result):
        """Sets the result of the response

        Sets the return body equal to the (string) argument "body". Also
        updates the "content-length" return header.

        If the body is a 2-element tuple, then it will be treated
        as (title,body)

        If is_error is true then the HTML will be formatted as a Zope error
        message instead of a generic HTML page.
        """
        body = premarshal(result)
        if isinstance(body, xmlrpclib.Fault):
            # Convert Fault object to XML-RPC response.
            body = xmlrpclib.dumps(body, methodresponse=True)
        else:
            # Marshall our body as an XML-RPC response. Strings will be sent
            # as strings, integers as integers, etc.  We do *not* 