zc.table-0.9.0/ 0000775 0001771 0002004 00000000000 12053234232 014344 5 ustar menesis menesis 0000000 0000000 zc.table-0.9.0/README.txt 0000664 0001771 0002004 00000000224 12053233200 016032 0 ustar menesis menesis 0000000 0000000 This is a Zope 3 extension that helps with the construction of (HTML) tables.
Features include dynamic HTML table generation, batching and sorting.
zc.table-0.9.0/src/ 0000775 0001771 0002004 00000000000 12053234232 015133 5 ustar menesis menesis 0000000 0000000 zc.table-0.9.0/src/zc/ 0000775 0001771 0002004 00000000000 12053234232 015547 5 ustar menesis menesis 0000000 0000000 zc.table-0.9.0/src/zc/__init__.py 0000664 0001771 0002004 00000000310 12053233200 017644 0 ustar menesis menesis 0000000 0000000 # this is a namespace package
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
zc.table-0.9.0/src/zc/table/ 0000775 0001771 0002004 00000000000 12053234232 016636 5 ustar menesis menesis 0000000 0000000 zc.table-0.9.0/src/zc/table/testing.py 0000664 0001771 0002004 00000002373 12053233200 020664 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""Testing helpers
$Id: testing.py 2520 2005-06-27 21:26:18Z benji $
"""
from zope import interface, component
import zc.table.interfaces
import zc.table.table
class SimpleFormatter(zc.table.table.Formatter):
interface.classProvides(zc.table.interfaces.IFormatterFactory)
def setUp(test):
gsm = component.getGlobalSiteManager()
gsm.registerUtility(SimpleFormatter, zc.table.interfaces.IFormatterFactory)
assert component.getUtility(zc.table.interfaces.IFormatterFactory) != None
def tearDown(test):
gsm = component.getGlobalSiteManager()
gsm.unregisterUtility(provided=zc.table.interfaces.IFormatterFactory)
zc.table-0.9.0/src/zc/table/README.txt 0000664 0001771 0002004 00000102372 12053233200 020333 0 ustar menesis menesis 0000000 0000000 ======
Tables
======
Tables are general purpose UI constructs designed to simplify presenting
tabular information. A table has a column set which collects columns and
manages configuration data.
We must register a faux resource directory in preparation::
>>> import zope.interface
>>> import zope.component
>>> import zope.publisher.interfaces
>>> @zope.component.adapter(zope.publisher.interfaces.IRequest)
... @zope.interface.implementer(zope.interface.Interface)
... def dummyResource(request):
... return lambda:'/@@/zc.table'
...
>>> zope.component.provideAdapter(dummyResource, name='zc.table')
Columns
=======
``Columns`` have methods to render a header and the contents of a cell based on
the item that occupies that cell. Here's a very simple example::
>>> from zope import interface
>>> from zc.table import interfaces
>>> class GetItemColumn:
... interface.implements(interfaces.IColumn)
... def __init__(self, title, name, attr):
... self.title = title
... self.name = name
... self.attr = attr # This isn't part of IColumn
... def renderHeader(self, formatter):
... return self.title
... def renderCell(self, item, formatter):
... return str(getattr(item, self.attr))
Note that the methods do not provide the
and
tags.
The title is for display, while the name is for identifying the column within
a collection of columns: a column name must be unique within a collection of
columns used for a table formatter.
`renderHeader` takes a formatter--the table formatter introduced in the section
immediately below this one. It has the responsibility of rendering the
contents of the header for the column. `renderCell` takes the item to be
rendered and the formatter, and is responsible for returning the cell contents
for the given item.
The formatter is passed because it contains references to a number of useful
values. The context and request are particularly important.
Columns may also support sorting by implementing the ISortableColumn interface.
This interface is comprised of two methods, `sort` and `reversesort`. Both
take the same rather large set of arguments: items, formatter, start, stop,
and sorters. At least two values should be unsurprising: the `items` are the
items to be sorted, the `formatter` is the table formatter. The `start` and
`stop` values are the values that are needed for the rendering, so some
implementations may be able to optimize to only give precise results for the
given range. The `sorters` are optional sub-sorters--callables with signatures
identical to `sort` and `reversesort` that are a further sort refinement that
an implementation may optionally ignore. If a column has two or
more values that will sort identically, the column might take advantage of any
sub-sorters to further sort the data.
The columns.py file has a number of useful base column classes. The
columns.txt file discusses some of them. For our examples here, we will use
the relatively simple and versatile zc.table.column.GetterColumn. It is
instantiated with two required values and two optional values::
title - (required) the title of the column.
getter - (required) a callable that is passed the item and the table
formatter; returns the value used in the cell.
cell_formatter - (optional) a callable that is passed the result of getter,
the item, and the table formatter; returns the formatted
HTML. defaults to a function that returns the result of
trying to convert the result to unicode.
name - (optional) the name of the column. The title is used if a name is
not specified.
It includes a reasonably simple implementation of ISortableColumn but does
not declare the interface itself. It tries to sort on the basis of the getter
value and can be customized simply by overriding the `getSortKey` method.
Let's import the GetterColumn and create some columns that we'll use later,
and then verify that one of the columns fully implements IColumn. We'll also
then declare that all three of them provide ISortableColumn and verify one of
them::
>>> from zc.table.column import GetterColumn
>>> columns = (
... GetterColumn(u'First', lambda i,f: i.a, subsort=True),
... GetterColumn(u'Second', lambda i,f: i.b, subsort=True),
... GetterColumn(u'Third', lambda i,f: i.c, subsort=True),
... )
>>> import zope.interface.verify
>>> zope.interface.verify.verifyObject(interfaces.IColumn, columns[0])
True
>>> for c in columns:
... interface.directlyProvides(c, interfaces.ISortableColumn)
...
>>> zope.interface.verify.verifyObject(
... interfaces.ISortableColumn, columns[0])
True
Formatters
==========
When a sequence of objects are to be turned into an HTML table, a
table.Formatter is used. The table package includes a simple implementation
of IFormatter as well as a few important variations.
The default Formatter is instantiated with three required arguments--
`context`, `request`, and `items`--and a long string of optional arguments
we'll discuss in a moment. The first two required arguments are reminiscent
of browser views--and in fact, a table formatter is a specialized browser
view. The `context` is the object for which the table formatter is being
rendered, and can be important to various columns; and the `request` is the
current request. The `items` are the full set of items on which the table will
give a view.
The first three optional arguments affect the display::
visible_column_names=None, batch_start=0, batch_size=0
visible_column_names are a list of column names that should be displayed; note
that even if a column is not visible, it may still affect other behavior such
as sorting, discussed for a couple of Formatter subclasses below.
batch_start is the item position the table should begin to render. batch_size
is the number of items the table should render; 0 means all.
The next optional argument, `prefix=None`, is particularly important when a
table formatter is used within a form: it sets a prefix for any form fields
and XML identifiers generated for the table or a contained element.
The last optional argument is the full set of columns for the table (not just
the ones curently visible). It is optional because it may be set instead as
a subclass attribute: the value itself is required on instances.
Lets create some data to format and instantiate the default Formatter.
Our formatters won't need the context, so we'll fake it. As an
exercise, we'll hide the second column.
>>> class DataItem:
... def __init__(self, a, b, c):
... self.a = a
... self.b = b
... self.c = c
>>> items = [DataItem('a0', 'b0', 'c0'),
... DataItem('a2', 'b2', 'c2'),
... DataItem('a1', 'b1', 'c1'),
... ]
>>> from zc.table import table
>>> import zope.publisher.browser
>>> request = zope.publisher.browser.TestRequest()
>>> context = None
>>> formatter = table.Formatter(
... context, request, items, visible_column_names=('First', 'Third'),
... columns=columns)
>>> zope.interface.verify.verifyObject(
... interfaces.IFormatter, formatter)
True
The simplest way to use a table formatter is to call it, asking the formatter
to render the entire table::
>>> print formatter()
First
Third
a0
c0
a2
c2
a1
c1
If you want more control over the output then you may want to call methods
on the formatter that generate various parts of the output piecemeal. In
particular, getRows, getHeaders, and getCells exist only for this sort of use.
Here is an example of getRows in use to generate even and odd rows and a
column with cells in a special class:
>>> html = '
\n'
>>> html += '
\n'+ formatter.renderHeaders() + '
\n'
>>> for index, row in enumerate(formatter.getRows()):
... if index % 2:
... html += '
'
... else:
... html += '
'
... for index, cell in enumerate(row):
... if index == 0:
... html += '
'
... else:
... html += '
'
... html += cell + '
'
... html += '
\n'
>>> html += '
'
>>> print html
First
Third
a0
c0
a2
c2
a1
c1
However, the formatter provides some simple support for style sheets, since it
is the most common form of customization. Each formatter has an attribute
called ``cssClasses``, which is a mapping from HTML elements to CSS
classes. As you saw above, by default there are no CSS classes registered for
the formatter. Let's now register one for the "table" element:
>>> formatter.cssClasses['table'] = 'list'
>>> print formatter()
...
This can be done for every element used in the table. Of course, you can also
unregister the class again:
>>> del formatter.cssClasses['table']
>>> print formatter()
...
If you are going to be doing a lot of this sort of thing (or if this approach
is more your style), a subclass of Formatter might be in order--but that
is jumping the gun a bit. See the section about subclasses below.
Columns are typically defined for a class and reused across requests.
Therefore, they have the request that columns need. They also have an
`annotations` attribute that allows columns to stash away information that
they need across method calls--for instance, an adapter that every single
cell in a column--and maybe even across multiple columns--will need.
>>> formatter.annotations
{}
Batching
========
As discussed above, ``Formatter`` instances can also batch. In order to
batch, `items` must minimally be iterable and ideally support a slice syntax.
batch_size and batch_start, introduced above, are the formatter values to use.
Typically these are passed in on instantiation, but we'll change the attributes
on the existing formatter.
>>> formatter.batch_size = 1
>>> print formatter()
First
Third
a0
c0
>>> formatter.batch_start=1
>>> print formatter()
First
Third
a2
c2
Fancy Columns
=============
It is easy to make columns be more sophisticated. For example, if we wanted
a column that held content that was especially wide, we could do this::
>>> class WideColumn(GetterColumn):
... def renderHeader(self, formatter):
... return '
This level of control over the way columns are rendered allows for creating
advanced column types.
Formatter Subclasses
====================
The Formatter is useful, but lacks some features you may need. The
factoring is such that, typically, overriding just a few methods can easily
provide what you need. The table module provides a few examples of these
subclasses. While the names are sometimes a bit unwieldy, the functionality is
useful.
AlternatingRowFormatter
-----------------------
The AlternatingRowFormatter is the simplest subclass, offering an
odd-even row formatter that's very easy to use::
>>> formatter = table.AlternatingRowFormatter(
... context, request, items, ('First', 'Third'), columns=columns)
>>> print formatter()
First
Third
a0
c0
a2
c2
a1
c1
If you want different classes other than "even" and "odd" then simply
define `row_classes` on your instance: the default is a tuple of "even" and
"odd", but "green" and "red" will work as well:
>>> formatter.row_classes = ("red", "green")
>>> print formatter()
First
Third
a0
c0
a2
c2
a1
c1
Note that this formatter also plays nicely with the other CSS classes defined
by the formatter:
>>> formatter.cssClasses['tr'] = 'list'
>>> print formatter()
First
Third
a0
c0
a2
c2
a1
c1
SortingFormatter
----------------
``SortingFormatter`` supports ``ISortableColumn`` instances by asking them to
sort using the ``ISortableColumn`` interface described above. Instantiating
one takes a new final optional argument, ``sort_on``, which is a sequence of
tuple pairs of (column name string, reverse sort boolean) in which the first
pair is the primary sort. Here's an example. Notice that we are sorting on
the hidden column--this is acceptable, and not even all that unlikely to
encounter.
>>> formatter = table.SortingFormatter(
... context, request, items, ('First', 'Third'), columns=columns,
... sort_on=(('Second', True),))
>>> print formatter()
First
Third
a2
c2
a1
c1
a0
c0
Sorting can also be done on multiple columns. This has the effect of
subsorting. It is up to a column to support the subsorting: it is not a
required behavior. The default GetterColumns we have been using it support it
at the expense of possibly doing a lot of wasted work; the behavior will come
in handy for some examples, though.
First, we'll add some data items that have the same value in the "First"
column. Then we'll configure the sort to sort with "First" being the primary
key and "Third" being the secondary key (you can provide more than two if you
wish). Note that, unlike some of the values examined up to this point, the
sort columns will only be honored when passed to the class on instanciation.
>>> big_items = items[:]
>>> big_items.append(DataItem('a1', 'b1', 'c9'))
>>> big_items.append(DataItem('a1', 'b1', 'c7'))
>>> big_items.append(DataItem('a1', 'b1', 'c8'))
>>> formatter = table.SortingFormatter(
... context, request, big_items, ('First', 'Third'), columns=columns,
... sort_on=(('First', True), ('Third', False)))
>>> print formatter()
First
Third
a2
c2
a1
c1
a1
c7
a1
c8
a1
c9
a0
c0
If the direction of the primary sort is changed, it doesn't effect the sub
sort::
>>> formatter = table.SortingFormatter(
... context, request, big_items, ('First', 'Third'), columns=columns,
... sort_on=(('First', False), ('Third', False)))
>>> print formatter()
First
Third
a0
c0
a1
c1
a1
c7
a1
c8
a1
c9
a2
c2
When batching sorted tables, the sorting is applied first, then the batching::
>>> formatter = table.SortingFormatter(
... context, request, items, ('First', 'Third'), columns=columns,
... batch_start=1, sort_on=(('Second', True),))
>>> print formatter()
First
Third
a1
c1
a0
c0
StandaloneSortFormatter and FormSortFormatter
---------------------------------------------
The sorting table formatter takes care of the sorting back end, but it's
convenient to encapsulate a bit of the front end logic as well, to provide
columns with clickable headers for sorting and so on without having to write
the code every time you need the behavior. Two subclasses of
SortingFormatter provide this capability. The
StandaloneSortFormatter is useful for tables that are not parts of a
form, while the FormSortFormatter is designed to fit within a form.
Both versions look at the request to examine what the user has requested be
sorted, and draw UI on the sortable column headers to enable sorting. The
standalone version uses javascript to put the information in the url, and
the form version puts the information in a hidden field.
Let's take a look at the output of one of these formatters. First there will
be no sorting information.
>>> request = zope.publisher.browser.TestRequest()
>>> formatter = table.FormSortFormatter(
... context, request, items, ('First', 'Third'), columns=columns)
>>> print formatter()
First...
Third...
a0
c0
a2
c2
a1
c1
...
Setting a prefix also affects the value used to store the sorting information.
>>> formatter = table.FormSortFormatter(
... context, request, items, ('First', 'Third'),
... prefix='slot.first', columns=columns)
>>> sort_on_name = table.getSortOnName(formatter.prefix)
>>> print formatter()
First...
Third...
a0
c0
a2
c2
a1
c1
...
Now we'll add information in the request about the sort, and use a prefix.
The value given in the request indicates that the form should be sorted by
the second column in reverse order.
>>> request.form[sort_on_name] = ['Second', 'Second']
>>> formatter = table.FormSortFormatter(
... context, request, items, ('First', 'Third'),
... prefix='slot.first', columns=columns)
>>> print formatter()
First...
Third...
a2
c2
a1
c1
a0
c0
...
Note that sort_on value explicitly passed to a FormSortFormatter is only an
initial value: if the request contains sort information, then the sort_on
value is ignored. This is correct behavior because the initial sort_on value
is recorded in the form, and does not need to be repeated.
For instance, if we re-use the big_items collection from above and pass a
sort_on but modify the request to effectively get a sort_on of
(('First', True), ('Third', False)), then the code will look something like
this--notice that we draw arrows indicating the direction of the primary
search.
>>> request = zope.publisher.browser.TestRequest()
>>> request.form[sort_on_name] = ['Third', 'First', 'First'] # LIFO
>>> formatter = table.FormSortFormatter(
... context, request, big_items, ('First', 'Third'), columns=columns,
... prefix='slot.first', sort_on=(('Second', False), ('Third', True)))
>>> interfaces.IColumnSortedItems.providedBy(formatter.items)
True
>>> zope.interface.verify.verifyObject(interfaces.IColumnSortedItems,
... formatter.items)
True
>>> formatter.items.sort_on
[['First', True], ['Third', False]]
>>> print formatter()
First...
Third...
a2
c2
a1
c1
a1
c7
a1
c8
a1
c9
a0
c0
...
The standalone non-form version uses almost all the same code but doesn't
draw the hidden field and calls a different JavaScript function (which puts the
sorting information in the query string rather than in a form field). Here's a
quick copy of the example above, modified to use the standalone version.
Because of the way the query string is used, more than two instances of a
column name may appear in the form field, so this is emulated in the example.
Because the standalone version doesn't have a form to record the initial
sort_on values, they are honored even if sort_on values exist in the request.
This is in direct contrast to the form-based formatter discussed immediately
above.
>>> request = zope.publisher.browser.TestRequest()
>>> request.form[sort_on_name] = [
... 'Third', 'First', 'Second', 'Third', 'Second', 'Third', 'First']
... # == First True, Third False, Second True
>>> formatter = table.StandaloneSortFormatter(
... context, request, big_items, ('First', 'Third'), columns=columns,
... prefix='slot.first', sort_on=(('Second', False), ('Third', True)))
>>> formatter.items.sort_on
[['First', True], ['Third', False], ['Second', False]]
>>> print formatter()
First
Third...
a2
c2
a1
c1
a1
c7
a1
c8
a1
c9
a0
c0
The sorting code is to be able to accept iterators as items, and only iterate
through them as much as necessary to accomplish the tasks. This needs to
support multiple simultaneous iterations. Another goal is to use the slice
syntax to let sort implementations be guided as to where precise sorting is
needed, in case n-best or other approaches can be used.
There is some trickiness about this in the implementation, and this part of
the document tries to explore some of the edge cases that have proved
problematic in the field.
In particular, we should examine using an iterator in sorted and unsorted
configurations within a sorting table formatter, with batching.
Unsorted:
>>> formatter = table.SortingFormatter(
... context, request, iter(items), ('First', 'Third'),
... columns=columns, batch_size=2)
>>> formatter.items[0] is not None # artifically provoke error :-(
True
>>> print formatter()
zc.table-0.9.0/src/zc/table/fieldcolumn.py 0000664 0001771 0002004 00000015675 12053233200 021521 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2006 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.
#
##############################################################################
import re
from xml.sax.saxutils import quoteattr
from zope import component
import zope.schema.interfaces
import zope.formlib.interfaces
import zope.formlib.form
from zope.formlib.interfaces import IInputWidget, IDisplayWidget
from zope.formlib.interfaces import WidgetInputError, WidgetsError
from zc.table import column
isSafe = re.compile(r'[\w +/]*$').match
def toSafe(string):
# We don't want to use base64 unless we have to,
# because it makes testing and reading html more difficult. We make this
# safe because all base64 strings will have a trailing '=', and our
# `isSafe` regex does not allow '=' at all. The only downside to the
# approach is that a 'safe' string generated by the `toSafe` base64 code
# will not pass the `isSafe` test, so the function is not idempotent. The
# simpler version (without the `isSafe` check) was not idempotent either,
# so no great loss.
if not isSafe(string):
string = ''.join(string.encode('base64').split())
return string
class BaseColumn(column.Column):
###### subclass helper API (not expected to be overridden) ######
def getPrefix(self, item, formatter):
prefix = self.getId(item, formatter)
if formatter.prefix:
prefix = '%s.%s' % (formatter.prefix, prefix)
return prefix
@property
def key(self):
return '%s.%s.%s' % (
self.__class__.__module__,
self.__class__.__name__,
self.name)
def setAnnotation(self, name, value, formatter):
formatter.annotations[self.key + name] = value
def getAnnotation(self, name, formatter, default=None):
return formatter.annotations.get(self.key + name, default)
###### subclass customization API ######
def getId(self, item, formatter):
return toSafe(str(item))
class FieldColumn(BaseColumn):
"""Column that supports field/widget update
"""
__slots__ = ('title', 'name', 'field') # to emphasize that this should not
# have thread-local attributes such as request
def __init__(self, field, title=None, name=''):
if zope.schema.interfaces.IField.providedBy(field):
field = zope.formlib.form.FormField(field)
else:
assert zope.formlib.interfaces.IFormField.providedBy(field)
self.field = field
if title is None:
title = self.field.field.title
if not name and self.field.__name__:
name = self.field.__name__
super(FieldColumn, self).__init__(title, name)
###### subclass helper API (not expected to be overridden) ######
def getInputWidget(self, item, formatter):
form_field = self.field
field = form_field.field
request = formatter.request
prefix = self.getPrefix(item, formatter)
context = self.getFieldContext(item, formatter)
if context is not None:
field = form_field.field.bind(context)
if form_field.custom_widget is None:
if field.readonly or form_field.for_display:
iface = IDisplayWidget
else:
iface = IInputWidget
widget = component.getMultiAdapter((field, request), iface)
else:
widget = form_field.custom_widget(field, request)
if form_field.prefix: # this should not be necessary AFAICT
prefix = '%s.%s' % (prefix, form_field.prefix)
widget.setPrefix(prefix)
return widget
def getRenderWidget(self, item, formatter, ignore_request=False):
widget = self.getInputWidget(item, formatter)
if (ignore_request or
IDisplayWidget.providedBy(widget) or
not widget.hasInput()):
widget.setRenderedValue(self.get(item, formatter))
return widget
###### subclass customization API ######
def get(self, item, formatter):
return self.field.field.get(item)
def set(self, item, value, formatter):
self.field.field.set(item, value)
def getFieldContext(self, item, formatter):
return None
###### main API: input, update, and custom renderCell ######
def input(self, items, formatter):
data = {}
errors = []
for item in items:
widget = self.getInputWidget(item, formatter)
if widget.hasInput():
try:
data[self.getId(item, formatter)] = widget.getInputValue()
except WidgetInputError, v:
errors.append(v)
if errors:
raise WidgetsError(errors)
return data
def update(self, items, data, formatter):
changed = False
for item in items:
id = self.getId(item, formatter)
v = data.get(id, self)
if v is not self and self.get(item, formatter) != v:
self.set(item, v, formatter)
changed = True
if changed:
self.setAnnotation('changed', changed, formatter)
return changed
def renderCell(self, item, formatter):
ignore_request = self.getAnnotation('changed', formatter)
return self.getRenderWidget(
item, formatter, ignore_request)()
class SubmitColumn(BaseColumn):
###### subclass helper API (not expected to be overridden) ######
def getIdentifier(self, item, formatter):
return '%s.%s' % (self.getPrefix(item, formatter), self.name)
def renderWidget(self, item, formatter, **kwargs):
res = ['%s=%s' % (k, quoteattr(v)) for k, v in kwargs.items()]
lbl = self.getLabel(item, formatter)
res[0:0] = [
'input',
'type="submit"',
'name=%s' % quoteattr(self.getIdentifier(item, formatter)),
'value=%s' % quoteattr(lbl)]
return '<%s />' % (' '.join(res))
###### customization API (expected to be overridden) ######
def getLabel(self, item, formatter):
return super(SubmitColumn, self).renderHeader(formatter) # title
###### basic API ######
def input(self, items, formatter):
for item in items:
if self.getIdentifier(item, formatter) in formatter.request.form:
return item
def update(self, items, item, formatter):
raise NotImplementedError
def renderCell(self, item, formatter):
return self.renderWidget(item, formatter)
def renderHeader(self, formatter):
return ''
zc.table-0.9.0/src/zc/table/column.py 0000664 0001771 0002004 00000026100 12053233200 020476 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""Useful predefined columns
$Id: column.py 4318 2005-12-06 03:41:37Z gary $
"""
import warnings
from xml.sax.saxutils import quoteattr
from zope import interface, component, schema, i18n
from zope.formlib.interfaces import IInputWidget
from zope.formlib.interfaces import WidgetInputError, WidgetsError
from zc.table import interfaces
class Column(object):
interface.implements(interfaces.IColumn)
title = None
name = None
def __init__(self, title=None, name=None):
if title is not None:
self.title = title
self.name = name or title
def renderHeader(self, formatter):
return i18n.translate(
self.title, context=formatter.request, default=self.title)
def renderCell(self, item, formatter):
raise NotImplementedError('Subclasses must provide their '
'own renderCell method.')
class SortingColumn(Column):
interface.implements(interfaces.ISortableColumn)
# sort and reversesort are part of ISortableColumn, not IColumn, but are
# put here to provide a reasonable default implementation.
def __init__(self, title=None, name=None, subsort=False):
self.subsort = subsort
super(SortingColumn, self).__init__(title, name)
def _sort(self, items, formatter, start, stop, sorters, multiplier):
if self.subsort and sorters:
items = sorters[0](items, formatter, start, stop, sorters[1:])
else:
items = list(items) # don't mutate original
getSortKey = self.getSortKey
items.sort(
cmp=lambda a, b: multiplier*cmp(a, b),
key=lambda item: getSortKey(item, formatter))
return items
def sort(self, items, formatter, start, stop, sorters):
return self._sort(items, formatter, start, stop, sorters, 1)
def reversesort(self, items, formatter, start, stop, sorters):
return self._sort(items, formatter, start, stop, sorters, -1)
# this is a convenience to override if you just want to keep the basic
# implementation but change the comparison values.
def getSortKey(self, item, formatter):
raise NotImplementedError
class GetterColumn(SortingColumn):
"""Column for simple use cases.
title - the title of the column
getter - a callable that is passed the item and the table formatter;
returns the value used in the cell
cell_formatter - a callable that is passed the result of getter, the
item, and the table formatter; returns the formatted HTML
"""
interface.implementsOnly(interfaces.IColumn)
def __init__(self, title=None, getter=None, cell_formatter=None,
name=None, subsort=False):
if getter is not None:
self.getter = getter
if cell_formatter is not None:
self.cell_formatter = cell_formatter
super(GetterColumn, self).__init__(title, name, subsort=subsort)
def getter(self, item, formatter):
return item
def cell_formatter(self, value, item, formatter):
return unicode(value).replace('&', '&') \
.replace('<', '<') \
.replace('>', '>')
def renderCell(self, item, formatter):
value = self.getter(item, formatter)
return self.cell_formatter(value, item, formatter)
# this is a convenience to override if you just want to keep the basic
# implementation but change the comparison values.
def getSortKey(self, item, formatter):
return self.getter(item, formatter)
class MailtoColumn(GetterColumn):
def renderCell(self, item, formatter):
email = super(MailtoColumn, self).renderCell(item, formatter)
return '%s' % (email, email)
class FieldEditColumn(Column):
"""Columns that supports field/widget update
Note that fields are only bound if bind == True.
"""
def __init__(self, title=None, prefix=None, field=None,
idgetter=None, getter=None, setter=None, name='', bind=False,
widget_class=None, widget_extra=None):
super(FieldEditColumn, self).__init__(title, name)
assert prefix is not None # this is required
assert field is not None # this is required
assert idgetter is not None # this is required
self.prefix = prefix
self.field = field
self.idgetter = idgetter
if getter is None:
getter = field.get
self.get = getter
if setter is None:
setter = field.set
self.set = setter
self.bind = bind
self.widget_class = widget_class
self.widget_extra = widget_extra
def makeId(self, item):
return ''.join(self.idgetter(item).encode('base64').split())
def input(self, items, request):
if not hasattr(request, 'form'):
warnings.warn(
'input should be called with a request, not a formatter',
DeprecationWarning, 2)
request = request.request
data = {}
errors = []
bind = self.bind
if not bind:
widget = component.getMultiAdapter(
(self.field, request), IInputWidget)
for item in items:
if bind:
widget = component.getMultiAdapter(
(self.field.bind(item), request), IInputWidget)
id = self.makeId(item)
# this is wrong: should use formatter prefix. column should not
# have a prefix. This requires a rewrite; this entire class
# will be deprecated.
widget.setPrefix(self.prefix + '.' + id)
if widget.hasInput():
try:
data[id] = widget.getInputValue()
except WidgetInputError, v:
errors.append(v)
if errors:
raise WidgetsError(errors)
return data
def update(self, items, data):
changed = False
for item in items:
id = self.makeId(item)
v = data.get(id, self)
if v is self:
continue
if self.get(item) != v:
self.set(item, v)
changed = True
return changed
def renderCell(self, item, formatter):
id = self.makeId(item)
request = formatter.request
field = self.field
if self.bind:
field = field.bind(item)
widget = component.getMultiAdapter((field, request), IInputWidget)
widget.setPrefix(self.prefix + '.' + id)
if self.widget_extra is not None:
widget.extra = self.widget_extra
if self.widget_class is not None:
widget.cssClass = self.widget_class
ignoreStickyValues = getattr(formatter, 'ignoreStickyValues', False)
if ignoreStickyValues or not widget.hasInput():
widget.setRenderedValue(self.get(item))
return widget()
class SelectionColumn(FieldEditColumn):
title = ''
def __init__(self, idgetter, field=None, prefix=None, getter=None,
setter=None, title=None, name='', hide_header=False):
if field is None:
field = schema.Bool()
if not prefix:
if field.__name__:
prefix = field.__name__ + '_selection_column'
else:
prefix = 'selection_column'
if getter is None:
getter = lambda item: False
if setter is None:
setter = lambda item, value: None
if title is None:
title = field.title or ""
self.hide_header = hide_header
super(SelectionColumn, self).__init__(field=field, prefix=prefix,
getter=getter, setter=setter,
idgetter=idgetter, title=title,
name=name)
def renderHeader(self, formatter):
if self.hide_header:
return ''
return super(SelectionColumn, self).renderHeader(formatter)
def getSelected(self, items, request):
"""Return the items which were selected."""
data = self.input(items, request)
return [item for item in items if data.get(self.makeId(item))]
class SubmitColumn(Column):
def __init__(self, title=None, prefix=None, idgetter=None, action=None,
labelgetter=None, condition=None,
extra=None, cssClass=None, renderer=None, name=''):
super(SubmitColumn, self).__init__(title, name)
# hacked together. :-/
assert prefix is not None # this is required
assert idgetter is not None # this is required
assert labelgetter is not None # this is required
assert action is not None # this is required
self.prefix = prefix
self.idgetter = idgetter
self.action = action
self.renderer=renderer
self.condition = condition
self.extra = extra
self.cssClass = cssClass
self.labelgetter = labelgetter
def makeId(self, item):
return ''.join(self.idgetter(item).encode('base64').split())
def input(self, items, request):
for item in items:
id = self.makeId(item)
identifier = '%s.%s' % (self.prefix, id)
if identifier in request.form:
if self.condition is None or self.condition(item):
return id
break
def update(self, items, data):
if data:
for item in items:
id = self.makeId(item)
if id == data:
self.action(item)
return True
return False
def renderCell(self, item, formatter):
if self.condition is None or self.condition(item):
id = self.makeId(item)
identifier = '%s.%s' % (self.prefix, id)
if self.renderer is not None:
return self.renderer(
item, identifier, formatter, self.extra, self.cssClass)
label = self.labelgetter(item, formatter)
label = i18n.translate(
label, context=formatter.request, default=label)
val = ""
return val
return ''
zc.table-0.9.0/src/zc/table/tests.py 0000664 0001771 0002004 00000005770 12053233200 020355 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""
$Id: tests.py 4428 2005-12-13 23:35:48Z gary $
"""
import unittest
from zope import component
from zope.component.testing import setUp, tearDown
import zope.publisher.interfaces.browser
import zope.schema.interfaces
import zope.formlib.widgets
def columnSetUp(test):
setUp(test)
component.provideAdapter(
zope.formlib.widgets.TextWidget,
(zope.schema.interfaces.ITextLine,
zope.publisher.interfaces.browser.IBrowserRequest,
),
zope.formlib.interfaces.IInputWidget)
component.provideAdapter(
zope.formlib.widgets.CheckBoxWidget,
(zope.schema.interfaces.IBool,
zope.publisher.interfaces.browser.IBrowserRequest,
),
zope.formlib.interfaces.IInputWidget)
def fieldColumnSetUp(test):
columnSetUp(test)
component.provideAdapter(
zope.formlib.widgets.ChoiceDisplayWidget,
(zope.schema.interfaces.IChoice,
zope.publisher.interfaces.browser.IBrowserRequest),
zope.formlib.interfaces.IDisplayWidget)
component.provideAdapter(
zope.formlib.widgets.ChoiceInputWidget,
(zope.schema.interfaces.IChoice,
zope.publisher.interfaces.browser.IBrowserRequest),
zope.formlib.interfaces.IInputWidget)
component.provideAdapter(
zope.formlib.widgets.DropdownWidget,
(zope.schema.interfaces.IChoice,
zope.schema.interfaces.IVocabularyTokenized,
zope.publisher.interfaces.browser.IBrowserRequest),
zope.formlib.interfaces.IInputWidget)
component.provideAdapter(
zope.formlib.widgets.ChoiceDisplayWidget,
(zope.schema.interfaces.IChoice,
zope.schema.interfaces.IVocabularyTokenized,
zope.publisher.interfaces.browser.IBrowserRequest),
zope.formlib.interfaces.IDisplayWidget)
def test_suite():
import doctest
return unittest.TestSuite((
doctest.DocFileSuite('README.txt',
optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS,
),
doctest.DocFileSuite(
'column.txt',
setUp=columnSetUp, tearDown=tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS,
),
doctest.DocFileSuite(
'fieldcolumn.txt',
setUp=fieldColumnSetUp, tearDown=tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS,
),
))
zc.table-0.9.0/src/zc/table/table.py 0000664 0001771 0002004 00000040611 12053233200 020273 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""Table formatting and configuration
$Id: table.py 4428 2005-12-13 23:35:48Z gary $
"""
from xml.sax.saxutils import quoteattr
from zope import interface, component
import zope.cachedescriptors.property
from zc.table import interfaces
import zc.resourcelibrary
class Formatter(object):
interface.implements(interfaces.IFormatter)
items = None
def __init__(self, context, request, items, visible_column_names=None,
batch_start=None, batch_size=None, prefix=None, columns=None):
self.context = context
self.request = request
self.annotations = {}
self.setItems(items)
if columns is None:
assert self.columns is not None
else:
self.columns = columns
if not visible_column_names:
self.visible_columns = self.columns
else:
self.visible_columns = [
self.columns_by_name[nm] for nm in visible_column_names]
self.batch_start = batch_start
self.batch_size = batch_size
self.prefix = prefix
self.cssClasses = {}
def setItems(self, items):
self.items = items
@zope.cachedescriptors.property.Lazy
def columns_by_name(self):
res = {}
for col in self.columns:
assert col.name not in res
res[col.name] = col
return res
def _getCSSClass(self, element):
klass = self.cssClasses.get(element)
return klass and ' class=%s' % quoteattr(klass) or ''
def __call__(self):
return '\n
\n' %(
self._getCSSClass('tr'), self.renderHeaders())
def renderHeaders(self):
return ''.join(
[self.renderHeader(col) for col in self.visible_columns])
def renderHeader(self, column):
return '
\n %s\n
\n' % (
self._getCSSClass('th'), self.getHeader(column))
def getHeaders(self):
return [self.getHeader(column) for column in self.visible_columns]
def getHeader(self, column):
return column.renderHeader(self)
def renderRows(self):
return ''.join([self.renderRow(item) for item in self.getItems()])
def getRows(self):
for item in self.getItems():
yield [column.renderCell(item, self)
for column in self.visible_columns]
def renderRow(self, item):
return '
\n%s
\n' % (
self._getCSSClass('tr'), self.renderCells(item))
def renderCells(self, item):
return ''.join(
[self.renderCell(item, col) for col in self.visible_columns])
def renderCell(self, item, column):
return '
\n %s\n
\n' % (
self._getCSSClass('td'), self.getCell(item, column),)
def getCells(self, item):
return [self.getCell(item, column) for column in self.visible_columns]
def getCell(self, item, column):
return column.renderCell(item, self)
def getItems(self):
batch_start = self.batch_start or 0
batch_size = self.batch_size or 0
if not self.batch_size:
if not batch_start: # ok, no work to be done.
for i in self.items:
yield i
raise StopIteration
batch_end = None
else:
batch_end = batch_start + batch_size
try:
for i in self.items[batch_start:batch_end]:
yield i
except (AttributeError, TypeError):
for i, item in enumerate(self.items):
if batch_end is not None and i >= batch_end:
return
if i >= batch_start:
yield item
# sorting helpers
class ColumnSortedItems(object):
# not intended to be persistent!
"""a wrapper for items that sorts lazily based on ISortableColumns.
Given items, a list of (column name, reversed boolean) pairs beginning
with the primary sort column, and the formatter, supports iteration, len,
and __getitem__ access including slices.
"""
interface.implements(interfaces.IColumnSortedItems)
formatter = None
def __init__(self, items, sort_on):
self._items = items
self.sort_on = sort_on # tuple of (column name, reversed) pairs
self._cache = []
self._iterable = None
@property
def items(self):
if getattr(self._items, '__getitem__', None) is not None:
return self._items
else:
return self._iter()
def _iter(self):
# this design is intended to handle multiple simultaneous iterations
ix = 0
cache = self._cache
iterable = self._iterable
if iterable is None:
iterable = self._iterable = iter(self._items)
while True:
try:
yield cache[ix]
except IndexError:
next = iterable.next() # let StopIteration fall through
cache.append(next)
yield next
ix += 1
def setFormatter(self, formatter):
self.formatter = formatter
@property
def sorters(self):
res = []
for nm, reversed in self.sort_on:
column = self.formatter.columns_by_name[nm]
if reversed:
res.append(column.reversesort)
else:
res.append(column.sort)
return res
def __getitem__(self, key):
if isinstance(key, slice):
start = slice.start
stop = slice.stop
stride = slice.step
else:
start = stop = key
stride = 1
items = self.items
if not self.sort_on:
try:
return items.__getitem__(key)
except (AttributeError, TypeError):
if stride != 1:
raise NotImplemented
res = []
for ix, val in enumerate(items):
if ix >= start:
res.append(val)
if ix >= stop:
break
if isinstance(key, slice):
return res
elif res:
return res[0]
else:
raise IndexError, 'list index out of range'
items = self.sorters[0](
items, self.formatter, start, stop, self.sorters[1:])
if isinstance(key, slice):
return items[start:stop:stride]
else:
return items[key]
def __nonzero__(self):
try:
iter(self.items).next()
except StopIteration:
return False
return True
def __iter__(self):
if not self.sort_on:
return iter(self.items)
else:
sorters = self.sorters
return iter(sorters[0](
self.items, self.formatter, 0, None, sorters[1:]))
def __len__(self):
return len(self.items)
def getRequestSortOn(request, sort_on_name):
"""get the sorting values from the request.
Returns a list of (name, reversed) pairs.
"""
# useful for code that wants to get the sort on values themselves
sort_on = None
sorting = request.form.get(sort_on_name)
if sorting:
offset = 0
res = {}
for ix, name in enumerate(sorting):
val = res.get(name)
if val is None:
res[name] = [ix + offset, name, False]
else:
val[0] = ix + offset
val[2] = not val[2]
if res:
res = res.values()
res.sort()
res.reverse()
sort_on = [[nm, reverse] for ix, nm, reverse in res]
return sort_on
def getMungedSortOn(request, sort_on_name, sort_on):
"""get the sorting values from the request.
optionally begins with sort_on values. Returns a list of (name, reversed)
pairs.
"""
res = getRequestSortOn(request, sort_on_name)
if res is None:
res = sort_on
elif sort_on:
for nm, reverse in sort_on:
for ix, (res_nm, res_reverse) in enumerate(res):
if nm == res_nm:
res[ix][1] = not (res_reverse ^ reverse)
break
else:
res.append([nm, reverse])
return res
def getSortOnName(prefix=None):
"""convert the table prefix to the 'sort on' name used in forms"""
# useful for code that wants to get the sort on values themselves
sort_on_name = 'sort_on'
if prefix is not None:
if not prefix.endswith('.'):
prefix += '.'
sort_on_name = prefix + sort_on_name
return sort_on_name
class SortingFormatterMixin(object):
"""automatically munges sort_on values with sort settings in the request.
"""
def __init__(self, context, request, items, visible_column_names=None,
batch_start=None, batch_size=None, prefix=None, columns=None,
sort_on=None, ignore_request=False):
if not ignore_request:
sort_on = getMungedSortOn(request, getSortOnName(prefix), sort_on)
else:
sort_on = sort_on
if sort_on or getattr(items, '__getitem__', None) is None:
items = ColumnSortedItems(items, sort_on)
super(SortingFormatterMixin, self).__init__(
context, request, items, visible_column_names,
batch_start, batch_size, prefix, columns)
if sort_on:
items.setFormatter(self)
def setItems(self, items):
if (interfaces.IColumnSortedItems.providedBy(self.items) and
not interfaces.IColumnSortedItems.providedBy(items)):
items = ColumnSortedItems(items, self.items.sort_on)
if interfaces.IColumnSortedItems.providedBy(items):
items.setFormatter(self)
self.items = items
class AbstractSortFormatterMixin(object):
"""provides sorting UI: concrete classes must declare script_name."""
script_name = None # Must be defined in subclass
def getHeader(self, column):
contents = column.renderHeader(self)
if (interfaces.ISortableColumn.providedBy(column)):
contents = self._addSortUi(contents, column)
return contents
def _addSortUi(self, header, column):
columnName = column.name
resource_path = component.getAdapter(self.request, name='zc.table')()
if (interfaces.IColumnSortedItems.providedBy(self.items) and
self.items.sort_on):
sortColumnName, sortReversed = self.items.sort_on[0]
else:
sortColumnName = sortReversed = None
if columnName == sortColumnName:
if sortReversed:
dirIndicator = ('' % resource_path)
else:
dirIndicator = ('' % resource_path)
else:
dirIndicator = ('' % resource_path)
sort_on_name = getSortOnName(self.prefix)
script_name = self.script_name
return self._header_template(locals())
def _header_template(self, options):
# The below is intentionally not in the because IE
# doesn't underline it correctly when the CSS class is changed.
# XXX can we avoid changing the className and get a similar effect?
template = """
%(header)s %(dirIndicator)s
"""
return template % options
class StandaloneSortFormatterMixin(AbstractSortFormatterMixin):
"A version of the sort formatter mixin for standalone tables, not forms"
script_name = 'onSortClickStandalone'
class FormSortFormatterMixin(AbstractSortFormatterMixin):
"""A version of the sort formatter mixin that plays well within forms.
Does *not* draw a form tag itself, and requires something else to do so.
"""
def __init__(self, context, request, items, visible_column_names=None,
batch_start=None, batch_size=None, prefix=None, columns=None,
sort_on=None, ignore_request=False):
if not ignore_request:
sort_on = (
getRequestSortOn(request, getSortOnName(prefix)) or sort_on)
else:
sort_on = sort_on
if sort_on or getattr(items, '__getitem__', None) is None:
items = ColumnSortedItems(items, sort_on)
super(FormSortFormatterMixin, self).__init__(
context, request, items, visible_column_names,
batch_start, batch_size, prefix, columns)
if sort_on:
items.setFormatter(self)
script_name = 'onSortClickForm'
def renderExtra(self):
"""Render the hidden input field used to keep up with sorting"""
if (interfaces.IColumnSortedItems.providedBy(self.items) and
self.items.sort_on):
value = []
for name, reverse in reversed(self.items.sort_on):
value.append(name)
if reverse:
value.append(name)
value = ' '.join(value)
else:
value = ''
sort_on_name = getSortOnName(self.prefix)
return '\n' % (
quoteattr(sort_on_name+":tokens"),
quoteattr(sort_on_name),
quoteattr(value)
) + super(FormSortFormatterMixin, self).renderExtra()
def setItems(self, items):
if (interfaces.IColumnSortedItems.providedBy(self.items) and
not interfaces.IColumnSortedItems.providedBy(items)):
items = ColumnSortedItems(items, self.items.sort_on)
if interfaces.IColumnSortedItems.providedBy(items):
items.setFormatter(self)
self.items = items
class AlternatingRowFormatterMixin(object):
row_classes = ('even', 'odd')
def renderRows(self):
self.row = 0
return super(AlternatingRowFormatterMixin, self).renderRows()
def renderRow(self, item):
self.row += 1
klass = self.cssClasses.get('tr', '')
if klass:
klass += ' '
return '
\n%s
\n' % (
quoteattr(klass + self.row_classes[self.row % 2]),
self.renderCells(item))
# TODO Remove all these concrete classes
class SortingFormatter(SortingFormatterMixin, Formatter):
pass
class AlternatingRowFormatter(AlternatingRowFormatterMixin, Formatter):
pass
class StandaloneSortFormatter(
SortingFormatterMixin, StandaloneSortFormatterMixin, Formatter):
pass
class FormSortFormatter(FormSortFormatterMixin, Formatter):
pass
class StandaloneFullFormatter(
SortingFormatterMixin, StandaloneSortFormatterMixin,
AlternatingRowFormatterMixin, Formatter):
pass
class FormFullFormatter(
FormSortFormatterMixin, AlternatingRowFormatterMixin, Formatter):
pass
zc.table-0.9.0/src/zc/table/batching.pt 0000664 0001771 0002004 00000003611 12053233200 020755 0 ustar menesis menesis 0000000 0000000
zc.table-0.9.0/src/zc/table/resources/ 0000775 0001771 0002004 00000000000 12053234232 020650 5 ustar menesis menesis 0000000 0000000 zc.table-0.9.0/src/zc/table/resources/sort_arrows.gif 0000664 0001771 0002004 00000000114 12053233200 023711 0 ustar menesis menesis 0000000 0000000 GIF89a
‘ ÿÿÿÌÌÌ™™™ÿÿÿ!ù ,
œ'p+ÊœƒHJ%²Ö ûl"fMGs-Õ£ÉR ; zc.table-0.9.0/src/zc/table/resources/sort_arrows_down.gif 0000664 0001771 0002004 00000000133 12053233200 024741 0 ustar menesis menesis 0000000 0000000 GIF89a
¢ òe"ÌÌÌÿÿÿ™™™ÿÿÿ !ù ,
HJ#û#¥¤ÌZ7º÷B(ŽÂgr@ªbƒ
`Jû>Kó$ ; zc.table-0.9.0/src/zc/table/resources/sorting.js 0000664 0001771 0002004 00000001014 12053233200 022661 0 ustar menesis menesis 0000000 0000000 function onSortClickStandalone(column_name, sort_on_name) {
window.location = addFieldToUrl(
window.location.href, sort_on_name+':list='+column_name);
}
function addFieldToUrl(url, field) {
if (url.indexOf('?') == -1)
sep = '?';
else
sep = '&';
return url + sep + field;
}
function onSortClickForm(column_name, sort_on_name) {
field = document.getElementById(sort_on_name);
if (field.value) field.value += ' ';
field.value += column_name;
field.form.submit();
}
zc.table-0.9.0/src/zc/table/resources/sort_arrows_up.gif 0000664 0001771 0002004 00000000133 12053233200 024416 0 ustar menesis menesis 0000000 0000000 GIF89a
¢ òe"ÌÌÌÿÿÿ™™™ÿÿÿ !ù ,
HJ#û ¥¤ÌZ7º÷B(ŽÂgrAªbƒ`Jû>Kó$ ; zc.table-0.9.0/src/zc/table/batching.py 0000664 0001771 0002004 00000010574 12053233200 020770 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2005 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.
#
##############################################################################
"""Table formatting and configuration
"""
from zope import interface
from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile
import zc.table.table
import zc.table.interfaces
unspecified = object()
class Formatter(zc.table.table.FormSortFormatterMixin,
zc.table.table.AlternatingRowFormatterMixin,
zc.table.table.Formatter):
interface.classProvides(zc.table.interfaces.IFormatterFactory)
def __init__(self, context, request, items, visible_column_names=None,
batch_start=None, batch_size=unspecified, prefix=None,
columns=None, sort_on=None):
if batch_size is unspecified:
batch_size = 20
if prefix is None:
prefix = 'zc.table'
super(Formatter, self).__init__(
context, request, items, visible_column_names,
batch_start, batch_size, prefix, columns,
sort_on=sort_on,
)
@property
def batch_change_name(self):
return self.prefix + '.batch_change'
@property
def batch_start_name(self):
return self.prefix + '.batch_start'
_batch_start = None
_batch_start_computed = False
def setPrefix(self, prefix):
super(Formatter, self).setPrefix(prefix)
self._batch_start_computed = False
@apply
def batch_start():
def fget(self):
if not self._batch_start_computed:
self.updateBatching()
return self._batch_start
def fset(self, value):
self._batch_start = value
self._batch_start_computed = False
def fdel(self):
self._batch_start = None
return property(fget, fset, fdel)
def updateBatching(self):
request = self.request
if self._batch_start is None:
try:
self.batch_start = int(request.get(self.batch_start_name, '0'))
except ValueError:
self._batch_start = 0
# Handle requests to change batches:
change = request.get(self.batch_change_name)
if change == "next":
self._batch_start += self.batch_size
try:
length = len(self.items)
except TypeError:
for length, ob in enumerate(self.items):
if length > self._batch_start:
break
else:
self._batch_start = length
else:
if self._batch_start > length:
self._batch_start = length
elif change == "back":
self._batch_start -= self.batch_size
if self._batch_start < 0:
self._batch_start = 0
self.next_batch_start = self._batch_start + self.batch_size
try:
self.items[self.next_batch_start]
except IndexError:
self.next_batch_start = None
self.previous_batch_start = self._batch_start - self.batch_size
if self.previous_batch_start < 0:
self.previous_batch_start = None
self._batch_start_computed = True
batching_template = ViewPageTemplateFile('batching.pt')
def renderExtra(self):
if not self._batch_start_computed:
self.updateBatching()
return self.batching_template() + super(Formatter, self).renderExtra()
def __call__(self):
return ('\n'
'
'
'\n'
'
\n' % self.prefix)
+ self.renderContents() +
'
\n'
+ self.renderExtra() +
'
\n'
)
zc.table-0.9.0/src/zc/table/__init__.py 0000664 0001771 0002004 00000001356 12053233200 020746 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""HTML Table support
$Id: __init__.py 1335 2005-04-19 05:21:10Z gary $
"""
from zc.table.table import Formatter
zc.table-0.9.0/src/zc/table/fieldcolumn.txt 0000664 0001771 0002004 00000040606 12053233200 021700 0 ustar menesis menesis 0000000 0000000 FieldColumn
===========
The FieldColumn is intended to be a replacement for the FieldEditColumn. It
has the following goals.
- Support the standard column pattern of instantiating a single module global
column set, and using formatters to store and access information about a
single given rendering. The formatter has annotations, prefix, context, and
request, all of which are desired or even essential for various derived
columns. Generally, the formatter has state that the columns need.
- Support display widgets, in addition to input widgets.
- Support formlib form fields, to support custom widgets and other formlib
field features.
It also has an important design difference. Rather than relying on functions
passed at initialization for column customization, the field column returns to
a standard subclass design. The column expects that any of the following
methods may be overridden:
- getId(item, formatter): given an item whose value is to be displayed in the
column, return a form-friendly string identifier, unique to the item. We
define 'form-friendly' as containing characters that are alphanumeric or a
space, a plus, a slash, or an equals sign (that is, the standard characters
used in MIME-based base64, excluding new lines).
- get(item, formatter): return the current value for the item.
- set(item, value, formatter): set the given value for the item.
- getFieldContext(item, formatter): return a context to which a field should
be bound, or None.
- renderCell(item, formatter): the standard rendering of a cell
- renderHeader(formatter): the standard rendering of a column header.
Without subclassing, the column uses field.get and field.set to get and set
values, returns None for bound context, and just renders the field's widget for
the cell, and the title for the header. Let's look at the default behavior.
We'll use a modified version of the examples given for the FieldEditColumn in
column.txt.
To create a FieldColumn, you must minimally provide a schema field or form
field. If title is not provided, it is the field's title, or empty if the
field has no title. A explicit title of an empty string is acceptable and will
be honored. If name is not provided, it is the field's __name__.
Let's look at a simple example. We have a collection of contacts,
with names and email addresses:
>>> import re
>>> from zope import schema, interface
>>> class IContact(interface.Interface):
... name = schema.TextLine(title=u'Name')
... email = schema.TextLine(
... title=u'Email Address',
... constraint=re.compile('\w+@\w+([.]\w+)+$').match)
... salutation = schema.Choice(
... title=u'Salutation',
... values = ['Mr','Ms'],
... )
>>> class Contact:
... interface.implements(IContact)
... def __init__(self, id, name, email, salutation):
... self.id = id
... self.name = name
... self.email = email
... self.salutation = salutation
>>> contacts = (
... Contact('1', 'Bob Smith', 'bob@zope.com', 'Mr'),
... Contact('2', 'Sally Baker', 'sally@zope.com', 'Ms'),
... Contact('3', 'Jethro Tul', 'jethro@zope.com', 'Mr'),
... Contact('4', 'Joe Walsh', 'joe@zope.com', 'Mr'),
... )
We'll define columns that allow us to display and edit name and
email addresses.
>>> from zc.table import fieldcolumn
>>> class ContactColumn(fieldcolumn.FieldColumn):
... def getId(self, item, formatter):
... return fieldcolumn.toSafe(item.id)
...
>>> class BindingContactColumn(ContactColumn):
... def getFieldContext(self, item, formatter):
... return item
...
>>> columns = (ContactColumn(IContact["name"]),
... ContactColumn(IContact["email"]),
... BindingContactColumn(IContact["salutation"])
... )
Now, with this, we can create a table with input widgets. The columns don't
need a context other than the items themselves, so we ignore that part of the
table formatter instantiation:
>>> from zc import table
>>> import zope.publisher.browser
>>> request = zope.publisher.browser.TestRequest()
>>> context = None
>>> formatter = table.Formatter(
... context, request, contacts, columns=columns, prefix='test')
>>> print formatter()
Name
Email Address
Salutation
Note that the input names do not include base64 encodings of the item ids
because they already match the necessary constraints.
If the request has input for a value, then this will override item data:
>>> request.form["test.4.email"] = u'walsh@zope.com'
>>> print formatter()
Name
Email Address
Salutation
and the contact data is unchanged:
>>> contacts[3].email
'joe@zope.com'
Field edit columns provide methods for getting and validating input
data, and for updating the undelying data:
>>> data = columns[1].input(contacts, formatter)
>>> data
{'4': u'walsh@zope.com'}
The data returned is a mapping from item id to input value. Items
that don't have input are ignored. The data can be used with the
update function to update the underlying data:
>>> columns[1].update(contacts, data, formatter)
True
>>> contacts[3].email
u'walsh@zope.com'
Note that the update function returns a boolean value indicating
whether any changes were made:
>>> columns[1].update(contacts, data, formatter)
False
The input function also validates input. If there are any errors, a
WidgetsError will be raised:
>>> request.form["test.4.email"] = u'walsh'
>>> data = columns[1].input(contacts, formatter)
Traceback (most recent call last):
...
WidgetsError: WidgetInputError:
('email', u'Email Address', ConstraintNotSatisfied(u'walsh'))
Custom getters and setters
--------------------------
Normally, the given fields getter and setter is used, however, custom
getters and setters can be provided. Let's look at an example of
a bit table:
>>> data = [0, 0], [1, 1], [2, 2], [3, 3]
>>> class BitColumn(fieldcolumn.FieldColumn):
... def __init__(self, field, bit, title=None, name=''):
... super(BitColumn, self).__init__(field, title, name)
... self.bit = bit
... def getId(self, item, formatter):
... return str(item[0])
... def get(self, item, formatter):
... return (1 << self.bit)&(item[1])
... def set(self, item, value, formatter):
... value = bool(value) << self.bit
... mask = 1 << self.bit
... item[1] = ((item[1] | mask) ^ mask) | value
>>> columns = (
... BitColumn(schema.Bool(__name__='0', title=u'Bit 0'), 0),
... BitColumn(schema.Bool(__name__='1', title=u'Bit 1'), 1))
>>> context = None # not needed
>>> request = zope.publisher.browser.TestRequest()
>>> formatter = table.Formatter(
... context, request, data, columns=columns, prefix='test')
>>> print formatter()
Bit 0
Bit 1
>>> request.form["test.3.1.used"] = ""
>>> request.form["test.0.1.used"] = ""
>>> request.form["test.0.1"] = "on"
>>> input = columns[1].input(data, formatter)
>>> from pprint import pprint
>>> pprint(input)
{'0': True,
'3': False}
>>> columns[1].update(data, input, formatter)
True
>>> data
([0, 2], [1, 1], [2, 2], [3, 1])
zc.table-0.9.0/src/zc/table/configure.zcml 0000664 0001771 0002004 00000000276 12053233200 021505 0 ustar menesis menesis 0000000 0000000
zc.table-0.9.0/src/zc/table/interfaces.py 0000664 0001771 0002004 00000021001 12053233200 021317 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""table package interfaces
$Id: interfaces.py 4318 2005-12-06 03:41:37Z gary $
"""
import re
from zope import interface, schema
pythonLikeNameConstraint = re.compile('^[a-zA-Z_]\w*$').match
class IColumn(interface.Interface):
name = schema.BytesLine(
title = u'Name',
description = (u'Name used for column in options of a table '
u'configuration. Must be unique within any set of '
u'columns passed to a table formatter.'),
constraint = pythonLikeNameConstraint,
)
title = schema.TextLine(
title = u'Title',
description = u'The title of the column, used in configuration '
u'dialogs.',
)
def renderHeader(formatter):
"""Renders a table header.
'formatter' - The IFormatter that is using the IColumn.
Returns html_fragment, not including any surrounding
tags.
"""
def renderCell(item, formatter):
"""Renders a table cell.
'item' - the object on this row.
'formatter' - The IFormatter that is using the IColumn.
Returns html_fragment, not including any surrounding
tags.
"""
class ISortableColumn(interface.Interface):
def sort(items, formatter, start, stop, sorters):
"""Return a list of items in sorted order.
Formatter is passed to aid calculation of sort parameters. Start and
stop are passed in order to provide a hint as to the range needed, if
the algorithm can optimize. Sorters are a list of zero or more
sub-sort callables with the same signature which may be used if
desired to sub-sort values with equivalent sort values according
to this column.
The original items sequence should not be mutated."""
def reversesort(items, formatter, start, stop, sorters):
"""Return a list of items in reverse sorted order.
Formatter is passed to aid calculation of sort parameters. Start and
stop are passed in order to provide a hint as to the range needed, if
the algorithm can optimize. Sorters are a list of zero or more
sub-sort callables with the same signature as this method, which may
be used if desired to sub-sort values with equivalent sort values
according to this column.
The original items sequence should not be mutated."""
class IColumnSortedItems(interface.Interface):
"""items that support sorting by column. setFormatter must be called
with the formatter to be used before methods work. This is typically done
in a formatter's __init__"""
sort_on = interface.Attribute(
"""list of (colmun name, reversed boolean) beginning with the primary
sort column.""")
def __getitem__(key):
"""given index or slice, returns requested item(s) based on sort order.
"""
def __iter__():
"""iterates over items in sort order"""
def __len__():
"""returns len of sorted items"""
def setFormatter(formatter):
"tell the items about the formatter before using any of the methods"
class IFormatter(interface.Interface):
annotations = schema.Dict(
title=u"Annotations",
description=
u"""Stores arbitrary application data under package-unique keys.
By "package-unique keys", we mean keys that are are unique by
virtue of including the dotted name of a package as a prefix. A
package name is used to limit the authority for picking names for
a package to the people using that package.
For example, when implementing annotations for a zc.foo package, the
key would be (or at least begin with) the following::
"zc.foo"
""")
request = schema.Field(
title = u'Request',
description = u'The request object.',
)
context = schema.Field(
title=u'Context',
description=u'The (Zope ILocation) context for which the table '
u'formatter is rendering')
items = schema.List(
title=u'Items',
description=u'The items that will be rendered by __call__. items '
'preferably support a way to get a slice (__getitem__ or the '
'deprecated getslice) or alternatively may merely be iterable. '
'see getItems.')
columns = schema.Tuple(
title = u'All the columns that make up this table.',
description = u'All columns that may ever be a visible column. A non-'
u'visible column may still have an effect on operations such as '
u'sorting. The names of all columns must be unique within the '
u'sequence.',
unique = True,
)
visible_columns = schema.Tuple(
title = u'The visible columns that make up this table.',
description = u'The columns to display when rendering this table.',
unique = True,
)
batch_size = schema.Int(
title = u'Number of rows per page',
description = u'The number of rows to show at a time. '
u'Set to 0 for no batching.',
default = 20,
min = 0,
)
batch_start = schema.Int(
title = u'Batch Start',
description = u'The starting index for batching.',
default = 0,
)
prefix = schema.BytesLine(
title = u'Prefix',
description = u'The prefix for all form names',
constraint = pythonLikeNameConstraint,
)
columns_by_name = schema.Dict(
title=u'Columns by Name',
description=u'A mapping of column name to column object')
cssClasses = schema.Dict(
title=u'CSS Classes',
description=u'A mapping from an HTML element to a CSS class',
key_type=schema.TextLine(title=u'The HTML element name'),
value_type=schema.TextLine(title=u'The CSS class name'))
def __call__():
"""Render a complete HTML table from self.items."""
def renderHeaderRow():
"""Render an HTML table header row from the column headers.
Uses renderHeaders."""
def renderHeaders():
"""Render the individual HTML headers from the columns.
Uses renderHeader."""
def renderHeader(column):
"""Render a header for the given column.
Uses getHeader."""
def getHeader(column):
"""Render header contents for the given column.
Includes appropriate code for enabling ISortableColumn.
Uses column.renderHeader"""
def getHeaders():
"""Retrieve a sequence of rendered column header contents.
Uses getHeader.
Available for more low-level use of a table; not used by the other
table code."""
def renderRows():
"""Render HTML rows for the self.items.
Uses renderRow and getItems."""
def getRows():
"""Retrieve a sequence of sequences of rendered cell contents.
Uses getCells and getItems.
Available for more low-level use of a table; not used by the other
table code."""
def getCells(item):
"""Retrieve a sequence rendered cell contents for the item.
Uses getCell.
Available for more low-level use of a table; not used by the other
table code."""
def getCell(item, column):
"""Render the cell contents for the item and column."""
def renderRow(item):
"""Render a row for the given item.
Uses renderCells."""
def renderCells(item):
"""Render the cells--the contents of a row--for the given item.
Uses renderCell."""
def renderCell(item, column):
"""Render the cell for the item and column.
Uses getCell."""
def getItems():
"""Returns the items to be rendered from the full set of self.items.
Should be based on batch_start and batch_size, if set.
"""
class IFormatterFactory(interface.Interface):
"""When called returns a table formatter.
Takes the same arguments as zc.table.table.Formatter"""
zc.table-0.9.0/src/zc/table/TODO.txt 0000664 0001771 0002004 00000000330 12053233200 020132 0 ustar menesis menesis 0000000 0000000 add tests for binding
add .getGuts call (with different name)
register utility and change clients to use it
make sorting UI more discoverable
add .renderExtra (poss. with different name) and change clients to use it
zc.table-0.9.0/src/zc/table/column.txt 0000664 0001771 0002004 00000036334 12053233200 020677 0 ustar menesis menesis 0000000 0000000 Useful column types
===================
We provide a number of pre-defined column types to use in table
definitions.
Field Edit Columns
------------------
Field edit columns provide support for tables of input widgets.
To define a field edit column, you need to provide:
- title, the label to be displayed
- a prefix, which is used to distinguish a columns inputs
from those of other columns
- a field that describes the type of data in the column
- an id getter
- an optional data getter, and
- an optional data setter
The id getter has to compute a string that uniquely identified an
item.
Let's look at a simple example. We have a collection of contacts,
with names and email addresses:
>>> import re
>>> from zope import schema, interface
>>> class IContact(interface.Interface):
... name = schema.TextLine()
... email = schema.TextLine(
... constraint=re.compile('\w+@\w+([.]\w+)+$').match)
>>> class Contact:
... interface.implements(IContact)
... def __init__(self, id, name, email):
... self.id = id
... self.name = name
... self.email = email
>>> contacts = (
... Contact('1', 'Bob Smith', 'bob@zope.com'),
... Contact('2', 'Sally Baker', 'sally@zope.com'),
... Contact('3', 'Jethro Tul', 'jethro@zope.com'),
... Contact('4', 'Joe Walsh', 'joe@zope.com'),
... )
We'll define columns that allow us to display and edit name and
email addresses.
>>> from zc.table import column
>>> columns = (
... column.FieldEditColumn(
... "Name", "test", IContact["name"],
... lambda contact: contact.id,
... ),
... column.FieldEditColumn(
... "Email address", "test", IContact["email"],
... lambda contact: contact.id,
... ),
... )
Now, with this, we can create a table with input widgets. The columns don't
need a context other than the items themselves, so we ignore that part of the
table formatter instantiation:
>>> from zc import table
>>> import zope.publisher.browser
>>> request = zope.publisher.browser.TestRequest()
>>> context = None
>>> formatter = table.Formatter(
... context, request, contacts, columns=columns)
>>> print formatter()
Name
Email address
Note that the input names include base64 encodings of the item ids.
If the request has input for a value, then this will override item data:
>>> request.form["test.NA==.email"] = u'walsh@zope.com'
>>> print formatter()
Name
Email address
and the contact data is unchanged:
>>> contacts[3].email
'joe@zope.com'
Field edit columns provide methods for getting and validating input
data, and fpr updating the undelying data:
>>> data = columns[1].input(contacts, request)
>>> data
{'NA==': u'walsh@zope.com'}
The data returned is a mapping from item id to input value. Items
that don't have input are ignored. The data can be used with the
update function to update the underlying data:
>>> columns[1].update(contacts, data)
True
>>> contacts[3].email
u'walsh@zope.com'
Note that the update function returns a boolean value indicating
whether any changes were made:
>>> columns[1].update(contacts, data)
False
The input function also validates input. If there are any errors, a
WidgetsError will be raised:
>>> request.form["test.NA==.email"] = u'walsh'
>>> data = columns[1].input(contacts, request)
Traceback (most recent call last):
...
WidgetsError: WidgetInputError:
('email', u'', ConstraintNotSatisfied(u'walsh'))
Custom getters and setters
--------------------------
Normally, the given fields getter and setter is used, however, custom
getters and setters can be provided. Let's look at an example of
a bit table:
>>> data = [0, 0], [1, 1], [2, 2], [3, 3]
>>> def setbit(data, bit, value):
... value = bool(value) << bit
... mask = 1 << bit
... data[1] = ((data[1] | mask) ^ mask) | value
>>> columns = (
... column.FieldEditColumn(
... "Bit 0", "test", schema.Bool(__name__='0'),
... lambda data: str(data[0]),
... getter = lambda data: 1&(data[1]),
... setter = lambda data, v: setbit(data, 0, v),
... ),
... column.FieldEditColumn(
... "Bit 1", "test", schema.Bool(__name__='1'),
... lambda data: str(data[0]),
... getter = lambda data: 2&(data[1]),
... setter = lambda data, v: setbit(data, 1, v),
... ),
... )
>>> context = None # not needed
>>> request = zope.publisher.browser.TestRequest()
>>> formatter = table.Formatter(
... context, request, data, columns=columns)
>>> print formatter()
Bit 0
Bit 1
>>> request.form["test.Mw==.1.used"] = ""
>>> request.form["test.MA==.1.used"] = ""
>>> request.form["test.MA==.1"] = "on"
>>> input = columns[1].input(data, request)
>>> from pprint import pprint
>>> pprint(input)
{'MA==': True,
'Mw==': False}
>>> columns[1].update(data, input)
True
>>> data
([0, 2], [1, 1], [2, 2], [3, 1])
Column names
============
When defining columns, you can supply separate names and titles. You
would do this, for example, to use a blank title:
>>> columns = (
... column.FieldEditColumn(
... "", "test", schema.Bool(__name__='0'),
... lambda data: str(data[0]),
... getter = lambda data: 1&(data[1]),
... setter = lambda data, v: setbit(data, 0, v),
... name = "0",
... ),
... column.FieldEditColumn(
... "", "test", schema.Bool(__name__='1'),
... lambda data: str(data[0]),
... getter = lambda data: 2&(data[1]),
... setter = lambda data, v: setbit(data, 1, v),
... name = "1",
... ),
... )
>>> formatter = table.Formatter(
... context, request, data[0:1], columns=columns)
>>> print formatter()
<>& encoding bug
================
There was a bug in column.py, it did not encode the characters <>& to
< > &
>>> bugcontacts = (
... Contact('1', 'Bob ', 'bob@zope.com'),
... Contact('2', 'Sally & Baker', 'sally@zope.com'),
... )
We'll define columns that displays name and email addresses.
>>> from zc.table import column
>>> bugcolumns = (
... column.GetterColumn(
... title="Name", name="name",
... getter=lambda contact, formatter: contact.name,
... ),
... column.GetterColumn(
... title="E-mail", name="email",
... getter=lambda contact, formatter: contact.email,
... ),
... )
>>> request = zope.publisher.browser.TestRequest()
>>> context = None
>>> formatter = table.Formatter(
... context, request, bugcontacts, columns=bugcolumns)
>>> print formatter()
zc.table-0.9.0/src/zc.table.egg-info/ 0000775 0001771 0002004 00000000000 12053234232 020327 5 ustar menesis menesis 0000000 0000000 zc.table-0.9.0/src/zc.table.egg-info/namespace_packages.txt 0000664 0001771 0002004 00000000003 12053234232 024653 0 ustar menesis menesis 0000000 0000000 zc
zc.table-0.9.0/src/zc.table.egg-info/not-zip-safe 0000664 0001771 0002004 00000000001 12053233224 022555 0 ustar menesis menesis 0000000 0000000
zc.table-0.9.0/src/zc.table.egg-info/requires.txt 0000664 0001771 0002004 00000000312 12053234232 022723 0 ustar menesis menesis 0000000 0000000 setuptools
zc.resourcelibrary >= 0.6
zope.browserpage >= 3.10
zope.formlib >= 4
zope.cachedescriptors
zope.component
zope.formlib
zope.i18n
zope.interface
zope.schema
[test]
zope.testing
zope.publisher zc.table-0.9.0/src/zc.table.egg-info/dependency_links.txt 0000664 0001771 0002004 00000000001 12053234232 024375 0 ustar menesis menesis 0000000 0000000
zc.table-0.9.0/src/zc.table.egg-info/SOURCES.txt 0000664 0001771 0002004 00000001555 12053234232 022221 0 ustar menesis menesis 0000000 0000000 CHANGES.txt
COPYRIGHT.txt
LICENSE.txt
README.txt
TODO.txt
bootstrap.py
buildout.cfg
setup.py
src/zc/__init__.py
src/zc.table.egg-info/PKG-INFO
src/zc.table.egg-info/SOURCES.txt
src/zc.table.egg-info/dependency_links.txt
src/zc.table.egg-info/namespace_packages.txt
src/zc.table.egg-info/not-zip-safe
src/zc.table.egg-info/requires.txt
src/zc.table.egg-info/top_level.txt
src/zc/table/README.txt
src/zc/table/TODO.txt
src/zc/table/__init__.py
src/zc/table/batching.pt
src/zc/table/batching.py
src/zc/table/column.py
src/zc/table/column.txt
src/zc/table/configure.zcml
src/zc/table/fieldcolumn.py
src/zc/table/fieldcolumn.txt
src/zc/table/interfaces.py
src/zc/table/table.py
src/zc/table/testing.py
src/zc/table/tests.py
src/zc/table/resources/sort_arrows.gif
src/zc/table/resources/sort_arrows_down.gif
src/zc/table/resources/sort_arrows_up.gif
src/zc/table/resources/sorting.js zc.table-0.9.0/src/zc.table.egg-info/PKG-INFO 0000664 0001771 0002004 00000003564 12053234232 021434 0 ustar menesis menesis 0000000 0000000 Metadata-Version: 1.1
Name: zc.table
Version: 0.9.0
Summary: Zope table
Home-page: http://pypi.python.org/pypi/zc.table/
Author: Zope Foundation and Contributors
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: This is a Zope 3 extension that helps with the construction of (HTML) tables.
Features include dynamic HTML table generation, batching and sorting.
CHANGES
=======
0.9.0 (2012-11-21)
------------------
- Using Python's ``doctest`` module instead of deprecated
``zope.testing.doctest``.
- Removed dependency on ``zope.app.testing`` and ``zope.app.form``.
- Add test extra, move ``zope.testing`` to it.
0.8.1 (2010-05-25)
------------------
- Replaced html entities with unicode entities since they are xthml valid.
0.8.0 (2009-07-23)
------------------
- Updated tests to latest packages.
0.7.0 (2008-05-20)
------------------
- Fixed HTML-encoding of cell contents for ``GetterColumn``.
- Add ``href`` attributes (for MSIE) and fix up JavaScript on Next/Prev links
for batching.
- Update packaging.
0.6 (2006-09-22)
----------------
Initial release on Cheeseshop.
Keywords: zope zope3
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3
zc.table-0.9.0/src/zc.table.egg-info/top_level.txt 0000664 0001771 0002004 00000000003 12053234232 023052 0 ustar menesis menesis 0000000 0000000 zc
zc.table-0.9.0/buildout.cfg 0000664 0001771 0002004 00000000141 12053233200 016642 0 ustar menesis menesis 0000000 0000000 [buildout]
develop = .
parts = test
[test]
recipe = zc.recipe.testrunner
eggs = zc.table [test]
zc.table-0.9.0/COPYRIGHT.txt 0000664 0001771 0002004 00000000040 12053233200 016441 0 ustar menesis menesis 0000000 0000000 Zope Foundation and Contributors zc.table-0.9.0/setup.py 0000664 0001771 0002004 00000004277 12053233200 016062 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2006 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 for zc.table package
"""
import os
from setuptools import setup, find_packages
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
setup(
name="zc.table",
version='0.9.0',
url="http://pypi.python.org/pypi/zc.table/",
install_requires=[
'setuptools',
'zc.resourcelibrary >= 0.6',
'zope.browserpage >= 3.10',
'zope.formlib >= 4',
'zope.cachedescriptors',
'zope.component',
'zope.formlib',
'zope.i18n',
'zope.interface',
'zope.schema',
],
extras_require=dict(
test=['zope.testing',
'zope.publisher']),
packages=find_packages('src'),
package_dir= {'':'src'},
namespace_packages=['zc'],
package_data = {
'': ['*.txt', '*.zcml', '*.gif', '*.js'],
'zc.table':['resources/*', '*.pt'],
},
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.org',
description="Zope table",
long_description=(
read('README.txt')
+ '\n\n' +
read('CHANGES.txt')
),
license='ZPL 2.1',
keywords="zope zope3",
classifiers = [
'Development Status :: 5 - Production/Stable',
'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'],
zip_safe=False,
)
zc.table-0.9.0/PKG-INFO 0000664 0001771 0002004 00000003564 12053234232 015451 0 ustar menesis menesis 0000000 0000000 Metadata-Version: 1.1
Name: zc.table
Version: 0.9.0
Summary: Zope table
Home-page: http://pypi.python.org/pypi/zc.table/
Author: Zope Foundation and Contributors
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: This is a Zope 3 extension that helps with the construction of (HTML) tables.
Features include dynamic HTML table generation, batching and sorting.
CHANGES
=======
0.9.0 (2012-11-21)
------------------
- Using Python's ``doctest`` module instead of deprecated
``zope.testing.doctest``.
- Removed dependency on ``zope.app.testing`` and ``zope.app.form``.
- Add test extra, move ``zope.testing`` to it.
0.8.1 (2010-05-25)
------------------
- Replaced html entities with unicode entities since they are xthml valid.
0.8.0 (2009-07-23)
------------------
- Updated tests to latest packages.
0.7.0 (2008-05-20)
------------------
- Fixed HTML-encoding of cell contents for ``GetterColumn``.
- Add ``href`` attributes (for MSIE) and fix up JavaScript on Next/Prev links
for batching.
- Update packaging.
0.6 (2006-09-22)
----------------
Initial release on Cheeseshop.
Keywords: zope zope3
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3
zc.table-0.9.0/bootstrap.py 0000664 0001771 0002004 00000003165 12053233200 016732 0 ustar menesis menesis 0000000 0000000 ##############################################################################
#
# Copyright (c) 2006 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.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
$Id: bootstrap.py 68905 2006-06-29 10:46:56Z jim $
"""
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
ws = pkg_resources.working_set
assert os.spawnle(
os.P_WAIT, sys.executable, sys.executable,
'-c', 'from setuptools.command.easy_install import main; main()',
'-mqNxd', tmpeggs, 'zc.buildout',
{'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)
zc.table-0.9.0/setup.cfg 0000664 0001771 0002004 00000000073 12053234232 016165 0 ustar menesis menesis 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
zc.table-0.9.0/TODO.txt 0000664 0001771 0002004 00000000306 12053233200 015643 0 ustar menesis menesis 0000000 0000000 To-do
-----
- Get an updated version of the buildout/testrunner recipes to
generate a usable test runner (bin/test). Currently needs to be
manually hacked to add the path for parts/zope3/src.
zc.table-0.9.0/CHANGES.txt 0000664 0001771 0002004 00000001341 12053233200 016146 0 ustar menesis menesis 0000000 0000000 CHANGES
=======
0.9.0 (2012-11-21)
------------------
- Using Python's ``doctest`` module instead of deprecated
``zope.testing.doctest``.
- Removed dependency on ``zope.app.testing`` and ``zope.app.form``.
- Add test extra, move ``zope.testing`` to it.
0.8.1 (2010-05-25)
------------------
- Replaced html entities with unicode entities since they are xthml valid.
0.8.0 (2009-07-23)
------------------
- Updated tests to latest packages.
0.7.0 (2008-05-20)
------------------
- Fixed HTML-encoding of cell contents for ``GetterColumn``.
- Add ``href`` attributes (for MSIE) and fix up JavaScript on Next/Prev links
for batching.
- Update packaging.
0.6 (2006-09-22)
----------------
Initial release on Cheeseshop.
zc.table-0.9.0/LICENSE.txt 0000664 0001771 0002004 00000004026 12053233200 016163 0 ustar menesis menesis 0000000 0000000 Zope Public License (ZPL) Version 2.1
A copyright notice accompanies this license document that identifies the
copyright holders.
This license has been certified as open source. It has also been designated as
GPL compatible by the Free Software Foundation (FSF).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions in source code must retain the accompanying copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the accompanying copyright
notice, this list of conditions, and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Names of the copyright holders must not be used to endorse or promote
products derived from this software without prior written permission from the
copyright holders.
4. The right to distribute this software or to use it for any purpose does not
give you the right to use Servicemarks (sm) or Trademarks (tm) of the
copyright
holders. Use of them is covered by separate agreement with the copyright
holders.
5. If any files are modified, you must cause the modified files to carry
prominent notices stating that you changed the files and the date of any
change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.