Skip to content

Commit

Permalink
Initial copy of collective.z3cform.keywordwidget
Browse files Browse the repository at this point in the history
  • Loading branch information
Johan Beyers committed Oct 30, 2011
0 parents commit 1c9be3e
Show file tree
Hide file tree
Showing 16 changed files with 589 additions and 0 deletions.
88 changes: 88 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
Introduction
============

This product adds a Keyword widget (similar to Archetypes.Widget:KeywordWidget)
for plone.z3cform.


How To Use (Doc Tests):
=======================

>>> from z3c.form import testing
>>> testing.setupFormDefaults()
>>> import zope.interface
>>> import zope.schema
>>> from zope.schema.fieldproperty import FieldProperty

Use the Keywords field your field type:

>>> from collective.z3cform.keywordwidget.field import Keywords
>>> class IFoo(zope.interface.Interface):
...
... keywords = Keywords(title=u'Keywords')

>>> class Foo(object):
... zope.interface.implements(IFoo)
... keywords = FieldProperty(IFoo['keywords'])
...
... def __init__(self, keywords):
... self.keywords = keywords
...
... def __repr__(self):
... return '<%s %r>' % (self.__class__.__name__, self.name)

We need to make sure that the keywords property is indexed in portal_catalog.

First, we write the indexer. The indexer is a special adapter that adapts the type of an object
and provides the value of the attribute to be indexed.

>>> from plone.indexer.decorator import indexer
>>> @indexer(IFoo)
... def keywords(obj):
... return IFoo(obj).keywords

We need to register our indexer as a named adapter, where the name corresponds to
the index name. In ZCML, that may be::

<adapter name="keywords" factory=".indexers.keywords" />

For testing purpoese, we will register it directly.

>>> from zope.component import provideAdapter
>>> provideAdapter(keywords, name='keywords')

Now we add a form in which the widget will be rendered:

Specify the KeywordWidget factory ('KeywordFieldWidget') as the field's widgetFactory.

>>> from z3c.form.testing import TestRequest
>>> from z3c.form import form, field
>>> from collective.z3cform.keywordwidget.widget import KeywordFieldWidget

>>> class FooAddForm(form.AddForm):
...
... fields = field.Fields(IFoo)
... fields['keywords'].widgetFactory = KeywordFieldWidget
...
... def create(self, data):
... return Foo(**data)
...
... def add(self, object):
... self.context[object.id] = object
...
... def nextURL(self):
... return 'index.hml'


Create, update and render the form:

>>> root = app
>>> request = TestRequest()

>>> addForm = FooAddForm(root, request)
>>> addForm.update()

>>> print addForm.render()



6 changes: 6 additions & 0 deletions collective/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
6 changes: 6 additions & 0 deletions collective/z3cform/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
128 changes: 128 additions & 0 deletions collective/z3cform/keywordwidget/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
Introduction
============

This product adds a Keyword widget (similar to Archetypes.Widget:KeywordWidget)
for plone.z3cform.


How To Use (Doc Tests):
=======================

>>> from z3c.form import testing
>>> testing.setupFormDefaults()

In your interface schema, use the Keywords field as your field type:

>>> import zope.interface
>>> import zope.schema
>>> from zope.schema.fieldproperty import FieldProperty
>>> from collective.z3cform.keywordwidget.field import Keywords
>>> class IFoo(zope.interface.Interface):
...
... id = zope.schema.TextLine(
... title=u'ID',
... readonly=True,
... required=True
... )
...
... keywords = Keywords(title=u'Keywords')


Let's now create a class that implements our interface.

>>> from AccessControl.Owned import Owned
>>> class Foo(object, Owned):
... zope.interface.implements(IFoo)
... id = FieldProperty(IFoo['id'])
... keywords = FieldProperty(IFoo['keywords'])
...
... def __init__(self, id, keywords):
... self.id = id
... self.keywords = keywords

For the keywordwidget to work, we need to make sure that the keywords
property is indexed in portal_catalog.

First, we write the indexer. The indexer is a special adapter that adapts the type of an object
and provides the value of the attribute to be indexed.

>>> from plone.indexer.decorator import indexer
>>> @indexer(IFoo)
... def keywords(obj):
... return IFoo(obj).keywords

We need to register our indexer as a named adapter, where the name corresponds to
the index name. In ZCML, that may be::

<adapter name="keywords" factory=".indexers.keywords" />

For testing purpoese, we will register it directly.

>>> from zope.component import provideAdapter
>>> provideAdapter(keywords, name='keywords')

Now we add a form in which the widget will be rendered:

Specify the KeywordWidget factory ('KeywordFieldWidget') as the field's widgetFactory.

>>> from z3c.form.testing import TestRequest
>>> from z3c.form import form, field
>>> from collective.z3cform.keywordwidget.widget import KeywordFieldWidget

>>> class FooAddForm(form.AddForm):
...
... fields = field.Fields(IFoo)
... fields['keywords'].widgetFactory = KeywordFieldWidget
...
... def create(self, data):
... return Foo(**data)
...
... def add(self, object):
... self.context[str(object.id)] = object
...
... def nextURL(self):
... return 'index.html'


Create an AddForm:

>>> request = TestRequest()
>>> addForm = FooAddForm(portal, request)
>>> addForm.update()

Check for the keyword widget and render it:

>>> addForm.widgets.keys()
['id', 'keywords']

>>> addForm.widgets['keywords'].render()
u'<div style="width: 45%; float: left">\n<span> Existing categories </span>\n<br />\n<select id="form-widgets-keywords"\n name="form.widgets.keywords:list"\n class="keyword-widget required keywords-field"\n multiple="multiple" size="14" style="width: 100%;">\n\n</select>\n</div>\n\n<div style="width: 45%; float: right;">\n<span>New categories</span>\n<br />\n<textarea id="form-widgets-keywords"\n name="form.widgets.keywords:list" cols="15"\n rows="13" wrap="off">\n</textarea>\n</div>\n\n<input name="form.widgets.keywords-empty-marker"\n type="hidden" value="1" />\n\n<div class="visualClear"><!-- --></div>\n'

Let's now submit the addform with data:

>>> request = TestRequest(form={
... 'form.widgets.id': u'myobject',
... 'form.widgets.keywords': [u'chocolate', u'vanilla'],
... 'form.buttons.add': u'Add'}
... )

>>> addForm = FooAddForm(portal, request)
>>> addForm.update()

Check that the object has been created:

>>> portal['myobject']
<Foo object at ...>

Check that the keywords attr has been set:

>>> portal['myobject'].keywords
[u'chocolate', u'vanilla']

Render the widget again and check that the keywords are present and selected:


>>> addForm.widgets['keywords'].render()
u'<div style="width: 45%; float: left">\n<span> Existing categories </span>\n<br />\n<select id="form-widgets-keywords"\n name="form.widgets.keywords:list"\n class="keyword-widget required keywords-field"\n multiple="multiple" size="14" style="width: 100%;">\n\n \n <option id="form-widgets-keywords-0"\n value="chocolate" selected="selected">chocolate</option>\n\n \n \n \n <option id="form-widgets-keywords-1" value="vanilla"\n selected="selected">vanilla</option>\n\n \n \n</select>\n</div>\n\n<div style="width: 45%; float: right;">\n<span>New categories</span>\n<br />\n<textarea id="form-widgets-keywords"\n name="form.widgets.keywords:list" cols="15"\n rows="13" wrap="off">\n</textarea>\n</div>\n\n<input name="form.widgets.keywords-empty-marker"\n type="hidden" value="1" />\n\n<div class="visualClear"><!-- --></div>\n'


Empty file.
43 changes: 43 additions & 0 deletions collective/z3cform/keywordwidget/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:z3c="http://namespaces.zope.org/z3c"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
i18n_domain="collective.z3cform.datepicker">

<genericsetup:registerProfile
name="testing"
title="collective.z3cform.keywordwidget testing profile"
directory="profiles/testing"
for="Products.CMFPlone.interfaces.ITestCasePloneSiteRoot"
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

<include package="plone.z3cform" />

<class class=".widget.KeywordWidget">
<require permission="zope.Public"
interface=".interfaces.IKeywordWidget" />
</class>

<adapter factory=".widget.KeywordFieldWidget" />

<z3c:widgetTemplate
mode="input"
widget=".interfaces.IKeywordWidget"
layer="z3c.form.interfaces.IFormLayer"
template="keyword_input.pt"
/>

<z3c:widgetTemplate
mode="display"
widget=".interfaces.IKeywordWidget"
layer="z3c.form.interfaces.IFormLayer"
template="keyword_display.pt"
/>

<adapter
factory=".field.KeywordsDataConverter"
/>

</configure>
42 changes: 42 additions & 0 deletions collective/z3cform/keywordwidget/field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import zope.component
import zope.schema
import zope.interface
import z3c.form.converter

import interfaces

class Keywords(zope.schema.List):
"""A field representing a set."""
zope.interface.implements(interfaces.IKeywordCollection)
unique = True
value_type = zope.schema.TextLine()


class KeywordsDataConverter(z3c.form.converter.BaseDataConverter):
"""A special converter between collections and sequence widgets."""

zope.component.adapts(interfaces.IKeywordCollection, interfaces.IKeywordWidget)

def toWidgetValue(self, value):
collectionType = self.field._type
if isinstance(collectionType, tuple):
collectionType = collectionType[-1]

if value:
return collectionType(value)
else:
return collectionType()

def toFieldValue(self, value):
"""See interfaces.IDataConverter
"""
widget = self.widget
if widget.terms is None:
widget.updateTerms()
collectionType = self.field._type
if isinstance(collectionType, tuple):
collectionType = collectionType[-1]

return collectionType(value)


10 changes: 10 additions & 0 deletions collective/z3cform/keywordwidget/interfaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import zope.schema
import z3c.form.interfaces

class IKeywordWidget(z3c.form.interfaces.ISequenceWidget):
"""A keyword widget.
"""

class IKeywordCollection(zope.schema.interfaces.ICollection):
""" Marker interfaces for keyword collections
"""
19 changes: 19 additions & 0 deletions collective/z3cform/keywordwidget/keyword_display.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<span id="" class=""
tal:attributes="id view/id;
class view/klass;
style view/style;
title view/title;
lang view/lang;
onclick view/onclick;
ondblclick view/ondblclick;
onmousedown view/onmousedown;
onmouseup view/onmouseup;
onmouseover view/onmouseover;
onmousemove view/onmousemove;
onmouseout view/onmouseout;
onkeypress view/onkeypress;
onkeydown view/onkeydown;
onkeyup view/onkeyup">

<tal:block condition="view/formatted_value" content="structure view/formatted_value"/>
</span>
Loading

0 comments on commit 1c9be3e

Please sign in to comment.