Part 5: Creating the search page – Create an EPiServer site from scratch
Posted on December 16, 2009 by Frederik Vig in EPiServerToday we’re implementing our sites search page. This will be a very standard search page with nothing fancy. Instead I’ve included some links that’ll help you extend this search page even more.
Quick search
We already have the markup for the quick search box placed in quicksearch.ascx, we’ll start by converting it to use ASP.NET controls instead.
Open up quicksearch.ascx. Lets replace the form element with a fieldset and give it the same id. Replace the input text element with an ASP.NET TextBox control and the input submit element with an ImageButton control. You should now have something like this.
<fieldset id="quick-search"> <p> <label for="qsearch">Search:</label> <asp:TextBox CssClass="tbox" runat="server" ID="txtQuickSearch" ToolTip="Start typing and hit ENTER" Text="Search..." /> <asp:ImageButton runat="server" CssClass="btn" ID="btnQuickSearch" CausesValidation="false" ToolTip="Search" ImageUrl="/Templates/Jungle/images/search.png" /> </p> </fieldset> |
Notice the CausesValidation=”false” on the ImageButton. The reason for this is that ASP.NET Validation controls that are used on the page would otherwise get triggered when the user uses our quicksearch button. Setting the CausesValidation property to false bypasses all validation.
Next we need to fix the label, and make sure that our users can use the enter key after filling out a search phrase. The reason that this doesn’t work out of the box in ASP.NET Web forms is that everything is placed inside one form, which means that only one button can be the default on. Though as you’ll see we can fix this for users that have JavaScript enabled.
<asp:Panel runat="server" DefaultButton="btnQuickSearch"> <fieldset id="quick-search"> <p> <asp:Label runat="server" AssociatedControlID="txtQuickSearch">Search:</asp:Label> <asp:TextBox CssClass="tbox" runat="server" ID="txtQuickSearch" ToolTip="Start typing and hit ENTER" Text="Search..." /> <asp:ImageButton runat="server" CssClass="btn" ID="btnQuickSearch" CausesValidation="false" ToolTip="Search" ImageUrl="/Templates/Jungle/images/search.png" /> </p> </fieldset> </asp:Panel> |
For the enter key issue I added a Panel control and set its DefaultButton to the ImageButton’s ID. This will add the necessary JavaScript code for us. For the label I added a Label control and sat its AssociatedControlID to the TextBox’s ID. This will render as a HTML Label element and set the for attribute to the correct input’s id (ASP.NET Label controls normally render as span elements).
I also had to update the CSS code in Jungleland.css a little to make the selectors work with the slightly changed markup. I just removed the form prefix (form#quick-search).
/* header quick search */ #header #quick-search { position: absolute; top: 95px; right: 15px; z-index: 999999; padding: 0; margin: 0; border: none; width: 222px; height: 32px; background: url(../images/header-search.png) no-repeat; } #header #quick-search p { margin: 0; padding: 0; border: none; } #header #quick-search input { float: left; margin: 0; padding: 5px; border: none; background: transparent; color: #4a413c; } #header #quick-search .tbox { margin: 6px 0 0 5px; width: 170px; display: inline; } #header #search #quick-search .btn{ width: 25px; height: 25px; } #header #quick-search label { display: none; } |
Lets add a click event method to the button and the necessary code for redirecting the user to our search page.
<asp:ImageButton runat="server" OnClick="btnQuickSearch_Click" CssClass="btn" ID="btnQuickSearch" CausesValidation="false" ToolTip="Search" ImageUrl="/Templates/Jungle/images/search.png" /> |
using System; using System.Web; using EPiCode.Extensions; using EPiServer.Core; using Jungle.Templates.Jungle.PageTypes; using PageTypeBuilder.UI; namespace Jungle.Templates.Jungle.Units { public partial class QuickSearch : UserControlBase<BasePageData> { protected void btnQuickSearch_Click(object sender, EventArgs e) { var startPage = PageReference.StartPage.GetPage() as StartPage; if (startPage == null || PageReference.IsNullOrEmpty(startPage.SearchPage)) { return; } PageData searchPage = startPage.SearchPage.GetPage(); if (searchPage != null) { Response.Redirect(HttpUtility.UrlPathEncode(string.Format("{0}&q={1}", searchPage.LinkURL, txtQuickSearch.Text))); } } } } |
As you can see we’re getting the search page from the dynamic property SearchPage.
I’ve also added suggestion functionality to the quick search field. See my post Extending search field with suggestion box, for more information. All the code is also available on EPiCode.
Remember to remove the Visible=”false” property from QuickSearch in PageHeader.ascx.
Search page
We can now start creating the search page. Create the Search page type by creating a new class with the name SearchPage, and the proper attribute declaration.
using PageTypeBuilder; namespace Jungle.Templates.Jungle.PageTypes { [PageType( Name = "[Jungle] Search page ", Filename = "/Templates/Jungle/Pages/Search.aspx", SortOrder = 1030)] public class SearchPage : BasePageData { } } |
For this page type we only need three properties. Heading, MainBody, and SearchRoot. Since we already have the code for Heading and MainBody in StandardPage, we’re going to refactor and create a super class that StandardPage and SearchPage can derive from.
using EPiServer.Core; using EPiServer.Editor; using EPiServer.SpecializedProperties; using PageTypeBuilder; namespace Jungle.Templates.Jungle.PageTypes { public abstract class EditorialPage : BasePageData { [PageTypeProperty(Searchable = true, Type = typeof(PropertyString))] public virtual string Heading { get; set; } [PageTypeProperty( EditCaption = "Main body", Searchable = true, Type = typeof(PropertyXhtmlString), LongStringSettings = ( EditorToolOption.All ^ EditorToolOption.Font))] public virtual string MainBody { get; set; } } } |
We can now update StandardPage to derive from EditorialPage instead of BasePageData, and delete the Heading and MainBody property.
using EPiServer.SpecializedProperties; using PageTypeBuilder; namespace Jungle.Templates.Jungle.PageTypes { [PageType( Name = "[Jungle] Standard page ", Filename = "/Templates/Jungle/Pages/Page.aspx", SortOrder = 1010)] public class StandardPage : EditorialPage { [PageTypeProperty(EditCaption = "List image", Type = typeof(PropertyImageUrl))] public virtual string ListImage { get; set; } [PageTypeProperty(EditCaption = "Featured image", Type = typeof(PropertyImageUrl))] public virtual string FeaturedNewsImage { get; set; } } } |
Lets also update SearchPage to derive from EditorialPage and add the SearchRoot property.
using EPiServer.Core; using PageTypeBuilder; namespace Jungle.Templates.Jungle.PageTypes { [PageType( Name = "[Jungle] Search page ", Filename = "/Templates/Jungle/Pages/Search.aspx", SortOrder = 1030)] public class SearchPage : EditorialPage { [PageTypeProperty( EditCaption = "Search root page", Type = typeof(PropertyPageReference))] public virtual PageReference SearchRoot { get; set; } } } |
Go back to edit mode and create the search page, just set the SearchRoot to our start page. Then go to the start page and point the SearchPage dynamic property to the search page (remember to check the checkbox that tells EPiServer to inherit this to sub pages). After you’ve done that make sure that quick search redirects to the correct page.
Search.aspx
We can now create Search.aspx.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Search.aspx.cs" Inherits="Jungle.Templates.Jungle.Pages.Search" MasterPageFile="~/Templates/Jungle/MasterPages/Site.Master" %> |
using Jungle.Templates.Jungle.PageTypes; using PageTypeBuilder.UI; namespace Jungle.Templates.Jungle.Pages { public partial class Search : TemplatePage<SearchPage> { } } |
Standard stuff :).
The code for our search page consists of a TextBox, Button, ListView for the result, some text if there are no results, and a SearchDataSource control for searching.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Search.aspx.cs" Inherits="Jungle.Templates.Jungle.Pages.Search" MasterPageFile="~/Templates/Jungle/MasterPages/Site.Master" %> <%@ Import Namespace="EPiServer.Core"%> <asp:Content runat="server" ContentPlaceHolderID="MainContentRegion"> <h2><%= CurrentPage.Heading() %></h2> <EPiServer:Property runat="server" PropertyName="MainBody" DisplayMissingMessage="false" EnableViewState="false" /> <div id="SearchArea"> <asp:Label runat="server" AssociatedControlID="txtSearch" CssClass="accessibility">Search:</asp:Label> <asp:TextBox ID="txtSearch" text="Search..." runat="server" /> <asp:Button ID="btnSearch" CssClass="button" runat="server" OnClick="btnSearch_Click" Text="Search" /><br /> </div> <div id="ResultArea"> <asp:ListView ID="SearchResult" DataSourceID="SearchDataSource" runat="server" Visible="false" ItemPlaceholderID="itemPlaceHolder"> <ItemTemplate> <li> <h3><EPiServer:Property runat="server" PropertyName="PageLink" /></h3> <p> <%# ((PageData)Container.DataItem).PreviewText(150)%> </p> </li> </ItemTemplate> <LayoutTemplate> <h2>Search result</h2> <ol> <asp:PlaceHolder runat="server" ID="itemPlaceHolder" /> </ol> </LayoutTemplate> <EmptyDataTemplate> <p>No matching results were found</p> </EmptyDataTemplate> </asp:ListView> </div> <EPiServer:SearchDataSource ID="SearchDataSource" runat="server" EnableVisibleInMenu="false"> <SelectParameters> <EPiServer:PropertyParameter Name="PageLink" PropertyName="SearchRoot" /> <asp:controlparameter name="SearchQuery" controlid="txtSearch" propertyname="Text"/> </SelectParameters> </EPiServer:SearchDataSource> </asp:Content> |
using System; using Jungle.Templates.Jungle.PageTypes; using PageTypeBuilder.UI; namespace Jungle.Templates.Jungle.Pages { public partial class Search : TemplatePage<SearchPage> { protected override void OnLoad(EventArgs e) { base.OnLoad(e); Form.DefaultButton = btnSearch.UniqueID; if (!IsPostBack) { string query = Request.QueryString["q"]; if (!String.IsNullOrEmpty(query)) { txtSearch.Text = query; SearchResult.Visible = true; } } } protected void btnSearch_Click(object sender, EventArgs e) { SearchResult.Visible = true; SearchResult.DataBind(); } } } |
A very simple search page that looks something like this when used.
The next post: Part 6: Creating the XForm page.
Other resources
Related Posts:
- Part 7: Creating the Sitemap page – Create an EPiServer site from scratch
- Part 6: Creating the XForm page – Create an EPiServer site from scratch
- Part 4: Creating the standard page – Create an EPiServer site from scratch
- Part 2: Creating a foundation – Create an EPiServer site from scratch
- Part 3: Creating the start page – Create an EPiServer site from scratch
Steve says:
Post Author December 16, 2009 at 10:31Well explained! That part about ticking the inherit checkbox is not neccessary. It is a common misconception that you need to tick it to save a dynamic property recursively. A dynamic property is by design recursive. That checkbox is there to clear any values defined for the same property below the page your now saving it for. In case you have child pages that also have a value for the same dynamic property.
Looking forward to the XForm post. XForms are powerful, but not very well understood. People should experiment more with XForms 🙂
Martin S. says:
Post Author December 16, 2009 at 10:52Neat stuff! Looking forward to a post on Search Server integration too, man! 😉
Frederik Vig says:
Post Author December 16, 2009 at 15:48@Steve – cool, didn’t know that!
@Martin – I’ve played a little with Search Server Express. Must say I’m not a fan, found it a little buggy and awkward to work against. But that might be because of some problems I had setting it up :).
Christine says:
Post Author December 17, 2009 at 16:40I like the idea of a suggestion box to extend the search field. Nice!
SreeRaghavendra says:
Post Author April 21, 2010 at 11:23Hi Frederik,
I have a search page whose result are fetched in another page (using QueryStrings).This result page is not having any Page Type created(and dont want one to be created) but is inherited from TemplatePage.
The language text used in that page are not getting translated if the language is changed/toggled instead it goes back to the “Start” Page. How is it possible to get these language files displayed without making the Page registered in the PageTypes?
Thanks in advance.
Regards,
S.K. SreeRaghavendra
Mahesh says:
Post Author June 2, 2010 at 09:25Hi Frederik,
When i redirect to search result page for the first time it works fine. Second time it shows me the earlier result but the address bar shows correct url along with new querystring entered.
While debugging i found that in the second instance after redirection the OnLoad method doesn’t get called.
But in the Edit mode all this stuff works fine.
Why does the same application behave differently ?
Thanks in advance