Canonical URL for Sitecore Items

Adding Canonical URL for Sitecore Item

SEO (Search Engine Optimization) best practice suggest you to use a single URL for a particular resource. Content with duplicate URLs are given lower rankings by crawlers and instead of using multiple canonical URLs and redirecting to it’s item URL to show in address bar for a particular resource, we should use search engine’s recommended canonical tag. This tag will be added in html head tag. It’s a link tag with rel=”canonical”. This rel property is used as a hint for crawlers to consider the actual URL of the resource and should be used in their indices for ranking purpose. Take a look at sample canonical tag.

<link rel=”canonical” href=”https://example.com/resource” />

How to add a canonical tag for sitecore Item?

To add a canonical tag for your sitecore item, use following code in your page load function in your layout file. This function will call GetCanonicalUrl() function to retrieve canonical URL for the current requested Item. The following code lines will create a tag with rel=”canonical” and add it into html head tag. Remember the head tag should be runat=”server” to access it in code behind.

var canonicalUrl = GetCanonicalUrl();
if (!canonicalUrl.IsNullOrEmpty())
{
var canonicalTag = new HtmlLink { Href = canonicalUrl };
canonicalTag.Attributes["rel"] = “canonical”;
Header.Controls.Add(canonicalTag);
}

Add the following GetCanonicalUrl() function in your same layout file to identify the canonical URL for current Item. Here in addition I have created a setting to ignore any number of trailing slash.

<setting name=”IgnoreTrailingSlash” value=”true”/>

The following function will retrieve the current sitecore item URL, if you are using the aliases in your site then it will resolve them also and will get existing item’s URL as your canonical URL.

/// <summary>
/// Retrieving canonical URL
/// </summary>
/// <returns>Link render in meta data</returns>
private string GetCanonicalUrl()
{
var request = HttpContext.Current.Request;
var uri = new Uri(request.Url, request.RawUrl);
string url = HttpUtility.UrlDecode(uri.AbsolutePath);
string baseUrl = uri.GetComponents(UriComponents.Scheme | UriComponents.Host, UriFormat.Unescaped);
string canonicalUrl = “”;
if (Sitecore.Configuration.Settings.AliasesActive && Sitecore.Context.Database.Aliases.Exists(url))
{
Sitecore.Data.Items.Item targetItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Database.Aliases.GetTargetID(url));
canonicalUrl = string.Format(“{0}{1}”, baseUrl, Sitecore.Links.LinkManager.GetItemUrl(targetItem));
}

 string itemUrl = Sitecore.Links.LinkManager.GetItemUrl(Sitecore.Context.Item);

if (url != itemUrl)
{
canonicalUrl = string.Format(“{0}{1}”, baseUrl, itemUrl);
}

 var ignoreTrailingSlash = Sitecore.Configuration.Settings.GetSetting(“IgnoreTrailingSlash”);

if (!canonicalUrl.IsNullOrEmpty())
{
if (!ignoreTrailingSlash.IsNullOrEmpty() && ignoreTrailingSlash.Equals(“true”))
{
return canonicalUrl.TrimEnd(‘/’);
}
else
{
return canonicalUrl;
}
}
return null;
}

 

GlassView with strongly typed context item and model

I have recently started working with Sitecore MVC using glass mapper. In my project I have several view renderings where I need to access the details of datasource item and as well as current context item. I knew that GlassView class takes the type of model as generic type argument and then if there is data source mentioned for the view rendering then it creates the model from data source item otherwise it creates the model from current context item.

Here my requirement was to use both (model as my data source and current context item) as strongly typed objects in my view renderings. Hence I started to look into the GlassView class and found some interesting public methods and properties that are useful to developers:

Properties:

S.No. Name Purpose
 1. ContextItem Returns the item specified by the current context item.
 2. DataSourceItem Returns the item specificed by the data source only. Returns null if no datasource set.
 3. LayoutItem Returns either the item specified by the DataSource or the current context item.

 

Methods:

S.No. Name Purpose
 1. GetContextItem Returns the Context Item as strongly typed.
 2. GetDataSourceItem Returns the Data Source Item as strongly typed.
 3. GetLayoutItem Returns the Layout Item as strongly typed.
 4. GetRenderingParameters Returns the parameters as strongly typed.

 

As there is out of box support available for strongly types in GlassView for data source and as well as context item so we started with defining a variable in our view renderings as below:

@inherits GlassView<MyDataSource>@{

var pageItem = GetContextItem<MyContentItem>();

}

<div>@pageIem.Title</div>

 

But this was tedious to verify that developers in my team were using this strongly typed approach instead of using the context item directly so I created small abstract class by inheriting from GlassView:

public abstract class CustomGlassView<TModel, TPageItem> : GlassView<TModel>where TModel : classwhere TPageItem : class

{

private TPageItem pageItem;

public TPageItem PageItem

{

get { return pageItem ?? (pageItem = this.GetContextItem<TPageItem>()); }

}

}

 

Now in my view renderings I am able to put the two strongly types for my view class as below:

@inherits CustomGlassView<MyDataSource, MyContentItem><div>@PageItem.Title</div>

This helps a lot in working with view renderings and the same can be done for parameters.

Thanks.

 

Extend Sitecore Dedicated Publishing Log

There is now a separate publishing log in Sitecore 7.2. Any operations happening as part of the publishing pipeline will now log to this file. This includes AUDIT entries. Mainly for backwards compatibility reasons, the main log.txt will still show main publishing related entries.

Challenge
Extend the Sitecore dedicated publishing log to show main publishing entries so that it’s easy for website admin to review publishing actives

Solution
We can can use Sitecore’s “Sitecore.Publishing.Diagnostics.PublishingLog” to write any custom information into publishing log file. In our case we have followed below steps to achieve it -

  • Create a class with the name let’s say  “PublishLogExtensions.cs” and inherit it from PublishItemProcessor
  • Code of this class looks something like below

public class PublishLogExtensions : PublishItemProcessor
 {
 public override void Process(PublishItemContext context)
 {
 Assert.ArgumentNotNull(context, "context");
 Assert.ArgumentNotNull(context.PublishOptions, "context.PublishOptions");
 Assert.ArgumentNotNull(context.PublishOptions.SourceDatabase, "context.PublishOptions.SourceDatabase");
 Assert.ArgumentNotNull(context.PublishOptions.TargetDatabase, "context.PublishOptions.TargetDatabase");
 Assert.ArgumentCondition(!ID.IsNullOrEmpty(context.ItemId), "context.ItemId", "context.ItemId must be set!");
 Assert.ArgumentNotNull(context.User, "context.User");

Database sourceDatabase = context.PublishOptions.SourceDatabase;
 Database targetDatabase = context.PublishOptions.TargetDatabase;
 ID itemId = context.ItemId;
 string userName = context.User.Name;
 Item item = context.PublishHelper.GetItemToPublish(context.ItemId);

if (item.HasContextLanguageVersion())
 {
 PublishingLog.Info("Publishing Path :" + item.Paths.FullPath.ToString() + ", ID: " + item.ID.ToString() + ", Item Language Verion: " + item.Language.Name + ", By User : " + userName);
 }
 }
}
  • Above code is will append below information to publishing log about items that gets published
    - Item Path
    - Item GUID
    - Language version
    - User who published this item
  • Finally you need to create a patch config file to hook this class to pipeline and config file looks like below
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<publishItem>
<processor type="Class,Assembly"                    patch:after="processor[@type='Sitecore.Publishing.Pipelines.PublishItem.UpdateStatistics, Sitecore.Kernel']" />
</publishItem>
</pipelines>
</sitecore>
</configuration>

After following above steps you should be able to see below entries on publishing log

publishing-log