[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services - field.py:1.1.2.1 zpt.py:1.1.2.1 configure.zcml:1.17.8.2 interfaces.py:1.1.2.4 view.py:1.1.2.2

Jim Fulton jim@zope.com
Thu, 12 Dec 2002 10:20:25 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Services
In directory cvs.zope.org:/tmp/cvs-serv23093/lib/python/Zope/App/OFS/Services

Modified Files:
      Tag: AdapterAndView-branch
	configure.zcml interfaces.py view.py 
Added Files:
      Tag: AdapterAndView-branch
	field.py zpt.py 
Log Message:
Got view service and view registration working TTW (sort of).

This includes a new "View Package". You set up a view package with
default registration parameters. Then, as you add ZPT templates to the
package, they are automatically registered as views.

There are lots of rough edges that need to be smoothed out after the
sprint and before this branch merge.



=== Added File Zope3/lib/python/Zope/App/OFS/Services/field.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.
# 
##############################################################################
"""Component location field.

$Id: field.py,v 1.1.2.1 2002/12/12 15:19:54 jim Exp $
"""
__metaclass__ = type

from Zope.Schema.IField import IField
from Zope.Schema import Field
from Zope.Schema.Exceptions import ValidationError
from Zope.App.Traversing import traverse
from Zope.App.ComponentArchitecture.InterfaceField import InterfaceField
from Zope.Exceptions import NotFoundError

class IComponentLocation(IField):
    """A field containing a component path.
    """

    type = InterfaceField(
        title = u"An interface that must be implemented by the component.",
        required = True,
        readonly = True,
        )

class ComponentLocation(Field):

    __implements__ = IComponentLocation

    _type = unicode

    def __init__(self, type, *args, **kw):
        self.type = type
        super(ComponentLocation, self).__init__(*args, **kw)

    def _validate(self, value):
        super(ComponentLocation, self)._validate(value)
        
        if not value.startswith('/'):
            raise ValidationError("Not an absolute path", value)
        
        try:
            component = traverse(self.context, value)
        except NotFoundError:
            raise ValidationError("Path for non-existent object", value)
        
        if not self.type.isImplementedBy(component):
            raise ValidationError("Wrong component type")


=== Added File Zope3/lib/python/Zope/App/OFS/Services/zpt.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.
#
##############################################################################
"""
$Id: zpt.py,v 1.1.2.1 2002/12/12 15:19:54 jim Exp $
"""

import re

from Interface import Interface
from Interface.Attribute import Attribute
import Zope.Schema
from Persistence import Persistent

from Zope.ContextWrapper import ContextMethod
from Zope.Proxy.ContextWrapper import getWrapperContainer
from Zope.Security.Proxy import ProxyFactory

from Zope.App.OFS.Content.IFileContent import IFileContent
from Zope.PageTemplate.PageTemplate import PageTemplate
from Zope.App.PageTemplate.Engine import AppPT
from interfaces import IZPTTemplate

class ZPTTemplate(AppPT, PageTemplate, Persistent):

    __implements__ = IZPTTemplate

    contentType = 'text/html'

    source = property(
        # get
        lambda self: self.read(),
        # set
        lambda self, text: self.pt_edit(text.encode('utf-8'), self.contentType)
        )

    def pt_getContext(self, view, **_kw):
        # instance is a View component
        namespace = super(ZPTTemplate, self).pt_getContext(**_kw)
        namespace['view'] = view
        namespace['request'] = view.request
        namespace['context'] = view.context
        return namespace

    def render(self, view, *args, **keywords):
        
        if args:
            args = ProxyFactory(args)
        kw = ProxyFactory(keywords)

        namespace = self.pt_getContext(view, args=args, options=kw)

        return self.pt_render(namespace)

# Adapter for ISearchableText

from Zope.App.index.text.interfaces import ISearchableText

tag = re.compile(r"<[^>]+>")
class SearchableText:

    __implements__ = ISearchableText
    __used_for__ = IZPTTemplate

    def __init__(self, page):
        self.page = page

    def getSearchableText(self):
        text = self.page.source
        if isinstance(text, str):
            text = unicode(self.page.source, 'utf-8')
        # else:
        #   text was already Unicode, which happens, but unclear how it
        #   gets converted to Unicode since the ZPTPage stores UTF-8 as
        #   an 8-bit string.
        
        if self.page.contentType.startswith('text/html'):
            text = tag.sub('', text)

        return [text]


=== Zope3/lib/python/Zope/App/OFS/Services/configure.zcml 1.17.8.1 => 1.17.8.2 ===
--- Zope3/lib/python/Zope/App/OFS/Services/configure.zcml:1.17.8.1	Wed Dec 11 06:41:08 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/configure.zcml	Thu Dec 12 10:19:54 2002
@@ -3,12 +3,16 @@
    xmlns:browser="http://namespaces.zope.org/browser"
    >
 
+<!-- Configuration registries -->
+
 <content class=".Configuration.ConfigurationRegistry">
    <require permission="Zope.ManageServices"
             interface=".ConfigurationInterfaces.IConfigurationRegistry"
             />
 </content>
 
+<!-- Adapter Service -->
+
 <content class=".adapter.AdapterService">
     <implements interface="Zope.App.OFS.Annotation.IAttributeAnnotatable." />
     <factory id="Zope.App.OFS.Services.AdapterService"
@@ -33,6 +37,67 @@
         />
 </content>    
 
+<!-- View Service -->
+
+<content class=".view.ViewService">
+    <implements interface="Zope.App.OFS.Annotation.IAttributeAnnotatable." />
+    <factory id="Zope.App.OFS.Services.ViewService"
+             permission="Zope.ManageServices"
+             />
+    <require permission="Zope.ManageServices"
+             interface=".ConfigurationInterfaces.IConfigurable"
+             attributes="getRegisteredMatching"
+             />
+</content>
+
+<content class=".view.ViewConfiguration">
+    <require
+        permission="Zope.ManageServices"
+        interface=".interfaces.IViewConfiguration"
+        set_schema=
+            "Zope.App.OFS.Services.ConfigurationInterfaces.IConfiguration"
+        />
+    <require
+        permission="Zope.ManageServices"
+        interface="Zope.App.OFS.Container.IDeleteNotifiable."
+        />
+</content>    
+
+<content class=".view.PageConfiguration">
+    <require
+        permission="Zope.ManageServices"
+        interface=".interfaces.IPageConfiguration"
+        set_schema=
+            "Zope.App.OFS.Services.ConfigurationInterfaces.IConfiguration"
+        />
+    <require
+        permission="Zope.ManageServices"
+        interface="Zope.App.OFS.Container.IDeleteNotifiable."
+        />
+</content>    
+
+<!-- Page Templates -->
+
+<content class=".zpt.ZPTTemplate">
+  <factory
+      id="Zope.app.services.zpt.template"
+      permission="Zope.ManageServices"
+      title="ZPT Template"
+      description="Page Template" />
+
+  <require
+      permission="Zope.View"
+      attributes="__call__" />
+
+  <require
+      permission="Zope.ManageServices"
+      interface=".interfaces.IZPTTemplate"
+      set_schema=".interfaces.IZPTTemplate" />
+
+  <implements
+      interface="Zope.App.OFS.Annotation.IAttributeAnnotatable." />
+</content>
+
 <include package=".ServiceManager" />
 <include package=".AuthenticationService" />
 <include package=".LocalEventService" />
@@ -42,8 +107,9 @@
 <include package=".CachingService" />
 <include package=".ObjectHub" />
 <include package=".ErrorReportingService" />
-<include package=".Browser" />
 <include package=".PrincipalAnnotationService" />
 <include package=".SessionService" />
+
+<include package=".Browser" />
 
 </zopeConfigure>


=== Zope3/lib/python/Zope/App/OFS/Services/interfaces.py 1.1.2.3 => 1.1.2.4 ===
--- Zope3/lib/python/Zope/App/OFS/Services/interfaces.py:1.1.2.3	Wed Dec 11 09:10:24 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/interfaces.py	Thu Dec 12 10:19:54 2002
@@ -18,8 +18,10 @@
 
 from ConfigurationInterfaces import IConfiguration
 from Zope.App.ComponentArchitecture.InterfaceField import InterfaceField
-from Zope.Schema import BytesLine, TextLine
+from Zope.Schema import BytesLine, TextLine, Text
 from Interface import Interface
+from Zope.App.OFS.Services.field import ComponentLocation
+from Zope.ComponentArchitecture.IPresentation import IPresentation
 
 class IAdapterConfigurationInfo(Interface):
 
@@ -66,10 +68,11 @@
         description = u"The presentation type of a view",
         readonly = True,
         required = True,
+        type = IPresentation,
         )
 
     factoryName = BytesLine(
-        title=u"The dotted name of a factory for creating the adapter",
+        title=u"The dotted name of a factory for creating the view",
         readonly = True,
         required = True,
         )
@@ -78,6 +81,7 @@
         title = u"View name",
         readonly = True,
         required = True,
+        min_length = 1,
         )
 
     layer = BytesLine(
@@ -85,6 +89,7 @@
         description = u"The skin layer the view is registered for",
         required = False,
         readonly = True,
+        min_length = 1,
         default = "default",
         )
 
@@ -98,3 +103,51 @@
         """
 
 
+class IZPTTemplate(Interface):
+    """ZPT Templates for use in views
+    """
+
+    contentType = BytesLine(
+        title=u'Content type of generated output',
+        required=True,
+        default='text/html'
+        )
+
+    source = Text(
+        title=u"Source",
+        description=u"""The source of the page template.""",
+        required=True)
+
+    def render(context, request, *args, **kw):
+        """Render the page template.
+
+        The context argument is bound to the top-level 'context'
+        variable.  The request argument is bound to the top-level
+        'request' variable. The positional arguments are bound to the
+        'args' variable and the keyword arguments are bound to the
+        'options' variable.
+
+        """
+    
+class IPageConfigurationInfo(IViewConfigurationInfo):
+
+    factoryName = BytesLine(
+        title=u"The dotted name of a factory for creating the view",
+        readonly = True,
+        required = False,
+        )
+
+    template = ComponentLocation(
+        title = u"Page template",
+        required = False,
+        readonly = True,
+        type = IZPTTemplate,
+        )
+
+class IPageConfiguration(IConfiguration, IPageConfigurationInfo):
+
+    def getView(object, request):
+        """Return a page for the object.
+        """
+
+    


=== Zope3/lib/python/Zope/App/OFS/Services/view.py 1.1.2.1 => 1.1.2.2 ===
--- Zope3/lib/python/Zope/App/OFS/Services/view.py:1.1.2.1	Wed Dec 11 09:10:24 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/view.py	Thu Dec 12 10:19:54 2002
@@ -33,9 +33,12 @@
 from Zope.App.ComponentArchitecture.NextService import getNextService
 from Zope.ComponentArchitecture import getSkin
 
-from interfaces import IViewConfiguration
-from adapter import PersistentAdapterRegistry
+from Zope.Proxy.ProxyIntrospection import removeAllProxies
+from Zope.App.Traversing import getPhysicalRoot, traverse
+from Zope.Exceptions import NotFoundError
 
+from interfaces import IViewConfiguration, IPageConfiguration
+from adapter import PersistentAdapterRegistry
 
 class ViewService(Persistent):
 
@@ -141,12 +144,34 @@
             view = registry.active().getView(object, request)
             return view
 
-        views = getNextService(self, 'Views') 
+        views = getNextService(self, 'Views')
 
         return views.queryView(object, name, request, default)
 
     queryView = ContextMethod(queryView)
 
+    def getDefaultViewName(self, object, request):
+        "See Zope.ComponentArchitecture.IViewService.IViewService"
+
+        name = self.queryDefaultViewName(object, request)
+
+        if name is None:
+            raise NotFoundError, \
+                  'No default view name found for object %s' % object
+
+        return name
+
+    getDefaultViewName = ContextMethod(getDefaultViewName)
+
+    def queryDefaultViewName(self, object, request, default=None):
+        "See Zope.ComponentArchitecture.IViewService.IViewService"
+
+        # XXX: need to do our own defaults as well.
+        views = getNextService(self, 'Views')
+        return views.queryDefaultViewName(object, request, default)
+
+    queryDefaultViewName = ContextMethod(queryDefaultViewName)
+
     def getRegisteredMatching(self,
                               required_interfaces=None,
                               presentation_type=None,
@@ -206,3 +231,53 @@
 
     getView = ContextMethod(getView)
     
+class PageConfiguration(ViewConfiguration):
+
+    __implements__ = IPageConfiguration
+    
+    def __init__(self,
+                 forInterface, viewName, presentationType,
+                 factoryName, template,
+                 layer='default'):
+        super(PageConfiguration, self).__init__(
+            forInterface, viewName, presentationType,
+            factoryName, layer)
+
+        self.template = template
+
+    def getView(self, object, request):
+        sm = getServiceManager(self)
+
+        if self.factoryName:
+            factory = sm.resolve(self.factoryName)
+        else:
+            factory = DefaultFactory
+        
+        view = factory(object, request)
+
+        # This is needed because we need to do an unrestricted traverse
+        root = removeAllProxies(getPhysicalRoot(sm))
+        
+        template = traverse(root, self.template)
+
+        return BoundTemplate(template, view)
+
+    getView = ContextMethod(getView)
+
+
+class DefaultFactory:
+
+    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, *args, **kw):
+        return self.template.render(self.view, *args, **kw)
+
+