From jim at zope.com Tue Oct 16 11:38:13 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture Docs/ZopeComponentArchitecture - New directory Message-ID: <200110161538.f9GFcDk28026@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture In directory cvs.zope.org:/tmp/cvs-serv28004/ZopeComponentArchitecture Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture added to the repository === Added directory Docs/ZopeComponentArchitecture === From jim at zope.com Wed Oct 24 09:31:52 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial Docs/ZopeComponentArchitecture/PythonProgrammerTutorial - New directory Message-ID: <200110241331.f9ODVql11956@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial In directory cvs.zope.org:/tmp/cvs-serv11950/PythonProgrammerTutorial Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial === From jim at zope.com Wed Oct 24 09:32:06 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - New directory Message-ID: <200110241332.f9ODW6b12020@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 In directory cvs.zope.org:/tmp/cvs-serv12014/Chapter1 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 === From jim at zope.com Wed Oct 24 09:33:17 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - New directory Message-ID: <200110241333.f9ODXH112851@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv12844/Step5 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 === From jim at zope.com Wed Oct 24 09:33:18 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 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 - New directory Message-ID: <200110241333.f9ODXIL12865@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 In directory cvs.zope.org:/tmp/cvs-serv12858/Step4 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 === From jim at zope.com Wed Oct 24 09:33:19 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - New directory Message-ID: <200110241333.f9ODXJu12879@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 In directory cvs.zope.org:/tmp/cvs-serv12872/Step3 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 === From jim at zope.com Wed Oct 24 09:33:19 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - New directory Message-ID: <200110241333.f9ODXJY12893@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv12886/Step2 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 === From jim at zope.com Wed Oct 24 09:33:20 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - New directory Message-ID: <200110241333.f9ODXKE12907@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv12900/Step1 Log Message: Directory /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 added to the repository === Added directory Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 === From jim at zope.com Wed Oct 24 09:41:56 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial - README.txt:1.1 Message-ID: <200110241341.f9ODfux14450@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial Added Files: README.txt Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/README.txt === Zope Component Architecture Tutorial for Python Programmers This tutorial presents the Zope Component Architecture to Python programmers in a step-by-step form. The tutorial is arranged into chapters, with a directory for each chapter. Each chapter is written around an evolving sample implementation. Each topic in a chapter is presented, in a chapter subdirectory, as an evolution of some sample code. The sample directories are actual working Python packages that can be installed into Zope 3 product directories. The Zope component architecture has several goals, the most important of which are: - Make it much easier to create objects that are usable in Zope: o Make it possible to use existing Python classes (with little or no change). o Utilize features of the Zope framework with small incremental changes. - Make it much easier to reuse objects in Zope. o Use existing non-Zope-specific objects o Add, remove, and replace functionality of existing objects. o Change the user interface of existing objects, o Provide alternative access methods (e.g. FTP, XML-RPC, wxWindows) to objects. Additional goals include: - Avoid namespace issues o Don't worry about conflicts with content names o Don't worry about a large number of names - Make it easy to discover content and application functionality. - Make it easier to distribute solutions. - Provide consistency between Zope and the CMF. This tutorial will demonstrate how these goals are achived by the component architecture. Chapters: Chapter1, Basic content types and components Chapter2, Containers Chapter3, Services, offers, customized traversal. Chapter4, Converting traditional Zope products to component-based products. From jim at zope.com Wed Oct 24 09:41:56 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - README.txt:1.1 Message-ID: <200110241341.f9ODfuC14457@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1 Added Files: README.txt Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/README.txt === Chapter1, Basic content types and components This chapter provides an introduction to the Zope component architecture through a simple content-type example. A content component is developed for storing simple personal contact information. We begin by showing the minimum necessary to create content objects in Zope, and then we show, gradually how functionality can be added with components. The presentation of the example proceeds in several steps: Step1, minimal contact class Step2, security asertions to make contact objects usable Step3, a simple contact presentation Step4, providing editing capabilities Step5, adding features and utilities Step6, providing a custom creation form Summary This chapter showed how to develop basic components with Python modules. The facilities demonstrated address a number of the component architecture goals: - Make it much easier to create objects that are usable in Zope: o Make it possible to use existing Python classes (with little or no change). We were able to use extremely simple classes. We needed to mix 'Persistent' into the content component, but this resriction will be relaxed in the future. We didn't need to mix in any special base classes in other components, although we chose to use acquisition in some cases. o Utilize features of the Zope framework with small incremental changes. Observe that as we progressed through the chapter, new modules were added, but existing modules, other than the configueration module remained unchanged. The incremental improvements could as easily have been provided in other packages by different authors. - Make it much easier to reuse objects in Zope. o Use existing non-Zope-specific objects See above. o Add, remove, and replace functionality of existing objects. This only requires changes to the configuration module(s). Configuration could be easily split over many packages and, though not mentioned here, could be performed through teh web. The ability to offer, rather than provide interfaces provides flexibility in cases where alternative implementations are expected. o Change the user interface of existing objects, This was not addressed directly, but it is as simple as simple as changing which presentations are registered. o Provide alternative access methods (e.g. FTP, XML-RPC, wxWindows) to objects. We'll see in later chapters how to add protocol support by adding presentation objects. Additional goals include: - Avoid namespace issues o Don't worry about conflicts with content names It should be aparent that, with the Zope component architecture, software components are accessed through API calls, which use services. Components aren't accessed in the same namespace as content. o Don't worry about a large number of names The separation of content from software, and the classification of software into separate component types (e.g. utilities and features) reduces the opportunity for namespace conflicts. Note that the names in the component namespaces typically have multiple parts, which are often interfaces, rather than strings. - Make it easy to discover content and application functionality. This was not discussed in this chapter, however, it should be obvious that, given an object, we should be able to find the presentations and features that extend it based on it's interaces. We'll show how this works in later chapters. - Make it easier to distribute solutions. This goal will be addressed in later chapters. - Provide consistency between Zope and the CMF. This goal will be addressed in later chapters. From jim at zope.com Wed Oct 24 09:41:57 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - Contact.py:1.1 README.txt:1.1 __init__.py:1.1 Message-ID: <200110241341.f9ODfvn14470@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step1 Added Files: Contact.py README.txt __init__.py Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/Contact.py === import Persistence class Contact (Persistence.Persistent): """Contacts keep track of personal data, such as name, email and postal address. All methods are protected.""" def __init__(self, first='', last='', email='', address='', pc=''): self.update(first, last, email, address, pc) def name(self): return "%s %s" % (self._first, self._last) def first(self): return self._first def last(self): return self._last def email(self): return self._email def address(self): return self._address def postal_code(self): return self._pc def update(self, first=None, last=None, email=None, address=None, pc=None): if first is not None: self._first = first if last is not None: self._last = last if email is not None: self._email = email if address is not None: self._address = address if pc is not None: self._pc = pc === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt === Step1, minimal contact class In this step, we present the minimum steps necessary to get a class usable in Zope. The file 'Contact.py' contains the basic class definition. This class doesn't use *any* Zope-specific classes. The class provides methods for accessing contact data and for modifying the data. This class mixes in the 'Persistent' base class. Doing so allows the class to be stored in Zope's object database and causes changes to the object to be saved automatically [1]. This is important because we want class instances to be saved and managed in Zope. If instances weren't going to ve stored in Zope, we would not need to mix in persistence. To use the contact class in Zope, we have to tell Zope about it. The file, '__init__.py' is where we do this. Components are contained in Zope file-based products [2], which are simply Python packages that are sub-packages of the Zope 'Products' package. The '__init__' module is responsible for initializing the package and for getting necessary registrations done. In this example, the '__init__' module calls the 'App.provideClass' function to register the class. The class being regstered is passed as the first argument. A keyword argument is used to specify a Zope permission needed for creating instances. If a permission wasn't specified, then contacts could not be created through the web or through code managed through the web. Additional keyword arguments can be used to provide additional meta data, however, the meta-data is infered from class meta-data. The 'App.provideClass' accomplishes two things: 1. A factory component is created and registered using the class. This allows other application code to create instances:: contact=ComponentArchitecture.createObject( ob, 'Products.Contact.Contact.Contact') A identifier for the factory is generated from the registered class module and class name. In this case, the module is 'Products.Contact.Contact' and the class is 'Contact'. A different identifier could have been provided in the 'provideClass' call. 2. The factory is registered with Zope so that it can be included in Zope's interface for adding objects to containers. With this registration, contacts can be added to Zope folders. Simply adding contacts to Zope folders is of limited usefulness. We can't do anything with the added contacts from the Zope management interface or from through the web code, because we haven't provided security assertions that make them accessable. Conceivably, we could use contacts from trusted (file-based) code. In the next step, we'll make security assertions for the class, which will make the class a lot more useful to Zope. ------------------------------------------------------------------------------ [1] -- There are a few additional restrictions, sometimes refered to as the "rules of persistence", that persistent class authors should be aware of. Persistent objects should be picklable with Python's standard pickle module. Subobjects should be immutable or persistent, or special handling is needed. See the Zope object database documentation. In is likely that, in the future, it will be possible to store objects in Zope without mixing in 'Persistent', however, it will be necessary to make explicit calls when objects are modified so that changes can be saved. [2] -- Components can also be developed through the web and contained in the Zope object database. This tutorial, however, only discussed file-based products. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/__init__.py === import App # Top-level package. It would be better if this was Zope.App import Contact ManageContacts = 'Manage Contacts' # Register the contact class App.provideClass(Contact.Contact, permission=ManageContacts) From jim at zope.com Wed Oct 24 09:41:57 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - Contact.py:1.1 README.txt:1.1 __init__.py:1.1 view.pt:1.1 Message-ID: <200110241341.f9ODfvL14484@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step2 Added Files: Contact.py README.txt __init__.py view.pt Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/Contact.py === import Persistence class Contact (Persistence.Persistent): """Contacts keep track of personal data, such as name, email and postal address. All methods are protected.""" def __init__(self, first='', last='', email='', address='', pc=''): self.update(first, last, email, address, pc) def name(self): return "%s %s" % (self._first, self._last) def first(self): return self._first def last(self): return self._last def email(self): return self._email def address(self): return self._address def postal_code(self): return self._pc def update(self, first=None, last=None, email=None, address=None, pc=None): if first is not None: self._first = first if last is not None: self._last = last if email is not None: self._email = email if address is not None: self._address = address if pc is not None: self._pc = pc === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/README.txt === Step2, security asertions to make contact objects usable In this step, we'll make security assertions that make the 'Contact' class we created in Step1 usable in Zope. The contact module, 'Contact.py' remains unchanged. We add a security assertion in the '__init__' module by calling 'AccessControl.protectClass'. The class to be protected and the desired Zope permission are passed as arguments. We also provide keyword arguments to specify protected methods, by name, and to specify that access to instances should be protected by the permission. We could have made security assertions in the class definition, using Zope declarative security API. Doing so would have made the class much more Zope-specific. In this case, we'd like to keep the class Zope-independent, so we made the security assertions with 'protectClass'. The two approaches are equivalent. The 'protectClass' call actually modifies the classes passed in exactly the same way that the declarative security API would have. In this example we protected the methods for getting contact information with the Zope 'View' permission. With this change, we can write templates and scripts that access these methods. The file 'view.pt' shows a sample page template for viewing contacts. This simple template used the contact methods 'first', 'last', 'address' and 'email' to access contact data. The template could be added to Zope as a Zope page Template. Typically, it would be included in the folder containing the data, or in a higher containing folder. For the template to work, the template must be "applied to" the contact object. This is typically done by visiting the contact with a URL like:: ..../aContact/view.pt When this happens the template variable, 'here' is bound to the contact, so the path expression 'here/first' calls the 'first' method on the contact. There is a little bit of a problem here. The URL above will only work if the page template can be acquired from the contact. This works for traditional Zope objects because they mix in the Acquisition.Implicit' base class. Implicit acquisition causes an object's containers to be automatically searched for attributes not found in the object. Implicit acquisition is quite powerful, but can sometimes lead to unpleasent surprises. In Zope 3, objects will still take advantage of acquisition, but in much more explicit ways. Most objects will have a searchable acquisition context, but the context will be searched explicitly. Because the contact class doesn't mix in acquisition, we can't automatically acquire the view template. We could change the class to mix in 'Acquisition.Implicit', but this would make the class a bit too Zope-specific. One way to get around this is to request acquisition explicitly in the URL:: ..../aContact/(acquire)view.pt Another approach would be to provide custom software that allows contacts to acquire specific objects in URLs. We'll discuss this approach in a later chapter of the tutorial. There's a deeper problem. We might want the view template to be invoked when we name just the contact:: ..../aContact We need a way to specify this. In the next step in the tutorial, we'll use the component architecture to provide a default presentation for contacts. In this example, the view template was put in the folder containing the content. The content, and the software for displaying the content are mixed up in the same folder (or folder hierarchy). Mixing software and content in the same place makes managing both more difficult. We could put the presentation in the class (or in a Zope through-the-web Class), but this requires changing the class implementation and mixes presentation and data. Components provide another way to accomplish the same thing, but in a more flexible and organized fashion. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/__init__.py === import App import Contact import AccessControl, AccessControl.Permissions # Protect contact view methods: protectMethods( ViewPermission, Contact, 'name', 'first', 'last', 'email', 'address', 'postal_code' ) # Register the contact factory ManageContacts = 'Manage Contacts' App.provideClass(Contact.Contact, permission=ManageContacts) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/view.pt === Contact Information
Contact information
Name: First Last
Address: Mailing address here
Email foo@bar.com
From jim at zope.com Wed Oct 24 09:41:57 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - Contact.py:1.1 ContactInfo.py:1.1 README.txt:1.1 __init__.py:1.1 view.pt:1.1 Message-ID: <200110241341.f9ODfvn14497@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step3 Added Files: Contact.py ContactInfo.py README.txt __init__.py view.pt Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/Contact.py === import Persistence, class Contact (Persistence.Persistent): """Contacts keep track of personal data, such as name, email and postal address. All methods are protected.""" def __init__(self, first='', last='', email='', address='', pc=''): self.update(first, last, email, address, pc) def name(self): return "%s %s" % (self._first, self._last) def first(self): return self._first def last(self): return self._last def email(self): return self._email def address(self): return self._address def postal_code(self): return self._pc def update(self, first=None, last=None, email=None, address=None, pc=None): if first is not None: self._first = first if last is not None: self._last = last if email is not None: self._email = email if address is not None: self._address = address if pc is not None: self._pc = pc === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/ContactInfo.py === import Interface, Contact class ContactInfo (Interface.Base): "Provides access to basic contact information." def first(): "Get the firt name" def last(): "Get the last name" def email(): "Get the electronic mail address" def address(): "Get the postal address" def postal_code(): "Get the postal code" def name(): """Gets the contact name. The contact name is the first and last name together. """ ContactInfo.assertImplementedByInstancesOf(Contact.Contact) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/README.txt === Step3, a simple contact presentation In this step, we'll begin to introduce Zope components. We'll use Zope components to provide a default Web presentation for contacts. Before getting into details, we'll define some terms, "component", "content component" and "presentation component". The definition of "component" is very simple. A component is an object with introspectable interfaces. Interfaces allow us to separate object behavior from implementation [1]. An interface is an abstract description of behavior. Many different implementations can provide the same behavior. This is important for a component system because it makes it easier to assemble components to provide specific functionality. In Zope, components have interfaces defined using the 'Interface' package [2]. Interfaces specify behavior. We'll often refer to an interface as a "contract" or a "promise". A component "implements" ("realizes", "satisfies", "provide") it's interfaces. Interfaces group (or "classify") objects based on their behavior. Objects that provide the same interface are similar and can be substituted for one another. The Zope component model interface to connect related components based on interfaces. A "content component" is a component that manages specific information (e.g. contact information). Most other components exist to support contact components by providing presentations or application functionality. A "presentation component" is a component used to present other components with a specific user interface mechanism (e.g. Web browser or a graphical user interface) or protocol (e.g. FTP or XML-RPC). Presentation components don't provide data or functionality except as necessary to support the presentation. We will often use the word "presentation" alone where the context is clear, as in "contact view presentation". In this step, we'll use the component architecture to cause the view page template to be used when viewing a contact. To use the component architecture, we need to be able to create contact components. That is, contact instances must be components and must have interfaces. The file 'ContactInfo.py' defines the 'ContactInfo' interface, which specifies the methods used to get contact information [3]. Note that the method definitions in the interface don't include a 'self' argument. The reason for this is that the 'self' argument is not part of the external behavior, but, rather, part of the Python implementation of the methods. A caller doesn't pass arguments to these functions, so arguments shouldn't appear in the method interfaces. After the interfac is created, the 'assertImplementedByInstancesOf' method is called to assert that contact instances implement the interface. In this example, we asserted the class' contract outside the class definition. We could have included the interface assertion in the 'Contact' class definition. We'll see an example of this in a later example. There are two advanages to specifying the interface externally, as we have here: - The interface assertion doesn't require modifying the class source. This could be handy if the class source is maintained by someone else or if the class is used in other application environments that don't support interfaces. - The 'assertImplementedByInstancesOf' performs minimal conformance checks and raises errors if the class can be determined not to implement the interface. [4] Now that contacts are components, we can associate presentation components with them. Our immediate need is to provide a simple view presentation for contacts. We normally create components using Python and interfaces, as we did with 'Contact'. This presentation component is very simple, consisting of a single page, expressed as a page template. Page templates provide a convenience functon, 'SimplePresentation' for creating a web presentation from a page template source file. We use this function to create our view presentation in the '__init__' module. Once the presentation has been created, we need to register it. In this example, we register it twice. First register it under the name 'view'. This allows us to access the presentation with URLs like:: ...../aContact/(p)view The string, '(p)' in the URL signifies that a presentation component should be accessed. We also register the presentation as the default presentation by providing an empty string. When a URL like:: ...../aContact is used, the default presentation will be used to display the object. The presentation component is registered using the 'ComponentArchitecture.providePresentation' method. The method takes four arguments: - context interface, - the presentation name, - the presentation type, and - a component creator. The context interface and the presentation name are used to select a presentation component for a given component. A presentation component is only applied to an object that implements it's context interface [5]. The presentation component will use the methods provided in the context interface to get the information to be presented. The presentation type is used to distinguish different kinds of presentations, such as web browser and FTP presentations. Presentation kinds are expressed as interfaces. In this example, we've used the 'ZPublisher.Browser.BrowserPublish' interface to indicate a web browser presentation. Finally, we provide the component creator. This is not actually a component, but, rather a callable object that creates the component when called with a context. This is necesary because the presentation needs the object being presented to do it's job. The view presentation is actually not a presentation, but an object that returns the presentation when called. We;ve made a change to the security assertions for the class. Rather than provide a specific method list, we now specify the 'ContactInfo' interface instead. If all of the methods in an interface are protected the same way, then we can use the interface and avoid maintaining a separate method list. ------------------------------------------------------------------------------ [1] -- Some would argue that abstract base classes could provide the same capability. This is true, however, we think interfaces provide a cleaner approach, for a number of reasons: - Using abstract base classes increases the depth and complexity of class hierarchies. - Using abstract base classes imposes the restriction that classes must satifsy all of the promises of their base classes. We feel that this is overly restrictive. This restriction limits opportunities for reuse and the restriction is often not satisfied in practice. - Abstract base classes make it harder to reason about (or introspect) the behavior of an object, because implementation classes are intermixed with specification classes. - Separating interfaces into separate objects allows interfaces to provide services without exposing the services in instances. For example, Zope interfaces have methods for instrospecting the interface meta-data. If the interfaces were base classes, then the methods would appear as instance methods, which would not make sense. [2] -- The 'Interface' package provides specific objects and utilities for defining interfaces for Python objects. We'll show a number of examples of interface use in this tutorial. For more information, see the "Zope Interfaces" documentation. [3] -- There are a number of ways to create interfaces, but a simple way is with the python 'class' statement. A 'class' statement' normally creates classes, but it can also be used to create other objects. In this case, because we specify 'Interface.Base' as a base, an interface is created. An interface *is not* a class. An interface is an object that specifies behavior (as opposed to a class, which is an object that provides behavior). [4] -- The 'assertImplementedByInstancesOf' does *not* verify that the instances of the class conforms to the interface. Such a verification requires additional semantic information that isn't in the interface. Thye best we can do is check for gross errors, like missing methods or mismatches arguments. [5] -- The context interface can be the special Python object, 'None', in which case the presentation can be applied to any object, howeverm it can't use any specific object methods (or attributes), so such presentations are rarely useful. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/__init__.py === import App import Interface import Products.PageTemplate from ContactInfo import ContactInfo from Contact import Contact import AccessControl, AccessControl.Permissions import ZPublisher.Browser # Protect contact view methods via the info interface: AccessControl.protectClass( Contact, AccessControl.Permissions.view, inteface=ContactInfo, instance=1, ) # Register the contact factory ManageContacts = 'Manage Contacts' App.provideClass(Contact.Contact, permission=ManageContacts) # Create a simple view presentation ViewPresentation = Products.PageTemplate.SimplePresentation( 'view.pt', globals(), permission = AccessControl.Permissions.view, ) # Register this presentation under the name "view" ComponentArchitecture.providePresentation( ContactInfo, 'view', ZPublisher.Browser.BrowserPublish, ViewPresentation) # Register this presentation as the default ComponentArchitecture.providePresentation( ContactInfo, '', ZPublisher.Browser.BrowserPublish, ViewPresentation) #JJJ Maybe default=1 === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/view.pt === Contact Information
Contact information
Name: First Last
Address: Mailing address here
Email foo@bar.com
From jim at zope.com Wed Oct 24 09:41:58 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.py:1.1 ContactEdit.py:1.1 ContactEditPresentation.py:1.1 ContactInfo.py:1.1 README.txt:1.1 __init__.py:1.1 configure.py:1.1 edit.pt:1.1 permissions.py:1.1 view.pt:1.1 Message-ID: <200110241341.f9ODfwO14516@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step4 Added Files: Contact.py ContactEdit.py ContactEditPresentation.py ContactInfo.py README.txt __init__.py configure.py edit.pt permissions.py view.pt Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/Contact.py === import Persistence, class Contact (Persistence.Persistent): """Contacts keep track of personal data, such as name, email and postal address. All methods are protected.""" def __init__(self, first='', last='', email='', address='', pc=''): self.update(first, last, email, address, pc) def name(self): return "%s %s" % (self._first, self._last) def first(self): return self._first def last(self): return self._last def email(self): return self._email def address(self): return self._address def postal_code(self): return self._pc def update(self, first=None, last=None, email=None, address=None, pc=None): if first is not None: self._first = first if last is not None: self._last = last if email is not None: self._email = email if address is not None: self._address = address if pc is not None: self._pc = pc === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEdit.py === from ContactInfo import ContactInfo from Contact import Contact class ContactEdit(ContactInfo): "Provides the ability to change basic contact information." def update(first, last, email, address, pc): """Modifies contact data 'None' values are ignored. """ ContactEdit.assertImplementedByInstancesOf(Contact) #BBB might want to reverse sense of this === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEditPresentation.py === from ZPublisher.Browser import BrowserPublish from AccessControl import ClassSecurityInfo from permissions import ManageContacts from Products.PageTemplate import PresentationPageTemplateFile from App.common import absolute_url from AccessControl.ZopeGuards import guarded_getattr import Acquisition class ContactEditPresentationClass(Acquisition.Explicit): """Provide an interface for editing a contact """ __implements__ = BrowserPublish # Create a SecurityInfo for this class security = ClassSecurityInfo() security.setDefaultAccess('deny') # All presentation components should provide this. def getContext(self): return self.aq_parent # Input form security.declareProtected(ManageContacts, 'form') form = PresentationPageTemplateFile('edit.pt', globals()) # action method security.declareProtected(ManageContacts, 'action') def action(self, first, last, email, address, pc): "Edit a contact" self.getContext().update(first, last, email, address, pc) return self.form() def browser_traverse(self, request, name): return guarded_getattr(self, name) def browser_default(self, request): return self, ('form',) Globals.InitializeClass(ContactEditPresentationClass) # Create the singleton component ContactEditPresentation = ContactEditPresentationClass() # and a binder for it ContactEditPresentation = Acquisition.binder(ContactEditPresentation) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactInfo.py === import Interface, Contact class ContactInfo (Interface.Base): "Provides access to basic contact information." def first(): "Get the firt name" def last(): "Get the last name" def email(): "Get the electronic mail address" def address(): "Get the postal address" def postal_code(): "Get the postal code" def name(): """Gets the contact name. The contact name is the first and last name together. """ ContactInfo.assertImplementedBy(Contact.Contact) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/README.txt === Step4, providing editing capabilities In this example, we'll provide an example of a presentation component with multiple methods. We'll create an editing interface for contacts. The file 'ContactEditPresentation.py' provides the definition for the edit presentation component. 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. Presentation components have to track a context for the presentation. There are two common approaches for doing this: - A separate instance of the component is created for each context. The context is passed to the class constructor and stored in an instance variable. - A single instance is used for all contexts and context information is kept in an acquisition wrapper created for each context. The contact edit presentation component takes the second approach, which is why it mixes in 'Acquisition.Explicit'. It is common to use a 'getContext' method to encapsulate the mechanism used for tracking context. In this case, the 'getContext' method uses the acquisition variable, 'aq_parent' to get the acquisition context. 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 component. The 'action' method applies the update and redisplays the editing form. The form and action are protected using Zope's declarative security API. This example illustrates making security assertions inline in the class. We needed to use the 'ManageContacts' permission that was defined in the '__init__' module in Step3. We've factored this permission into a separate 'permissions' module. The last two methods in the class are defined by the 'BrowserPublish' interface. They affect how URL traversal is accomplished. The 'browser_traverse' method is to lookup a name during URL traversal. The implementation here simply uses 'guarded_getattr' to retrieve an attribute, subject to security checks. The 'browser_default' method is used to control what happens when a URL points to a component, rather than to one of its methods. The return value is a two-element tuple consisting if a new object and a sequence of additional names to traverse the object with. In this case, the component is returned with a tuple containing the name '"form"'. When a URL like: ...../aContact/(p)edit is used, the 'form' is displayed as if the URL had been: ...../aContact/(p)edit/form At the end of the module, we create a singleton presentation component and then create an object from it that, when called, will return the component bound to it's context using acquisition. 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. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/__init__.py === # do configuration import configure; del configure # export class and interfaces: from Contact import Contact from ContactInfo import ContactInfo from ContactEdit import ContactEdit === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/configure.py === import App import Interface import Products.PageTemplate import AccessControl, AccessControl.Permissions import ZPublisher.Browser from ContactInfo import ContactInfo from ContactEdit import ContactEdit from ContactEditPresentation import ContactEditPresentation from Contact import Contact from permissions import ManageContacts # Protect contact view methods via the info interface: AccessControl.protectClass( Contact, AccessControl.Permissions.view, inteface=ContactInfo, instance=1, ) # Register the contact factory App.provideClass(Contact.Contact, permission=ManageContacts) # Create a simple view presentation ViewPresentation = Products.PageTemplate.SimplePresentation( 'viewContact', globals(), permission = AccessControl.Permissions.view, ) # Register this presentation under the name "view" ComponentArchitecture.providePresentation( ContactInfo, 'view', ZPublisher.Browser.BrowserPublish, ViewPresentation) # Register this presentation as the default ComponentArchitecture.providePresentation( ContactInfo, '', ZPublisher.Browser.BrowserPublish, ViewPresentation) # Allow the update method to be accessed by people with ManageContacts AccessControl.protectClass(Contact, ManageContacts, method='update') # Register the edit presentation ComponentArchitecture.providePresentation( ContactEdit, 'edit', ZPublisher.Browser.BrowserPublish, ContactEditPresentation) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/edit.pt === Edit contact Enter the information about the contact.
First name
Last name
Email
Address
Postal Code
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/permissions.py === ManageContacts = 'Manage Contacts' === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/view.pt === Contact Information
Contact information
Name: First Last
Address: Mailing address here
Email foo@bar.com
From jim at zope.com Wed Oct 24 09:41:58 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - Contact.py:1.1 ContactCityState.py:1.1 ContactEdit.py:1.1 ContactEditPresentation.py:1.1 ContactInfo.py:1.1 Postal.py:1.1 README.txt:1.1 __init__.py:1.1 configure.py:1.1 edit.pt:1.1 permissions.py:1.1 stubpostal.py:1.1 view.pt:1.1 Message-ID: <200110241341.f9ODfw914527@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv14436/PythonProgrammerTutorial/Chapter1/Step5 Added Files: Contact.py ContactCityState.py ContactEdit.py ContactEditPresentation.py ContactInfo.py Postal.py README.txt __init__.py configure.py edit.pt permissions.py stubpostal.py view.pt Log Message: work in progress. still pretty rough === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Contact.py === import Persistence, class Contact (Persistence.Persistent): """Contacts keep track of personal data, such as name, email and postal address. All methods are protected.""" def __init__(self, first='', last='', email='', address='', pc=''): self.update(first, last, email, address, pc) def name(self): return "%s %s" % (self._first, self._last) def first(self): return self._first def last(self): return self._last def email(self): return self._email def address(self): return self._address def postal_code(self): return self._pc def update(self, first=None, last=None, email=None, address=None, pc=None): if first is not None: self._first = first if last is not None: self._last = last if email is not None: self._email = email if address is not None: self._address = address if pc is not None: self._pc = pc === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactCityState.py === import Postal import ComponentArchitecture class ContactCityState: "Provide access to city and state information for a contact" __implements__=Postal.PostalInfo def __init__(self, contact): self._contact=contact lookup=ComponentArchitecture.getUtility(contact, Postal.PostalLookup) info = lookup.lookup(contact.postal_code()) if info is None: self._city, self._state = '', '' else: self._city, self._state = info.city(), info.state() def city(self): return self._city def state(self): return self._state === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEdit.py === from ContactInfo import ContactInfo from Contact import Contact class ContactEdit(ContactInfo): "Provides the ability to change basic contact information." def update(first, last, email, address, pc): """Modifies contact data 'None' values are ignored. """ ContactEdit.assertImplementedBy(Contact) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEditPresentation.py === from ZPublisher.Browser import BrowserPublish from AccessControl import ClassSecurityInfo from permissions import ManageContacts from Products.PageTemplate import PresentationPageTemplateFile from App.common import absolute_url from AccessControl.ZopeGuards import guarded_getattr import Acquisition class ContactEditPresentationClass(Acquisition.Explicit): """Provide an interface for editing a contact """ __implements__ = BrowserPublish # Create a SecurityInfo for this class security = ClassSecurityInfo() security.setDefaultAccess('deny') # All presentation components should provide this. def getContext(self): return self.aq_parent # Input form security.declareProtected(ManageContacts, 'form') form = PresentationPageTemplateFile('edit.pt', globals()) # action method security.declareProtected(ManageContacts, 'action') def action(self, first, last, email, address, pc): "Edit a contact" self.getContext().update(first, last, email, address, pc) return self.form() def browser_traverse(self, request, name): return guarded_getattr(self, name) def browser_default(self, request): return self, ('form',) Globals.InitializeClass(ContactEditPresentationClass) # Create the singleton component ContactEditPresentation=ContactEditPresentationClass() # and a binder for it ContactEditPresentation=Acquisition.binder(ContactEditPresentation) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactInfo.py === import Interface, Contact class ContactInfo (Interface.Base): "Provides access to basic contact information." def first(): "Get the firt name" def last(): "Get the last name" def email(): "Get the electronic mail address" def address(): "Get the postal address" def postal_code(): "Get the postal code" def name(): """Gets the contact name. The contact name is the first and last name together. """ ContactInfo.assertImplementedBy(Contact.Contact) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Postal.py === import Interface class PostalInfo(Interface.Base): "Provide information for postal codes" def city(): """Return the city associated with the postal code. An empty string is returned if the city is unknown. """ def state(): """Return the state associated with the postal code. An empty string is returned if the state is unknown. """ class PostalLookup(Interface.Base): "Provide postal code lookup" def lookup(postal_code): """Lookup information for a postal code. A PostalInfo is returned if the postal code is known. None is returned otherwise. """ === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/README.txt === Step5, adding features and utilities In earlier steps we showed how to use simple classes to create content objects that can be used in Zope. We also showed how to create separate presentation components that provide web interfaces for your objects. In this step, we'll learn about components for providing additional functionality to your objects and applications. Components that provide new presentation independent logic are called "application functionality" components. There are three standard kinds of application functionality components. Features provide new functionality for other components, typically content components. Utilities and services provide stand-alone functionality. Utilities and services differ primarily in their role with respect to the system overall and in their management. Services are foundational. Many components and applications will depend on services and services are moe prominent within the compoent-management facilities. A common use of services is to manage component. There are services for managing presentation, features, utilities and services. Services may provide other foundational capabilities, such as cataloging, version management, and event channels. Utilities provide functionality that may be used by a few applications or components. In this step, we'll add a simple (trivial) feature for getting city and state information for contacts. The functionality is provided using a feature because the computation depends on a content component, specifically a contact. The feature will look up the city and state for a contact using the contact postal code. Looking up a city and state for a postal code is not unique to working with contacts however. We'll use a separate utility for doing the postal-code lookup. The 'Postal' module, in 'Postal.py', defines two interfaces needed for postal code data lookup. The first interface defines how to get postal-code data from postal code information objects. The second interface defines postal code lookup. The 'stubpostal' module, in 'stubpostal.py' provides an implementation of the postal interfaces suitable for testing. This module allows us to build and test our contact feature, but we don't expect it to be used in a production environment. Our configuration module registers the the stub utility using 'offerUtility'. The 'offerUtility' method takes two arguments, an interface or a name [1], and a component. The interface identifies the utility and the compnent provides the implementation. In this case, we register an actual component, which is an instance of our lookup class. This is in contrast to the way that presentation components are registered. For presentation components, we don't register the component directly, but register a callable object that returns the presentation component for a given cntext. We "offer" a component when we expect there to be multiple implementations of the same component. We could have used 'provideUtility' instead, if our implementation was production quality. It is an error to provide duplicate utilities with the same interface. It is not an error to offer multiple utilities. An "offer" is always superceded by a "provide". The configuration module makes security assertions for the utility classes, declaring the utilities and their methods to be public. The 'ContactCityState' module, in 'ContactCityState.py' defines a feature component for getting the city and state for a contact object. The constructor for the component uses the contact 'postal_code' method, defined in the 'ContactInfo' interface to get the contact postal code. It then retrieves a 'PostalLookup' utility to get a 'PostalInfo' object for the given postal code. It looks up the utility with 'ComponentArchitecture.getUtility', which takes two arguments [2], an object and a desired interface. The first argument is passed to allow the utility lookup to be context dependent. Utilities can be registered in specific locations (e.g. folders) in a Zope object system. By passing an object to getUtility, we cause local utility registries, if any, to be searched. The constructor gets the city and state, which it saves for later use in the 'city' and 'state' methods. The feature is registered with 'ComponentArchitecture.provideFeature' in the configuration module. The 'provideFeature' function takes three arguments: an input interface, an output interface, and a callable object that creates a feature for a given context. The feature can be used with any component that provides the input interface. In this example, we pass the class as the callable object. Features are like presentation components in that they are context-dependent and must be registered with a callable object that creates a component for a given context. In this example, we chose to create a separate component for each context. We could, instead, have used acquisition to manage context as we did for the edit preentation in step 4. To use the feature, we need to look it up with the 'ComponentArchitecture.getFeature' method. For example, consider a Python script that wants to find out how many contacts, in a list of contacts live in Virginia:: nva=0 for contact in aListOfContacts: info = ComponentArchitecture.getFeature(contact, PostalInfo, None) if info is not None and info.state() == 'Virginia': nva = nva+1 In this example, 'getFeature' is passed three arguments, an object, an interface, and a default value. In this case, the object passed specifies both a location to search for the feature and an object that the feature will be passed when it is created (or bound). It's worth noting that if the object passed as the first argument already implements the interface passed as the second argument, the object will be returned. It's worth noting that the feature can be used with any implementation of 'ContactInfo' and 'PostalLookup'. If there were multiple or alternative contact implementations, this feature would be applicable. Similarly, the feature works equally well with a porduction-quality postal-information lookup component and with the stub that we've provided here. ------------------------------------------------------------------------------ [1] -- We allow either an interface or a name. The name is only used in cases where defining an interface is too much bother. Interfaces are prefered. [2] -- An optional third argument can provide a default. If no default is specified, an error is raised if the lookup fails. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/__init__.py === # do configuration import configure; del configure # export class and interfaces: from Contact import Contact from ContactInfo import ContactInfo from ContactEdit import ContactEdit === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/configure.py === import App import Interface import Products.PageTemplate import AccessControl, AccessControl.Permissions import ZPublisher.Browser from ContactInfo import ContactInfo from Contact import Contact from permissions import ManageContacts # Protect contact view methods via the info interface: protectInterface(ViewPermission, Contact, ContactInfo) protectInstance(ViewPermission, Contact) # Register the contact factory App.provideClass(Contact.Contact, permission=ManageContacts) # Create a simple view presentation ViewPresentation = SimplePresentation( 'viewContact', globals(), permission = view, ) # Register this presentation under the name "view" ComponentArchitecture.providePresentation( ContactInfo, 'view', BrowserPublish, ViewPresentation) # Register this presentation as the default ComponentArchitecture.providePresentation( ContactInfo, '', BrowserPublish, ViewPresentation) ############################################################################## # Edit presentation from ContactEdit import ContactEdit from ContactEditPresentation import ContactEditPresentation # Allow the update method to be accessed by people with ManageContacts protectMethods(ManageContacts, Contact, 'update') # Register the edit presentation ComponentArchitecture.providePresentation( ContactEit, '', ZPublisher.Browser.BrowserPublish, ContactEditPresentation) ############################################################################## # Postal code utility import Postal, stubpostal # Offer out postal info utility. Hopefully, someone will provide a # replacement. ComponentArchitecture.offerUtility(Postal.PostalLookup, stubpostal.lookup()) # Allow our postal utilities to be used by anyone: AccessControl.unprotectClass( stubpostal.lookup, interface=Postal.PostalLookup, instance=1) AccessControl.unprotectClass( stubpostal.info, interface=Postal.PostalInfo, instance=1) ############################################################################## # Contact city and state lookup from ContactCityState import ContactCityState # Provide the city state feature ComponentArchitecture.provideFeature(ContactInfo, PostalInfo, ContactCityState) # Provide public access to the feature: AccessControl.unprotectClass( ContactCityState, interface=Postal.PostalInfo, instance=1) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/edit.pt === Edit contact Enter the information about the contact.
First name
Last name
Email
Address
Postal Code
=== Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/permissions.py === ManageContacts = 'Manage Contacts' === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/stubpostal.py === # Stub postal utility implemantation import Postal class info: __implements__ = Postal.PostalInfo def __init__(self, city, state): self._city, self._state = city, state def city(self): return self._city def state(self): return self._state class lookup: __implements__ = Postal.PostalLookup _data = { '22401': ('Fredericksburg', 'Virginia'), '44870': ('Sandusky', 'Ohio'), '90051': ('Los Angeles', 'California'), } def lookup(self, postal_code): data=self._data.get(postal_code) if data is not None: data = info(*data) return data === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/view.pt === Contact Information
Contact information
Name: First Last
Address: Mailing address here
Email foo@bar.com
From jim at zope.com Fri Oct 26 10:26:12 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - slides.sdd.gz:1.1 Message-ID: <200110261426.f9QEQC313065@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 In directory cvs.zope.org:/tmp/cvs-serv13058 Added Files: slides.sdd.gz Log Message: StarOffice slides for Chapter 1 === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/slides.sdd.gz === (632/732 lines abridged) *;] xEL\$$! a}-r (&pʑPHTv]}XP/Utq-cUO}" {3 N~ԿLүJp󎬏KTRS!.]p.֐VwwwlC{q^ H$@K$)@*- h d@ |5 h v@{rt B tz=^@o_τ~A``0\ F#kQh` 0(qx L&}0Lt`0(J2`0@P  p3p p+ X ,ˀJ ^ EA4?Z`ŘS4:5QE{/޾ |CiSGc k|#l K˃Ӄs* / N_0b^% w.kWiEPnM|)*[4b0el+3/s_PB\P0'h-'VTXP./$t^ ゖ[1bt¨%D-’l vSVJmOln%3Sx` :v%좀'[S+gʍ3}nsV*"6&ߪo!`n"bA"a"O8E3&"e:<$x}9[m΍;@v,H4c^^q ([/Z#17KJo,7b.TA U U'OՓW)kzF=STjmMKzTHЮ.O@:CP:^Et$^CGt K"Zaqݍk Y y*w3@O_lL̍0!xr mctACӬ1_2D.oMM5nqCI3q.aTV$_S:qg3M5d  kQO\sMz,^C T.+~t2eپ?|;7ԊsF}=cr7|bWWҿ8 q4F7yB߃TB?/H4|<o? E涌sܞ&n2{!둹 M\2"{lIxSK7$~*2-yJN|NC&OJOG&s;-ܑ&nv9TpĎ0sSbraf'$)"AR$RNH_ $$<sPTU5q 3q"ŕݐ0q#5=6\GR;2z7ԑHWG:"%bm-|<R8T2 BE oJtb-)d^R܇TȡUG7WȭW=ٓ:tS7 @'@`0ZS2rq* 5!W,h {"Œktyk,sRQ((4kS=sJC'ǠP֥^nF'EĥeO(_ }5d):L tN(:bV2u*ףܒz/4צu2>+]fS{FR/N(d)P'.,(-I(-u2FAB2~Po@i)SNC)St2u3PgARULR2,W->.-l~_Y$!z/-aᯒo7-y˳:HnߢoYZxGj gT;˴]] u(a uG u>,߀\Ɵ-2j!Gls~B>SUxB[q6v.;tG6iy/ &n"(=: s#uG;CYn T G#!4;0QT)t1.a3~{9ˆ!Gge81R+]ǖPK^Esmhq %Bɸ|VYX}F,{d)׀gSrTX=^"z|yMDxkBR@k^Gړ|>/,kU^$JUn$K)^PbOq-z Q~G^$g}-+v8[P$\2<Gқin:Q-3UZx D_OXm <>=)|D`ZK< +j=7XydKN.dR|\L2nMeC]. +虼nˈr,3$q 砕KBvI~֥gYYM$SH ht%@%<З2Z&Vi'4I7uS-8M-}Bk)NMQ<(=7^禅( z !yzun_bue*$$%BI:%;DtJNSZ9t)VS,n:xHA15(v7OS@fUr%H#OK=HE-kB4Y$"%X`f!|U =@wX l^>܈m!zGm*y'.EH` p;N6@`:<2 |$z?Va- (&Q=@;|J;n_qC;uSQk ׊=aOM;.H@ nE=À\ewF gTjZ^)/ȃx>`< -Z>kNyiRYب=m6ki[#ڣc6 _NO\z^AǪCyFk}7}h.4LEuҭoAo_Ω_&u6hzpa'YQ1ҎxIO8iNvDݖA~QժZѪavũn3lvC4w;)LLe=ěB>];OkŧyߖilNiڕOœzc"r¦N~Gk"vb<n!{wt7ŋU/n1QTm$k}Wduç#g%#q/J:z1sAG1Da;ёV֞=I"d?!>Fn"!8 :I6 D!rǖ.K,JȬQL!o-: I)!٭ YԑD"$`!= 0 !xӞA~hPKBYbՅ=7Bjrχ?^o&8DĻbӉ3'roBRS动rj0˴VG{ooVXat g 7=-_YԵ~cֺ̐Q6ě$tjB+,#` !MI$"bC oPR$x9job̘8"1)x~Gs^sƏIL9hB-)hlh)f8GFlf*H6@y_s _>|tA0BY `@ Mg,ISo{=hJ(Z? {$)PNW4vz՘NJq\'L14fD)ZIJ >  E׉dg1$Oa뢐#k~ίC ލ#LtUqzf."lfRcЗښ0Ӗ⭔Xj.zn%ަP,/n.AbS kB]AL\,ީ0gB RK!=$7[H,Y)FJwCKwEtH}W6IȯL0`eQgXld tX&,@ y$(8C $, C _/'_[zW\v[nREoPJ&:,Ldq \f,!QPjL|Ԙ(4]TNJ`##>b#v?O؃C#T\lܶ-`;at'u~8jClX};WZh{F_EQ*M 怷 - [!N!+LMI/"^`()smt|0r#Ð1aDvϨcSVJ[+)3FɘX[, \i1~CO"Lڅ=ọEJR2) wɈbYu֔ȍKC{yr HGD)yixG'HE>0i$+P'V :ȺIY#g2A%i>C4$GU<xPd5VM:ʐu%(GuTfU^Lߠ>Y΂&:^GYmD[&ڲM:}D5 4іkնGD[&ڪ5Vwu>8Sm/sں|D\9m]?TKn7Ϣ^KdP]J*p@.5x;[Y#Kt L#M,IL*˥:lwt\>+k'M&WEtUk y/gza%KBåT+2oʤȦRVېtLz``_~ɾ\V~ž} !9%>,q"NX`'rj9u`MNXtqz9}BN؀3l6sƀM8)f\3NOۅa=ms9vwvGw%n׳3el2Ԑn3BDNNI<^Ֆ{U ߜ5Z?%xZКt'KY(eqTHY*eqRDY)e馔e9Y)3eE) eU)+eM)e,M&H" M}eDvV9}Z8~B>ߢR OgaTjYZ8B>oR OgsTj }>J->O1l)A)' *Cƍ4f/~"F)?2S%EI_~ӄA(?AP8@6+"P @L(puB{i,- a t`.BpǓS+!BP 䛀LSoP \sh -`: kC#hdbBܘdsQ(#C_BcW{fjL1yz83FFMw\UMo|(";d%ȟqXh'Հ.4 !/Lz|Q?*.N/ ǴRg;W^3fY6>al^W/=k= CLˁegWeM:k9M'qoKV$ wؗyJ ]bjQəiի%DZVN+oyrJ˒)-*nd_([19 Gm*$]V2$;gsڗ7^zLj3ӲlZ^{j9W띱t7i(ߑ&eŒ)ޜ[wok$,zl ^?gȬ^Y]swZ݌y͉81I}Fa'"k(`y{փV#鞒_` ȫp~h`-N.OY:ɡvBO]Zsn~/bKn|-.lsŇ_/V,5{n&r7w.ةn/jiq|k֚gYK#D֤ÎkgeUʃUkkY@jw)b.U8=s|mFz0UQ^i=ө\7?{M(cmz?ТgDm! .oXH#. b~ lTĂ'nYxIoG%|wl-`+f{, {yRUw<{` Ck״4)@Oxl՘f~:|dWΔ+'+׺o_ws_W6BgIY v҂gU]] =딸G=N9:ws#BrJFW]lqƽ0/#%~ՅzZb# F2WI4g=VZwdcg^N?1;u.!s-D{^VZhyZ-Vږ?\d߉LSrpSsn=Z;,P8v[!yvOW>qbwڽSvǜDz̸[?/2~]Bx`3"%2]۳viyF9Wڿ,۴jCjScMfp;ܷv}laLW͈ &{аXpۼl9B'>7^܈Ua>QUSGC:VpdO]ljM= jcc#=XVtKNG;Q%榎ز3/s.'ʣ_H-9tNJK%=ӆ>I`aa_rO/6NXvwʽSf Âgjn$XluX`M_h9ܹi]õa/dkw Kw2f*jv~?< {hKv {:iCMsfFh* 3Vq6$ ,q((=IP͵H_&J0ٕ;h-ic[b^)mR4b粊svx"fi]V{idFmeИC<[lՕXѾ1/sn}1{eáz:f6V(an0XDu!:JYY}O9>_zUusι}~[y4|q#C.mo{(Cfk2RVt />4 H|`5!]%4HfAqȢ1gR:U^ qʡq9dKHEeps$BOFtqԛT& NL^yry3b v"r:=jO_[v*oڢmdמ!%Z V5b R#3^Y8M03պ ?<5s|ߥ=* 'mo!w7wsr]T+d+٬Kd&7Z5Fjd(m OVsً*$S)qa2SɯUM79}H 0qxd.Ck %kL"ý8] YQ<$fM3VGhͫxD;q}I #RP|]񹵱Z/ow~_U}Vů2uǟ5'l o04QdW_۷ྉV^ɻ( /C9 tCXWa} OLnv=+{|Cb2b闻Lv;.'?n+_EKr>zJ' t|CkP[y">[ \`d) A=2t xք?8)2?Z|)t/v8+o"W;ߺ^*@v. 8V9n i$?j4 cCaBnӵ\Sie_jK/CYݾ_W۾S!z{Q: Uyi[p(J_Ov1zk1 ?\Jty`tuWVjrO\aOPC]S[r0w^;$^P_!OFgTY0ķ(W橉6],5/?FQJ,Ѭh&?],5Yv-xDuBywk]Ŋ*Mg?$`EpTQ>ʽm{Tz9^ұy:;u'c/W[آeCþ aMtywSrjdiH 7|U?!y7RfC/ eO 4WtC8} {&XAG6|mcE ˰Gf5x5OAvIrtZakBY++~8|ؖL*2TASfQP-ڻGߦ,{R?t%g^;Hp$䟖_wX'xt92Ͽ=,0,Őqlݲ"mֻ+ tE⊥P\WP]rh!RFA%ǩrMH%㵕0[tYc`T^Yjˬ=0t [-=- -=- -=- 632 lines omitted -=- -=- -=-] C:go O%? ar mpr7"/62#)?S~~@ptk~A*H/N%$ 9 A>>T npZ  oٝ;d8?_-Um|-iYTB 2CFOکdN%h3 HjgJO¨(-QOy|7FlPǩL(Kǿȟױ7*(? hՎt5G\&' wr0<>3U`OYKn%[r0ߕw~-qg,=ڝhtЇCG,lO9)F;ŸPn@ڀ z>> +++ig,MsTAg26Y8ACn rpj<اÂ3B@ ЅA >5dtd#rZy 3Ϝ-V$ﴣb8U Pc\ r(X}PxȤ+.R/ybY:v9/M ‘s4{ ?<#[Q$RDTsD '#j8wvՎ G:0~FY-9K ] pnRbf'֟/rd)_"~"BUh@Ap!>}djl} Ƿv8[t/F8~/@kȂ.= Z ;Hii=x}@Hbg'{BS"p>)"VCX3CyhA | )A|}*ޓ%pP% 6F'T OMͦf'28۟$罐: C| 97X X\<p0.$@,IV3-NgX*e+.,[2]DKvR*)r\,%Uˑ(rgbŊ8JE{4Y`ϵ?]i{ulVbENӱ2n$!6ֿjj=43>Kws]rA㋺@c,+i&BֳzJDIF7u$\lMʽw LOs~ș+.Df2l&t r)kIrhҝ^m[/S /˹C-F6bePGrzgE.w Hͳ4[R)Ҳ]-sv`XPKE%< dU3*Ym\^#ɪa&8?|ƥhG+I?uX~DI<>Gt9fIK-+*ߠ?ElPǼwgX~aͥ0ve]u3`J, c "? Aw(MP%6.}.v2CJ}K:h/qr@,pwiKB߬I(ev*e\ZgTTv\^Fa#p) S̿hEǧ[)ڑuJC ɮ$%i)! D%!y.2HR 6`%F+|KTLܓGԞ-@P}QfxUrVADn:p7(!Y!rΝVq荝 mC1_0/]zK-P˒bN/UnY*'B/j,үBi'Cv1fPATZ >HUXZBvgɮ$WyT緈bq%<<3{$|ksi~zF/9_\t5#7.H !(uRW\I0 >Sh0U6YdeKujQ*~J~d=DWqp@}lH69cTs t'_9c'<9$ϯ@2Wylzr4|?V6,Ўtw$ J$kX+``*v G@D*8[H 7[&>Ƞ)Od0S| rA.2 >Ҫ?~>< cg{x]#Hz?i5BR#NAJEE>t;]6y$5Ic@" }|0,T _2^f'O_8ֱٕb ߑ7jlvۉFcէ)GJ˴ݡROm&,ew0;W9j@Ah=ZF!4נF bոY 5 .ntW'9W<n9gr}I$9EK R)I$[słijdٺÎѯj"Y#C;-~x W.L35C!yoV2 P<M;S.Na 4JO@ H6"L di4a:lۃ0-t;@Ic@ 70VFƞʪ!>Gu =Gm`-;}=϶\^E.&8ǽ ry9'I&vo =+,Sd rֈi#saW6[|h=2ڳ0J24ph{7&%2"m#O[c9dpY Ƅ(Gh~EDCZU8 `B,U ŁEӠjV P$2C,uGE[Pf^_XietWɇQ" YE 4r+ր9ё6U9$3~ (6+ywq{ۄَy>_gp a&`k!ր1@ CV ]rCEUve[B}xڍ>Ryw+ZX{$񛐊 a ѷ Qd_JҡD'y&*LfT> %p<#kzdRj\Y8ݰ> Oɦ)x__Qo# LE4js1U&( k-֑/ ȗs] ȗlXw /- x_`y3BtGVz [=A!Aۚ?DW0!-}:aZBc@ 5!e2ݩ^ XwIGN*,\ZgPOh dB}T&EWKy!Ynfx|FdS4j"'NXflarcI4=,)>KժQmM/Tp\FxH xo83:i43 *36"rۂ<љٕm Xg 3[y^bh7>Ex._BRAǯ)mEJ[ <.CE)dȖnǡ UCl;ێĶ'MɥmєX<<ԁޟ d gra ~vO(3a5eժB"~29N^&'хq>ʌU-Gb(@磏Ah^.ؖ  J`/db~3}ן}Dcj3 ?Cw1F>H~ݿL_!|2);)rLbvmbkvy8.mFQ,vӽ˝0c:-e1m KzPz0QؕhsyBp`` "ן}_-' D72d7/n8 w@GP)}L:[l:t[hLhV"/^Z9Bnܱ Sl:`Kśr*⚿E+Vpw"d皹|e72s|R'vIBE R_f<յ;e ,chwdeRGV+IYIh*ypZ RBH9yٓzJw¬6nq碫VV.jGxk烲1>C ,gHɫئ]dfLζxd<b=lErp3{E o9/hKZ 1$.zX6? ~?O\7 ;Rɻ J mHDTzDIgyzu5݅T)T2RF*JJJgwvz9z(ded޴u.,&RĀtW^5su벯 P`;Vr'qY8kbca&!l˭ -JgKfW[Mi(6l(ޒa÷y[\-u[>0i%lKI k fl)jKY.kKBȜg8`F-Ym#FI/M[7f.߻7-o#0Q|h3v8NjS`=~/" ;=7DlG+Ib,&/Z;i^-?IDK3/y`cE;րd66Ms-nIs~BǗz4{C7[Xs Dc:Q2 }r%t0PW-ò(vnpyUeG/Մ,|xnNBom()Y-o|de7Q୾8Eo[*Op,!t\<чd~?& .ANֽ?" .DJ3/hxNv g,a'Juz[ֻ.%E>*%[-5"n(;Rv>j5B2&H v*kc.3E&t>D~=D'FTLC!S\p3tjӰOb(g_pQ^hs`W)"딻TdX>Exӷ´ }qj̇IBu5lZUBFOt?CBTٰ$b}`䆐"D"+:{>dIpfc?9am 2p!8rGDu853;jzyVV1CMĮxNEcYgfD0av/;l]Lbae^37wcy?,Ys턂  E^$<"Us QM}*[ iLA#t-6 E`uZrzJh^ {>yO9ʺn$=F8rØ,56 ׷>mJ5Ek Fx'ѽri4+ 7;ɻ L_ظހb7Ӱ4|y7B<%^ʾžcz ?a/"<-1{e:QPLUÂ?LKCnE/. opt9:eC@tP? d:8DFǥG?؛F=ϝdgNR--uNu>_Q^E~.월:.*GѲÚu's]bG_gW-Iz ,(?IW=^gQ:'`cV+fFk-k#Bk"J)SOSҩ4-rt2BF ]zD7&-U91h,;>O^¤#SWLD=`|"PwFLqb9yjX<\DpI%7ai(T A wGCю?)|$e6oOD|yDɗyq֙~"2~l&e~aڲR{e75.yMV?jSE)%˖/kZO^ɚnT~cd(ٴ"jBP6@r Coa„VT`訦vxT1)͏2UX&lWͷ iޫ蕪;^|} L~]UУ^JkkQzi ^!I%;4io6=)k𣦽  U[|(v(-D Hݤ1# zvh5FWՐIc5^)Qd`Mkq&mv8rK a=UkZ71IIMc`?~q-=:S TOeY^{>z"-NNW-ZFKz\*__2F$@D\&,?i9pL^f_,g Kvi<4>̠E #^s^/!_+I_2o6ߑ#1dM*LTT)w#)l+(6ȲU %)U'K9Ta\0nQvU/5,.U+ S:[w$j(ނڂ_SuHv%)< VmC-N@*4!wܪŢwvšK}ɾ~Qfg+UwzYOS.G7x5ؒnafY-Hg BGH$LdgOV{>KB(Rj"ۓۓl"* Y[xtHiI;cXJ;˔̞\:Mxllus1B.2+:{"!_gMuکD4r1&4&;$4&'10|`Z& #,j0,#%ǒ|cIFnݭ˩eI%\;K7Kr *I& :MRyO0v(R;R" Ep:`3쟋  KxM%t`xP5VWPsxO }$˵}"̄8 (אk5r‰<q͢Ї:I$iv,Hv%iA>'VzߵǑV >|(#򡞨ws^x_ڍ}}\RqYϾd#Ko^}GFVk+Nl'/9Zy= "N9AG'^[U&s5l44r95 XQ9^R9~sNV4[#ƹ.2fQ@sϼ#]π_#jGx>} ~$IOpvU̎BYByP+I ۆo GIBk<2lhQ'qW#EݡR'`m.n.#7,$MCAa8jA, u$2FegS}ܦB sff+!ۈ\2!ZV;PUc]+q!*K~L?X'&RC%!/++l٢ 2϶줪?CWD@7]\'!ϛa$\Pl,ҡbz(!H»޸Sa[r:,",,:~_Rt (6[J7Ǐ@j;H&??V;?RK=K6?nx3d[C;8^Lȯ2}fcr~4Rr{蒞E\6,J UBV1?[ZIY/C^/=MLv+^h4km#CM@p/b-~ʵcK>A'Y.08%2kXJ1 wBmdzݒ8O6p*ZK"cӳWD<ëEդꮀ,T!m~vx.j"bqLժU\G}]Kl (T \!Q2$SkI刾j%V¶!^] i 3)" ZWhUiܦ7NF!W:xv?Նrjb&i<#QO3ޔSWגGz*whšK}5őCx>n oP?RXeikv*\(FlÌ܊mp;UbB< ,I67ֲQVyy .k97XCa|0snޢ|o~/n:pCCܱIՎt`WRᥗәnh ݻbR+pZ L?; 淛g];>&淪 ѷzy_T&U(7ӕ7jjը>/\Y"Spg+K{<^~Ro_¿n[wq;U}JdWK nZ}'7_৚2^b?'GHJ<rׂtڋ=r_%%r,46tzL\µt hSQ;u.̂Y`6ضik0pZ/}!|=O]rAT%Y%YQyV7H_"wډC?6{<:yBo5p=r_wS0DgϞ2BŢ_iJFCF 9@2ѯ] 'b:DUO?<j From jim at zope.com Fri Oct 26 13:48:57 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 - IContactEdit.py:1.1 IContactInfo.py:1.1 ContactEditPresentation.py:1.2 README.txt:1.2 __init__.py:1.2 configure.py:1.2 view.pt:1.2 ContactEdit.py:NONE ContactInfo.py:NONE Message-ID: <200110261748.f9QHmvq24369@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4 In directory cvs.zope.org:/tmp/cvs-serv23341/Chapter1/Step4 Modified Files: ContactEditPresentation.py README.txt __init__.py configure.py view.pt Added Files: IContactEdit.py IContactInfo.py Removed Files: ContactEdit.py ContactInfo.py Log Message: Numerous changes to simplify presentation, some of which has been started prior to the previous checkin. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/IContactEdit.py === from IContactInfo import IContactInfo from Contact import Contact from Interface import implements class IContactEdit(IContactInfo): "Provides the ability to change basic contact information." def update(first, last, email, address, pc): """Modifies contact data 'None' values are ignored. """ implements(Contact, IContactEdit) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/IContactInfo.py === from Interface import Interface, implements # Local Package imports from Contact import Contact class IContactInfo(Interface): "Provides access to basic contact information." def first(): "Get the firt name" def last(): "Get the last name" def email(): "Get the electronic mail address" def address(): "Get the postal address" def postal_code(): "Get the postal code" def name(): """Gets the contact name. The contact name is the first and last name together. """ implements(Contact, IContactInfo) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEditPresentation.py 1.1 => 1.2 === -from AccessControl import ClassSecurityInfo -from permissions import ManageContacts +# Zope imports +from ZPublisher.Browser import AttributePublisher +from ComponentArchitecture.Singleton import SingletonBase, singleton from Products.PageTemplate import PresentationPageTemplateFile -from App.common import absolute_url -from AccessControl.ZopeGuards import guarded_getattr -import Acquisition -class ContactEditPresentationClass(Acquisition.Explicit): + +class ContactEditPresentationClass(AttributePublisher, Singleton): """Provide an interface for editing a contact """ - __implements__ = BrowserPublish - - # Create a SecurityInfo for this class - security = ClassSecurityInfo() - security.setDefaultAccess('deny') - - # All presentation components should provide this. - def getContext(self): - return self.aq_parent - # Input form - security.declareProtected(ManageContacts, 'form') - form = PresentationPageTemplateFile('edit.pt', globals()) + view = PresentationPageTemplateFile('edit.pt', globals()) # action method - security.declareProtected(ManageContacts, 'action') def action(self, first, last, email, address, pc): "Edit a contact" self.getContext().update(first, last, email, address, pc) - return self.form() - - def browser_traverse(self, request, name): - return guarded_getattr(self, name) - - def browser_default(self, request): - return self, ('form',) - -Globals.InitializeClass(ContactEditPresentationClass) - -# Create the singleton component -ContactEditPresentation = ContactEditPresentationClass() + return self.view() -# and a binder for it -ContactEditPresentation = Acquisition.binder(ContactEditPresentation) +ContactEditPresentation = singleton(ContactEditPresentationClass) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/README.txt 1.1 => 1.2 === In this example, we'll provide an example of a presentation - component with multiple methods. We'll create an editing interface - for contacts. The file 'ContactEditPresentation.py' provides the - definition for the edit presentation component. + component with multiple attributes (URLs). We'll create an editing + interface for contacts. The file 'ContactEditPresentation.py' + provides the definition for the edit presentation component. 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: + + __implements__ = IBrowserPublisher + + def browser_traverse(self, request, name): + if name[:1] == '_': + raise 'NotFound' + return getattr(self, name) + + def browser_default(self, request): + return "view" + + This base class provides three things: + + - An assertion that instances of the class implement the + 'BrowserPublish' interface by defining the '__implements__' + attribute. This example illustrates how to assert interfaces in a + class definition. + + - An implementation of the 'browser_traverse' method of the + 'IBrowserPublish' interface. In this case,'browser_traverse' + method provides publishing of attribute values. + + - 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 + than to one of its methods. The return value can be either: + + o a string, providing the name of a method to publish, or + + o a two-element tuple consisting if a new object and a + 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 + their default interface, or they must override this method to + supply a different name. + + Presentation components present other components. We refer to the + 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. + Presentation components have to track a context for the presentation. There are two common approaches for doing this: - - A separate instance of the component is created for each + - 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. + 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 an acquisition wrapper created for each context. - - The contact edit presentation component takes the second approach, - which is why it mixes in 'Acquisition.Explicit'. It is common to - use a 'getContext' method to encapsulate the mechanism used for - tracking context. In this case, the 'getContext' method uses the - acquisition variable, 'aq_parent' to get the acquisition context. + 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 @@ -35,34 +86,7 @@ the component. The 'action' method applies the update and redisplays the editing form. - The form and action are protected using Zope's declarative security - API. This example illustrates making security assertions inline in - the class. We needed to use the 'ManageContacts' permission that - was defined in the '__init__' module in Step3. We've factored this - permission into a separate 'permissions' module. - - The last two methods in the class are defined by the 'BrowserPublish' - interface. They affect how URL traversal is accomplished. The - 'browser_traverse' method is to lookup a name during URL - traversal. The implementation here simply uses 'guarded_getattr' to - retrieve an attribute, subject to security checks. - - The 'browser_default' method is used to control what happens when a - URL points to a component, rather than to one of its methods. The - return value is a two-element tuple consisting if a new object and a - sequence of additional names to traverse the object with. In this - case, the component is returned with a tuple containing the name - '"form"'. When a URL like: - - ...../aContact/(p)edit - - is used, the 'form' is displayed as if the URL had been: - - ...../aContact/(p)edit/form - - At the end of the module, we create a singleton presentation - component and then create an object from it that, when called, will - return the component bound to it's context using acquisition. + 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 @@ -84,5 +108,3 @@ 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. - - === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/__init__.py 1.1 => 1.2 === -import configure; del configure +import configure # export class and interfaces: from Contact import Contact === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/configure.py 1.1 => 1.2 === -import Interface -import Products.PageTemplate -import AccessControl, AccessControl.Permissions -import ZPublisher.Browser -from ContactInfo import ContactInfo -from ContactEdit import ContactEdit -from ContactEditPresentation import ContactEditPresentation +# Zope imports +from App import provideClass +from AccessControl import protectInterface, protectInstances, protectMethods +from AccessControl.Permissions import view as ViewPermission +from Products.PageTemplate import SimplePresentation +from ComponentArchitecture import providePresentation +from ZPublisher.Browser import IBrowserPublish + +# Local Package imports from Contact import Contact +from IContactInfo import IContactInfo from permissions import ManageContacts -# Protect contact view methods via the info interface: -AccessControl.protectClass( - Contact, AccessControl.Permissions.view, - inteface=ContactInfo, - instance=1, - ) - +# Protect contact info interface and instances: +protectInterface(ViewPermission, Contact, IContactInfo) +protectInstances(ViewPermission, Contact) + # Register the contact factory -App.provideClass(Contact.Contact, permission=ManageContacts) +provideClass(Contact, permission=ManageContacts) # Create a simple view presentation -ViewPresentation = Products.PageTemplate.SimplePresentation( - 'viewContact', globals(), - permission = AccessControl.Permissions.view, - ) +view = SimplePresentation('view.pt', globals(), permission = ViewPermission) # Register this presentation under the name "view" -ComponentArchitecture.providePresentation( - ContactInfo, 'view', ZPublisher.Browser.BrowserPublish, - ViewPresentation) - -# Register this presentation as the default -ComponentArchitecture.providePresentation( - ContactInfo, '', ZPublisher.Browser.BrowserPublish, - ViewPresentation) +providePresentation(IContactInfo, 'view', IBrowserPublish, view) +providePresentation(IContactInfo, '', IBrowserPublish, view) + +############################################################ +# Edit presentation +from IContactEdit import IContactEdit +from ContactEditPresentation import ContactEditPresentation # Allow the update method to be accessed by people with ManageContacts -AccessControl.protectClass(Contact, ManageContacts, method='update') +protectMethods(ManageContacts, Contact, 'update') + +# Protect the contact edit presentation: +protectMethods(ManageContacts, ContactEditPresentation, 'form', 'action') +protectInstances(ManageContacts, ContactEditPresentation) # Register the edit presentation -ComponentArchitecture.providePresentation( - ContactEdit, 'edit', ZPublisher.Browser.BrowserPublish, - ContactEditPresentation) +providePresentation(IContactEdit, 'edit', BrowserPublish, + ContactEditPresentation) + +############################################################ +# Register our prosentations as tabbed views +from App import defineTab +from Interface import implements + +class IContact(IContactInfo, IContactEdit): + "Contact Content" +implements(Contact, IContact) + +defineTab(IContact, 1, "Edit", "(p)edit", ManageContacts) +defineTab(IContact, 2, "View", "(p)view", ViewPermission) + +############################################################ +# Include the security tab +from AccessControl import IRoleManageable +from AccessControl.permissions import change_permissions + +implements(Contact, IRoleManageable) +defineTab(IContact, 3, "Security", "(p)security", change_permissions) + +############################################################ +# Include the undo tab +from AccessControl.permissions import undo_changes +defineTab(IContact, 4, "Undo", "(p)undo", undo_changes) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/view.pt 1.1 => 1.2 === Contact information +
Name: First Last === Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactEdit.py === === Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step4/ContactInfo.py === From jim at zope.com Fri Oct 26 13:48:57 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - IContactEdit.py:1.1 IContactInfo.py:1.1 IPostal.py:1.1 ContactEditPresentation.py:1.2 README.txt:1.2 __init__.py:1.2 configure.py:1.2 stubpostal.py:1.2 view.pt:1.2 ContactEdit.py:NONE ContactInfo.py:NONE Postal.py:NONE Message-ID: <200110261748.f9QHmvw24377@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv23341/Chapter1/Step5 Modified Files: ContactEditPresentation.py README.txt __init__.py configure.py stubpostal.py view.pt Added Files: IContactEdit.py IContactInfo.py IPostal.py Removed Files: ContactEdit.py ContactInfo.py Postal.py Log Message: Numerous changes to simplify presentation, some of which has been started prior to the previous checkin. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/IContactEdit.py === from IContactInfo import IContactInfo from Contact import Contact from Interface import implements class IContactEdit(IContactInfo): "Provides the ability to change basic contact information." def update(first, last, email, address, pc): """Modifies contact data 'None' values are ignored. """ implements(Contact, IContactEdit) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/IContactInfo.py === from Interface import Interface, implements # Local Package imports from Contact import Contact class IContactInfo(Interface): "Provides access to basic contact information." def first(): "Get the firt name" def last(): "Get the last name" def email(): "Get the electronic mail address" def address(): "Get the postal address" def postal_code(): "Get the postal code" def name(): """Gets the contact name. The contact name is the first and last name together. """ implements(Contact, IContactInfo) === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/IPostal.py === from Interface import Interface class IPostalInfo(Interface): "Provide information for postal codes" def city(): """Return the city associated with the postal code. An empty string is returned if the city is unknown. """ def state(): """Return the state associated with the postal code. An empty string is returned if the state is unknown. """ class IPostalLookup(Interface): "Provide postal code lookup" def lookup(postal_code): """Lookup information for a postal code. An IPostalInfo is returned if the postal code is known. None is returned otherwise. """ === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEditPresentation.py 1.1 => 1.2 === -from AccessControl import ClassSecurityInfo -from permissions import ManageContacts +# Zope imports +from ZPublisher.Browser import AttributePublisher +from ComponentArchitecture.Singleton import SingletonBase, singleton from Products.PageTemplate import PresentationPageTemplateFile -from App.common import absolute_url -from AccessControl.ZopeGuards import guarded_getattr -import Acquisition -class ContactEditPresentationClass(Acquisition.Explicit): + +class ContactEditPresentationClass(AttributePublisher, Singleton): """Provide an interface for editing a contact """ - __implements__ = BrowserPublish - - # Create a SecurityInfo for this class - security = ClassSecurityInfo() - security.setDefaultAccess('deny') - - # All presentation components should provide this. - def getContext(self): - return self.aq_parent - # Input form - security.declareProtected(ManageContacts, 'form') - form = PresentationPageTemplateFile('edit.pt', globals()) + view = PresentationPageTemplateFile('edit.pt', globals()) # action method - security.declareProtected(ManageContacts, 'action') def action(self, first, last, email, address, pc): "Edit a contact" self.getContext().update(first, last, email, address, pc) - return self.form() - - def browser_traverse(self, request, name): - return guarded_getattr(self, name) - - def browser_default(self, request): - return self, ('form',) - -Globals.InitializeClass(ContactEditPresentationClass) - -# Create the singleton component -ContactEditPresentation=ContactEditPresentationClass() + return self.view() -# and a binder for it -ContactEditPresentation=Acquisition.binder(ContactEditPresentation) +ContactEditPresentation = singleton(ContactEditPresentationClass) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/README.txt 1.1 => 1.2 === postal-code lookup. - The 'Postal' module, in 'Postal.py', defines two interfaces needed + The 'IPostal' module, in 'IPostal.py', defines two interfaces needed for postal code data lookup. The first interface defines how to get postal-code data from postal code information objects. The second interface defines postal code lookup. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/__init__.py 1.1 => 1.2 === -import configure; del configure +import configure # export class and interfaces: from Contact import Contact === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/configure.py 1.1 => 1.2 === -import Interface -import Products.PageTemplate -import AccessControl, AccessControl.Permissions -import ZPublisher.Browser -from ContactInfo import ContactInfo +# Zope imports +from App import provideClass +from AccessControl import protectInterface, protectInstances, protectMethods +from AccessControl.Permissions import view as ViewPermission +from Products.PageTemplate import SimplePresentation +from ComponentArchitecture import providePresentation +from ZPublisher.Browser import IBrowserPublish + +# Local Package imports from Contact import Contact +from IContactEdit import IContactEdit from permissions import ManageContacts -# Protect contact view methods via the info interface: -protectInterface(ViewPermission, Contact, ContactInfo) -protectInstance(ViewPermission, Contact) - - +# Protect contact info interface and instances: +protectInterface(ViewPermission, Contact, IContactInfo) +protectInstances(ViewPermission, Contact) + # Register the contact factory -App.provideClass(Contact.Contact, permission=ManageContacts) +provideClass(Contact, permission=ManageContacts) # Create a simple view presentation -ViewPresentation = SimplePresentation( - 'viewContact', globals(), - permission = view, - ) +view = SimplePresentation('view.pt', globals(), permission = ViewPermission) # Register this presentation under the name "view" -ComponentArchitecture.providePresentation( - ContactInfo, 'view', BrowserPublish, - ViewPresentation) - -# Register this presentation as the default -ComponentArchitecture.providePresentation( - ContactInfo, '', BrowserPublish, - ViewPresentation) +providePresentation(IContactInfo, 'view', IBrowserPublish, view) +providePresentation(IContactInfo, '', IBrowserPublish, view) -############################################################################## +############################################################ # Edit presentation - -from ContactEdit import ContactEdit +from IContactInfo import IContactInfo from ContactEditPresentation import ContactEditPresentation # Allow the update method to be accessed by people with ManageContacts protectMethods(ManageContacts, Contact, 'update') +# Protect the contact edit presentation: +protectMethods(ManageContacts, ContactEditPresentation, 'form', 'action') +protectInstances(ManageContacts, ContactEditPresentation) + # Register the edit presentation -ComponentArchitecture.providePresentation( - ContactEit, '', ZPublisher.Browser.BrowserPublish, - ContactEditPresentation) +providePresentation(IContactEdit, 'edit', BrowserPublish, + ContactEditPresentation) + +############################################################ +# Register our prosentations as tabbed views +from App import defineTab + +defineTab(ContactEdit, 1, "Edit", "(p)edit", ManageContacts) +defineTab(ContactEdit, 2, "View", "(p)view", ViewPermission) + ############################################################################## # Postal code utility -import Postal, stubpostal +# Zope imports +from ComponentArchitecture import offerUtility +from AccessControl import publicInterface, publicInstances + +# local imports +from Postal import IPostalLookup, IPostalInfo +from stubpostal import lookup, info # Offer out postal info utility. Hopefully, someone will provide a # replacement. -ComponentArchitecture.offerUtility(Postal.PostalLookup, stubpostal.lookup()) +offerUtility(IPostalLookup, lookup()) # Allow our postal utilities to be used by anyone: -AccessControl.unprotectClass( - stubpostal.lookup, interface=Postal.PostalLookup, instance=1) -AccessControl.unprotectClass( - stubpostal.info, interface=Postal.PostalInfo, instance=1) +publicInterface(lookup, IPostalLookup) +publicInstances(lookup) +publicInterface(info, IPostalInfo) +publicInstances(info) ############################################################################## # Contact city and state lookup +from ComponentArchitecture import provideFeature from ContactCityState import ContactCityState # Provide the city state feature -ComponentArchitecture.provideFeature(ContactInfo, PostalInfo, ContactCityState) +provideFeature(IContactInfo, IPostalInfo, ContactCityState) # Provide public access to the feature: -AccessControl.unprotectClass( - ContactCityState, interface=Postal.PostalInfo, instance=1) +publicInterface(ContactCityState, IPostalInfo) +publicInstances(ContactCityState) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/stubpostal.py 1.1 => 1.2 === - +from Postal import IPostalLookup, IPostalInfo import Postal class info: - __implements__ = Postal.PostalInfo + __implements__ = IPostalInfo def __init__(self, city, state): self._city, self._state = city, state @@ -15,7 +15,7 @@ class lookup: - __implements__ = Postal.PostalLookup + __implements__ = IPostalLookup _data = { '22401': ('Fredericksburg', 'Virginia'), === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/view.pt 1.1 => 1.2 === Contact information +
Name: First Last === Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactEdit.py === === Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactInfo.py === === Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/Postal.py === From jim at zope.com Fri Oct 26 13:49:26 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 - README.txt:1.2 Message-ID: <200110261749.f9QHnQI24491@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1 In directory cvs.zope.org:/tmp/cvs-serv23341/Chapter1 Modified Files: README.txt Log Message: Numerous changes to simplify presentation, some of which has been started prior to the previous checkin. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/README.txt 1.1 => 1.2 === Step5, adding features and utilities - Step6, providing a custom creation form - Summary This chapter showed how to develop basic components with Python @@ -105,6 +103,6 @@ This goal will be addressed in later chapters. - - Provide consistency between Zope and the CMF. + - Improve consistency between Zope and the CMF. This goal will be addressed in later chapters. From jim at zope.com Fri Oct 26 13:49:26 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 - README.txt:1.2 __init__.py:1.2 Message-ID: <200110261749.f9QHnQQ24496@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1 In directory cvs.zope.org:/tmp/cvs-serv23341/Chapter1/Step1 Modified Files: README.txt __init__.py Log Message: Numerous changes to simplify presentation, some of which has been started prior to the previous checkin. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/README.txt 1.1 => 1.2 === methods for accessing contact data and for modifying the data. - This class mixes in the 'Persistent' base class. Doing so allows the - class to be stored in Zope's object database and causes changes to - the object to be saved automatically [1]. This is important because - we want class instances to be saved and managed in Zope. If - instances weren't going to ve stored in Zope, we would not need to - mix in persistence. + This class mixes in the 'Persistent' base class. Doing so allows + instances of the class to be stored in Zope's object database and + causes changes to the object to be saved automatically [1]. This is + important because we want class instances to be saved and managed in + Zope. If instances weren't going to ve stored in Zope, we would not + need to mix in persistence. To use the contact class in Zope, we have to tell Zope about it. The file, '__init__.py' is where we do this. Components are contained in Zope file-based products [2], which are simply Python packages that are sub-packages of the Zope 'Products' package. The '__init__' module is responsible for initializing the package and - for getting necessary registrations done. + for getting necessary registrations done. In this example, the '__init__' module calls the 'App.provideClass' function to register the class. The class being regstered is passed @@ -35,14 +35,18 @@ 1. A factory component is created and registered using the class. This allows other application code to create instances:: - contact=ComponentArchitecture.createObject( - ob, 'Products.Contact.Contact.Contact') + from ComponentArchitecture import createObject + + contact=createObject(ob, 'Products.Contact.Contact.Contact') A identifier for the factory is generated from the registered class module and class name. In this case, the module is 'Products.Contact.Contact' and the class is 'Contact'. A different identifier could have been provided in the 'provideClass' call. + + The first argument passed to 'createObject' affects where + 'createObject' searches for factories. 2. The factory is registered with Zope so that it can be included in Zope's interface for adding objects to containers. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step1/__init__.py 1.1 => 1.2 === -import Contact +# Zope imports +from App import provideClass +# Local Package imports +from Contact import Contact + +# Define contact-specific permission ManageContacts = 'Manage Contacts' # Register the contact class -App.provideClass(Contact.Contact, permission=ManageContacts) +provideClass(Contact, permission=ManageContacts) From jim at zope.com Fri Oct 26 13:49:26 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 - README.txt:1.2 __init__.py:1.2 Message-ID: <200110261749.f9QHnQn24501@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2 In directory cvs.zope.org:/tmp/cvs-serv23341/Chapter1/Step2 Modified Files: README.txt __init__.py Log Message: Numerous changes to simplify presentation, some of which has been started prior to the previous checkin. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/README.txt 1.1 => 1.2 === +Step2, security assertions to make contact objects usable In this step, we'll make security assertions that make the 'Contact' class we created in Step1 usable in Zope. The contact module, 'Contact.py' remains unchanged. We add a security assertion in the - '__init__' module by calling 'AccessControl.protectClass'. The class - to be protected and the desired Zope permission are passed as - arguments. We also provide keyword arguments to specify protected - methods, by name, and to specify that access to instances should be - protected by the permission. + '__init__' module by calling 'AccessControl.protectMethods'. The + desired Zope permissio, class to be protected, and the names + of the methods to be protected are passed as + arguments. We could have made security assertions in the class definition, using Zope declarative security API. Doing so would have made the === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step2/__init__.py 1.1 => 1.2 === -import Contact -import AccessControl, AccessControl.Permissions +# Zope imports +from App import provideClass +from AccessControl import protectMethods, protectInstances +from AccessControl.Permissions import view as ViewPermission -# Protect contact view methods: +# Local Package imports +from Contact import Contact + +# Protect contact view methods and instances: protectMethods( ViewPermission, Contact, 'name', 'first', 'last', 'email', 'address', 'postal_code' ) +protectInstances(ViewPermission, Contact) + +# Define contact-specific permission +ManageContacts = 'Manage Contacts' # Register the contact factory -ManageContacts = 'Manage Contacts' -App.provideClass(Contact.Contact, permission=ManageContacts) - - - - - +provideClass(Contact, permission=ManageContacts) From jim at zope.com Fri Oct 26 13:49:26 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 - IContactInfo.py:1.1 README.txt:1.2 __init__.py:1.2 ContactInfo.py:NONE Message-ID: <200110261749.f9QHnQG24506@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3 In directory cvs.zope.org:/tmp/cvs-serv23341/Chapter1/Step3 Modified Files: README.txt __init__.py Added Files: IContactInfo.py Removed Files: ContactInfo.py Log Message: Numerous changes to simplify presentation, some of which has been started prior to the previous checkin. === Added File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/IContactInfo.py === from Interface import Interface, implements # Local Package imports from Contact import Contact class IContactInfo(Interface): "Provides access to basic contact information." def first(): "Get the firt name" def last(): "Get the last name" def email(): "Get the electronic mail address" def address(): "Get the postal address" def postal_code(): "Get the postal code" def name(): """Gets the contact name. The contact name is the first and last name together. """ implements(Contact, IContactInfo) === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/README.txt 1.1 => 1.2 === have interfaces. - The file 'ContactInfo.py' defines the 'ContactInfo' interface, which + The file 'IContactInfo.py' defines the 'IContactInfo' interface, which specifies the methods used to get contact information [3]. Note that the method definitions in the interface don't include a 'self' argument. The reason for this is that the 'self' argument is not @@ -51,9 +51,9 @@ these functions, so arguments shouldn't appear in the method interfaces. - After the interfac is created, the 'assertImplementedByInstancesOf' - method is called to assert that contact instances implement the - interface. In this example, we asserted the class' contract outside + After the interface is created, the 'implements' + function is called to assert that contact instances implement the + interface. In this example, we asserted the instance contract outside the class definition. We could have included the interface assertion in the 'Contact' class definition. We'll see an example of this in a later example. There are two advanages to specifying the interface @@ -64,9 +64,9 @@ someone else or if the class is used in other application environments that don't support interfaces. - - The 'assertImplementedByInstancesOf' performs minimal conformance - checks and raises errors if the class can be determined not to - implement the interface. [4] + - The 'implements' function performs minimal conformance checks and + raises errors if the class can be determined not to implement the + interface. [4] Now that contacts are components, we can associate presentation components with them. @@ -128,7 +128,7 @@ view presentation is actually not a presentation, but an object that returns the presentation when called. - We;ve made a change to the security assertions for the class. Rather + We've made a change to the security assertions for the class. Rather than provide a specific method list, we now specify the 'ContactInfo' interface instead. If all of the methods in an interface are protected the same way, then we can use the interface === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/__init__.py 1.1 => 1.2 === -import Interface -import Products.PageTemplate -from ContactInfo import ContactInfo +# Zope imports +from App import provideClass +from AccessControl import protectInterface, protectInstances +from AccessControl.Permissions import view as ViewPermission +from Products.PageTemplate import SimplePresentation +from ComponentArchitecture import providePresentation +from ZPublisher.Browser import IBrowserPublish + +# Local Package imports from Contact import Contact -import AccessControl, AccessControl.Permissions -import ZPublisher.Browser +from IContactInfo import IContactInfo -# Protect contact view methods via the info interface: -AccessControl.protectClass( - Contact, AccessControl.Permissions.view, - inteface=ContactInfo, - instance=1, - ) +# Protect contact info interface and instances: +protectInterface(ViewPermission, Contact, IContactInfo) +protectInstances(ViewPermission, Contact) -# Register the contact factory +# Define contact-specific permission ManageContacts = 'Manage Contacts' -App.provideClass(Contact.Contact, permission=ManageContacts) + +# Register the contact factory +provideClass(Contact, permission=ManageContacts) # Create a simple view presentation -ViewPresentation = Products.PageTemplate.SimplePresentation( - 'view.pt', - globals(), - permission = AccessControl.Permissions.view, - ) +view = SimplePresentation('view.pt', globals(), permission = ViewPermission) # Register this presentation under the name "view" -ComponentArchitecture.providePresentation( - ContactInfo, - 'view', - ZPublisher.Browser.BrowserPublish, - ViewPresentation) - -# Register this presentation as the default -ComponentArchitecture.providePresentation( - ContactInfo, - '', - ZPublisher.Browser.BrowserPublish, - ViewPresentation) - -#JJJ Maybe default=1 +providePresentation(IContactInfo, 'view', IBrowserPublish, view) +providePresentation(IContactInfo, '', IBrowserPublish, view) === Removed File Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step3/ContactInfo.py === From jim at zope.com Fri Oct 26 13:49:52 2001 From: jim at zope.com (Jim Fulton) Date: Sun Aug 10 16:40:35 2008 Subject: [Zope-book] CVS: Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 - ContactCityState.py:1.2 Message-ID: <200110261749.f9QHnqq24557@cvs.baymountain.com> Update of /cvs-repository/Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5 In directory cvs.zope.org:/tmp/cvs-serv24520/Chapter1/Step5 Modified Files: ContactCityState.py Log Message: Simplified and updates to use 'I' prefix in interfaces. === Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter1/Step5/ContactCityState.py 1.1 => 1.2 === -import ComponentArchitecture +from Postal import IPostalLookup, IPostalInfo +from ComponentArchitecture import getUtility class ContactCityState: "Provide access to city and state information for a contact" - __implements__=Postal.PostalInfo + __implements__=IPostalInfo def __init__(self, contact): self._contact=contact - lookup=ComponentArchitecture.getUtility(contact, Postal.PostalLookup) + lookup = getUtility(contact, IPostalLookup) info = lookup.lookup(contact.postal_code()) if info is None: self._city, self._state = '', ''