This package provides a form wizard concept based on z3c.form for Zope3.
.. contents::
======
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
change or add is not a part of this implementation. If you like to implement
some additional wizard logic you probably need to use a session and collect the
values in the different wizard steps and create and add an object in the
wizard's ``doComplete`` or ``doFinish`` or the step's ``doComplete`` method.
All steps are available by their 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 override existing steps by registering 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 load the formui configuration, which will make sure that
all macros get registered correctly:
>>> from zope.configuration import xmlconfig
>>> import z3c.form
>>> import z3c.formui
>>> import z3c.macro
>>> import z3c.template
>>> import zope.browserresource
>>> import zope.component
>>> import zope.i18n
>>> import zope.security
>>> import zope.viewlet
>>> xmlconfig.XMLConfig('meta.zcml', z3c.form)()
>>> xmlconfig.XMLConfig('meta.zcml', z3c.macro)()
>>> xmlconfig.XMLConfig('meta.zcml', z3c.template)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.browserresource)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.component)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.i18n)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.security)()
>>> xmlconfig.XMLConfig('meta.zcml', zope.viewlet)()
>>> xmlconfig.XMLConfig('configure.zcml', z3c.form)()
>>> 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.location.interfaces import ILocation
>>> from zope.schema.fieldproperty import FieldProperty
>>> class IPerson(ILocation):
... """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')
>>> @zope.interface.implementer(IPerson)
... class Person(object):
... """Person content."""
...
...
... __name__ = __parent__ = None
...
... 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."""
>>> @zope.interface.implementer(IPersonWizard)
... class PersonWizard(wizard.Wizard):
...
... 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 use in our steps. Because our steps are forms:
>>> from z3c.formui.interfaces import IDivFormLayer
>>> from zope.interface import alsoProvides
>>> import zope.publisher.browser
>>> import z3c.form.interfaces
>>> @zope.interface.implementer(z3c.form.interfaces.IFormLayer)
... class TestRequest(zope.publisher.browser.TestRequest):
... pass
>>> 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 rendering 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 'wizard'>
>>> names
('person',)
Now traverse to the step, update and render it:
>>> 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" name="form">
<div class="viewspace">
<div class="label">Person</div>
<div class="required-info">
<span class="required">*</span>– required
</div>
<div class="step">
<div id="form-widgets-firstName-row" class="row required">
<div class="label">
<label for="form-widgets-firstName">
<span>First Name</span>
<span class="required">*</span>
</label>
</div>
<div class="widget">
<input id="form-widgets-firstName" name="form.widgets.firstName" class="text-widget required textline-field" value="" type="text" />
</div>
</div>
<div id="form-widgets-lastName-row" class="row required">
<div class="label">
<label for="form-widgets-lastName">
<span>Last Name</span>
<span class="required">*</span>
</label>
</div>
<div class="widget">
<input id="form-widgets-lastName" name="form.widgets.lastName" class="text-widget required textline-field" value="" type="text" />
</div>
</div>
</div>
<div>
<div class="buttons">
<span class="back">
</span>
<span class="step">
<input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field" value="Apply" type="submit" />
</span>
<span class="forward">
<input id="form-buttons-next" name="form.buttons.next" class="submit-widget button-field" value="Next" type="submit" />
</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 them.
>>> 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 it:
>>> 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" name="form">
<div class="viewspace">
<div class="label">Address</div>
<div class="required-info">
<span class="required">*</span>– required
</div>
<div class="step">
<div id="form-widgets-street-row" class="row required">
<div class="label">
<label for="form-widgets-street">
<span>Street</span>
<span class="required">*</span>
</label>
</div>
<div class="widget">
<input id="form-widgets-street" name="form.widgets.street" class="text-widget required textline-field" value="" type="text" />
</div>
</div>
<div id="form-widgets-city-row" class="row required">
<div class="label">
<label for="form-widgets-city">
<span>City</span>
<span class="required">*</span>
</label>
</div>
<div class="widget">
<input id="form-widgets-city" name="form.widgets.city" class="text-widget required textline-field" value="" type="text" />
</div>
</div>
</div>
<div>
<div class="buttons">
<span class="back">
<input id="form-buttons-back" name="form.buttons.back" class="submit-widget button-field" value="Back" type="submit" />
</span>
<span class="step">
<input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field" value="Apply" type="submit" />
</span>
<span class="forward">
</span>
</div>
</div>
</div>
</form>
</div>
=========================
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 it:
>>> wizard
<MyWizard '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 it
>>> firstStep
<FirstStep 'first'>
>>> firstStep.context
<object object at ...>
>>> firstStep.wizard
<MyWizard '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']
=======
CHANGES
=======
2.0 (2023-02-10)
----------------
- Add support for Python 3.8, 3.9, 3.10, 3.11.
- Drop support for Python 2.7, 3.4, 3.5, 3.6.
1.1 (2019-01-27)
----------------
- Add support for Python 3.7.
1.0 (2017-06-16)
----------------
- Add support for Python 3.4 up to 3.6, PyPy2 and PyPy3.
0.9.1 (2011-10-28)
------------------
- Using Python's ``doctest`` module instead of depreacted
``zope.testing.doctest``.
- Allowing in step complete check that values are not existing on
context. This is consistent with the way `z3c.form` handles not existing
values.
0.9.0 (2009-12-29)
------------------
- Avoid `z3c.form.testing` in tests: It depends on `lxml`, but `lxml`
features are not needed here.
- Using ``requiredInfo`` property to render the information about
required fields. This property returns an i18n message id making the
information translateable.
0.8.0 (2009-11-30)
------------------
- Adjusted dependencies, reflecting changes in zope packages: use new
packages and skip dependency to `zope.app.publisher`.
0.7.1 (2009-10-27)
------------------
- Bugfix for z3c.form 2.2.0 changes. Removed name definition in Step
class. This will prevent to run into an error based on the z3c.form
changes.
0.7.0 (2009-08-15)
------------------
- Added support for field groups in step template. (Copied over from
z3c.formui.)
- There were two metal define-slots named `header`. Renamed the first
one to `wizard-header`.
0.6.0 (2009-07-10)
------------------
- Remove dependency on z3c.i18n as it wasn't really used and we can
easily recreate a message factory for the "z3c" domain.
- Fixed tests to work with z3c.form 2.0.
- Added another doctest to the `long_description`.
- Changed package's mailing list address to zope-dev at zope.org instead
of the retired zope3-dev one.
0.5.0 (2009-02-22)
------------------
- Initial Release
Raw data
{
"_id": null,
"home_page": "https://github.com/zopefoundation/z3c.wizard",
"name": "z3c.wizard",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "zope zope3 z3c form wizard",
"author": "Roger Ineichen and the Zope Community",
"author_email": "zope-dev@zope.dev",
"download_url": "https://files.pythonhosted.org/packages/7a/f3/639a612383bea7409bcec5227740348ffa56a9b552afdaae2cbaa97b6f41/z3c.wizard-2.0.tar.gz",
"platform": null,
"description": "This package provides a form wizard concept based on z3c.form for Zope3.\n\n.. contents::\n\n======\nWizard\n======\n\nThe goal of this package is to offer a form wizard. This implementation doesn't\nuse a session. It just offers the wizard logic, the data which the wizard will\nchange or add is not a part of this implementation. If you like to implement\nsome additional wizard logic you probably need to use a session and collect the\nvalues in the different wizard steps and create and add an object in the\nwizard's ``doComplete`` or ``doFinish`` or the step's ``doComplete`` method.\n\nAll steps are available by their own url. This allows us to cache each step if\nneeded. Each step url is only available if we are allowed to access a step. If\na step is accessible depends on the conditions of each step.\n\nSince steps are adapters, we can register steps for already existing wizards or\nwe can also override existing steps by registering a UnavailableStep step which\nalways will return ``False`` for the ``available`` argument.\n\nIf the wizard is completed we get redirected to the confirmation page. If we\naccess a completed wizard again, we will get redirected to the confirmation\npage again.\n\nNow let's show how this works and setup our tests.\n\n\nForm support\n------------\n\nWe need to load the formui configuration, which will make sure that\nall macros get registered correctly:\n\n >>> from zope.configuration import xmlconfig\n >>> import z3c.form\n >>> import z3c.formui\n >>> import z3c.macro\n >>> import z3c.template\n >>> import zope.browserresource\n >>> import zope.component\n >>> import zope.i18n\n >>> import zope.security\n >>> import zope.viewlet\n >>> xmlconfig.XMLConfig('meta.zcml', z3c.form)()\n >>> xmlconfig.XMLConfig('meta.zcml', z3c.macro)()\n >>> xmlconfig.XMLConfig('meta.zcml', z3c.template)()\n >>> xmlconfig.XMLConfig('meta.zcml', zope.browserresource)()\n >>> xmlconfig.XMLConfig('meta.zcml', zope.component)()\n >>> xmlconfig.XMLConfig('meta.zcml', zope.i18n)()\n >>> xmlconfig.XMLConfig('meta.zcml', zope.security)()\n >>> xmlconfig.XMLConfig('meta.zcml', zope.viewlet)()\n >>> xmlconfig.XMLConfig('configure.zcml', z3c.form)()\n >>> xmlconfig.XMLConfig('configure.zcml', z3c.formui)()\n\nAnd load the z3c.wizard macro configuration:\n\n >>> import z3c.wizard\n >>> xmlconfig.XMLConfig('configure.zcml', z3c.wizard)()\n\n\nSample data setup\n-----------------\n\nLet's define a sample content class:\n\n >>> import zope.interface\n >>> import zope.schema\n >>> from zope.location.interfaces import ILocation\n >>> from zope.schema.fieldproperty import FieldProperty\n >>> class IPerson(ILocation):\n ... \"\"\"Person interface.\"\"\"\n ...\n ... firstName = zope.schema.TextLine(title=u'First Name')\n ... lastName = zope.schema.TextLine(title=u'Last Name')\n ... street = zope.schema.TextLine(title=u'Street')\n ... city = zope.schema.TextLine(title=u'City')\n\n >>> @zope.interface.implementer(IPerson)\n ... class Person(object):\n ... \"\"\"Person content.\"\"\"\n ...\n ...\n ... __name__ = __parent__ = None\n ...\n ... firstName = FieldProperty(IPerson['firstName'])\n ... lastName = FieldProperty(IPerson['lastName'])\n ... street = FieldProperty(IPerson['street'])\n ... city = FieldProperty(IPerson['city'])\n\nSetup a person for our wizard:\n\n >>> person = Person()\n >>> root['person'] = person\n >>> person.__parent__ = root\n >>> person.__name__ = u'person'\n\n\nStep\n----\n\nLet's define some steps. First use a step which knows how to store the name\nof a person:\n\n >>> from z3c.form import form\n >>> from z3c.form import field\n >>> from z3c.wizard import step\n\n >>> class PersonStep(step.EditStep):\n ... label = u'Person'\n ... fields = field.Fields(IPerson).select('firstName', 'lastName')\n\nAnd another step for collect some address data:\n\n >>> class AddressStep(step.EditStep):\n ... label = u'Address'\n ... fields = field.Fields(IPerson).select('street', 'city')\n\n\nWizard\n------\n\nNow we can define our ``Wizard`` including our steps. Steps are named\nadapters. Let's use the global method ``addStep`` for doing the step setup:\n\n >>> from z3c.wizard import wizard\n >>> class IPersonWizard(z3c.wizard.interfaces.IWizard):\n ... \"\"\"Person wizard marker.\"\"\"\n\n >>> @zope.interface.implementer(IPersonWizard)\n ... class PersonWizard(wizard.Wizard):\n ...\n ... label = u'Person Wizard'\n ...\n ... def setUpSteps(self):\n ... return [\n ... step.addStep(self, 'person', weight=1),\n ... step.addStep(self, 'address', weight=2),\n ... ]\n\nAs next, we need to register our steps as named ``IStep`` adapters. This can be\ndone by the ``z3c:wizardStep`` directive. Let's define our adapters with the\nprovideAdapter method for now:\n\n >>> import zope.interface\n >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer\n >>> from zope.publisher.interfaces.browser import IBrowserRequest\n >>> import z3c.wizard.interfaces\n >>> zope.component.provideAdapter(\n ... PersonStep, (None, IBrowserRequest, None),\n ... z3c.wizard.interfaces.IStep, name='person')\n\n >>> zope.component.provideAdapter(\n ... AddressStep, (None, IBrowserRequest, None),\n ... z3c.wizard.interfaces.IStep, name='address')\n\nWe need to support the div form layer for our request. This is needed for the\nform part we use in our steps. Because our steps are forms:\n\n >>> from z3c.formui.interfaces import IDivFormLayer\n >>> from zope.interface import alsoProvides\n >>> import zope.publisher.browser\n >>> import z3c.form.interfaces\n >>> @zope.interface.implementer(z3c.form.interfaces.IFormLayer)\n ... class TestRequest(zope.publisher.browser.TestRequest):\n ... pass\n >>> request = TestRequest()\n >>> alsoProvides(request, IDivFormLayer)\n\nNow we can use our wizard. Our wizard will allways force to traverse to the\ncurrent active step. This means the wizard provides a browserDefault which\nreturns the default step instead of rendering the wizard as view. This allows us\nto use the step as an adapter discriminator for viewlets and other adapters\nlike the menu implementation uses. The wizard acts like a dispatcher to the\nright step and not as a view itself.\n\n >>> personWizard = PersonWizard(person, request)\n >>> personWizard.__parent__ = person\n >>> personWizard.__name__ = u'wizard'\n\nNow get the default view (step) arguments from the wizard:\n\n >>> obj, names = personWizard.browserDefault(request)\n >>> obj\n <PersonWizard 'wizard'>\n\n >>> names\n ('person',)\n\nNow traverse to the step, update and render it:\n\n >>> personStep = obj.publishTraverse(request, names[0])\n >>> personStep.update()\n >>> print(personStep.render())\n <div class=\"wizard\">\n <div class=\"header\">Person Wizard</div>\n <div class=\"wizardMenu\">\n <span class=\"selected\">\n <span>Person</span>\n </span>\n <span>\n <a href=\"http://127.0.0.1/person/wizard/address\">Address</a>\n </span>\n </div>\n <form action=\"http://127.0.0.1\" method=\"post\" enctype=\"multipart/form-data\" class=\"edit-form\" id=\"form\" name=\"form\">\n <div class=\"viewspace\">\n <div class=\"label\">Person</div>\n <div class=\"required-info\">\n <span class=\"required\">*</span>– required\n </div>\n <div class=\"step\">\n <div id=\"form-widgets-firstName-row\" class=\"row required\">\n <div class=\"label\">\n <label for=\"form-widgets-firstName\">\n <span>First Name</span>\n <span class=\"required\">*</span>\n </label>\n </div>\n <div class=\"widget\">\n <input id=\"form-widgets-firstName\" name=\"form.widgets.firstName\" class=\"text-widget required textline-field\" value=\"\" type=\"text\" />\n </div>\n </div>\n <div id=\"form-widgets-lastName-row\" class=\"row required\">\n <div class=\"label\">\n <label for=\"form-widgets-lastName\">\n <span>Last Name</span>\n <span class=\"required\">*</span>\n </label>\n </div>\n <div class=\"widget\">\n <input id=\"form-widgets-lastName\" name=\"form.widgets.lastName\" class=\"text-widget required textline-field\" value=\"\" type=\"text\" />\n </div>\n </div>\n </div>\n <div>\n <div class=\"buttons\">\n <span class=\"back\">\n </span>\n <span class=\"step\">\n <input id=\"form-buttons-apply\" name=\"form.buttons.apply\" class=\"submit-widget button-field\" value=\"Apply\" type=\"submit\" />\n </span>\n <span class=\"forward\">\n <input id=\"form-buttons-next\" name=\"form.buttons.next\" class=\"submit-widget button-field\" value=\"Next\" type=\"submit\" />\n </span>\n </div>\n </div>\n </div>\n </form>\n </div>\n\n\nWe can't go to the next step if we not complete the first step:\n\n >>> request = TestRequest(form={'form.buttons.next': 'Next'})\n >>> alsoProvides(request, IDivFormLayer)\n >>> personWizard = PersonWizard(person, request)\n >>> personWizard.__parent__ = person\n >>> personWizard.__name__ = u'wizard'\n >>> personStep = personWizard.publishTraverse(request, names[0])\n >>> personStep.update()\n >>> print(personStep.render())\n <div class=\"wizard\">\n ...\n <div class=\"summary\">There were some errors.</div>\n ...\n <div class=\"error\">Required input is missing.</div>\n ...\n <div class=\"error\">Required input is missing.</div>\n ...\n\n\nWe can complete this step if we fill in the required values and click next:\n\n >>> request = TestRequest(form={'form.widgets.firstName': u'Roger',\n ... 'form.widgets.lastName': u'Ineichen',\n ... 'form.buttons.next': 'Next'})\n >>> alsoProvides(request, IDivFormLayer)\n >>> personWizard = PersonWizard(person, request)\n >>> personWizard.__parent__ = person\n >>> personWizard.__name__ = u'wizard'\n >>> personStep = personWizard.publishTraverse(request, names[0])\n >>> personStep.update()\n >>> print(personStep.render())\n\nAs you can see the step get processed and the wizard will redirect to the next\nstep using the response redirect concept:\n\n >>> personWizard.nextURL\n 'http://127.0.0.1/person/wizard/address'\n\nLet's access the next step using the traverser. This will setup the next step\nand them.\n\n >>> request = TestRequest()\n >>> alsoProvides(request, IDivFormLayer)\n >>> personWizard = PersonWizard(person, request)\n >>> personWizard.__parent__ = person\n >>> personWizard.__name__ = u'wizard'\n\nAs you can see we see our next step is the address step:\n\n >>> addressStep = personWizard.publishTraverse(request, 'address')\n >>> addressStep\n <AddressStep 'address'>\n\nUpdate and render it:\n\n >>> addressStep.update()\n >>> print(addressStep.render())\n <div class=\"wizard\">\n <div class=\"header\">Person Wizard</div>\n <div class=\"wizardMenu\">\n <span>\n <a href=\"http://127.0.0.1/person/wizard/person\">Person</a>\n </span>\n <span class=\"selected\">\n <span>Address</span>\n </span>\n </div>\n <form action=\"http://127.0.0.1\" method=\"post\" enctype=\"multipart/form-data\" class=\"edit-form\" id=\"form\" name=\"form\">\n <div class=\"viewspace\">\n <div class=\"label\">Address</div>\n <div class=\"required-info\">\n <span class=\"required\">*</span>– required\n </div>\n <div class=\"step\">\n <div id=\"form-widgets-street-row\" class=\"row required\">\n <div class=\"label\">\n <label for=\"form-widgets-street\">\n <span>Street</span>\n <span class=\"required\">*</span>\n </label>\n </div>\n <div class=\"widget\">\n <input id=\"form-widgets-street\" name=\"form.widgets.street\" class=\"text-widget required textline-field\" value=\"\" type=\"text\" />\n </div>\n </div>\n <div id=\"form-widgets-city-row\" class=\"row required\">\n <div class=\"label\">\n <label for=\"form-widgets-city\">\n <span>City</span>\n <span class=\"required\">*</span>\n </label>\n </div>\n <div class=\"widget\">\n <input id=\"form-widgets-city\" name=\"form.widgets.city\" class=\"text-widget required textline-field\" value=\"\" type=\"text\" />\n </div>\n </div>\n </div>\n <div>\n <div class=\"buttons\">\n <span class=\"back\">\n <input id=\"form-buttons-back\" name=\"form.buttons.back\" class=\"submit-widget button-field\" value=\"Back\" type=\"submit\" />\n </span>\n <span class=\"step\">\n <input id=\"form-buttons-apply\" name=\"form.buttons.apply\" class=\"submit-widget button-field\" value=\"Apply\" type=\"submit\" />\n </span>\n <span class=\"forward\">\n </span>\n </div>\n </div>\n </div>\n </form>\n </div>\n\n\n=========================\nWizard and Step directive\n=========================\n\nShow how we can use the ``wizard`` and ``wizardStep``\ndirectives. Register the meta configuration for the directive.\n\n >>> import sys\n >>> from zope.configuration import xmlconfig\n >>> import z3c.wizard\n >>> context = xmlconfig.file('meta.zcml', z3c.wizard)\n\nWe need also a custom wizard class:\n\n >>> import z3c.wizard\n >>> class MyWizard(z3c.wizard.wizard.Wizard):\n ... \"\"\"Custom wizard\"\"\"\n\nMake them available under the fake package `custom`:\n\n >>> sys.modules['custom'] = type(\n ... 'Module', (),\n ... {'MyWizard': MyWizard})()\n\nRegister a wizard within the directive with minimal attributes:\n\n >>> context = xmlconfig.string(\"\"\"\n ... <configure\n ... xmlns:z3c=\"http://namespaces.zope.org/z3c\">\n ... <z3c:wizard\n ... name=\"wizard\"\n ... class=\"custom.MyWizard\"\n ... permission=\"zope.Public\"\n ... />\n ... </configure>\n ... \"\"\", context)\n\nNow define a step,\n\n >>> import z3c.wizard\n >>> class FirstStep(z3c.wizard.step.Step):\n ... \"\"\"First step\"\"\"\n\nregister the new step classes in the custom module\n\n >>> sys.modules['custom'].FirstStep = FirstStep\n\nand use them in the ``wizardStep`` directive:\n\n >>> context = xmlconfig.string(\"\"\"\n ... <configure\n ... xmlns:z3c=\"http://namespaces.zope.org/z3c\">\n ... <z3c:wizardStep\n ... name=\"first\"\n ... wizard=\"custom.MyWizard\"\n ... class=\"custom.FirstStep\"\n ... permission=\"zope.Public\"\n ... />\n ... </configure>\n ... \"\"\", context)\n\nLet's get the wizard\n\n >>> import zope.component\n >>> from zope.publisher.browser import TestRequest\n >>> wizard = zope.component.queryMultiAdapter((object(), TestRequest()),\n ... name='wizard')\n\nand check it:\n\n >>> wizard\n <MyWizard 'wizard'>\n\n >>> z3c.wizard.interfaces.IWizard.providedBy(wizard)\n True\n\nLet's get the wizard step\n\n >>> import zope.component\n >>> from zope.publisher.browser import TestRequest\n >>> firstStep = zope.component.queryMultiAdapter(\n ... (object(), TestRequest(), wizard), name='first')\n\nand check it\n\n >>> firstStep\n <FirstStep 'first'>\n\n >>> firstStep.context\n <object object at ...>\n\n >>> firstStep.wizard\n <MyWizard 'wizard'>\n\n >>> z3c.wizard.interfaces.IStep.providedBy(firstStep)\n True\n\n >>> z3c.wizard.interfaces.IWizard.providedBy(firstStep.wizard)\n True\n\nClean up the custom module:\n\n >>> del sys.modules['custom']\n\n\n=======\nCHANGES\n=======\n\n2.0 (2023-02-10)\n----------------\n\n- Add support for Python 3.8, 3.9, 3.10, 3.11.\n\n- Drop support for Python 2.7, 3.4, 3.5, 3.6.\n\n\n1.1 (2019-01-27)\n----------------\n\n- Add support for Python 3.7.\n\n\n1.0 (2017-06-16)\n----------------\n\n- Add support for Python 3.4 up to 3.6, PyPy2 and PyPy3.\n\n\n0.9.1 (2011-10-28)\n------------------\n\n- Using Python's ``doctest`` module instead of depreacted\n ``zope.testing.doctest``.\n\n- Allowing in step complete check that values are not existing on\n context. This is consistent with the way `z3c.form` handles not existing\n values.\n\n0.9.0 (2009-12-29)\n------------------\n\n- Avoid `z3c.form.testing` in tests: It depends on `lxml`, but `lxml`\n features are not needed here.\n\n- Using ``requiredInfo`` property to render the information about\n required fields. This property returns an i18n message id making the\n information translateable.\n\n\n0.8.0 (2009-11-30)\n------------------\n\n- Adjusted dependencies, reflecting changes in zope packages: use new\n packages and skip dependency to `zope.app.publisher`.\n\n\n0.7.1 (2009-10-27)\n------------------\n\n- Bugfix for z3c.form 2.2.0 changes. Removed name definition in Step\n class. This will prevent to run into an error based on the z3c.form\n changes.\n\n\n0.7.0 (2009-08-15)\n------------------\n\n- Added support for field groups in step template. (Copied over from\n z3c.formui.)\n\n- There were two metal define-slots named `header`. Renamed the first\n one to `wizard-header`.\n\n\n\n0.6.0 (2009-07-10)\n------------------\n\n- Remove dependency on z3c.i18n as it wasn't really used and we can\n easily recreate a message factory for the \"z3c\" domain.\n\n- Fixed tests to work with z3c.form 2.0.\n\n- Added another doctest to the `long_description`.\n\n- Changed package's mailing list address to zope-dev at zope.org instead\n of the retired zope3-dev one.\n\n0.5.0 (2009-02-22)\n------------------\n\n- Initial Release\n",
"bugtrack_url": null,
"license": "ZPL 2.1",
"summary": "Wizard based on z3c.form for for Zope3",
"version": "2.0",
"split_keywords": [
"zope",
"zope3",
"z3c",
"form",
"wizard"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "85994c1cbdcbba74f1feef6dffa55a72d9dae069f745dc67d3d7c7b56acf8f41",
"md5": "00aba1c51575dabc655183201eeea42b",
"sha256": "f77c773b6cf17193e15b81d0ec5de4bc8f760eaf8e73ba035a998ded13e67082"
},
"downloads": -1,
"filename": "z3c.wizard-2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "00aba1c51575dabc655183201eeea42b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 26673,
"upload_time": "2023-02-10T09:28:08",
"upload_time_iso_8601": "2023-02-10T09:28:08.787892Z",
"url": "https://files.pythonhosted.org/packages/85/99/4c1cbdcbba74f1feef6dffa55a72d9dae069f745dc67d3d7c7b56acf8f41/z3c.wizard-2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "7af3639a612383bea7409bcec5227740348ffa56a9b552afdaae2cbaa97b6f41",
"md5": "e1161de79b615f7eb81d12bcaf2ef037",
"sha256": "6c14bc5761406cad3fac0560f13295b62ee2fd09ffa04f0714d1ab35dc088def"
},
"downloads": -1,
"filename": "z3c.wizard-2.0.tar.gz",
"has_sig": false,
"md5_digest": "e1161de79b615f7eb81d12bcaf2ef037",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 25866,
"upload_time": "2023-02-10T09:28:10",
"upload_time_iso_8601": "2023-02-10T09:28:10.358482Z",
"url": "https://files.pythonhosted.org/packages/7a/f3/639a612383bea7409bcec5227740348ffa56a9b552afdaae2cbaa97b6f41/z3c.wizard-2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-02-10 09:28:10",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "zopefoundation",
"github_project": "z3c.wizard",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "z3c.wizard"
}