Wednesday, December 13, 2006

10 steps to adding search providers to a sitecore website using OpenSearch specifications

yep, 10 steps to adding search providers to a sitecore website using OpenSearch specifications. ok, so there's a lot of sub-steps and some code, but at least the highlighted parts are only 10 :) here we go!

1) create a new layout called OpenSearch

2) create a new template called OpenSearchSource     2.1) add the following fields to this template         2.1.1) OpenSearchDescription : text         2.1.2) ShortName : text         2.1.3) LongName : text         2.1.4) Description : text         2.1.5) Tags : text         2.1.6) Contact : text         2.1.7) URL_RSS : text         2.1.8) URL_Atom : text         2.1.9) URL_HTTP : text         2.1.10) Attribution : text         2.1.11) Language: text         2.1.12) AdultContent : checkbox         2.1.13) inputEncoding : text         2.1.14) outputEncoding : text         2.1.15) count : text         2.1.16) Icon_Large : text         2.1.17) Icon_Small : text     2.2) set the standard values of the fields accordingly (without the qoutes):         2.2.1) OpenSearchDescription : "http://a9.com/-/spec/opensearch/1.1/"         2.2.2) ShortName : "MyExample" (set your site name or something)         2.2.3) LongName : "Search using MyExample Search" (just a more detailed name)         2.2.4) Description : "Use our own search engine to search our site"         2.2.5) Tags : "example web"         2.2.6) Contact : "peter@interfolio.se" (your email would be better)         2.2.7) URL_RSS : "http://www.interfolio.se/search.aspx?q={searchTerms}&view=rss" (set this to the result querystring of your rss search page (if any) and add the {searchTerms} to the query param, or leave empty)         2.2.8) URL_Atom : "http://www.interfolio.se/search.aspx?q={searchTerms}&view=atom" (set this to the result querystring of your atom search page (if any) and add the {searchTerms} to the query param, or leave empty)         2.2.9) URL_HTTP : "http://www.interfolio.se/search.aspx?q={searchTerms"} (set this to the result querystring of your search page and add the {searchTerms} to the query param)         2.2.10) Attribution : "Copyright 2006, Peter Johansson, All Rights Reserved."         2.2.11) Language: "*" (can be set to a desired language, like sv-SE)         2.2.12) AdultContent : unchecked :)         2.2.13) inputEncoding : "UTF-8"         2.2.14) outputEncoding : "UTF-8"         2.2.15) count : 10         2.2.16) Icon_Large : "http://www.interfolio.se/images/find.png" (or leave blank to show no icon)         2.2.17) Icon_Small : "http://www.interfolio.se/images/find.png" (or leave blank to show no icon)     2.3) create a master for the newly created template

3) add a folder where you'll store your OpenSearch specification items (perhaps /sitecore/content/global/OpenSearch)     3.1) assing the master created in 2.4 to the newly created folder     3.2) assing the layout we created at step 1 to the standard values of our OpenSearchSource template

4) add a dummy specification item that we can change later, i'm naming mine OpenSearchExample, but you can name it what you want.

5) here you can approach it from many angles, but since this is my tutorial we're gonna look at it my way :),     5.1) create a template that your site-specific templates will inherit from (make sure it inherits Standard template), maybe even name it SitewideSettings like i'm doing.     5.2) add a Tree list field to that template, name it OpenSearch_Sources and specify it's source to be your newly created folder at step 3, and finally check the shared checkbox.     5.3) make your site-specific templates inherit from the SitewideSettings template created at step 4.1.

6) edit an item (or create a new one) and select the dummy item we created at step 4 in the OpenSearch_Sources tree list

7) commit a full publish

That's it for sitecore desktop stuff.. for now.. next up, let's change the OpenSearch layout file.

8) Create a new class called OpenSearchPage     8.1) Set the Inherits attribute of the layout to the newly created class, like Inherits="OpenSearch.OpenSearchPage"     8.2) make the new class inherit from the standard Page class     8.3) override the OnLoad event     8.4) create code to output our OpenSearch specification. Instead of doing this the really fancy way i'll just show a really simple way to output it. What should really be done is to make use of an xmltextwriter and an actual xml document to make sure the output is nice all follows standards, but to save time i've simply sent the format straight to the page and made sure the ContentType is text/xml. to make this better, go ahead and change all this, it should be made better so if you use any of this think about that :) anyway, here's really all of the code needed for (the not-so-correct-way-of) returning the output:

public class OpenSearchPage : Page { protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (Sitecore.Context.Item != null) { this.Context.Response.Clear(); this.Context.Response.ContentType = "text/xml"; System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.AppendLine("<OpenSearchDescription xmlns=\"" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["OpenSearchDescription"].Value, "") + "\">"); sb.AppendLine("<ShortName>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Shortname"].Value, "") + "</ShortName>"); sb.AppendLine("<Description>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Description"].Value, "") + "</Description>"); sb.AppendLine("<Tags>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Tags"].Value, "") + "</Tags>"); sb.AppendLine("<Contact>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Contact"].Value, "") + "</Contact>"); if (Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["URL_RSS"].Value, "") != "") sb.AppendLine("<Url type=\"application/rss+xml\" template=\"" + Server.HtmlEncode(Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["URL_RSS"].Value)) + "\"/>"); if (Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["URL_Atom"].Value, "") != "") sb.AppendLine("<Url type=\"application/atom+xml\" template=\"" + Server.HtmlEncode(Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["URL_Atom"].Value)) + "\"/>"); if (Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["URL_HTTP"].Value, "") != "") sb.AppendLine("<Url type=\"text/html\" method=\"get\" template=\"" + Server.HtmlEncode(Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["URL_HTTP"].Value)) + "\"/>"); if (Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["LongName"].Value, "") != "") sb.AppendLine("<LongName>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["LongName"].Value) + "</LongName>"); if (Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Icon_Large"].Value, "") != "") sb.AppendLine("<Image height=\"64\" width=\"64\" type=\"image/png\">" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Icon_Large"].Value) + "</Image>"); if (Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Icon_Small"].Value, "") != "") sb.AppendLine("<Image height=\"16\" width=\"16\" type=\"image/png\">" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Icon_Small"].Value) + "</Image>"); sb.AppendLine("<Attribution>"); sb.AppendLine(Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["Attribution"].Value, "Copyright, All rights reserved.")); sb.AppendLine("</Attribution>"); sb.AppendLine("<AdultContent>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["AdultContent"].Value, "false") + "</AdultContent>"); sb.AppendLine("<Language>" + Server.HtmlEncode(Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["language"].Value, "*")) + "</Language>"); sb.AppendLine("<OutputEncoding>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["outputEncoding"].Value, "UTF-8") + "</OutputEncoding>"); sb.AppendLine("<InputEncoding>" + Sitecore.StringUtil.GetString(Sitecore.Context.Item.Fields["inputEncoding"].Value, "UTF-8") + "</InputEncoding>"); sb.AppendLine("</OpenSearchDescription>"); this.Context.Response.Write(sb.ToString()); this.Context.Response.End(); } } }

9) add functionality to add content to the header. all we really need now is a way to tell the browser that there's a search provider available on the site, and here's a little code snippet for that as well

public class OpenSearchRenderer { public static void GetOpenSearchUrls() { Item root = Sitecore.Context.Item; System.Text.StringBuilder sb = new System.Text.StringBuilder(); if (root != null) { if (root.Fields["OpenSearch_Sources"] != null) { Sitecore.Data.Fields.MultilistField mf = root.Fields["OpenSearch_Sources"]; if (mf != null) { if (mf.Count > 0) { foreach (Item itm in mf.GetItems()) { sb.AppendLine("<link title=\"" + itm.Fields["ShortName"].Value.ToString() + "\" rel=\"search\" type=\"application/opensearchdescription+xml\" href=\"http://localhost" + itm.Paths.GetFriendlyUrl() + "\">"); } } } Sitecore.Context.Page.Page.Header.Controls.Add(new LiteralControl(sb.ToString())); } } } }

10) output the header information. to finalize the entire thing and actually get the output to the page, all you have to do is add a call to the GetOpenSearchUrls method on the pages, so for example just add a call to it in the Page_Load event;

protected void Page_Load(object sender, EventArgs e) { OpenSearch.OpenSearchRenderer.GetOpenSearchUrls(); }

now, to wrap it all up you should have a working example of how to add OpenSearch information that will enable your site to 'tell' the browser there's one or more search providers available on your site, it should light up the down arrow to the right of the magnifying glass in the top-right search area part of your IE7 toolbar and if you expand it it should show your newly added search provider. you could also extend the definition of the xml output to include mozilla specific resources (like xmlns:moz="http://www.mozilla.org/2006/browser/search/") and even further extend the functionality in a lot of customized ways since OpenSearch has many ways to extend the specifications and results.

further reading: OpenSearch: http://www.opensearch.org/ OpenSearch Specifications 1.1: http://www.opensearch.org/Specifications/OpenSearch/1.1 Search Provider Extensibility in Internet Explorer 7: http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/external/overview/ie7_opensearch_ext.asp Adding Search providers to IE 7 using OpenSearch 1.1: http://blogs.msdn.com/ie/archive/2005/09/14/466186.aspx

well, i guess that's that. Sorry for the bad formatting, one of these days i'll actually change the design, but right now this will have to do. take care, P.

No comments: