- siteRegion - The item id value of the region content item
- 1 These items where located in the global / shared location within our site. See part 1 for more information.
- pageNotFound - Redirect the user to a path within our site that contains a "page not found" page if they try and access a page that didn't match their country or region.
<sites>
<site name="MySiteUSA" patch:before="site[@name='website']"
virtualFolder="/"
physicalFolder="/"
rootPath="/sitecore/content"
startItem="/MySite"
database="master"
domain="extranet"
allowDebug="true"
cacheHtml="true"
htmlCacheSize="10MB"
enablePreview="true"
enableWebEdit="true"
enableDebugger="true"
disableClientData="false"
hostName="*MySiteusa.com"
siteRegion="{B4F05B9C-1A40-4B95-AC03-C2115E7CA448}"
pageNotFound="/Page-Not-Found"
/>
<site name="MySiteCA" patch:before="site[@name='website']"
virtualFolder="/"
physicalFolder="/"
rootPath="/sitecore/content"
startItem="/MySite"
database="master"
domain="extranet"
allowDebug="true"
cacheHtml="true"
htmlCacheSize="10MB"
enablePreview="true"
enableWebEdit="true"
enableDebugger="true"
disableClientData="false"
hostName="*MySitecda.com"
siteRegion="{A71B1C90-107B-4740-9913-8E683B019BF7}"
pageNotFound="/Page-Not-Found"
/>
</sites>
Quick note:
This site definition is not meant for a production instance. You obviously want to increase the cache size and turn off things like debugging in production.
Next, we added a simple class that we would use to retrieve an object that contains our site's context as well as the above-mentioned attributes from the configuration xml via it's properties.
When looking at the code below (starting at line 44), you will notice that we are attempting to pull the region object out of cache if it exists. We added a caching layer, because we wanted to ensure that this processor would be as fast as possible. You will see this being used a lot in our processor code that follows.
1: public class SiteRegion
2: {
3: private const string SiteNode = "/sitecore/sites";
4: private XmlNode CurrentSiteNode
5: {
6: get
7: {
8: XmlNode targetParamsNode = Factory.GetConfigNode(SiteNode);
9: var currentSiteContext = Context.Site;
10: foreach (XmlNode childNode in targetParamsNode.ChildNodes)
11: {
12: if (XmlUtil.GetAttribute("name", childNode)
13: .Equals(currentSiteContext.Name, StringComparison.InvariantCultureIgnoreCase))
14: {
15: return childNode;
16: }
17: }
18: return null;
19: }
20: }
21: public string CacheKey
22: {
23: get
24: {
25: return Context.Site.Name;
26: }
27: }
28: public SiteContext Site
29: {
30: get { return Context.Site; }
31: }
32: public string Region
33: {
34: get { return XmlUtil.GetAttribute("siteRegion", CurrentSiteNode); }
35: }
36: public string PageNotFoundUrl
37: {
38: get { return XmlUtil.GetAttribute("pageNotFound", CurrentSiteNode); }
39: }
40: public static string CurrentRegion
41: {
42: get
43: {
44: var siteRegion = CacheHelper.RegionCache.GetObject(Context.Site.Name) as SiteRegion;
45: if (siteRegion == null)
46: {
47: siteRegion = new SiteRegion();
48: CacheHelper.RegionCache.SetObject(Context.Site.Name, siteRegion);
49: }
50: return siteRegion.Region;
51: }
52: }
53: }
Region Resolver Processor
Next, we built out the region resolver pipeline processor to check if an item was meant for a specific country / region.Sitecore provides us with a nice SafeDictionary KeyValuePair object in their PipelineArgs that is useful for adding custom data to pass down the pipeline. This was ideal for us to pass the message along to the Region Page Not Found processor (next up) telling it whether the "page not found" page should be displayed or not (Line 26).
Line's 15 and 16 are performing the check for the context item's region field being set.
1: public class RegionResolver : HttpRequestProcessor
2: {
3: public override void Process(HttpRequestArgs args)
4: {
5: Assert.ArgumentNotNull(args, "args");
6: var showRegionPage = true;
7: var siteRegion = CacheHelper.RegionCache.GetObject(Context.Site.Name) as SiteRegion;
8: if (siteRegion == null)
9: {
10: siteRegion = new SiteRegion();
11: CacheHelper.RegionCache.SetObject(Context.Site.Name, siteRegion);
12: }
13: if (Context.Item != null && siteRegion.Site.HostName.IsNotEmpty())
14: {
15: if (Context.Item.Fields["Region"] != null &&
16: !Context.Item.Fields["Region"].Value.Contains(siteRegion.Region))
17: {
18: showRegionPage = false;
19: }
20: }
21: if (showRegionPage)
22: {
23: return;
24: }
25: //Add entry to safe dectionary to tell region page not found processor to redirect to page not found
26: args.CustomData.Add("hideRegionPage", true);
27: var notFoundProcessor = new RegionPageNotFound();
28: notFoundProcessor.Process(args);
29: }
30: }
Region Page Not Found Processor
One of things that we wanted was to have a "page not found" bit of logic that would be able to handle both our normal page not found / 404's for our sites, as well as those that were not supposed to be displayed for our country / region.So, we wrote a processor that would be able to catch both.
Before looking at the code, there are a few things to note:
- On line 7, we are checking to see if our Region Resolver has told us to hide the context item via the "message" in the safe dictionary object .
- If you have defined custom MVC routes, you would need to check for those in the pipeline and allow them to be processed (line 31).
- This processor would do the job of redirecting visitors to our page not found path, set in our site definition that was mentioned above (line 47).
1: public class RegionPageNotFound : HttpRequestProcessor
2: {
3: public override void Process(HttpRequestArgs args)
4: {
5: Assert.ArgumentNotNull(args, "args");
6: //Check for safe dectionary object indicating that page needs to be "hidden"
7: var hideRegionPage = args.CustomData.ContainsKey("hideRegionPage");
8: if (!hideRegionPage &&
9: (Context.Item != null
10: || Context.Site == null
11: || Context.Site.Name.Equals("shell", StringComparison.CurrentCultureIgnoreCase)
12: || Context.Site.Name.Equals("website", StringComparison.CurrentCultureIgnoreCase)
13: || Context.Database == null
14: || Context.Database.Name.Equals("core", StringComparison.CurrentCultureIgnoreCase)
15: || string.IsNullOrEmpty(Context.Site.VirtualFolder)
16: ))
17: {
18: return;
19: }
20: // The path in the requested URL.
21: var filePath = Context.Request.FilePath.ToLower();
22: if (string.IsNullOrEmpty(filePath)
23: || WebUtil.IsExternalUrl(filePath)
24: || System.IO.File.Exists(HttpContext.Current.Server.MapPath(filePath)))
25: {
26: return;
27: }
28: //Api path checks
29: var uri = HttpContext.Current.Request.Url.AbsoluteUri;
30: if (uri.Contains("sitecore/api")
31: || uri.Contains("api/mycustomroute"))
32: {
33: return;
34: }
35: var siteRegion = CacheHelper.RegionCache.GetObject(Context.Site.Name) as SiteRegion;
36: if (siteRegion == null)
37: {
38: siteRegion = new SiteRegion();
39: CacheHelper.RegionCache.SetObject(Context.Site.Name, siteRegion);
40: }
41: // Send the NotFound page content to the client with a 404 status code
42: if (!string.IsNullOrEmpty(siteRegion.Region) && !string.IsNullOrEmpty(siteRegion.PageNotFoundUrl))
43: {
44: var ctx = HttpContext.Current;
45: ctx.Response.TrySkipIisCustomErrors = true;
46: ctx.Response.StatusCode = 404;
47: ctx.Response.Redirect(siteRegion.PageNotFoundUrl);
48: ctx.ApplicationInstance.CompleteRequest();
49: }
50: }
51: }
Hooking into the HttpBeginRequest Pipeline
The final piece of this puzzle, was to add our new processors to the HttpBeginRequest Pipeline, after the ItemResolver processor.Here is what the config file looked like that would make the magic happen:
<pipelines> <httpRequestBegin> <processor type="MyProject.Library.Pipelines.HttpRequestBegin.RegionResolver, MyProject.Library" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/> <processor type="MyProject.Library.Pipelines.HttpRequestBegin.RegionPageNotFound,
MyProject
.Library" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/> </httpRequestBegin> </pipelines>
Order is important here, because as I mentioned, we want the Region Resolver Processor to be able to tell the Region Not Found Processor whether or not the page should be displayed for the context site.
0 comments:
Post a Comment