[Zope3-checkins] CVS: Zope3/src/zope/app/component - contentdirective.py:1.1 meta.zcml:1.3

Steve Alexander steve@cat-box.net
Mon, 12 May 2003 12:33:10 -0400


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

Modified Files:
	meta.zcml 
Added Files:
	contentdirective.py 
Log Message:
Moved  zope.app.contentdirective into zope.app.component.


=== Added File Zope3/src/zope/app/component/contentdirective.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.
#
##############################################################################
""" Register class directive.

$Id: contentdirective.py,v 1.1 2003/05/12 16:32:39 stevea Exp $
"""
from zope.interface import classProvides
from types import ModuleType
from zope.interface.implements import implements
from zope.configuration.interfaces import INonEmptyDirective
from zope.configuration.interfaces import ISubdirectiveHandler
from zope.component import getService
from zope.app.services.servicenames import Interfaces, Factories
from zope.configuration.exceptions import ConfigurationError
from zope.configuration.action import Action
from zope.app.component.classfactory import ClassFactory
from zope.app.component.metaconfigure import resolveInterface
from zope.app.security.protectclass \
    import protectLikeUnto, protectName, protectSetAttribute
from zope.app.security.registries.permissionregistry import permissionRegistry
from zope.security.proxy import ProxyFactory
from zope.security.checker import NamesChecker, CheckerPublic
from zope.schema.interfaces import IField

PublicPermission = 'zope.Public'

class ProtectionDeclarationException(Exception):
    """Security-protection-specific exceptions."""
    pass

def handler(serviceName, methodName, *args, **kwargs):
    method=getattr(getService(None, serviceName), methodName)
    method(*args, **kwargs)

def assertPermission(permission=None, *args, **kw):
    """Check if permission is defined"""
    if permission is not None:
        permissionRegistry.ensurePermissionDefined(permission)

class ContentDirective:

    classProvides(INonEmptyDirective)
    __implements__ = ISubdirectiveHandler

    def __init__(self, _context, class_):
        self.__id = class_
        self.__class = _context.resolve(class_)
        if isinstance(self.__class, ModuleType):
            raise ConfigurationError('Content class attribute must be a class')
        # not used yet
        #self.__name = class_
        #self.__normalized_name = _context.getNormalizedName(class_)
        self.__context = _context

    def implements(self, _context, interface):
        r = []
        for interface in interface.strip().split():

            resolved_interface = resolveInterface(_context, interface)
            r += [
                Action(
                    discriminator = ('ContentDirective', self.__class, object()),
                    callable = implements,
                    # the last argument is check=1, which causes implements
                    # to verify that the class does implement the interface
                    args = (self.__class, resolved_interface, 1),
                    ),
                Action(
                   discriminator = None,
                   callable = handler,
                   args = (Interfaces, 'provideInterface',
                           resolved_interface.__module__+
                           '.'+
                           resolved_interface.__name__,
                           resolved_interface)
                   )
                ]
        return r

    def require(self, _context,
                permission=None, attributes=None, interface=None,
                like_class=None, set_attributes=None, set_schema=None):
        """Require a the permission to access a specific aspect"""

        if like_class:
            r = self.__mimic(_context, like_class)
        else:
            r = []

        if not (interface or attributes or set_attributes or set_schema):
            if r:
                return r
            raise ConfigurationError("Nothing required")

        if not permission:
            raise ConfigurationError("No permission specified")


        if interface:
            for i in interface.strip().split():
                self.__protectByInterface(i, permission, r)
        if attributes:
            self.__protectNames(attributes, permission, r)
        if set_attributes:
            self.__protectSetAttributes(set_attributes, permission, r)
        if set_schema:
            for s in set_schema.strip().split():
                self.__protectSetSchema(s, permission, r)


        return r

    def __mimic(self, _context, class_):
        """Base security requirements on those of the given class"""
        class_to_mimic = _context.resolve(class_)
        return [
            Action(discriminator=('mimic', self.__class, object()),
                   callable=protectLikeUnto,
                   args=(self.__class, class_to_mimic),
                   )
            ]

    def allow(self, _context, attributes=None, interface=None):
        """Like require, but with permission_id zope.Public"""
        return self.require(_context, PublicPermission, attributes, interface)



    def __protectByInterface(self, interface, permission_id, r):
        "Set a permission on names in an interface."
        interface = resolveInterface(self.__context, interface)
        for n, d in interface.namesAndDescriptions(1):
            self.__protectName(n, permission_id, r)
        r.append(
            Action(
               discriminator = None,
               callable = handler,
               args = (Interfaces, 'provideInterface',
                       interface.__module__+ '.'+ interface.__name__,
                       interface)
               )
            )

    def __protectName(self, name, permission_id, r):
        "Set a permission on a particular name."
        r.append((
            ('protectName', self.__class, name),
            protectName, (self.__class, name, permission_id)))

    def __protectNames(self, names, permission_id, r):
        "Set a permission on a bunch of names."
        for name in names.split():
            self.__protectName(name.strip(), permission_id, r)

    def __protectSetAttributes(self, names, permission_id, r):
        "Set a permission on a bunch of names."
        for name in names.split():
            r.append((
                ('protectSetAttribute', self.__class, name),
                protectSetAttribute, (self.__class, name, permission_id)))

    def __protectSetSchema(self, schema, permission_id, r):
        "Set a permission on a bunch of names."
        schema = resolveInterface(self.__context, schema)
        for name in schema:
            field = schema[name]
            if IField.isImplementedBy(field) and not field.readonly:
                r.append((
                    ('protectSetAttribute', self.__class, name),
                    protectSetAttribute, (self.__class, name, permission_id)))

        r.append(
            Action(
               discriminator = None,
               callable = handler,
               args = (Interfaces, 'provideInterface',
                       schema.__module__+ '.'+ schema.__name__,
                       schema)
               )
            )


    def __call__(self):
        "Handle empty/simple declaration."
        return ()


    def factory(self, _context,
                permission=None, title="", id=None, description=''):
        """Register a zmi factory for this class"""

        id = id or self.__id

        # note factories are all in one pile, services and content,
        # so addable names must also act as if they were all in the
        # same namespace, despite the service/content division
        return [
            Action(
                discriminator = ('FactoryFromClass', id),
                callable = provideClass,
                args = (id, self.__class,
                        permission, title, description)
                )
            ]

def provideClass(id, _class, permission=None,
                 title='', description=''):
    """Provide simple class setup

    - create a component

    - set component permission
    """

    assertPermission(permission)
    factory = ClassFactory(_class)

    if permission == PublicPermission:
        permission = CheckerPublic

    if permission:
        # XXX should getInterfaces be public, as below?
        factory = ProxyFactory(factory,
                               NamesChecker(('getInterfaces',),
                                            __call__=permission))

    getService(None, Factories).provideFactory(id, factory)


=== Zope3/src/zope/app/component/meta.zcml 1.2 => 1.3 ===
--- Zope3/src/zope/app/component/meta.zcml:1.2	Wed Dec 25 09:12:45 2002
+++ Zope3/src/zope/app/component/meta.zcml	Mon May 12 12:32:39 2003
@@ -32,7 +32,7 @@
 
     <directive name="skin" attributes="name type layers" 
         handler="zope.app.component.metaconfigure.skin" />
-    
+
     <directive name="serviceType" attributes="id interface"
        handler="zope.app.component.metaconfigure.serviceType" />
 
@@ -40,5 +40,126 @@
        handler="zope.app.component.metaconfigure.service" />
 
   </directives>
+
+<directives namespace="http://namespaces.zope.org/zope">
+
+  <directive
+      name="content"
+      handler="zope.app.component.contentdirective.ContentDirective"
+      description="Make a component available as a content object type"
+      >
+    <attribute
+        name="class"
+        required="yes"
+        description="resolvable name of a class" 
+        />
+
+    <subdirective name="implements">
+
+      <description>
+            Declare that the class given by the content
+            directive's class attribute implements a given interface
+        </description>
+
+      <attribute
+          name="interface"
+          required="yes"
+          description="resolvable name of an Interface" 
+          />
+    </subdirective>
+
+    <subdirective name="require">
+      <description>
+          Indicate that the a specified list of names or the
+          names in a given Interface require a given permission for
+          access.
+        </description>
+
+      <attribute
+          name="permission"
+          required="yes"
+          description="a permission id" 
+          />
+
+      <attribute
+          name="attributes"
+          description="space-separated list of attribute names" 
+          />
+
+      <attribute
+          name="interface"
+          description="the resolvable name of an interface" 
+          />
+
+      <attribute name="like_class">
+        <description>
+              a class on which the security requirements
+              for this class will be based 
+            </description>
+        </attribute>
+
+    </subdirective>
+
+    <subdirective name="allow">
+      <description>
+            Declare a part of the class to be publicly
+            viewable (that is, requires the zope.Public
+            permission).  Only one of the following two
+            attributes may be used.
+         </description>
+
+      <attribute
+          name="attributes"
+          description="space-separated list of attribute names" 
+          />
+
+      <attribute
+          name="interface"
+          description="the resolvable name of an interface" 
+          />
+
+    </subdirective>
+
+    <subdirective name="factory">
+
+      <description>
+            Specify the factory used to create this
+            content object
+        </description>
+
+      <attribute name="permission">
+        <description>
+              permission id required to use this factory.
+              Although optional, this attribute should normally
+              be specified. 
+              </description>
+        </attribute>
+
+      <attribute name="id">
+        <description>
+              the identifier for this factory in the
+              ZMI factory identification scheme.  If not given, defaults
+              to the literal string given as the content directive's
+              'class' attribute.
+            </description>
+        </attribute>
+
+      <attribute name="title">
+        <description>
+              text suitable for use in the 'add content' menu of
+              a management interface
+            </description>
+        </attribute>
+
+      <attribute name="description">
+        <description>
+              longer narrative description of what this
+              factory does
+          </description>
+        </attribute>
+
+    </subdirective>
+  </directive>
+</directives>
 
 </zopeConfigure>