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;
}

 

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

 

SITECORE CDN CONNECTOR for Sitecore 7.x

Challenge – Existing Sitecore CDN connector available at click here is not working with Sitecore 7.2

https://marketplace.sitecore.net/en/Modules/Sitecore_CDN_Connector.aspx

Solution

Follow below steps –

  1. Download source code from github https://github.com/NTTDATA/SitecoreCDN
  2. Open this source code in visual studio. Go to project properties and update target framework to .Net framework 4.5zProject Property
  3. Update reference of below DLLs in this project to use new  DLLs from Sitecore 7.2
    1. HtmlAgilityPack
    2. Sitecore.Kernel
  4. Build the project and you should be all set to use this connector

In case of multisite environment you have to take care of 2 points –

  1. Update site name in sites patch define in config file SitecoreCDN.config

<sites>
<sitename="yoursitename">
<patch:attributename="cdnHostName">imagerepo</patch:attribute>
</site>
</sites>

  1. If you are using SiteDefinition.config to manage multiple sites then make sure SitecoreCDN.config execute after SiteDefinition.config file. One thing you can do is update name of SitecoreCDN.config by adding prefix “Z” like ZSitecoreCDN.config

CDN Sitecore connector package for Sitecore 7.2 can be downloaded from here

Conclusion – Existing CDN Sitecore connector contains reference of old DLLs  and .net framework. By updating reference it will start working with latest Sitecore version

Custom Form Editor in Web Form For Marketer | Sitecore

Some time we encounter situation where we are using WFFM to create form but because of custom requirement we don’t want to use Sitecore in-built send mail action instead we want to write our own custom action.
In-built Send Email action editor GUI looks like below -

image001

Figure – 1

We want to update this GUI so that we can hide To, CC, BCC fields because we are not using these fields. Its always good to hide extra fields so that content editor will not get confuse. Now how can we do that? Answer is – follow below steps and you are all set:
1. There is an editor associated with inbuild send mail action. Sheet UI of this editor can be found at “website root\sitecore\shell\Applications\Modules\Web Forms for Marketers\Dialogs\Action Editor\SendEmail.xml” . UI of this editor is shown in fiture-1. First step is to create a copy this UI with name “CustomSendEmail.xml”
2. Open CustomSendEmail.xml file and change tag <SendMail.Editor> to <CustomSendEmail>

image002

3. In CustomSendEmail.xml file addd visible =”false” in below tags. This will hide to, CC, BCC fields from editor

image004

Please note – Right now we are only using existing editor code behind class that why we are only hiding controls not completely removing them from xml file. In next post I will explain you how you can create new class for editor

4. Now in Sitecore create a new custom action with name CustomSendEmail under /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions. Update class name for custom action as shown in below image

image006

5.Now update editor name in field “Editor” under section “Editor” with value “Control: CustomSendEmail”. As shown in below image

image008

6. Save Custom save action item and you are all set.
7. Now assign CustomSendMail action to your form and UI of CustomSendMail will look like below.\

image010

Figure-2

Creating a custom action is 2 step process -
1. Create a class under your project in visual studio
2. Create custom action under “/sitecore/system/Modules/Web Forms for Marketers/Settings/Actions” and register custom action created in first step under it.

Custom send email action with attachments in sitecore web form for marketer

Steps to send mail with attachment in web form for marketer by custom action

1. Create a custom send mail item under “/sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions” in sitecore.

2. Create custom class for this action and inherited by ‘ISaveAction, ISubmit’.

3. Enter Assembly name in Assembly field and class name in Class field of custom send mail item.

4. Create a custom send mail function (please note that tricky part of below code is getting reading attachment from media library)

// send email with attachments
public static bool SendEmailWithAttachments(string To, string From, string Subject, string Message, string atchmnt1, string atchmnt2, string atchmnt3) {
bool result = true;
try {
MailMessage mailMsg = new MailMessage();
// mailaddress of sender
MailAddress mailFrom = new MailAddress(From);
mailMsg.From = mailFrom;
// mail addresses for recipients
string[] mailAddressList = To.Split(',');
foreach (string str in mailAddressList) {
try {
MailAddress mailTo = new MailAddress(str.Trim());
mailMsg.To.Add(mailTo);
}
catch { }
}
mailMsg.Subject = Subject;
mailMsg.Body = Message;
mailMsg.IsBodyHtml = true;
if (!string.IsNullOrEmpty(atchmnt1)) {
mailMsg.Attachments.Add(ReadAttachment(atchmnt1));
}
if (!string.IsNullOrEmpty(atchmnt2)) {
mailMsg.Attachments.Add(ReadAttachment(atchmnt2));
}
if (!string.IsNullOrEmpty(atchmnt3)) {
mailMsg.Attachments.Add(ReadAttachment(atchmnt3));
}
var smtp = new SmtpClient(Sitecore.Configuration.Settings.MailServer, Sitecore.Configuration.Settings.MailServerPort);
smtp.Send(mailMsg);
}
catch {
result = false;
}
return result;
}
// get attachement by media item id
public static Attachment ReadAttachment(string value) {
MediaItem mediaItem = null;
ItemUri itemUri = ItemUri.Parse(value);
if (itemUri != null) {
Item item = Database.GetItem(itemUri);
if (item != null) {
mediaItem = new MediaItem(item);
}
}
// create attachment using media item properties
Attachment attachment = new Attachment(mediaItem.GetMediaStream(), string.Join(".", new string[] { mediaItem.Name, mediaItem.Extension}), mediaItem.MimeType);
return attachment;
}

 

Import Export Content in Different Languages in Sitecore

Below image show content of a sample item in English language in fresh Sitecore instance.

Export

Now we will export it using Export language to a file wizard and Sitecore generates below XML

<sitecore>
<phrase path="/sitecore/content/Home/Sample Item" key="Sample Item" itemid="{9B53F11E-C767-4939-9587-E81FD1C401B1}" fieldid="Title" updated="20120921T155939">
<en>Sample Item</en>
</phrase>
<phrase path="/sitecore/content/Home/Sample Item" key="Sample Item" itemid="{9B53F11E-C767-4939-9587-E81FD1C401B1}" fieldid="Text" updated="20120921T155939">
<en>&lt;p&gt;Mitigate your risk and speed up time to market with our integrated solutions.&lt;/p&gt;</en>
</phrase>
</sitecore>

As we can see we have English content between the <en> tag.

Now, we will add content in some other languages.

It’s a very simple process, as we need to add content between the language specfic tag e.g., if we want to have content in French then we can add <fr> tag and we can add French content here.

Please note that we have to use language specific tags to enter content in that language. If you enter content of different language under<en>it will replace English language content for an item during import.

Now we have updated this xml to include translated content from different languages and produce below XML.

<sitecore>
<phrase path="/sitecore/content/Home/Sample Item" key="Sample Item" itemid="{9B53F11E-C767-4939-9587-E81FD1C401B1}" fieldid="Title"
updated="20120921T155350">
<en>Sample Item</en>
<fr>Article échantillon</fr>
<da>Sample Item</da>
<es>Muestra del artículo</es>
<ja-JP>サンプルアイテム</ja-JP>
</phrase>
<phrase path="/sitecore/content/Home/Sample Item" key="Sample Item" itemid="{9B53F11E-C767-4939-9587-E81FD1C401B1}" fieldid="Text"
updated="20120921T155350">
<en>&lt;p&gt;Mitigate your risk and speed up time to market with our integrated solutions.&lt;/p&gt;</en>
<fr>&lt;p&gt;Atténuer les risques et d'accélérer les délais de commercialisation de nos solutions intégrées.&lt;/p&gt;</fr>
<da>&lt;p&gt;Mindske din risiko og fremskynde time to market med vores integrerede løsninger.&lt;/p&gt;</da>
<es>&lt;p&gt;Mitigar el riesgo y acelerar el tiempo de comercialización de nuestras soluciones integradas.&lt;/p&gt;</es>
<ja-JP>&lt;p&gt;あなたのリスクを軽減し、当社の統合ソリューションを市場に出すまでの時間をスピードアップ。&lt;/p&gt;</ja-JP>
</phrase>
</sitecore>

When we imported above XML file in Sitecore it will automatically create different language version of Sitecore item present in file. After importing above xml file in Sitecore we can see 4 more languages items gets created for Sample Item. In below image we have select Japanese Version of Sample Item.

Import

Please note that if you don’t specify the language tag<fr> </fr>Sitecore will not detect the language while import.