Blog posts by Paul Smith2015-10-06T16:09:35.2030000Z/blogs/Paul-Smith/Optimizely WorldLimiting Page Type Instances/blogs/Paul-Smith/Dates1/2015/10/limiting-page-type-instances/2015-10-06T16:09:35.2030000Z<p>We had a requirement to limit the number of instances of a particular page type within parts of the page tree. I developed a simple attribute and validator which implements this.</p>
<p>You add the attribute to your page type class and specifiy the scope of the check. The scope options I added were :</p>
<ul>
<li>Site - only x instances of the page type can exist on the site, anywhere in the page tree. Probably useful for singletons</li>
<li>Same parent - only x instances of the page type can exist in the same parent node, i.e. amongst siblings</li>
<li>Same parent and descendants - only x instances of the page type can exist in the same parent node including descendant nodes</li>
</ul>
<p>Obviously, you could easily add more scope variants here to suite your requirements. The code can be found below:</p>
<pre class="language-csharp"><code>using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using EPiServer;
using EPiServer.Core;
using EPiServer.Validation;
[AttributeUsage(AttributeTargets.Class)]
public class AllowedInstancesAttribute : Attribute
{
public AllowedInstancesAttribute(int maxInstances)
{
MaxInstances = maxInstances;
}
public enum InstanceScope
{
Site,
SameParent,
SameParentOrDescendant
}
public int MaxInstances { get; set; }
public InstanceScope Scope { get; set; }
}
public class AllowedInstancesValidator : IValidate<PageData>
{
private readonly IContentLoader contentLoader;
public AllowedInstancesValidator(IContentLoader contentLoader)
{
this.contentLoader = contentLoader;
}
public IEnumerable<ValidationError> Validate(PageData instance)
{
var allowedInstanceAttribute = instance.GetType().GetCustomAttribute<AllowedInstancesAttribute>(true);
if (allowedInstanceAttribute == null)
{
return Enumerable.Empty<ValidationError>();
}
var searchRoot = allowedInstanceAttribute.Scope == AllowedInstancesAttribute.InstanceScope.Site
? ContentReference.StartPage
: instance.ParentLink;
var instancesOfType = this.GetInstancesOfType(instance.GetType(), searchRoot, allowedInstanceAttribute.Scope);
if (instance.PendingPublish)
{
instancesOfType++;
}
if (instancesOfType > allowedInstanceAttribute.MaxInstances)
{
return new[]
{
new ValidationError()
{
ErrorMessage = string.Format("Only {0} instances of this page type can exist at this level", allowedInstanceAttribute.MaxInstances),
PropertyName = "PageName",
Severity = ValidationErrorSeverity.Error,
ValidationType = ValidationErrorType.StorageValidation
}
};
}
return Enumerable.Empty<ValidationError>();
}
private int GetInstancesOfType(Type type, ContentReference rootPage, AllowedInstancesAttribute.InstanceScope instanceScope)
{
var instances = 0;
var children = contentLoader.GetChildren<IContent>(rootPage, new LoaderOptions());
foreach (var child in children)
{
if (type == child.GetType())
{
instances++;
}
if (instanceScope == AllowedInstancesAttribute.InstanceScope.SameParent)
{
continue;
}
instances += this.GetInstancesOfType(type, child.ContentLink, instanceScope);
}
return instances;
}
}</code></pre>
DDS Remap Type Admin Plug-in/blogs/Paul-Smith/2014/10/dds-remap-type-admin-plug-in/2014-10-28T13:43:45.0000000Z<p>After reading <a href="http://swapcode.wordpress.com/2014/10/03/alice-in-wonderland-and-episerver-upgrade-epiremapddstypes/" target="_blank">this blog post</a> I was inspired to make an EPiServer CMS Admin Plug-in to allow a Type remap in the DDS to be done direct from the site. This will come in useful when a Type you store in the DDS has changed and it is not decorated with the EPiServerDataStoreAttribute and / or the AutomaticallyRemapStore property is not set to true.</p> <p>The plug-in is available <a href="https://dl.dropboxusercontent.com/u/72541696/DdsTypeRemapperAddOn.1.0.0.0.nupkg">here</a> as an EPiServer Add-on. I will also make the source code available shortly.</p> <p>NOTE: There seems to be a caching bug in the DDS code which means that the remap isn’t always effective until the site is restarted. The bug has been reported (unofficially confirmed as a bug) and will hopefully be fixed in due course.</p>Problems with XForms when upgrading to CMS 6 R2/blogs/Paul-Smith/Dates1/2012/4/Problems-with-XForms-when-upgrading-to-CMS-6-R2/2012-04-11T10:08:31.0000000Z<p>It seems that many people have had problems when upgrading an EPiServer CMS site to 6 R2 with the last part of the upgrade script.</p> <p>What this tries to do is read all the XForm definitions from the database, perform a html decode on the form name, and then save the definition back to the database. Why this has to be done is not relevant to this post.</p> <p>What is relevant is that it often doesn’t work for various reasons. This part of the script is run in a separate transaction after the main upgrade transaction has completed. The reason for this is that it has to run in a separate application domain in the context of the website. </p> <p>If your site doesn’t use XForms or if you know that there aren’t any forms that have names that required html encoding then you can just ignore this error. As mentioned above, the main upgrade has been committed and your site should be ready to go.</p> <p>If however you do need to run this part of the script and it keeps failing then there is a simple workaround.</p> <p><a href="http://dl.dropbox.com/u/72541696/UpgradeXFormNames.aspx">Here</a> you will find an ASPX file which contains the same code as the script executes. Place it in your site, execute it (i.e. browse to it’s Url) and it will do the business. When it’s finished it will tell you how many XForms it’s upgraded. If for some reason it should fail or get interrupted, it is safe to run again as it will only upgrade those XForms that need it.</p> <p>When all is done you can just remove the ASPX from the site.</p> <p><i>Voilà</i></p>New design and functionality on nuget.episerver.com/blogs/Paul-Smith/Dates1/2011/10/New-design-and-functionality-on-nugetepiservercom/2011-10-05T09:37:16.0000000Z<p>The first round of upgrades to nuget.episerver.com took place last night. The site now has a cleaner design as well as package searching and sorting.</p> <p><a href="/link/12f029065202457fba02bbf14ffab483.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/cbebd212ca334138b90cf6e20cb10a3e.png" width="403" height="293" /></a></p> <p>I found that I had to clear my browser cache to get the new css to take affect so if it looks a bit weird then try this first before reporting it as a bug.</p> <p>I would like to thank <a href="http://world.episerver.com/System/Users-and-profiles/Community-Profile-Card/Cecilia%20von%20Wachenfeldt/" target="_blank">Cecilia</a> and <a href="http://world.episerver.com/System/Users-and-profiles/Community-Profile-Card/Andreas%20Oldeskog/" target="_blank">Andreas</a> from <a href="http://www.nansen.se" target="_blank">Nansen</a> for their help with the design.</p> <p>Feedback as always is welcome.</p>EPiServer.CMS.Core and EPiServer.Framework now on EPiServer Nuget/blogs/Paul-Smith/Dates1/2011/9/EPiServerCMSCore-and-EPiServerFramework-now-on-EPiServer-Nuget/2011-09-22T16:30:01.0000000Z<p>I have added 2 new packages to the EPiServer Nuget feed which removes the need to distribute EPiServer assemblies with your packages.</p> <h2>EPiServer.CMS.Core</h2> <p>This package contains the core CMS assemblies that are often required when building modules:</p> <ul> <li>EPiServer </li> <li>EPiServer.BaseLibrary </li> <li>EPiServer.Implementation </li> <li>EPiServer.Enterprise </li> </ul> <h2>EPiServer.Framework</h2> <p>This package contains the assemblies most often referenced in the EPiServer Framework:</p> <ul> <li>EPiServer.ApplicationModules </li> <li>EPiServer.Data </li> <li>EPiServer.Events </li> <li>EPiServer.Framework </li> <li>EPiServer.Shell </li> <li>System.ComponentModel.Composition (EPiServer compiled version) </li> </ul> <p>If your Nuget package is dependent on any of these assemblies you can just add a package dependency instead of an assembly dependency.</p> <p>Feedback as always is welcome.</p>What do you want from EPiServer Nuget?/blogs/Paul-Smith/Dates1/2011/9/What-do-you-want-from-EPiServer-Nuget/2011-09-20T14:45:06.0000000Z<p>I would like to get some input from the EPiServer Developer community about what kind of features/changes you would like to see on EPiServer Nuget. Suggestions can be for the web site or the actual feed itself. </p> <p>From the suggestions I get I will create a prioritized backlog based on how many +1’s I get for a suggestion.</p> <p>As always, your help is really appreciated.</p>EPiServer Falcon Seminar Video/blogs/Paul-Smith/Dates1/2011/9/EPiServer-Falcon-Seminar-Video/2011-09-15T19:19:57.0000000Z<p>You can watch the video of the EPiServer Falcon Seminar held in Stockholm on Wednesday 14th September using the links below:</p> <p><a href="https://episerver.webex.com/episerver/ldr.php?AT=pb&SP=MC&rID=41374487&rKey=032A2EF634BA26FD" target="_blank">Stream</a> | <a href="https://episerver.webex.com/episerver/lsr.php?AT=dw&SP=MC&rID=41374487&rKey=03F378D129E45FCD" target="_blank">Download</a></p> <p>For those of you who didn’t watch live then it’s good to know that the computer we were using to record the video decided to shutdown during the recording. This happens at 00:45:45 with the action continuing at  00:50:30.</p> <p>If you have any feedback about any of the features seen in the video then please send it by email to paul [dot] smith [at] episerver [dot] com.</p> <p>Thanks for your participation, we really appreciate it.</p>EPiServer Falcon Preview Seminar WebEx Details/blogs/Paul-Smith/Dates1/2011/9/EPiServer-Falcon-Preview-Seminar-WebEx-Details/2011-09-13T18:00:28.0000000Z<p>The EPiServer Falcon Preview Seminar starts at 18:00, Sweden Summer Time (Stockholm, GMT+02:00) on Wednesday 14th September.</p> <p>Details of the WebEx meeting can be found below:</p> <p>Meeting Url: <a href="https://episerver.webex.com/episerver/j.php?ED=186886232&UID=484980697&PW=NZjdlZjJhN2Mx&RT=MTUjMTMw">https://episerver.webex.com/episerver/j.php?ED=186886232&UID=484980697&PW=NZjdlZjJhN2Mx&RT=MTUjMTMw</a></p> <p>Meeting Password: epifalcon2011</p> <p>The meeting can be joined 15 minutes prior to the start. </p> <p>After each topic is presented, an open Q & A session will be held. From WebEx you will be able to use the Chat facility.</p>EPiServer CMS Falcon Seminar – A Few Clarifications/blogs/Paul-Smith/Dates1/2011/9/EPiServer-CMS-Falcon-Seminar--A-Few-Clarifications/2011-09-01T23:36:24.0000000Z<p>Since my <a href="http://world.episerver.com/Blogs/Paul-Smith/Dates1/2011/8/A-Preview-of-EPiServer-CMS-vNext/" target="_blank">blog-post about the EPiServer CMS Falcon Seminar</a> to be held on 14<sup>th</sup> September, we have received quite a lot of questions about what we are planning to show, so I would like to clarify a few things.</p> <p>First of all, <a href="http://joelabrahamsson.com/entry/about-episerver-introducing-a-native-solution-for-declaring-page-types-in-code" target="_blank">as Peter Sunna said on Joel Abrahamsson’s blog</a>, what we are going to show is far from finished code, in some cases we are still at the research level. Nothing done is set in stone (or even Jell-o) yet.</p> <p>Secondly the point of the seminar is to share our thoughts so far and our ideas for the future and get as much early feedback from the developer community as possible. This is a great opportunity for all EPiServer developers to tell us what they think and help shape the future of the product.</p> <p>At the time of writing this blog, there are still 9 places available in the on-site audience. We really appreciate your help with this matter so I encourage you to claim a place or if you can’t make it then participate by WebEx. Details of the WebEx will be published a couple of days before the event on my blog.</p>EPiServer CMS Visual Studio Integration Extensions/blogs/Paul-Smith/Dates1/2011/9/EPiServer-CMS-Visual-Studio-Integration-Extensions/2011-09-01T15:20:00.0000000Z<p>EPiServer CMS 6 R2 ships with a Visual Studio Integration package that you can optionally install from the zip download found <a href="http://world.episerver.com/Download/Items/EPiServer-CMS/EPiServer-CMS-6-R2/" target="_blank">here</a>.</p>
<p>Once this is installed, you get not only a new EPiServer Project type but also EPiServer components that can be created using the Add New Item menu:</p>
<p><a href="/link/2a08126d7d1243c5a68c73af02ae3476.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="/link/93ac184e064e42d3947d21bb2c5e5b2e.png" border="0" alt="image" width="390" height="271" /></a></p>
<p> </p>
<p>Unfortunately, due to time constraints, this hasn’t been kept inline with the juicy new features added in CMS 6 R1 and R2.</p>
<p>With that said, I have taken an old VS Integration extension package that Fredrik Tjärnberg released a while back and added some of those juicy bits from CMS 6 R1 and R2.</p>
<p>After you have installed the extension, in addition to the 4 EPiServer items already available, you get 3 new ones: Dynamic Content, Online Center Gadget and Visitor Group Criterion:</p>
<p><a href="/link/188fc890d91344d58cb2071506f60198.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="/link/2c5ac653ddd7427c9c9b49190d45eaa0.png" border="0" alt="image" width="395" height="274" /></a></p>
<p> </p>
<p>All of the new items come with an array of options to chose from when you create one.</p>
<p>For Dynamic Content, you chose what type of component to create to render the content along with the option to create a custom Editor:</p>
<p><a href="/link/84033c5bded846b790fc4df88394087e.png"><img style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="/link/f6f1fc5412a0451a89080deecc160bbc.png" border="0" alt="image" width="244" height="158" /></a></p>
<p> </p>
<p>For an Online Center Gadget, you can chose to optionally create a model class and a JQuery based Javascript file:</p>
<p><a href="/link/b7d95b16e2514eab9165ec4481e4d522.png"><img style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="/link/e0d767e645a4496fbc9cca25fbd467df.png" border="0" alt="image" width="244" height="108" /></a></p>
<p> </p>
<p>And last but not least, for a Visitor Group Criterion, you get options to create a model class, a custom Editor and a Dojo based Javascript file (for the custom Editor):</p>
<p><a href="/link/66425880c5a14bbbb95f76acad92d020.png"><img style="background-image: none; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="/link/35e130dbea214eacb562343612756d12.png" border="0" alt="image" width="244" height="190" /></a></p>
<p> </p>
<p>Whilst VS Integration may not feel mission critical for seasoned EPiServer developers, it can be very useful for new developers. I also think that some of the new options could be useful for all when creating components with custom editors and javascript files.</p>
<p>The VS Integration Extension installer can be found <a href="http://world.episerver.com/Download/Items/EPiServer-CMS/EPiServer-CMS-6-R2/EPiServer-Visual-Studio-Integration-Extensions/">here</a>. Both Visual Studio 2008 and 2010 are supported including Visual Web Developer and VS Professional / Team Edition.</p>
<p>Whilst this is not an official EPiServer release I intend to fix bugs and add new features so let me know if you have any issues or ideas.</p>
<p>Happy coding!</p>Announcing Visitor Group Tracking and Statistics/blogs/Paul-Smith/Dates1/2011/8/Announcing-Visitor-Group-Tracking-and-Statistics/2011-08-22T22:30:43.0000000Z<p>EPiServer Visitor Groups have some basic tracking and statistics built-in which tells you how many times a Visitor Group was “visited” or tested for a match, in other words.</p> <p>Many people have commented that whilst they love Visitor Groups, sooner rather than later, marketing departments or whoever is in charge of a site is going to want more information about how successful their Visitor Groups and criteria are.</p> <h2>Say hello to EPiServerVisitorGroupTracker</h2> <h2></h2> <p>With that in mind I have developed a framework called EPiServerVisitorGroupTracker that will give programmers a large amount of information every time a Visitor Group is tested. From this information a huge array of statistics and reports can be compiled which should keep site owners busy for a while.</p> <p>You hook into the information stream by adding an event handler to the framework. You should do this when the site starts, so either in the Application_Start event or in an EPiServer Initialization Module.</p> <pre class="language-csharp"><code><span class="kwrd">protected</span> <span class="kwrd">void</span> Application_Start(Object sender, EventArgs e)
{
EPiServerVisitorGroupTracker.
VisitorGroupEvents.
VisitorGroupTested += VisitorGroupTested;
}
<span class="kwrd">private</span> <span class="kwrd">void</span> VisitorGroupTested(<span class="kwrd">object</span> sender,
VisitorGroupTestedEventArgs e)
{
<span class="rem">// Record as much or as little from the </span>
<span class="rem">// VisitorGroupTestedEventArgs as needed </span>
}</code></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style><style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>That's it, you're done! How much information to store and where to store it, depends on the requirements you have of course. The EPiServer Dynamic Data Store is an ideal candidate as a store.</p>
<h2>Now the juicy bit</h2>
<p>What information do you have access to in your event handler? The VisitorGroupTestedEventArgs class is defined as follows:</p>
<pre class="language-csharp"><code><span class="kwrd">public</span> <span class="kwrd">class</span> VisitorGroupTestedEventArgs : EventArgs
{
<span class="kwrd">public</span> VisitorGroup VisitorGroup { get; <span class="kwrd">protected</span> <span class="kwrd">internal</span> set; }
<span class="kwrd">public</span> IPrincipal CurrentPrincipal { get; <span class="kwrd">protected</span> <span class="kwrd">internal</span> set; }
<span class="kwrd">public</span> HttpContextBase HttpContext { get; <span class="kwrd">protected</span> <span class="kwrd">internal</span> set; }
<span class="kwrd">public</span> <span class="kwrd">bool</span> Matched { get; <span class="kwrd">protected</span> <span class="kwrd">internal</span> set; }
<span class="kwrd">public</span> IEnumerable<VisitorGroupCriterion> CriteriaMatched { get; set; }
<span class="kwrd">public</span> IEnumerable<VisitorGroupCriterion> CriteriaNotMatched { get; set; }
<span class="kwrd">public</span> IEnumerable<VisitorGroupCriterion> CriteriaNotTested { get; set; }
<span class="kwrd">public</span> <span class="kwrd">int</span> PointsMatched { get; set; }
<span class="kwrd">public</span> PageReference VisitedPageReference { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> VisitedPageLanguage { get; set; }
}</code></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>As you can see, there is plenty of useful information to allow site owners to determine if their Visitor Groups are matching and if not, why not by examining the CriteriaMatched, CriteriaNotMatched and CriteriaNotTested properties. You also have access to all the same information the criteria executed had via the HttpContext and CurrentPrincipal properties.</p>
<h2>Performance Considerations</h2>
<p>The events will only be fired if statistics are enabled for the Visitor Group in the Visitor Group Admin user interface. Obviously, capturing this information does add extra load to the site so use it with caution. </p>
<p>The events are fired on a separate IIS Request Thread from the original request that triggered the Visitor Group test so responses to page visitors will not be affected by the event firing.</p>
<h2>How does it work?</h2>
<p>It works by inserting proxies around the VisitorGroupRole class from EPiServer and the criteria classes that EPiServer and others develop. The information presented in the VisitorGroupTestedEventArgs class is captured in these proxies. Unfortunately, I had to use reflection is two places to achieve this but I have a couple of friends on the inside at EPiServer who I can talk to about making the Visitor Group API’s more open in EPiServer vNext <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="/link/d24018ae91ff40dab1362e6a3bf26ec7.png" /></p>
<h2></h2>
<h2>Where can I get this?</h2>
<p>The EPiServerVisitorGroupTracker assembly is available from EPiServer’s Nuget feed and the source code is available from <a href="http://episervervgtracker.codeplex.com">http://episervervgtracker.codeplex.com</a></p>
<p>Happy tracking!</p>Visitor Group Usage Report/blogs/Paul-Smith/Dates1/2011/8/Visitor-Group-Usage-Report/2011-08-18T21:43:26.0000000Z<p>I have created a simple report to show which CMS pages EPiServer Visitor Groups are used on:</p> <p><a href="/link/cf17d7c44bdc4cdfb5a897b9d9e837cf.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/66d30ccfb5c24c31a010122e8bc80cf1.png" width="382" height="278" /></a></p> <p>Clicking the Visitor Group name will take you to VG Admin for the Visitor Group. Clicking the page name will take you to CMS Edit Mode for that page.</p> <p>The source code is available in the EPiServer.Samples.VisitorGroupUsageReport package on EPiServer Nuget.</p> <p>Feel free to use as is, amend, rip apart etc. Feedback, as always, is welcome.</p> <p>Happy reporting!</p>Creating EPiServer Admin/Edit Plug-ins using MVC/blogs/Paul-Smith/Dates1/2011/8/Creating-EPiServer-AdminEdit-Plug-ins-using-MVC/2011-08-15T21:01:39.0000000Z<p>Since version 6 R1, EPiServer CMS has had full support for MVC routing. This is what enables you to create OnlineCenter Gadgets in MVC.</p> <p>What may not have been obvious is that you can also create Admin and Edit mode plug-ins using MVC instead of Web Forms.</p> <p>You start by creating a controller with a default Index method, an index view and optionally a model. I am not going to go into the specifics of how MVC works as there are plenty of blog posts out there by MVC experts that explain it far better than I could. I recommend looking at the <a href="http://www.asp.net/mvc" target="_blank">Microsoft ASP.NET MVC site</a> to start with.</p> <p>To make your MVC controller / view into an EPiServer Admin / Edit plug-in, you need to add an EPiServer.PlugIn.GuiPlugIn attribute to the controller class. You can read more about EPiServer Plug-ins in the <a href="http://sdk.episerver.com/library/cms5/html/T_EPiServer_PlugIn_GuiPlugInAttribute.htm" target="_blank">EPiServer SDK</a> and also see <a href="http://www.frederikvig.com/2009/10/creating-an-episerver-plugin/" target="_blank">an example of creating one using Web Forms by Frederik Vig</a>.</p> <div class="csharpcode"> <pre class="language-csharp"><code>[EPiServer.PlugIn.GuiPlugIn(</code></pre>
<pre class="language-csharp"><code>Area=EPiServer.PlugIn.PlugInArea.AdminConfigMenu, </code></pre>
<pre class="language-csharp"><code>Url=<span class="str">"/modules/Alloy/MyAdminPlugin/Index"</span>, </code></pre>
<pre class="language-csharp"><code>DisplayName=<span class="str">"My MVC Admin Plugin"</span>)]</code></pre>
<pre class="language-csharp"><code><span class="kwrd">public</span> <span class="kwrd">class</span> MyAdminPluginController : Controller</code></pre>
<pre class="language-csharp"><code>{ </code></pre>
<pre class="language-csharp"><code> <span class="kwrd">public</span> ActionResult Index()</code></pre>
<pre class="language-csharp"><code> {</code></pre>
<pre class="language-csharp"><code> <span class="kwrd">return</span> View();</code></pre>
<pre class="language-csharp"><code> } </code></pre>
<pre class="language-csharp"><code>}</code></pre>
</div>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>The crucial item here is the Url value: </p>
<p>/modules/Alloy/MyAdminPlugin/Index. </p>
<p>That tells EPiServer CMS what to execute to render the HTML for your plug-in.</p>
<h2></h2>
<h2>How the Url is determined</h2>
<p>To understand this we need to look in the EPiServer CMS web.config in the EPiServer Shell public modules section:</p>
<div class="csharpcode">
<pre class="language-csharp"><code><span class="kwrd"><</span><span class="html">episerver.shell</span><span class="kwrd">></span></code></pre>
<pre class="language-csharp"><code> <span class="kwrd"><</span><span class="html">publicModules</span> <span class="attr">rootPath</span><span class="kwrd">="~/modules/"</span> <span class="attr">autoDiscovery</span><span class="kwrd">="Minimal"</span><span class="kwrd">></span></code></pre>
<pre class="language-csharp"><code> <span class="kwrd"><</span><span class="html">add</span> <span class="attr">name</span><span class="kwrd">="Alloy"</span><span class="kwrd">></span></code></pre>
<pre class="language-csharp"><code> <span class="kwrd"><</span><span class="html">assemblies</span><span class="kwrd">></span></code></pre>
<pre class="language-csharp"><code> <span class="kwrd"><</span><span class="html">add</span> <span class="attr">assembly</span><span class="kwrd">="EPiServer.Templates.AlloyTech"</span> <span class="kwrd">/></span></code></pre>
<pre class="language-csharp"><code> <span class="kwrd"></</span><span class="html">assemblies</span><span class="kwrd">></span></code></pre>
<pre class="language-csharp"><code> <span class="kwrd"></</span><span class="html">add</span><span class="kwrd">></span> </code></pre>
<pre class="language-csharp"><code> <span class="kwrd"></</span><span class="html">publicModules</span><span class="kwrd">></span></code></pre>
<pre class="language-csharp"><code><span class="kwrd"></</span><span class="html">episerver.shell</span><span class="kwrd">></span></code></pre>
</div>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>You will see that the rootPath value for public modules is “modules” and that I have added a module called “Alloy”. Those 2 values make up the first 2 parts of the Url to the plug-in. This works because EPiServer CMS adds these values to the ASP.NET routing table when the application starts.</p>
<p>The last 2 parts of the Url are the controller name (without the controller suffix) and the name of the method to execute on the controller, in this case “Index”. This is standard MVC convention based programming in action in terms of matching a route in the format {controller}/{method}. In fact, you can even remove the “Index” part as this is assumed to be the default action. In that case the Url would be:</p>
<p>/modules/Alloy/MyAdminPlugin/</p>A Preview of EPiServer CMS vNext/blogs/Paul-Smith/Dates1/2011/8/A-Preview-of-EPiServer-CMS-vNext/2011-08-09T17:02:00.0000000Z<p>On Wednesday 14th September 2011 we will be holding a developer seminar where we will be showing some of the new features planned for EPiServer CMS vNext, or as we call it, Project Falcon.</p>
<p>Project Falcon has many planned features but we would like to show you, and very importantly, get feedback on, the following three:</p>
<h2>Typed Page Data</h2>
<p>Yes, this is the big one many of you have been waiting for and now it finally looks like it’s on it’s way. PageTypeBuilder has done a fantastic job plugging this hole but we thought it was about time we did it ourselves.</p>
<h2>Better Module Management</h2>
<p>It seems like there are a thousand ways to obtain and install modules on an EPiServer CMS site at the moment. In Project Falcon we are looking into a new system for packaging, installing and even un-installing modules which will hopefully become the de-facto standard.</p>
<h2>New Localization Handling</h2>
<p>There are currently several ways to work with localization across the EPiServer product range. Project Falcon plans to introduce a new unified system.</p>
<h1>The Event</h1>
<p>The seminar will be held on Wednesday 14th September from 17.30 at our head office in Stockholm. There are 30 places in the audience up for grabs which will be allocated based upon your EPiServer World Community Points ranking. Food and drink will be served.</p>
<p>Don’t worry if you can’t make it or don’t get a place, the event will also be broadcast live over Webex and I am hoping we can also record it for offline viewing.</p>
<p>If you’re interesting in attending then please send an email to <a href="mailto:paul.smith@episerver.com">Paul Smith</a> stating your name and the EPiServer Partner you work for. The cut-off date for applications is 31st August 2011. After that date, emails will be sent out confirming place allocations.</p>
<p><strong>Update: I don't need an email if you want to participate in the meeting over WebEx. Details of the WebEx event and how you can ask questions will be published on my blog a couple of days before the event.</strong></p>EPiServer NuGet now has package upload/blogs/Paul-Smith/Dates1/2011/7/EPiServer-NuGet-now-has-package-upload/2011-07-27T00:16:54.0000000Z<p>Our NuGet site, <a href="http://nuget.episerver.com/" target="_blank">nuget.episerver.com</a> has now been updated so you can log-in (using your normal EPiServer World account) and <a href="http://nuget.episerver.com/en/Upload/" target="_blank">upload</a> your own packages. Uploads are subject to moderation so they won’t appear in the live feed immediately but should normally be available with 24 hours.</p> <p>The site is still at the beta stage so please take the time to report any problems you have to <a href="mailto:nuget@episerver.com">nuget@episerver.com</a>. Please note that package submissions are no longer accepted via email.</p>Time Period Visitor Group Criterion/blogs/Paul-Smith/Dates1/2011/7/Time-Period-Visitor-Group-Criterion/2011-07-12T00:25:31.0000000Z<p>I saw a requirement from a customer who wanted to be able to set start and stop publish dates for different parts of a page.</p> <p>This of course can be done by creating and maintaining several page versions but sounds like quite a clumsy solution to me.</p> <p>With that said, please say hello to <strong>TimePeriodCriterion</strong>. This works by having a start date and time and an end date and time. The criterion only matches when the current date and time are within the time range specified.</p> <pre class="language-csharp"><code><span class="kwrd">namespace</span> CriteriaPack.TimePeriodCriterion
{
[VisitorGroupCriterion(
DisplayName = <span class="str">"Time Period"</span>,
Category = <span class="str">"Time and Place Criteria"</span>,
LanguagePath = <span class="str">"/shell/cms/visitorgroups/criteria/timeperiod"</span>)]
<span class="kwrd">public</span> <span class="kwrd">class</span> TimePeriodCriterion : CriterionBase<TimePeriodCriterionModel>
{
<span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> IsMatch(System.Security.Principal.IPrincipal principal, HttpContextBase httpContext)
{
<span class="kwrd">return</span> DateTime.Now >= <span class="kwrd">this</span>.RangeStart && DateTime.Now <= RangeEnd;
}
<span class="kwrd">protected</span> DateTime RangeStart
{
get
{
<span class="kwrd">return</span> Model.StartDate.Add(Model.StartTime.TimeOfDay);
}
}
<span class="kwrd">protected</span> DateTime RangeEnd
{
get
{
<span class="kwrd">return</span> Model.EndDate.Add(Model.EndTime.TimeOfDay);
}
}
}
<span class="kwrd">public</span> <span class="kwrd">class</span> TimePeriodCriterionModel : CriterionModelBase
{
[DojoWidget(WidgetType=<span class="str">"dijit.form.DateTextBox"</span>)]
[Required]
<span class="kwrd">public</span> DateTime StartDate { get; set; }
[DojoWidget(WidgetType = <span class="str">"dijit.form.TimeTextBox"</span>)]
[Required]
<span class="kwrd">public</span> DateTime StartTime { get; set; }
[DojoWidget(WidgetType = <span class="str">"dijit.form.DateTextBox"</span>)]
[Required]
<span class="kwrd">public</span> DateTime EndDate { get; set; }
[DojoWidget(WidgetType = <span class="str">"dijit.form.TimeTextBox"</span>)]
[Required]
<span class="kwrd">public</span> DateTime EndTime { get; set; }
<span class="kwrd">public</span> <span class="kwrd">override</span> ICriterionModel Copy()
{
<span class="rem">// We can use the ShallowCopy method as this class</span>
<span class="rem">// does not have any members that need to be deep copied</span>
<span class="kwrd">return</span> <span class="kwrd">base</span>.ShallowCopy();
}
}
}</code></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>A couple of the advantages of implementing start/stop publish as a criterion are that it can of course be combined with other criteria in a Visitor Group and you can also specify fallback content, i.e. something else to display on the page when the end time and date has been passed.</p>
<p>For example, competition information on a start page:</p>
<p>Create an EPiServer Visitor Group with the Time Period criterion specifying the dates the July competition is open for entry:</p>
<p><a href="/link/06217175796e47aea470cc8d3263ba92.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/16f9d62c3c7e4000bf0ce73c16896c6c.png" width="244" height="139" /></a></p>
<p>Information can now be added about the competition to the start page in a personalized content block selecting the ‘July Competition Entry Period’ Visitor Group created with the Time Period criterion. </p>
<p><a href="/link/fcca2fd142224f03995f50a318a4e71f.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/d0bcfe79e770464e9245fad52ef8ed9f.png" width="244" height="192" /></a></p>
<p>The information about entering the competition will now only be shown between the dates specified in the criterion, otherwise the fallback content will be specified.</p>
<p>A NuGet package called TimePeriodCriterion containing just the re-distributable components (as with all CriterionPack NuGet packages) can be downloaded from <a href="http://nuget.episerver.com" target="_blank">nuget.episerver.com</a>, alternatively you can get the source code from <a href="http://criteriapack.codeplex.com/" target="_blank">CodePlex</a>.</p>Session and Unit Of Work for Dynamic Data Store/blogs/Paul-Smith/Dates1/2011/7/Session-and-Unit-Of-Work-for-Dynamic-Data-Store/2011-07-06T00:31:47.0000000Z<p>I’ve created a couple of classes that make it even easier to work with the Dynamic Data Store:</p> <h3>DynamicDataStoreSession</h3> <p>This wraps the main functionality of a DynamicDataStore instance with the following additional benefits:</p> <ul> <li>Thread Safe – Ensures all calls to a session instance are done from the same thread that created it and throws an exception if not. This is done because a DynamicDataStore instance is not thread safe but has no enforcement thus resulting in weird errors when used from multiple threads </li> <li>Specify Type Only Once - When working with the DDS you need to specify the Type in the call to DynamicDataStoreFactory.CreateStore/GetStore and then again when calling Load / Find / Items etc. on a DynamicDataStore instance. This is because the Type passed to CreateStore/GetStore is used to derive the store's name and the Type passed to Load / Find / Items defines what container to load the properties from the store into. The DynamicDataStoreSession is a generic class so the Type you pass as a generic argument is used in all cases where needed. E.g. <pre class="language-csharp"><code>var session = <span class="kwrd">new</span> DynamicDataStoreSession<Person>();
var people = from person <span class="kwrd">in</span> session.Items
<span class="kwrd">where</span> person.FirstName == <span class="str">"Jones"</span>
select person;</code></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style></li>
<li>Find requires no magic strings - The Find method of the DynamicDataStore takes a string property name and value (or collection of). Some people quite rightly don't like using 'magic' strings in this way. The DynamicDataStoreSession provides a Find method where property names can be specified by using a lambda expression (thanks to Anders Hattestad for this). It also has a LINQ style method chaining syntax. E.g.
<pre class="language-csharp"><code>var session = <span class="kwrd">new</span> DynamicDataStoreSession<Person>();
var people = session.Find()
.Where(p => p.LastName)
.Equals(<span class="str">"Jones"</span>)
.And(p => p.FirstName)
.Equals(<span class="str">"Mark"</span>)
.Go();</code></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style></li>
<li>IoC / Mocking / Unit Testing - The DynamicDataStoreSession and associated find classes implement interfaces. They also allow constructor injection of a DynamicDataStoreFactory </li>
</ul>
<h3>DynamicDataStoreUnitOfWork</h3>
<p>It has always been possible to implement ‘unit of work’ / transactions between DDS stores and external data sources. The DynamicDataStoreUnitOfWork class wraps this functionality up nicely and should be used like a TransactionScope in a using block.</p>
<pre class="language-csharp"><code><span class="kwrd">class</span> Person
{
<span class="kwrd">public</span> <span class="kwrd">string</span> FirstName { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> LastName { get; set; }
<span class="kwrd">public</span> Identity AddressId { get; set; }
}
<span class="kwrd">public</span> <span class="kwrd">class</span> Address
{
<span class="kwrd">public</span> <span class="kwrd">string</span> Line1 { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> Line2 { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> City { get; set; }
}
<span class="kwrd">using</span> (var work = <span class="kwrd">new</span> DynamicDataStoreUnitOfWork())
{
var addressSession = work.GetSession<Address>();
var addressId = addressSession.Save(<span class="kwrd">new</span> Address()
{
Line1 = <span class="str">"Main Street"</span>,
City = <span class="str">"Chicago"</span>
});
var personSession = work.GetSession<Person>();
personSession.Save(<span class="kwrd">new</span> Person()
{
FirstName = <span class="str">"Paul"</span>,
LastName = <span class="str">"Smith"</span>,
AddressId = addressId
});
work.Complete();
}</code></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>You can get an assembly <a href="/link/34002cd8b3c548609f83b200f83afa08.dll">here</a> or if you want the source code then download the EPiServer.Extensions.DynamicDataStore.1.0.nupkg from nuget.episerver.com.</p>Automatically Updating the Geo-IP Database/blogs/Paul-Smith/Dates1/2011/7/Automatically-Updating-the-Geo-IP-Database/2011-07-04T18:39:00.0000000Z<p>Last week, David Knipe <a href="http://www.david-tec.com/2011/06/Updating-the-EPiServer-Geo-IP-database/" target="_blank">blogged about how to update the MaxMind GeoIP database</a> that ships with the EPiServer Framework.</p>
<p>This inspired me to create an EPiServer Scheduled Job to do this automatically:</p>
<p><a href="/link/73ccf4e656774d1dbe9c9ec1d4c32efc.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="/link/a282bd83c27048f38c8bbc87f293c405.png" border="0" alt="image" width="488" height="293" /></a></p>
<p>As the job needs to download a file from the internet, which could be a lengthy operation, I made it a ‘stoppable’ job. You can read more about stoppable scheduled jobs <a href="http://thisisnothing.wordpress.com/2010/05/13/scheduled-jobs-in-episerver-cms-6/" target="_blank">here</a>.</p>
<p>The job also has a settings page where you can adjust the URL of the downloadable database file as well as a couple of other settings:</p>
<p><a href="/link/f9a1547c37f840c099fa87a318ff53fa.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0pt none;" title="image" src="/link/14f5f9b5550a4ea988746511289179c0.png" alt="image" width="488" height="293" /></a></p>
<p>It seems that MaxMind update their ‘Lite’ database on the 1st of every month so it’s probably a good idea to schedule the job for the 2nd of every month.</p>
<p>You can obtain a Deployment Center Zip Module <a href="/link/3e0c5b95d4f3470c854c99a9ffcd2f06.epimodule">here</a> to add the functionality to a site or if you want the source code then download the EPiServer.Extensions.GeoIPUpdate.1.0.nupkg package from nuget.episerver.com.</p>
<p>The code has been tested but of course if you find any bugs or have any suggestions for improvements then please let me know.</p>
<p>Happy updating!</p>Code Samples from EPiServer North America Partner Summit 2011/blogs/Paul-Smith/Dates1/2011/6/Code-Samples-from-EPiServer-North-America-Partner-Summit-2011/2011-06-15T22:10:00.0000000Z<p>At the EPiServer North America Partner Summit last week, I showed some code examples of:</p>
<ul>
<li>Dynamic Content using User Controls including a custom edit control </li>
<li>Personalization Criteria including a custom edit control </li>
<li>An OnlineCenter Gadget storing data in the Dynamic Data Store </li>
<li>An OnlineCenter Menu Provider </li>
<li>An OnlineCenter Search Provider </li>
</ul>
<p>The code and installation instructions can be found in a zip file <a href="/link/4f4038139ac743e6a195a55b682fd9db.zip">here</a>.</p>EPiServer Full Text Search Now Available For CMS 6 R2/blogs/Paul-Smith/Dates1/2011/5/EPiServer-Full-Text-Search-Now-Available-For-CMS-6-R2/2011-05-27T10:21:37.0000000Z<p>You may or may not be aware that the <a href="http://www.episerver.com/en/Products/Social-media-and-community/" target="_blank">EPiServer Relate</a> product ships with a full text search service. I am pleased to announce that this now being made available to all CMS 6 R2 customers as a separate download <a href="http://world.episerver.com/Download/Items/EPiServer-CMS/EPiServer-CMS-6-R2/EPiServer-Full-Text-Search-Service/" target="_blank">here</a>.</p> <p>The search service is split into 2 parts:</p> <p>- A client API to push content into the indexing server and obtain search results</p> <p>- A REST based web service which wraps <a href="http://incubator.apache.org/lucene.net/" target="_blank">Lucene.net</a> to provide the default indexing service implementation.</p> <p>The idea of this is that the REST based web service can be replaced with another implementation wrapping a different indexing engine that support the push model, without having to change your site code that pushes content and performs search requests.</p> <h2>Getting Started with EPiServer FTS</h2> <p>1. <a href="http://world.episerver.com/Download/Items/EPiServer-CMS/EPiServer-CMS-6-R2/EPiServer-Full-Text-Search-Service/" target="_blank">Download the EPiServer Search Windows Installer</a> and run it on the desired machine</p> <p>2. Deploy the Indexing Service using EPiServer Deployment Center. The default service is implemented as a Windows Communication Foundation Service with a SVC file endpoint. The service can be deployed on a new or existing ASP.NET enabled web site. Note that an existing site in this context does not need to be an EPiServer CMS site although this works just as well:</p> <p><a href="/link/a19e74c0e9cb418b99160448c2159cfb.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/4a8015c390bd468593390b0cb28110f5.png" width="531" height="404" /></a></p> <p>Whilst experimenting with it, I would recommend installing the service on the same EPiServer CMS site that will be it’s client:</p> <p><a href="/link/f38acb54dc0a4edba877c162df9514ee.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/719f0e3b2f3b4e1099c10e11ef802417.png" width="533" height="406" /></a></p> <p>You need to provide the path of where the indexing service will store its index files. This should not be under a website root. I recommend using the the EPiServer CMS site’s VPP folder with a sub “Index” folder. Note that this folder does not need to exist at this time:</p> <p><a href="/link/6b1c0d18d92b41e99217849e6c034b91.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/4c08ea5c62814a9db750329a76dea399.png" width="534" height="406" /></a></p> <p>You should now be ready to deploy:</p> <p><a href="/link/15b6a7b565904099b00895a3a99c80e1.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="/link/62e9aa8804594e499f9639a675d11201.png" width="539" height="410" /></a></p> <p> </p> <p>When the deployment is finished the index service target website’s configuration file should have been updated with the following element:</p> <pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">episerver.search.indexingservice</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">clients</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">add</span> <span style="color: #ff0000">name</span>=<span style="color: #0000ff">"local"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">description</span>=<span style="color: #0000ff">"local"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">allowLocal</span>=<span style="color: #0000ff">"true"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">readonly</span>=<span style="color: #0000ff">"false"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">clients</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">namedIndexes</span> <span style="color: #ff0000">defaultIndex</span>=<span style="color: #0000ff">"default"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">indexes</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">add</span> <span style="color: #ff0000">name</span>=<span style="color: #0000ff">"default"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">directoryPath</span>=<span style="color: #0000ff">"C:\EPiServer\VPP\MyEPiServerSite3\Index"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">readonly</span>=<span style="color: #0000ff">"false"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">indexes</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">namedIndexes</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"></</span><span style="color: #800000">episerver.search.indexingservice</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre></pre>
<p>You now need to update the web.config of the EPiServer CMS site that is going to be the client to the installed index service (in the example above, it’s the same site). The file should have an episerver.search element like this:</p>
<pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">episerver.search</span> <span style="color: #ff0000">active</span>=<span style="color: #0000ff">"false"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">namedIndexingServices</span> <span style="color: #ff0000">defaultService</span>=<span style="color: #0000ff">""</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">services</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #008000"><!--<add name="{serviceName}"
</span></pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> baseUri="{indexingServiceBaseUri}"
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> accessKey="{accessKey}"/>-->
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">services</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">namedIndexingServices</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">searchResultFilter</span> <span style="color: #ff0000">defaultInclude</span>=<span style="color: #0000ff">"true"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">providers</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">searchResultFilter</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">episerver.search</span><span style="color: #0000ff">></span></pre></pre>
<p>The “active” attribute needs to be set to true, the “services” element needs to have a child entry identifying the deployed index service and the defaultService attribute needs to identify the service instance:</p>
<pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">episerver.search</span> <span style="color: #ff0000">active</span>=<span style="color: #0000ff">"<b>true</b>"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">namedIndexingServices</span> <span style="color: #ff0000">defaultService</span>=<span style="color: #0000ff">"<b>EPiServerFTS</b>"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">services</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000"><b>add</b></span> <span style="color: #ff0000"><b>name</b></span>=<span style="color: #0000ff">"<b>EPiServerFTS</b>"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000"><b>baseUri</b></span>=<span style="color: #0000ff">"<b>http://localhost:17007/IndexingService/IndexingService.svc</b>"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000"><b>accessKey</b></span>=<span style="color: #0000ff">"<b>93CC0599-3D95-4B30-8F25-4B8A21D289A0</b>"</span><span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">services</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">namedIndexingServices</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">searchResultFilter</span> <span style="color: #ff0000">defaultInclude</span>=<span style="color: #0000ff">"true"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">providers</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">searchResultFilter</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"></</span><span style="color: #800000">episerver.search</span><span style="color: #0000ff">></span></pre></pre>
<p>Note for the “accesskey” attribute I generated a GUID. This value needs to be replicated to the indexing service configuration to authorize access from this client:</p>
<pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">episerver.search.indexingservice</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">clients</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">add</span> <span style="color: #ff0000">name</span>=<span style="color: #0000ff">"<b>93CC0599-3D95-4B30-8F25-4B8A21D289A0</b>"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">description</span>=<span style="color: #0000ff">"local"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">allowLocal</span>=<span style="color: #0000ff">"true"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">readonly</span>=<span style="color: #0000ff">"false"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">clients</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">namedIndexes</span> <span style="color: #ff0000">defaultIndex</span>=<span style="color: #0000ff">"default"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">indexes</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">add</span> <span style="color: #ff0000">name</span>=<span style="color: #0000ff">"default"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">directoryPath</span>=<span style="color: #0000ff">"C:\EPiServer\VPP\MyEPiServerSite3\Index"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">readonly</span>=<span style="color: #0000ff">"false"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">indexes</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">namedIndexes</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">episerver.search.indexingservice</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre></pre>
<h2></h2>
<h2>Populating the index</h2>
<p>Your site should now be ready to start feeding data into the index. You can of course write this code from scratch, but for those of you who want something as a base to get started, you can download the EPiServer FTS Facade code I have developed. This is available on the <a href="http://nuget.episerver.com" target="_blank">EPiServer Nuget</a> feed as EPiServer.Samples.FTSFacade.1.0.nupkg. Those of you using Visual Studio 2008 can get the code <a href="http://world.episerver.com/Code/Paul-Smith/EPiServer-Full-Text-Search-Client-Facade1/">here</a>.</p>
<p>The Nuget package includes:</p>
<p>1. A client facade class which abstracts away some of the work you need to do when talking to the <a href="http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-Framework/EPiServer-Framework-61/EPiServer-Full-Text-Search-Client/" target="_blank">EPiServer Search Client API</a></p>
<p>2. An initialization module which on first run will traverse the CMS page tree in order to push the page’s searchable properties and metadata to the index. The “Documents”, “Global” and “PageFiles” VPP folders are also enumerated to push information about the files stored in them to the index.</p>
<p>3. Events handlers so whenever a page or file is added, updated or removed, the relevant information is pushed to the index</p>
<p>4. A class of extension methods for the EPiServer.Search.IndexResponseItem class to obtain the CMS page or file that the search result is for and methods to generate urls, preview and title text for a search result</p>
<p>4. An Online Center Search Provider to provide search results from the index when searching in EPiServer Online Center</p>
<p>5. A “Re-Index” Online Center Gadget which gives Administrators the ability to trigger a re-index of the site’s pages and files in the same way as the initialization module does. Note that your will need to update the web.config file with the name of your assembly in the episerver.shell/publicModules section:</p>
<pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"><</span><span style="color: #800000">configuration</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">episerver.shell</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">publicModules</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">add</span> <span style="color: #ff0000">name</span>=<span style="color: #0000ff">"ReIndex"</span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #ff0000">resourcePath</span>=<span style="color: #0000ff">"~/FTSFacade/Gadgets/ReIndex/"</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">assemblies</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"><</span><span style="color: #800000">add</span> <span style="color: #ff0000">assembly</span>=<span style="color: #0000ff">"<b>your assembly name goes here</b>"</span> <span style="color: #0000ff">/></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">assemblies</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">add</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">publicModules</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff"></</span><span style="color: #800000">episerver.shell</span><span style="color: #0000ff">></span>
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff"></</span><span style="color: #800000">configuration</span><span style="color: #0000ff">></span></pre></pre>
<p>The next time the site is re-compiled and started the initial indexing should be done. Note that indexing is done asynchronously using a persistent queue so it normally takes a few seconds before you see anything happening in the index folder you specified when you installed the indexing service.</p>
<p>The code in the FTSFacade folder installed can now be modified to suite the needs of your site.</p>
<p>Your site’s search page can now be wired up to the FTSFacade’s CmsSearchHandler.GetSearchResults method:</p>
<pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">public</span> SearchResults GetSearchResults
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">(
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">string</span> searchTerm,
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">bool</span> includeAuthorNamesInSearch,
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">bool</span> includeFilesInSearch,
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">int</span> page,
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">int</span> pageSize
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">);
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"></pre></pre>
<p>The SearchResults object returned is defined as follows:</p>
<pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> SearchResults
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">{
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> Collection<IndexResponseItem> IndexResponseItems { <span style="color: #0000ff">get</span>; }
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> TotalHits { <span style="color: #0000ff">get</span>; <span style="color: #0000ff">internal</span> <span style="color: #0000ff">set</span>; }
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px"> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Version { <span style="color: #0000ff">get</span>; <span style="color: #0000ff">internal</span> <span style="color: #0000ff">set</span>; }
</pre><pre style="background-color: #ffffff; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 12px">}</pre></pre>
<p>You can populate your search results page with the IndexResponseItems. As mentioned above, the FTSFacade includes extension methods for IndexResponseItem to obtain CMS pages and files for the search result.</p>
<p><strong>Disclaimer: The code in the FTS Client Facade is provided “as is” and should be thoroughly tested before being deployed in a commercial project.</strong></p>
<p>The following tech-notes contain more information about the EPiServer FTS Client and Service:</p>
<p><a href="http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-Framework/EPiServer-Framework-61/EPiServer-Full-Text-Search-Client/" target="_blank">Full Text Search Client</a></p>
<p><a href="http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-Framework/EPiServer-Framework-61/EPiServer-Full-Text-Search-Client-Configuration/" target="_blank">Full Text Search Client Configuration</a></p>
<p><a href="http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-Framework/EPiServer-Framework-61/EPiServer-Full-Text-Search-Product-Integration/" target="_blank">Full Text Search Product Integration</a></p>
<p><a href="http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-Framework/EPiServer-Framework-61/EPiServer-Full-Text-Search-Service/" target="_blank">Full Text Search Service</a></p>
<p><a href="http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-Framework/EPiServer-Framework-61/EPiServer-Full-Text-Search-Service-Configuration/" target="_blank">Full Text Search Service Configuration</a></p>
<p>Happy searching!</p>