[Checkins] SVN: z3c.wizard/trunk/ implemented new z3c.form based wizard

Roger Ineichen roger at projekt01.ch
Mon Aug 25 11:37:05 EDT 2008


Log message for revision 90223:
  implemented new z3c.form based wizard

Changed:
  A   z3c.wizard/trunk/CHANGES.txt
  A   z3c.wizard/trunk/README.txt
  A   z3c.wizard/trunk/bootstrap.py
  A   z3c.wizard/trunk/buildout.cfg
  A   z3c.wizard/trunk/setup.py
  A   z3c.wizard/trunk/src/
  A   z3c.wizard/trunk/src/z3c/
  A   z3c.wizard/trunk/src/z3c/__init__.py
  A   z3c.wizard/trunk/src/z3c/wizard/
  A   z3c.wizard/trunk/src/z3c/wizard/README.txt
  A   z3c.wizard/trunk/src/z3c/wizard/__init__.py
  A   z3c.wizard/trunk/src/z3c/wizard/browser.zcml
  A   z3c.wizard/trunk/src/z3c/wizard/button.py
  A   z3c.wizard/trunk/src/z3c/wizard/configure.zcml
  A   z3c.wizard/trunk/src/z3c/wizard/interfaces.py
  A   z3c.wizard/trunk/src/z3c/wizard/meta.zcml
  A   z3c.wizard/trunk/src/z3c/wizard/step.pt
  A   z3c.wizard/trunk/src/z3c/wizard/step.py
  A   z3c.wizard/trunk/src/z3c/wizard/testing.py
  A   z3c.wizard/trunk/src/z3c/wizard/tests.py
  A   z3c.wizard/trunk/src/z3c/wizard/wizard.pt
  A   z3c.wizard/trunk/src/z3c/wizard/wizard.py
  A   z3c.wizard/trunk/src/z3c/wizard/zcml.py
  A   z3c.wizard/trunk/src/z3c/wizard/zcml.txt

-=-
Added: z3c.wizard/trunk/CHANGES.txt
===================================================================
--- z3c.wizard/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.wizard/trunk/CHANGES.txt	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,9 @@
+=======
+CHANGES
+=======
+
+
+Version 0.5.0dev (unreleased)
+-----------------------------
+
+- Initial Release


Property changes on: z3c.wizard/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/README.txt
===================================================================
--- z3c.wizard/trunk/README.txt	                        (rev 0)
+++ z3c.wizard/trunk/README.txt	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1 @@
+This package provides a form wizard concept based on z3c.form for Zope3.
\ No newline at end of file


Property changes on: z3c.wizard/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/bootstrap.py
===================================================================
--- z3c.wizard/trunk/bootstrap.py	                        (rev 0)
+++ z3c.wizard/trunk/bootstrap.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 75940 2007-05-24 14:45:00Z srichter $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: z3c.wizard/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/buildout.cfg
===================================================================
--- z3c.wizard/trunk/buildout.cfg	                        (rev 0)
+++ z3c.wizard/trunk/buildout.cfg	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,26 @@
+[buildout]
+develop = .
+parts = test checker coverage-test coverage-report
+
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.wizard [test]
+
+
+[checker]
+recipe = lovely.recipe:importchecker
+path = src/z3c/wizard
+
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3c.wizard [test]
+defaults = ['--coverage', '../../coverage']
+
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')

Added: z3c.wizard/trunk/setup.py
===================================================================
--- z3c.wizard/trunk/setup.py	                        (rev 0)
+++ z3c.wizard/trunk/setup.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,82 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup
+
+$Id:$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+    name='z3c.wizard',
+    version='0.5.0dev',
+    author = "Roger Ineichen and the Zope Community",
+    author_email = "zope3-dev at zope.org",
+    description = "Wizard based on z3c.form for for Zope3",
+    long_description=(
+        read('README.txt')
+        + '\n\n' +
+        read('CHANGES.txt')
+        ),
+    license = "ZPL 2.1",
+    keywords = "zope3 z3c form wizard",
+    classifiers = [
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+    url = 'http://cheeseshop.python.org/pypi/z3c.wizard',
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c'],
+    extras_require = dict(
+        test = [
+            'z3c.macro',
+            'z3c.testing',
+            'zope.app.pagetemplate',
+            'zope.app.testing',
+            'zope.publisher',
+            'zope.testing',
+            ],
+        ),
+    install_requires = [
+        'setuptools',
+        'z3c.form',
+        'z3c.formui',
+        'z3c.i18n',
+        'z3c.pagelet',
+        'zope.app.component',
+        'zope.app.publisher',
+        'zope.component',
+        'zope.configuration',
+        'zope.contentprovider',
+        'zope.event',
+        'zope.interface',
+        'zope.lifecycleevent',
+        'zope.publisher',
+        'zope.schema',
+        'zope.security',
+        'zope.traversing',
+        ],
+    zip_safe = False,
+)


Property changes on: z3c.wizard/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/__init__.py
===================================================================
--- z3c.wizard/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/__init__.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,7 @@
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace('z3c')
+except ImportError:
+    pass
+


Property changes on: z3c.wizard/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/README.txt
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/README.txt	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/README.txt	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,373 @@
+======
+Wizard
+======
+
+The goal of this package is to offer a form wizard. This implementation doesn't
+use a session. It just offers the wizard logic, the data which the wizard will
+changes or add is not a part of this implementation. If you liek to implement
+some add wizard logic you probably need to use a session anf collect the values
+in the different wizard steps and create and add an object in the wizard
+doComplete or doFinish or the step doComplete method.
+
+All steps are available by it's own url. This allows us to cache each step if
+needed. Each step url is only available if we are allowed to access a step. If
+a step is accessible depends on the conditions of each step.
+
+Since steps are adapters, we can register steps for already existing wizards
+or we can also ovreride existing steps by register a UnavailableStep step which 
+always will return False for the ``available`` argument.
+
+If the wizard is completed we get redirected to the confirmation page. If we
+access a completed wizard again, we will get redirected to the confirmation 
+page again.
+
+Now let's show how this works and setup our tests.
+
+
+Form support
+------------
+
+We need to setup the form defaults first:
+
+  >>> from z3c.form.testing import setupFormDefaults
+  >>> setupFormDefaults()
+
+And load the formui confguration, which will make sure that all macros get 
+registered correctly.
+
+  >>> from zope.configuration import xmlconfig
+  >>> import zope.component
+  >>> import zope.viewlet
+  >>> import zope.app.component
+  >>> import zope.app.publisher.browser
+  >>> import z3c.macro
+  >>> import z3c.template
+  >>> import z3c.formui
+  >>> xmlconfig.XMLConfig('meta.zcml', zope.component)()
+  >>> xmlconfig.XMLConfig('meta.zcml', zope.viewlet)()
+  >>> xmlconfig.XMLConfig('meta.zcml', zope.app.component)()
+  >>> xmlconfig.XMLConfig('meta.zcml', zope.app.publisher.browser)()
+  >>> xmlconfig.XMLConfig('meta.zcml', z3c.macro)()
+  >>> xmlconfig.XMLConfig('meta.zcml', z3c.template)()
+  >>> xmlconfig.XMLConfig('configure.zcml', z3c.formui)()
+
+And load the z3c.wizard macro configuration:
+
+  >>> import z3c.wizard
+  >>> xmlconfig.XMLConfig('configure.zcml', z3c.wizard)()
+
+
+Sample data setup
+-----------------
+
+Let's define a sample content class:
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> from zope.schema.fieldproperty import FieldProperty
+  >>> class IPerson(zope.interface.Interface):
+  ...     """Person interface."""
+  ...  
+  ...     firstName = zope.schema.TextLine(title=u'First Name')
+  ...     lastName = zope.schema.TextLine(title=u'Last Name')
+  ...     street = zope.schema.TextLine(title=u'Street')
+  ...     city = zope.schema.TextLine(title=u'City')
+
+  >>> class Person(object):
+  ...     """Person content."""
+  ...     zope.interface.implements(IPerson)
+  ... 
+  ...     firstName = FieldProperty(IPerson['firstName'])
+  ...     lastName = FieldProperty(IPerson['lastName'])
+  ...     street = FieldProperty(IPerson['street'])
+  ...     city = FieldProperty(IPerson['city'])
+
+Setup a person for our wizard:
+
+  >>> person = Person()
+  >>> root['person'] = person
+  >>> person.__parent__ = root
+  >>> person.__name__ = u'person'
+
+
+Step
+----
+
+Let's define some steps. First use a step which knows how to store the name
+of a person.
+
+  >>> from z3c.form import form
+  >>> from z3c.form import field
+  >>> from z3c.wizard import step
+
+  >>> class PersonStep(step.EditStep):
+  ...     label = u'Person'
+  ...     fields = field.Fields(IPerson).select('firstName', 'lastName')
+
+And another step for collect some address data:
+
+  >>> class AddressStep(step.EditStep):
+  ...     label = u'Address'
+  ...     fields = field.Fields(IPerson).select('street', 'city')
+
+
+Wizard
+------
+
+Now we can define our ``Wizard`` including our steps. Steps are named
+adapters. Let's use the global method addStep for doing the step setup:
+
+  >>> from z3c.wizard import wizard
+  >>> class IPersonWizard(z3c.wizard.interfaces.IWizard):
+  ...     """Person wizard marker."""
+
+  >>> class PersonWizard(wizard.Wizard):
+  ... 
+  ...     zope.interface.implements(IPersonWizard)
+  ...
+  ...     label = u'Person Wizard'
+  ... 
+  ...     def setUpSteps(self):
+  ...         return [
+  ...             step.addStep(self, 'person', weight=1),
+  ...             step.addStep(self, 'address', weight=2),
+  ...             ]
+
+As next, we need to register our steps as named IStep adapters. This can be 
+done by the z3c:wizardStep directive. Let's define our adapters with the 
+provideAdapter method for now:
+
+  >>> import zope.interface
+  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+  >>> from zope.publisher.interfaces.browser import IBrowserRequest
+  >>> import z3c.wizard.interfaces
+  >>> zope.component.provideAdapter(
+  ...     PersonStep, (None, IBrowserRequest, None),
+  ...     z3c.wizard.interfaces.IStep, name='person')
+
+  >>> zope.component.provideAdapter(
+  ...     AddressStep, (None, IBrowserRequest, None),
+  ...     z3c.wizard.interfaces.IStep, name='address')
+
+We need to support the div form layer for our request. This is needed for the
+form part we usein our steps. Because our steps are forms:
+
+  >>> from z3c.formui.interfaces import IDivFormLayer
+  >>> from zope.interface import alsoProvides
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> alsoProvides(request, IDivFormLayer)
+
+Now we can use our wizard. Our wizard will allways force to traverse to the 
+current active step. This means the wizard provides a browserDefault which 
+returns the default step instead of render the wizard as view. This allows us 
+to use the step as an adapter discriminator for viewlets and other adapters 
+like the menu implementation uses. The wizard acts like a dispatcher to the 
+right step and not as a view itself.
+
+  >>> personWizard = PersonWizard(person, request)
+  >>> personWizard.__parent__ = person
+  >>> personWizard.__name__ = u'wizard'
+
+Now get the default view (step) arguments from the wizard:
+
+  >>> obj, names = personWizard.browserDefault(request)
+  >>> obj
+  <PersonWizard u'wizard'>
+  
+  >>> names
+  ('person',)
+
+Now traverse to the step, update and render them:
+
+  >>> personStep = obj.publishTraverse(request, names[0])
+  >>> personStep.update()
+  >>> print personStep.render()
+  <div class="wizard">
+      <div class="header">Person Wizard</div>
+      <div class="wizardMenu">
+        <span class="selected">
+            <span>Person</span>
+        </span>
+        <span>
+            <a href="http://127.0.0.1/person/wizard/address">Address</a>
+        </span>
+      </div>
+    <form action="http://127.0.0.1" method="post"
+          enctype="multipart/form-data" class="edit-form"
+          id="form">
+        <div class="viewspace">
+            <div class="label">Person</div>
+            <div class="required-info">
+               <span class="required">*</span>
+               &ndash; required
+            </div>
+          <div class="step">
+            <div id="form-widgets-firstName-row" class="row">
+                <div class="label">
+                  <label for="form-widgets-firstName">
+                    <span>First Name</span>
+                    <span class="required">*</span>
+                  </label>
+                </div>
+                <div class="widget"><input type="text" id="form-widgets-firstName"
+                         name="form.widgets.firstName"
+                         class="text-widget required textline-field" value="" />
+              </div>
+            </div>
+            <div id="form-widgets-lastName-row" class="row">
+                <div class="label">
+                  <label for="form-widgets-lastName">
+                    <span>Last Name</span>
+                    <span class="required">*</span>
+                  </label>
+                </div>
+                <div class="widget"><input type="text" id="form-widgets-lastName"
+                         name="form.widgets.lastName"
+                         class="text-widget required textline-field" value="" />
+              </div>
+            </div>
+          </div>
+            <div>
+              <div class="buttons">
+                <span class="back">
+                </span>
+                <span class="step">
+                  <input type="submit" id="form-buttons-apply"
+                         name="form.buttons.apply"
+                         class="submit-widget button-field" value="Apply" />
+                </span>
+                <span class="forward">
+                  <input type="submit" id="form-buttons-next"
+                         name="form.buttons.next"
+                         class="submit-widget button-field" value="Next" />
+                </span>
+              </div>
+            </div>
+        </div>
+    </form>
+  </div>
+
+
+We can't go to the next step if we not complete the first step:
+
+  >>> request = TestRequest(form={'form.buttons.next': 'Next'})
+  >>> alsoProvides(request, IDivFormLayer)
+  >>> personWizard = PersonWizard(person, request)
+  >>> personWizard.__parent__ = person
+  >>> personWizard.__name__ = u'wizard'
+  >>> personStep = personWizard.publishTraverse(request, names[0])
+  >>> personStep.update()
+  >>> print personStep.render()
+  <div class="wizard">
+  ...
+    <div class="summary">There were some errors.</div>
+  ...
+    <div class="error">Required input is missing.</div>
+  ...
+    <div class="error">Required input is missing.</div>
+  ...
+
+
+We can complete this step if we fill in the required values and click next:
+
+  >>> request = TestRequest(form={'form.widgets.firstName': u'Roger',
+  ...                             'form.widgets.lastName': u'Ineichen',
+  ...                             'form.buttons.next': 'Next'})
+  >>> alsoProvides(request, IDivFormLayer)
+  >>> personWizard = PersonWizard(person, request)
+  >>> personWizard.__parent__ = person
+  >>> personWizard.__name__ = u'wizard'
+  >>> personStep = personWizard.publishTraverse(request, names[0])
+  >>> personStep.update()
+  >>> print personStep.render()
+
+As you can see the step get processed and the wizard will redirect to the next
+step using the response redirect concept:
+
+  >>> personWizard.nextURL
+  'http://127.0.0.1/person/wizard/address'
+
+Let's access the next step using the traverser. This will setup the next step
+and tehm.
+
+  >>> request = TestRequest()
+  >>> alsoProvides(request, IDivFormLayer)
+  >>> personWizard = PersonWizard(person, request)
+  >>> personWizard.__parent__ = person
+  >>> personWizard.__name__ = u'wizard'
+
+As you can see we see our next step is the address step:
+
+  >>> addressStep = personWizard.publishTraverse(request, 'address')
+  >>> addressStep
+  <AddressStep 'address'>
+
+Update and render them:
+
+  >>> addressStep.update()
+  >>> print addressStep.render()
+  <div class="wizard">
+      <div class="header">Person Wizard</div>
+      <div class="wizardMenu">
+        <span>
+            <a href="http://127.0.0.1/person/wizard/person">Person</a>
+        </span>
+        <span class="selected">
+            <span>Address</span>
+        </span>
+      </div>
+    <form action="http://127.0.0.1" method="post"
+          enctype="multipart/form-data" class="edit-form"
+          id="form">
+        <div class="viewspace">
+            <div class="label">Address</div>
+            <div class="required-info">
+               <span class="required">*</span>
+               &ndash; required
+            </div>
+          <div class="step">
+            <div id="form-widgets-street-row" class="row">
+                <div class="label">
+                  <label for="form-widgets-street">
+                    <span>Street</span>
+                    <span class="required">*</span>
+                  </label>
+                </div>
+                <div class="widget"><input type="text" id="form-widgets-street"
+                         name="form.widgets.street"
+                         class="text-widget required textline-field" value="" />
+              </div>
+            </div>
+            <div id="form-widgets-city-row" class="row">
+                <div class="label">
+                  <label for="form-widgets-city">
+                    <span>City</span>
+                    <span class="required">*</span>
+                  </label>
+                </div>
+                <div class="widget"><input type="text" id="form-widgets-city"
+                         name="form.widgets.city"
+                         class="text-widget required textline-field" value="" />
+              </div>
+            </div>
+          </div>
+            <div>
+              <div class="buttons">
+                <span class="back">
+                  <input type="submit" id="form-buttons-back"
+                         name="form.buttons.back"
+                         class="submit-widget button-field" value="Back" />
+                </span>
+                <span class="step">
+                  <input type="submit" id="form-buttons-apply"
+                         name="form.buttons.apply"
+                         class="submit-widget button-field" value="Apply" />
+                </span>
+                <span class="forward">
+                </span>
+              </div>
+            </div>
+        </div>
+    </form>
+  </div>


Property changes on: z3c.wizard/trunk/src/z3c/wizard/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/__init__.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/__init__.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/__init__.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1 @@
+# make a package


Property changes on: z3c.wizard/trunk/src/z3c/wizard/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/browser.zcml
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/browser.zcml	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/browser.zcml	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,72 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c">
+
+  <z3c:macro
+      name="wizard"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-header"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-menu"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-form"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-subform"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-label"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-required-info"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-widgets"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-errors"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:macro
+      name="wizard-buttons"
+      template="wizard.pt"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+  <z3c:template
+      template="step.pt"
+      for=".interfaces.IStep"
+      layer="z3c.form.interfaces.IFormLayer"
+      />
+
+</configure>


Property changes on: z3c.wizard/trunk/src/z3c/wizard/browser.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/button.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/button.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/button.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+from z3c.form import button
+from z3c.wizard import interfaces
+
+
+class WizardButtonActions(button.ButtonActions):
+    """Wizard Button Actions."""
+
+    @property
+    def backActions(self):
+        return [action for action in self.values()
+                if interfaces.IBackButton.providedBy(action.field)]
+
+    @property
+    def forwardActions(self):
+        return [action for action in self.values()
+                if interfaces.INextButton.providedBy(action.field)]


Property changes on: z3c.wizard/trunk/src/z3c/wizard/button.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/configure.zcml
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/configure.zcml	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/configure.zcml	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,8 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c">
+
+  <include file="browser.zcml" />
+
+</configure>


Property changes on: z3c.wizard/trunk/src/z3c/wizard/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/interfaces.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/interfaces.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/interfaces.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,285 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+import zope.location.interfaces
+from zope.contentprovider.interfaces import IContentProvider
+
+from z3c.i18n import MessageFactory as _
+from z3c.form import interfaces
+from z3c.form import button
+from z3c.pagelet.interfaces import IPagelet
+
+
+class IBackButton(interfaces.IButton):
+    """A button that redirects to the previous step."""
+
+
+class INextButton(interfaces.IButton):
+    """A button that redirects to the next step."""
+
+
+class IWizardButtons(zope.interface.Interface):
+    """Wizard button interfaces."""
+
+    back = button.Button(
+        title=_('Back'),
+        condition=lambda form: form.showBackButton)
+    zope.interface.alsoProvides(back, (IBackButton,))
+
+    next = button.Button(
+        title=_('Next'),
+        condition=lambda form: form.showNextButton)
+    zope.interface.alsoProvides(next, (INextButton,))
+
+    complete = button.Button(
+        title=_('Complete'),
+        condition=lambda form: form.showCompleteButton)
+    zope.interface.alsoProvides(complete, (INextButton,))
+
+
+class IStep(interfaces.IForm, IPagelet):
+    """An interface marking a step sub-form."""
+
+    available = zope.schema.Bool(
+        title=u'Available',
+        description=u'Marker for available step',
+        default=True,
+        required=False)
+
+    visible = zope.schema.Bool(
+        title=u'Show step in wizard step menu',
+        description=u'Show step in wizard step menu',
+        default=True,
+        required=False)
+
+    showRequired = zope.schema.Bool(
+        title=u'Show required label',
+        description=u'Show required label',
+        default=True,
+        required=False)
+
+    weight = zope.schema.Int(
+        title=u'Step weight in wizard',
+        description=u'Step weight in wizard',
+        default=0,
+        required=False)
+
+    completed = zope.schema.Bool(
+        title=u'Completed',
+        description=u'Marker for completed step',
+        default=False,
+        required=False)
+
+    handleApplyOnBack = zope.schema.Bool(
+        title=u'Handle apply changes on back',
+        description=u'Handle apply changes on back will force validation',
+        default=False,
+        required=False)
+
+    handleApplyOnNext = zope.schema.Bool(
+        title=u'Handle apply changes on next',
+        description=u'Handle apply changes on next will force validation',
+        default=True,
+        required=False)
+
+    handleApplyOnComplete = zope.schema.Bool(
+        title=u'Handle apply changes on complete',
+        description=u'Handle apply changes on complete will force validation',
+        default=True,
+        required=False)
+
+    showSaveButton = zope.schema.Bool(
+        title=u'Show save button',
+        description=u'Show save button',
+        default=True,
+        required=False)
+
+    showBackButton = zope.schema.Bool(
+        title=u'Show back button',
+        description=u'Back button condition',
+        default=True,
+        required=False)
+
+    showNextButton = zope.schema.Bool(
+        title=u'Show next button',
+        description=u'Next button condition',
+        default=True,
+        required=False)
+
+    showCompleteButton = zope.schema.Bool(
+        title=u'Show complete button',
+        description=u'Complete button condition',
+        default=True,
+        required=False)
+
+    def goToStep(stepName):
+        """Redirect to step by name."""
+
+    def goToNext():
+        """Redirect to next step."""
+
+    def goToBack():
+        """Redirect to back step."""
+
+    def applyChanges(data):
+        """Generic form save method taken from z3c.form.form.EditForm."""
+
+    def doHandleApply(action):
+        """Extract data and calls applyChanges."""
+
+    def doBack(action):
+        """Process back action and return True on sucess."""
+
+    def doNext(action):
+        """Process next action and return True on sucess."""
+
+    def doComplete(action):
+        """Process complete action and return True on sucess."""
+
+    def update():
+        """Update the step."""
+
+    def render():
+        """Render the step content w/o wrapped layout."""
+
+    def __call__():
+        """Compute a response body including the layout"""
+
+
+class IWizard(zope.location.interfaces.ILocation):
+    """An interface marking the controlling wizard form."""
+
+    firstStepAsDefault = zope.schema.Bool(
+        title=u'Show first step as default',
+        description=u'Show first step or first not completed step as default',
+        default=True,
+        required=True)
+
+    adjustStep = zope.schema.Bool(
+        title=u'Adjust step',
+        description=u'Force fallback (redirect) to last incomplete step',
+        default=True,
+        required=False)
+
+    confirmationPageName = zope.schema.ASCIILine(
+        title=u'Confirmation page name',
+        description=u'The confirmation page name shown after completed',
+        default=None,
+        required=False)
+
+    cssActive = zope.schema.ASCIILine(
+        title=u'Active step menu CSS class',
+        description=u'The active step menu CSS class',
+        default='selected',
+        required=False)
+
+    cssInActive = zope.schema.ASCIILine(
+        title=u'In-Active step menu item CSS class',
+        description=u'The in-active step menu item CSS class',
+        default=None,
+        required=False)
+
+    stepInterface = zope.interface.Attribute('Step lookup interface.')
+
+    steps = zope.interface.Attribute(
+        """List of one or more IStep (can be lazy).""")
+
+    stepMenu = zope.interface.Attribute("""Step menu info.""")
+
+    step = zope.schema.Object(
+        title=u'Current step',
+        description=u'Current step',
+        schema=IStep)
+
+    completed = zope.schema.Bool(
+        title=u'Completed',
+        description=u'Marker for completed step',
+        default=False,
+        required=False)
+
+    isFirstStep = zope.schema.Bool(
+        title=u'Is first step',
+        description=u'Is first step',
+        default=False,
+        required=False)
+
+    isLastStep = zope.schema.Bool(
+        title=u'Is last step',
+        description=u'Is last step',
+        default=False,
+        required=False)
+
+    previousStepName = zope.schema.TextLine(
+        title=u'Previous step name',
+        description=u'Previous step name',
+        default=None,
+        required=False)
+
+    nextStepName = zope.schema.TextLine(
+        title=u'NExt step name',
+        description=u'Next step name',
+        default=None,
+        required=False)
+
+    def doAdjustStep():
+        """Ensure that we can't traverse to more then the first not completed 
+        step.
+        """
+
+    def getDefaultStep():
+        """Can return the first or first not completed step as default."""
+
+    def doAdjustStep():
+        """Make sure all previous steps got completed. If not, redirect to the 
+        last uncomplete step.
+        """
+
+    def updateActions():
+        """Update wizard actions."""
+
+    def publishTraverse(request, name):
+        """Traverse to step by it's name."""
+
+    def browserDefault(request):
+        """The default step is our browserDefault traversal setp."""
+
+    def goToStep(stepName):
+        """Redirect to the step by name."""
+
+    def goToBack():
+        """Redirect to next step if previous get sucessfuly processed."""
+
+    def goToNext():
+        """Redirect to next step if previous get sucessfuly processed."""
+
+    def doBack(action):
+        """Process something if back action get exceuted."""
+
+    def doNext(action):
+        """Process something if next action get exceuted."""
+
+    def doComplete(action):
+        """Process something if complete action get exceuted."""
+
+    def doFinish():
+        """Process something on complete wizard."""
+
+    def update():
+        """Adjust step and update actions."""


Property changes on: z3c.wizard/trunk/src/z3c/wizard/interfaces.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/meta.zcml
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/meta.zcml	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/meta.zcml	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,22 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/z3c">
+
+    <meta:directive
+        name="wizard"
+        schema=".zcml.IWizardDirective"
+        handler=".zcml.wizardDirective"
+        />
+
+    <meta:directive
+        name="wizardStep"
+        schema=".zcml.IWizardStepDirective"
+        handler=".zcml.wizardStepDirective"
+        />
+
+  </meta:directives>
+
+</configure>
+


Property changes on: z3c.wizard/trunk/src/z3c/wizard/meta.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/step.pt
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/step.pt	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/step.pt	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1 @@
+<div metal:use-macro="macro:wizard"></div>


Property changes on: z3c.wizard/trunk/src/z3c/wizard/step.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/step.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/step.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/step.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,221 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.component
+import zope.interface
+import zope.event
+import zope.lifecycleevent
+
+from z3c.i18n import MessageFactory as _
+from z3c.form.interfaces import IDataManager
+from z3c.form.interfaces import IActionHandler
+from z3c.form import button
+from z3c.form import field
+from z3c.form import subform
+from z3c.formui import form
+from z3c.formui import layout
+from z3c.wizard import interfaces
+
+
+def addStep(self, name, label=None, weight=None, available=None, **kws):
+    step = zope.component.getMultiAdapter((self.context, self.request, self),
+        interfaces.IStep, name=name)
+    step.__name__ = name
+    if label is not None:
+        step.label = label
+    if weight is not None:
+        step.weight = weight
+    if available is not None:
+        step.available = available
+    for name, value in kws.items():
+        setattr(step, name, value)
+    return step
+
+
+class Step(form.Form):
+    """Wizard base step implementation.
+    
+    The step offers hooks for action handlers for all wizard actions. The step
+    can also provide own actions and handlers. This actions get rendered as step
+    actions. Between the bak an next wizard actions.
+    
+    A step can access the context or any object which you return by the 
+    getContent method. See z3c.form for more info about that. If you need a 
+    complexer wizard setup, you probably have to use a session and store 
+    temporary collected values in the session and store it if the wizard will
+    call doComplete on the last step or in the wizard itself. Such a session
+    is not a part of this implementation. This wizard implementation works 
+    on any context like other z3c.form IForm implementations. For more infos
+    see z3c.form which this wizard is based on.
+    """
+
+    zope.interface.implements(interfaces.IStep)
+
+    name = None
+    label = None
+    available = True
+    visible = True
+    weight = 0
+    showRequired = True
+
+    handleApplyOnBack = False
+    handleApplyOnNext = True
+    handleApplyOnComplete = True
+
+    # button condition
+    showSaveButton = True
+
+    formErrorsMessage = _('There were some errors.')
+    successMessage = _('Data successfully updated.')
+    noChangesMessage = _('No changes were applied.')
+
+    def __init__(self, context, request, wizard):
+        self.context = context
+        self.request = request
+        self.wizard = self.__parent__ = wizard
+
+    @property
+    def showBackButton(self):
+        """Back button condition."""
+        return not self.wizard.isFirstStep
+
+    @property
+    def showNextButton(self):
+        """Next button condition."""
+        return not self.wizard.isLastStep
+
+    @property
+    def showCompleteButton(self):
+        """Complete button condition."""
+        return self.wizard.isLastStep and self.wizard.completed
+
+    @property
+    def nextURL(self):
+        """Next step url known by wizard."""
+        return self.wizard.nextURL
+
+    @property
+    def completed(self):
+        """Simple default check for find out if a step is complete.
+        
+        This method will ensure that we store at least all required form values
+        and that this values are valid. You can implement any other or
+        additional condition in your custom step implementation.
+        """
+        content = self.getContent()
+        for field in self.fields.values():
+            if not field.field.required:
+                continue
+            dm = zope.component.getMultiAdapter(
+                (content, field.field), IDataManager)
+            if dm.get() is field.field.missing_value:
+                return False
+        return True
+
+    def goToStep(self, stepName):
+        self.wizard.goToStep(stepName)
+
+    def goToNext(self):
+        self.wizard.goToNext()
+
+    def goToBack(self):
+        self.wizard.goToBack()
+
+    def applyChanges(self, data):
+        """Generic form save method taken from z3c.form.form.EditForm."""
+        content = self.getContent()
+        changes = form.applyChanges(self, content, data)
+        # ``changes`` is a dictionary; if empty, there were no changes
+        if changes:
+            # Construct change-descriptions for the object-modified event
+            descriptions = []
+            for interface, names in changes.items():
+                descriptions.append(
+                    zope.lifecycleevent.Attributes(interface, *names))
+            # Send out a detailed object-modified event
+            zope.event.notify(
+                zope.lifecycleevent.ObjectModifiedEvent(content, *descriptions))
+        return changes
+
+    def doHandleApply(self, action):
+        """Extract data and calls applyChanges."""
+        data, errors = self.extractData()
+        if errors:
+            self.status = self.formErrorsMessage
+            return False
+        changes = self.applyChanges(data)
+        if changes:
+            self.status = self.successMessage
+        else:
+            self.status = self.noChangesMessage
+        return True
+
+    def doBack(self, action):
+        """Process back action and return True on sucess."""
+        if self.handleApplyOnBack:
+            return self.doHandleApply(action)
+        return True
+
+    def doNext(self, action):
+        """Process next action and return True on sucess."""
+        if self.handleApplyOnNext:
+            return self.doHandleApply(action)
+        return True
+
+    def doComplete(self, action):
+        """Process complete action and return True on sucess."""
+        if self.handleApplyOnComplete:
+            return self.doHandleApply(action)
+        return True
+
+    def update(self):
+        # setup wizard actions
+        self.wizard.update()
+        if self.nextURL is not None:
+            # abort and do redirect in render method
+            return
+        # update and excecute step actions
+        super(Step, self).update()
+        # excecute wizard actions
+        self.wizard.actions.execute()
+
+    def render(self):
+        # render content template
+        if self.nextURL is not None:
+            self.request.response.redirect(self.nextURL)
+            return u''
+        return super(Step, self).render()
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.__name__)
+
+
+class EditStep(Step):
+    """Step with default save button action."""
+
+    @button.buttonAndHandler(_('Apply'), name='apply')
+    def handleApply(self, action):
+        self.doHandleApply(action)
+
+
+class UnavailableStep(Step):
+    """A step that is not available.
+
+    This class is particularly useful for turning off an adapter steps.
+    """
+    available = False


Property changes on: z3c.wizard/trunk/src/z3c/wizard/step.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/testing.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/testing.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/testing.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+from zope.app.pagetemplate import metaconfigure
+from zope.app.testing import setup
+
+import z3c.macro.tales
+
+
+###############################################################################
+#
+# testing setup
+#
+###############################################################################
+
+def setUp(test):
+    test.globs = {'root': setup.placefulSetUp(True)}
+
+    metaconfigure.registerType('macro', z3c.macro.tales.MacroExpression)
+
+
+def tearDown(test):
+    setup.placefulTearDown()


Property changes on: z3c.wizard/trunk/src/z3c/wizard/testing.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/tests.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/tests.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/tests.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,116 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+import zope.interface
+from zope.testing import doctest
+from zope.publisher.browser import TestRequest
+
+import z3c.testing
+from z3c.wizard import interfaces
+from z3c.wizard import wizard
+from z3c.wizard import step
+from z3c.wizard import testing
+
+
+class IContentStub(zope.interface.Interface):
+    """Content stub marker."""
+
+
+class ContentStub(object):
+    """Content stub."""
+
+    zope.interface.implements(IContentStub)
+
+    def values(self):
+        pass
+
+
+class StepTestClass(step.Step):
+    """Simple test step."""
+
+
+class WizardTestClass(wizard.Wizard):
+    """Wizard Test class providing a step."""
+
+    baseURL = '#'
+
+    def __init__(self, context, request):
+        super(WizardTestClass, self).__init__(context, request)
+        self.step = StepTestClass(context, request, self)
+        self.step.__name__ = 'first'
+        self.step.__parent__ = self
+
+
+def setStubs():
+    zope.component.provideAdapter(StepTestClass,
+        (IContentStub, None, None), provides=interfaces.IStep, name='first')
+    zope.component.provideAdapter(StepTestClass,
+        (IContentStub, None, None), provides=interfaces.IStep, name='last')
+
+
+class TestStep(z3c.testing.InterfaceBaseTest):
+
+    def setUp(self):
+        setStubs()
+
+    def getTestInterface(self):
+        return interfaces.IStep
+
+    def getTestClass(self):
+        return step.Step
+
+    def getTestPos(self):
+        content = ContentStub()
+        request = TestRequest()
+        wiz = wizard.Wizard(content, request)
+        return (content, request, wiz)
+
+
+class TestWizard(z3c.testing.InterfaceBaseTest):
+
+    def setUp(self):
+        setStubs()
+
+    def getTestInterface(self):
+        return interfaces.IWizard
+
+    def getTestClass(self):
+        return WizardTestClass
+
+    def getTestPos(self):
+        return (ContentStub(), TestRequest())
+
+
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+            ),
+        doctest.DocFileSuite('zcml.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,),
+        unittest.makeSuite(TestStep),
+        unittest.makeSuite(TestWizard),
+        ))
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: z3c.wizard/trunk/src/z3c/wizard/tests.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/wizard.pt
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/wizard.pt	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/wizard.pt	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,101 @@
+<div class="wizard"
+     metal:define-macro="wizard">
+  <metal:block define-slot="header">
+    <div class="header" metal:define-macro="wizard-header"
+         tal:content="view/wizard/label">label</div>
+  </metal:block>
+  <metal:block define-slot="menu">
+    <div class="wizardMenu" metal:define-macro="wizard-menu">
+      <span tal:repeat="item view/wizard/stepMenu"
+            tal:attributes="class item/class">
+          <span
+             tal:condition="item/selected"
+             tal:content="item/title" />
+          <a href=""
+             tal:condition="not:item/selected"
+             tal:attributes="href item/url"
+             tal:content="item/title" />
+      </span>
+    </div>
+  </metal:block>
+  <form action="." method="post" enctype="multipart/form-data" class="edit-form"
+        metal:define-macro="wizard-form"
+        tal:attributes="method view/method;
+                        enctype view/enctype;
+                        acceptCharset view/acceptCharset;
+                        accept view/accept;
+                        action view/action;
+                        name view/name;
+                        id view/id">
+    <metal:block define-macro="wizard-subform">
+      <div class="viewspace" metal:define-slot="viewspace">
+        <metal:block define-slot="label">
+          <div class="label" metal:define-macro="wizard-label"
+              tal:condition="view/label|nothing"
+              tal:content="view/label">
+            Form Label
+          </div>
+        </metal:block>
+        <metal:block define-slot="info">
+          <div class="required-info"
+               metal:define-macro="wizard-required-info"
+               tal:condition="view/showRequired">
+             <span class="required">*</span>
+             &ndash; required
+          </div>
+        </metal:block>
+        <metal:block define-slot="header">
+          <div class="status" tal:condition="view/status">
+            <div class="summary"
+                 i18n:translate=""
+                 tal:content="view/status">
+              Form status summary
+            </div>
+            <ul class="errors"
+                tal:condition="view/widgets/errors"
+                metal:define-macro="wizard-errors">
+              <li tal:repeat="error view/widgets/errors">
+                <tal:block condition="error/widget">
+                  <span tal:replace="error/widget/label" />:
+                </tal:block>
+                <span tal:replace="structure error/render">Error Type</span>
+              </li>
+            </ul>
+          </div>
+        </metal:block>
+        <div metal:define-slot="extra-info" tal:replace="nothing">
+        </div>
+        <div class="step" metal:define-slot="widget-rows">
+           <div metal:use-macro="macro:widget-rows" />
+        </div>
+        <metal:block define-slot="above-buttons">
+        </metal:block>
+        <metal:block define-slot="buttons">
+          <div metal:define-macro="wizard-buttons">
+            <div class="buttons">
+              <span class="back">
+                <input tal:repeat="action view/wizard/actions/backActions"
+                     tal:replace="structure action/render"
+                     />
+              </span>
+              <span class="step">
+                <input tal:repeat="action view/actions/values"
+                     tal:replace="structure action/render"
+                     />
+              </span>
+              <span class="forward">
+                <input tal:repeat="action view/wizard/actions/forwardActions"
+                     tal:replace="structure action/render"
+                     />
+              </span>
+            </div>
+          </div>
+        </metal:block>
+        <metal:block define-slot="bottom">
+        </metal:block>
+      </div>
+    </metal:block>
+  </form>
+</div>
+
+


Property changes on: z3c.wizard/trunk/src/z3c/wizard/wizard.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/wizard.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/wizard.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/wizard.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,285 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.component
+import zope.interface
+from zope.cachedescriptors.property import Lazy
+from zope.publisher.interfaces import NotFound
+from zope.traversing.browser import absoluteURL
+
+from z3c.form import button
+from z3c.form import field
+from z3c.form import subform
+from z3c.formui import form
+from z3c.formui import layout
+from z3c.wizard import interfaces
+from z3c.wizard.button import WizardButtonActions
+
+
+def nameStep(step, name):
+    """Give a step a __name__."""
+    step.__name__ = name
+    return step
+
+
+class Wizard(form.Form):
+    """Wizard form.
+    
+    The wizard is responsible for manage the steps and offers the wizard menu
+    navigation and knows the step order. The wizard can check the conditions
+    given from the steps. The wizard is also responsible for delegate the
+    back, next and complete actions to the steps.
+
+    This IWizard object is modeled as a Controller known from the MVC
+    (Model, view, controller) patter version 2.0 and the step is implemented as
+    a view.
+    """
+
+    zope.interface.implements(interfaces.IWizard)
+
+    buttons = button.Buttons(interfaces.IWizardButtons)
+
+    # custmize this part if needed
+    stepInterface = interfaces.IStep
+
+    firstStepAsDefault = True
+    adjustStep = True
+    confirmationPageName = None
+    nextURL = None
+
+    cssActive = 'selected'
+    cssInActive = None # None will skip class attribute in DOM element
+
+    # for internal use
+    __name__ = None
+    steps = None
+    step = None
+
+    @property
+    def baseURL(self):
+        return absoluteURL(self, self.request)
+
+    def setUpSteps(self):
+        """Return a list of steps. This implementation uses IStep adapters.
+        
+        Take a look at the addStep method defined in step.py. This method 
+        allows you to setup steps directly in the method and offers an API for 
+        customized step setup.
+        """
+        steps = list(zope.component.getAdapters((self.context, self.request,
+            self), self.stepInterface))
+        return [nameStep(step, name) for name, step in steps]
+
+    def filterSteps(self, steps):
+        """Make sure to only select available steps and we give a name."""
+        return [step for step in steps if step.available]
+
+    def orderSteps(self, steps):
+        # order steps by it's weight
+        return sorted(steps, key=lambda step: step.weight)
+
+    @property
+    def steps(self):
+        steps = self.setUpSteps()
+        steps = self.filterSteps(steps)
+        return self.orderSteps(steps)
+
+    @property
+    def completed(self):
+        for step in self.steps:
+            if not step.completed:
+                return False
+        return True
+
+    @property
+    def isFirstStep(self):
+        """See interfaces.IWizard"""
+        return self.step and self.step.__name__ == self.steps[0].__name__
+
+    @property
+    def isLastStep(self):
+        """See interfaces.IWizard"""
+        return self.step and self.step.__name__ == self.steps[-1].__name__
+
+    @property
+    def showBackButton(self):
+        """Ask the step."""
+        return self.step and self.step.showBackButton
+
+    @property
+    def showNextButton(self):
+        """Ask the step."""
+        return self.step and self.step.showNextButton
+
+    @property
+    def showCompleteButton(self):
+        """Ask the step."""
+        return self.step.showCompleteButton
+
+    @property
+    def previousStepName(self):
+        if self.step is None:
+            return
+        stepNames = [step.__name__ for step in self.steps]
+        idx = stepNames.index(self.step.__name__)
+        if idx == 0:
+            return
+        return stepNames[idx-1]
+
+    @property
+    def nextStepName(self):
+        if self.step is None:
+            return
+        stepNames = [step.__name__ for step in self.steps]
+        idx = stepNames.index(self.step.__name__)
+        if idx == len(stepNames)-1:
+            return
+        return stepNames[idx+1]
+
+    @property
+    def stepMenu(self):
+        items = []
+        append = items.append
+        lenght = len(self.steps)-1
+        for idx, step in enumerate(self.steps):
+            firstStep = False
+            lastStep = False
+            if step.visible:
+                isSelected = self.step and self.step.__name__ == step.__name__
+                cssClass = isSelected and self.cssActive or self.cssInActive
+                if idx == 0:
+                    firstStep = True
+                if idx == lenght:
+                    lastStep = True
+                append({
+                    'name': step.__name__,
+                    'title': step.label,
+                    'number': str(idx+1),
+                    'url': '%s/%s' % (self.baseURL, step.__name__),
+                    'selected': self.step.__name__ == step.__name__,
+                    'class': cssClass,
+                    'first': firstStep,
+                    'last': lastStep
+                    })
+        return items
+
+    def getDefaultStep(self):
+        """Can return the first or first not completed step as default."""
+        # return first step if this option is set
+        if self.firstStepAsDefault:
+            return self.steps[0]
+        # return first not completed step
+        for step in self.steps:
+            if step.completed == False:
+                return step
+        # fallback to first step if all steps completed
+        return self.steps[0]
+
+    def doAdjustStep(self):
+        # Make sure all previous steps got completed. If not, redirect to the 
+        # last uncomplete step
+        if not self.adjustStep:
+            return False
+        for step in self.steps:
+            if step.__name__ is self.step.__name__:
+                break
+            if step.completed == False:
+                # prepare redirect to not completed step and return True
+                self.nextURL = '%s/%s' % (self.baseURL, step.__name__)
+                return True
+        # or return False
+        return False
+
+    def updateActions(self):
+        self.actions = WizardButtonActions(self, self.request,
+            self.context)
+        self.actions.update()
+
+    def update(self):
+        if self.doAdjustStep():
+            return
+        self.updateActions()
+
+    def publishTraverse(self, request, name):
+        """Traverse to step by it's name."""
+        # Remove HTML ending
+        if '.' in name:
+            rawName = name.rsplit('.', 1)[0]
+        else:
+            rawName = name
+        # Find the active step
+        for step in self.steps:
+            if step.__name__ == rawName:
+                self.step = step
+                return self.step
+        raise NotFound(self, name, request)
+
+    def browserDefault(self, request):
+        """The default step is our browserDefault traversal setp."""
+        if self.step is None:
+            step = self.getDefaultStep()
+        # always return default step as default view for our wizard
+        return self, (step.__name__,)
+
+    def goToStep(self, stepName):
+        self.nextURL = '%s/%s' % (self.baseURL, stepName)
+
+    def goToBack(self):
+        # redirect to next step if previous get sucessfuly processed
+        self.goToStep(self.previousStepName)
+
+    def goToNext(self):
+        # redirect to next step if previous get sucessfuly processed
+        self.goToStep(self.nextStepName)
+
+    def doBack(self, action):
+        if self.step.doBack(action):
+            self.goToBack()
+
+    def doNext(self, action):
+        if self.step.doNext(action):
+            self.goToNext()
+
+    def doComplete(self, action):
+        if self.step.doComplete(action):
+            # do finsih after step get completed is completed
+            self.doFinish()
+
+    def doFinish(self):
+        """Force redirect after doComplete if confirmationPageName is given."""
+        if self.confirmationPageName is not None:
+            self.nextURL = '%s/%s' % (absoluteURL(self.context, self.request),
+                self.confirmationPageName)
+
+    @button.handler(interfaces.IWizardButtons['back'])
+    def handleBack(self, action):
+        self.doBack(action)
+
+    @button.handler(interfaces.IWizardButtons['next'])
+    def handleNext(self, action):
+        self.doNext(action)
+
+    @button.handler(interfaces.IWizardButtons['complete'])
+    def handleComplete(self, action):
+        self.doComplete(action)
+
+    def render(self, *args, **kws):
+        raise NotImplementedError('render is no supported')
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.__name__)


Property changes on: z3c.wizard/trunk/src/z3c/wizard/wizard.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/zcml.py
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/zcml.py	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/zcml.py	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,220 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Wizard button actions implementation
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.schema
+import zope.configuration.fields
+import zope.security.checker
+import zope.security.zcml
+from zope.configuration.exceptions import ConfigurationError
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from zope.app.publisher.browser import viewmeta
+from zope.app.component import metadirectives
+
+import z3c.pagelet.zcml
+
+from z3c.wizard import interfaces
+from z3c.wizard import step
+from z3c.wizard import wizard
+
+
+class IWizardDirective(z3c.pagelet.zcml.IPageletDirective):
+    """A directive to register a new wizard.
+
+    The wizard directive also supports an undefined set of keyword arguments
+    that are set as attributes on the wizard after creation.
+    """
+
+
+class IWizardStepDirective(metadirectives.IBasicViewInformation):
+    """A directive to register a new wizard step.
+
+    The wizard step directive also supports an undefined set of keyword 
+    arguments that are set as attributes on the wizard step after creation.
+    """
+
+    name = zope.schema.TextLine(
+        title=u"The name of the pagelet.",
+        description=u"The name shows up in URLs/paths. For example 'foo'.",
+        required=True)
+
+    class_ = zope.configuration.fields.GlobalObject(
+        title=u"Class",
+        description=u"A class that provides attributes used by the pagelet.",
+        required=True,
+        )
+
+    permission = zope.security.zcml.Permission(
+        title=u"Permission",
+        description=u"The permission needed to use the pagelet.",
+        required=True
+        )
+
+    for_ = zope.configuration.fields.GlobalObject(
+        title=u"Context",
+        description=u"The content interface or class this pagelet is for.",
+        required=False
+        )
+
+    wizard = zope.configuration.fields.GlobalObject(
+        title=u"Wizard",
+        description=u"The wizard interface or class this step is for.",
+        required=False
+        )
+
+    provides = zope.configuration.fields.GlobalInterface(
+        title=u"The interface this pagelets provides.",
+        description=u"""
+        A pagelet can provide an interface.  This would be used for
+        views that support other views.""",
+        required=False,
+        default=interfaces.IPagelet,
+        )
+
+
+# Arbitrary keys and values are allowed to be passed to the wizard.
+IWizardDirective.setTaggedValue('keyword_arguments', True)
+
+
+# Arbitrary keys and values are allowed to be passed to the step.
+IWizardStepDirective.setTaggedValue('keyword_arguments', True)
+
+
+# wizard directive
+def wizardDirective(
+    _context, class_, name, permission, for_=zope.interface.Interface, 
+    layer=IDefaultBrowserLayer, provides=interfaces.IWizard,
+    allowed_interface=None, allowed_attributes=None, **kwargs):
+
+    # Security map dictionary
+    required = {}
+
+    # Get the permission; mainly to correctly handle CheckerPublic.
+    permission = viewmeta._handle_permission(_context, permission)
+
+    # The class must be specified.
+    if not class_:
+        raise ConfigurationError("Must specify a class.")
+
+    if not zope.interface.interfaces.IInterface.providedBy(provides):
+        raise ConfigurationError("Provides interface provide IInterface.")
+
+    ifaces = list(zope.interface.Declaration(provides).flattened())
+    if interfaces.IWizard not in ifaces:
+        raise ConfigurationError("Provides interface must inherit IWizard.")
+
+    # Build a new class that we can use different permission settings if we
+    # use the class more then once.
+    cdict = {}
+    cdict['__name__'] = name
+    cdict.update(kwargs)
+    new_class = type(class_.__name__, (class_, wizard.Wizard), cdict)
+
+    # Set up permission mapping for various accessible attributes
+    viewmeta._handle_allowed_interface(
+        _context, allowed_interface, permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, allowed_attributes, permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, kwargs.keys(), permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, ('__call__', 'browserDefault', 'update', 'render', 
+                   'publishTraverse'), permission, required)
+
+    # Register the interfaces.
+    viewmeta._handle_for(_context, for_)
+
+    # provide the custom provides interface if not allready provided
+    if not provides.implementedBy(new_class):
+        zope.interface.classImplements(new_class, provides)
+
+    # Create the security checker for the new class
+    zope.security.checker.defineChecker(new_class, 
+        zope.security.checker.Checker(required))
+
+    # register pagelet
+    _context.action(
+        discriminator = ('pagelet', for_, layer, name),
+        callable = zope.component.zcml.handler,
+        args = ('registerAdapter',
+                new_class, (for_, layer), provides, name, _context.info),)
+
+# step directive
+def wizardStepDirective(
+    _context, class_, name, permission, for_=zope.interface.Interface, 
+    layer=IDefaultBrowserLayer, wizard=interfaces.IWizard,
+    provides=interfaces.IStep, allowed_interface=None,
+    allowed_attributes=None, **kwargs):
+
+    # Security map dictionary
+    required = {}
+
+    # Get the permission; mainly to correctly handle CheckerPublic.
+    permission = viewmeta._handle_permission(_context, permission)
+
+    # The class must be specified.
+    if not class_:
+        raise ConfigurationError("Must specify a class.")
+
+    if not zope.interface.interfaces.IInterface.providedBy(provides):
+        raise ConfigurationError("Provides interface provide IInterface.")
+
+    ifaces = list(zope.interface.Declaration(provides).flattened())
+    if interfaces.IPagelet not in ifaces:
+        raise ConfigurationError("Provides interface must inherit IPagelet.")
+
+    if not interfaces.IWizard.implementedBy(wizard):
+        raise ConfigurationError("Provides interface must inherit IWizard.")
+
+    # Build a new class that we can use different permission settings if we
+    # use the class more then once.
+    cdict = {}
+    cdict['__name__'] = name
+    cdict.update(kwargs)
+    new_class = type(class_.__name__, (class_, step.Step), cdict)
+
+    # Set up permission mapping for various accessible attributes
+    viewmeta._handle_allowed_interface(
+        _context, allowed_interface, permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, allowed_attributes, permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, kwargs.keys(), permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, ('__call__', 'browserDefault', 'update', 'render', 
+                   'publishTraverse'), permission, required)
+
+    # Register the interfaces.
+    viewmeta._handle_for(_context, for_)
+
+    # provide the custom provides interface if not allready provided
+    if not provides.implementedBy(new_class):
+        zope.interface.classImplements(new_class, provides)
+
+    # Create the security checker for the new class
+    zope.security.checker.defineChecker(new_class, 
+        zope.security.checker.Checker(required))
+
+    # register pagelet
+    _context.action(
+        discriminator = ('pagelet', for_, layer, name),
+        callable = zope.component.zcml.handler,
+        args = ('registerAdapter',
+                new_class, (for_, layer, wizard), provides, name, _context.info),)


Property changes on: z3c.wizard/trunk/src/z3c/wizard/zcml.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.wizard/trunk/src/z3c/wizard/zcml.txt
===================================================================
--- z3c.wizard/trunk/src/z3c/wizard/zcml.txt	                        (rev 0)
+++ z3c.wizard/trunk/src/z3c/wizard/zcml.txt	2008-08-25 15:37:04 UTC (rev 90223)
@@ -0,0 +1,103 @@
+=========================
+Wizard and Step directive
+=========================
+
+Show how we can use the wizard and wizardStep directives. Register the meta 
+configuration for the directive.
+
+  >>> import sys
+  >>> from zope.configuration import xmlconfig
+  >>> import z3c.wizard
+  >>> context = xmlconfig.file('meta.zcml', z3c.wizard)
+
+We need also a custom wizard class:
+
+  >>> import z3c.wizard
+  >>> class MyWizard(z3c.wizard.wizard.Wizard):
+  ...     """Custom wizard"""
+
+Make them available under the fake package ``custom``:
+
+  >>> sys.modules['custom'] = type(
+  ...     'Module', (), 
+  ...     {'MyWizard': MyWizard})()
+
+Register a wizard within the directive with minimal attributes:
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:wizard
+  ...       name="wizard"
+  ...       class="custom.MyWizard"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... """, context)
+
+Now define a step:
+
+  >>> import z3c.wizard
+  >>> class FirstStep(z3c.wizard.step.Step):
+  ...     """First step"""
+
+register the new step classes in the custom module...
+
+  >>> sys.modules['custom'].FirstStep = FirstStep
+
+and use them in the ``wizardStep`` directive:
+
+  >>> context = xmlconfig.string("""
+  ... <configure
+  ...     xmlns:z3c="http://namespaces.zope.org/z3c">
+  ...   <z3c:wizardStep
+  ...       name="first"
+  ...       wizard="custom.MyWizard"
+  ...       class="custom.FirstStep"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... """, context)
+
+Let's get the wizard
+
+  >>> import zope.component
+  >>> from zope.publisher.browser import TestRequest
+  >>> wizard = zope.component.queryMultiAdapter((object(), TestRequest()), 
+  ...     name='wizard')
+
+and check them:
+
+  >>> wizard
+  <MyWizard u'wizard'>
+
+  >>> z3c.wizard.interfaces.IWizard.providedBy(wizard)
+  True
+
+Let's get the wizard step
+
+  >>> import zope.component
+  >>> from zope.publisher.browser import TestRequest
+  >>> firstStep = zope.component.queryMultiAdapter(
+  ...     (object(), TestRequest(), wizard), name='first')
+
+and check them
+
+  >>> firstStep
+  <FirstStep u'first'>
+
+  >>> firstStep.context
+  <object object at ...>
+
+  >>> firstStep.wizard
+  <MyWizard u'wizard'>
+
+  >>> z3c.wizard.interfaces.IStep.providedBy(firstStep)
+  True
+
+  >>> z3c.wizard.interfaces.IWizard.providedBy(firstStep.wizard)
+  True
+
+Clean up the custom module.
+
+  >>> del sys.modules['custom']


Property changes on: z3c.wizard/trunk/src/z3c/wizard/zcml.txt
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list