[Checkins] SVN: zc.site/trunk/src/zc/site/ Initial import. A grab-bag that needs some rearrangement. Currently includes zc.table columns useful for search results, an "addLocalUtility" function that is currently a dependency for some other packages, and some other bits.

Gary Poster gary at zope.com
Tue Aug 15 17:23:28 EDT 2006


Log message for revision 69544:
  Initial import.  A grab-bag that needs some rearrangement.  Currently includes zc.table columns useful for search results, an "addLocalUtility" function that is currently a dependency for some other packages, and some other bits.
  

Changed:
  A   zc.site/trunk/src/zc/site/__init__.py
  A   zc.site/trunk/src/zc/site/browser/
  A   zc.site/trunk/src/zc/site/browser/__init__.py
  A   zc.site/trunk/src/zc/site/browser/addSelect.pt
  A   zc.site/trunk/src/zc/site/browser/configure.zcml
  A   zc.site/trunk/src/zc/site/configure.zcml
  A   zc.site/trunk/src/zc/site/i18n.py
  A   zc.site/trunk/src/zc/site/search.py
  A   zc.site/trunk/src/zc/site/search.txt
  A   zc.site/trunk/src/zc/site/tests.py
  A   zc.site/trunk/src/zc/site/utils.py

-=-
Added: zc.site/trunk/src/zc/site/__init__.py
===================================================================
--- zc.site/trunk/src/zc/site/__init__.py	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/__init__.py	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1 @@
+#

Added: zc.site/trunk/src/zc/site/browser/__init__.py
===================================================================
--- zc.site/trunk/src/zc/site/browser/__init__.py	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/browser/__init__.py	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1 @@
+#

Added: zc.site/trunk/src/zc/site/browser/addSelect.pt
===================================================================
--- zc.site/trunk/src/zc/site/browser/addSelect.pt	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/browser/addSelect.pt	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,47 @@
+<metal:block define-macro="standard_add">
+  <tal:block define="adding nocall:context/@@+|nothing;" 
+             condition="nocall:adding">
+    <span tal:define="addingInfo adding/addingInfo;" class="createSelect" 
+          tal:condition="addingInfo"
+          i18n:domain="zc.site">
+      <span i18n:translate="">Create New:</span>
+      <tal:block 
+           define="has_custom_add_view adding/hasCustomAddView; 
+                   names_required adding/nameAllowed"
+           condition="adding/isSingleMenuItem">
+        <tal:block define="info python:addingInfo[0];">
+          <input type="submit" name="container_add_button"
+                 class="submit"
+                 tal:attributes="value info/title" i18n:attributes="value" />
+          <input type="text" name="single_new_value" id="focusid"
+                 tal:condition="python:names_required and not has_custom_add_view"
+               />
+          <input type="hidden" name="single_type_name"
+               value=""
+               tal:attributes="value info/action" 
+               />
+        </tal:block>
+      </tal:block>
+      <tal:block condition="not: adding/isSingleMenuItem">
+        <script type="text/javascript">
+          function zcAddSelect(elem) {
+            var index = elem.selectedIndex;
+            var value = elem.options[index].value;
+            if (value) {
+              location.href = "@@+/action.html?type_name=" + value;
+            }
+          }
+        </script>
+        <select onchange="zcAddSelect(this);"
+                name="zc-addSelect-type" id="zc-addSelect-type">
+          <option value='' i18n:translate="">-- Select --</option>
+          <tal:block repeat="info addingInfo">
+            <option i18n:translate=""
+                    tal:attributes="value info/action"
+                    tal:content="info/title">Option</option>
+          </tal:block>
+        </select>
+      </tal:block>
+    </span>
+  </tal:block>
+</metal:block>

Added: zc.site/trunk/src/zc/site/browser/configure.zcml
===================================================================
--- zc.site/trunk/src/zc/site/browser/configure.zcml	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/browser/configure.zcml	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,14 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:zc="http://namespaces.zope.com/zc"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zc.site">
+
+<browser:page
+    for="*"
+    name="container_add"
+    permission="zope.Public"
+    template="addSelect.pt"
+    />
+
+</configure>

Added: zc.site/trunk/src/zc/site/configure.zcml
===================================================================
--- zc.site/trunk/src/zc/site/configure.zcml	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/configure.zcml	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,15 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+  <include package=".browser" />
+
+  <class class=".search.ReadStatusColumn">
+    <require
+        permission="zope.Public"
+        interface="zc.table.interfaces.IColumn
+                   zc.table.interfaces.ISortableColumn"
+        />
+  </class>
+
+  <class class=".search.TypeColumn">
+    <require like_class=".search.ReadStatusColumn"/>
+  </class>
+</configure>

Added: zc.site/trunk/src/zc/site/i18n.py
===================================================================
--- zc.site/trunk/src/zc/site/i18n.py	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/i18n.py	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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
+#
+##############################################################################
+"""I18N support for the site package.
+
+This defines a `MessageFactory` for the I18N domain for the site
+package.  This is normally used with this import::
+
+  from i18n import MessageFactory as _
+
+The factory is then used normally.  Two examples::
+
+  text = _('some internationalized text')
+  text = _('helpful-descriptive-message-id', 'default text')
+"""
+__docformat__ = "reStructuredText"
+
+
+from zope import i18nmessageid
+
+MessageFactory = _ = i18nmessageid.MessageFactory("zc.site")

Added: zc.site/trunk/src/zc/site/search.py
===================================================================
--- zc.site/trunk/src/zc/site/search.py	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/search.py	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,227 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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.
+#
+##############################################################################
+"""
+Search support classes.
+
+These classes are used to support handling and presenting search
+results.  Results must be sets of int ids.
+
+"""
+__docformat__ = "reStructuredText"
+
+import zope.i18n
+import zope.formlib.form
+import zope.copypastemove.interfaces
+
+from zope.app import zapi
+
+import zc.readcatalog.utils
+import zc.table.column
+
+from zc.site.i18n import _
+
+
+# Specialized column types
+
+UNKNOWN_TYPE = _('unknown_type-title', 'Unknown')
+
+
+class ReadStatusColumn(zc.table.column.SortingColumn):
+
+    def renderCell(self, item, formatter):
+        return zope.i18n.translate(
+            zc.readcatalog.utils.readStatus(
+                formatter.context.getObject(item), 
+                formatter.request.principal.id))
+
+    getSortKey = renderCell
+
+
+class TypeColumn(zc.table.column.SortingColumn):
+
+    types = () # types should be sequence of (interface, message
+    # id) tuple pairs
+    
+    unknown_type = UNKNOWN_TYPE
+
+    def __init__(self, title, types, name=None):
+        self.types = types
+        super(TypeColumn, self).__init__(title, name)
+
+    def renderCell(self, item, formatter):
+        obj = formatter.context.getObject(item)
+        for i, msg in self.types:
+            if i.providedBy(obj):
+                return zope.i18n.translate(msg,
+                                           default=msg,
+                                           context=formatter.request)
+        return zope.i18n.translate(self.unknown_type,
+                                   context=formatter.request)
+
+    getSortKey = renderCell
+
+
+class SelectionColumn(zc.table.column.SelectionColumn):
+
+    """Specialized selection column for use with search results.
+
+    This provides a default idgetter implementation suitable for use
+    with intids.
+
+    """
+
+    # It is important that the column selection provide only items that
+    # are in the search results.  This ensures that security filters are
+    # honored and that objects cannot be accessed simply by crafting a
+    # request that references the int id of an object that should not be
+    # referencable by the current user.
+
+    # One expected improvement is to control whether the selection box
+    # for each item is enabled or disabled based on whether the object
+    # can be copied.
+
+    def __init__(self, idgetter=None, field=None, prefix=None, getter=None,
+                 setter=None, title=None, name='', hide_header=False):
+        if idgetter is None:
+            idgetter = lambda item: "%s_selected" % item
+        super(SelectionColumn, self).__init__(
+            idgetter, field=field, prefix=prefix,
+            getter=getter, setter=setter, title=title, name=name,
+            hide_header=hide_header)
+
+
+# Action for use in forms:
+
+class CopySelectionError(zope.app.form.interfaces.WidgetInputError):
+
+    def __init__(self, msg):
+        zope.app.form.interfaces.WidgetInputError.__init__(
+            self, u"selected", u"Selected", msg)
+
+    def doc(self):
+        return self.errors
+
+class UncopyableSelectionError(CopySelectionError):
+    """Exception raised when an entry is selected that can't be copied."""
+
+class NoItemsSelectedError(CopySelectionError):
+    """Exception raised when no entry is selected for copying."""
+
+    def __init__(self):
+        CopySelectionError.__init__(
+            self, _("No items selected for copying."))
+
+
+class SelectionToClipboard(zope.formlib.form.Action):
+    """Action that can copy selected items to the clipboard.
+
+    The constructor needs to get the selection column that should be
+    used as the basis for determining which items to copy.
+
+    """
+
+    def __init__(self, *args, **kw):
+        """Initialize the action.
+
+        Additional keyword arguments are accepted:
+
+        `column` is the `SelectionColumn` that should be used as the
+        selection discriminator.  This is required.
+
+        `items` is a callable that should be used to get the set of
+        items that needs to be passed to the selection column's
+        `getSelected()` method.  The callable will be passed the form
+        as an argument.  This is required.
+
+        `key` is the key that should be used in the data dictionary.
+        The default is 'selected-objects'.
+
+        """
+        self._selection_column = kw.pop("column")
+        self._selection_name = kw.pop("key", "selected-objects")
+        self._form_items = kw.pop("items")
+        if not kw.get("label"):
+            kw["label"] = _("zc-searchportlet-copy-selection-button",
+                            default="Copy")
+        if not kw.get("name"):
+            kw["name"] = "copy"
+        super(SelectionToClipboard, self).__init__(*args, **kw)
+
+    def available(self):
+        # Only show this action if there is anything to select from.
+        items = self._form_items(self.form)
+        if not items:
+            return False
+        it = iter(items)
+        try:
+            it.next()
+        except StopIteration:
+            return False
+        else:
+            return True
+
+    def validate(self, data):
+        """Check that selected objects can be copied.
+
+        If any objects can't be copied, return
+        UncopyableSelectionError instances for each.
+
+        Add a list of selected, copyable objects to `data` using the
+        key passed to the constructor.
+
+        """
+        items = []
+        errors = []
+        selected = self._selection_column.getSelected(
+            self._form_items(self.form), self.form.request)
+
+        for uid in selected:
+            ob = self.form.getObject(uid)
+            copier = zope.copypastemove.interfaces.IObjectCopier(ob)
+            if copier.copyable():
+                items.append(ob)
+            else:
+                m = {"name": ob.__name__}
+                title = zope.app.container.browser.contents.getDCTitle(ob)
+                if title:
+                    msg = _(
+                        "Object '${name}' (${title}) cannot be copied")
+                    m["title"] = title
+                else:
+                    msg = _("Object '${name}' cannot be copied")
+                msg.mapping.update(m)
+                errors.append(UncopyableSelectionError(msg))
+        data[self._selection_name] = items
+        if not (errors or items):
+            # No errors, but no items selected:
+            errors.append(NoItemsSelectedError())
+        return errors
+
+    def success(self, data):
+        """Copy the selected objects to the clipboard."""
+        items = data[self._selection_name]
+        clipboard = zope.app.container.browser.contents.getPrincipalClipboard(
+            self.form.request)
+        clipboard.clearContents()
+        paths = [zapi.getPath(ob) for ob in items]
+        clipboard.addItems('copy', paths)
+        # Provide positive feedback that we've copied something;
+        # `paths` will not be empty since `validate()` returns an
+        # error in that case.
+        if len(paths) == 1:
+            self.form.status = _("One item copied to the clipboard.")
+        else:
+            self.form.status = _("${count} items copied to the clipboard.",
+                                 mapping={"count": str(len(paths))})

Added: zc.site/trunk/src/zc/site/search.txt
===================================================================
--- zc.site/trunk/src/zc/site/search.txt	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/search.txt	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,48 @@
+=======================================
+Copying Search Results to the Clipboard
+=======================================
+
+.. NOTE: This is all tutorial, not a test (for now).
+
+The `zc.site.search` module includes tools to support copying search
+results to the clipboard, which can be used for both portlet and
+non-portlet uses of searching.  The support provided requires that
+search results are represented by sets of int ids and are presented
+using a table formatter based on `zc.table`.
+
+There are two pieces needed to make use of this support.
+
+A column of checkboxes is used to allow the user to select specific
+search results; this can be included in the column set for the table
+like any other column (though it is commonly placed first).  This can
+be included very simply::
+
+    >>> import zc.searchportlet.portlet
+    >>> import zc.searchportlet.table
+    >>> import zc.searchportlet.view
+    >>> import zc.site.search
+
+    >>> class MySearchPortlet(zc.searchportlet.portlet.AbstractPortlet):
+    ...     columns = (
+    ...         zc.site.search.SelectionColumn(),
+    ...         zc.searchportlet.table.TitleColumn(u'Title'),
+    ...         )
+
+The form used to display and work with search results needs to have an
+action that checks the selection and performs the copy to the
+clipboard.  The constructor for the action needs the column provided
+in the column set and a callable that can return the items in the
+result set as keyword arguments::
+
+    >>> class MySearchPortletNormalView(
+    ...           zc.searchportlet.view.AbstractNormalView):
+    ...
+    ...     # Make a copy of the base actions so we can modify it:
+    ...     actions = zc.searchportlet.view.AbstractNormalView.actions[:]
+    ...
+    ...     actions.append(
+    ...         zc.site.search.SelectionToClipboard(
+    ...             column=MySearchPortlet.columns[0],
+    ...             items=lambda form: form.items))
+
+And that's all you need to support copying of search results.


Property changes on: zc.site/trunk/src/zc/site/search.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.site/trunk/src/zc/site/tests.py
===================================================================
--- zc.site/trunk/src/zc/site/tests.py	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/tests.py	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,29 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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.
+#
+##############################################################################
+"""site package test runner
+
+$$
+"""
+
+import unittest
+from zope.testing import doctest
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('search.txt'),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: zc.site/trunk/src/zc/site/utils.py
===================================================================
--- zc.site/trunk/src/zc/site/utils.py	2006-08-15 21:19:32 UTC (rev 69543)
+++ zc.site/trunk/src/zc/site/utils.py	2006-08-15 21:23:27 UTC (rev 69544)
@@ -0,0 +1,36 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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.
+#
+##############################################################################
+"""site utils
+
+$Id: utils.py 12899 2006-07-26 21:57:37Z benji $
+"""
+import zope.component.interfaces
+import zope.app.container.interfaces
+import zope.event
+import zope.lifecycleevent
+import zope.app.component.interfaces.registration
+
+# from a site, to get to the default package, the incantation is
+# site.getSiteManager()['default']
+
+def addLocalUtility(package, utility, interface=None,
+                    name='', name_in_container='', comment=u''):
+    chooser = zope.app.container.interfaces.INameChooser(package)
+    name_in_container = chooser.chooseName(name_in_container, utility)
+    zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(utility))
+    package[name_in_container] = utility
+    # really want IComponentRegistry, but that is not set up in Zope 3 ATM
+    registry = zope.component.interfaces.IComponentLookup(package)
+    registry.registerUtility(utility, interface, name, comment)



More information about the Checkins mailing list