Limiting Search to Sections of Site
Suppose you have a plone site for your intranet with numerous folders under the main site-root that represent different departments, projects, etc. You want to allow users to search either the entire site, or a particular subsection (i.e., someone looking for the latest expense report form probably doesn't want a search a search returning documents about the latest board meeting, as well as the desired form).
Here's how I customized the portal_search template to give users to option to only search the current folder and its children:
-I went to the ZMI and customized plone_forms/search_form
-I edited my customized copy to include (right after the field for the text to be searched):
<div class="field">
<input type="text"
name="SearchableText"
size="25"
tabindex=""
onfocus="formtooltip('text_search_help',1)"
onblur="formtooltip('text_search_help',0)"
tal:attributes="tabindex tabindex/next;"
/>
<input type="checkbox"
name="path"
value="#"
class="noborder"
checked="checked"
tabindex=""
onfocus="formtooltip('limit_search_help',1)"
onblur="formtooltip('limit_search_help',0)"
tal:attributes="tabindex tabindex/next;value
string:/gri${request/URLPATH1};"
/> Search only the current folder (and sub-folders)
</div>
Note that in the line ...string:/gri${request/URLPATH1};... the "gri" refers to the plone instance (which is hidden behind apache, so www.mysite.org redirects to www.mysite.org/gri). I couldn't figure out how to avoid hardcoding the plone instance name, though I'm sure it can be done.
This code gives the user a checkbox that, if checked, limits the search to the current folder and any sub-folders. If it's unchecked, then the search scope is the entire portal.
With a little more work, you could probably adapt this technique to provide, for example, a list of sections to be searched and allow the user to select one or more sub-sections to be included in the search scope.
As done on plone.org
Additional notes by pupq (Joel Burton) on how this is implemented on plone.org
We do something very similiar on plone.org; allowing searching in two sections (documentation and development), and in current folder. We try to be smart--when you're in the top of documentation section, you should get that as the default, and when you're in the top of the development section, you should get that.
The full global_searchbox for plone.org (as of Feb 2004):
<div id="portal-searchbox" metal:define-macro="quick_search">
<form name="searchform"
style="position: relative; top: -0.6em;"
action="search"
tal:attributes="action string:${portal_url}/search" >
<label for="searchGadget" class="hiddenStructure" i18n:translate="text_search">Search</label>
<input id="searchGadget"
name="SearchableText"
type="text"
size="30"
value=""
alt="Search"
style="font-family: Verdana; font-size: 110%; margin-bottom: 3px; margin-right: 0; padding: 3px;"
title="Search"
accesskey="accesskeys-search"
i18n:attributes="alt accesskey title"
tal:attributes="value request/SearchableText|nothing;
tabindex tabindex/next" class="visibility:visible;" /><br />
<select id="searchContext" name="path" style="font-family: Verdana; font-size: 100%;"
tal:define="path request/form/path|nothing;">
<option value="/plone.org"
tal:attributes="selected python:test(path=='/plone.org', 'selected', nothing)">All of plone.org</option>
<option value="/plone.org/documentation"
tal:attributes="selected python:test(path=='/plone.org/documentation', 'selected', nothing)">In Documentation</option>
<option value="/plone.org/development"
tal:attributes="selected python:test(path=='/plone.org/development', 'selected', nothing)">In Development</option>
<option tal:condition="python: here.aq_parent != portal_object"
tal:attributes="value python:'/'.join(here.aq_parent.getPhysicalPath())">In this folder</option>
</select>
<input class="searchButton"
type="submit"
value="Search"
accesskey="accesskeys-search"
tal:attributes="tabindex tabindex/next"
i18n:attributes="value accesskey" />
</form>
</div>
Additional ideas from Bill Page
I really liked this idea but I had a lot of trouble getting the right search path. In the end I changed the action for the Search tab in portal_action as described above. Then I customized plong_form/search_form by inserting the following code for the path:
<div class="field">
<input type="text"
name="SearchableText"
size="25" style="width:100%"
tabindex=""
onfocus="formtooltip('text_search_help',1)"
onblur="formtooltip('text_search_help',0)"
tal:attributes="tabindex tabindex/next;"
/> <br />
<input type="checkbox"
name="path"
value="#"
class="noborder"
checked="checked"
tabindex=""
onfocus="formtooltip('limit_search_help',1)"
onblur="formtooltip('limit_search_help',0)"
tal:attributes="tabindex tabindex/next;
value python:'/'.join(here.getPhysicalPath())"
/> Search only the current folder (and sub-folders)
</div>
Notice that I want the path to the current folder not the path to the parent so I use just here.getPhysicalPath
The other thing I did was to tidy up the checkboxes for the item types:
<div class="row">
<div class="label">
<span i18n:translate="label_item_type">Item type</span>
<div id="item_type_search_help"
i18n:translate="help_search_item_type"
class="help"
style="visibility:hidden">
Return items of a specific type.
</div>
</div>
<input
class="noborder" type="checkbox"
src="select_all_icon.gif"
name="selectButton" title="Select all items"
alt="Select all items"
onclick="toggleSelect(this); return false;"
tal:attributes="src string:$portal_url/select_all_icon.gif" />
All / None<br />
<div class="field">
<span tal:repeat="type python:container.portal_types.listContentTypes()">
<div nowrap="true" style="float: left; width:34ex">
<metal:block tal:define="tabindex tabindex/next">
<input type="checkbox"
name="portal_type:list"
value="#"
class="noborder"
checked="checked"
tabindex=""
onfocus="formtooltip('item_type_search_help',1)"
onblur="formtooltip('item_type_search_help',0)"
tal:attributes="value type;
tabindex tabindex;
id string:portal_type_${tabindex}"/>
<label for=""
i18n:translate=""
tal:attributes="for string:portal_type_${tabindex}"
tal:content="python:here.portal_types.getTypeInfo(type).Title()"
/>
</metal:block>
</div></span>
</div>
The first checkbox uses a bit of javascript cribbed from elsewhere in Plone:
<disabled script>
// Functions for selecting all checkboxes
isSelected = true;
function selectAll() {
checkboxes = document.getElementsByName('portal_type:list');
for (i = 0; i < checkboxes.length; i++)
checkboxes[i].checked = true ;
isSelected = true;
return isSelected;
}
function deselectAll() {
checkboxes = document.getElementsByName('portal_type:list');
for (i = 0; i < checkboxes.length; i++)
checkboxes[i].checked = false ;
isSelected = false;
return isSelected;
}
function toggleSelect(selectbutton) {
if (isSelected == false) {
selectbutton.setAttribute('src','<dtml-var portal_url>/select_none_icon.gif');
return selectAll();
}
else {
selectbutton.setAttribute('src','<dtml-var portal_url>/select_all_icon.gif');
return deselectAll();
}
}
<disabled /script>
and the following bit of inline CSS:
<div nowrap="true" style="float: left; width:34ex">
permits the permits the browser to arrange the type checkboxes in a multi-column format to save space.
I'm happily using Plone 1.0.5 on Zope 2.6.2 on Solaris.
Limiting search to a member's folder
I needed to be able to have each member of my site search only within their own member folder. So I modified global_searchbox.pt and search_form.pt and added the following code:
<form name="searchform"
action="search"
tal:attributes="action string:${portal_url}/search"
tal:define="member_folder python:context.get_aq_member_folder" >
<!-- specify the path as the member folder, if any -->
<input type="hidden"
name="path"
tal:attributes="value member_folder"
tal:condition="member_folder" />
Then you need to put a Python script named get_aq_member_folder in your skin folder::
## Script (Python) "get_aq_member_folder_id" ##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title= ## # returns the member folder path in the aq chain, if any. # if no member, it returns None
members = context.portal_membership.getMembersFolder() path_is_long_enough = len(context.getPhysicalPath()) len(members.getPhysicalPath())
# no need to check for member, if we can see on the path length that
# we are not below the Members folder
if path_is_long_enough:
parents = context.REQUEST.PARENTS
for i in range(len(parents)):
if parents[i] == members:
member_folder = parents[i-1].getPhysicalPath()
return /.join(member_folder)
return None
Thanks to Max M for get_aq_member_folder_id which was the starting point for this script. The only difference is that the original script returns the ID of the member folder instead of the full path of the member folder: return parents[i-1].getId()

