Monday, October 08, 2007

Adding the OpenSearch Descriptors to Confluence or Other WebSites

Earlier today I blogged about the browser search plugins for SunWikis and Confluence.

This is how it all works:

1. Create the descriptor file

Create an OpenSearch descriptor (OSD) file:
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
   <ShortName>SunWikis</ShortName>
   <Description>SunWikis (wikis.sun.com) Search</Description>
   <InputEncoding>UTF-8</InputEncoding>
   <OutputEncoding>UTF-8</OutputEncoding>
   <Image width="16" height="16" type="image/x-icon">http://wikis.sun.com/favicon.ico</Image>
   <Url type="text/html" method="GET"
template="http://wikis.sun.com/dosearchsite.action?searchQuery.queryString={searchTerms}&searchQuery.spaceKey=conf_global&searchQuery.lastModified="/>
</OpenSearchDescription>
Download

Check out the template attribute - the {searchTerms} will get replaced with the keyword typed into browser's search box. Keep in mind that if your template url contains an "&" character, it needs to be escaped as &amp;, otherwise your XML will not be well formated.

2. Host the descriptor file

Once you have the file ready, host it on your server e.g. as wikis.sun.com/search/wikis-osd.xml

3. Expose the descriptor file

Now you need to let browsers know about the descriptor. You can do this by placing a snippet like this somewhere between the head HTML tags.
<link rel="search" type="application/opensearchdescription+xml"
   title="SunWikis" href="$req.contextPath/search/osd.xml">
In the case of using Confluence, you can do this by editing your template.

The $req.contextPath part is Confluence specific. Replace it with a piece of code that retrieves the context path or replace it with static context path e.g. /myapplication.

4. Bonus: Click-to-install link

Firefox and IE support the installation of search engines via JavaScript, which can be handy if you want to provide an "install" link.

As usual, both browsers use a different approach, and to be fair IE is ahead of Firefox in this one. :-/

Here is a JavaScript I put together based on some resources
function addEngine(srcUrl, osdUrl, faviconUrl, fullName, category)
{
 if ((typeof window.sidebar == "object") && (typeof window.sidebar.addSearchEngine == "function")) /* Firefox */
 {
     window.sidebar.addSearchEngine(
         srcUrl,                                                /* engine URL */
         faviconUrl,                                            /* icon URL */
         fullName,                                              /* engine name */
         category);                                             /* category name */
 } else { /* IE */
    if (window.external.AddSearchProvider) {
       window.external.AddSearchProvider(osdUrl);
    }
 }
}
Download

For some weird reason (legacy?), Firefox uses Apple's Sherlock format for JavaScript installation instead of OpenSearch descriptors. For this reason you need to create a Shrelock descriptor based on your OpenSearch descriptor. Keep in mind that the Shrelock descriptor filename must end with a .src extension.

In my case, my sherlock.src looks like this:
<search
version="1.0"
name="SunWikis"
description="SunWikis (wikis.sun.com) Search"
action="http://wikis.sun.com/dosearchsite.action"
searchForm="http://wikis.sun.com/dosearchsite.action"
queryCharset="UTF-8"
method="GET">

<input name="searchQuery.queryString" user>
<input name="searchQuery.spaceKey" value="conf_global">
<input name="searchQuery.lastModified" value="&">

</search>


It's not perfect because it is deliberately missing the browser element, but it will do. :)

The user attribute is an equivalent of the {searchTerms} variable in OpenSearch specification.

Once the file is created, upload it to your site, e.g. wikis.sun.com/search/sherlock.src.

And now you can create the link on a page that contains the JavaScript code mentioned above:
<a onclick="addEngine('/search/sherlock.src', 
    '/search/osd.xml', '/favicon.ico', 'SunWikis Search', 'Wiki')" 
    href="#">Install
</a>


For more information on this topic check out this blog, but watch out for an XSS hole - the searchTerms variable in the JavaScript snippet in the section "Step 3: The Search" must be url-encoded before it is used later in the script.

Some extra Confluece-specific stuff

You can create OSD files that search in all the spaces of your Confluence instance or in one space only. You can specify this via the Url attribute in the OSD file.
<Url type="text/html" method="GET"
template="http://wikis.sun.com/dosearchsite.action?searchQuery.queryString={searchTerms}&searchQuery.spaceKey=conf_global&searchQuery.lastModified="/>


The searchQuery.spaceKey=conf_global part means - search in all Confluence spaces (with the exception of personal spaces IIRC).

You can replace conf_global with a space key and you get a descriptor for space specific search.

For example, the url for a descriptor for our About space would look like this:
<Url type="text/html" method="GET"
template="http://wikis.sun.com/dosearchsite.action?searchQuery.queryString={searchTerms}&searchQuery.spaceKey=About&searchQuery.lastModified="/>


If you want to get really fancy you can use the power of the Lucene search engine (used in Confluence) to craft all kinds of interesting descriptors. For example this one that searches all the Confluence related spaces (the DOC, DISC, and CONFEXT) at confluence.atlassian.com by specifying the spaces in the earchQuery.queryString parameter instead of the searchQuery.spaceKey, which accepts only one space key.
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
   <ShortName>Confluence Extras</ShortName>
   <Description>Documentation for the latest version of Confluence and Confluence Community + Confluence Extension Docs</Description>
   <InputEncoding>UTF-8</InputEncoding>
   <OutputEncoding>UTF-8</OutputEncoding>
   <Image width="16" height="16" type="image/x-icon">http://confluence.atlassian.com/favicon.ico</Image>
   <Url type="text/html" method="GET"
           template="http://confluence.atlassian.com/dosearchsite.action?searchQuery.queryString=spacekey%3A%28DOC+OR+DISC+OR+CONFEXT%29+AND+%28{searchTerms}%29&searchQuery.lastModified="/>
</OpenSearchDescription>
Download

This OSD is based on a wicked Lucene search query that looks like this:
spacekey:(DOC OR DISC OR CONFEXT) AND({searchTerms})
You can find out more about Lucene goodies here.

The last word of advice - instead of creating n+1 descriptors, one for each space + one for the search in all the spaces, use a JSP page to dynamically render the OSD file - that's how I implemented it for SunWikis.

1 comment:

Anonymous said...

Nice one!