What is Sitecore Azure

What is Sitecore Azure?

If you have come to this post, I assume that you’re a beginner with the terms Azure and Sitecore Azure. When you don’t want to be in charge of your own physical kit, you choose to deploy to something like Azure. This includes stuff like hardware, operating system, network load balancing and things that have nothing much to do with the core application.

Before moving forward to Sitecore Azure, let’s have a brief overlook at Microsoft Azure’s interface.

Microsoft Azure Interface

The Management Portal for Microsoft Azure looks somewhat like this:

sitecore-azure-1

Here is how the services look like:

Sitecore-Azure-2

Sitecore Azure

Now that you saw how the interface for Microsoft’s much hyped Azure looks like, let us come to using Azure for Sitecore.

We all know that Sitecore itself has had a lot of success as an ‘on-premises’ software solution and content management system that facilitates on-premises deployments. Sitecore, with its advanced personalization capabilities and marketing automation, gives all the power to deliver relevant content or ecommerce offers that their audiences want to see.

And then, deploying #Sitecore on #Azure makes it even better as #Sitecore Azure.

Sitecore-Azure-3

Implementing a cloud version of Sitecore, using Azure, is definitely a great idea because of the many added benefits you get while using Sitecore on Azure or Sitecore Azure. While using Azure to enjoy the cloud version of Sitecore, you get to choose from cloud platform services like IaaS and PaaS and increase your productivity. Hybrid cloud solutions open new IT options for you without adding on to the complexity of the whole thing. Sitecure Azure makes it way easier and way more efficient to build applications and dal with stuff like data storage, backup, recovery, etc. It’s not only scalable but economical with its per-minute billing to ensure that you pay only for what you use.

Sitecore-Azure-4

Going for Microsoft Azure with Sitecore only brings you more flexibility, superior infrastructure, predictable costs, enhanced performance and opportunity to review workload.

Understanding cloud platform services: IaaS, PaaS, SaaS

When using Sitecore on Azure, it is important to know what the cloud platform services like IaaS, PaaS and SaaS, have to do with Azure and services. Moving to the cloud is a very much hyped things but most remain confused about the various services and what each of them means for organizations. Here we shed some light on the differences between them.

IaaS, PaaS and SaaS are basically three approaches to deploy or consume ‘cloud’ applications.

What do IaaS, PaaS, SaaS stand for?

IaaS stands for Infrastructure as a Service

PaaS stands for Platform as a Service

SaaS stands for Software as a Service

Sitecore-Azure-5

The three of them are not services in themselves. These are just choices that you make about the level of abstraction you require. You can choose a final setup with the elements of any of these or your final Azure setup may also be a combination of IaaS, PaaS and SaaS elements.

Major Difference between IaaS, PaaS, SaaS

IaaS: IaaS or infrastructure as a Service means that you are handling over the responsibility of machines and network to a cloud platform. So, basically, with IaaS approach, you choose virtual machines over physical machines. You can still log in to the virtual machines and configure IIS, mess around in MSSQL or look at your web root.

The basic infrastructure including hardware, networking, servers is supplied by a service provider. A central, knowledgeable group holds the responsibility for infrastructure.

Sitecore-Azure-6

PaaS: However, while using PaaS or Platform as a Service, you are handling over the responsibility for SQL management to the cloud platform using its services. The 5 Azure PaaS services in Sitecore Azure module are:

1. Cloud Service

2. Storage Service

3. SQL Databases Service

4. Traffic Manager Service

5. Cache Service

Under PaaS approach, you let the cloud platform manage all these aspects.

Application platform components like persistence, runtime, frameworks, etc. are provided by a third-party. Private PaaS cloud offered as service provider and Public PaaS cloud that is offered by a third-party company.

The application development organization builds their solutions on a known, stable platform with a consistent set of components.

With PaaS, you need only to develop the application or business level logic.

The application teams and companies would have to pay a fee for this service.

Sitecore-Azure-7

SaaS: Finally, SaaS or Software as a Service takes over all your responsibility. All you need to do is to sign up, SaaS takes care of everything else. For example, Google Apps for Work is a SaaS.

In Saas, business or consumer software application is provided by a third party. Rather than calling it as Software as a Service, you can also call it Application as a Service.

Salesforce.com CRM and SAP SuccessFactors are a good example of SaaS in the business world while Facebook, Gmail and My Yahoo are consumer focused SaaS examples.

Customers typically pay some subscription, either time-based like monthly or yearly, or service-based like pay for n no. of messages, volume of photos, etc.

Where the three approaches should be used?

Who should use IaaS?

IaaS achieves cost-benefit gains at physical infrastructure level. It is a widely applicable approach. Small and medium sized organizations use IaaS from a third-party to alleviate the need for expensive infrastructure and resources in-house. Publc IaaS is used by large organizations to share up internal data centers by taking on additional load during peak periods or is used for temporary purposes like testing.

Who should use PaaS?

PaaS is applicable to organizations who develop their own comprehensive applications. PaaS drives consistency by enforcing a common application framework. It also provides access to a common development platform across the globe. As an organization, you must have knowledge of:

1. The programming languages you need to support

2. Application services/frameworks

3. Accessibility by development and the user base

4. Security policies and regulations

Who should use SaaS?

SaaS can be used by organizations of any size and in any type of business. Any organization can be used as a single SaaS application while large organizations can make use of several different SaaS applications like Salesforce.com for CRM, Office365 or Box.net.

Other Important Terms

Once you start with Sitecore Azure, there are two very important terms in Sitecore Azure:

1. Azure Search – Sitecore

2. Redis Cache – Sitecore

Check out my next posts to know more about what each one of them means and what they have to do with Sitecore Azure.

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

 

Google Events Integration with Sitecore

Purpose of this module

With the help this module user can easily create onclick events on different elements like anchors. Content editor can edit arguments of google events directly from Sitecore without opening code file.

What is google event tracking

  • Event Tracking is a method available in the ”ga.js” tracking code that we can use to record user interaction with website elements.
  • To track any event we need to call “on click” attribute with tracking arguments on anchor tag.
  • The specification for the _trackEvent() method is:
    _trackEvent(category, action, opt_label, opt_value, opt_noninteraction)

How this module work

To use this module please follow below steps –

  1. Install the provided Sitecore package. Package name is “GoogleEventTracking-1.0.zip”
  2. Update your web.config file to replace standard GetLinkFieldValue processor with the custom one
    1. Standard GetLinkFieldValue –
      <processor type=”Sitecore.Pipelines.RenderField.GetLinkFieldValue, Sitecore.Kernel” />
    2. Custom (Replace standard with this)
      <processor type=”Oasis.GoogleEventTracking.CustomGetLinkFieldValue, Oasis.GoogleEventTracking” />
  3. Once above setting is updated open Sitecore content tree. You can add google events to anchors using two fields type in Sitecore
    1. Rich Text Editor
    2. General Link
  4. To insert google event using Rich Text Editor follow below steps –
    1. Select any item which Contains RTE field.
      sitecorecode-GET1
    2. Select show editor option highlighted in above image. In RTE type any text like “Google Event”. Select this text and insert link on this text by selecting Hyperlink Manger from the tools of RTE.
      sitecorecode-GET2
    3. Select Analytics Events tab in Hyperlink Manager Popup and enter events details in the form as shown in below image
      sitecorecode-GET3
    4. Click ok when you are done
    5. Accept the changes in RTE and save the Sitecore item. This event will look like below in html of page. You can see this in Sitecore as well by clicking on HTML tab of RTE
      <a onclick=”_gaq.push(['_trackEvent','Category','Action','Label',1]);”>goolge event</a>
  5. To insert google event using General Link Field Type follow below steps –
    1. Select any item which Contains General Link field.
    2. Inset link detail by selecting Insert option Link
      sitecorecode-GET4
    3. To crate google event click on Google Event option highlighted in above image. It will open a popup with the form. Fill the form with proper argument for google event
      sitecorecode-GET5
    4. Click ok when you are done
    5. Save the Sitecore item. This event will look like below in html of page. You can see this in Sitecore as well by selecting raw value checkbox
      onclick=”_gaq.push(['_trackEvent','Category','Action','Label',Value]);”

Note – This package is based on assumption that webpages are already rending google event tracking js provided by google.

Sitecore Package is available on Sitecore market marketplace Click here to download

 

How to use Sitecore dictionary domain in your Sitecore project

Purpose of this article is to explain new developers how to properly used dictionary domain. Good technical explanation of dictionary domain can be found here

Sitecore 6.6 introduces the concept of dictionary domains. In any database, you can create any number of dictionary domains, which are items based on the System/Dictionary/Dictionary Domain data template. On each dictionary domain you can specify the fallback domain. If certain key is not found in site specific dictionary domain then program will search key in fallback domain.

Follow below steps to use dictionary domain in your Sitecore website:

  1. Based on your Sitecore content tree structure create item of template “/configuration/sitecore/sites/site”. We normally use below structure in our project where we organize different sites in different folders in case of multi-site environment (you can use same structure for single website as well) and each site contains 2 node under it. One is used to store global setting (Used to store all global meta data used on different pages of website like header, footer) of this website and other is home item under which all pages of website get store
                Sitecore
                            Content
                                        Site One Node
                                                    Global Settings
                                                                Sample Dictionary Domain
                                                    Home
  2. Under dictionary domain folder you can add dictionary entries. You can create folder as well under dictionary domain to organize dictionary entries in logical structure. You can also nest dictionary domains
  3. Once dictionary domain structure is created in Sitecore content tree next step is to use dictionary in layout or sublayouts. There are two ways to use Sitecore dictionary

         i.    Define Dictionary domain in Sites setting in web.config.
                 a.  With this approach open web.config file and add dictionary domain attridute                                                                         (dictionaryDomain=”Sample Dictionary Domain”) to your  managed sites                                                                       (/configuration/sitecore/sites/site)
                b.  dictionaryDomain attribute in web.config could be name of dictionary domain or GUID of dictionary                                  domain
                c.  Use below code to get dictionary phrase using key in code
                                Sitecore.Globalization.Translate.Text(“key”);  

         ii.    You can also define dictionary domain in code as shown below                                                                                                  Sitecore.Globalization.Translate.TextByDomain(“Domian”,”Key”);

Must Read:

http://www.sitecore.net/Learn/Blogs/Technical-Blogs/John-West-Sitecore-Blog/Posts/2012/11/Sitecore-ASPNET-CMS-6-6-Features-Dictionary-Domains.aspx

As with any other pipeline, you can add processors to, remove processors from, and override processors in the getTranslation pipeline. For example, when porting an existing ASP.NET site into Sitecore, you could add a processor that looks for entries in the resource files from the old site if no entry for a key exists in the Sitecore dictionary system.

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.

 

Add new tab in General Link field type of Sitecore

Task: Do you want to extend general link field type to include one more tab like create tab for tracking google events?

You can achieve this by following below steps:

1: Create a new item (Menu item) in core database under (/sitecore/system/Field types/Link Types/General Link/Menu).

2: Create a Class Library project.

3: Create a config file and write namespace, assembly and prefix.

<configuration xmlns:x="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <controlSources>
            <source mode="on" namespace="GoogleAnalyticsLib" assembly="GoogleAnalyticsLib" prefix="content"/>
        </controlSources>
    </sitecore>
</configuration>

4: Update the Control field of General Link Item by prefix (from config file) colon (:) class name which are responsible for handle message.

content:GoogleEvent

5: Write the content in message field of newly created item(Menu Item).

Ex. contentlink:GoogleEvent(id=$Target).

6: Create a class for open new tab in form window and inherit by Link class (Sitecore.Kernel).

Code -

public class GoogleEvent : Link
{
    // Sets the value.
    public void SetValue(string value)
    {
        Value = value;
    }
    // Gets the value.
    public string GetValue()
    {
        return Value;
    }
    // Handles the message.
    public override void HandleMessage(Sitecore.Web.UI.Sheer.Message message)
    {
        base.HandleMessage(message);
        if (message["id"] != ID || String.IsNullOrEmpty(message.Name))
        {
            return;
        }
        // in future can be some other message handling
        switch (message.Name)
        {
            case "contentlink:GoogleEvent":
            var url = new UrlString(UIUtil.GetUri("control:GoogleTrackinglink"));
            base.Insert(url.ToString());
            return;
        }
        if (Value.Length > 0)
        {
            SetModified();
        }
        Value = String.Empty;
    }
}
Note:The Message name should be same as new menu itemmessagefield content.

7: Create a XAML file with required fields.

Code of XAML for google event tracking:

<GoogleTrackinglink>
    <FormDialog
    Icon="Network/32x32/link.png"
    Header="Google Link"
    Text="Select the item that you want to create a link to and specify the appropriate properties."
    OKButton="OK" />
        <script type="text/javascript" language="javascript">
            function isNumberKey(evt)
            {
                var charCode = (evt.which) ? evt.which : event.keyCode;
                if (charCode > 40)
                if(charCode &lt; 48 || charCode > 57)
                {
                       return false;
                }
                return true;
            }
        </script>
        <CodeBeside Type="GoogleAnalyticsLib.GoogleEventLinkForm,GoogleAnalyticsLib"/>
        <DataContext ID="GoogleLinkDataContext"/>
        <GridPanel Columns="2" Width="100%" Height="100%" CellPadding="4" Style="table-layout:fixed">
            <Scrollbox Width="256" Height="100%" Background="transparent" Border="none" GridPanel.VAlign="top" GridPanel.Width="256">
                    <GridPanel CellPadding="2" Columns="2">
                            <Literal Text="Category:" GridPanel.NoWrap="true"/>
                            <Edit ID="Category"/>
                            <Literal Text="Action:" GridPanel.NoWrap="true"/>
                            <Edit ID="Action"/>
                            <Literal Text="Label:" GridPanel.NoWrap="true"/>
                            <Edit ID="Label1"/>
                            <Literal Text="Value:" GridPanel.NoWrap="true" />
                            <Edit ID="Value1" onkeypress="javascript:return isNumberKey(event);"/>
                            <Literal Text="Non-interaction:" GridPanel.NoWrap="true"/>
                            <Checkbox ID="Noninteraction"/>
                    </GridPanel>
            </Scrollbox>
        </GridPanel>
    </FormDialog>
</GoogleTrackinglink>
8: Create a class to handle the Dialog form fields
Code -
namespace GoogleAnalyticsLib
{
    public class GoogleEventLinkForm:DialogForm
    {
        protected Edit Category;
        protected Edit Action;
        protected Edit Label1;
        protected DataContext GoogleLinkDataContext;
        protected Edit Value1;
        protected Checkbox NonInteraction;
        Database masterDb = Sitecore.Configuration.Factory.GetDatabase("master");
        protected override void OnLoad(EventArgs e)
        {
            Assert.ArgumentNotNull(e, "e");
            base.OnLoad(e);
            if (Sitecore.Context.ClientPage.IsEvent)
            {
                return;
            }
            this.GoogleLinkDataContext.GetFromQueryString();
            string xml = StringUtil.GetString(new string[] { WebUtil.GetQueryString("va"), "" });
            string queryString = WebUtil.GetQueryString("ro");
            XmlDocument document = XmlUtil.LoadXml(xml);
            System.Xml.XmlNode node = document.SelectSingleNode("/link");
            string attribute = XmlUtil.GetAttribute("category", node);
            string str4 = XmlUtil.GetAttribute("action", node);
            string str5 = XmlUtil.GetAttribute("label1", node);
            string str6 = string.Empty;
            string str7 = XmlUtil.GetAttribute("value1", node);
            string str8 = XmlUtil.GetAttribute("noninteraction", node);
            this.Category.Value = attribute;
            this.Action.Value = str4;
            this.Label1.Value = str5;
            this.Value1.Value = str7;
            this.NonInteraction.Value = str8;
            string str11 = XmlUtil.GetAttribute("id", node);
        }
        // Handler for ok button click
        protected override void OnOK(object sender, EventArgs args)
        {
            Assert.ArgumentNotNull(sender, "sender");
            Assert.ArgumentNotNull(args, "args");
            this.GoogleLinkDataContext.GetFromQueryString();
            string xml = StringUtil.GetString(new string[] {WebUtil.GetQueryString("va"), ""});
            string queryString = WebUtil.GetQueryString("ro");
            XmlDocument document = XmlUtil.LoadXml(xml);
            System.Xml.XmlNode node = document.SelectSingleNode("/link");
            Packet packet = new Packet(document);
            SetAttribute(packet, "category", this.Category.Value);
            SetAttribute(packet, "action", this.Action.Value);
            SetAttribute(packet, "label1", this.Label1.Value);
            SetAttribute(packet, "value1", this.Value1.Value);
            SetAttribute(packet, "noninteraction", this.NonInteraction.Value);
            if (!string.IsNullOrEmpty(this.Category.Value) && !string.IsNullOrEmpty(this.Action.Value))
            {
                // get event tracking code from global header
                Item itm = ProServ.Globals.Common.GetGlobalHeader(masterDb);
                string onClickValues = itm.Fields["Google Event Tracking Code"].Value;
                string strParameters = null;
                strParameters = this.Category.Value;
                strParameters = strParameters + "," + this.Action.Value;
                if (!string.IsNullOrEmpty(this.Label1.Value))
                {
                    strParameters = strParameters + "," + this.Label1.Value;
                }
                if (!string.IsNullOrEmpty(this.Value1.Value))
                {
                    strParameters = strParameters + "," + this.Value1.Value;
                }
                if (!string.IsNullOrEmpty(this.NonInteraction.Value) && this.NonInteraction.Value=="1")
                {
                    strParameters = strParameters + "," + "true";
                }
                onClickValues = onClickValues.Replace("$EventDetail$", strParameters);
                string finalValues = onClickValues;
                SetAttribute(packet, "onclick", finalValues);
            }
            Sitecore.Context.ClientPage.ClientResponse.SetDialogValue(packet.OuterXml);
            base.OnOK(sender, args);
        }
        // set attribute name and value of controls
        private static void SetAttribute(Packet packet, string name, string value)
        {
            Assert.ArgumentNotNull(packet, "packet");
            Assert.ArgumentNotNullOrEmpty(name, "name");
            Assert.ArgumentNotNull(value, "value");
            packet.SetAttribute(name, value);
        }
    }
}