[Zope3-checkins] CVS: Zope3/src/zope/app/services - README.txt:1.1.2.4 configuration.py:1.11.2.2 utility.py:1.1.2.4

Guido van Rossum guido@python.org
Tue, 18 Mar 2003 16:10:35 -0500


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

Modified Files:
      Tag: local-utility-branch
	README.txt configuration.py utility.py 
Log Message:
Another checkpoint.

=== Zope3/src/zope/app/services/README.txt 1.1.2.3 => 1.1.2.4 ===
--- Zope3/src/zope/app/services/README.txt:1.1.2.3	Tue Mar 18 07:35:51 2003
+++ Zope3/src/zope/app/services/README.txt	Tue Mar 18 16:10:04 2003
@@ -257,15 +257,19 @@
 ``zope/app/interfaces/services/utility.py``.  The schema should extend
 ``zope.app.interfaces.services.configuration.IConfiguration``.
 There's a more specific interface,
-``zope.app.interfaces.services.configuration.INamedComponentConfiguration``
-that is much closer to what we need.  It has all of the fields and
-methods we need except for an ``interface`` field to hold the utility
-interface.
+``zope.app.interfaces.services.configuration.IComponentConfiguration``
+that is much closer to what we need.  (XXX Add footnote explaining why
+we can't use INamedComponentConfiguration in this example.)
+We extend this interface as IUtilityConfiguration, which adds a name
+field (which is required but may be empty -- note the subtle
+difference, because the empty string is still used as part of the
+lookup key) and an interface field.  We also override the
+componentPath field to make it read-only (this is for the UI
+definition).
 
 A ``UtilityConfiguration`` class is added to the ``utility`` module in
 ``zope/app/services`` that implements the configuration interface.
-We can subclass NamedComponentConfiguration, which does most of the
-work.
+We can subclass ComponentConfiguration, which does much of the work.
 
 
 For utility components stored in folders, we want to do the
@@ -279,9 +283,9 @@
 There's a view on utility components for managing their
 configurations; similar to the corresponding view on service
 components, it shows a list of all configurations for the component,
-and a link to add one.
+and a link to add a new one.
 
-To implement this view, we need them to keep track of the
+To implement this view, we need to keep track of the
 configuration objects for given utility components.  The reference
 from a configuration object to the component it configures is
 contained in the configuration object itself.  In order to find the
@@ -310,7 +314,133 @@
 ``removeUsage`` and ``usages``, which provide access to the back
 pointers.
 
-XXX now write the code for this view.
+We also need to provide two summary lines for the configuration
+manager.  These summary lines are returned by two methods that are
+(questionably, but conveniently) defined in the IConfiguration
+interface: usageSummary() and implementationSummary().  We override
+usageSummary() to return a string of the form "<interface> utility
+[named <name>]"; we inherit implementationSummary() from the
+ComponentConfiguration base class, which returns the component
+pathname as a string.  These two lines are used in the configuration
+manager's default view, which lists all the configurations it knows
+about; the first line is a a link to an edit view for configuration
+object.
+
+We're now ready to write view code.  We will create three views:
+
+- A "Configurations" view for utility components.
+
+- An "add configuration" view to configure a utility.
+
+- An "edit configuration" view to change a utility's configuration.
+
+The first view we create is the "Configurations" view for utility
+components.  This is very similar to the Configurations view for
+service components, and for several other components that are handled
+by the configuration manager.  The view is a "tab" on any object that
+implements ILocalUtility; the view name is useConfigurations.html and
+the tab label is "Configurations".  All this is expressed by the ZCML
+for the view, in zope/app/browser/services/utility/configure.zcml::
+
+  <page
+      for="zope.app.interfaces.services.utility.ILocalUtility"
+      name="useConfiguration.html"
+      template="useconfiguration.pt"
+      class=".useconfiguration.UseConfiguration"
+      permission="zope.ManageServices"
+      menu="zmi_views" title="Configurations"
+      />
+
+We won't show the template (useconfiguration.pt) here; it renders a
+bulleted list giving links to the configurations (each linking to the
+edit view for the configuration object), and a link to add a new
+configuration.
+
+The information for the bulleted list is computed by the
+UseConfiguration class in the file useconfiguration.py, which we also
+won't show here.  As described earlier, it adapts the component to
+IUseConfiguration and gets the back pointers to configuration objects
+from the adapter's usages() method.  For each configuration object it
+returns enough information to render the utility's interface, name,
+activity status, and URL.
+
+The second view we create is the add view for utility configurations.
+Here's the ZCML::
+
+  <addform
+      for="zope.app.interfaces.services.utility.ILocalUtility"
+      name="addConfiguration.html"
+      schema="zope.app.interfaces.services.utility.IUtilityConfiguration"
+      class=".useconfiguration.AddConfiguration"
+      permission="zope.ManageServices"
+      content_factory="zope.app.services.utility.UtilityConfiguration"
+      arguments="name interface componentPath"
+      set_after_add="status"
+      fields="name interface componentPath permission status"
+      />
+
+Notice that there's no template!  The <addform> directive creates the
+form for us using a generic template, zope/app/browser/form/add.pt,
+and information about the specific fields to be displayed extracted
+from the schema.  We do specify a class name: the AddConfiguration
+class.  This class needs some explanation.
+
+The <addform> directive uses the AddConfiguration class as a mix-in
+class.  It may override various methods to customize the add form; the
+set of methods that can be customized is given by the
+zope.app.interfaces.browser.form.IAddFormCustomization class.  In this
+particular case, we must override add() and nextURL() because their
+default implementations only work when the add form is a view on an
+IAdding view.  That is the normal way to use add forms, but here we
+don't do that; this particular add form is a view on a local utility
+component.
+
+The AddConfiguration class defines a class attribute::
+
+    componentPath = CustomWidget(ComponentPathDisplayWidget)
+
+This tells the forms machinery to use a ComponentPathDisplayWidget to
+display the componentPath field, rather than the default widget for
+componentPath fields.  The default widget allows the user to enter the
+component path, but that is not what we want here: we always add a
+utility configuration object for a given utility component, so the
+component path is a given.  The ComponentPathDisplayWidget widget
+displays the path as an active link which goes back to the component's
+selected manangement view.
+
+The nextURL() method redirects the viewer to the useConfiguration.html
+view on the utility component; this means that after adding a
+configuration we'll go back to the view we created a bit earlier.
+
+The add() method contains some boilerplate to add an object to a
+configuration manager::
+
+        configure = traverse(getWrapperContainer(self.context), 'configure')
+
+Here, self.context is the utility component; its wrapper container is
+the folder containing it.  We traverse from the folder to the
+configuration manager (which is always named 'configure').  Next::
+
+        container = getAdapter(configure, IZopeContainer)
+        name = container.setObject("", content)
+        return container[name]
+
+This gets an IZopeContainer adapter for the configuration mananger.
+This adapter has a setObject() method that adds an object to a
+container, takin care of generating the standard events and calling
+standard hooks.  It returns the name chosen by the container, which is
+important in this case because the configuration manager always picks
+the names for objects added to it (it uses successive small
+integers).  We then ask this container for the object we just added
+using this name; this wraps the object in a context wrapper.  (A
+different way of doing the same thing would be
+``return traverse(container, name)``.)
+
+We also override beforeUpdateHook().  XXX I'll let Jim describe this.
+
+The third view we create is the edit view for utility configuration
+objects.  This is similar to the add view, although it XXX
+
 
 - XXX ILocalUtility: to be used as a utility; adds IUseConfigurable,
   promises adaptable to IUseConfigurationm enables "Utility


=== Zope3/src/zope/app/services/configuration.py 1.11.2.1 => 1.11.2.2 ===
--- Zope3/src/zope/app/services/configuration.py:1.11.2.1	Tue Mar 18 14:21:16 2003
+++ Zope3/src/zope/app/services/configuration.py	Tue Mar 18 16:10:04 2003
@@ -38,7 +38,8 @@
 from zope.app.interfaces.services.configuration \
      import INameComponentConfigurable, INamedConfiguration, IConfiguration
 from zope.app.interfaces.services.configuration \
-     import INamedComponentConfiguration, INameConfigurable
+     import INamedComponentConfiguration, INameConfigurable, \
+            IComponentConfiguration
 from zope.app.interfaces.services.configuration import IUseConfiguration
 from zope.app.interfaces.services.configuration \
      import Unregistered, Registered, Active
@@ -310,29 +311,28 @@
 
     def __init__(self, name):
         self.name = name
-        super(NamedConfiguration, self).__init__()
 
     def usageSummary(self):
         return "%s %s" % (self.name, self.__class__.__name__)
 
 
-class NamedComponentConfiguration(NamedConfiguration):
-    """Named component configuration
+class ComponentConfiguration(SimpleConfiguration):
+    """Component configuration.
 
     Subclasses should define a getInterface() method returning the interface
     of the component.
     """
 
-    # NamedConfiguration.__implements__ includes IDeleteNotifiable
-    __implements__ = (INamedComponentConfiguration,
-                      NamedConfiguration.__implements__, IAddNotifiable)
+    # SimpleConfiguration.__implements__ includes IDeleteNotifiable
+    __implements__ = (IComponentConfiguration,
+                      SimpleConfiguration.__implements__,
+                      IAddNotifiable)
 
-    def __init__(self, name, component_path, permission=None):
+    def __init__(self, component_path, permission=None):
         self.componentPath = component_path
         if permission == 'zope.Public':
             permission = CheckerPublic
         self.permission = permission
-        super(NamedComponentConfiguration, self).__init__(name)
 
     def implementationSummary(self):
         return locationAsUnicode(self.componentPath)
@@ -378,12 +378,27 @@
 
     def beforeDeleteHook(self, configuration, container):
         "See IDeleteNotifiable"
-        super(NamedComponentConfiguration, self
-              ).beforeDeleteHook(configuration, container)
+        super(ComponentConfiguration, self).beforeDeleteHook(configuration,
+                                                             container)
         component = configuration.getComponent()
         dependents = getAdapter(component, IDependable)
         objectpath = getPhysicalPathString(configuration)
         dependents.removeDependent(objectpath)
+
+
+class NamedComponentConfiguration(NamedConfiguration, ComponentConfiguration):
+    """Configurations for named components.
+
+    This configures components that live in folders, by name.
+    """
+
+    __implements__ = (INamedComponentConfiguration,
+                      NamedConfiguration.__implements__,
+                      ComponentConfiguration.__implements__)
+
+    def __init__(self, name, component_path, permission=None):
+        NamedConfiguration.__init__(self, name)
+        ComponentConfiguration.__init__(self, component_path, permission)
 
 
 class NameConfigurable:


=== Zope3/src/zope/app/services/utility.py 1.1.2.3 => 1.1.2.4 ===
--- Zope3/src/zope/app/services/utility.py:1.1.2.3	Tue Mar 18 07:35:51 2003
+++ Zope3/src/zope/app/services/utility.py	Tue Mar 18 16:10:04 2003
@@ -25,7 +25,7 @@
 from zope.app.interfaces.services.utility import IUtilityConfiguration
 from zope.app.services.configuration import ConfigurationRegistry
 from zope.app.services.configuration import ConfigurationStatusProperty
-from zope.app.services.configuration import NamedComponentConfiguration
+from zope.app.services.configuration import ComponentConfiguration
 from zope.app.services.configuration import SimpleConfiguration
 from zope.component.exceptions import ComponentLookupError
 from zope.component import getAdapter
@@ -93,7 +93,7 @@
         return ContextWrapper(registry, self)
 
 
-class UtilityConfiguration(NamedComponentConfiguration):
+class UtilityConfiguration(ComponentConfiguration):
     """Utility component configuration for persistent components
 
     This configuration configures persistent components in packages to
@@ -104,17 +104,26 @@
     status = ConfigurationStatusProperty('Utilities')
 
     __implements__ = (IUtilityConfiguration,
-                      NamedComponentConfiguration.__implements__)
+                      ComponentConfiguration.__implements__)
 
     def __init__(self, name, interface, component_path, permission=None):
-        super(UtilityConfiguration, self).__init__(
-            name, component_path, permission)
+        ComponentConfiguration.__init__(self, component_path, permission)
+        self.name = name
         self.interface = interface
 
+    def usageSummary(self):
+        # Override IConfiguration.usageSummary()
+        s = "%s utility" % self.interface.__name__
+        if self.name:
+            s += " named %s" % self.name
+        return s
+
     def getInterface(self):
+        # ComponentConfiguration calls this when you specify a
+        # permission; it needs the interface to create a security
+        # proxy for the interface with the given permission.
         return self.interface
 
-
     # The following hooks are called only if we implement
     # IAddNotifiable and IDeleteNotifiable.
 
@@ -123,9 +132,8 @@
 
         Defined in IAddNotifiable.
         """
-        NamedComponentConfiguration.afterAddHook(self,
-                                                 configuration,
-                                                 container)
+        super(UtilityConfiguration, self).afterAddHook(configuration,
+                                                       container)
         utility = configuration.getComponent()
         adapter = getAdapter(utility, IUseConfiguration)
         adapter.addUsage(getPhysicalPathString(configuration))
@@ -138,6 +146,6 @@
         utility = configuration.getComponent()
         adapter = getAdapter(utility, IUseConfiguration)
         adapter.removeUsage(getPhysicalPathString(configuration))
-        NamedComponentConfiguration.beforeDeleteHook(self,
-                                                     configuration,
-                                                     container)
+        super(UtilityConfiguration, self).beforeDeleteHook(configuration,
+                                                           container)
+