[Zope3-checkins] CVS: Zope3/src/zope/app/services - presentation.py:1.2 presentation.zcml:1.2

Jim Fulton cvs-admin at zope.org
Fri Nov 21 12:10:30 EST 2003


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

Added Files:
	presentation.py presentation.zcml 
Log Message:
Implemented a local presentation service based on local surrogates.


=== Zope3/src/zope/app/services/presentation.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 21 12:10:29 2003
+++ Zope3/src/zope/app/services/presentation.py	Fri Nov 21 12:09:59 2003
@@ -0,0 +1,537 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Local presentation service
+
+$Id$
+"""
+
+from zope.app import zapi
+from zope.app.i18n import ZopeMessageIDFactory as _
+from zope.component.presentation import IDefaultViewName
+from zope.security.checker import NamesChecker, ProxyFactory
+import persistence.dict
+import zope.app.component.interfacefield
+import zope.app.component.nextservice
+import zope.app.container.contained
+import zope.app.interfaces.services.registration
+import zope.app.interfaces.services.service
+import zope.app.services.adapter
+import zope.app.services.field
+import zope.app.interfaces.services.interface
+import zope.app.services.surrogate
+import zope.app.services.zpt
+import zope.component.interfaces
+import zope.configuration.exceptions
+import zope.interface
+import zope.proxy
+import zope.publisher.interfaces.browser
+import zope.schema
+
+# XXX How do we define skins and layers here?
+# For now, we will leave skin and layer definition to services above,
+# which effectively means to the global service.
+
+class LocalPresentationService(
+    zope.app.services.surrogate.LocalSurrogateBasedService,
+    ):
+
+    zope.interface.implements(
+        zope.component.interfaces.IPresentationService,
+        zope.app.interfaces.services.service.ISimpleService,
+        zope.app.interfaces.services.registration.IRegistry,
+        zope.app.interfaces.services.interface.IInterfaceBasedRegistry,
+        )
+
+    next = base = None
+
+    def __init__(self):
+        self.layers = persistence.dict.PersistentDict()
+        self.base = zapi.getService(None, zapi.servicenames.Presentation)
+
+    def setNext(self, next, global_):
+        if next is None:
+            self.delegate = global_
+        else:
+            self.delegate = next
+            
+        self.next = next
+        self.base = global_
+        for layername in self.layers:
+            nextlayer = next.queryLayer(layername)
+            globlayer = global_.queryLayer(layername)
+            self.layers[layername].setNext(nextlayer, globlayer)
+
+    def defaultSkin(self):
+        return self.delegate.defaultSkin
+    defaultSkin = property(defaultSkin)
+
+    def querySkin(self, name):
+        return self.delegate.querySkin(name)
+
+    def queryLayer(self, name):
+        r = self.layers.get(name)
+        if r is not None:
+            return r
+        return self.delegate.queryLayer(name)
+
+    def queryView(self, object, name, request,
+                  providing=zope.interface.Interface, default=None):
+        """Look for a named view for a given object and request
+
+        The request must implement IPresentationRequest.
+
+        The default will be returned if the component can't be found.
+        """
+        skin = request.getPresentationSkin() or self.defaultSkin
+        layers = self.querySkin(skin)
+        if not layers:
+            return default
+        
+        objects = object, request
+        for layername in layers:
+            layer = self.layers.get(layername)
+            if layer is None:
+                layer = self.delegate.queryLayer(layername)
+                if layer is None:
+                    raise ValueError("Bad layer", layer)
+
+            r = layer.queryMultiAdapter(objects, providing, name)
+            if r is not None:
+                return r
+        return default
+
+
+    def queryResource(self, name, request, providing=zope.interface.Interface,
+                      default=None):
+        """Look up a named resource for a given request
+        
+        The request must implement IPresentationRequest.
+        
+        The default will be returned if the component can't be found.
+        """
+        skin = request.getPresentationSkin() or self.defaultSkin
+        layers = self.querySkin(skin)
+        if not layers:
+            return default
+
+        for layername in layers:
+            layer = self.layers.get(layername)
+            if layer is None:
+                layer = self.delegate.queryLayer(layername)
+            if layer is None:
+                raise ValueError("Bad layer", layer)
+
+            r = layer.queryNamedAdapter(request, providing,
+                                        name)
+            if r is not None:
+                return r
+
+        return default
+
+    def queryMultiView(self, objects, name, request,
+                       providing=zope.interface.Interface, default=None):
+        """Adapt the given objects and request
+
+        The first argument is a sequence of objects to be adapted with the
+        request.
+        """
+
+        skin = request.getPresentationSkin() or self.defaultSkin
+        layers = self.querySkin(skin)
+        if not layers:
+            return default
+
+        objects = objects + (request, )
+        for layername in layers:
+            layer = self.layers.get(layername)
+            if layer is None:
+                layer = self.delegate.queryLayer(layername)
+            if layer is None:
+                raise ValueError("Bad layer", layer)
+
+            r = layer.queryMultiAdapter(objects, providing, name)
+            if r is not None:
+                return r
+        return default
+
+    def queryDefaultViewName(self, object, request, default=None):
+        skin = request.getPresentationSkin() or self.defaultSkin
+        layers = self.querySkin(skin)
+        if not layers:
+            return default
+
+        objects = object, request
+        for layername in layers:
+            layer = self.layers.get(layername)
+            if layer is None:
+                layer = self.delegate.queryLayer(layername)
+            if layer is None:
+                raise ValueError("Bad layer", layer)
+            r = layer.queryMultiAdapter(objects, IDefaultViewName, raw=True)
+            if r is not None:
+                return r
+        return default
+
+
+    def queryRegistrationsFor(self, registration, default=None):
+        layername = registration.layer
+        layer = self.layers.get(layername)
+        if layer is None:
+            return default
+        return layer.queryRegistrationsFor(registration, default)
+
+    def createRegistrationsFor(self, registration):
+        layername = registration.layer
+        layer = self.layers.get(layername)
+        if layer is None:
+            if self.next is None:
+                next = None
+            else:
+                next = self.next.queryLayer(layername)
+            base = self.base.queryLayer(layername)
+            if base is None:
+                raise ValueError("Undefined layer", layername)
+            layer = LocalLayer(base, next, self, layername)
+            self.layers[layername] = layer
+            
+        return layer.createRegistrationsFor(registration)
+
+    def getRegistrationsForInterface(self, required):
+        # XXX relying on global service for layer definitions
+
+        iro = required.__iro__ + (None,)
+        for layername in self.base._layers:
+            layer = self.queryLayer(layername)
+            if isinstance(layer, LocalLayer):
+                while layer is not None:
+                    for iface in iro:
+                        stacks = layer.stacks.get(iface)
+                        if not stacks:
+                            continue
+                        for stack in stacks.itervalues():
+                            registration = stack.active()
+                            if registration is not None:
+                                yield registration
+                    layer = layer.next
+                layer = self.base.queryLayer(layername)
+            if layer is None:
+                continue
+
+            for (req, provided, with, name, factories
+                 ) in layer.getRegisteredMatching(required=required):
+                # XXX just do views for now. We need a more general
+                # solution
+                if len(with) == 1:
+                    yield GlobalViewRegistration(req, with[0], factories,
+                                                 layername, name)
+                    
+                
+
+class GlobalViewRegistration:
+    """Registrations representing global view service thingies."""
+
+    zope.interface.implements(
+        zope.app.interfaces.services.registration.IRegistration)
+
+    serviceType = zapi.servicenames.Presentation
+    status = zope.app.interfaces.services.registration.ActiveStatus
+
+    def __init__(self, req, ptype, factories, layer, viewName):
+        self.required = req
+        self.ptype = ptype
+        self.factories = factories
+        self.layer = layer
+        self.viewName = viewName
+
+    def usageSummary(self):
+        if self.required is None:
+            ifname = _("any-interface", "Anything")
+        else:
+            ifname = self.required.getName()
+        summary = _("${view_name} ${ptype} View for ${iface_name}")
+        if self.layer and self.layer != "default":
+            summary = _(
+                "${view_name} ${ptype} View for ${iface_name} in layer ${layer}"
+                )
+        summary.mapping = {'view_name':  self.viewName,
+                           'ptype':      self.ptype.getName(),
+                           'iface_name': ifname,
+                           'layer':      self.layer}
+        return summary
+
+    def implementationSummary(self):
+        # XXX This should report the ZCML that it came from.
+        return _("Registered by ZCML")
+
+
+class LocalLayer(
+    zope.app.services.surrogate.LocalSurrogateRegistry,
+    zope.app.container.contained.Contained,
+    ):
+
+    def __init__(self, base, next, parent, name):
+        zope.app.services.surrogate.LocalSurrogateRegistry.__init__(
+            self, base, next)
+        self.__parent__ = parent
+        self.__name__ = name
+
+
+class IViewRegistration(zope.app.services.adapter.IAdapterRegistration):
+
+    required = zope.app.component.interfacefield.InterfaceField(
+        title = u"For interface",
+        description = u"The interface of the objects being viewed",
+        readonly = True,
+        required = True,
+        basetype = None
+        )
+
+    requestType = zope.app.component.interfacefield.InterfaceField(
+        title = u"Request type",
+        description = u"The type of requests the view works with",
+        readonly = True,
+        required = True,
+        )
+
+    layer = zope.schema.BytesLine(
+        title = u"Layer",
+        description = u"The skin layer the view is registered for",
+        required = False,
+        readonly = True,
+        min_length = 1,
+        default = "default",
+        )
+
+class ViewRegistration(zope.app.services.registration.SimpleRegistration):
+
+    zope.interface.implements(IViewRegistration)
+
+    serviceType = zapi.servicenames.Presentation
+
+    provided = zope.interface.Interface
+
+    # For usageSummary(); subclass may override
+    _what = _("view-component", 'View')
+
+    def __init__(self,
+                 required, name, requestType,
+                 factoryName, permission, layer='default'):
+        self.required = required
+        self.requestType = requestType
+        self.factoryName = factoryName
+        self.name = name
+        self.layer = layer
+        self.permission = permission
+
+    def usageSummary(self):
+        if self.required is None:
+            ifname = _('any-interface', "Anything")
+        else:
+            ifname = self.required.getName()
+
+        pname = self.requestType.getName()
+        summary = _("${view_name} for ${pname} {what} {iface_name}")
+        if self.layer and self.layer != "default":
+            summary = _(
+                "${view_name} for ${pname} ${what} ${iface_name}"
+                " in layer ${layer}"
+                )
+        summary.mapping = {'view_name':  self.name,
+                           'pname':      pname,
+                           'what':       self._what,
+                           'iface_name': ifname,
+                           'layer':      self.layer}
+        return summary
+
+    def with(self):
+        return (self.requestType, )
+    with = property(with)
+
+    def factories(self):
+        folder = self.__parent__.__parent__
+        return (folder.resolve(self.factoryName), )
+    factories = property(factories)
+
+class IPageRegistration(IViewRegistration):
+
+    factoryName = zope.schema.BytesLine(
+        title=u"Page class",
+        required = False,
+        )
+
+    template = zope.app.services.field.ComponentPath(
+        title = u"Page template",
+        required = False,
+        type = zope.app.services.zpt.IZPTTemplate,
+        )
+
+    attribute = zope.schema.TextLine(
+        title = u"Class attribute",
+        required = False,
+        )
+
+    factories = zope.interface.Attribute(
+        "A sequence of factories to be called to construct an adapter"
+        )
+
+    def validate(self):
+        """Verifies that the registration is valid.
+
+        Raises a ConfigurationError if the validation is failed.
+        """
+
+class PageRegistration(ViewRegistration):
+
+    zope.interface.implements(IPageRegistration)
+
+    # We only care about browser pages
+    requestType = zope.publisher.interfaces.browser.IBrowserRequest
+
+    # For usageSummary()
+    _what = _("page-component", "Page")
+
+    def __init__(self,
+                 required, name, permission,
+                 factoryName=None, template=None, attribute=None,
+                 layer='default'):
+
+        # XXX A Interface comes out of the interface widget
+        # wrapped on a proxy currently, which is not pickable
+        required = zope.proxy.removeAllProxies(required)
+
+        super(PageRegistration, self).__init__(
+            required, name, self.requestType,
+            factoryName, permission, layer)
+
+        self.template = template
+        self.attribute = attribute
+
+    def implementationSummary(self):
+        L = []
+        if self.template:
+            prefix = "/++etc++site/"
+            t = self.template
+            i = t.rfind(prefix)
+            if i >= 0:
+                t = t[i + len(prefix):]
+            L.append("template=%s" % t)
+        if self.factoryName:
+            L.append("class=%s" % self.factoryName)
+        if self.attribute:
+            L.append("attribute=%s" % self.attribute)
+        return ", ".join(L)
+
+    def validate(self):
+        if self.template and self.attribute:
+            raise zope.configuration.exceptions.ConfigurationError(
+                "PageRegistration for %s view name %s: "
+                "Cannot have both 'template' and 'attribute' at the same "
+                "time." %
+                (self.required, self.name))
+
+        if not self.template and not self.attribute:
+            raise zope.configuration.exceptions.ConfigurationError(
+                "PageRegistration for %s view name %s: "
+                "Should have a 'template' or 'attribute' attribute." %
+                (self.required, self.name))
+
+        if not self.factoryName and self.attribute:
+            raise zope.configuration.exceptions.ConfigurationError(
+                "PageRegistration for %s view name %s: "
+                "Cannot have an 'attribute' without a 'factoryName'." %
+                (self.required, self.name))
+
+    def factories(self):
+
+        self.validate()
+
+        sm = zapi.getServiceManager(self)
+
+        if self.factoryName:
+            folder = self.__parent__.__parent__
+            class_ = folder.resolve(self.factoryName)
+        else:
+            class_  = DefaultClass
+
+
+        # This is needed because we need to do an unrestricted zapi.traverse
+        root = zope.proxy.removeAllProxies(zapi.getRoot(sm))
+
+        if self.attribute:
+            return (AttrViewFactory(class_, self.attribute), )
+
+        else:
+            template = zapi.traverse(root, self.template)
+            return (TemplateViewFactory(class_, template, self.permission), )
+
+    factories = property(factories)
+
+class TemplateViewFactory:
+
+    def __init__(self, cls, template, permission):
+        self.cls, self.template, self.permission = cls, template, permission
+
+    def __call__(self, object, request):
+        checker = NamesChecker(__call__ = self.permission)
+        template = BoundTemplate(self.template, self.cls(object, request))
+        return ProxyFactory(template, checker)
+
+class AttrViewFactory:
+
+    def __init__(self, cls, attr):
+        self.cls, self.attr = cls, attr
+
+    def __call__(self, object, request):
+        attr = getattr(self.cls(object, request), self.attr)
+        return ProxyFactory(attr)
+
+class DefaultClass:
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+class BoundTemplate:
+
+    def __init__(self, template, view):
+        self.template = template
+        self.view = view
+
+    def __call__(self, template_usage=u'', *args, **kw):
+        if not template_usage:
+            kw["template_usage"] = template_usage
+        return self.template.render(self.view, *args, **kw)
+
+#BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+
+from zope.app.event.function import Subscriber
+import persistence
+import sys
+from zope.interface.surrogate import ReadProperty
+
+ViewRegistration.required    = ReadProperty(lambda self: self.forInterface)
+ViewRegistration.factoryName = ReadProperty(lambda self: self.class_)
+ViewRegistration.name        = ReadProperty(lambda self: self.viewName)
+
+class ViewService(persistence.Persistent):
+    pass
+
+def fixup(event):
+    # We delay this evil hackery until database open to prevent someone
+    # successfully importing IBrowserPresentation through a normal import
+    sys.modules['zope.app.services.view'] = sys.modules[__name__]
+    IBrowserRequest = zope.publisher.interfaces.browser.IBrowserRequest
+    zope.publisher.interfaces.browser.IBrowserPresentation = IBrowserRequest 
+    
+fixup = Subscriber(fixup)


=== Zope3/src/zope/app/services/presentation.zcml 1.1 => 1.2 ===
--- /dev/null	Fri Nov 21 12:10:29 2003
+++ Zope3/src/zope/app/services/presentation.zcml	Fri Nov 21 12:09:59 2003
@@ -0,0 +1,41 @@
+<configure
+    xmlns='http://namespaces.zope.org/zope'
+    xmlns:event='http://namespaces.zope.org/event'
+    xmlns:fssync='http://namespaces.zope.org/fssync'
+    >
+
+
+<content class=".presentation.LocalPresentationService">
+  <factory permission="zope.ManageServices" />
+  <require
+      permission="zope.ManageServices"
+      interface="zope.app.interfaces.services.registration.IRegistry"
+      attributes="getRegisteredMatching"
+      />
+</content>
+
+<content class=".presentation.ViewRegistration">
+  <require
+      permission="zope.ManageServices"
+      interface=".presentation.IViewRegistration"
+      set_schema="zope.app.interfaces.services.registration.IRegistration"
+      />
+  <require
+      permission="zope.ManageServices"
+      interface="zope.app.interfaces.container.IRemoveNotifiable"
+      />
+</content>
+
+<content class=".presentation.PageRegistration">
+  <require
+      permission="zope.ManageServices"
+      interface=".presentation.IPageRegistration"
+      set_schema=".presentation.IPageRegistration"
+      />
+  <require
+      permission="zope.ManageServices"
+      interface="zope.app.interfaces.container.IRemoveNotifiable"
+      />
+</content>
+
+</configure>




More information about the Zope3-Checkins mailing list