Build a custom search form with YAFOWIL
Create a basic egg
Let's create your basic egg with paster.
paster create -t basic_namespace
Answer the questions and name it example.searchform, this will get you a new directory with the necessary boilerplate and metadata.
Next add a configure.zcml file, we'll need that soon.
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
</configure>
Move your newly created egg to the source folder of your buildout and add register the egg within the develop-part of your buildout
develop =
src/example.searchform
Also add it to the egg and zcml part of the instance in your buildout.
egg =
example.searchform
zcml =
example.searchform
Dependencies
Add yafowil and since we're building a form for Plone also add yafowil.zope2 to the install_requires of your setup.py file.
install_requires=[
'setuptools',
# -*- Extra requirements: -*-
'yafowil',
'yafowil.zope2',
],
Rerun your buildout and the dependencies will be fetched for you.
Building the Form
Add a searchform.py to you egg and start with the imports for your form.
We import yafowil.zope that will make the bindings for Zope/Plone and yafowil.loader for all the basic YAFOWIL blueprints into the factory. The factory will later build the widgets out of the blueprints. The Controller will be responsible for form processing, delegation of actions and form rendering. Also create a MessageFactory object for the internationalization of your form.
import yafowil.zope2 import yafowil.loader from yafowil.base import factory, UNSET from yafowil.controller import Controller from zope.i18nmessageid import MessageFactory from Products.Five.browser import BrowserView from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.CMFCore.utils import getToolByName
_ = MessageFactory('ExampleSearchForm')
Next we create a BrowserView class for your form and create a page template searchform.pt where we will show your form.
class SearchView(BrowserView):
template = ViewPageTemplateFile('searchform.pt')
Within the searchform.pt we place the result of your SearchView
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
metal:use-macro="context/main_template/macros/master"
i18n:domain="ExampleSearchForm">
<body>
<metal:main fill-slot="main">
<tal:main-macro metal:define-macro="main">
<div tal:content="structure python:view.form()">form</div>
</tal:main-macro>
</metal:main>
</body>
</html>
Register your form within your configure.zcml as a browser page.
<browser:page
for="*"
name="examplesearch"
permission="zope.Public"
class=".searchform.SearchView"
/>
Return to your SearchView and we'll continue to build your form. We create a _form_action that will point back to your form to display the search results later. The _form_handler ist responsible for saving the contents of our form as criterias of our search.
class SearchView(BrowserView):
template = ViewPageTemplateFile('searchform.pt')
def _form_action(self, widget, data):
return '%s/@@examplesearch' % self.context.absolute_url()
def _form_handler(self, widget, data):
self.searchterm = data['searchterm'].extracted
Next we'll create the form elements within our SearchView. We define the form, give it a name and tell which action will be executed on submit. For the search term we create a string input field, give it a unicode string for the label which can later be translated to your desired languages. Additionally you can add classes to almost every element of your widget. More widget properties can be find in the YAFOWIL widget documentation. To complete our form we add a controller which returns the rendered form.
...
def form(self):
form = factory('form',
name='search',
props={
'action': self._form_action,
})
form['searchterm'] = factory(
'field:label:error:text',
props={
'label': _(u'Search term:'),
'field.class': 'myFieldClass',
'text.class': 'myInputClass',
'size': '20',
})
form['submit'] = factory(
'field:submit',
props={
'label': _(u'Search'),
'submit.class': '',
'handler': self._form_handler,
'action': 'search'
})
controller = Controller(form, self.request)
return controller.rendered
Restart your Plone instance and point your browser to your form http://localhost:8080/@@examplesearch
The Search Query
For the query we check if our form handler has the attribute and that the attribute is not empty. Now we take our searchterm, create the catalog search and return the results.
def results(self):
if not hasattr(self,'searchterm') or not self.searchterm:
return []
cat = getToolByName(self.context, 'portal_catalog')
query = {}
qterm = self.searchterm
if qterm:
qterm = '%s' % (qterm)
query['SearchableText'] = qterm.decode('utf-8')
print query
return cat(**query)
Complete your page template with some code to show the results and you are good to go.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
metal:use-macro="context/main_template/macros/master"
i18n:domain="ExampleSearchForm">
<body>
<metal:main fill-slot="main">
<tal:main-macro metal:define-macro="main">
<div tal:content="structure python:view.form()">form</div>
<ul>
<tal:block repeat="item python:view.results()">
<li>
<a tal:content="item/Title"
tal:attributes="href item/getURL|item/absolute_url"></a>
</li>
</tal:block>
</ul>
</tal:main-macro>
</metal:main>
</body>
</html>
More information and documentation on http://yafowil.info/

Author: