[Checkins] SVN: z3c.formext/trunk/ initial import z3c.formext package.
Paul Carduner
paulcarduner at gmail.com
Wed Jan 21 23:50:33 EST 2009
Log message for revision 94921:
initial import z3c.formext package.
Changed:
A z3c.formext/trunk/
A z3c.formext/trunk/README.txt
A z3c.formext/trunk/bootstrap.py
A z3c.formext/trunk/buildout.cfg
A z3c.formext/trunk/setup.py
A z3c.formext/trunk/src/
A z3c.formext/trunk/src/z3c/
A z3c.formext/trunk/src/z3c/__init__.py
A z3c.formext/trunk/src/z3c/formext/
A z3c.formext/trunk/src/z3c/formext/README.txt
A z3c.formext/trunk/src/z3c/formext/__init__.py
A z3c.formext/trunk/src/z3c/formext/component.py
A z3c.formext/trunk/src/z3c/formext/component.txt
A z3c.formext/trunk/src/z3c/formext/configure.zcml
A z3c.formext/trunk/src/z3c/formext/converter.py
A z3c.formext/trunk/src/z3c/formext/form.pt
A z3c.formext/trunk/src/z3c/formext/form.py
A z3c.formext/trunk/src/z3c/formext/form.txt
A z3c.formext/trunk/src/z3c/formext/interfaces.py
A z3c.formext/trunk/src/z3c/formext/jsmodule.py
A z3c.formext/trunk/src/z3c/formext/jsoncompat.py
A z3c.formext/trunk/src/z3c/formext/jsoncompat.txt
A z3c.formext/trunk/src/z3c/formext/meta.zcml
A z3c.formext/trunk/src/z3c/formext/metadirectives.py
A z3c.formext/trunk/src/z3c/formext/resources/
A z3c.formext/trunk/src/z3c/formext/resources/form-script.js
A z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js
A z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js
A z3c.formext/trunk/src/z3c/formext/testing.py
A z3c.formext/trunk/src/z3c/formext/tests/
A z3c.formext/trunk/src/z3c/formext/tests/__init__.py
A z3c.formext/trunk/src/z3c/formext/tests/jsmodules/
A z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js
A z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js
A z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js
A z3c.formext/trunk/src/z3c/formext/tests/script.js
A z3c.formext/trunk/src/z3c/formext/tests/test_doc.py
A z3c.formext/trunk/src/z3c/formext/widget.py
A z3c.formext/trunk/src/z3c/formext/zcml.py
A z3c.formext/trunk/src/z3c/formext/zcml.txt
-=-
Added: z3c.formext/trunk/README.txt
===================================================================
--- z3c.formext/trunk/README.txt (rev 0)
+++ z3c.formext/trunk/README.txt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+This package provides integration of ExtJS into the z3c.form framework.
\ No newline at end of file
Property changes on: z3c.formext/trunk/README.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/bootstrap.py
===================================================================
--- z3c.formext/trunk/bootstrap.py (rev 0)
+++ z3c.formext/trunk/bootstrap.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Property changes on: z3c.formext/trunk/bootstrap.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/buildout.cfg
===================================================================
--- z3c.formext/trunk/buildout.cfg (rev 0)
+++ z3c.formext/trunk/buildout.cfg 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,19 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report
+index = http://download.zope.org/zope3.4
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.formext [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3c.formext [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
Property changes on: z3c.formext/trunk/buildout.cfg
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/setup.py
===================================================================
--- z3c.formext/trunk/setup.py (rev 0)
+++ z3c.formext/trunk/setup.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup
+
+$Id$
+"""
+import os
+import xml.sax.saxutils
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ text = open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+ text = unicode(text, 'utf-8').encode('ascii', 'xmlcharrefreplace')
+ return xml.sax.saxutils.escape(text)
+
+setup (
+ name='z3c.formext',
+ version='0.1.0dev',
+ author = "Paul Carduner",
+ description = "ExtJS integration for z3c.form",
+ long_description=read('README.txt'),
+ license = "ZPL 2.1",
+ keywords = "zope3 form extjs",
+ classifiers = [
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ packages = find_packages('src'),
+ include_package_data = True,
+ package_dir = {'':'src'},
+ namespace_packages = ['z3c'],
+ extras_require = dict(
+ test = [
+ 'zope.app.container',
+ 'zope.testing',
+ 'z3c.coverage',
+ 'z3c.template',
+ 'zope.app.i18n',
+ ],
+ adding = ['zope.app.container'],
+ ),
+ install_requires = [
+ 'setuptools',
+ 'zope.app.pagetemplate',
+ 'zope.app.testing',
+ 'zope.component',
+ 'zope.configuration',
+ 'zope.event',
+ 'zope.i18n',
+ 'zope.i18nmessageid',
+ 'zope.interface',
+ 'zope.lifecycleevent',
+ 'zope.location',
+ 'zope.pagetemplate',
+ 'zope.publisher',
+ 'zope.schema',
+ 'zope.security',
+ 'zope.viewlet',
+ 'z3c.form',
+ 'z3c.formjs',
+ 'z3c.pagelet',
+ 'z3c.versionedresource',
+ 'python-cjson',
+ 'rwproperty'
+ ],
+ zip_safe = False,
+ )
Property changes on: z3c.formext/trunk/setup.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/__init__.py
===================================================================
--- z3c.formext/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.formext/trunk/src/z3c/__init__.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,7 @@
+try:
+ # Declare this a namespace package if pkg_resources is available.
+ import pkg_resources
+ pkg_resources.declare_namespace('z3c')
+except ImportError:
+ pass
+
Property changes on: z3c.formext/trunk/src/z3c/__init__.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/README.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/README.txt (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/README.txt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,6 @@
+=================
+Forms and Widgets
+=================
+
+ >>> 1+1
+ 2
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/README.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/__init__.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/__init__.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/__init__.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.formext/trunk/src/z3c/formext/__init__.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/component.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/component.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/component.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,265 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ExtJS Component representation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+from zope.traversing.browser.absoluteurl import absoluteURL
+
+from z3c.form.interfaces import IForm, DISPLAY_MODE, HIDDEN_MODE
+from z3c.form.interfaces import IPasswordWidget, IRadioWidget, IButtonAction
+from z3c.form.interfaces import ITextAreaWidget
+from z3c.form.interfaces import ITextWidget, ISelectWidget, ISingleCheckBoxWidget
+
+from z3c.formext import interfaces
+from z3c.formext.jsoncompat import jsonEncode
+
+class Component(object):
+
+ def _getConfig(self):
+ return {}
+
+ def getConfig(self, json=False):
+ return jsonEncode(self._getConfig()) if json else self._getConfig()
+
+class Field(Component):
+
+ xtype = None
+
+ def __init__(self, widget):
+ self.widget = widget
+
+ def _getConfig(self):
+ config = dict(
+ name = self.widget.name,
+ fieldLabel = self.widget.label,
+ id = self.widget.id,
+ value = self.widget.value)
+ if self.xtype:
+ config['xtype'] = self.xtype
+ if self.widget.title:
+ config['title'] = self.widget.title
+ if self.widget.mode == DISPLAY_MODE:
+ config['disabled'] = True
+ if self.widget.mode == HIDDEN_MODE:
+ config['hidden'] = True
+ return config
+
+
+class TextField(Field):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(ITextWidget)
+
+ xtype = 'textfield'
+
+ def _getConfig(self, json=False):
+ config = super(TextField, self)._getConfig()
+ if IPasswordWidget.providedBy(self.widget):
+ config['inputType'] = 'password'
+ return config
+
+
+class TextArea(TextField):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(ITextAreaWidget)
+
+ xtype = 'textarea'
+
+
+class DateField(TextField):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(interfaces.IExtJSDateWidget)
+
+ xtype = 'datefield'
+
+class ComboBox(Field):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(ISelectWidget)
+
+ xtype = 'combo'
+ def _getConfig(self, json=False):
+ config = super(ComboBox, self)._getConfig()
+ config['hiddenName'] = config['name']+':list'
+ config['triggerAction'] = 'all'
+ config['editable'] = False
+ #XXX: commented out, not sure why this was here
+ #del config['name']
+ config['store'] = [(item['value'], item['content'])
+ for item in self.widget.items]
+ return config
+
+class CheckBox(Field):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(ISingleCheckBoxWidget)
+
+ xtype = 'checkbox'
+ def _getConfig(self, json=False):
+ config = super(CheckBox, self)._getConfig()
+ checkbox = self.widget.items[0]
+ config['checked'] = checkbox['checked']
+ config['fieldLabel'] = checkbox['label']
+ del config['value']
+ return config
+
+class RadioGroup(Field):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(IRadioWidget)
+
+ def _getConfig(self, json=False):
+ config = dict(
+ fieldLabel = self.widget.label,
+ id = self.widget.id,
+ xtype = 'radiogroup',
+ items = [dict(boxLabel=item['label'],
+ id='%s-%s' % (self.widget.id, index),
+ name=self.widget.name,
+ inputValue=item['value'],
+ checked=item['checked'])
+ for index, item in enumerate(self.widget.items)]
+ )
+ # we must pass in an items list even if there aren't any. So
+ # we will just pass in one item that is hidden. This is most
+ # certainly less than ideal.
+ if not config['items']:
+ config['items'].append(dict(hidden=True))
+ if self.widget.title:
+ config['title'] = self.widget.title
+ return config
+
+class Button(Field):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(IButtonAction)
+
+ xtype = 'button'
+ def _getConfig(self, json=False):
+ config = super(Button, self)._getConfig()
+ config['text'] = self.widget.value
+ del config['value']
+ del config['fieldLabel']
+ return config
+
+
+def getButtonsConfig(form, asDict=True):
+ if not hasattr(form, 'actions'):
+ form.updateActions()
+ if not asDict:
+ return [interfaces.IExtJSComponent(action).getConfig()
+ for action in form.actions.values()]
+ return dict([(name, interfaces.IExtJSComponent(action).getConfig())
+ for name, action in form.actions.items()])
+
+def getWidgetsConfig(form, asDict=True):
+ if not asDict:
+ widgets = []
+ for widget in form.widgets.values():
+ factory = interfaces.IExtJSComponent
+ if hasattr(widget, 'componentFactory'):
+ factory = widget.componentFactory
+ widgets.append(factory(widget).getConfig())
+ return widgets
+ widgets = {}
+ for name, widget in form.widgets.items():
+ factory = interfaces.IExtJSComponent
+ if hasattr(widget, 'componentFactory'):
+ factory = widget.componentFactory
+ widgets[name] = factory(widget).getConfig()
+ return widgets
+
+class FormPanel(Component):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(IForm)
+
+ xtype = 'formpanel'
+
+ def __init__(self, form):
+ self.form = form
+
+ def _getConfig(self, json=False):
+ config = dict(
+ xtype=self.xtype,
+ id=self.form.id,
+ submitURL=self.form.action)
+ if self.form.label:
+ config['title'] = self.form.label
+ if not self.form.widgets:
+ self.form.updateWidgets()
+ items = getWidgetsConfig(self.form, asDict=False)
+ if items:
+ config['items'] = items
+
+ buttons = getButtonsConfig(self.form, asDict=False)
+ if buttons:
+ config['buttons'] = buttons
+ if hasattr(self.form, 'renderTo'):
+ config['renderTo'] = self.form.renderTo
+
+ return config
+
+
+class ClientButton(Button):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(interfaces.IClientButtonAction)
+
+ def _getConfig(self, json=False):
+ config = super(ClientButton, self)._getConfig()
+ config['handler'] = {}
+ if self.widget.field.success:
+ config['handler']['success'] = self.widget.field.success
+ if self.widget.field.failure:
+ config['handler']['failure'] = self.widget.field.failure
+ return config
+
+def getAjaxButtonsConfig(form, asDict=True):
+ if not asDict:
+ buttons = getButtonsConfig(form, asDict=False)
+ if hasattr(form, 'ajaxRequestHandlers'):
+ for name, handler in form.ajaxRequestHandlers.items():
+ if name in form.actions:
+ index = form.actions.keys().index(name)
+ buttons[index]['url'] = '%s/@@ajax/%s' % (
+ absoluteURL(form, form.request), name)
+ return buttons
+ buttons = getButtonsConfig(form, asDict=True)
+ if hasattr(form, 'ajaxRequestHandlers'):
+ for name, handler in form.ajaxRequestHandlers.items():
+ if name in form.actions:
+ buttons[name]['url'] = '%s/@@ajax/%s' % (
+ absoluteURL(form, form.request), name)
+ return buttons
+
+
+class ExtFormPanel(FormPanel):
+ zope.interface.implements(interfaces.IExtJSComponent)
+ zope.component.adapts(interfaces.IExtJSForm)
+
+ def _getConfig(self, json=False):
+ config = super(ExtFormPanel, self)._getConfig()
+ config['ajaxHandlers'] = {}
+ if hasattr(self.form, 'ajaxRequestHandlers'):
+ for name, handler in self.form.ajaxRequestHandlers.items():
+ if name in self.form.actions:
+ index = self.form.actions.keys().index(name)
+ config['buttons'][index]['url'] = '%s/@@ajax/%s' % (
+ absoluteURL(self.form, self.form.request), name)
+ id = self.form.actions[name].id
+ config['ajaxHandlers'][id] = '%s/@@ajax/%s' % (
+ absoluteURL(self.form, self.form.request), name)
+ if hasattr(self.form, 'ownerCt'):
+ config['ownerCt'] = self.form.ownerCt
+ return config
+
Property changes on: z3c.formext/trunk/src/z3c/formext/component.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/component.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/component.txt (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/component.txt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,438 @@
+================
+ExtJS Components
+================
+
+We can construct configuration objects for basic extjs components.
+
+ >>> from z3c.formext import interfaces, component, testing
+
+ >>> from pprint import pprint
+
+Setup
+-----
+
+ >>> testing.setupExtJSComponents()
+
+ >>> from z3c.form.testing import setupFormDefaults, TestRequest
+ >>> setupFormDefaults()
+
+Widgets
+-------
+
+TextWidget
+..........
+
+ >>> from z3c.form.interfaces import HIDDEN_MODE, DISPLAY_MODE
+ >>> from z3c.form.browser.text import TextWidget
+
+ >>> widget = TextWidget(TestRequest())
+ >>> widget.label = u'Widget Label'
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+ >>> widget.value = 'widget value'
+
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'fieldLabel': u'Widget Label',
+ 'id': 'widget-id',
+ 'name': 'widget.name',
+ 'value': 'widget value',
+ 'xtype': 'textfield'}
+
+ >>> widget.mode = HIDDEN_MODE
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'fieldLabel': u'Widget Label',
+ 'hidden': True,
+ 'id': 'widget-id',
+ 'name': 'widget.name',
+ 'value': 'widget value',
+ 'xtype': 'textfield'}
+
+ >>> widget.mode = DISPLAY_MODE
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'disabled': True,
+ 'fieldLabel': u'Widget Label',
+ 'id': 'widget-id',
+ 'name': 'widget.name',
+ 'value': 'widget value',
+ 'xtype': 'textfield'}
+
+TextArea
+........
+
+ >>> from z3c.form.browser.textarea import TextAreaWidget
+
+ >>> widget = TextAreaWidget(TestRequest())
+ >>> widget.label = u'Widget Label'
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+ >>> widget.value = 'widget value'
+
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'fieldLabel': u'Widget Label',
+ 'id': 'widget-id',
+ 'name': 'widget.name',
+ 'value': 'widget value',
+ 'xtype': 'textarea'}
+
+Date Widget
+...........
+
+ >>> from z3c.formext.widget import ExtJSDateWidget
+
+ >>> widget = ExtJSDateWidget(TestRequest())
+ >>> widget.label = u'Widget Label'
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+ >>> widget.value = 'widget value'
+
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'fieldLabel': u'Widget Label',
+ 'id': 'widget-id',
+ 'name': 'widget.name',
+ 'value': 'widget value',
+ 'xtype': 'datefield'}
+
+Select Widget
+.............
+
+ >>> from z3c.form.browser.select import SelectFieldWidget
+ >>> from zope.schema import Choice
+
+ >>> widget = SelectFieldWidget(
+ ... Choice(values=[1,2,3,4]),
+ ... TestRequest())
+ >>> widget.update()
+ >>> widget.label = u'Widget Label'
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+ >>> widget.value = 'widget value'
+
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'editable': False,
+ 'fieldLabel': u'Widget Label',
+ 'hiddenName': 'widget.name:list',
+ 'id': 'widget-id',
+ 'name': 'widget.name',
+ 'store': [('1', '1'), ('2', '2'), ('3', '3'), ('4', '4')],
+ 'triggerAction': 'all',
+ 'value': 'widget value',
+ 'xtype': 'combo'}
+
+Radio Widget
+............
+
+ >>> from z3c.form.browser.radio import RadioFieldWidget
+ >>> widget = RadioFieldWidget(
+ ... Choice(values=[1, 2, 3]),
+ ... TestRequest())
+ >>> widget.update()
+ >>> widget.label = u'Widget Label'
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+ >>> widget.value = 'widget value'
+
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'fieldLabel': u'Widget Label',
+ 'id': 'widget-id',
+ 'items': [{'boxLabel': '1',
+ 'checked': False,
+ 'id': 'widget-id-0',
+ 'inputValue': '1',
+ 'name': 'widget.name'},
+ {'boxLabel': '2',
+ 'checked': False,
+ 'id': 'widget-id-1',
+ 'inputValue': '2',
+ 'name': 'widget.name'},
+ {'boxLabel': '3',
+ 'checked': False,
+ 'id': 'widget-id-2',
+ 'inputValue': '3',
+ 'name': 'widget.name'}],
+ 'xtype': 'radiogroup'}
+
+An edge case is a radio widget that has no values to select. ExtJS
+requires there to be an items attribute that is a non empty list. To
+get a similar effect, we just add one hidden item to the items list.
+
+ >>> widget = RadioFieldWidget(
+ ... Choice(values=[]),
+ ... TestRequest())
+ >>> widget.update()
+ >>> widget.label = u'Widget Label'
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+ >>> widget.value = 'widget value'
+
+ >>> pprint(interfaces.IExtJSComponent(widget).getConfig())
+ {'fieldLabel': u'Widget Label',
+ 'id': 'widget-id',
+ 'items': [{'hidden': True}],
+ 'xtype': 'radiogroup'}
+
+
+Form Panels
+-----------
+
+ExtJS has the concept of a form panel. We can directly adapt a from
+from z3c.form.
+
+ >>> from z3c.form import form, field, button
+ >>> import zope.schema
+ >>> import zope.interface
+
+
+ >>> class IPerson(zope.interface.Interface):
+ ...
+ ... id = zope.schema.TextLine(
+ ... title=u'ID',
+ ... readonly=True,
+ ... required=True)
+ ...
+ ... isCool = zope.schema.Bool(
+ ... title=u'Are you Cool?',
+ ... required=True)
+ ...
+ ... name = zope.schema.TextLine(
+ ... title=u'Name',
+ ... required=True)
+ ...
+ ... gender = zope.schema.Choice(
+ ... title=u'Gender',
+ ... values=('male', 'female'),
+ ... required=False)
+ ...
+ ... age = zope.schema.Int(
+ ... title=u'Age',
+ ... description=u"The person's age.",
+ ... min=0,
+ ... default=20,
+ ... required=False)
+ ...
+ ... passwd = zope.schema.Password(
+ ... title=u'Password',
+ ... required=True)
+ ...
+ ... birthDay = zope.schema.Date(
+ ... title=u'Birthday')
+ ...
+ ... isNice = zope.schema.Bool(
+ ... title=u'Are you Nice?',
+ ... required=True)
+ ...
+
+ >>> from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+ >>> from z3c.form.browser.password import PasswordFieldWidget
+ >>> from z3c.form.interfaces import HIDDEN_MODE
+ >>> from z3c.formext.widget import ExtJSDateFieldWidget
+ >>> class ContactForm(form.Form):
+ ... label = u'My Contact Form'
+ ... ignoreContext = True
+ ... fields = field.Fields(IPerson)
+ ... fields['isCool'].widgetFactory = SingleCheckBoxFieldWidget
+ ... fields['passwd'].widgetFactory = PasswordFieldWidget
+ ... fields['birthDay'].widgetFactory = ExtJSDateFieldWidget
+ ... buttons = button.Buttons(button.Button(__name__='save', title=u'Save'))
+ ... renderTo = 'my-dom-id'
+ ... def updateWidgets(self):
+ ... super(ContactForm, self).updateWidgets()
+ ... self.widgets['id'].mode = HIDDEN_MODE
+
+ >>> myForm = ContactForm('context', TestRequest())
+
+ >>> formPanel = interfaces.IExtJSComponent(myForm)
+ >>> config = formPanel.getConfig()
+
+Now we can look more closely at what we actually get
+
+ >>> config['xtype']
+ 'formpanel'
+ >>> config['title']
+ u'My Contact Form'
+ >>> config['id']
+ 'form'
+ >>> config['renderTo']
+ 'my-dom-id'
+
+Here is a simple text widget
+
+ >>> pprint(config['items'][0])
+ {'fieldLabel': u'ID',
+ 'hidden': True,
+ 'id': 'form-widgets-id',
+ 'name': 'form.widgets.id',
+ 'value': u'',
+ 'xtype': 'textfield'}
+
+Here is a single checkbox widget
+
+ >>> pprint(config['items'][1])
+ {'checked': False,
+ 'fieldLabel': u'Are you Cool?',
+ 'id': 'form-widgets-isCool',
+ 'name': 'form.widgets.isCool',
+ 'xtype': 'checkbox'}
+
+Here is another simple text widget
+
+ >>> pprint(config['items'][2])
+ {'fieldLabel': u'Name',
+ 'id': 'form-widgets-name',
+ 'name': 'form.widgets.name',
+ 'value': u'',
+ 'xtype': 'textfield'}
+
+Here is a select widget
+
+ >>> pprint(config['items'][3])
+ {'editable': False,
+ 'fieldLabel': u'Gender',
+ 'hiddenName': 'form.widgets.gender:list',
+ 'id': 'form-widgets-gender',
+ 'name': 'form.widgets.gender',
+ 'store': [('--NOVALUE--', u'no value'),
+ ('male', 'male'),
+ ('female', 'female')],
+ 'triggerAction': 'all',
+ 'value': (),
+ 'xtype': 'combo'}
+
+ >>> pprint(config['buttons'])
+ [{'id': 'form-buttons-save',
+ 'name': 'form.buttons.save',
+ 'text': u'Save',
+ 'title': u'Save',
+ 'xtype': 'button'}]
+
+Here is an integer widget
+
+ >>> pprint(config['items'][4])
+ {'fieldLabel': u'Age',
+ 'id': 'form-widgets-age',
+ 'name': 'form.widgets.age',
+ 'value': u'20',
+ 'xtype': 'textfield'}
+
+Here is a password widget
+
+ >>> pprint(config['items'][5])
+ {'fieldLabel': u'Password',
+ 'id': 'form-widgets-passwd',
+ 'inputType': 'password',
+ 'name': 'form.widgets.passwd',
+ 'value': u'',
+ 'xtype': 'textfield'}
+
+Here is a date widget
+
+ >>> pprint(config['items'][6])
+ {'fieldLabel': u'Birthday',
+ 'id': 'form-widgets-birthDay',
+ 'name': 'form.widgets.birthDay',
+ 'value': u'',
+ 'xtype': 'datefield'}
+
+Here is a radio group
+
+ >>> pprint(config['items'][7])
+ {'fieldLabel': u'Are you Nice?',
+ 'id': 'form-widgets-isNice',
+ 'items': [{'boxLabel': u'yes',
+ 'checked': False,
+ 'id': 'form-widgets-isNice-0',
+ 'inputValue': 'true',
+ 'name': 'form.widgets.isNice'},
+ {'boxLabel': u'no',
+ 'checked': False,
+ 'id': 'form-widgets-isNice-1',
+ 'inputValue': 'false',
+ 'name': 'form.widgets.isNice'}],
+ 'xtype': 'radiogroup'}
+
+Helper functions
+----------------
+
+It is sometimes the case that we want to provide information about an
+extjs component in a format that doesn't quite fit what extjs wants to
+use, but which is easier for custom components to configure and play
+with. For example, extjs likes container items to be a list, but if
+the script that uses the config object wants to present items in a
+different order, it is easier to reference the items by name rather
+than by index.
+
+Buttons
+.......
+
+For example, we can get the set of buttons in either a dictionary
+based format (easy for rearraging) or a list format (the way extjs
+likes it).
+
+ >>> pprint(component.getButtonsConfig(myForm))
+ {'save': {'id': 'form-buttons-save',
+ 'name': 'form.buttons.save',
+ 'text': u'Save',
+ 'title': u'Save',
+ 'xtype': 'button'}}
+
+ >>> pprint(component.getButtonsConfig(myForm, asDict=False))
+ [{'id': 'form-buttons-save',
+ 'name': 'form.buttons.save',
+ 'text': u'Save',
+ 'title': u'Save',
+ 'xtype': 'button'}]
+
+Widgets
+.......
+
+This works just like buttons, except for widgets.
+
+ >>> pprint(component.getWidgetsConfig(myForm)['isNice'])
+ {'fieldLabel': u'Are you Nice?',
+ 'id': 'form-widgets-isNice',
+ 'items': [{'boxLabel': u'yes',
+ 'checked': False,
+ 'id': 'form-widgets-isNice-0',
+ 'inputValue': 'true',
+ 'name': 'form.widgets.isNice'},
+ {'boxLabel': u'no',
+ 'checked': False,
+ 'id': 'form-widgets-isNice-1',
+ 'inputValue': 'false',
+ 'name': 'form.widgets.isNice'}],
+ 'xtype': 'radiogroup'}
+
+ >>> pprint(component.getWidgetsConfig(myForm, asDict=False)[0])
+ {'fieldLabel': u'ID',
+ 'hidden': True,
+ 'id': 'form-widgets-id',
+ 'name': 'form.widgets.id',
+ 'value': u'',
+ 'xtype': 'textfield'}
+
+
+Custom Widget Component Factories
+---------------------------------
+
+We can also specify custom components for widgets.
+
+ >>> class MyComponent(component.TextField):
+ ... xtype = 'my-component'
+ >>> class ContactForm(form.Form):
+ ... ignoreContext = True
+ ... fields = field.Fields(IPerson).select('id')
+ ... def updateWidgets(self):
+ ... super(ContactForm, self).updateWidgets()
+ ... self.widgets['id'].componentFactory = MyComponent
+
+ >>> myForm = ContactForm('context', TestRequest())
+
+ >>> formPanel = interfaces.IExtJSComponent(myForm)
+ >>> config = formPanel.getConfig()
+ >>> pprint(config['items'][0])
+ {'disabled': True,
+ 'fieldLabel': u'ID',
+ 'id': 'form-widgets-id',
+ 'name': 'form.widgets.id',
+ 'value': u'',
+ 'xtype': 'my-component'}
+
Property changes on: z3c.formext/trunk/src/z3c/formext/component.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/configure.zcml
===================================================================
--- z3c.formext/trunk/src/z3c/formext/configure.zcml (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/configure.zcml 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,68 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c">
+
+
+ <!-- Data Converters -->
+ <adapter
+ factory=".converter.ExtJSDateDataConverter"
+ />
+ <adapter
+ factory=".converter.ExtJSSingleCheckBoxDataConverter"
+ />
+
+ <browser:versionedResource
+ name="z3c.formext.loader.js"
+ file="resources/z3c.formext.loader.js"
+ layer="z3c.form.interfaces.IFormLayer"
+ />
+
+ <z3c:jsmodule
+ file="resources/z3c.formext.form.js"
+ layer="z3c.form.interfaces.IFormLayer"
+ />
+
+ <!-- ExtJS Component adapters -->
+ <adapter
+ factory=".component.FormPanel"
+ />
+ <adapter
+ factory=".component.ExtFormPanel"
+ />
+ <adapter
+ factory=".component.TextField"
+ />
+ <adapter
+ factory=".component.TextArea"
+ />
+ <adapter
+ factory=".component.DateField"
+ />
+ <adapter
+ factory=".component.ComboBox"
+ />
+ <adapter
+ factory=".component.CheckBox"
+ />
+ <adapter
+ factory=".component.RadioGroup"
+ />
+ <adapter
+ factory=".component.Button"
+ />
+ <adapter
+ factory=".component.ClientButton"
+ />
+
+ <adapter
+ factory=".form.ClientButtonAction"
+ provides="z3c.form.interfaces.IButtonAction"
+ />
+
+ <z3c:template
+ for=".form.ScriptProvider"
+ template="form.pt"
+ layer="z3c.form.interfaces.IFormLayer" />
+
+</configure>
Property changes on: z3c.formext/trunk/src/z3c/formext/configure.zcml
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/converter.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/converter.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/converter.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Special ExtJS data converters
+
+$Id$
+"""
+
+import zope.i18n.format
+from z3c.form import converter
+from z3c.formext import interfaces
+
+class ExtJSDateDataConverter(converter.CalendarDataConverter):
+ zope.component.adapts(
+ zope.schema.interfaces.IDate, interfaces.IExtJSDateWidget)
+ type = "date"
+
+ def toWidgetValue(self, value):
+ """See interfaces.IDataConverter"""
+ if value is self.field.missing_value:
+ return u''
+ return self.formatter.format(value, pattern="MM/dd/yyyy")
+
+ def toFieldValue(self, value):
+ """See interfaces.IDataConverter"""
+ if value == u'':
+ return self.field.missing_value
+ try:
+ # Note: use ExtJS.DateField.format = 'm/d/Y', not the default!
+ return self.formatter.parse(value, pattern="MM/dd/yyyy")
+ except zope.i18n.format.DateTimeParseError, err:
+ raise converter.FormatterValidationError(err.args[0], value)
+
+
+class ExtJSSingleCheckBoxDataConverter(converter.BaseDataConverter):
+ zope.component.adapts(
+ zope.schema.interfaces.IBool, interfaces.IExtJSSingleCheckBoxWidget)
+
+ def toWidgetValue(self, value):
+ """Convert from Python bool to HTML representation."""
+ if value:
+ return 'on'
+ return 'off'
+
+ def toFieldValue(self, value):
+ """See interfaces.IDataConverter"""
+ if value == 'on':
+ return True
+ return False
Property changes on: z3c.formext/trunk/src/z3c/formext/converter.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/form.pt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/form.pt (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/form.pt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+<script tal:replace="structure view/scriptTag"/>
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/form.pt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/form.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/form.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/form.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,209 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ExtJS Component representation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import sys
+from rwproperty import getproperty, setproperty
+
+import zope.interface
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from zope.i18n import translate
+from zope.pagetemplate.interfaces import IPageTemplate
+from zope.schema.fieldproperty import FieldProperty
+from zope.security.proxy import removeSecurityProxy
+
+import z3c.form.form
+from z3c.form.button import Button, Buttons
+from z3c.form.button import ButtonAction
+from z3c.form.button import ButtonAction
+from z3c.form.interfaces import IFormLayer
+from z3c.form.util import SelectionManager
+from z3c.formjs.ajax import AJAXHandler, AJAXHandlers
+from z3c.formjs.ajax import AJAXRequestHandler
+from z3c.pagelet.browser import BrowserPagelet
+
+from z3c.formext import interfaces
+from z3c.formext.jsoncompat import jsonEncode
+
+
+class JSProperties(SelectionManager):
+ """JSProperties selection manager."""
+ zope.interface.implements(interfaces.IJSProperties)
+ managerInterface = interfaces.IJSProperties
+
+ def __init__(self, *args):
+ super(JSProperties, self).__init__()
+ for arg in args:
+ if self.managerInterface.providedBy(arg):
+ for name, prop in arg.items():
+ self._data_keys.append(name)
+ self._data_values.append(prop)
+ self._data[name] = prop
+ else:
+ self._data_keys.append(arg.__name__)
+ self._data_values.append(arg)
+ self._data[arg.__name__] = arg
+
+
+def jsproperty(func):
+ frame = sys._getframe(1)
+ properties = frame.f_locals.setdefault('jsproperties', JSProperties())
+ frame.f_locals['jsproperties'] += JSProperties(func)
+ return frame.f_locals['jsproperties'][func.__name__]
+
+
+def dependencyWrap(dep):
+ return ('z3c.formext.ModuleLoader.load(\n'
+ ' "%s",\n'
+ ' function(){\n'
+ ' %%s\n'
+ ' });' % dep)
+
+class ScriptProvider(object):
+
+ script = ''
+ scriptDependencies = ()
+
+ @property
+ def scriptTag(self):
+ tagWrap = '<script type="text/javascript" language="Javascript">\n%s\n</script>'
+ closureWrap = ' (function(){\n%s\n})();'
+
+ depWraps = '%s'
+ for dep in self.scriptDependencies:
+ depWraps = depWraps % dependencyWrap(dep)
+
+ script = self.script
+ if hasattr(script, '__call__'):
+ #this is a page template.
+ script = script()
+
+ jsVars = ''
+ if hasattr(self, 'jsproperties'):
+ jsVars = '\n'.join([' var %s=%s;' % (name, jsonEncode(prop(self)))
+ for name, prop in self.jsproperties.items()])
+
+ return tagWrap % (closureWrap % (depWraps % ('%s\n%s' % (jsVars, script))))
+
+
+class ScriptPagelet(ScriptProvider, AJAXRequestHandler, BrowserPagelet):
+ """A class that can be extended to get all this functionality."""
+
+
+class ExtJSForm(ScriptProvider, AJAXRequestHandler, z3c.form.form.Form):
+ zope.interface.implements(interfaces.IExtJSForm)
+ jsonResponse = None
+
+ script = ViewPageTemplateFile('resources/form-script.js')
+
+ @jsproperty
+ def config(self):
+ return interfaces.IExtJSComponent(self).getConfig()
+
+ @property
+ def response(self):
+ return jsonEncode(self.jsonResponse or dict(success=True))
+
+ def addFormError(self, error):
+ self.jsonResponse['success'] = False
+ self.jsonResponse.setdefault('formErrors', [])
+ if isinstance(error, unicode):
+ error = translate(error)
+ self.jsonResponse['formErrors'].append(str(error))
+
+ def extractData(self):
+ data, errors = super(ExtJSForm, self).extractData()
+ self.jsonResponse = dict(success=True)
+ if errors:
+ self.jsonResponse = dict(
+ success=False,
+ # I shouldn't need the below security proxy
+ errors={},
+ formErrors=[])
+ for error in errors:
+ error = removeSecurityProxy(error)
+ message = translate(error.message)
+ if error.widget:
+ self.jsonResponse['errors'][error.widget.id] = message
+ else:
+ self.jsonResponse['formErrors'].append(message)
+ return data, errors
+
+
+class ClientButton(Button):
+ zope.interface.implements(interfaces.IClientButton)
+
+ success = FieldProperty(interfaces.IClientButton['success'])
+ failure = FieldProperty(interfaces.IClientButton['failure'])
+
+ def __init__(self, *args, **kwargs):
+ self.success = kwargs.pop('success', None)
+ self.failure = kwargs.pop('failure', None)
+ super(ClientButton, self).__init__(*args, **kwargs)
+
+
+class ClientButtonAction(ButtonAction):
+ zope.interface.implements(interfaces.IClientButtonAction)
+ zope.component.adapts(IFormLayer, interfaces.IClientButton)
+
+ _success = FieldProperty(interfaces.IClientButton['success'])
+ _failure = FieldProperty(interfaces.IClientButton['failure'])
+
+ @getproperty
+ def success(self):
+ if self._success is None:
+ return self.field.success
+ return self._success
+
+ @getproperty
+ def failure(self):
+ if self._failure is None:
+ return self.field.failure
+ return self._failure
+
+ @setproperty
+ def success(self, value):
+ self._success = value
+
+ @setproperty
+ def failure(self, value):
+ self._failure = value
+
+
+def buttonAndHandler(title, **kwargs):
+
+ # Add the title to button constructor keyword arguments
+ kwargs['title'] = title
+
+ # Extract directly provided interfaces:
+ provides = kwargs.pop('provides', ())
+
+ # Create button and add it to the button manager
+ button = ClientButton(**kwargs)
+ zope.interface.alsoProvides(button, provides)
+ frame = sys._getframe(1)
+ f_locals = frame.f_locals
+ buttons = f_locals.setdefault('buttons', Buttons())
+ f_locals['buttons'] += Buttons(button)
+
+ def ajaxHandler(func):
+ handler = AJAXHandler(func)
+ handlers = f_locals.setdefault('ajaxRequestHandlers', AJAXHandlers())
+ handlers.addHandler(button.__name__, handler)
+ return handler
+
+ return ajaxHandler
Property changes on: z3c.formext/trunk/src/z3c/formext/form.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/form.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/form.txt (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/form.txt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,232 @@
+ExtJS Form Integration
+======================
+
+This package also provides a tight integration between z3c.form -
+which provides a server side model and validation system, and Ext JS
+forms which provide an advanced client side form model.
+
+ >>> from z3c.formext import form, interfaces
+
+Setup
+-----
+
+ >>> from z3c.formext import testing
+ >>> testing.setupFormExt()
+
+Script Provider
+---------------
+
+We have a base class that represents a component that provides some
+javascript. It has support for several features which we will show
+off here.
+
+ >>> from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+ >>> class MyPage(form.ScriptProvider):
+ ... script = ViewPageTemplateFile('tests/script.js')
+ ... scriptDependencies = ('z3c.formext.form','someOtherLib')
+ ... @form.jsproperty
+ ... def appearsInJS(self):
+ ... return ['yay','for',{'complex':'types'}]
+
+ >>> myPage = MyPage()
+ >>> myPage.request = testing.TestRequest()
+ >>> myPage.context = object()
+ >>> print myPage.scriptTag
+ <script type="text/javascript" language="Javascript">
+ (function(){
+ z3c.formext.ModuleLoader.load(
+ "z3c.formext.form",
+ function(){
+ z3c.formext.ModuleLoader.load(
+ "someOtherLib",
+ function(){
+ var appearsInJS=["yay", "for", {"complex": "types"}];
+ alert("foo!");
+ });
+ });
+ })();
+ </script>
+
+Creating forms
+--------------
+
+Ext JS forms are created in the same manner as regular z3c.form
+forms. First we will create an interface for the form.
+
+
+ >>> from z3c.form import field, button
+ >>> import zope.schema
+ >>> import zope.interface
+
+ >>> class IPerson(zope.interface.Interface):
+ ...
+ ... id = zope.schema.TextLine(
+ ... title=u'ID',
+ ... readonly=True,
+ ... required=True)
+ ...
+ ... isCool = zope.schema.Bool(
+ ... title=u'Are you Cool?',
+ ... required=True)
+ ...
+ ... name = zope.schema.TextLine(
+ ... title=u'Name',
+ ... required=True)
+ ...
+ ... gender = zope.schema.Choice(
+ ... title=u'Gender',
+ ... values=('male', 'female'),
+ ... required=False)
+ ...
+ ... age = zope.schema.Int(
+ ... title=u'Age',
+ ... description=u"The person's age.",
+ ... min=0,
+ ... default=20,
+ ... required=False)
+ ...
+ ... passwd = zope.schema.Password(
+ ... title=u'Password',
+ ... required=True)
+
+Now we can create the form.
+
+ >>> from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+ >>> from z3c.form.browser.password import PasswordFieldWidget
+ >>> from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+ >>> class ContactForm(testing.TestingForm, form.ExtJSForm):
+ ... label = u'My Contact Form'
+ ... ignoreContext = True
+ ... fields = field.Fields(IPerson)
+ ... fields['isCool'].widgetFactory = SingleCheckBoxFieldWidget
+ ... fields['passwd'].widgetFactory = PasswordFieldWidget
+ ...
+ ... ownerCt = 'my-ext-component-id'
+ ...
+ ... @form.buttonAndHandler(title=u'Save', success='handleSave')
+ ... def handleSave(self):
+ ... pass
+ ... jsproperties = form.ExtJSForm.jsproperties.copy()
+ ... @form.jsproperty
+ ... def foo(self):
+ ... return 'the foo property'
+
+Note that the jsproperties are not changed on the superclass
+
+ >>> form.ExtJSForm.jsproperties.keys()
+ ['config']
+
+ >>> ContactForm.jsproperties.keys()
+ ['config', 'foo']
+
+Now we can invoke the form and try to extract the data.
+
+ >>> myForm = ContactForm(None, testing.TestRequest())
+ >>> myForm.update()
+ >>> data, errors = myForm.extractData()
+
+Since we have not entered in any data, there will be errors.
+
+ >>> len(errors)
+ 3
+
+To propogate these errors to the client side ext js form, we must
+construct a json response. This is done for us by ``ExtJSForm``.
+
+ >>> from pprint import pprint
+ >>> pprint(myForm.jsonResponse)
+ {'errors': {'form-widgets-isCool': u'Required input is missing.',
+ 'form-widgets-name': u'Required input is missing.',
+ 'form-widgets-passwd': u'Required input is missing.'},
+ 'formErrors': [],
+ 'success': False}
+
+The other nice part of this is the button handler, which is
+automatically converted into an ajax request handler
+
+ >>> myForm.handleSave
+ <AJAXHandler 'handleSave'>
+
+The ajax handler is published using the name of the button rather than
+the name of the handler.
+
+ >>> myForm.ajaxRequestHandlers
+ <AJAXHandlers ['save']>
+
+The form config object will then have an ajaxHandlers property that is
+a mapping from button id to the url which should be called for that button.
+
+ >>> config = interfaces.IExtJSComponent(myForm).getConfig()
+ >>> config['ajaxHandlers']
+ {'form-buttons-save': 'http://127.0.0.1/index.html/@@ajax/save'}
+
+This url is also available on the button.
+
+ >>> from pprint import pprint
+ >>> pprint(config['buttons'][0])
+ {'handler': {'success': 'handleSave'},
+ 'id': 'form-buttons-save',
+ 'name': 'form.buttons.save',
+ 'text': u'Save',
+ 'title': u'Save',
+ 'url': 'http://127.0.0.1/index.html/@@ajax/save',
+ 'xtype': 'button'}
+
+If we want to get the listing of buttons separately, and have it
+include the url, we can use the helper function getAjaxButtonsConfig:
+
+ >>> from z3c.formext import component
+ >>> pprint(component.getAjaxButtonsConfig(myForm))
+ {'save': {'handler': {'success': 'handleSave'},
+ 'id': 'form-buttons-save',
+ 'name': 'form.buttons.save',
+ 'text': u'Save',
+ 'title': u'Save',
+ 'url': 'http://127.0.0.1/index.html/@@ajax/save',
+ 'xtype': 'button'}}
+
+This also works in list form as well:
+
+ >>> pprint(component.getAjaxButtonsConfig(myForm, asDict=False))
+ [{'handler': {'success': 'handleSave'},
+ 'id': 'form-buttons-save',
+ 'name': 'form.buttons.save',
+ 'text': u'Save',
+ 'title': u'Save',
+ 'url': 'http://127.0.0.1/index.html/@@ajax/save',
+ 'xtype': 'button'}]
+
+
+ >>> print myForm.script()
+ z3c.formext.ModuleLoader.load(
+ 'z3c.formext.form',
+ function(){
+ if (config.ownerCt){
+ var container = Ext.getCmp(config.ownerCt);
+ delete config.ownerCt;
+ container.add(new z3c.formext.form.Z3CFormPanel(config));
+ container.doLayout();
+ } else if (config.renderTo){
+ new z3c.formext.form.Z3CFormPanel(config);
+ }
+ });
+
+ >>> print myForm.scriptTag
+ <script type="text/javascript" language="Javascript">
+ (function(){
+ var config={"xtype": "formpanel", "title": "My Contact Form", "items": ...
+ var foo="the foo property";
+ z3c.formext.ModuleLoader.load(
+ 'z3c.formext.form',
+ function(){
+ if (config.ownerCt){
+ var container = Ext.getCmp(config.ownerCt);
+ delete config.ownerCt;
+ container.add(new z3c.formext.form.Z3CFormPanel(config));
+ container.doLayout();
+ } else if (config.renderTo){
+ new z3c.formext.form.Z3CFormPanel(config);
+ }
+ });
+ })();
+ </script>
Property changes on: z3c.formext/trunk/src/z3c/formext/form.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/interfaces.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/interfaces.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/interfaces.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,102 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ExtJS integration.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+
+from z3c.form.interfaces import ITextWidget
+from z3c.form.interfaces import ISingleCheckBoxWidget
+from z3c.form.interfaces import IForm
+from z3c.form.interfaces import IButton
+from z3c.form.interfaces import IButtonAction
+from z3c.form.interfaces import ISelectionManager
+from z3c.formjs.interfaces import IAJAXRequestHandler
+from z3c.versionedresource.interfaces import IVersionedResource
+
+
+class IClientButton(IButton):
+ success = zope.schema.ASCIILine(
+ title=u'Success function',
+ required=False)
+
+ failure = zope.schema.ASCIILine(
+ title=u'Failure function',
+ required=False)
+
+class IClientButtonAction(IButtonAction):
+ success = zope.schema.ASCIILine(
+ title=u'Success function',
+ required=False)
+
+ failure = zope.schema.ASCIILine(
+ title=u'Failure function',
+ required=False)
+
+
+class IJSModule(IVersionedResource):
+ """A javascript module."""
+
+
+class IExtJSDateWidget(ITextWidget):
+ """Marker interface for an ExtJSDate widget.
+
+ When ExtJS sends back data for a date, it does not fit the
+ standard l10n locale specific date format. This Widget thus has a
+ special data converter associated with it that handles the format
+ given by ExtJS.
+ """
+
+
+class IExtJSSingleCheckBoxWidget(ISingleCheckBoxWidget):
+ """Marker interface for an ExtJSSingleChexkBox widget.
+
+ When ExtJS sends back data for a checkbox, it does not fit the
+ standard checkbox format. This Widget thus has a special data
+ converter associated with it that handles the format given by
+ ExtJS.
+ """
+
+
+class IExtJSComponent(zope.interface.Interface):
+ """An object that represents and ExtJS component.
+
+ This object provides a serialized representation of itself that
+ can be used as the config object for an extjs component.
+ """
+
+ def getConfig(json=False):
+ """Return the configuration object of this component.
+
+ If the json flag is passed, the result is returned in json
+ serialization form.
+ """
+
+class IJSProperties(ISelectionManager):
+ """A Selection Manager for JavaScript Properties"""
+
+
+class IExtJSForm(IAJAXRequestHandler, IForm):
+
+ jsonResponse = zope.schema.Field(
+ title=u'JSON Response',
+ description=u'The response data structure')
+
+ response = zope.schema.Text(
+ title=u'Response',
+ description=u'A json encoded response')
Property changes on: z3c.formext/trunk/src/z3c/formext/interfaces.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/jsmodule.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/jsmodule.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/jsmodule.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,68 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Versioned Resources Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+import cjson
+from zope.publisher.interfaces import NotFound
+from zope.app.publisher.browser import resource, resources
+from zope.app.publisher.browser import directoryresource
+from zope.app.publisher.browser import fileresource
+from zope.app.publisher.browser import pagetemplateresource
+from zope.viewlet.viewlet import ViewletBase
+from zope.viewlet.viewlet import JavaScriptViewlet
+from z3c.versionedresource import interfaces
+from z3c.versionedresource.resource import FileResource
+
+from z3c.formext import interfaces
+
+class JSModuleResourceFactory(fileresource.FileResourceFactory):
+ resourceClass = FileResource
+
+ def __init__(self, path, checker, name, namespace, dependencies):
+ super(JSModuleResourceFactory, self).__init__(path, checker, name)
+ self.namespace = namespace
+ self.dependencies = dependencies
+
+ def __call__(self, request):
+ resource = super(JSModuleResourceFactory, self).__call__(request)
+ resource.namespace = self.namespace
+ resource.dependencies = self.dependencies
+ return resource
+
+
+class JSModulesViewlet(ViewletBase):
+
+ def render(self):
+ result = {}
+ for name, resource in zope.component.getAdapters((self.request,),
+ interfaces.IJSModule):
+ result[resource.namespace] = {
+ "name":resource.namespace,
+ "scripts":[resource()],
+ "requires":resource.dependencies,
+ "url":resource(),
+ "dependencies":resource.dependencies}
+ return ('<script type="text/javascript">'
+ 'Ext.ns("z3c.formext"); '
+ 'z3c.formext.JS_MODULES = %s;'
+ 'for (pkg in z3c.formext.JS_MODULES){'
+ 'z3c.formext.ModuleLoader.register(z3c.formext.JS_MODULES[pkg]);'
+ '}'
+ '</script>' % cjson.encode(result))
+
+LoaderViewlet = JavaScriptViewlet('z3c.formext.loader.js')
Property changes on: z3c.formext/trunk/src/z3c/formext/jsmodule.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/jsoncompat.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/jsoncompat.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/jsoncompat.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,54 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ExtJS Component representation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+try:
+ # the fast way
+ import cjson
+ encode = cjson.encode
+ jsonDecode = cjson.decode
+except ImportError:
+ try:
+ # the python 2.6 way
+ import json
+ encode = json.dumps
+ jsonDecode = json.loads
+ except ImportError:
+ # the slow python < 2.6 way
+ import simplejson
+ encode = simplejson.dumps
+ jsonDecode = simplejson.loads
+
+from zope.i18n import translate
+
+def translateObject(o):
+ if isinstance(o, list):
+ for index, value in enumerate(o):
+ o[index] = translateObject(value)
+ elif isinstance(o, tuple):
+ o = [translateObject(value) for value in o]
+ elif isinstance(o, dict):
+ for key, value in o.items():
+ o[key] = translateObject(value)
+ elif isinstance(o, unicode):
+ o = translate(o)
+ return o
+
+def jsonEncode(o):
+ o = translateObject(o)
+ return encode(o)
Property changes on: z3c.formext/trunk/src/z3c/formext/jsoncompat.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/jsoncompat.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/jsoncompat.txt (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/jsoncompat.txt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,56 @@
+=================================
+JSON Encoder Compatibility Module
+=================================
+
+Different versions of python have different json encoding modules
+available to them. There is also c-optimized json encoder optionally
+available. The ``z3c.formext.jsoncompat`` module picks the best
+available json encoder.
+
+
+i18n support
+------------
+
+Since much of the json that gets produced ends up being used in the
+UI, it's important that all translatable strings are translated before
+being sent to the encoder (which doesn't know about translation). The
+``jsonEncode`` function in ``z3c.formext.jsoncompat`` translates all
+the messages before passing them on to the json encoder. Let's try
+this out.
+
+
+ >>> from zope.i18n.simpletranslationdomain import SimpleTranslationDomain
+ >>> test = SimpleTranslationDomain('test',
+ ... {
+ ... ('es', u'Hello'): u'Hola',
+ ... ('es', u"What's up"): u'Que pasa',
+ ... ('es', u'How are you'): u'Como estas',
+ ... })
+
+ >>> from zope.component import provideUtility
+ >>> provideUtility(test, name='test')
+
+ >>> from zope.i18nmessageid.message import MessageFactory
+ >>> _ = MessageFactory('test')
+
+ >>> data = {
+ ... 'aList': [_(u'Hello'), _(u"What's up"), _(u'How are you')],
+ ... 'aTuple':(_(u'Hello'), _(u"What's up"), _(u'How are you')),
+ ... 'aNest': ([{'foo':_(u'How are you')}, _('Hello')], _(u"What's up")),
+ ... }
+
+ >>> from z3c.formext import jsoncompat
+ >>> import zope.i18n
+ >>> jsoncompat.translate = lambda s: zope.i18n.translate(s, target_language='es')
+
+ >>> zope.i18n.translate(_(u'Hello'), target_language='es')
+ u'Hola'
+
+
+ >>> from pprint import pprint as pp
+ >>> pp(jsoncompat.jsonDecode(jsoncompat.jsonEncode(data)))
+ {'aList': ['Hola', 'Que pasa', 'Como estas'],
+ 'aNest': [[{'foo': 'Como estas'}, 'Hola'], 'Que pasa'],
+ 'aTuple': ['Hola', 'Que pasa', 'Como estas']}
+
+ >>> jsoncompat.translate = zope.i18n.translate
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/jsoncompat.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/meta.zcml
===================================================================
--- z3c.formext/trunk/src/z3c/formext/meta.zcml (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/meta.zcml 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,16 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta"
+ xmlns:zcml="http://namespaces.zope.org/zcml">
+
+ <meta:directives namespace="http://namespaces.zope.org/z3c">
+
+ <meta:directive
+ name="jsmodule"
+ schema="z3c.formext.metadirectives.IJSModuleDirective"
+ handler=".zcml.jsmoduleHandler"
+ />
+
+ </meta:directives>
+
+</configure>
Property changes on: z3c.formext/trunk/src/z3c/formext/meta.zcml
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/metadirectives.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/metadirectives.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/metadirectives.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+"""jsmodule meta directives
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.app.publisher.browser.metadirectives import IBasicResourceInformation
+from zope.schema import TextLine
+from zope.configuration.fields import Path, Tokens
+
+class IJSModuleDirective(IBasicResourceInformation):
+ """
+ Defines a browser resource
+ """
+
+ name = TextLine(
+ title=u"The name of the resource",
+ description=u"""
+ This is the name used in resource urls. Resource urls are of
+ the form site/@@/resourcename, where site is the url of
+ "site", a folder with a site manager.
+
+ We make resource urls site-relative (as opposed to
+ content-relative) so as not to defeat caches.""",
+ required=False
+ )
+
+ namespace = TextLine(
+ title=u"The namespace of the JavaScript module",
+ description=u'''
+ This is the namespace used to reference the javascript module
+ in module loaders. If not specified it will be extracted from
+ the file by looking for Ext.ns("my.module.namespace");
+ declarations.''',
+ required=False
+ )
+
+ dependencies = Tokens(
+ title=u"Other modules that this module depends on",
+ description=u'''
+ A whitespace-separated list of other modules that this module
+ depends on.
+ ''',
+ value_type=TextLine(),
+ required=False
+ )
+
+ file = Path(
+ title=u"File",
+ description=u"The file containing the JavaScript Module.",
+ required=False
+ )
Property changes on: z3c.formext/trunk/src/z3c/formext/metadirectives.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/resources/form-script.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/resources/form-script.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/resources/form-script.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,12 @@
+z3c.formext.ModuleLoader.load(
+ 'z3c.formext.form',
+ function(){
+ if (config.ownerCt){
+ var container = Ext.getCmp(config.ownerCt);
+ delete config.ownerCt;
+ container.add(new z3c.formext.form.Z3CFormPanel(config));
+ container.doLayout();
+ } else if (config.renderTo){
+ new z3c.formext.form.Z3CFormPanel(config);
+ }
+ });
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/resources/form-script.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,85 @@
+Ext.ns('z3c.formext.form');
+
+(function(){
+
+var mod = z3c.formext.form;
+
+mod.Z3CFormPanel = Ext.extend(
+ Ext.FormPanel,
+ {
+ initComponent: function(){
+ this.items = [
+ {
+ xtype: 'panel',
+ hidden: true,
+ cls: 'x-form-invalid-msg',
+ id: this.id+'-errors'
+ }].concat(this.items || []);
+
+ if (this.buttons){
+ for (var i=0; i < this.buttons.length; i++){
+ var button = this.buttons[i];
+ if (typeof button.handler === 'object'){
+ var thisForm = this;
+ //damn, we have to do a copy. hopefully a shallow copy is good enough.
+ button.handlerConfig = {};
+ for (var prop in button.handler){
+ button.handlerConfig[prop] = button.handler[prop];
+ }
+ if (typeof button.handlerConfig.success === 'string'){
+ button.handlerConfig.success = this.handlers[button.handlerConfig.success];
+ }
+ if (typeof button.handlerConfig.failure === 'string'){
+ button.handlerConfig.failure = this.handlers[button.handlerConfig.failure];
+ }
+ Ext.applyIf(
+ button.handlerConfig,
+ {
+ url: this.ajaxHandlers[button.id],
+ failure: this.handleErrors,
+ success: function(){}
+ });
+ button.handler = function(){
+ this.handlerConfig.method = thisForm.getForm().getValues(true) ? 'POST':'GET';
+ thisForm.getForm().doAction(
+ 'submit',
+ this.handlerConfig
+ );
+ };
+ }
+ }
+
+ }
+ mod.Z3CFormPanel.superclass.initComponent.call(this);
+ },
+
+ afterRender: function(){
+ mod.Z3CFormPanel.superclass.afterRender.call(this);
+ this.getForm().items.each(
+ function(field){
+ if (field.title){
+ field.on(
+ 'render',
+ function(){
+ Ext.QuickTips.register(
+ {
+ target: this.getEl(),
+ text: this.title
+ });
+
+ }, field, {single:true});
+ }
+ });
+ },
+
+ handleErrors: function(form, action){
+ var errors = Ext.getCmp(this.id+'-errors');
+ if (errors) {
+ errors.getEl().update(action.result.formErrors.join('\n'));
+ errors.show();
+ }
+ }
+ });
+Ext.reg('z3c-formpanel', mod.Z3CFormPanel);
+
+})();
Property changes on: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.form.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,136 @@
+//see http://extjs.com/forum/showthread.php?t=34808
+/**
+ * Javascript ResourceManager definition inc preloaders
+ * @author Adam J Benson
+ * @todo Add in the CSS on demand loaders
+ * @version 1.3.1
+ *
+ * EXAMPLE PACKAGE DEFINITION: (All packages should be defined prior to trying to load them)
+ *
+ * var pack={
+ * name: "ExamplePack",
+ * description:"This is an example 'package object' which is used by the resource managers",
+ * scripts:["url1","url2","url3","url4"],
+ * callback: function(){},
+ * requires:["packageName"] //Dependancies for this package to be loaded before it...
+ * }
+ *
+ * EXAMPLE USAGE (with callback)
+ * ScriptManager('Common', function(){
+ * //YOUR CODE HERE
+ * var simple = new Ext.FormPanel();
+ * });
+ *
+ * EXAMPLE USAGE (without callback)
+ * ScriptManager('Common');
+ *
+ */
+Ext.ns('z3c.formext');
+
+var ScriptManager = window.ScriptManager = function( packageName, callback ) {
+ /**
+ * Constructor
+ */
+ ScriptManager.load(packageName,callback);
+};
+
+z3c.formext.ModuleLoader = ScriptManager;
+
+ScriptManager.prototype = {
+ registeredPackages:[], //List of packages that have been registered
+ loadQueue:[], //Queue of packages that have been requested to load (we load sequentially to avoid dependancy issues)
+ processing: false,
+
+ register: function(packageConfig){
+ //Register the package for loading
+ this.registeredPackages[packageConfig.name]=packageConfig;
+ this.registeredPackages[packageConfig.name].loaded=false;
+ },
+
+ load: function(packageName, callback) {
+ /**
+ * Load the specified package, with an optional callback (in addition to the callback configured by the packages config)
+ */
+ if(!this.registeredPackages[packageName]){
+ //Package Not Found
+ if (console && console.log){
+ console.log("The package "+packageName+" has not been registered but was requested by a script.");
+ }
+ return;
+ }
+ if(this.registeredPackages[packageName].loaded==true){
+ //Already loaded... Trigger the scripted callback
+ ScriptManager.prototype.nextQueueItem();
+ if(callback)callback.call();
+ return;
+ }
+ if(this.registeredPackages[packageName].requires){
+ //Required Scripts
+ var required=this.registeredPackages[packageName].requires;
+ for(var i=0, m=required.length; i < m;i++){
+ if(this.registeredPackages[required[i]].loaded!=true){
+ //There is a dependant not loaded, so add self to the queue and load it instead...
+
+ /* the original author pushed the callback function onto the
+ queue. But then the callback would be called for each
+ requirement being loaded. Now we pass in an empty function
+ as the call back so only the requested package's load does
+ the callback */
+ this.loadQueue.push([packageName,/*callback*/ function(){}]);
+ this.load(required[i]);
+ }
+ }
+ }
+ if(ScriptManager.processing){
+ //Already proccessing a script (or document body hasn't finished loading)! Add it to the queue...
+ this.loadQueue.push([packageName,callback]);
+ return;
+ }
+ ScriptManager.processing=true;
+ ScriptManager.srcScript(this.registeredPackages[packageName], callback);
+ },
+ genScriptNode : function() {
+ var scriptNode = document.createElement("script");
+ scriptNode.setAttribute("type", "text/javascript");
+ return scriptNode;
+ },
+ srcScript : function(packageConfig, callback) {
+ var scriptNode = ScriptManager.prototype.genScriptNode();
+ scriptNode.setAttribute("src", packageConfig.scripts[0]);
+ scriptNode.onload = scriptNode.onreadystatechange = function() {
+ if (!scriptNode.readyState || scriptNode.readyState == "loaded" || scriptNode.readyState == "complete" ||
+ scriptNode.readyState == 4 && scriptNode.status == 200) {
+ setTimeout(
+ function(){
+ ScriptManager.registeredPackages[packageConfig.name].loaded=true;
+ ScriptManager.prototype.scriptLoaded();
+ if(packageConfig.callback)packageConfig.callback.call();
+ if(callback)callback.call();
+ }, 200);
+ }
+ };
+ var headNode = document.getElementsByTagName("head")[0];
+ headNode.appendChild(scriptNode);
+ },
+ nextQueueItem : function(){
+ if(this.loadQueue.length > 0){
+ var currentItem=this.loadQueue.shift();
+ ScriptManager.prototype.load(currentItem[0],currentItem[1]);
+ }
+
+ },
+ scriptLoaded : function(){
+ /**
+ * Callback function for whenever a script finishes loading
+ */
+ ScriptManager.processing=false;
+ ScriptManager.prototype.nextQueueItem();
+ }
+
+};
+ScriptManager.register = ScriptManager.prototype.register;
+ScriptManager.load = ScriptManager.prototype.load;
+ScriptManager.loadQueue = ScriptManager.prototype.loadQueue;
+ScriptManager.registeredPackages = ScriptManager.prototype.registeredPackages;
+ScriptManager.srcScript = ScriptManager.prototype.srcScript;
+ScriptManager.nextQueueItem=ScriptManager.prototype.nextQueueItem();
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/resources/z3c.formext.loader.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/testing.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/testing.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/testing.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test Setup.
+
+$Id$
+"""
+import zope.component
+import zope.interface
+from zope.traversing.testing import setUp as setupTraversing
+from zope.traversing.interfaces import IContainmentRoot
+
+import z3c.form.testing
+from z3c.form.interfaces import IButtonAction
+from z3c.form.testing import setupFormDefaults
+from z3c.formext import component, form, converter
+
+TestRequest = z3c.form.testing.TestRequest
+
+def setupExtJSComponents():
+ zope.component.provideAdapter(component.TextField)
+ zope.component.provideAdapter(component.TextArea)
+ zope.component.provideAdapter(component.DateField)
+ zope.component.provideAdapter(component.FormPanel)
+ zope.component.provideAdapter(component.ExtFormPanel)
+ zope.component.provideAdapter(component.ComboBox)
+ zope.component.provideAdapter(component.CheckBox)
+ zope.component.provideAdapter(component.RadioGroup)
+ zope.component.provideAdapter(component.Button)
+ zope.component.provideAdapter(component.ClientButton)
+ zope.component.provideAdapter(form.ClientButtonAction,
+ provides=IButtonAction)
+
+def setupFormExt():
+ setupExtJSComponents()
+ setupFormDefaults()
+ setupTraversing()
+ zope.component.provideAdapter(converter.ExtJSDateDataConverter)
+ zope.component.provideAdapter(converter.ExtJSSingleCheckBoxDataConverter)
+
+
+class Context(object):
+ zope.interface.implements(IContainmentRoot)
+ __name__ = ''
+
+class TestingForm(object):
+
+ __name__ = 'index.html'
+
+ def getContent(self):
+ if self.context is None:
+ self.context = Context()
+ return self.context
Property changes on: z3c.formext/trunk/src/z3c/formext/testing.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/tests/__init__.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/__init__.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/__init__.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+# Make a package.
Property changes on: z3c.formext/trunk/src/z3c/formext/tests/__init__.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,5 @@
+Ext.ns("my.module");
+
+my.module.someFunc = function(){
+ alert("this function lives in my.module!");
+};
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.module.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,2 @@
+//a different js module that depends on my.module
+Ext.ns("my.other");
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/my.other.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+/* an example js file from a third party that does not specify a namespace. */
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/tests/jsmodules/thirdParty.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/tests/script.js
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/script.js (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/script.js 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1 @@
+alert("foo!");
\ No newline at end of file
Property changes on: z3c.formext/trunk/src/z3c/formext/tests/script.js
___________________________________________________________________
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/tests/test_doc.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/tests/test_doc.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/tests/test_doc.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""z3c.form Test Module
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import unittest
+import re
+
+from zope.testing import doctest, renormalizing
+from zope.app.testing import placelesssetup
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from z3c.form import testing
+
+class ITestLayer(IDefaultBrowserLayer):
+ pass
+
+def test_suite():
+ return unittest.TestSuite((
+
+ doctest.DocFileSuite(
+ '../README.txt',
+ setUp=testing.setUp, tearDown=testing.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+
+ doctest.DocFileSuite(
+ '../zcml.txt',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+
+ doctest.DocFileSuite(
+ '../jsoncompat.txt',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+
+ doctest.DocFileSuite(
+ '../component.txt',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+
+ doctest.DocFileSuite(
+ '../form.txt',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+
+ ))
Property changes on: z3c.formext/trunk/src/z3c/formext/tests/test_doc.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/widget.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/widget.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/widget.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ExtJS Widgets.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema.interfaces
+import zope.component
+
+from z3c.form.browser.checkbox import SingleCheckBoxWidget
+from z3c.form.browser.text import TextWidget
+from z3c.form.interfaces import IFieldWidget
+from z3c.form.interfaces import IFormLayer
+from z3c.form.interfaces import ITextWidget
+from z3c.form.interfaces import NOVALUE
+from z3c.form.widget import FieldWidget, Widget
+
+from z3c.formext import interfaces
+
+class ExtJSDateWidget(TextWidget):
+ zope.interface.implementsOnly(interfaces.IExtJSDateWidget)
+
+ at zope.component.adapter(zope.schema.interfaces.IField, IFormLayer)
+ at zope.interface.implementer(IFieldWidget)
+def ExtJSDateFieldWidget(field, request):
+ """IFieldWidget factory for TextWidget."""
+ return FieldWidget(field, ExtJSDateWidget(request))
+
+
+
+class ExtJSSingleCheckBoxWidget(SingleCheckBoxWidget):
+ zope.interface.implementsOnly(interfaces.IExtJSSingleCheckBoxWidget)
+
+ def extract(self, default=u'off'):
+ return Widget.extract(self, default=default)
+
+
+ at zope.component.adapter(zope.schema.interfaces.IField, IFormLayer)
+ at zope.interface.implementer(IFieldWidget)
+def ExtJSSingleCheckBoxFieldWidget(field, request):
+ """IFieldWidget factory for TextWidget."""
+ return FieldWidget(field, ExtJSSingleCheckBoxWidget(request))
Property changes on: z3c.formext/trunk/src/z3c/formext/widget.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/zcml.py
===================================================================
--- z3c.formext/trunk/src/z3c/formext/zcml.py (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/zcml.py 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,68 @@
+###############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+"""jsmodule directives
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+import os
+import re
+
+from zope.app.publisher.browser.pagetemplateresource import PageTemplateResourceFactory
+from zope.app.publisher.browser.resourcemeta import ResourceFactoryWrapper
+from zope.app.publisher.browser.resourcemeta import allowed_names
+from zope.component.zcml import handler
+from zope.configuration.exceptions import ConfigurationError
+from zope.interface import Interface
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.security.checker import CheckerPublic, NamesChecker
+
+from z3c.formext import interfaces, jsmodule
+from z3c.versionedresource.resource import FileResourceFactory
+
+
+EXTNamespaceRE = "((?<=Ext.ns\((\"|\'))|(?<=Ext.namespace\((\"|\'))).*(?=(\"|\'))"
+
+def jsmoduleHandler(_context, file, layer=IDefaultBrowserLayer,
+ permission='zope.Public', name=None,
+ namespace=None, dependencies=None):
+
+ if permission == 'zope.Public':
+ permission = CheckerPublic
+
+ checker = NamesChecker(allowed_names, permission)
+
+ if dependencies is None:
+ dependencies = []
+
+ if namespace is None:
+ contents = open(file, 'r').read()
+ match = re.search(EXTNamespaceRE, contents)
+ if not match:
+ raise ConfigurationError(
+ "No namespace was specified and no namespace could be extracted")
+ namespace = contents[match.start() : match.end()]
+ if name is None:
+ name = '%s.js' % namespace
+
+ factory = jsmodule.JSModuleResourceFactory(file, checker, name,
+ namespace, dependencies)
+
+ _context.action(
+ discriminator = ('resource', name, IBrowserRequest, layer),
+ callable = handler,
+ args = ('registerAdapter', factory, (layer,),
+ interfaces.IJSModule, name, _context.info),
+ )
Property changes on: z3c.formext/trunk/src/z3c/formext/zcml.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: z3c.formext/trunk/src/z3c/formext/zcml.txt
===================================================================
--- z3c.formext/trunk/src/z3c/formext/zcml.txt (rev 0)
+++ z3c.formext/trunk/src/z3c/formext/zcml.txt 2009-01-22 04:50:33 UTC (rev 94921)
@@ -0,0 +1,238 @@
+==================
+JavaScript Modules
+==================
+
+When writing a big application built on top of ExtJS, you often don't
+want to load *all* the client JavaScript at the same time. Instead,
+it is much better to load only the javascript necessary to run a
+particular part of the application. Unfortunately, JavaScript itself
+has no concept of a module which can be imported as needed, so the
+server side application must provide this concept.
+
+Here we provide a ``jsmodule`` zcml directive that allows you to
+declare a javascript file as a module (and also as a versioned
+resource). The idea is that by declaring a bunch of javascript
+modules up front, we can generate a dependency graph that can be sent
+to the client for use by a client side lazy module loader.
+
+Setup
+~~~~~
+
+In order to test the zcml directives, we have to do some setup. First
+lets set the path to some of our testing files.
+
+ >>> import os.path
+ >>> import z3c.formext.tests
+ >>> modulesdir = os.path.join(
+ ... os.path.dirname(z3c.formext.tests.__file__),
+ ... 'jsmodules')
+
+Since our jsmodules are just another incarnation of a versioned
+resource, we have to set up the version manager.
+
+ >>> from z3c.versionedresource import version
+ >>> manager = version.VersionManager('1.0.0')
+ >>> import zope.component
+ >>> zope.component.provideUtility(manager)
+
+Now we will create a resources view so we can traverse to the
+resources we will be creating.
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> from z3c.versionedresource import resource
+ >>> request = TestRequest()
+ >>> context = object()
+ >>> resources = resource.Resources(context, request)
+
+Finally, we have to set up the zcml context by loading the meta.zcml
+file:
+
+ >>> from zope.configuration import xmlconfig
+ >>> import z3c.formext
+ >>> context = xmlconfig.file('meta.zcml', z3c.formext)
+
+
+ZCML Directive
+~~~~~~~~~~~~~~
+
+Simple Modules
+--------------
+
+Using the ``jsmodule`` directive, we can now register a javascript
+module. All we need to specify is the file.
+
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsmodule
+ ... file="%s"
+ ... />
+ ... </configure>
+ ... """ % os.path.join(modulesdir, 'my.module.js') , context=context)
+
+We want all of our modules to have a namespace associated with them so
+they can be sanely referenced within the dependency graph and by the
+client-side lazy loader. If we do not specify the ``namespace``
+attribute in the zcml, as we did in this case, then the namespace will
+be extracted from the file. See the *Extracting Namespaces* section
+for how this works. Now the javascript resource can be accessed using
+a name generated from the extracted namespace (which just happens to
+be the same as the file name.
+
+ >>> moduleResource = resources.publishTraverse(request, '1.0.0')\
+ ... .publishTraverse(request, 'my.module.js')
+
+The resource we get is just a plain FileResource object except that it
+contains some additional metadata used by the dependency graph
+generator. In particular, we store the namespace and dependencies
+associated with this file resource.
+
+ >>> moduleResource
+ <FileResource u'.../z3c/formext/tests/jsmodules/my.module.js'>
+ >>> moduleResource.namespace
+ 'my.module'
+ >>> moduleResource.dependencies
+ []
+ >>> moduleResource()
+ 'http://127.0.0.1/@@/1.0.0/my.module.js'
+
+
+Modules with dependencies
+-------------------------
+
+We can also declar that a jsmodule has dependencies on other modules
+by specifying the ``dependencies`` attribute.
+
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsmodule
+ ... file="%s"
+ ... dependencies="my.module jQuery"
+ ... />
+ ... </configure>
+ ... """ % os.path.join(modulesdir, 'my.other.js') , context=context)
+
+This attribtute gets converted into a list.
+
+ >>> moduleResource = resources.publishTraverse(request, '1.0.0')\
+ ... .publishTraverse(request, 'my.other.js')
+ >>> moduleResource.dependencies
+ [u'my.module', u'jQuery']
+
+
+Third Party Modules
+-------------------
+
+In cases where we are using third party libraries which do not have
+namespaces defined within the file. We must specify the namespace
+manually. One example where the namespace extraction won't work is
+for jQuery. If the namespace extraction fails and we do not specify a
+namepsace manually, we get a configuration error:
+
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsmodule
+ ... file="%s"
+ ... />
+ ... </configure>
+ ... """ % os.path.join(modulesdir, 'thirdParty.js') , context=context)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-6.8
+ ConfigurationError: No namespace was specified and no namespace could be extracted
+
+We know however that jQuery uses the ``jQuery`` namespace so we can
+declare that manually and no namespace extraction will be attempted.
+
+ >>> context = xmlconfig.string("""
+ ... <configure
+ ... xmlns:z3c="http://namespaces.zope.org/z3c">
+ ... <z3c:jsmodule
+ ... file="%s"
+ ... namespace="jQuery"
+ ... />
+ ... </configure>
+ ... """ % os.path.join(modulesdir, 'thirdParty.js') , context=context)
+ >>> resources.publishTraverse(request, '1.0.0')\
+ ... .publishTraverse(request, 'jQuery.js')()
+ 'http://127.0.0.1/@@/1.0.0/jQuery.js'
+
+Note that the name of the resource is based on the namespace and *not*
+the filename. You can also specify a ``name`` attribtue manually, but
+this is not recommended.
+
+Listing ``JSModule``s
+---------------------
+
+Now that we have a couple JavaScript modules registered, we can get a
+list of all the modules pretty easily:
+
+ >>> import zope.component
+ >>> from z3c.formext import interfaces
+ >>> for name, resource in zope.component.getAdapters((request,), interfaces.IJSModule):
+ ... print resource.namespace.ljust(10), str(resource.dependencies).ljust(25), resource()
+ jQuery [] http://127.0.0.1/@@/1.0.0/jQuery.js
+ my.module [] http://127.0.0.1/@@/1.0.0/my.module.js
+ my.other [u'my.module', u'jQuery'] http://127.0.0.1/@@/1.0.0/my.other.js
+
+The ``JSModulesViewlet``
+------------------------
+
+We can also render a viewlet that makes all the jsmodule information
+available on the client.
+
+ >>> from z3c.formext import jsmodule
+ >>> viewlet = jsmodule.JSModulesViewlet(
+ ... 'fake context', request,
+ ... 'fake view','fake manager')
+ >>> print viewlet.render()
+ <script type="text/javascript">Ext.ns("z3c.formext"); z3c.formext.JS_MODULES = {"jQuery": {"url": "http://127.0.0.1/@@/1.0.0/jQuery.js", "dependencies": [], "requires": [], "name": "jQuery", "scripts": ["http://127.0.0.1/@@/1.0.0/jQuery.js"]}, "my.other": {"url": "http://127.0.0.1/@@/1.0.0/my.other.js", "dependencies": ["my.module", "jQuery"], "requires": ["my.module", "jQuery"], "name": "my.other", "scripts": ["http://127.0.0.1/@@/1.0.0/my.other.js"]}, "my.module": {"url": "http://127.0.0.1/@@/1.0.0/my.module.js", "dependencies": [], "requires": [], "name": "my.module", "scripts": ["http://127.0.0.1/@@/1.0.0/my.module.js"]}};for (pkg in z3c.formext.JS_MODULES){z3c.formext.ModuleLoader.register(z3c.formext.JS_MODULES[pkg]);}</script>
+
+
+Extracting Namespaces
+~~~~~~~~~~~~~~~~~~~~~
+
+In ExtJS, we can declare namespaces using the ``Ext.ns`` or
+``Ext.namespace`` functions. Namespaces for JavaScript modules are
+extracted by looking for these patterns in the JavaScript file.
+
+ >>> from z3c.formext import zcml
+
+Here we test the usage of Ext.ns with double quotes,
+
+ >>> import re
+ >>> contents = """
+ ... Ext.ns("my.module");
+ ... """
+ >>> match = re.search(zcml.EXTNamespaceRE, contents)
+ >>> print contents[match.start(): match.end()]
+ my.module
+
+and single quotes:
+
+ >>> contents = """
+ ... Ext.ns('my.module');
+ ... """
+ >>> match = re.search(zcml.EXTNamespaceRE, contents)
+ >>> print contents[match.start(): match.end()]
+ my.module
+
+Here we test the usage of Ext.namespace with double quotes,
+
+ >>> contents = """
+ ... Ext.namespace("my.module");
+ ... """
+ >>> match = re.search(zcml.EXTNamespaceRE, contents)
+ >>> print contents[match.start(): match.end()]
+ my.module
+
+and single quotes:
+
+ >>> contents = """
+ ... Ext.namespace('my.module');
+ ... """
+ >>> match = re.search(zcml.EXTNamespaceRE, contents)
+ >>> print contents[match.start(): match.end()]
+ my.module
Property changes on: z3c.formext/trunk/src/z3c/formext/zcml.txt
___________________________________________________________________
Added: svn:eol-style
+ native
More information about the Checkins
mailing list