[Zope3-checkins] CVS: Zope3/src/zope/app/browser/form - configure.zcml:1.8 vocabularywidget.py:1.3
Fred L. Drake, Jr.
fred@zope.com
Wed, 21 May 2003 17:11:54 -0400
Update of /cvs-repository/Zope3/src/zope/app/browser/form
In directory cvs.zope.org:/tmp/cvs-serv28591/app/browser/form
Modified Files:
configure.zcml vocabularywidget.py
Log Message:
Lots of refactoring, separating out some helper classes to make reuse
easier in (at least some) advanced applications.
=== Zope3/src/zope/app/browser/form/configure.zcml 1.7 => 1.8 ===
--- Zope3/src/zope/app/browser/form/configure.zcml:1.7 Tue May 20 12:10:27 2003
+++ Zope3/src/zope/app/browser/form/configure.zcml Wed May 21 17:11:23 2003
@@ -140,6 +140,15 @@
factory=".vocabularywidget.VocabularyMultiFieldEditWidget"
/>
+ <view
+ permission="zope.Public"
+ type="zope.publisher.interfaces.browser.IBrowserPresentation"
+ allowed_interface="zope.app.interfaces.browser.form.IVocabularyQueryView"
+ for="zope.schema.interfaces.IIterableVocabularyQuery"
+ name="widget-query-multi-helper"
+ factory=".vocabularywidget.IterableVocabularyQueryView"
+ />
+
<!-- Default simple display view -->
<browser:page
permission="zope.Public"
=== Zope3/src/zope/app/browser/form/vocabularywidget.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/browser/form/vocabularywidget.py:1.2 Tue May 20 12:10:27 2003
+++ Zope3/src/zope/app/browser/form/vocabularywidget.py Wed May 21 17:11:23 2003
@@ -20,11 +20,15 @@
"""
+from xml.sax.saxutils import quoteattr
+
from zope.app.browser.form import widget
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.interfaces.browser.form import IVocabularyQueryView
from zope.publisher.browser import BrowserView
from zope.component import getView
+from zope.schema.interfaces import IIterableVocabulary, IVocabularyQuery
+from zope.schema.interfaces import IIterableVocabularyQuery
# These widget factories delegate to the vocabulary on the field.
@@ -62,27 +66,49 @@
queryname = "widget-query-helper"
view = _get_vocabulary_widget(field, request, viewname)
query = field.vocabulary.getQuery()
+ if query is None and IIterableVocabulary.isImplementedBy(field.vocabulary):
+ query = IterableVocabularyQuery(vocabulary)
if query is not None:
queryview = getView(query, queryname, request)
view.setQuery(query, queryview)
return view
+class IterableVocabularyQuery:
+ """Simple query object used to invoke the simple selection mechanism."""
+
+ __implements__ = IIterableVocabularyQuery
+
+ def __init__(self, vocabulary):
+ self.vocabulary = vocabulary
+
+
# Widget implementation:
class ViewSupport:
"""Helper class for vocabulary and vocabulary-query widgets."""
- def __init__(self, context, request):
- self.context = context
- self.request = request
- self.field = None
-
def textForValue(self, term):
# Extract the value from the term. This can be overridden to
# support more complex term objects.
return term.value
+ def mkselectionlist(self, type, info, name):
+ L = ["<table>\n"]
+ for term, selected, disabled in info:
+ flag = ""
+ if selected:
+ flag = "checked "
+ if disabled:
+ flag += "disabled "
+ L.append("<tr><td>"
+ "<input type='%s' value='%s' name='%s' %s/>"
+ "</td>\n <td>%s</td>"
+ "</tr>\n"
+ % (type, term.value, name, flag, self.textForValue(term)))
+ L.append("</table>")
+ return ''.join(L)
+
class VocabularyWidgetBase(ViewSupport, widget.BrowserWidget):
"""Convenience base class for vocabulary-based widgets."""
@@ -92,23 +118,27 @@
extra = ""
type = "vocabulary"
+ def __init__(self, context, request):
+ self.request = request
+ self.context = None
+
def _getDefault(self):
# Override this since the context is not the field for
# vocabulary-based widgets.
- return self.field.default
+ return self.context.default
def setField(self, field):
- assert self.field is None
+ assert self.context is None
# only allow this to happen for a bound field
assert field.context is not None
- self.field = field
+ self.context = field
self.name = self._prefix + field.__name__
def __call__(self):
if self.haveData():
value = self._showData()
else:
- value = self.field.get(self.field.context)
+ value = self.context.get(self.context.context)
return self.render(value)
def render(self, value):
@@ -120,7 +150,7 @@
"""Simple single-selection display that can be used in many cases."""
def render(self, value):
- term = self.field.vocabulary.getTerm(value)
+ term = self.context.vocabulary.getTerm(value)
return self.textForValue(term)
@@ -153,7 +183,7 @@
def renderItems(self, value):
L = []
- vocabulary = self.context
+ vocabulary = self.context.vocabulary
cssClass = self.getValue('cssClass') or ''
if cssClass:
cssClass += "-item"
@@ -231,7 +261,7 @@
# vocabulary, so that need not be considered here
rendered_items = []
count = 0
- for term in self.context:
+ for term in self.context.vocabulary:
item_value = term.value
item_text = self.textForValue(term)
@@ -350,6 +380,12 @@
# This specifically isn't a widget in it's own right, but is a
# form of BrowserView (at least conceptually).
+ def __init__(self, context, request):
+ self.vocabulary = context.vocabulary
+ self.context = context
+ self.request = request
+ super(VocabularyQueryViewBase, self).__init__(context, request)
+
def setName(self, name):
assert not name.endswith(".")
self.name = name
@@ -377,3 +413,146 @@
# object (self.context), and returning a results object. If
# there isn't a query in the form, returns None.
return None
+
+ _performed_action = False
+
+ def performAction(self, value):
+ """Make sure the real action, performQueryAction(), only runs once."""
+ if self._performed_action:
+ return value
+ self._performed_action = True
+ return self.performQueryAction(value)
+
+
+ADD_DONE = "adddone"
+ADD_MORE = "addmore"
+MORE = "more"
+
+def _message(msgid, default):
+ msgid.default = default
+ return msgid
+
+
+class IterableVocabularyQueryView(VocabularyQueryViewBase):
+ """Query view for IIterableVocabulary objects without more
+ specific query views.
+
+ This should only be used (directly) for vocabularies for which
+ getQuery() returns None.
+ """
+
+ __implements__ = IVocabularyQueryView
+
+ queryResultBatchSize = 8
+
+ action = None
+
+ def setName(self, name):
+ VocabularyQueryViewBase.setName(self, name)
+ name = self.name
+ self.adddone_name = name + "." + ADD_DONE
+ self.addmore_name = name + "." + ADD_MORE
+ self.more_name = name + "." + MORE
+ self.query_index_name = name + ".start"
+ self.query_selections_name = name + ".picks"
+ #
+ get = self.request.form.get
+ if get(self.adddone_name):
+ self.action = ADD_DONE
+ elif get(self.addmore_name):
+ self.action = ADD_MORE
+ elif get(self.more_name):
+ self.action = MORE
+ try:
+ self.query_index = int(get(self.query_index_name, 0))
+ except ValueError:
+ self.query_index = 0
+ else:
+ if self.query_index < 0:
+ self.query_index = 0
+ self.query_selections = get(self.query_selections_name, [])
+ if not isinstance(self.query_selections, list):
+ self.query_selections = [self.query_selections]
+
+ def renderQueryInput(self):
+ # There's no query support, so we can't actually have input.
+ return ""
+
+ def getResults(self):
+ return self.vocabulary
+
+ def renderQueryResults(self, results, value):
+ # display query results batch
+ it = iter(results)
+ qi = self.query_index
+ have_more = True
+ try:
+ for xxx in range(qi):
+ it.next()
+ except StopIteration:
+ have_more = False
+ items = []
+ QS = []
+ try:
+ for i in range(qi, qi + self.queryResultBatchSize):
+ term = it.next()
+ disabled = term.value in value
+ selected = disabled
+ if term.value in self.query_selections:
+ QS.append(term.value)
+ selected = True
+ items.append((term, selected, disabled))
+ else:
+ # see if there's anything else:
+ it.next()
+ except StopIteration:
+ have_more = False
+ self.query_selections = QS
+ L = ["<div class='results'>\n",
+ self.mkselectionlist("checkbox",
+ items, self.query_selections_name), "\n",
+ self._mkbutton(ADD_DONE), "\n",
+ self._mkbutton(ADD_MORE, not have_more), "\n",
+ self._mkbutton(MORE, not have_more), "\n"]
+ if qi:
+ L.append("<input type='hidden' name='%s' value='%d' />\n"
+ % (self.query_index_name, qi))
+ L.append("</div>")
+ return ''.join(L)
+
+ _messages = {
+ ADD_DONE: _message(_("vocabulary-query-button-add-done"), "Add"),
+ ADD_MORE: _message(_("vocabulary-query-button-add-more"), "Add+More"),
+ MORE: _message(_("vocabulary-query-button-more"), "More"),
+ }
+
+ def _mkbutton(self, action, disabled=False):
+ msg = self._messages[action]
+ return ("<input name='%s.%s' type='submit' value=%s %s/>"
+ % (self.name, action,
+ quoteattr(self.translate(msg)),
+ (disabled and "disabled " or "")))
+
+ def translate(self, msgid):
+ # XXX This is where we should be calling on the translation service
+ return msgid.default
+
+ def performQueryAction(self, value):
+ if self.action == ADD_DONE:
+ value = self.addSelections(value)
+ self.query_index = 0
+ self.query_selections = []
+ elif self.action == ADD_MORE:
+ value = self.addSelections(value)
+ self.query_index += self.queryResultBatchSize
+ elif self.action == MORE:
+ self.query_index += self.queryResultBatchSize
+ elif self.action:
+ raise ValueError("unknown action in request: %r" % self.action)
+ return value
+
+ def addSelections(self, value):
+ for item in self.query_selections:
+ if item not in value and item in self.context.vocabulary:
+ value.append(item)
+ return value