From jim at zope.com Thu Nov 15 18:11:49 2001
From: jim at zope.com (Jim Fulton)
Date: Sun Aug 10 16:40:35 2008
Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 - Contact.zcml:1.1 ContactViewPresentation.py:1.1 IContact.py:1.1 Contact.py:1.2 ContactEditPresentation.py:1.3 README.txt:1.3 __init__.py:1.3 edit.pt:1.2 view.pt:1.3 configure.py:NONE permissions.py:NONE
Message-ID: <200111152311.fAFNBnK17498@cvs.baymountain.com>
Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4
In directory cvs.zope.org:/tmp/cvs-serv15795/PythonProgrammerTutorial/Chapter1/Step4
Modified Files:
Contact.py ContactEditPresentation.py README.txt __init__.py
edit.pt view.pt
Added Files:
Contact.zcml ContactViewPresentation.py IContact.py
Removed Files:
configure.py permissions.py
Log Message:
Various changes based on feedback from presentations to Python Labs,
ZPUG, and Zope Corp engineers:
- Introduce configuration files that are inteded to be copied and
managed by site managers or integrators. They use an XML syntax
chosen to make configuration easier.
- Removed mention of "offer", since configuration files make this
less important except in advanced cases.
- Changed presentation templates sources to use a standard look and
feel macro. (Need more updates to docs to reflect this.)
- Took out use of special URL syntax for presentation components,
since it really isn't needed for contacts. It will be described
later.
- Changed to reflect Zope3 package structure.
- Added a safety belt for presentation components and features. They
can declare what interfaces they can be used for to prevent
accidental misconfiguration.
I think that the tutorial is ready to be exposed to a much wider
audience.
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/Contact.zcml ===
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactViewPresentation.py ===
from Zope.Products.PageTemplate import SimplePresentationClass
from IContactInfo import IContactInfo
# Create a simple view presentation
ContactViewPresentation = SimplePresentationClass(
'view.pt', applicable_for=IContactInfo)
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/IContact.py ===
from Interface import Interface, implements
# Local Package imports
from Contact import Contact
class IContactInfo(Interface):
"Marker for objects that provide specific behavior"
implements(Contact, IContact)
=== Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/Contact.py 1.1 => 1.2 ===
-import Persistence,
+import Persistence
class Contact (Persistence.Persistent):
"""Contacts keep track of personal data, such as name, email
=== Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEditPresentation.py 1.2 => 1.3 ===
-from ZPublisher.Browser import AttributePublisher
-from ComponentArchitecture.Singleton import SingletonBase, singleton
-from Products.PageTemplate import PresentationPageTemplateFile
+from Zope.Publisher.Browser import AttributePublisher
+from Zope.Products.PageTemplate import PresentationPageTemplateFile
+from IContactEdit import IContactEdit
-
-class ContactEditPresentationClass(AttributePublisher, Singleton):
+class ContactEditPresentationClass(AttributePublisher):
"""Provide an interface for editing a contact
"""
+ # Boiler plate
+ def __init__(self, context):
+ self._context=context
+
+ def getContext(self):
+ return self._context
+
+ # Assert that we can only be applied to IContactEdit
+ __used_for__=IContactEdit
+
# Input form
- view = PresentationPageTemplateFile('edit.pt', globals())
+ index = PresentationPageTemplateFile('edit.pt', globals())
# action method
def action(self, first, last, email, address, pc):
"Edit a contact"
self.getContext().update(first, last, email, address, pc)
return self.view()
-
-ContactEditPresentation = singleton(ContactEditPresentationClass)
=== Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/README.txt 1.2 => 1.3 ===
The component is defined by the 'ContactEditPresentation' class.
- The class asserts that it implements the 'BrowserPublish' interface by
- defining the '__implements__' attribute. This example illustrates
- how to assert interfaces in a class definition.
-
The class inherits from a standard base class, 'AttributePublisher'::
class AttributePublisher:
@@ -19,10 +15,14 @@
def browser_traverse(self, request, name):
if name[:1] == '_':
raise 'NotFound'
- return getattr(self, name)
+ if name[-5:]=='.html':
+ return getattr(self, name[:5])
+
+ return getattr(self, name)
+
def browser_default(self, request):
- return "view"
+ return "index.html"
This base class provides three things:
@@ -35,6 +35,9 @@
'IBrowserPublish' interface. In this case,'browser_traverse'
method provides publishing of attribute values.
+ This implementation handles names with an ".html" suffix by
+ removing the suffix before looking up an attribute.
+
- An implementation of the 'browser_default' method of the
'IBrowserPublish' interface. The 'browser_default' method is used
to control what happens when a URL points to a component, rather
@@ -46,7 +49,7 @@
sequence of additional names to traverse the object with.
In this case, we use the simpler form and simply return the string
- "view". Subclasses must provide an attribute, 'view' that provides
+ "index.html". Subclasses must provide an attribute, 'index' that provides
their default interface, or they must override this method to
supply a different name.
@@ -54,57 +57,62 @@
component presented by a presentation component as it's "context".
The methods of a presentation component need access to the
presentation context. They do so by calling the 'getContext' method
- that must be provided by a presentation component.
+ that must be provided by a presentation component. The class
+ provides a constructor that stores the argument given as the context
+ so that it can be later returned by the 'getContext' method.
+
+ The presentation component provides a web page that displays an edit
+ form. It implements this using a 'PresentationPageTemplateFile',
+ with the source in the file 'edit.pt'.
- Presentation components have to track a context for the
- presentation. There are two common approaches for doing this:
-
- - A separate transient instance of the component is created for each
- context. The context is passed to the class constructor and stored
- in an instance variable. The transient instance can store
- information in attributes if necessary.
-
- - A single instance is used for all contexts and context information
- is kept in a wrapper object created for each context.
- This approach is a little more efficient, but no data can be
- stored in attributes except at instance creation time.
-
- The contact edit presentation component takes the singeton approach.
- The 'ComponentArchitecture.Singleton' module provides
- a 'SingletonBase' mix-in class that provides a 'getContext'
- implementation, which we mix into the 'ContactEditPresentationClass'.
- The module also provides a 'singleton' function that creates a
- callable object that can be registered with the component
- architecture.
-
- The presentation component provides a web page that displays an
- edit form. It implements this using a
- 'PresentationPageTemplateFile', with the source in the file
- 'edit.pt'.
-
- The action of the form is "action", which is the 'action' method of
+ The action of the form is "action.html", which names the 'action' method of
the component. The 'action' method applies the update and redisplays
the editing form.
- At the end of the module, we create a singleton from the class.
-
In addition to the contact information methods, this presentation
component uses the 'update' method of contacts. The update method
- isn't in the 'ContactInfo' interface we created in the last step.
- We create a new interface, 'ContactEdit', in 'ContactEdit.py', that
- extends the 'ContactInfo' interface with the 'update' method.
-
- We need to register our new presentation component. The '__init__'
- module in Step3 was getting rather complicated, so in this step, we
- create a separate configuration module, in 'configure.py'. The
- '__init__' module simply imports the configuration module and
- imports the class and interfaces so that other file-system-based
- modules can use them.
-
- The configuration file includes the old logic from init and adds the
- registration of the new editing component.
-
- The configuration module also adds a security assertion for the
- contact 'update' method. Note that we can't use the 'ContactEdit'
- interface to make this assertion because it extends the
- 'ContactInfo' interface for which there is a conflicting assertion.
+ isn't in the 'IContactInfo' interface we created in the last step.
+ We create a new interface, 'IContactEdit', in 'IContactEdit.py', that
+ extends the 'IContactInfo' interface with the 'update' method.
+
+ The presentation component uses and thus is only applicable to
+ object objects that implement the 'IContactEdit' interface. We
+ specify this by defining the '__used_for__' attribute. This
+ prevents someone from mistakedly configuring the component for use
+ with other (incompatable) interfaces. The '__used_for__'
+ attribute is optional. Only one interfac can be specifeid. One
+ could concievably create a component that is applicable to several
+ alternative interfaces, in which case, the '__applicable__'
+ attribute would not be set.
+
+ We modify the configuration file to add a security assertion for the
+ new component and we add a 'browser::presentation' directive to
+ register the component.
+
+ We also need to add a security assertion for the 'update' method of
+ the contact class. Because the class is now protected with multiple
+ permissions, we need to use a more complicated form for the security
+ assertions. Rather than using a single empty element for the
+ assertion, we use three empty elements grouped by a non-empty
+ 'security:protectClass' element. When need to include an explicit assertion
+ for protecting class instances (as opposed to their methods)
+ whenever we use the complex form of 'security:protectClass'.
+
+ Note that we protect the 'update' method by name, rather than by
+ interface. We can't protect the 'IContactEdit' interface because
+ 'IContactEdit' extends 'IContactInfo', which is protected by a
+ different permission.
+
+ To work well with the Zope management interface (ZMI), we need to specify
+ which presentations are used for ZMI tabbed views. We can specify
+ this in the configuration file using the 'zmi:tabs' directive. The
+ 'zmi:tabs' directive contains multiple 'zmi:tab' directives for a given
+ interface. Each directive specifies a label and an action. The
+ action is simply a string that is added to the content object's URL.
+
+ The tabs are defined using a new 'IContact' interface, defined in
+ the 'IContact' module. This is a "marker" interface. It doesn't
+ actually specify any contacts but provides a way of very
+ specifically tagging certain classes (usually one). We do this for
+ tabs because we usually want very specific control over the tabs
+ that a content object displays.
=== Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/__init__.py 1.2 => 1.3 ===
-import configure
-
-# export class and interfaces:
-from Contact import Contact
-from ContactInfo import ContactInfo
-from ContactEdit import ContactEdit
-
-
=== Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/edit.pt 1.1 => 1.2 ===
+
+
Edit contact
+
+
Enter the information about the contact.
-
-
+