[Checkins] SVN: plone.z3cform/trunk/ Relicense and move plone.z3cform into Zope repository

Daniel Nouri daniel.nouri at gmail.com
Sun Jul 13 17:54:25 EDT 2008


Log message for revision 88331:
  Relicense and move plone.z3cform into Zope repository
  

Changed:
  A   plone.z3cform/trunk/
  A   plone.z3cform/trunk/README.txt
  A   plone.z3cform/trunk/bootstrap.py
  A   plone.z3cform/trunk/buildout.cfg
  A   plone.z3cform/trunk/docs/
  A   plone.z3cform/trunk/docs/HISTORY.txt
  A   plone.z3cform/trunk/docs/INSTALL.txt
  A   plone.z3cform/trunk/docs/LICENSE.txt
  A   plone.z3cform/trunk/plone/
  A   plone.z3cform/trunk/plone/__init__.py
  A   plone.z3cform/trunk/plone/z3cform/
  A   plone.z3cform/trunk/plone/z3cform/__init__.py
  A   plone.z3cform/trunk/plone/z3cform/add.py
  A   plone.z3cform/trunk/plone/z3cform/base.py
  A   plone.z3cform/trunk/plone/z3cform/base.txt
  A   plone.z3cform/trunk/plone/z3cform/configure.zcml
  A   plone.z3cform/trunk/plone/z3cform/converter.py
  A   plone.z3cform/trunk/plone/z3cform/crud/
  A   plone.z3cform/trunk/plone/z3cform/crud/README.txt
  A   plone.z3cform/trunk/plone/z3cform/crud/__init__.py
  A   plone.z3cform/trunk/plone/z3cform/crud/crud-master.pt
  A   plone.z3cform/trunk/plone/z3cform/crud/crud-row.pt
  A   plone.z3cform/trunk/plone/z3cform/crud/crud-table.pt
  A   plone.z3cform/trunk/plone/z3cform/crud/crud.py
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/README.txt
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/__init__.py
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/extensible.py
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/group.py
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/interfaces.py
  A   plone.z3cform/trunk/plone/z3cform/fieldsets/utils.py
  A   plone.z3cform/trunk/plone/z3cform/form.pt
  A   plone.z3cform/trunk/plone/z3cform/macros.pt
  A   plone.z3cform/trunk/plone/z3cform/overrides.zcml
  A   plone.z3cform/trunk/plone/z3cform/plone_skeleton.pt
  A   plone.z3cform/trunk/plone/z3cform/queryselect/
  A   plone.z3cform/trunk/plone/z3cform/queryselect/README.txt
  A   plone.z3cform/trunk/plone/z3cform/queryselect/__init__.py
  A   plone.z3cform/trunk/plone/z3cform/subform.pt
  A   plone.z3cform/trunk/plone/z3cform/templates.py
  A   plone.z3cform/trunk/plone/z3cform/templates.zcml
  A   plone.z3cform/trunk/plone/z3cform/testing.zcml
  A   plone.z3cform/trunk/plone/z3cform/tests.py
  A   plone.z3cform/trunk/plone/z3cform/widget.py
  A   plone.z3cform/trunk/plone/z3cform/wysiwyg/
  A   plone.z3cform/trunk/plone/z3cform/wysiwyg/README.txt
  A   plone.z3cform/trunk/plone/z3cform/wysiwyg/__init__.py
  A   plone.z3cform/trunk/plone/z3cform/wysiwyg/configure.zcml
  A   plone.z3cform/trunk/plone/z3cform/wysiwyg/widget.py
  A   plone.z3cform/trunk/plone/z3cform/wysiwyg/wysiwyg_input.pt
  A   plone.z3cform/trunk/plone/z3cform/z2.py
  A   plone.z3cform/trunk/setup.cfg
  A   plone.z3cform/trunk/setup.py

-=-
Added: plone.z3cform/trunk/README.txt
===================================================================
--- plone.z3cform/trunk/README.txt	                        (rev 0)
+++ plone.z3cform/trunk/README.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,45 @@
+=============
+plone.z3cform
+=============
+
+plone.z3cform is a library that allows use of z3c.form with Zope 2 and
+Plone.
+
+Quick start
+===========
+
+Tons of examples of using ``z3c.form`` can be found online.  This is a
+simple example of a form for Plone:
+
+  >>> from zope import interface, schema
+  >>> from z3c.form import form, field, button
+  >>> from plone.z3cform import base
+
+  >>> class MySchema(interface.Interface):
+  ...     age = schema.Int(title=u"Age")
+
+  >>> class MyForm(form.Form):
+  ...     fields = field.Fields(MySchema)
+  ...     ignoreContext = True # don't try to get data from context
+  ...
+  ...     @button.buttonAndHandler(u'Apply')
+  ...     def handleApply(self, action):
+  ...         data, errors = self.extractData()
+  ...         print data['age'] # ... or do stuff
+
+  >>> class MyView(base.FormWrapper):
+  ...     form = MyForm
+  ...     label = u"Please enter your age"
+
+Note that we're using ``base.FormWrapper`` as a base class for our
+browser view.  We can register the ``MyView`` view just like any other
+``browser:page``.
+
+Only the ``MyView`` bit is specific to ``plone.z3cform``. The rest is
+standard ``z3c.form`` stuff. For more details on the base FormWrapper
+class, see the ``plone.z3cform.base`` module.
+
+Please also refer to the `online documentation`_ for more details.
+
+.. _online documentation: http://plone.org/documentation/how-to/easy-forms-with-plone3
+


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

Added: plone.z3cform/trunk/bootstrap.py
===================================================================
--- plone.z3cform/trunk/bootstrap.py	                        (rev 0)
+++ plone.z3cform/trunk/bootstrap.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 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 21220 2008-06-20 15:57:16Z jfroche $
+"""
+
+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: plone.z3cform/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/buildout.cfg
===================================================================
--- plone.z3cform/trunk/buildout.cfg	                        (rev 0)
+++ plone.z3cform/trunk/buildout.cfg	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,66 @@
+[buildout]
+parts =
+    zope2
+    instance
+    plone
+    PILwoTK
+    test
+
+develop =
+    .
+
+find-links =
+    http://download.zope.org/distribution/
+    http://effbot.org/downloads
+
+[zope2]
+recipe = plone.recipe.zope2install
+url = ${plone:zope2-url}
+fake-zope-eggs = true
+# requiring zope.testing for z2testrunner
+# requiring zope.component for zope.i18n >= 3.4 which is needed for z3c.form
+skip-fake-eggs = zope.testing
+                 zope.component
+                 zope.i18n
+
+[instance]
+recipe = plone.recipe.zope2instance
+zope2-location = ${zope2:location}
+user = admin:admin
+verbose-security = on
+debug-mode = on
+products =
+    ${buildout:directory}/parts/plone
+
+eggs =
+    ${plone:eggs}
+    PILwoTK
+    zope.testing
+    plone.z3cform
+    zope.i18n
+
+zcml =
+    plone.z3cform
+
+[plone]
+recipe = plone.recipe.plone
+
+[PILwoTK]
+recipe = zc.recipe.egg
+find-links =
+    http://download.zope.org/distribution/
+
+[zopepy]
+recipe = zc.recipe.egg
+eggs = ${instance:eggs}
+interpreter = zopepy
+extra-paths = ${zope2:location}/lib/python
+scripts = zopepy
+
+[test]
+recipe = collective.recipe.z2testrunner
+zope2part = instance
+packages =
+    plone.z3cform
+extra-paths = ${buildout:directory}
+exit-with-status = true


Property changes on: plone.z3cform/trunk/buildout.cfg
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/docs/HISTORY.txt
===================================================================
--- plone.z3cform/trunk/docs/HISTORY.txt	                        (rev 0)
+++ plone.z3cform/trunk/docs/HISTORY.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,42 @@
+Changelog
+=========
+
+0.3 - unreleased
+----------------
+
+* Add German translation.
+  [saily]
+
+0.2 - 2008-06-20
+----------------
+
+* Fix usage of NumberDataConverter with zope.i18n >= 3.4 as the
+  previous test setup was partial and did not register all adapters
+  from z3c.form (some of them depends on zope >= 3.4)
+  [gotcha, jfroche]
+
+* More tests
+  [gotcha, jfroche]
+
+0.1 - 2008-05-21
+----------------
+
+* Provide and *register* default form and subform templates.  These
+  allow forms to be used with the style provided in this package
+  without having to declare ``form = ViewPageTemplateFile('form.pt')``.
+
+  This does not hinder you from overriding with your own ``form``
+  attribute like usual.  You can also still register a more
+  specialized IPageTemplate for your form.
+
+* Add custom FileUploadDataConverter that converts a Zope 2 FileUpload
+  object to a Zope 3 one before handing it to the original
+  implementation.  Also add support for different enctypes.
+  [skatja, nouri]
+
+* Added Archetypes reference selection widget (queryselect)
+  [malthe]
+  
+* Moved generic Zope 2 compatibility code for z3c.form and a few
+  goodies from Singing & Dancing into this new package.
+  [nouri]


Property changes on: plone.z3cform/trunk/docs/HISTORY.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/docs/INSTALL.txt
===================================================================
--- plone.z3cform/trunk/docs/INSTALL.txt	                        (rev 0)
+++ plone.z3cform/trunk/docs/INSTALL.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,2 @@
+Please refer to this online HOWTO for installation instructions for Plone:
+http://plone.org/documentation/how-to/easy-forms-with-plone3


Property changes on: plone.z3cform/trunk/docs/INSTALL.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/docs/LICENSE.txt
===================================================================
--- plone.z3cform/trunk/docs/LICENSE.txt	                        (rev 0)
+++ plone.z3cform/trunk/docs/LICENSE.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+   accompanying copyright notice, this list of conditions,
+   and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+   copyright notice, this list of conditions, and the
+   following disclaimer in the documentation and/or other
+   materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from the copyright
+   holders.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use
+   Servicemarks (sm) or Trademarks (tm) of the copyright
+   holders. Use of them is covered by separate agreement
+   with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS``
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.


Property changes on: plone.z3cform/trunk/docs/LICENSE.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/__init__.py
===================================================================
--- plone.z3cform/trunk/plone/__init__.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/__init__.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: plone.z3cform/trunk/plone/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/__init__.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/__init__.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/__init__.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,2 @@
+import zope.i18nmessageid
+MessageFactory = zope.i18nmessageid.MessageFactory('plone.z3cform')


Property changes on: plone.z3cform/trunk/plone/z3cform/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/add.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/add.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/add.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,74 @@
+from zope.app.container.interfaces import IContainerNamesContainer
+from zope.app.container.interfaces import INameChooser
+from zope.app.container.constraints import checkObject
+
+from Acquisition import aq_inner, aq_base
+from AccessControl import Unauthorized
+
+from Products.CMFCore.utils import getToolByName
+
+from z3c.form import form
+
+class AddForm(form.AddForm):
+    """CMF implementation of the add form
+    """
+    
+    contentName = None
+    
+    def add(self, object):
+        
+        container = aq_inner(self.context)
+        content = object
+        
+        name = self.contentName
+        chooser = INameChooser(container)
+
+        # Ensure that construction is allowed
+
+        portal_types = getToolByName(container, 'portal_types')
+        fti = portal_types.getTypeInfo(content)
+
+        if fti is not None:
+            # Check add permission
+            if not fti.isConstructionAllowed(container):
+                raise Unauthorized(u"You are not allowed to create a %d here" % fti.getId())
+            # Check allowable content types
+            if  getattr(aq_base(container), 'allowedContentTypes', None) is not None and \
+                    not fti.getId() in container.allowedContentTypes():
+                raise Unauthorized(u"You are not allowed to create a %d here" % fti.getId())
+
+        # check preconditions
+        checkObject(container, name, content)
+
+        if IContainerNamesContainer.providedBy(container):
+            # The container picks it's own names.
+            # We need to ask it to pick one.
+            name = chooser.chooseName(self.contentName or '', content)
+        else:
+            request = self.request
+            name = request.get('add_input_name', name)
+
+            if name is None:
+                name = chooser.chooseName(self.contentName or '', content)
+            elif name == '':
+                name = chooser.chooseName('', content)
+            else:
+                # Invoke the name chooser even when we have a
+                # name. It'll do useful things with it like converting
+                # the incoming unicode to an ASCII string.
+                name = chooser.chooseName(name, container)
+        
+        if not name:
+            raise ValueError("Cannot add content: name chooser did not provide a name")
+        
+        content.id = name
+        container._setObject(name, content)
+        content = container._getOb(name)
+        
+        if fti is not None:
+            fti._finishConstruction(content)
+            
+        self.contentName = name
+
+    def nextURL(self):
+        return "%s/%s/view" % (self.context.absolute_url(), self.contentName)
\ No newline at end of file


Property changes on: plone.z3cform/trunk/plone/z3cform/add.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/base.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/base.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/base.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,48 @@
+import z3c.form.interfaces
+from Products.Five import BrowserView
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+
+from plone.z3cform import z2
+
+class FormWrapper(BrowserView):
+    """Use this as a base class for your Five view and override the
+    'form' attribute with your z3c.form form class.  Your form will
+    then be rendered in the contents area of a Plone main template.
+    """
+    index = ViewPageTemplateFile('plone_skeleton.pt')
+    form = None # override this
+    request_layer = z3c.form.interfaces.IFormLayer
+    
+    def __call__(self):
+        """This method renders the outer skeleton template, which in
+        turn calls the 'contents' method below.
+
+        We use an indirection to 'self.index' here to allow users to
+        override the skeleton template through the 'browser' zcml
+        directive.
+        """
+        return self.index()
+
+    def contents(self):
+        """This is the method that'll call your form.  You don't
+        usually override this.
+        """
+        # A call to 'switch_on' is required before we can render
+        # z3c.forms within Zope 2.
+        z2.switch_on(self, request_layer=self.request_layer)
+        return self.render_form()
+
+    def render_form(self):
+        """This method returns the rendered z3c.form form.
+
+        Override this method if you need to pass a different context
+        to your form, or if you need to render a number of forms.
+        """
+        return self.form(self.context.aq_inner, self.request)()
+
+    def label(self):
+        """Override this method to use a different way of acquiring a
+        label or title for your page.  Overriding this with a simple
+        attribute works as well.
+        """
+        return self.form.label


Property changes on: plone.z3cform/trunk/plone/z3cform/base.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/base.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/base.txt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/base.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,97 @@
+Base
+====
+
+Testing the FormWrapper class::
+
+    >>> from zope.interface import alsoProvides
+    >>> from zope.publisher.browser import TestRequest
+    >>> from zope.annotation.interfaces import IAttributeAnnotatable
+    >>> from z3c.form.interfaces import IFormLayer
+
+    >>> def make_request(form={}):
+    ...     request = TestRequest()
+    ...     request.form.update(form)
+    ...     alsoProvides(request, IFormLayer)
+    ...     alsoProvides(request, IAttributeAnnotatable)
+    ...     return request
+
+Then we create a simple z3c form::
+
+    >>> from zope import interface, schema
+    >>> from z3c.form import form, field, button
+    >>> from plone.z3cform.base import FormWrapper
+
+    >>> class MySchema(interface.Interface):
+    ...     age = schema.Int(title=u"Age")
+
+    >>> from z3c.form.interfaces import IFieldsForm
+    >>> from zope.interface import implements
+    >>> class MyWrappedForm(form.Form):
+    ...     implements(IFieldsForm)
+    ...     fields = field.Fields(MySchema)
+    ...     ignoreContext = True # don't use context to get widget data
+    ...
+    ...     @button.buttonAndHandler(u'Apply')
+    ...     def handleApply(self, action):
+    ...         data, errors = self.extractData()
+    ...         print data['age'] # ... or do stuff
+
+    >>> class MyFormWrapper(FormWrapper):
+    ...     form = MyWrappedForm
+    ...     label = u"Please enter your age"
+
+    >>> from zope.component import provideAdapter
+    >>> from zope.publisher.interfaces.browser import IBrowserRequest
+    >>> from zope.interface import Interface
+
+    >>> provideAdapter(adapts=(Interface, IBrowserRequest),
+    ...                provides=Interface,
+    ...                factory=MyFormWrapper,
+    ...                name=u"test-form")
+
+Let's verify that worked:
+
+    >>> from zope.component import getMultiAdapter
+    >>> context = object()
+    >>> request = make_request()
+    >>> getMultiAdapter((context, request), name=u"test-form")
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    <MyFormWrapper object ...>
+    >>> del context, request
+
+Send bad data to the form:
+
+    >>> request = make_request(form={'form.widgets.age': '12.1'})
+    >>> from zope.interface import Interface, implements
+    >>> from Acquisition import Implicit
+    >>> class Bar(Implicit):
+    ...     implements(Interface)
+    >>> context = Bar()
+    >>> formWrapper = getMultiAdapter((context, request), name=u"test-form")
+    >>> form = formWrapper.form(context, request)
+    >>> form.update()
+    >>> data, errors = form.extractData()
+    >>> data
+    {}
+    >>> errors
+    (<ValueErrorViewSnippet for ValueError>,)
+
+And then send correct data to the form:
+
+    >>> request = make_request(form={'form.widgets.age': '12'})
+    >>> from zope.interface import Interface, implements
+    >>> from Acquisition import Implicit
+    >>> class Bar(Implicit):
+    ...     implements(Interface)
+    >>> context = Bar()
+    >>> formWrapper = getMultiAdapter((context, request), name=u"test-form")
+    >>> form = formWrapper.form(context, request)
+    >>> form.update()
+    >>> data, errors = form.extractData()
+    >>> data
+    {'age': 12}
+    >>> errors
+    ()
+
+
+


Property changes on: plone.z3cform/trunk/plone/z3cform/base.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/configure.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/configure.zcml	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/configure.zcml	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:i18n="http://namespaces.zope.org/i18n"
+    i18n_domain="plone.z3cform">
+
+  <include package="z3c.form" file="meta.zcml" />
+  <include package="z3c.form" />
+
+  <includeOverrides file="overrides.zcml" />
+  <include file="templates.zcml" />
+
+  <i18n:registerTranslations directory="locales"/>
+
+</configure>


Property changes on: plone.z3cform/trunk/plone/z3cform/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/converter.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/converter.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/converter.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,26 @@
+import cgi
+import zope.publisher.browser
+import ZPublisher.HTTPRequest
+
+import z3c.form.interfaces
+import z3c.form.converter
+
+class FileUploadDataConverter(z3c.form.converter.FileUploadDataConverter):
+    """Although ZPublisher's and zope.publisher's FileUpload
+    implementations are almost identical, ``FileUploadDataConverter``
+    makes an ``isinstance`` call that breaks duck-typing.
+
+    Therefore, we override the stock ``FileUploadDataConverter`` with
+    this little class that will do the right thing when a Zope 2
+    FileUpload object is received.
+    """
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        if isinstance(value, ZPublisher.HTTPRequest.FileUpload):
+            fieldstorage = cgi.FieldStorage()
+            fieldstorage.file = value
+            fieldstorage.headers = value.headers
+            fieldstorage.filename = value.filename
+            value = zope.publisher.browser.FileUpload(fieldstorage)
+
+        return super(FileUploadDataConverter, self).toFieldValue(value)


Property changes on: plone.z3cform/trunk/plone/z3cform/converter.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/crud/README.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/README.txt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/crud/README.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,390 @@
+Crud
+====
+
+This module gives you an abstract base class to make CRUD forms with.
+These forms give you by default a tabular view of the objects, where
+attributes of the object can be edited in-place.  Please refer to the
+``ICrudForm`` interface for more details.
+
+  >>> from plone.z3cform.crud import crud
+
+Setup
+-----
+
+  >>> from plone.z3cform.tests import setup_defaults
+  >>> setup_defaults()
+
+A simple form
+-------------
+
+First, let's define an interface and a class to play with:
+
+  >>> from zope import interface, schema
+  >>> class IPerson(interface.Interface) :
+  ...     name = schema.TextLine()
+  ...     age = schema.Int()
+
+  >>> class Person(object):
+  ...     interface.implements(IPerson)
+  ...     def __init__(self, name=None, age=None):
+  ...         self.name, self.age = name, age
+  ...     def __repr__(self):
+  ...         return "<Person with name=%r, age=%r>" % (self.name, self.age)
+
+For this test, we take the the name of our persons as keys in our
+storage:
+
+  >>> storage = {'Peter': Person(u'Peter', 16),
+  ...            'Martha': Person(u'Martha', 32)}
+
+Our simple form looks like this:
+
+  >>> class MyForm(crud.CrudForm):
+  ...     update_schema = IPerson
+  ... 
+  ...     def get_items(self):
+  ...         return sorted(storage.items(), key=lambda x: x[1].name)
+  ... 
+  ...     def add(self, data):
+  ...         person = Person(**data)
+  ...         storage[str(person.name)] = person
+  ...         return person
+  ... 
+  ...     def remove(self, (id, item)):
+  ...         del storage[id]
+
+This is all that we need to render a combined edit add form containing
+all our items:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> print MyForm(None, TestRequest())() \
+  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+  <div class="crud-form">...Martha...Peter...</div>
+
+Editing items with our form
+---------------------------
+
+Before we start with editing objects, let's log all events that the
+form fires for us:
+
+  >>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
+  >>> from plone.z3cform.tests import create_eventlog
+  >>> log = create_eventlog(IObjectModifiedEvent)
+
+  >>> request = TestRequest()
+  >>> request.form['crud-edit.Martha.widgets.select-empty-marker'] = u'1'
+  >>> request.form['crud-edit.Peter.widgets.select-empty-marker'] = u'1'
+  >>> request.form['crud-edit.Martha.widgets.name'] = u'Martha'
+  >>> request.form['crud-edit.Martha.widgets.age'] = 55
+  >>> request.form['crud-edit.Peter.widgets.name'] = u'Franz'
+  >>> request.form['crud-edit.Peter.widgets.age'] = 16
+  >>> request.form['crud-edit.form.buttons.edit'] = u'Apply changes'
+  >>> html = MyForm(None, request)()
+  >>> "Successfully updated" in html
+  True
+
+Two modified events should have been fired:
+
+  >>> event1, event2 = log.pop(), log.pop()
+  >>> storage['Peter'] in (event1.object, event2.object)
+  True
+  >>> storage['Martha'] in (event1.object, event2.object)
+  True
+  >>> log
+  []
+
+If we don't make any changes, we'll get a message that says so:
+
+  >>> html = MyForm(None, request)()
+  >>> "No changes made" in html
+  True
+  >>> log
+  []
+
+Now that we renamed Peter to Franz, it would be also nice to have
+Franz use 'Franz' as the id in the storage, wouldn't it?
+
+  >>> storage['Peter']
+  <Person with name=u'Franz', age=16>
+
+We can override the CrudForm's ``before_update`` method to perform a
+rename whenever the name of a person is changed:
+
+  >>> class MyRenamingForm(MyForm):
+  ...     def before_update(self, item, data):
+  ...         if data['name'] != item.name:
+  ...             del storage[item.name]
+  ...             storage[str(data['name'])] = item
+
+Let's rename Martha to Maria.  This will give her another key in our
+storage:
+
+  >>> request.form['crud-edit.Martha.widgets.name'] = u'Maria'
+  >>> html = MyRenamingForm(None, request)()
+  >>> "Successfully updated" in html
+  True
+  >>> log.pop().object == storage['Maria']
+  True
+  >>> log
+  []
+  >>> sorted(storage.keys())
+  ['Maria', 'Peter']
+
+Next, we'll submit the form for edit, but we'll make no changes.
+Instead, we'll select one time.  This shouldn't do anything, since we
+clicked the 'Apply changes' button:
+
+  >>> request.form['crud-edit.Maria.widgets.name'] = u'Maria'
+  >>> request.form['crud-edit.Maria.widgets.age'] = 55
+  >>> request.form['crud-edit.Maria.widgets.select'] = [u'selected']
+  >>> html = MyRenamingForm(None, request)()
+  >>> "No changes" in html
+  True
+  >>> log
+  []
+
+And what if we do have changes *and* click the checkbox?
+
+  >>> request.form['crud-edit.Maria.widgets.age'] = 50
+  >>> html = MyRenamingForm(None, request)()
+  >>> "Successfully updated" in html
+  True
+  >>> log.pop().object == storage['Maria']
+  True
+  >>> log
+  []
+
+If we omit the name, we'll get an error:
+
+  >>> request.form['crud-edit.Maria.widgets.name'] = u''
+  >>> html = MyRenamingForm(None, request)()
+  >>> "There were some errors" in html
+  True
+  >>> "Required input is missing" in html
+  True
+
+We expect an error message in the title cell of Maria:
+
+  >>> checkbox_pos = html.index('crud-edit.Maria.widgets.select-empty-marker')
+  >>> "Required input is missing" in html[checkbox_pos:]
+  True
+
+Delete an item with our form
+----------------------------
+
+We can delete an item by selecting the item we want to delete and
+clicking the "Delete" button:
+
+  >>> request = TestRequest()
+  >>> request.form['crud-edit.Peter.widgets.select'] = ['selected']
+  >>> request.form['crud-edit.form.buttons.delete'] = u'Delete'
+  >>> html = MyForm(None, request)()
+  >>> "Successfully deleted items" in html
+  True
+  >>> 'Franz' in html
+  False
+  >>> storage
+  {'Maria': <Person with name=u'Maria', age=50>}
+
+Add an item with our form
+-------------------------
+
+  >>> from zope.lifecycleevent.interfaces import IObjectCreatedEvent
+  >>> from plone.z3cform.tests import create_eventlog
+  >>> log = create_eventlog(IObjectCreatedEvent)
+
+  >>> request = TestRequest()
+  >>> request.form['crud-add.form.widgets.name'] = u'Daniel'
+  >>> request.form['crud-add.form.widgets.age'] = 28
+  >>> request.form['crud-add.form.buttons.add'] = u'Add'
+  >>> html = MyForm(None, request)()
+  >>> "Item added successfully" in html
+  True
+
+Added items should show up right away:
+
+  >>> "Daniel" in html
+  True
+
+  >>> storage['Daniel']
+  <Person with name=u'Daniel', age=28>
+  >>> log.pop().object == storage['Daniel']
+  True
+  >>> log
+  []
+
+Render some of the fields in view mode
+--------------------------------------
+
+We can implement in our form a ``view_schema`` attribute, which will
+then be used to view information in our form's table.  Let's say we
+wanted the name of our persons to be viewable only in the table:
+
+  >>> from z3c.form import field
+  >>> class MyAdvancedForm(MyForm):
+  ...     update_schema = field.Fields(IPerson).select('age')
+  ...     view_schema = field.Fields(IPerson).select('name')
+  ...     add_schema = IPerson
+
+  >>> print MyAdvancedForm(None, TestRequest())() \
+  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+  <div class="crud-form">...Daniel...Maria...</div>
+
+We can still edit the age of our Persons:
+
+  >>> request = TestRequest()
+  >>> request.form['crud-edit.Maria.widgets.age'] = 40
+  >>> request.form['crud-edit.Daniel.widgets.age'] = 35
+  >>> request.form['crud-edit.form.buttons.edit'] = u'Apply Changes'
+  >>> html = MyAdvancedForm(None, request)()
+  >>> "Successfully updated" in html
+  True
+
+  >>> storage['Maria'].age
+  40
+  >>> storage['Daniel'].age
+  35
+
+We can still add a Person using both name and age:
+
+  >>> request = TestRequest()
+  >>> request.form['crud-add.form.widgets.name'] = u'Thomas'
+  >>> request.form['crud-add.form.widgets.age'] = 28
+  >>> request.form['crud-add.form.buttons.add'] = u'Add'
+  >>> html = MyAdvancedForm(None, request)()
+  >>> "Item added successfully" in html
+  True
+  >>> len(storage)
+  3
+  >>> storage['Thomas']
+  <Person with name=u'Thomas', age=28>
+
+Our form can also contain links to our items:
+
+  >>> class MyAdvancedLinkingForm(MyAdvancedForm):
+  ...     def link(self, item, field):
+  ...         if field == 'name':
+  ...             return 'http://en.wikipedia.org/wiki/%s' % item.name
+
+  >>> print MyAdvancedLinkingForm(None, TestRequest())() \
+  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+  <div class="crud-form">...
+  ...<a href="http://en.wikipedia.org/wiki/Daniel"...
+  ...<a href="http://en.wikipedia.org/wiki/Maria"...
+  ...<a href="http://en.wikipedia.org/wiki/Thomas"...
+  </div>
+
+What if we wanted the name to be both used for linking to the item
+*and* for edit?  We can just include the title field twice:
+
+  >>> class MyAdvancedLinkingForm(MyAdvancedLinkingForm):
+  ...     update_schema = IPerson
+  ...     view_schema = field.Fields(IPerson).select('name')
+  ...     add_schema = IPerson
+
+  >>> print MyAdvancedLinkingForm(None, TestRequest())() \
+  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+  <div class="crud-form">...
+  ...<a href="http://en.wikipedia.org/wiki/Thomas"...Thomas...</a>...
+  </div>
+
+We can now change Thomas's name and see the change reflected in the
+Wikipedia link immediately:
+
+  >>> request = TestRequest()
+  >>> for name in 'Daniel', 'Maria', 'Thomas':
+  ...     request.form['crud-edit.%s.widgets.name' % name] = storage[name].name
+  ...     request.form['crud-edit.%s.widgets.age' % name] = storage[name].age
+  >>> request.form['crud-edit.Thomas.widgets.name'] = u'Dracula'
+  >>> request.form['crud-edit.form.buttons.edit'] = u'Apply Changes'
+
+  >>> print MyAdvancedLinkingForm(None, request)() \
+  ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+  <div class="crud-form">...
+  ...<a href="http://en.wikipedia.org/wiki/Dracula"...Dracula...</a>...
+  </div>
+  >>> storage['Thomas'].name = u'Thomas'
+
+Don't render one part
+---------------------
+
+What if we wanted our form to display only one part, that is, only the
+add *or* the edit form.  Our CrudForm can implement
+``editform_factory`` and ``addform_factory`` to override one or both
+forms.  Seeting one of these to ``crud.NullForm`` will make them
+disappear:
+
+  >>> class OnlyEditForm(MyForm):
+  ...     addform_factory = crud.NullForm
+  >>> html = OnlyEditForm(None, TestRequest())()
+  >>> 'Edit' in html, 'Add' in html
+  (True, False)
+
+  >>> class OnlyAddForm(MyForm):
+  ...     editform_factory = crud.NullForm
+  >>> html = OnlyAddForm(None, TestRequest())()
+  >>> 'Edit' in html, 'Add' in html
+  (False, True)
+
+Render only in view, and define own actions
+-------------------------------------------
+
+Sometimes you want to present a list of items, possibly in view mode
+only, and have the user select one or more of the items to perform
+some action with them.  We'll present a minimal example that does this
+here.
+
+We can simply leave the ``update_schema`` class attribute out (it
+defaults to ``None``).  Furthermore, we'll need to override the
+ediform_factory with our custom version that provides other buttons
+than the 'edit' and 'delete' ones:
+
+  >>> from pprint import pprint
+  >>> from z3c.form import button
+
+  >>> class MyEditForm(crud.EditForm):
+  ...     @button.buttonAndHandler(u'Capitalize', name='capitalize')
+  ...     def handle_capitalize(self, action):
+  ...         self.status = u"Please select items to capitalize first."
+  ...         selected = self.selected_items()
+  ...         if selected:
+  ...             self.status = u"Capitalized items"
+  ...             for id, item in selected:
+  ...                 item.name = item.name.upper()
+
+  >>> class MyCustomForm(crud.CrudForm):
+  ...     view_schema = IPerson
+  ...     editform_factory = MyEditForm
+  ...     addform_factory = crud.NullForm     # We don't want an add part.
+  ... 
+  ...     def get_items(self):
+  ...         return sorted(storage.items(), key=lambda x: x[1].name)
+
+  >>> request = TestRequest()
+  >>> html = MyCustomForm(None, TestRequest())()
+  >>> "Delete" in html, "Apply changes" in html, "Capitalize" in html
+  (False, False, True)
+  >>> pprint(storage)
+  {'Daniel': <Person with name=u'Daniel', age=35>,
+   'Maria': <Person with name=u'Maria', age=40>,
+   'Thomas': <Person with name=u'Thomas', age=28>}
+
+  >>> request.form['crud-edit.Thomas.widgets.select'] = ['selected']
+  >>> request.form['crud-edit.form.buttons.capitalize'] = u'Capitalize'
+  >>> html = MyCustomForm(None, request)()
+  >>> "Capitalized items" in html
+  True
+  >>> pprint(storage)
+  {'Daniel': <Person with name=u'Daniel', age=35>,
+   'Maria': <Person with name=u'Maria', age=40>,
+   'Thomas': <Person with name=u'THOMAS', age=28>}
+
+We *cannot* use any of the other buttons:
+
+  >>> del request.form['crud-edit.form.buttons.capitalize']
+  >>> request.form['crud-edit.form.buttons.delete'] = u'Delete'
+  >>> html = MyCustomForm(None, request)()
+  >>> "Successfully deleted items" in html
+  False
+  >>> 'Thomas' in storage
+  True


Property changes on: plone.z3cform/trunk/plone/z3cform/crud/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/crud/__init__.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/__init__.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/crud/__init__.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1 @@
+#


Property changes on: plone.z3cform/trunk/plone/z3cform/crud/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/crud/crud-master.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/crud-master.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/crud/crud-master.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,26 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone.z3cform"
+      tal:omit-tag="">
+
+  <div class="portalMessage"
+       tal:condition="view/status" tal:content="view/status">
+  </div>
+  
+  <p class="crud-description"
+       tal:condition="view/description" 
+       tal:content="view/description">
+  </p>
+  
+  <div tal:repeat="form view/subforms"
+       tal:content="structure form/render"
+       class="crud-form">
+  </div>
+
+  <div class="action" tal:repeat="action view/actions/values">
+    <input type="submit" tal:replace="structure action/render" />
+  </div>
+
+</html>


Property changes on: plone.z3cform/trunk/plone/z3cform/crud/crud-master.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/crud/crud-row.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/crud-row.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/crud/crud-row.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,25 @@
+<tal:block define="tups view/getCombinedWidgets;
+                   niceTitles view/getNiceTitles" 
+           repeat="tup tups">
+           
+  <td tal:define="error python:tup[0].error;
+                  idx repeat/tup/index"
+      tal:attributes="class python:'field' + (error and ' error' or '')">
+    <tal:block repeat="widget tup">
+
+    <a href=""
+       tal:define="link python:view.context.context.link(view.getContent(), widget.field.__name__)"
+       tal:omit-tag="python:widget.mode == 'input' or link is None"
+       tal:attributes="href link">
+
+      <span class="error" tal:omit-tag="not:error">
+        <div tal:condition="error" tal:replace="structure error/render" />
+        <input type="text" tal:replace="structure widget/render" />
+      </span>
+      
+    </a>
+    <br/>
+    </tal:block>
+
+  </td>
+</tal:block>


Property changes on: plone.z3cform/trunk/plone/z3cform/crud/crud-row.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/crud/crud-table.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/crud-table.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/crud/crud-table.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,56 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone.z3cform"
+      tal:omit-tag=""
+      tal:condition="view/subforms">
+
+  <h2 tal:condition="view/label | nothing"
+      tal:content="view/label">Form title</h2>
+
+  <div class="portalMessage"
+       tal:condition="view/status" tal:content="view/status">
+  </div>
+
+  <form action="." method="post" tal:attributes="action request/getURL">
+
+    <table class="crud-table" tal:define="rows view/subforms">
+        <thead tal:define="row1 python:len(rows) and rows[0] or None"
+               tal:condition="python:row1 is not None">
+          <tr>
+            <th tal:define="widgetsForTitles row1/getTitleWidgets;
+                            niceTitles row1/getNiceTitles"
+                tal:repeat="widget widgetsForTitles">
+              
+              <span tal:define="idx repeat/widget/index"
+                    tal:content="python: niceTitles[idx]"
+                    tal:attributes="title widget/field/description;
+                                    class python: 'header-' + niceTitles[idx]">
+                Field
+              </span>
+
+              <span class="fieldRequired"
+                    tal:condition="python:widget.required and widget.mode == 'input'"
+                    i18n:attributes="title">
+                *
+              </span>
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tal:block repeat="row view/subforms">
+            <a name="" tal:attributes="name string:edit-${row/content_id}" />
+            <tr tal:content="structure row/render"
+                tal:attributes="class python:repeat['row'].odd() and 'odd' or 'even'" />
+          </tal:block>
+        </tbody>
+    </table>
+
+    <div class="action" tal:repeat="action view/actions/values">
+      <input type="submit" tal:replace="structure action/render" />
+    </div>
+
+  </form>
+
+</html>


Property changes on: plone.z3cform/trunk/plone/z3cform/crud/crud-table.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/crud/crud.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/crud/crud.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/crud/crud.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,310 @@
+from ZODB.POSException import ConflictError
+from zope import interface
+import zope.event
+import zope.lifecycleevent
+from z3c.form import button
+from z3c.form import field
+from z3c.form import form
+import z3c.form.widget
+from z3c.form.interfaces import DISPLAY_MODE, INPUT_MODE, NOVALUE
+from zope.app.pagetemplate import viewpagetemplatefile
+
+from plone.z3cform.widget import singlecheckboxwidget_factory
+from plone.z3cform import MessageFactory as _
+
+
+class ICrudForm(interface.Interface):
+
+    update_schema = interface.Attribute(
+        "Editable part of the schema for use in the update form.")
+
+    view_schema = interface.Attribute(
+        "Viewable (only) part of the schema for use in the update form.")
+
+    add_schema = interface.Attribute(
+        "Schema for use in the add form; defaults to ``update_schema``.")
+
+    def get_items():
+        """Subclasses must a list of all items to edit.
+
+        This list contains tuples of the form ``(id, item)``, where
+        the id is a unique identifiers to the items.  The items must
+        be adaptable to the schema returned by ``update_schema`` and
+        ``view_schema`` methods.
+        """
+
+    def add(data):
+        """Subclasses must implement this method to create an item for
+        the given `data` *and* add it to a container, and return it.
+
+        The `data` mapping corresponds to the schema returned by
+        `add_schema`.
+        """
+
+    def remove((id, item)):
+        """Subclasses must implement this method to remove the given
+        item from the site.
+
+        It's left to the implementing class to notify of
+        ``zope.app.container.contained.ObjectRemovedEvent``.
+        """
+
+    def before_update(item, data):
+        """A hook that gets called before an item is updated.
+        """
+
+    def link(item, field):
+        """Return a URL for this item's field or None.
+        """
+
+class AbstractCrudForm(object):
+    """The AbstractCrudForm is not a form but implements methods
+    necessary to comply with the ``ICrudForm`` interface:
+    
+      >>> from zope.interface.verify import verifyClass
+      >>> verifyClass(ICrudForm, AbstractCrudForm)
+      True
+    """
+    interface.implements(ICrudForm)
+
+    update_schema = None
+    view_schema = None
+
+    @property
+    def add_schema(self):
+        return self.update_schema
+
+    def get_items(self):
+        raise NotImplementedError
+
+    def add(self, data):
+        raise NotImplementedError
+
+    def remove(self, (id, item)):
+        raise NotImplementedError
+
+    def before_update(self, item, data):
+        pass
+
+    def link(self, item, field):
+        return None
+
+class EditSubForm(form.EditForm):
+    template = viewpagetemplatefile.ViewPageTemplateFile('crud-row.pt')
+
+    @property
+    def prefix(self):
+        return 'crud-edit.%s.' % self.content_id
+
+    # These are set by the parent form
+    content = None
+    content_id = None
+
+    @property
+    def fields(self):
+        fields = field.Fields(self._select_field())
+
+        crud_form = self.context.context
+
+        update_schema = crud_form.update_schema
+        if update_schema is not None:
+            fields += field.Fields(update_schema)
+
+        view_schema = crud_form.view_schema
+        if view_schema is not None:
+            view_fields = field.Fields(view_schema)
+            for f in view_fields.values():
+                f.mode = DISPLAY_MODE
+                # This is to allow a field to appear in both view
+                # and edit mode at the same time:
+                if not f.__name__.startswith('view_'):
+                    f.__name__ = 'view_' + f.__name__
+            fields += view_fields
+            
+        return fields
+
+    def getContent(self):
+        return self.content
+
+    def _select_field(self):
+        select_field = field.Field(
+            zope.schema.Bool(__name__='select',
+                             required=False,
+                             title=_(u'select')))
+        select_field.widgetFactory[INPUT_MODE] = singlecheckboxwidget_factory
+        return select_field
+        
+    # XXX: The three following methods, 'getCombinedWidgets',
+    # 'getTitleWidgets', and 'getNiceTitles' are hacks to support the
+    # page template.  Let's get rid of them.
+    def getCombinedWidgets(self):
+        """Returns pairs of widgets to improve layout"""
+        widgets = self.widgets.items()
+        combined = []
+        seen = set()
+        for name, widget in list(widgets):
+            if widget.mode == INPUT_MODE:
+                view_widget = self.widgets.get('view_%s' % name)
+                if view_widget is not None:
+                    combined.append((widget, view_widget))
+                    seen.add(view_widget)
+                else:
+                    combined.append((widget,))
+            else:
+                if widget not in seen:
+                    combined.append((widget,))
+        return combined
+             
+    def getTitleWidgets(self):
+        combinedWidgets = self.getCombinedWidgets()
+        widgetsForTitles = [w[0] for w in combinedWidgets]
+        return widgetsForTitles
+
+    def getNiceTitles(self):
+        widgetsForTitles = self.getTitleWidgets()        
+        freakList = []
+        for item in widgetsForTitles:
+            freakList.append(item.field.title)
+        return freakList
+        
+class EditForm(form.Form):
+    label = _(u"Edit")
+    template = viewpagetemplatefile.ViewPageTemplateFile('crud-table.pt')
+        
+    @property
+    def prefix(self):
+        parent_prefix = getattr(self.context, 'prefix', '')
+        return 'crud-edit.' + parent_prefix
+
+    def update(self):
+        self._update_subforms()
+        super(EditForm, self).update()
+    
+    def _update_subforms(self):
+        self.subforms = []
+        for id, item in self.context.get_items():
+            subform = EditSubForm(self, self.request)
+            subform.content = item
+            subform.content_id = id
+            subform.update()
+            self.subforms.append(subform)
+
+    @button.buttonAndHandler(_('Apply changes'), name='edit')
+    def handle_edit(self, action):
+        success = _(u"Successfully updated")
+        partly_success = _(u"Some of your changes could not be applied.")
+        status = no_changes = _(u"No changes made.")
+        for subform in self.subforms:
+            # With the ``extractData()`` call, validation will occur,
+            # and errors will be stored on the widgets amongst other
+            # places.  After this we have to be extra careful not to
+            # call (as in ``__call__``) the subform again, since
+            # that'll remove the errors again.  With the results that
+            # no changes are applied but also no validation error is
+            # shown.
+            data, errors = subform.extractData()
+            if errors:
+                if status is no_changes:
+                    status = subform.formErrorsMessage
+                elif status is success:
+                    status = partly_success
+                continue
+            del data['select']
+            self.context.before_update(subform.content, data)
+            changes = subform.applyChanges(data)
+            if changes:
+                if status is no_changes:
+                    status = success
+                elif status is subform.formErrorsMessage:
+                    status = partly_success
+
+                # If there were changes, we'll update the view widgets
+                # again, so that they'll actually display the changes
+                [w.update() for w in subform.widgets.values()
+                 if w.mode == DISPLAY_MODE]
+        self.status = status
+
+    @button.buttonAndHandler(_('Delete'), name='delete')
+    def handle_delete(self, action):
+        selected = self.selected_items()
+        if selected:
+            self.status = _(u"Successfully deleted items.")
+            for id, item in selected:
+                try:
+                    self.context.remove((id, item))
+                except ConflictError:
+                    raise
+                except:
+                    # In case an exception is raised, we'll catch it
+                    # and notify the user; in general, this is
+                    # unexpected behavior:
+                    self.status = _(u'Unable to remove one or more items.')
+                    break
+        
+            # We changed the amount of entries, so we update the subforms again.
+            self._update_subforms()
+        else:
+            self.status = _(u"Please select items to delete.")
+
+    def selected_items(self):
+        tuples = []
+        for subform in self.subforms:
+            data = subform.widgets['select'].extract()
+            if not data or data is NOVALUE:
+                continue
+            else:
+                tuples.append((subform.content_id, subform.content))
+        return tuples
+
+class AddForm(form.Form):
+    label = _(u"Add")
+    template = viewpagetemplatefile.ViewPageTemplateFile('../form.pt')
+    ignoreContext = True
+    ignoreRequest = True
+
+    @property
+    def prefix(self):
+        parent_prefix = getattr(self.context, 'prefix', '')
+        return 'crud-add.' + parent_prefix
+
+    @property
+    def fields(self):
+        return field.Fields(self.context.add_schema)
+
+    @button.buttonAndHandler(_('Add'), name='add')
+    def handle_add(self, action):
+        data, errors = self.extractData()
+        if errors:
+            self.status = form.AddForm.formErrorsMessage
+            return
+        item = self.context.add(data)
+        zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(item))
+        self.status = _(u"Item added successfully.")
+
+class NullForm(object):
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def update(self):
+        pass
+
+    def render(self):
+        return ''
+    __call__ = render
+
+class CrudForm(AbstractCrudForm, form.Form):
+    template = viewpagetemplatefile.ViewPageTemplateFile('crud-master.pt')
+    description = u''
+
+    editform_factory = EditForm
+    addform_factory = AddForm
+
+    def update(self):
+        super(CrudForm, self).update()
+
+        addform = self.addform_factory(self, self.request)
+        editform = self.editform_factory(self, self.request)
+        addform.update()
+        editform.update()
+        self.subforms = [editform, addform]


Property changes on: plone.z3cform/trunk/plone/z3cform/crud/crud.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/fieldsets/README.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/fieldsets/README.txt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/fieldsets/README.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,144 @@
+Fieldsets and extensible forms
+==============================
+
+The ``fieldsets`` package provides support for groups/fieldsets and other
+modifications via "extender" adapters. The idea is that a third party
+component can modify the fields in the form and the way that they are grouped
+and ordered.
+
+This support relies on a mixin class, which is itself a subclass of 
+z3c.form's GroupForm.
+
+    >>> from plone.z3cform.fieldsets import group, extensible
+
+To use this, you have to mix it into another form as the *first* base class:
+
+  >>> from zope.annotation import IAttributeAnnotatable
+  >>> from z3c.form import form, field, tests, group
+  >>> from zope.interface import Interface, implements
+  >>> from zope import schema
+
+  >>> class ITest(Interface):
+  ...     title = schema.TextLine(title=u"Title")
+
+  >>> class Test(object):
+  ...     implements(ITest, IAttributeAnnotatable)
+  ...     title = u""
+
+  >>> class TestForm(extensible.ExtensibleForm, form.Form):
+  ...     fields = field.Fields(ITest)
+
+Here, note the order of the base classes. Also note that we use an ordinary
+set of fields. This known as the default fieldset.
+
+This form should work as-is, i.e. we can update it:
+
+  >>> from z3c.form.testing import TestRequest
+
+  >>> request = TestRequest()
+  >>> context = Test()
+
+  >>> form = TestForm(context, request)
+  >>> form.update()
+  >>> _ = form.render()
+
+Now let's register an adapter that adds two new fields - one in the
+default fieldset as the first field, and one in a new group. To do this,
+we only need to register a named multi-adapter. However, we can use a 
+convenience base class to make it easier to manipulate the fields of the
+form.
+
+  >>> from plone.z3cform.fieldsets.interfaces import IFormExtender
+  >>> from zope.component import adapts, provideAdapter
+  
+  >>> class IExtraBehavior(Interface):
+  ...     foo = schema.TextLine(title=u"Foo")
+  ...     bar = schema.TextLine(title=u"Bar")
+  ...     baz = schema.TextLine(title=u"Baz")
+  ...     fub = schema.TextLine(title=u"Fub")
+  
+One plausible implementation is to use an annotation to store this data.
+
+  >>> from zope.annotation import factory
+  >>> from zope.annotation.attribute import AttributeAnnotations
+  >>> from persistent import Persistent
+  >>> class ExtraBehavior(Persistent):
+  ...     implements(IExtraBehavior)
+  ...     adapts(Test)
+  ...     
+  ...     foo = u""
+  ...     bar = u""
+  ...     baz = u""
+  ...     fub = u""
+  
+  >>> ExtraBehavior = factory(ExtraBehavior)
+  >>> provideAdapter(ExtraBehavior)
+  >>> provideAdapter(AttributeAnnotations)
+ 
+We can now write the extender. The base class gives us some helper methods
+to add, remove and move fields. Here, we do a bit of unnecessary work just
+to exercise these methods.
+ 
+  >>> class ExtraBehaviorExtender(extensible.FormExtender):
+  ...     adapts(Test, TestRequest, TestForm) # context, request, form
+  ...     
+  ...     def __init__(self, context, request, form):
+  ...         self.context = context
+  ...         self.request = request
+  ...         self.form = form
+  ...     
+  ...     def update(self):
+  ...         # Add all fields from an interface
+  ...         self.add(IExtraBehavior, prefix="extra")
+  ...         
+  ...         # Remove the fub field
+  ...         self.remove('fub', prefix="extra")
+  ...         
+  ...         all_fields = field.Fields(IExtraBehavior, prefix="extra")
+  ...         
+  ...         # Insert fub again, this time at the top
+  ...         self.add(all_fields.select("fub", prefix="extra"), index=0)
+  ...         
+  ...         # Move 'baz' above 'fub'
+  ...         self.move('baz', before='fub', prefix='extra', relative_prefix='extra')
+  ...         
+  ...         # Move 'foo' after 'bar' - here we specify prefix manually
+  ...         self.move('foo', after='extra.bar', prefix='extra')
+  ...         
+  ...         # Remove 'bar' and re-insert into a new group
+  ...         self.remove('bar', prefix='extra')
+  ...         self.add(all_fields.select('bar', prefix='extra'), group='Second')
+  ...         
+  ...         # Move 'baz' after 'bar'. This means it also moves gropu.
+  ...         self.move('extra.baz', after='extra.bar')
+
+  
+  >>> provideAdapter(factory=ExtraBehaviorExtender, name=u"test.extender")
+    
+With this in place, let's update the form once again.
+
+  >>> form = TestForm(context, request)
+  >>> form.update()
+
+At this point, we should have a set of default fields that represent the
+ones set in the adapter.
+
+  >>> form.fields.keys()
+  ['extra.fub', 'title', 'extra.foo']
+  
+And we should have one group created by the group factory:
+
+  >>> form.groups # doctest: +ELLIPSIS
+  (<plone.z3cform.fieldsets.group.Group object at ...>,)
+
+Note that the created group is of a subtype of the standard z3c.form group,
+which has got support for a separate label and description as well as a 
+canonical name.
+
+  >>> isinstance(form.groups[0], group.Group)
+  True
+
+This should have the group fields provided by the adapter as well.
+
+  >>> form.groups[0].fields.keys()
+  ['extra.bar', 'extra.baz']
\ No newline at end of file


Property changes on: plone.z3cform/trunk/plone/z3cform/fieldsets/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/fieldsets/__init__.py
===================================================================


Property changes on: plone.z3cform/trunk/plone/z3cform/fieldsets/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/fieldsets/extensible.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/fieldsets/extensible.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/fieldsets/extensible.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,64 @@
+from zope.interface import implements
+from zope.component import getAdapters
+
+from plone.z3cform.fieldsets.interfaces import IExtensibleForm
+from plone.z3cform.fieldsets.interfaces import IFormExtender
+
+from z3c.form.group import GroupForm
+
+from plone.z3cform.fieldsets import utils
+
+from plone.z3cform import MessageFactory as _
+
+def order_key(adapter_tuple):
+    return adapter_tuple[1].order
+
+class FormExtender(object):
+    """Base class for IFormExtender adapters with convenience APIs
+    """
+    implements(IFormExtender)
+    
+    # Change this to prioritise
+    order = 0
+    
+    def __init__(self, context, request, form):
+        self.context = context
+        self.request = request
+        self.form = form
+    
+    def add(self, *args, **kwargs):
+        """Add one or more fields. Keyword argument 'index' can be used to
+        specify an index to insert at. Keyword argument 'group' can be used
+        to specify the label of a group, which will be found and used or
+        created if it doesn't exist.
+        """
+        
+        utils.add(self.form, *args, **kwargs)
+    
+    def remove(self, field_name, prefix=None):
+        """Get rid of a field. The omitted field will be returned.
+        """
+        
+        return utils.remove(self.form, field_name, prefix=prefix)
+    
+    def move(self, field_name, before=None, after=None, prefix=None, relative_prefix=None):
+        """Move the field with the given name before or after another field.
+        """
+        
+        utils.move(self.form, field_name, before=before, after=after,
+                    prefix=prefix, relative_prefix=relative_prefix)
+
+class ExtensibleForm(GroupForm):
+    implements(IExtensibleForm)
+    
+    groups = []
+    default_fieldset_label = _(u"Default")
+    
+    def update(self):
+        self.updateFields()
+        super(ExtensibleForm, self).update()
+        
+    def updateFields(self):
+        extenders = getAdapters((self.context, self.request, self), IFormExtender)
+        for name, extender in sorted(extenders, key=order_key):
+            extender.update()
\ No newline at end of file


Property changes on: plone.z3cform/trunk/plone/z3cform/fieldsets/extensible.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/fieldsets/group.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/fieldsets/group.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/fieldsets/group.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,31 @@
+from zope.interface import implements
+
+from plone.z3cform.fieldsets.interfaces import IGroupFactory
+from plone.z3cform.fieldsets.interfaces import IDescriptiveGroup
+
+from z3c.form import group
+
+class Group(group.Group):
+    implements(IDescriptiveGroup)
+    
+    __name__ = u""
+    label = u""
+    description = u""
+
+class GroupFactory(object):
+    implements(IGroupFactory)
+    
+    def __init__(self, __name__, fields, label=None, description=None):
+        self.__name__ = __name__
+        self.fields = fields
+        
+        self.label = label or __name__
+        self.description = description
+        
+    def __call__(self, context, request, parentForm):
+        g = Group(context, request, parentForm)
+        g.__name__ = self.__name__
+        g.label = self.label
+        g.description = self.description
+        g.fields = self.fields
+        return g
\ No newline at end of file


Property changes on: plone.z3cform/trunk/plone/z3cform/fieldsets/group.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/fieldsets/interfaces.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/fieldsets/interfaces.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/fieldsets/interfaces.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,67 @@
+from zope.interface import Interface
+from zope import schema
+
+from z3c.form.interfaces import IFields, IGroup, IGroupForm
+
+class IFormExtender(Interface):
+    """A component that can add, modify, sort and group fields in a form.
+    
+    This should be a named multi adapter from (context, request, form).
+    """
+    
+    order = schema.Int(title=u"Order",
+                       description=u"Use this property to order the sorter. " +
+                                   u"Low numbers are executed before high ones.",
+                       required=True)
+    
+    def update():
+        """Modify the form in place. Supported operations include:
+        
+         - modify the 'fields' object to change the default fieldset
+         - modify the 'groups' list to add, remove or reorder fieldsets
+         - modify the 'fields' property of a given group
+        """
+
+class IDescriptiveGroup(IGroup):
+    """Extension to z3c.form's Group class that can separate out a name,
+    a label and a description.
+    """
+    
+    __name__ = schema.TextLine(title=u"Name of this group")
+    
+    label = schema.TextLine(title=u"Fieldset title",
+                            description=u"The __name__ will be used if this is not given",
+                            required=False)
+                            
+    description = schema.Text(title=u"Fieldset description",
+                              required=False)
+
+class IGroupFactory(Interface):
+    """An object that can be used to create a z3c.form.group.Group.
+    """
+    
+    __name__ = schema.TextLine(title=u"Name of this group")
+    
+    label = schema.TextLine(title=u"Fieldset title",
+                            description=u"The __name__ will be used if this is not given",
+                            required=False)
+                            
+    description = schema.Text(title=u"Fieldset description",
+                              required=False)
+    
+    fields = schema.Object(title=u"Fields in this form", schema=IFields)
+
+class IExtensibleForm(Interface):
+    """A special version of the IGroupForm that is extensible via
+    IFormExtender adapters.
+    """
+
+    groups = schema.List(title=u'Groups',
+                         value_type=schema.Object(title=u"Group", schema=IGroupFactory))
+
+    default_fieldset_label = schema.TextLine(title=u"Label of the default fieldset")
+    
+    def updateFields():
+        """Called during form update to allow updating of self.fields
+        and self.groups.
+        """
\ No newline at end of file


Property changes on: plone.z3cform/trunk/plone/z3cform/fieldsets/interfaces.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/fieldsets/utils.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/fieldsets/utils.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/fieldsets/utils.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,108 @@
+from z3c.form.field import Fields
+from z3c.form.util import expandPrefix
+
+from plone.z3cform.fieldsets.group import GroupFactory
+
+def add(form, *args, **kwargs):
+    """Add one or more fields. Keyword argument 'index' can be used to
+    specify an index to insert at. Keyword argument 'group' can be used
+    to specify the label of a group, which will be found and used or
+    created if it doesn't exist.
+    """
+    
+    index = kwargs.pop('index', None)
+    group = kwargs.pop('group', None)
+    
+    new_fields = Fields(*args, **kwargs)
+
+    if not group or isinstance(group, basestring):
+        source = find_source(form, group=group)
+    else:
+        source = group
+
+    if source is None and group:
+        source = GroupFactory(group, new_fields)
+        form.groups.append(source)
+    else:        
+        if index is None or index >= len(source.fields):
+            source.fields += new_fields
+        else:
+            field_names = source.fields.keys()
+            source.fields = source.fields.select(*field_names[:index]) + \
+                            new_fields + \
+                            source.fields.select(*field_names[index:])
+
+def remove(form, field_name, prefix=None):
+    """Get rid of a field. The omitted field will be returned.
+    """
+    
+    if prefix:
+        field_name = expandPrefix(prefix) + field_name
+    
+    if field_name in form.fields:
+        field = form.fields[field_name]
+        form.fields = form.fields.omit(field_name)
+        return field
+    else:
+        for group in form.groups:
+            if field_name in group.fields:
+                field = group.fields[field_name]
+                group.fields = group.fields.omit(field_name)
+                return field
+
+def move(form, field_name, before=None, after=None, prefix=None, relative_prefix=None):
+    """Move the field with the given name before or after another field.
+    """
+    
+    if prefix:
+        field_name = expandPrefix(prefix) + field_name
+    
+    if before and after:
+        raise ValueError(u"Only one of 'before' or 'after' is allowed")
+    
+    offset = 0
+    if after:
+        offset = 1
+    
+    relative = before or after
+    if relative_prefix:
+        relative = expandPrefix(relative_prefix) + relative
+    
+    if field_name not in form.fields:
+        raise KeyError("Field %s not found" % field_name)
+    
+    if relative not in form.fields:
+        found = False
+        for group in form.groups:
+            if relative in group.fields:
+                found = True
+                break
+        if not found:
+            raise KeyError("Field %s not found" % relative)
+    
+    field = remove(form, field_name)
+    
+    group = None
+    index = None
+    
+    if relative in form.fields:
+        index = form.fields.keys().index(relative)
+    else:
+        for group in form.groups:
+            if relative in group.fields:
+                index = group.fields.keys().index(relative)
+                break
+    
+    if index is None:
+        raise KeyError("Field %s not found" % relative)
+    
+    add(form, field, group=group, index=index+offset)
+
+def find_source(form, group=None):
+    if group is not None:
+        group_factory = [g for g in form.groups if g.__name__ == group]
+        if len(group_factory) >= 1:
+            return group_factory[0]
+        else:
+            return None
+    return form
\ No newline at end of file


Property changes on: plone.z3cform/trunk/plone/z3cform/fieldsets/utils.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/form.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/form.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/form.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1 @@
+<metal:use use-macro="context/@@ploneform-macros/form" />


Property changes on: plone.z3cform/trunk/plone/z3cform/form.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/macros.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/macros.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/macros.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,168 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="plone.z3cform"
+      tal:omit-tag="">
+
+  <head></head>
+
+  <body>
+
+    <div class="form" metal:define-macro="form">
+
+      <h3 tal:condition="view/label | nothing">
+          <span tal:replace="view/label">Form title</span>:</h3>
+
+      <metal:define define-macro="titlelessform">
+        
+        <tal:status define="status view/status" condition="status">
+            <dl class="portalMessage error"
+                tal:condition="view/widgets/errors">
+                <dt i18n:translate="">
+                    Error
+                </dt>
+                <dd tal:content="status" />
+            </dl>
+ 
+            <dl class="portalMessage info"
+                tal:condition="not: view/widgets/errors">
+                <dt i18n:translate="">
+                    Info
+                </dt>
+                <dd tal:content="status" />
+            </dl>
+        </tal:status>
+
+        <tal:comment condition="nothing">
+          <ul tal:define="errors view/widgets/errors"
+              tal:condition="errors">
+            <li tal:repeat="error errors">
+              <tal:block replace="error/widget/label"/>:
+              <tal:block replace="structure error/render" />
+            </li>
+          </ul>
+        </tal:comment>
+        
+        <p  class="discreet"
+            tal:define="description view/description | nothing"
+            tal:content="description">
+                Description
+        </p>
+
+        <form class="rowlike enableUnloadProtection" action="." method="post"
+              tal:define="groups view/groups | nothing;
+                          form_name view/form_name | nothing;
+                          default_fieldset_label view/default_fieldset_label | form_name;
+                          has_groups python:bool(groups);
+                          show_default_label python:has_groups and default_fieldset_label or form_name;"
+              tal:attributes="action request/getURL; enctype view/enctype;
+                              class python: has_groups and 'rowlike enableUnloadProtection enableFormTabbing' or default">
+
+          <input type="hidden"
+               name="fieldset.current"
+               tal:condition="has_groups"
+               tal:attributes="value request/fieldset.current | string:" />
+
+          <!-- Default fieldset -->
+
+          <metal:define define-macro="fields">
+            
+            <fieldset id="fieldset-default" tal:omit-tag="not:show_default_label">
+                
+                <legend tal:condition="show_default_label"
+                        tal:attributes="id string:fieldsetlegend-default"
+                        tal:content="default_fieldset_label">Form name</legend>
+            
+                <metal:define define-macro="widget_rendering">
+                    <tal:widgets repeat="widget view/widgets/values">
+                      <div class="row horizontal"
+                           tal:define="hidden python:widget.mode == 'hidden'"
+                           tal:omit-tag="hidden">
+                
+                        <metal:field define-macro="field">
+                          <div class="field"
+                               tal:define="error widget/error"
+                               tal:attributes="class python:'field' + (error and ' error' or '')">
+                    
+                            <label for="" class="horizontal"
+                                   tal:attributes="for widget/id"
+                                   tal:condition="not:hidden">
+                              <span i18n:translate=""
+                                    tal:content="widget/label">label</span>
+                            </label>
+                    
+                            <span class="fieldRequired horizontal" title="Required"
+                                  tal:condition="python:widget.required and not hidden"
+                                  i18n:translate="label_required"
+                                  i18n:attributes="title title_required;">
+                              (Required)
+                            </span>
+                    
+                            <div class="formHelp"
+                                 tal:define="description widget/field/description"
+                                 i18n:translate=""
+                                 tal:content="description"
+                                 tal:condition="python:description and not hidden"
+                                 >field description</div>
+                    
+                            <div tal:condition="error"
+                                 tal:content="error/message">
+                              Error
+                            </div>
+                    
+                            <div class="widget horizontal">
+                              <input type="text" tal:replace="structure widget/render" />
+                            </div>  
+                          </div>
+                        </metal:field>
+
+                      </div>
+                    </tal:widgets>
+                </metal:define>
+                
+            </fieldset>
+            
+            <!-- Secondary fieldsets -->
+
+            <fieldset
+                tal:condition="has_groups"
+                tal:repeat="group groups"
+                tal:attributes="id string:fieldset-${repeat/group/index}">
+                
+                    <legend tal:define="form_name group/label"
+                            tal:condition="form_name"
+                            tal:attributes="id string:fieldsetlegend-${repeat/group/index}"
+                            tal:content="form_name">Form name</legend>
+
+                    <p i18n:translate=""
+                       tal:define="group_description group/description|nothing"
+                       tal:condition="group_description"
+                       tal:content="group_description">
+                        Description
+                    </p>
+
+                    <tal:block define="view nocall:group">
+                        <metal:block use-macro="context/@@ploneform-macros/widget_rendering" />
+                    </tal:block>
+
+            </fieldset>
+          </metal:define>
+
+          <metal:define define-macro="actions">
+            
+            <div class="formFields" tal:condition="view/actions/values|nothing">
+                <tal:block repeat="action view/actions/values">
+                  <input type="submit"
+                    tal:replace="structure action/render" />
+                </tal:block>
+            </div>
+          </metal:define>
+
+        </form>
+      </metal:define>
+      </div>
+   
+    
+  </body>
+</html>


Property changes on: plone.z3cform/trunk/plone/z3cform/macros.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/overrides.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/overrides.zcml	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/overrides.zcml	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,7 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <adapter
+      factory=".converter.FileUploadDataConverter"
+      />
+
+</configure>


Property changes on: plone.z3cform/trunk/plone/z3cform/overrides.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/plone_skeleton.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/plone_skeleton.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/plone_skeleton.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,24 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      lang="en"
+      metal:use-macro="here/main_template/macros/master"
+      i18n:domain="plone.z3cform">
+<body>
+
+  <metal:main fill-slot="main">
+    <tal:main-macro metal:define-macro="main">
+      
+      <h1 class="documentFirstHeading" tal:content="view/label">Title</h1>
+      
+      <div id="skel-contents">
+	<span tal:replace="structure view/contents" />
+      </div>
+      
+    </tal:main-macro>
+</metal:main>
+
+</body>
+</html>
+


Property changes on: plone.z3cform/trunk/plone/z3cform/plone_skeleton.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/queryselect/README.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/queryselect/README.txt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/queryselect/README.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,34 @@
+Query select widget
+===================
+
+The ``queryselect`` module provides a query source compatible with
+``z3c.formwidget.query`` which combines to a selection field that can
+be queried.
+
+The native value type for the widget is Archetypes UID collections.
+The default implementation will simply search using the
+``SearchableText`` index in the portal catalog.
+
+This is how your form schema could look like:
+
+  >>> from zope import interface, schema
+  >>> from plone.z3cform.queryselect import ArchetypesContentSourceBinder
+  
+  >>> class ISelection(interface.Interface):
+  ...     items = schema.Set(
+  ...         title=u"Selection",
+  ...         description=u"Search for content",
+  ...         value_type=schema.Choice(
+  ...             source=ArchetypesContentSourceBinder()))
+
+Optionally, instead of storing Archetypes UIDs, you can choose to use
+``persistent.wref``, i.e. weak references, instead of UIDs:
+
+  >>> from plone.z3cform.queryselect import uid2wref
+  >>> factory = uid2wref(ISelection['items'])
+
+To store weak references instead of UIDs you would register such a
+factory as a component adapting the context.  The factory
+automatically provides the interface which defines the field.
+(XXX: Please rewrite this paragraph.)
+


Property changes on: plone.z3cform/trunk/plone/z3cform/queryselect/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/queryselect/__init__.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/queryselect/__init__.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/queryselect/__init__.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,71 @@
+from zope import interface
+from zope import schema
+
+import zope.schema.interfaces
+import zope.schema.vocabulary
+
+import z3c.formwidget.query.interfaces
+import persistent.wref
+
+from Products.CMFCore import utils as cmfutils
+
+class ArchetypesContentSource(object):
+    interface.implements(z3c.formwidget.query.interfaces.IQuerySource)
+
+    def __init__(self, context):
+        self.context = context
+
+    def __contains__(self, uid):
+        """Verify the item exists."""
+        
+        return bool(self.catalog(uid=uid))
+    
+    def __iter__(self):
+        return [].__iter__()
+
+    @property
+    def catalog(self):
+        return cmfutils.getToolByName(self.context, 'portal_catalog')
+        
+    def getTermByToken(self, token):
+        uid = token
+        brain = self.catalog(UID=uid)[0]
+        return self._term_for_brain(brain)
+
+    def getTermByValue(self, value):
+        uid = value
+        brain = self.catalog(UID=uid)[0]
+        return self._term_for_brain(brain)
+    
+    def search(self, query_string, limit=20):
+        brains = self.catalog(SearchableText=query_string)[:limit]
+        return map(self._term_for_brain, brains)
+
+    def _term_for_brain(self, brain):
+        return zope.schema.vocabulary.SimpleTerm(brain.UID, brain.UID, brain.Title)
+
+class ArchetypesContentSourceBinder(object):
+    interface.implements(zope.schema.interfaces.IContextSourceBinder)
+
+    def __call__(self, context):
+        return ArchetypesContentSource(context)
+
+def uid2wref(field):
+    class Adapter(object):
+        interface.implements(field.interface)
+
+        def __init__(self, context):
+            self.context = context
+
+    def _get_items(self):
+        items = filter(None, (wref() for wref in self.context.items))
+        return [item.UID() for item in items]
+
+    def _set_items(self, uids):
+        catalog = cmfutils.getToolByName(self.context, 'portal_catalog')
+        brains = catalog(UID=tuple(uids))
+        items = [brain.getObject() for brain in brains]
+        self.context.items = map(persistent.wref.WeakRef, items)
+
+    setattr(Adapter, field.__name__, property(_get_items, _set_items))
+    return Adapter


Property changes on: plone.z3cform/trunk/plone/z3cform/queryselect/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/subform.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/subform.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/subform.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,7 @@
+<div tal:attributes="class view/css_class|nothing">
+  <h3 tal:replace="structure view/heading|nothing" />
+  <div tal:replace="structure view/contents_top|nothing" />
+  <metal:use use-macro="context/@@ploneform-macros/fields" />
+  <metal:use use-macro="context/@@ploneform-macros/actions" />
+  <div tal:replace="structure view/contents_bottom|nothing" />
+</div>


Property changes on: plone.z3cform/trunk/plone/z3cform/subform.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/templates.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,28 @@
+"""This module provides "form template factories" that can be
+registered to provide default form templates for forms and subforms
+that have a Plone style.  These default templates draw from a macro
+page template which you can use by itself to render parts of it.
+"""
+
+import os
+import zope.publisher.browser
+import zope.app.pagetemplate.viewpagetemplatefile
+
+import z3c.form.interfaces
+from z3c.form.form import FormTemplateFactory
+
+import plone.z3cform
+
+path = lambda p: os.path.join(os.path.dirname(plone.z3cform.__file__), p)
+
+form_factory = FormTemplateFactory(
+    path('form.pt'), form=z3c.form.interfaces.IForm)
+subform_factory = FormTemplateFactory(
+    path('subform.pt'), form=z3c.form.interfaces.ISubForm)
+
+class Macros(zope.publisher.browser.BrowserView):
+    template = zope.app.pagetemplate.viewpagetemplatefile.ViewPageTemplateFile(
+        'macros.pt')
+    
+    def __getitem__(self, key):
+        return self.template.macros[key]


Property changes on: plone.z3cform/trunk/plone/z3cform/templates.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/templates.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/templates.zcml	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/templates.zcml	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,18 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="plone.z3cform">
+
+  <browser:page
+      name="ploneform-macros"
+      for="*"
+      class=".templates.Macros"
+      template="macros.pt"
+      allowed_interface="zope.interface.common.mapping.IItemMapping"
+      permission="zope.Public"
+      />
+
+  <adapter factory=".templates.form_factory" />
+  <adapter factory=".templates.subform_factory" />
+
+</configure>


Property changes on: plone.z3cform/trunk/plone/z3cform/templates.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/testing.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/testing.zcml	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/testing.zcml	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,16 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:browser="http://namespaces.zope.org/browser"
+           xmlns:five="http://namespaces.zope.org/five"
+           xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+           xmlns:plone="http://namespaces.plone.org/plone"
+           i18n_domain="plone.z3cform">
+
+    <include file="configure.zcml" package="Products.Five"/>
+    <include file="meta.zcml" package="z3c.form"/>
+    <include file="configure.zcml" package="z3c.form"/>
+    <includeOverrides file="overrides.zcml" package="plone.z3cform"/>
+
+    <include file="configure.zcml" package="plone.z3cform"/>
+
+</configure>
+


Property changes on: plone.z3cform/trunk/plone/z3cform/testing.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/tests.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/tests.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/tests.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,96 @@
+import unittest
+from zope.testing import doctest
+
+from zope import component
+from zope import interface
+import zope.traversing.adapters
+import zope.traversing.namespace
+from zope.component import testing
+import zope.publisher.interfaces.browser
+import z3c.form.testing
+import plone.z3cform
+import os
+import plone.z3cform.templates
+
+def create_eventlog(event=interface.Interface):
+    value = []
+    @component.adapter(event)
+    def log(event):
+        value.append(event)
+    component.provideHandler(log)
+    return value
+
+def setup_defaults():
+    # Set up z3c.form defaults
+    z3c.form.testing.setupFormDefaults()
+    # Make traversal work; register both the default traversable
+    # adapter and the ++view++ namespace adapter
+    component.provideAdapter(
+        zope.traversing.adapters.DefaultTraversable, [None])
+    component.provideAdapter(
+        zope.traversing.namespace.view, (None, None), name='view')
+
+    # Setup ploneform macros
+    component.provideAdapter(
+        plone.z3cform.templates.Macros,
+        (None, None),
+        zope.publisher.interfaces.browser.IBrowserView,
+        name='ploneform-macros')
+    # setup plone.z3cform template
+    from zope.pagetemplate.interfaces import IPageTemplate
+
+    component.provideAdapter(
+        plone.z3cform.templates.form_factory,
+        (None, None),
+        IPageTemplate)
+
+    component.provideAdapter(
+        z3c.form.error.ErrorViewSnippet,
+        (None, None, None, None, None, None),
+        z3c.form.interfaces.IErrorViewSnippet)
+
+from zope.app.testing.functional import ZCMLLayer
+testingZCMLPath = os.path.join(os.path.dirname(__file__), 'testing.zcml')
+fullZ3CFormLayer = ZCMLLayer(testingZCMLPath,
+                             'plone.z3cform',
+                             'fullZ3CFormLayer')
+
+def test_suite():
+    fullZ3CForm = doctest.DocFileSuite('base.txt')
+    fullZ3CForm.layer = fullZ3CFormLayer
+    
+    fieldsets = doctest.DocFileSuite('fieldsets/README.txt')
+    fieldsets.layer = fullZ3CFormLayer
+    
+    return unittest.TestSuite([
+
+        doctest.DocFileSuite(
+           'crud/README.txt',
+           setUp=testing.setUp, tearDown=testing.tearDown,
+           ),
+
+        fullZ3CForm,
+        
+        fieldsets,
+
+        doctest.DocFileSuite(
+           'wysiwyg/README.txt',
+           setUp=testing.setUp, tearDown=testing.tearDown,
+           ),
+
+        doctest.DocFileSuite(
+           'queryselect/README.txt',
+           setUp=testing.setUp, tearDown=testing.tearDown,
+           ),
+
+        doctest.DocTestSuite(
+           'plone.z3cform.crud.crud',
+           setUp=testing.setUp, tearDown=testing.tearDown,
+           ),
+
+        doctest.DocTestSuite(
+           'plone.z3cform.wysiwyg.widget',
+           setUp=testing.setUp, tearDown=testing.tearDown,
+           ),
+
+        ])


Property changes on: plone.z3cform/trunk/plone/z3cform/tests.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/widget.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/widget.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/widget.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,38 @@
+from zope import interface
+from zope.schema import vocabulary
+
+import z3c.form.term
+import z3c.form.browser.checkbox
+import z3c.form.interfaces
+
+class SingleCheckboxWidget(z3c.form.browser.checkbox.SingleCheckBoxWidget):
+    """XXX: We need to refactor this and patch z3c.form where
+    it makes sense.
+    """
+
+    def update(self):
+        self.ignoreContext = True
+        super(SingleCheckboxWidget, self).update()
+
+    def updateTerms(self):
+        # The default implementation would render "selected" as a
+        # lebel for the single checkbox.  We use no label instead.
+        if self.terms is None:
+            self.terms = z3c.form.term.Terms()
+            self.terms.terms = vocabulary.SimpleVocabulary((
+                vocabulary.SimpleTerm(True, 'selected', u''),
+                ))
+        return self.terms
+
+    def extract(self, default=z3c.form.interfaces.NOVALUE):
+        # The default implementation returns [] here.
+        if (self.name not in self.request and
+            self.name+'-empty-marker' in self.request):
+            return default
+        else:
+            return super(SingleCheckboxWidget, self).extract(default)
+
+ at interface.implementer(z3c.form.interfaces.IDataConverter)
+def singlecheckboxwidget_factory(field, request):
+    widget = SingleCheckboxWidget(request)
+    return z3c.form.widget.FieldWidget(field, widget)


Property changes on: plone.z3cform/trunk/plone/z3cform/widget.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/wysiwyg/README.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/wysiwyg/README.txt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/wysiwyg/README.txt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,25 @@
+WYSIWYG widget
+==============
+
+The ``wysiwyg`` package provides an implementation of the Plone
+WYSIWYG widget compatible with ``z3c.form``.  This will allow you to
+use Kupu, FCKeditor and other editors compatible with the Plone
+WYSIWYG interface in your ``z3c.form`` forms.
+
+To use, simply set the widget factory for the widget you'd like to be
+displayed with the WYSIWYG widget:
+
+  >>> from zope import interface, schema
+  >>> from z3c.form import form, field
+  >>> from z3c.form.interfaces import INPUT_MODE
+  >>> from plone.z3cform.wysiwyg.widget import WysiwygFieldWidget
+
+  >>> class IProfile(interface.Interface):
+  ...     name = schema.TextLine(title=u"Name")
+  ...     age = schema.Int(title=u"Age")
+  ...     bio = schema.Text(title=u"Bio")
+
+  >>> class MyForm(form.Form):
+  ...     fields = field.Fields(IProfile)
+  ...     fields['bio'].widgetFactory[INPUT_MODE] = WysiwygFieldWidget
+


Property changes on: plone.z3cform/trunk/plone/z3cform/wysiwyg/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/wysiwyg/__init__.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/wysiwyg/__init__.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/wysiwyg/__init__.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1 @@
+#


Property changes on: plone.z3cform/trunk/plone/z3cform/wysiwyg/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/wysiwyg/configure.zcml
===================================================================
--- plone.z3cform/trunk/plone/z3cform/wysiwyg/configure.zcml	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/wysiwyg/configure.zcml	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,20 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="plone.z3cform">
+
+  <class class=".widget.WysiwygWidget">
+    <require
+        permission="zope.Public"
+        interface=".widget.IWysiwygWidget"
+        />
+  </class>
+
+  <z3c:widgetTemplate
+      mode="input"
+      widget=".widget.IWysiwygWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="wysiwyg_input.pt"
+      />
+
+</configure>


Property changes on: plone.z3cform/trunk/plone/z3cform/wysiwyg/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/wysiwyg/widget.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/wysiwyg/widget.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/wysiwyg/widget.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,26 @@
+import zope.interface
+import zope.schema.interfaces
+
+import z3c.form.interfaces
+import z3c.form.browser.textarea
+import z3c.form.widget
+
+class IWysiwygWidget(z3c.form.interfaces.ITextAreaWidget):
+    pass
+
+class WysiwygWidget(z3c.form.browser.textarea.TextAreaWidget):
+    zope.interface.implementsOnly(IWysiwygWidget)
+    
+    klass = u'kupu-widget'
+    value = u''
+
+    def update(self):
+        super(z3c.form.browser.textarea.TextAreaWidget, self).update()
+        z3c.form.browser.widget.addFieldClass(self)
+
+ at zope.component.adapter(zope.schema.interfaces.IField,
+                        z3c.form.interfaces.IFormLayer)
+ at zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
+def WysiwygFieldWidget(field, request):
+    """IFieldWidget factory for WysiwygWidget."""
+    return z3c.form.widget.FieldWidget(field, WysiwygWidget(request))


Property changes on: plone.z3cform/trunk/plone/z3cform/wysiwyg/widget.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/wysiwyg/wysiwyg_input.pt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/wysiwyg/wysiwyg_input.pt	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/wysiwyg/wysiwyg_input.pt	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,24 @@
+<div lang="en"
+     xml:lang="en"
+     xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal"
+     xmlns:metal="http://xml.zope.org/namespaces/metal"
+     xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+     i18n:domain="plone">
+
+     <tal:editor define="context            nocall:view/context;
+                         here               nocall:context;
+                         portal_url         nocall:context/portal_url;
+                         inputname          view/name;
+                         inputvalue         view/value;
+                         here_url           context/absolute_url;
+                         member             context/portal_membership/getAuthenticatedMember;
+                         editor             python: member.getProperty('wysiwyg_editor','').lower();
+                         support            python: path('nocall:here/%s_wysiwyg_support|here/%s/wysiwyg_support|here/portal_skins/plone_wysiwyg/wysiwyg_support' % (editor, editor));
+                         tabindex           nothing">
+        <div metal:use-macro="context/wysiwyg_support/macros/wysiwygEditorBox">
+            The WYSIWYG code
+        </div>
+    </tal:editor>
+  
+</div>


Property changes on: plone.z3cform/trunk/plone/z3cform/wysiwyg/wysiwyg_input.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/plone/z3cform/z2.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/z2.py	                        (rev 0)
+++ plone.z3cform/trunk/plone/z3cform/z2.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,47 @@
+from zope import interface
+from zope.i18n.interfaces import IUserPreferredLanguages
+from zope.i18n.locales import locales, LoadLocaleError
+from Products.Five.browser import decode
+import z3c.form.interfaces
+
+class IFixedUpRequest(interface.Interface):
+    pass
+
+# XXX This is ripped from zope.publisher.http.HTTPRequest; we should
+# move this into Five
+def setup_locale(request):
+    envadapter = IUserPreferredLanguages(request, None)
+    if envadapter is None:
+        return None
+    
+    langs = envadapter.getPreferredLanguages()
+    for httplang in langs:
+        parts = (httplang.split('-') + [None, None])[:3]
+        try:
+            return locales.getLocale(*parts)
+        except LoadLocaleError:
+            # Just try the next combination
+            pass
+    else:
+        # No combination gave us an existing locale, so use the default,
+        # which is guaranteed to exist
+        return locales.getLocale(None, None, None)
+
+# XXX Add a getURL method on the request object; we should move this
+# into Five
+def add_getURL(request):
+    def getURL(level=0, path_only=False):
+        assert level == 0 and path_only == False
+        return request['ACTUAL_URL']
+    request.getURL = getURL
+
+def switch_on(view, request_layer=z3c.form.interfaces.IFormLayer):
+    request = view.request
+    from zope.publisher.interfaces.browser import IBrowserApplicationRequest
+    if not IFixedUpRequest.providedBy(request) and \
+       not IBrowserApplicationRequest.providedBy(request):
+        interface.alsoProvides(request, IFixedUpRequest)
+        interface.alsoProvides(request, request_layer)
+        request.locale = setup_locale(request)
+        add_getURL(request)
+        decode.processInputs(request)


Property changes on: plone.z3cform/trunk/plone/z3cform/z2.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/setup.cfg
===================================================================
--- plone.z3cform/trunk/setup.cfg	                        (rev 0)
+++ plone.z3cform/trunk/setup.cfg	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true


Property changes on: plone.z3cform/trunk/setup.cfg
___________________________________________________________________
Name: svn:eol-style
   + native

Added: plone.z3cform/trunk/setup.py
===================================================================
--- plone.z3cform/trunk/setup.py	                        (rev 0)
+++ plone.z3cform/trunk/setup.py	2008-07-13 21:54:24 UTC (rev 88331)
@@ -0,0 +1,44 @@
+from setuptools import setup, find_packages
+import os
+
+version = '0.3'
+
+
+def description():
+    join = lambda *paths: os.path.join('plone', 'z3cform', *paths)
+    return (open('README.txt').read() + '\n' +
+            open(join('wysiwyg', 'README.txt')).read() + '\n' +
+            open(join('queryselect', 'README.txt')).read() + '\n' +
+            open(join('crud', 'README.txt')).read() + '\n' +
+            open(os.path.join('docs', 'HISTORY.txt')).read() + '\n')
+
+setup(name='plone.z3cform',
+      version=version,
+      description="A library that allows use of z3c.form with Zope 2 (and Plone)",
+      long_description=description(),
+      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=[
+        "Framework :: Plone",
+        "Framework :: Zope2",
+        "Programming Language :: Python",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='zope plone forms',
+      author='Daniel Nouri and contributors',
+      author_email='daniel.nouri at gmail.com',
+      url='http://pypi.python.org/pypi/plone.z3cform',
+      license='ZPL 2.1',
+      packages=find_packages(exclude=['ez_setup']),
+      namespace_packages=['plone'],
+      include_package_data=True,
+      zip_safe=False,
+
+      # If the dependency to z3c.form gives you trouble within a Zope
+      # 2 environment, try the `fakezope2eggs` recipe
+      install_requires=[
+          'setuptools',
+          'z3c.form',
+          'z3c.formwidget.query',
+          'zope.i18n>=3.4'
+      ],
+      )


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



More information about the Checkins mailing list