[Checkins] SVN: z3c.contents/trunk/ Merge darrylcousins branch back
to the trunk
Roger Ineichen
roger at projekt01.ch
Fri Apr 11 10:57:35 EDT 2008
Log message for revision 85255:
Merge darrylcousins branch back to the trunk
Improve buildout setup
Changed:
U z3c.contents/trunk/CHANGES.txt
U z3c.contents/trunk/buildout.cfg
_U z3c.contents/trunk/externals/
U z3c.contents/trunk/setup.py
A z3c.contents/trunk/src/z3c/contents/BROWSER.txt
U z3c.contents/trunk/src/z3c/contents/README.txt
U z3c.contents/trunk/src/z3c/contents/browser.py
U z3c.contents/trunk/src/z3c/contents/browser.zcml
U z3c.contents/trunk/src/z3c/contents/column.py
U z3c.contents/trunk/src/z3c/contents/configure.zcml
U z3c.contents/trunk/src/z3c/contents/contents.pt
A z3c.contents/trunk/src/z3c/contents/ftesting.zcml
A z3c.contents/trunk/src/z3c/contents/ftests.py
A z3c.contents/trunk/src/z3c/contents/header.py
U z3c.contents/trunk/src/z3c/contents/interfaces.py
A z3c.contents/trunk/src/z3c/contents/search.pt
A z3c.contents/trunk/src/z3c/contents/search.py
A z3c.contents/trunk/src/z3c/contents/testing.pt
U z3c.contents/trunk/src/z3c/contents/testing.py
U z3c.contents/trunk/src/z3c/contents/tests.py
-=-
Modified: z3c.contents/trunk/CHANGES.txt
===================================================================
--- z3c.contents/trunk/CHANGES.txt 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/CHANGES.txt 2008-04-11 14:57:35 UTC (rev 85255)
@@ -5,4 +5,6 @@
Version 0.5.0 (unreleased)
--------------------------
+- Merged search branch back to trunk
+
- Initial Release
Modified: z3c.contents/trunk/buildout.cfg
===================================================================
--- z3c.contents/trunk/buildout.cfg 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/buildout.cfg 2008-04-11 14:57:35 UTC (rev 85255)
@@ -1,12 +1,13 @@
[buildout]
develop = .
externals/z3c.table
-parts = test checker coverage
+parts = test checker coverage-test coverage-report
[test]
recipe = zc.recipe.testrunner
eggs = z3c.contents [test]
+defaults = ['--tests-pattern', '^f?tests$', '-v']
[checker]
@@ -14,6 +15,14 @@
path = src/z3c/contents
-[coverage]
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3c.contents [test]
+defaults = ['--coverage', '../../coverage']
+
+
+[coverage-report]
recipe = zc.recipe.egg
eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
Property changes on: z3c.contents/trunk/externals
___________________________________________________________________
Name: svn:externals
- z3c.table svn://svn.zope.org/repos/main/z3c.table/trunk
+ z3c.table svn://svn.zope.org/repos/main/z3c.table/trunk
Modified: z3c.contents/trunk/setup.py
===================================================================
--- z3c.contents/trunk/setup.py 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/setup.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -23,7 +23,7 @@
setup (
name='z3c.contents',
- version='0.5.0',
+ version='0.5.0dev',
author = "Roger Ineichen and the Zope Community",
author_email = "zope3-dev at zope.org",
description = "Container management page based on z3c.form and z3c.table for Zope3",
@@ -52,10 +52,14 @@
extras_require = dict(
test = [
'z3c.macro',
+ 'z3c.layer.ready2go',
+ 'z3c.macro',
'z3c.table',
+ 'z3c.etestbrowser',
'zope.app.pagetemplate',
+ 'zope.app.securitypolicy',
'zope.app.testing',
- 'zope.component',
+ 'zope.publisher',
'zope.testing',
],
),
@@ -68,12 +72,18 @@
'z3c.table',
'z3c.template',
'zope.annotation',
+ 'zope.app.container',
+ 'zope.component',
'zope.contentprovider',
'zope.copypastemove',
'zope.exceptions',
+ 'zope.i18n',
+ 'zope.i18nmessageid',
+ 'zope.index',
'zope.interface',
+ 'zope.schema',
'zope.security',
'zope.traversing',
],
zip_safe = False,
-)
\ No newline at end of file
+)
Copied: z3c.contents/trunk/src/z3c/contents/BROWSER.txt (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/BROWSER.txt)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/BROWSER.txt (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/BROWSER.txt 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,60 @@
+========
+Contents
+========
+
+The goal of this package is to offer a modular replacement for the default
+contents.html page used in Zope3.
+
+Initially
+=========
+
+In ftesting.zcml we have set up a test layer to use for the following tests and
+have also registered a Contents view as 'index' for the root folder:
+
+ >>> from z3c.etestbrowser.testing import ExtendedTestBrowser
+ >>> import zope.component
+ >>> browser = ExtendedTestBrowser()
+ >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+ >>> browser.handleErrors = False
+ >>> URL = 'http://localhost/++skin++ContentsTesting%s'
+
+We have sorting urls in the column headers. Initially the table is sorted on the
+name column ascending.
+
+ >>> browser.open(URL % '/')
+ >>> printElement(browser, "//table/thead/tr/th", multiple=True)
+ <th>X</th>
+ <th><a
+ href="?contents-sortOn=contents-renameColumn-1&contents-sortOrder=descending"
+ title="Sort">Name</a></th>
+ <th><a
+ href="?contents-sortOn=contents-createdColumn-2&contents-sortOrder=ascending"
+ title="Sort">Created</a></th>
+ <th><a
+ href="?contents-sortOn=contents-modifiedColumn-3&contents-sortOrder=ascending"
+ title="Sort">Modified</a></th>
+
+
+Sample data set up
+==================
+
+ >>> container = getRootFolder()
+ >>> from z3c.contents.testing import Content
+ >>> container[u'zero'] = Content('Zero', 0)
+ >>> container[u'first'] = Content('First', 1)
+ >>> container[u'second'] = Content('Second', 2)
+ >>> container[u'third'] = Content('Third', 3)
+ >>> container[u'fourth'] = Content('Fourth', 4)
+
+We can search within the container - this is a simple search on ids and string
+attributes on the contained objects.
+
+ >>> browser.getControl(name="search.widgets.searchterm").value = u'4 second Third'
+ >>> browser.getControl(name="search.buttons.search").click()
+
+Note that the searchterms are also now included in the sorting header link.
+
+ >>> printElement(browser, "//table/thead/tr/th[2]")
+ <th><a
+ href="?contents-sortOn=contents-renameColumn-1&contents-sortOrder=descending&search.widgets.searchterm=4+second+Third"
+ title="Sort">Name</a></th>
Modified: z3c.contents/trunk/src/z3c/contents/README.txt
===================================================================
--- z3c.contents/trunk/src/z3c/contents/README.txt 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/README.txt 2008-04-11 14:57:35 UTC (rev 85255)
@@ -44,7 +44,8 @@
>>> from z3c.form.testing import setupFormDefaults
>>> setupFormDefaults()
-And we need to configure our contents.pt template for the ContentsPage:
+And we need to configure our contents.pt template for the ContentsPage (we
+also configure the template for the search sub form here too)
>>> import os
>>> import sys
@@ -53,6 +54,8 @@
>>> context = xmlconfig.file('meta.zcml', z3c.template)
>>> contentsTemplate = os.path.join(os.path.dirname(z3c.contents.__file__),
... 'contents.pt')
+ >>> searchTemplate = os.path.join(os.path.dirname(z3c.contents.__file__),
+ ... 'search.pt')
>>> context = xmlconfig.string("""
... <configure
... xmlns:z3c="http://namespaces.zope.org/z3c">
@@ -60,8 +63,12 @@
... for="z3c.contents.interfaces.IContentsPage"
... template="%s"
... />
+ ... <z3c:template
+ ... for="z3c.contents.browser.ContentsSearchForm"
+ ... template="%s"
+ ... />
... </configure>
- ... """ % contentsTemplate, context=context)
+ ... """ % (contentsTemplate, searchTemplate), context=context)
And load the formui confguration, which will make sure that all macros get
@@ -110,6 +117,7 @@
– required
</div>
<div>
+ ...
</div>
</div>
<div>
@@ -174,6 +182,7 @@
– required
</div>
<div>
+ ...
<table>
<thead>
<tr>
@@ -236,7 +245,6 @@
</div>
</form>
-
Sorting
-------
@@ -361,6 +369,7 @@
– required
</div>
<div>
+ ...
<table>
<thead>
<tr>
@@ -827,7 +836,113 @@
[(u'fifth', <Content Second 2>), (u'first', <Content First 1>),
(u'zero', <Content Zero 0>)]
+Search
+------
+Add an IFind adapter for the search:
+
+ >>> from z3c.contents.interfaces import ISearch
+ >>> from z3c.contents.search import SearchForContainer
+ >>> zope.component.provideAdapter(SearchForContainer)
+
+And also register ISearchableText adapter for the contained objects, the default
+filters for searching include searching the keys (content __name__) and using
+an ISearchableText adapter for the contained objects.
+
+ >>> from z3c.contents.testing import SearchableTextForContent
+ >>> zope.component.provideAdapter(SearchableTextForContent)
+
+The default search adapter matches search terms to the objects id in the
+container or to any possible string attribute.
+
+ >>> searchRequest = TestRequest(form={'search.buttons.search': 'Search',
+ ... 'search.widgets.searchterm': u'1 zero'})
+ >>> alsoProvides(searchRequest, IDivFormLayer)
+ >>> searchPage = browser.ContentsPage(secondContainer, searchRequest)
+ >>> searchPage.update()
+ >>> print searchPage.render()
+ <form action="http://127.0.0.1" method="post"
+ enctype="multipart/form-data" class="edit-form"
+ name="contents" id="contents">
+ ...
+ <tbody>
+ <tr>
+ <td><input type="checkbox" class="checkbox-widget" name="contents-checkBoxColumn-0-selectedItems" value="first" /></td>
+ <td><a href="http://127.0.0.1/secondContainer/first">first</a></td>
+ <td>01/01/01 01:01</td>
+ <td>02/02/02 02:02</td>
+ </tr>
+ <tr>
+ <td><input type="checkbox" class="checkbox-widget" name="contents-checkBoxColumn-0-selectedItems" value="zero" /></td>
+ <td><a href="http://127.0.0.1/secondContainer/zero">zero</a></td>
+ <td>01/01/01 01:01</td>
+ <td>02/02/02 02:02</td>
+ </tr>
+ </tbody>
+ ...
+
+Headers
+-------
+
+We have adapters to the columns that support inclusion of links in the headers
+to sort the columns.
+
+ >>> from z3c.contents.header import ContentsColumnHeader
+ >>> from z3c.table.interfaces import IColumnHeader
+ >>> zope.component.provideAdapter(ContentsColumnHeader,
+ ... (IContainer, None, interfaces.IContentsPage, column.RenameColumn),
+ ... provides=IColumnHeader)
+
+Now we shall see that the name column header includes a link with arguments to
+sort the table by that column.
+
+ >>> headerRequest = TestRequest()
+ >>> alsoProvides(headerRequest, IDivFormLayer)
+ >>> headerPage = browser.ContentsPage(secondContainer, headerRequest)
+ >>> headerPage.update()
+ >>> print headerPage.render()
+ <form action="http://127.0.0.1" method="post"
+ enctype="multipart/form-data" class="edit-form"
+ name="contents" id="contents">
+ ...
+ <thead>
+ <tr>
+ <th>X</th>
+ <th><a
+ href="?contents-sortOn=contents-renameColumn-1&contents-sortOrder=descending"
+ title="Sort">Name</a></th>
+ <th>Created</th>
+ <th>Modified</th>
+ </tr>
+ </thead>
+ ...
+
+When we perform a search we also expect the the search terms will also be
+included in the query so as to maintain the search across views.
+
+ >>> searchRequest = TestRequest(form={'search.buttons.search': 'Search',
+ ... 'search.widgets.searchterm': u'First zero'})
+ >>> alsoProvides(searchRequest, IDivFormLayer)
+ >>> searchPage = browser.ContentsPage(secondContainer, searchRequest)
+ >>> searchPage.update()
+ >>> print searchPage.render()
+ <form action="http://127.0.0.1" method="post"
+ enctype="multipart/form-data" class="edit-form"
+ name="contents" id="contents">
+ ...
+ <thead>
+ <tr>
+ <th>X</th>
+ <th><a
+ href="?contents-sortOn=contents-renameColumn-1&contents-sortOrder=descending&search.widgets.searchterm=First+zero"
+ title="Sort">Name</a></th>
+ <th>Created</th>
+ <th>Modified</th>
+ </tr>
+ </thead>
+ ...
+
+
Batching
--------
@@ -855,11 +970,29 @@
– required
</div>
<div>
+ <fieldset>
+ <legend>Search</legend>
+ <table>
+ <tr>
+ <td class="row">
+ <label for="search-widgets-searchterm">Search</label>
+ <input type="text" id="search-widgets-searchterm"
+ name="search.widgets.searchterm"
+ class="text-widget required textline-field" value="" />
+ </td>
+ <td class="action">
+ <input type="submit" id="search-buttons-search"
+ name="search.buttons.search"
+ class="submit-widget button-field" value="Search" />
+ </td>
+ </tr>
+ </table>
+ </fieldset>
<table class="contents">
<thead>
<tr>
<th>X</th>
- <th>Name</th>
+ <th><a href="?contents-sortOn=contents-renameColumn-1&contents-sortOrder=descending" title="Sort">Name</a></th>
<th>Created</th>
<th>Modified</th>
</tr>
Modified: z3c.contents/trunk/src/z3c/contents/browser.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/browser.py 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/browser.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -31,16 +31,17 @@
from zope.security.interfaces import Unauthorized
from zope.traversing.interfaces import TraversalError
from zope.traversing import api
-
+from zope.app.container.find import SimpleIdFindFilter
from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.container.interfaces import DuplicateIDError
-from z3c.form import button
+from z3c.form import button, field
from z3c.formui import form
from z3c.table import table
from z3c.template.template import getPageTemplate
from z3c.contents import interfaces
+from z3c.contents.search import SearchableTextFindFilter
_ = zope.i18nmessageid.MessageFactory('z3c')
@@ -62,6 +63,41 @@
return default
+class ContentsSearch(object):
+ """An adapter for container context to satisfy search form requirements
+
+ """
+
+ zope.interface.implements(interfaces.IContentsSearch)
+ zope.component.adapts(zope.interface.Interface)
+
+ def __init__(self, context):
+ self.context = context
+
+ def get_searchterm(self):
+ return u''
+
+ def set_searchterm(self, value):
+ pass
+
+ searchterm = property(get_searchterm, set_searchterm)
+
+class ContentsSearchForm(form.Form):
+
+ template = getPageTemplate()
+ fields = field.Fields(interfaces.IContentsSearch)
+ prefix = 'search'
+ table = None
+
+ @button.buttonAndHandler(_('Search'), name='search')
+ def handleSearch(self, action):
+ data, errors = self.extractData()
+ if errors:
+ self.status = u'Some error message'
+ return
+ self.table.searchterm = data.get('searchterm', '')
+
+
# conditions
def canCut(form):
return form.supportsCut
@@ -90,6 +126,9 @@
template = getPageTemplate()
+ # search sub-form
+ search = None
+
# internal defaults
selectedItems = []
ignoreContext = False
@@ -100,6 +139,9 @@
supportsPaste = False
supportsRename = False
+ # sort attributes
+ sortOn = 1 # initial sort on name column
+
# customize this part
allowCut = True
allowCopy = True
@@ -108,6 +150,7 @@
allowRename = True
prefix = 'contents'
+ searchterm = ''
# error messages
cutNoItemsMessage = _('No items selected for cut')
@@ -128,10 +171,16 @@
renameItemNotFoundMessage = _('Item not found')
def update(self):
+
+ self.search = ContentsSearchForm(self.context, self.request)
+ self.search.table = self
+ self.search.update()
+
# first setup columns and process the items as selected if any
super(ContentsPage, self).update()
# second find out if we support paste
self.clipboard = queryPrincipalClipboard(self.request)
+
self.setupCopyPasteMove()
self.updateWidgets()
self.updateActions()
@@ -162,6 +211,28 @@
return self.template()
@property
+ def values(self):
+
+ # not searching
+ if not self.searchterm:
+ return self.context.values()
+
+ # no search adapter for the context
+ try:
+ search = interfaces.ISearch(self.context)
+ except TypeError:
+ return self.context.values()
+
+ # perform the search
+ searchterms = self.searchterm.split(' ')
+
+ # possible enhancement would be to look up these filters as adapters to
+ # the container! Maybe we can use catalogs here?
+ return search.search(id_filters=[SimpleIdFindFilter(searchterms)],
+ object_filters=[SearchableTextFindFilter(searchterms)])
+
+
+ @property
def hasContent(self):
return bool(self.values)
@@ -326,7 +397,7 @@
return
try:
for item in self.selectedItems:
- del self.context[api.getName(item)]
+ self.executeDelete(item)
except KeyError:
self.status = self.deleteErrorMessage
transaction.doom()
@@ -334,6 +405,11 @@
self.updateAfterActionExecution()
self.status = self.deleteSucsessMessage
+ def executeDelete(self, item):
+ """Do the actual item deletion
+ """
+ del self.context[api.getName(item)]
+
@button.buttonAndHandler(_('Rename'), name='rename', condition=canRename)
def handlerRename(self, action):
changed = False
Modified: z3c.contents/trunk/src/z3c/contents/browser.zcml
===================================================================
--- z3c.contents/trunk/src/z3c/contents/browser.zcml 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/browser.zcml 2008-04-11 14:57:35 UTC (rev 85255)
@@ -16,4 +16,10 @@
layer="zope.publisher.interfaces.browser.IBrowserRequest"
/>
+ <z3c:template
+ template="search.pt"
+ for=".browser.ContentsSearchForm"
+ layer="zope.publisher.interfaces.browser.IBrowserRequest"
+ />
+
</configure>
Modified: z3c.contents/trunk/src/z3c/contents/column.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/column.py 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/column.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -70,3 +70,4 @@
itemLink, key, newName, msg)
else:
return itemLink
+
Modified: z3c.contents/trunk/src/z3c/contents/configure.zcml
===================================================================
--- z3c.contents/trunk/src/z3c/contents/configure.zcml 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/configure.zcml 2008-04-11 14:57:35 UTC (rev 85255)
@@ -38,6 +38,47 @@
provides="z3c.table.interfaces.IColumn"
/>
+ <adapter
+ factory="z3c.contents.browser.ContentsSearch"
+ />
+
+ <adapter
+ provides="z3c.contents.interfaces.ISearch"
+ for="zope.app.container.interfaces.IReadContainer"
+ permission="zope.Public"
+ factory="z3c.contents.search.SearchForContainer"
+ />
+
+ <!-- include also sorting column headers for some columns
+ leaving out CheckBoxColumn -->
+ <adapter
+ factory="z3c.contents.header.ContentsColumnHeader"
+ for="zope.app.container.interfaces.IContainer
+ zope.interface.Interface
+ z3c.contents.interfaces.IContentsPage
+ z3c.contents.column.RenameColumn"
+ provides="z3c.table.interfaces.IColumnHeader"
+ />
+
+ <adapter
+ factory="z3c.contents.header.ContentsColumnHeader"
+ for="zope.app.container.interfaces.IContainer
+ zope.interface.Interface
+ z3c.contents.interfaces.IContentsPage
+ z3c.table.column.CreatedColumn"
+ provides="z3c.table.interfaces.IColumnHeader"
+ />
+
+ <adapter
+ factory="z3c.contents.header.ContentsColumnHeader"
+ for="zope.app.container.interfaces.IContainer
+ zope.interface.Interface
+ z3c.contents.interfaces.IContentsPage
+ z3c.table.column.ModifiedColumn"
+ provides="z3c.table.interfaces.IColumnHeader"
+ />
+
+
<include file="browser.zcml" />
</configure>
Modified: z3c.contents/trunk/src/z3c/contents/contents.pt
===================================================================
--- z3c.contents/trunk/src/z3c/contents/contents.pt 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/contents.pt 2008-04-11 14:57:35 UTC (rev 85255)
@@ -1,5 +1,9 @@
<div metal:use-macro="macro:form">
<div metal:fill-slot="main">
+ <fieldset>
+ <legend>Search</legend>
+ <tal:block replace="structure view/search/render">search form</tal:block>
+ </fieldset>
<tal:block replace="structure view/renderTable">table</tal:block>
<tal:block define="batch view/renderBatch">
<div class="batch" tal:condition="batch">
Copied: z3c.contents/trunk/src/z3c/contents/ftesting.zcml (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/ftesting.zcml)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/ftesting.zcml (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/ftesting.zcml 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,98 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="demo">
+
+ <include package="zope.app.component" file="meta.zcml" />
+ <include package="zope.app.component.browser" file="meta.zcml" />
+ <include package="zope.app.container.browser" file="meta.zcml" />
+ <include package="zope.app.form.browser" file="meta.zcml" />
+ <include package="zope.app.pagetemplate" file="meta.zcml" />
+ <include package="zope.app.publication" file="meta.zcml" />
+ <include package="zope.app.publisher" file="meta.zcml" />
+ <include package="zope.app.security" file="meta.zcml" />
+ <include package="zope.app.securitypolicy" file="meta.zcml" />
+ <include package="zope.viewlet" file="meta.zcml" />
+ <include package="z3c.form" file="meta.zcml" />
+ <include package="z3c.macro" file="meta.zcml" />
+ <include package="z3c.pagelet" file="meta.zcml" />
+ <include package="z3c.template" file="meta.zcml" />
+
+ <browser:menu id="zmi_views" title="Views" />
+ <browser:menu id="zmi_actions" title="Actions" />
+
+ <include package="zope.app.appsetup" />
+ <include package="zope.app.component" />
+ <include package="zope.app.container" />
+ <include package="zope.app.error" />
+ <include package="zope.app.folder" />
+ <include package="zope.app.i18n" />
+ <include package="zope.app.publication" />
+ <include package="zope.app.security" />
+ <include package="zope.app.securitypolicy" />
+ <include package="zope.app.session" />
+ <include package="zope.app.wsgi" />
+ <include package="zope.annotation" />
+ <include package="zope.component" />
+ <include package="zope.contentprovider" />
+ <include package="zope.location" />
+ <include package="zope.publisher" />
+ <include package="zope.traversing" />
+ <include package="zope.traversing.browser" />
+ <include package="zope.viewlet" />
+
+ <include package="z3c.form" />
+ <include package="z3c.formui" />
+ <include package="z3c.macro" />
+ <include package="z3c.pagelet" />
+ <include package="z3c.table" />
+ <include package="z3c.contents" />
+
+ <securityPolicy
+ component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
+
+ <role id="zope.Anonymous" title="Everybody" />
+ <grantAll role="zope.Anonymous" />
+
+ <browser:defaultView
+ for="*"
+ name="index"
+ />
+
+ <interface
+ interface="z3c.contents.testing.IContentsTestBrowserSkin"
+ type="zope.publisher.interfaces.browser.IBrowserSkinType"
+ name="ContentsTesting"
+ />
+
+ <z3c:layout
+ for="*"
+ layer="z3c.contents.testing.IContentsTestBrowserLayer"
+ template="testing.pt"
+ />
+
+ <!-- test contents page for the root folder -->
+ <z3c:pagelet
+ name="index"
+ for="zope.app.folder.interfaces.IRootFolder"
+ class="z3c.contents.browser.ContentsPage"
+ layer="z3c.contents.testing.IContentsTestBrowserLayer"
+ permission="zope.ManageContent"
+ />
+
+ <class class="z3c.contents.testing.Content">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <allow
+ interface="z3c.contents.testing.IContent"
+ />
+ <require
+ permission="zope.ManageContent"
+ set_schema="z3c.contents.testing.IContent"
+ />
+ </class>
+
+
+</configure>
Copied: z3c.contents/trunk/src/z3c/contents/ftests.py (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/ftests.py)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/ftests.py (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/ftests.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,41 @@
+import os
+import doctest
+import lxml
+import transaction
+
+from zope.app.testing import functional
+import zope.component
+
+ftesting_zcml = os.path.join(os.path.dirname(__file__),
+ 'ftesting.zcml')
+TestLayer = functional.ZCMLLayer(
+ ftesting_zcml, __name__, 'TestLayer')
+
+def printElement(browser, xpath, multiple=False, serialize=True):
+ """Print method to use with z3c.etestbrowser"""
+ result = [serialize and lxml.etree.tounicode(elem) or elem
+ for elem in browser.etree.xpath(xpath)]
+ if not multiple:
+ print result[0]
+ return
+ for elem in result:
+ print elem
+
+def setUp(test):
+ functional.FunctionalTestSetup().setUp()
+ test.globs['getRootFolder'] = functional.getRootFolder
+ test.globs['printElement'] = printElement
+
+def tearDown(test):
+ functional.FunctionalTestSetup().tearDown()
+
+def test_suite():
+ suite = functional.FunctionalDocFileSuite('BROWSER.txt',
+ setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ )
+ suite.layer = TestLayer
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Copied: z3c.contents/trunk/src/z3c/contents/header.py (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/header.py)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/header.py (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/header.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+
+from z3c.table.header import SortingColumnHeader
+
+class ContentsColumnHeader(SortingColumnHeader):
+ """Sorting column header."""
+
+ _request_args = ['search.widgets.searchterm']
+
Modified: z3c.contents/trunk/src/z3c/contents/interfaces.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/interfaces.py 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/interfaces.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -16,11 +16,46 @@
"""
__docformat__ = "reStructuredText"
+import zope.schema
import zope.i18nmessageid
+
from z3c.table import interfaces
_ = zope.i18nmessageid.MessageFactory('z3c')
class IContentsPage(interfaces.ITable):
- """Container management page"""
+ """Container management page
+ """
+
+class IContentsSearch(zope.interface.Interface):
+ """We would like to provide a search field for searching within the
+ container.
+
+ Possible addition here could be a choice field to search within specific
+ columns.
+ """
+
+ searchterm = zope.schema.TextLine(title=_(u'Search'))
+
+
+class ISearch(zope.interface.Interface):
+ """
+ Search support for containers.
+
+ This is different to z.a.c.interfaces.IFind in that the search method
+ will match **any** filter whereas the find method of IFind will match
+ if **all** filters match.
+ """
+
+ def search(id_filters=None, object_filters=None):
+ """Find object that matches **any** filters in all sub-objects.
+
+ This container itself is not included.
+ """
+
+class IOrderableColumn(zope.interface.Interface):
+ """
+ A column that may be ordered
+
+ """
Copied: z3c.contents/trunk/src/z3c/contents/search.pt (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/search.pt)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/search.pt (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/search.pt 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,15 @@
+<table>
+<tr>
+<td class="row" tal:repeat="widget view/widgets/values">
+ <b tal:condition="widget/error"
+ tal:content="structure widget/error/render"
+ /><label for=""
+ tal:attributes="for widget/id"
+ tal:content="widget/label" />
+ <input type="text" tal:replace="structure widget/render"
+/></td>
+<td class="action" tal:repeat="action view/actions/values">
+ <input type="submit" tal:replace="structure action/render"
+/></td>
+</tr>
+</table>
Copied: z3c.contents/trunk/src/z3c/contents/search.py (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/search.py)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/search.py (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/search.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,94 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+from zope.app.container.interfaces import (IObjectFindFilter,
+ IReadContainer)
+from zope.security.proxy import removeSecurityProxy
+from zope.index.text.interfaces import ISearchableText
+
+from z3c.contents.interfaces import ISearch
+
+class SearchForContainer(object):
+
+ zope.interface.implements(ISearch)
+ zope.component.adapts(IReadContainer)
+
+ def __init__(self, context):
+ self._context = context
+
+ def search(self, id_filters=None, object_filters=None):
+ 'See ISearch'
+ id_filters = id_filters or []
+ object_filters = object_filters or []
+ result = []
+ container = self._context
+ for id, object in container.items():
+ _search_helper(id, object, container,
+ id_filters, object_filters,
+ result)
+ return result
+
+
+def _search_helper(id, object, container, id_filters, object_filters, result):
+ # check id filters if we get a match then return immediately
+ for id_filter in id_filters:
+ if id_filter.matches(id):
+ result.append(object)
+ return
+
+ # now check all object filters
+ for object_filter in object_filters:
+ if object_filter.matches(object):
+ result.append(object)
+ return
+
+ # do we need to check sub containers?
+ if not IReadContainer.providedBy(object):
+ return
+
+ container = object
+ for id, object in container.items():
+ _search_helper(id, object, container, id_filters, object_filters, result)
+
+
+class SearchableTextFindFilter(object):
+ """Filter objects on the ISearchableText adapters to the object
+
+ """
+
+ zope.interface.implements(IObjectFindFilter)
+
+ def __init__(self, terms):
+ self.terms = terms
+
+ def matches(self, object):
+ """Check if one of the search terms is found in the searchable text
+ interface
+ """
+
+ adapter = zope.component.queryAdapter(object, ISearchableText)
+ if not adapter:
+ return False
+ searchable = adapter.getSearchableText().lower()
+ for term in [t.lower() for t in self.terms]:
+ if term in searchable:
+ return True
+ return False
+
Copied: z3c.contents/trunk/src/z3c/contents/testing.pt (from rev 85254, z3c.contents/branches/darrylcousins/src/z3c/contents/testing.pt)
===================================================================
--- z3c.contents/trunk/src/z3c/contents/testing.pt (rev 0)
+++ z3c.contents/trunk/src/z3c/contents/testing.pt 2008-04-11 14:57:35 UTC (rev 85255)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W4C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <title>Contents</title>
+</head>
+<body>
+ <tal:block replace="structure provider:pagelet">
+ content
+ </tal:block>
+</body>
+</html>
+
Modified: z3c.contents/trunk/src/z3c/contents/testing.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/testing.py 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/testing.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -16,9 +16,31 @@
"""
__docformat__ = "reStructuredText"
+import zope.interface
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.index.text.interfaces import ISearchableText
+import z3c.layer.ready2go
+
+class IContentsTestBrowserLayer(z3c.layer.ready2go.IReady2GoBrowserLayer):
+ """test layer."""
+
+
+class IContentsTestBrowserSkin(IContentsTestBrowserLayer):
+ """The browser skin."""
+
+
+class IContent(zope.interface.Interface):
+
+ title = zope.schema.TextLine(title=u'Title')
+ number = zope.schema.Int(title=u'Number')
+
+
class Content(object):
"""Sample content which is pickable for copy test."""
+
+ zope.interface.implements(IContent)
+
def __init__(self, title, number):
self.title = title
self.number = number
@@ -26,3 +48,15 @@
def __repr__(self):
return u'<%s %s %s>' % (self.__class__.__name__, self.title,
self.number)
+
+
+class SearchableTextForContent:
+
+ zope.interface.implements(ISearchableText)
+ zope.component.adapts(IContent)
+
+ def __init__(self, content):
+ self.content = content
+
+ def getSearchableText(self):
+ return '%s %d' % (self.content.title, self.content.number)
Modified: z3c.contents/trunk/src/z3c/contents/tests.py
===================================================================
--- z3c.contents/trunk/src/z3c/contents/tests.py 2008-04-11 14:23:24 UTC (rev 85254)
+++ z3c.contents/trunk/src/z3c/contents/tests.py 2008-04-11 14:57:35 UTC (rev 85255)
@@ -35,6 +35,7 @@
from z3c.macro import tales
import z3c.table.testing
+from z3c.contents import browser
class PrincipalAnnotations(dict):
@@ -73,7 +74,10 @@
# dublin core stub adapter
zope.component.provideAdapter(z3c.table.testing.DublinCoreAdapterStub)
+ # contents search adapter
+ zope.component.provideAdapter(browser.ContentsSearch)
+
def tearDown(test):
setup.placefulTearDown()
More information about the Checkins
mailing list