SitecoreBlog - navavayas.com
  • HOME
  • ABOUT
  • navavayas

Extension method to get sitecore start item or home item from any itemId on sitecore multisite environment

Tags:     extension-method, sitecore-multisite, Sitecore

When we are in website context it's easy to get context item like below: 

var currentItem = Sitecore.Context.Item;

  

When you are in website context it's easy enough to get Home item on the site as below: 

var currentSiteHomeItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);

 

When you are not in website context, its bit tricky to get HomeItem based on Item id or path of the item. We can get this kind of scenario when we are in Sitecore shell site context and have Sitecore multiple sites.  But still, it's possible, below are the extension methods I have written for an item.

public static Item GetSiteStartItem(this Item contextItem)
{
	return contextItem?.Database.GetItem(contextItem.GetSiteStartItemPath(), Context.Language);
}

public static string GetSiteStartItemPath(this Item item)
{
	var siteConfig = item.GetSiteInfo();
	if (siteConfig != null)	
        {
		var rootPath = siteConfig.RootPath;
		var startItemPath = siteConfig.StartItem;
		return string.Concat(rootPath, startItemPath);
        }     
return null;
}
public static SiteInfo GetSiteInfo(this Item item)
{
	var siteInfoList = Factory.GetSiteInfoList();
	SiteInfo currentSiteinfo = null;
	var matchLength = 0;
	foreach (var siteInfo in siteInfoList)	{
		if (!item.Paths.FullPath.StartsWith(siteInfo.RootPath,
 StringComparison.OrdinalIgnoreCase) ||			siteInfo.RootPath.Length <= matchLength) continue;
		matchLength = siteInfo.RootPath.Length;
		currentSiteinfo = siteInfo;
	}
	return currentSiteinfo;
}

Hope this helps someone!

READ MORE

Sitecore get all base templates for an item recursively

Tags:     sitecore-templates, sitecore

It's a good approach to divide all your Sitecore templates into small chunks. This helps reusability of the templates.

What I normally do is create a template called "page" derived from a standard template and all my page templates will be derived from Page.

similarly, I create a template called "content" derived from a standard template and all content templates will be derived from the content template.

Sometimes we need to find out all the base templates associated with a single template.

This is how we can achieve this:

TemplateManager.GetTemplate(currentVersionToPublish)
                        .GetBaseTemplates()
                        .Any(template => template.ID == new ID("{TemplateIDTocompare}"));

 

 

GetBaseTemplates method will return all bases templates for the template recursively.

 public TemplateList GetBaseTemplates()
    {
      List<Template> result = new List<Template>();
      Set<ID> processed = new Set<ID>();
      this.AddBaseTemplates(result, true, processed);
      return new TemplateList((IEnumerable<Template>) result);
    }

 

 

2nd parameter true in AddBaseTemplate specifies deep.

 

Hope this helps.

 

 

READ MORE

Sitecore publish at specific time

Tags:     sitecore-scheduling, sitecore-autopublish, sitecore-events, sitecore-publishing, Sitecore

Publishing Sitecore items at a specific time increase performance of the Sitecore website, rather than going without of box Sitecore functionality of running an agent every x number of minutes to do publish.

Background:

Sitecore provides a functionality to auto-publish items based on the publishable from and publishable to dates set on the item.

More info on this can be found in this blog post. I would recommend reading that blog post before going forward.

However, there is a problem with this approach.

An agent which runs for every x number of minutes will clear the cache, this will be a huge impact on the website. So to solve this problem we need to run publish only when needed.

Solution:

To achieve this we are going to follow below steps:

  1. On item save, we are going to check if the item has publishing restrictions set.

  2. If the item has publishing restrictions. We create two scheduling tasks one for start date time and other for end date time.

  3. These scheduled tasks will be triggered every one minute.

  4. The scheduled tasks will trigger the publish only on specific time which was set in the publishing restrictions.

  5. Delete the scheduled task on a successful run.

Creating schedule task on item save:

For creating and implementing an item save the event, I would recommend reading this blog post

Create below methods on item event handlers:

 /// <summary>
        /// creates scheduled task item(which will publish this item) if this item is set to publish in future.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected void CreateScheduleTaskToPublish(object sender, EventArgs args)
        {
            var item = (Item)Event.ExtractParameter(args, 0);
            if (item != null && item.Paths.IsContentItem)
            {
                var itemName = item.ID.ToShortID();
                if (item.Publishing.ValidFrom != DateTime.MinValue && item.Publishing.ValidFrom > DateTime.UtcNow)
                {
                    CreateScheduleItem(item, string.Concat(itemName, "-", "start"));
                }
                if (item.Publishing.ValidTo != DateTime.MaxValue)
                {
                    CreateScheduleItem(item, string.Concat(itemName, "-", "end"));
                }
            }
        }

Above method creates scheduling tasks on item save the event. U need to hook this method to item save event like below:

<events>
      <event name="item:saved">
        <handler type="sitecoreblog.tools4geeks.Data.Items.ItemEventHandler, sitecoreblog.tools4geeks" method="CreateScheduleTaskToPublish"/>
      </event>
</events>

Below is the method which creates the scheduled task:

        /// <summary>
        /// This creates a custom schedule task item.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="itemName"></param>
        private void CreateScheduleItem(Item item, string itemName)
        {
            using (new SecurityDisabler())
            {
                var masterDb = Sitecore.Configuration.Factory.GetDatabase("master");
                var parentItem = masterDb.GetItem(ItemIds.AutomaticPublishingFolder);

                var currentScheduleItem =
                    parentItem.Axes
                        .GetDescendants()
                        .FirstOrDefault(x => x.Name == itemName);

                if (currentScheduleItem == null)
                {
                    TemplateItem scheduleTemplate = masterDb.GetItem(Sitecore.TemplateIDs.Schedule);
                    currentScheduleItem = parentItem.Add(itemName, scheduleTemplate);
                }

                currentScheduleItem.Editing.BeginEdit();

                currentScheduleItem.Fields["Command"].Value = ItemIds.PublishWhenNeededCommand;

                if (itemName.Contains("start"))
                {
                    currentScheduleItem.Fields["Schedule"].Value = $"{item.Publishing.ValidFrom:yyyyMMddHH}|{item.Publishing.ValidFrom.AddHours(1):yyyyMMddhhmmss}|127|00:01:00";
                    currentScheduleItem.Fields["Last run"].Value = item.Publishing.ValidFrom.ToString("yyyyMMddTHHmmssZ");
                }
                else
                {
                    if (itemName.Contains("end"))
                    {
                        currentScheduleItem.Fields["Schedule"].Value = $"{item.Publishing.ValidTo:yyyyMMdd}|{item.Publishing.ValidTo.AddHours(1):yyyyMMddhhmmss}|127|00:01:00";
                        currentScheduleItem.Fields["Last run"].Value = item.Publishing.ValidTo.ToString("yyyyMMddTHHmmssZ");
                    }
                }
                currentScheduleItem.Fields["Items"].Value = item.ID.ToString();

                currentScheduleItem.Editing.AcceptChanges();
                currentScheduleItem.Editing.EndEdit();
            }
        }

 

The scheduled task I created above is normal scheduled task based on a template: /sitecore/templates/System/Tasks/Schedule.

The command i used in here is custom command i created based on template:  /sitecore/templates/System/Tasks/Command

Set the command type and method based on the class we going to create like below: Publish when needed.

In Items Field store the item that needs to be published.

Populate the schedule field based on Item Publishable from and To date.

 

Set these scheduled tasks to run every x number of minutes:

We need to run all scheduled tasks from root folder based on the set date and time. We can set this by using Sitecore scheduling agents.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <sitecore>
    <scheduling>
      <!-- An agent that processes scheduled tasks embedded as items in the master database. -->
      <agent type="Sitecore.Tasks.DatabaseAgent" method="Run" interval="00:01:00" name="Automatic_Publishing">
        <param desc="database">master</param>
        <param desc="schedule root">/sitecore/system/tasks/automatic publishing</param>
        <LogActivity>true</LogActivity>
      </agent>
    </scheduling>
  </sitecore>
</configuration>

 

So now on every 1-minute Sitecore checks if it needs to run any schedule tasks based on the time set for the tasks. when the task is ready to be run. its runs the task of running the task we need to publish the item which we stored the ID on that scheduled task. We can do this like below:

 public class PublishWhenNeeded
    {
        /// <summary>
        /// This method publish the item that are associated with schedule item.
        /// </summary>
        public void Execute(Item[] items, Sitecore.Tasks.CommandItem command, Sitecore.Tasks.ScheduleItem schedule)
        {
            Sitecore.Diagnostics.Log.Info("Started running custom schedule task", this);
            var itemsToPublish = schedule.Items;
            string currentSiteName = Context.Site.Name;
            Context.SetActiveSite("shell");
            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                if (itemsToPublish.Any())
                {
                    var publishingTargetDatabaseNames =
                        Sitecore.Publishing.PublishManager.GetPublishingTargets(
                            Sitecore.Configuration.Factory.GetDatabase("master")).Select(x => x["Target database"]).ToArray();

                    var publishingTargetLanguages = Sitecore.Configuration.Factory.GetDatabase("master").Languages;
                    Sitecore.Publishing.PublishManager.PublishItem(itemsToPublish.FirstOrDefault(),
                                                                    publishingTargetDatabaseNames.Select(Sitecore.Configuration.Factory.GetDatabase).ToArray(),
                                                                    publishingTargetLanguages,
                                                                    deep: true,
                                                                    compareRevisions: true, //Do smart publish for the item
                                                                    publishRelatedItems: true);
                    //Clear cache manually
                    Sitecore.Caching.CacheManager.ClearAllCaches();
                }
            }

            //Remove the schedule task once its ran.
            schedule.Remove();

            //switch back to current site
            Context.SetActiveSite(currentSiteName);
            Sitecore.Diagnostics.Log.Info("End of custom schedule task", this);
        }
    }

 

This will run the publish for that item and delete the scheduled task. This happens same for publishing To date scheduled task.

By this way, we can control when we can publish the item rather than trying to publish on every x number of minutes.

 

Hope this helps!

Comment below for any questions/suggestions and better ways and I will come back as soon as I can

 

 

 

READ MORE

Value cannot be null Parameter name objectToSwitchTo - Glassmapper Rendering parameters

Tags:     sitecore8.1, Rendering Parameters, GlassMapper, Sitecore

 

Page Editable Rendering Parameters

Problem:

We are on Sitecore 8.1 update 3 and using the latest release of Glassmapper (4.2.1.188). When we try to use @Editable it works for data source items. But not for rendering parameters.

in .cshtml we have below code:


 var rp = GetRenderingParameters<IEditorialComponent>();
 @Html.Glass().Editable(rp, x => x.Heading) @Editable(rp, x => x.Heading)

 

Both ways are not working, in page editor mode and getting below error:

Value cannot be null. Parameter name: objectToSwitchTo
at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName) at Sitecore.Common.Switcher 2.Enter(TValue objectToSwitchTo)
at Glass.Mapper.Sc.GlassHtml.MakeEditable[T](Expression`1 field, Expression 1 standardOutput, T model, Object parameters, Context context, Database database, TextWriter writer) in \Glass.Mapper.Sc\GlassHtml.cs:line 582

 

Rendering Parameters class:


[SitecoreType(TemplateId = "{templateId}")]
public partial interface IEditorialComponent : IGlassBase 
{
    [SitecoreField(FieldId = "FieldId")]
     string Body { get; set; }
}

 

Solution:

Sitecore does not support making rendering parameters editable via the page editor in the same way as fields. You have to edit rendering parameters by using the rendering properties window.

Therefore Glass doesn't support this either.

I get to know about this by raising a question in StackOverflow link here

 

 

 

READ MORE

Better way of handling sitecore 404 pages

Tags:     sitecore-best-practises, sitecore

Out of the box Sitecore way of handling 404 error is to respond with 302 redirect the original request to "sitecore/service/notfound.aspx". Then "sitecore/service/notfound.aspx" will respond with 404 response code.

It is not a good way for SEO purposes as crawlers think the page exists. It did not know it is a 404 response.

Sitecore provides a configuration setting not to redirect on an item not found.

 <setting name="RequestErrors.UseServerSideRedirect">
        <patch:attribute name="value">true</patch:attribute>
 </setting>

 

 

 Changing RequestErrors.UseServerSideRedirect to true will not redirect but with the response with 404 status code.

This helps in some way, but not completely, as in Sitecore we want 404 page to be content manage.

 So, on the page not found 

  • we need to respond with 404.
  • Do a server tranfer to sitecore 404 content page.
  • reposnds 200 status code of 404 content page.

There is a configuration setting in Sitecore to set 404 content pages in Sitecore:

 <setting name="ItemNotFoundUrl">
        <patch:attribute name="value">/404</patch:attribute>
      </setting>
      <setting name="LinkItemNotFoundUrl">
        <patch:attribute name="value">/404</patch:attribute>
 </setting>

 

We can set the name of 404 content page here.

Here is how we can achieve this:

  • Add a processor after HttpRequest.ItemResolver on HttpRequestBegin pipeline.
  • Find if the content item is null if null get 404 content page set as the context item.
  • Add another processor in the pipeline httpRequestEnd after EndDiagnostics to set the 404 status code for not found Item.

 

Adding a processor after HttpRequest.ItemResolver on HttpRequestBegin pipeline

Add code to your patch file

<pipelines>
<httpRequestBegin>
        <processor type="sitecoreBlog.tools4geeks.SitecoreCustom.Pipelines.HttpRequest.NotFoundProcessor, sitecoreBlog.tools4geeks.SitecoreCustom"
             patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" />
      </httpRequestBegin>
</pipelines>

 

public class NotFoundProcessor : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
    {
        public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
        {
            if (Sitecore.Context.Item != null || Sitecore.Context.Site == null || Sitecore.Context.Database == null)
            {
                return;
            }

            if (Sitecore.Context.Item == null)
            {
                var notFoundItem =
                    Sitecore.Context.Database.GetItem(string.Concat(Sitecore.Context.Site.StartPath,
                        Settings.ItemNotFoundUrl));
                if (notFoundItem != null)
                {
                    CustomContext.Current.PageNotFound = true;
                    Sitecore.Context.Item = notFoundItem;
                }
            }
        }
	}

 

Inherit your NotFoundProcessor from HttpRequestProcessor and override the process method.

I have created a singleton class called CustomContext to store values on every HTTP request, where I have stored current context request is PageNotFound.

Adding processor in the pipeline httpRequestEnd after EndDiagnostics

Add this to your patch file:

<pipelines>
	<httpRequestEnd>
        <processor type="sitecoreBlog.tools4geeks.SitecoreCustom.Pipelines.HttpRequest.SetHttpResponseCode, sitecoreBlog.tools4geeks.SitecoreCustom"
             patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.EndDiagnostics, Sitecore.Kernel']"/>
	</httpRequestEnd>
</pipelines>

 

public class SetHttpResponseCode : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            if (CustomContext.Current.PageNotFound)
            {
                HttpContext.Current.Response.StatusCode = (int) HttpStatusCode.NotFound;
            }
        }
    }

 

Here we are checking the value we set on HttpRequestBegin Pipeline CustomContext.Current.PageNotFound based on it we are setting response status code to 404.


Let me know if you have any questions. Hope this helps.

 

 

READ MORE

Custom name for rendering in presentation details

Tags:     sitecore-layoutdetails, sitecore-modals, sitecore-client, sitecore-deviceeditor

Modify the  code-behind file of the xml file in 

Website\sitecore\shell\Applications\Layouts\DeviceEditor\DeviceEditor.xml

from

<CodeBeside Type="Sitecore.Shell.Applications.Layouts.DeviceEditor.DeviceEditorForm,Sitecore.Client"/>

to

<CodeBeside Type="sitecoreblog.tools4geeks.Applications.Layouts.DeviceEditor.DeviceEditorForm,sitecoreblog.tools4geeks"/>

 

Duplicate original class with your new class name DeviceEditorForm with above namespace. replace RenderRenderings method with below code

private void RenderRenderings(DeviceDefinition deviceDefinition, int selectedIndex, int index)
    {
      Assert.ArgumentNotNull((object) deviceDefinition, "deviceDefinition");
      ArrayList renderings = deviceDefinition.Renderings;
      if (renderings == null)
        return;
      foreach (RenderingDefinition rendering in renderings)
      {
        if (rendering.ItemID != null)
        {
          Item obj = Client.ContentDatabase.GetItem(rendering.ItemID);
          XmlControl webControl = Resource.GetWebControl("DeviceRendering") as XmlControl;
          Assert.IsNotNull((object) webControl, typeof (XmlControl));
          HtmlGenericControl htmlGenericControl1 = new HtmlGenericControl("div");
          htmlGenericControl1.Style.Add("padding", "0");
          htmlGenericControl1.Style.Add("margin", "0");
          htmlGenericControl1.Style.Add("border", "0");
          htmlGenericControl1.Style.Add("position", "relative");
          htmlGenericControl1.Controls.Add((System.Web.UI.Control) webControl);
          string uniqueId = Sitecore.Web.UI.HtmlControls.Control.GetUniqueID("R");
          this.Renderings.Controls.Add((System.Web.UI.Control) htmlGenericControl1);
          htmlGenericControl1.ID = Sitecore.Web.UI.HtmlControls.Control.GetUniqueID("C");
          webControl["Click"] = (object) ("OnRenderingClick(\"" + (object) index + "\")");
          webControl["DblClick"] = (object) "device:edit";
          if (index == selectedIndex)
            webControl["Background"] = (object) "#D0EBF6";
          this.Controls.Add((object) uniqueId);
          if (obj != null)
          {
            webControl["ID"] = (object) uniqueId;
            webControl["Icon"] = (object) obj.Appearance.Icon;
            webControl["Header"] = (object) obj.DisplayName + "-" + (object) rendering.Datasource ;
            webControl["Placeholder"] = (object) WebUtil.SafeEncode(rendering.Placeholder);
          }
          else
          {
            webControl["ID"] = (object) uniqueId;
            webControl["Icon"] = (object) "Applications/24x24/forbidden.png";
            webControl["Header"] = (object) "Unknown rendering";
            webControl["Placeholder"] = (object) string.Empty;
          } 
          if (rendering.Rules != null && !rendering.Rules.IsEmpty)
          {
            int num = rendering.Rules.Elements((XName) "rule").Count<XElement>();
            if (num > 1)
            {
              HtmlGenericControl htmlGenericControl2 = new HtmlGenericControl("span");
              if (num > 9)
                htmlGenericControl2.Attributes["class"] = "scConditionContainer scLongConditionContainer";
              else
                htmlGenericControl2.Attributes["class"] = "scConditionContainer";
              htmlGenericControl2.InnerText = num.ToString();
              htmlGenericControl1.Controls.Add((System.Web.UI.Control) htmlGenericControl2);
            }
          }
          RenderDeviceEditorRenderingPipeline.Run(rendering, webControl, (System.Web.UI.Control) htmlGenericControl1);
          ++index;
        }
      }
    }

 

This should display rendering name as rendering name - Datasource  

 

 

 

 

 

READ MORE

Global content sharing strategy for buckets and repositories in sitecore

Tags:     sitecore-buckets, sitecore-wildcards, sitecore-best-practises

If you have a global section with a repository of items in Sitecore buckets and are shared between different Sitecore websites in a multi-site environment, here is the way how to deal it in a nicer way.

Let's imagine we have a repository of products in Sitecore buckets, which will be used by multiple websites. If we need to render these products on different websites, there are many ways we can approach this.

  • Wildcards and custom code on wildcard page layout to render product details.
  • Wildcard pages and making context item as the product item
  • using custom item resolver

There are pros and cons in all above cases, the solution I found best is using wildcard pages and making product item as the context item.

It has many advantages, by doing this way we can use Sitecore out of the box personalisation, experience editor works as good as normal website item. The only con is it needs a little bit of customisation.

So let's see how we can do this.

Firstly we need to add a new processor at the end of mvc.getPageItem pipeline like:

<pipelines>
      <mvc.getPageItem>
        <processor patch:after="processor[@type='Sitecore.Mvc.Pipelines.Response.GetPageItem.GetFromOldContext, Sitecore.Mvc']" type="sitecoreblog.tools4geeks.SitecoreCustom.Pipelines.Response.GetPageItem.GetForWildcardPages, sitecoreblog.tools4geeks.SitecoreCustom"/>
      </mvc.getPageItem>
    </pipelines>

 

Second step is to implement the processor.

public class GetForWildcardPages : GetPageItemProcessor
    {
	Assert.ArgumentNotNull(args, "args");
            if (args.Result != null)
            {
                if (args.Result.TemplateID == new ID(TemplateIds.productWildcard))
                {
                    var path = args.RouteData.Values.FirstOrDefault(x => x.Key == "pathInfo").Value.ToString();
                    var productId = GetIdFromPath(path);

                    if (!string.IsNullOrEmpty(productId))
                    {
                        var product = GetComposerItem(productId);
                        if (product == null)
                        {
                           //set 404 status code.
                        }
                        args.Result = product;
                    }
                }
			}
    }

 

I feel this is the cleanest way of using repositories in Sitecore website. Please let me know in comments if you had a better idea.

The other two approaches are not good for personalisation and experienced editor.

 

READ MORE

Building your first chatbot using Microsoft Chatbot Framework for beginners - Part 1

Tags:     microsoft-Bot-Framework, Chatbot-Beginners, ChatBot-Emulator, botFramework, FirstChatBot, chatbot

This is how i started with my first chat bot adventure.

  • Install Chatbot emulator.
  • Install Visual studio template for chatbot.

Download and install Chatbot emulator from https://emulator.botframework.com/

Download and install Visual studio template from http://aka.ms/bf-bc-vstemplate

Now, you got tools to make your first chatbot locally.

First Chat bot

Create a new project in visual studio from the newly installed template called "Bot Application".

First chatbot bot framework by Microsoft

You can find that files created are similar to an Web API project.

File system for chatbot

Now, lets run this project and hook it to Emulator so we can test the default chatbot application that comes with "Bot Application" template. For running the project lets hit F5.

Restore all nuget packages if you have build errors. To restore then right click on project or solution. You will see a message like below.

Restoring Nuget packages in visual studio

Click on Restore and that should run the project with no errors. You will see below response in the browser. 

First chatbot start page

As chatbot is like an API. All the requests and responses should go the EndPoints. If you see our files generated we have a folder called Controllers and class called MessagesController which will deal with messages endpoint and is inherited from APIController just like any other web API endpoint class.

 

Now start your emulator(botFrameWork emulator).

 

Ignore about Microsoft App ID and Microsoft App Password for now. We will come back to it later. Locale by default is en-us. Depends on what port number your application is running you need to add that to the emulator. Normally it will be on 3978 or 3979. So input http://localhost:3979/api/messages on the emulator. Details pane on the top right shows the details of the request and Log pane on the bottom right shows all the logs for the application.

 

Let's start interacting with ChatBot. Click on Connect and you are good to start typing. 

 

First chatbot emulator response

 

You can see chatbot is responding to our chat. All it does here is echo your message and count the characters of your message.

Done, You have created your first chatbot. 

Let's Discuss the code which created this response in our next blog post.

 

READ MORE

Creating a web API in sitecore for ajax calls with in sitecore context

Tags:     sitecore-customroutes, sitecore-api, sitecore-best-practises

Sometimes we do need to create web APIs for ajax calls in our Sitecore solution.

By doing a normal web API will not initialize Sitecore pipeline, so this makes Sitecore.Context.Site || Sitecore.Context.Database null. 

To get the normal Sitecore context we need to add a custom route to route table and add it to Sitecore pipeline.

Steps to follow:

1. Add your custom Route to RouteTable.
2. Add your custom processor at the end of the initialize pipeline.
3. Inherit your custom Sitecore API controller from SitecoreController

Adding custom Route to RouteTable

    public class RegisterCustomRoute
    {
        public virtual void Process(PipelineArgs args)
        {
            RouteTable.Routes.MapRoute("RouteName", "CustomApi/{controller}/{action}/");
        }
    }

 

 

Add your custom processor at the end of the initialize pipeline

<sitecore>
    <pipelines>
      <initialize>
        <processor patch:after="processor[@type='Sitecore.Pipelines.Loader.EnsureAnonymousUsers, Sitecore.Kernel']" type="sitecoreBlog.tools4geeks.Custom.RegisterCustomRoute, sitecoreBlog.tools4geeks"/>
      </initialize>
    </pipelines>
</sitecore>	

 

While creating a controller inherit it from SitecoreController.

This is it, now you got fully flexible sitecore API which can be useful for ajax calls with in sitecoreContext.

 

READ MORE

Change sitecore default content editor language

Tags:     sitecore-usermanager, sitecore-languge-settings, sitecore-content-language

By default sitecore content editor languge for Shell website(Sitecore CMS) is "en". This can be modified globally or per user basis.

Below is the patch config that can be used to change the content editor language to "en-GB"

<site name="shell">
    <patch:attribute name="contentLanguage">en-GB</patch:attribute>
</site>

This can be done per user basis in sitecore by using User Manager. On each user there is a setting called Language Settings where you can set "Default Content-Language"

Default content language

READ MORE

Dealing with 500 errors in Sitecore 8,7,6

Tags:     sitecore-handling-500-errors, sitecore-error, sitecore-best-practises

Step 1: Add below to sytem.web section in web.config file :

<system.web>
    <customErrors defaultRedirect="~/Error/500.aspx" mode="RemoteOnly" redirectMode="ResponseRewrite">
	    <error statusCode="500" redirect="~/Error/500.aspx"/>
    </customErrors>
</system.web>

Above code deals with asp.net runtime errors. Make sure it is a .aspx extension but not .html as IIS has some issue dealing with .html extensions on 500 errors.

 

Step 2: Add below to sytem.webServer section in web.config  :

<system.webServer>
    <httpErrors errorMode="DetailedLocalOnly" existingResponse="Auto">
			<remove statusCode="500" subStatusCode="-1"/>
			<error statusCode="500" path="~/Error/500.html" responseMode="File"/>
    </httpErrors>
</system.webServer>

Above code deals with any IIS errors.

Now add 500.html and 500.aspx to your build according to your project requirements.

Please let me know if u have any questions about this, i will come back as soon as i can.

 

READ MORE

Creating multiple bots in single endpoint url

Tags:     microsoft-Bot-Framework, Chatbot-Beginners, ChatBot-Emulator, chatbot

I get into to a problem last week where i want all my bots to be available in one single endpoint so i dont need to worry about domain names and SSL certificates for them.

I searched online and could not able to find an answer easily. So, i ended up writing what i have done. If you faced the same problem here it is.

Create MultiCredentialProvider class as below:

public class MultiCredentialProvider : ICredentialProvider
    {
        public Dictionary<string, string> Credentials = new Dictionary<string, string>
        {
            { "AppId1", "AppPassword1" },
            { "AppId2", "AppPassword2" },
            { "AppId3", "AppPassword3" }
        };

        public Task<bool> IsValidAppIdAsync(string appId)
        {
            return Task.FromResult(this.Credentials.ContainsKey(appId));
        }

        public Task<string> GetAppPasswordAsync(string appId)
        {
            return Task.FromResult(this.Credentials.ContainsKey(appId) ? this.Credentials[appId] : null);
        }

        public Task<bool> IsAuthenticationDisabledAsync()
        {
            return Task.FromResult(!this.Credentials.Any());
        }
    }

Add this MultiCredentialProvider to your controller code:

[BotAuthentication(CredentialProviderType = typeof(MultiCredentialProvider))]
 public class MessagesController : ApiController
    {
	static MessagesController()
        {

            // Update the container to use the right MicorosftAppCredentials based on
            // Identity set by BotAuthentication
            var builder = new ContainerBuilder();

            builder.Register(c => ((ClaimsIdentity)HttpContext.Current.User.Identity).GetCredentialsFromClaims())
                .AsSelf()
                .InstancePerLifetimeScope();
            builder.Update(Conversation.Container);
        }
	

Add below to your ActivityTypes.ConversationUpdate:

 IConversationUpdateActivity update = activity;
                        // resolve the connector client from the container to make sure that it is 
                        // instantiated with the right MicrosoftAppCredentials
                        using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                        {
                            var client = scope.Resolve<IConnectorClient>();
                            if (update.MembersAdded.Any())
                            {
                                var reply = activity.CreateReply();
                                foreach (var newMember in update.MembersAdded)
                                {
                                    if (newMember.Id != activity.Recipient.Id)
                                    {
                                        reply.Text = $"Welcome {newMember.Name}!";
                                        await client.Conversations.ReplyToActivityAsync(reply);
                                    }
                                }
                            }
                        }

 

That's it. It should now handle multiple appIds and passwords with in the same endpoint URL.

 

Here is the full code:

// Use the MultiCredentialProvider as credential provider for BotAuthentication
    [BotAuthentication(CredentialProviderType = typeof(MultiCredentialProvider))]
    //[BotAuthentication]
    public class MessagesController : ApiController
    {
        static MessagesController()
        {

            // Update the container to use the right MicorosftAppCredentials based on
            // Identity set by BotAuthentication
            var builder = new ContainerBuilder();

            builder.Register(c => ((ClaimsIdentity)HttpContext.Current.User.Identity).GetCredentialsFromClaims())
                .AsSelf()
                .InstancePerLifetimeScope();
            builder.Update(Conversation.Container);
        }

        /// <summary>
        /// POST: api/Messages
        /// receive a message from a user and send replies
        /// </summary>
        /// <param name="activity"></param>
        [ResponseType(typeof(void))]
        public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
        {
            if (activity != null)
            {
                switch (activity.GetActivityType())
                {
                    case ActivityTypes.Message:
                        await Conversation.SendAsync(activity, () => new RootDialog());
                        break;

                    case ActivityTypes.ConversationUpdate:
                        IConversationUpdateActivity update = activity;
                        // resolve the connector client from the container to make sure that it is 
                        // instantiated with the right MicrosoftAppCredentials
                        using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                        {
                            var client = scope.Resolve<IConnectorClient>();
                            if (update.MembersAdded.Any())
                            {
                                var reply = activity.CreateReply();
                                foreach (var newMember in update.MembersAdded)
                                {
                                    if (newMember.Id != activity.Recipient.Id)
                                    {
                                        reply.Text = $"Welcome {newMember.Name}!";
                                        await client.Conversations.ReplyToActivityAsync(reply);
                                    }
                                }
                            }
                        }
                        break;
                    case ActivityTypes.ContactRelationUpdate:
                    case ActivityTypes.Typing:
                    case ActivityTypes.DeleteUserData:
                    case ActivityTypes.Ping:
                    default:
                        Trace.TraceError($"Unknown activity type ignored: {activity.GetActivityType()}");
                        break;
                }
            }
            return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
        }
 

Good luck!

READ MORE

Auto publish sitecore items

Tags:     sitecore-autopublish, sitecore-publishing, Sitecore

Sitecore provides a functionality to auto publish items based on the publishable from and publishable to dates set on the item.

This functionality is not turned on by default in Sitecore. We need to configure an agent which runs every x number of minutes and publish the items based on the publishable from and to dates.

This configuration can be turned on in Sitecore.config file.

<agent type="Sitecore.Tasks.PublishAgent" method="Run" interval="00:00:00">
      <param desc="source database">master</param>
      <param desc="target database">web</param>
      <param desc="mode (full or smart or incremental)">incremental</param>
      <param desc="languages">en, da</param>
</agent>

Source database is the database from which publish should happen from.

The target database is the database to which publish should happen to.

the mode can be full, smart and incremental. Full will do a complete republish, smart will publish the items that are modified from last publish, incremental will publish items that are in the queue.

We can specify the languages that are to be published as well from languages parameter.

If the interval is 00:00:00 means this agent is disabled. Modify this to 00:01:00 to run this agent for every one minute.

Keep in mind on every publish the HTML cache clear will happen. Make sure not to run this task very often. It will be a huge impact on performance.

Better way of handling auto publish at specific time can be found here - Sitecore auto publish at specific time

Hope this helps.

 

READ MORE

Renamed template field is not published correctly?

Tags:     sitecore-bug, sitecore-publishing

I have found the same issue. All i have done is renamed an existing field and published but sitecore is not updating the web database with new field name.
This means some item/rendering parameter fields are not updating on publish.

I have raised a support ticket and her is the response from sitecore support:

The behavior you have reported is a known issue in 8.2 rev. 170407 (Update-3). It is fixed in Sitecore XP 8.2 Update-4.

Be aware that the hotfix was built specifically for 8.2 rev. 170407 (Update-3), and you should not install it on other Sitecore versions or in combination with other hotfixes, unless explicitly instructed by Sitecore Support.

Note that you need to extract ZIP file contents to locate installation instructions and related files inside it.
To find detailed information about the hotfix and monitor changes of the related issue use the reference number 161148.
More information about public reference numbers can be found here:
https://kb.sitecore.net/articles/853187

 

Please contact sitecore support to get hotfix for this issue.

READ MORE

Sitecore 8 stuck on publish

Tags:     sitecore-publishing

There might be several reasons for sitecore to hange on publish. Below are the most common one's:

  • Make sure permissions for your packages folder in Data folder are right.
  • Make sure your MongoDB server is up and running and can connect from webserver.

For previous versions of sitecore this blog post might hellp - publishing stucks

 

*please let me know if u have any questions on this and i can come back as soon as possible

READ MORE

Cannot upload multiple images or files at once in content ediotor?

Tags:     sitecore-content-editor

The ability to upload multiple files simultaneously is provided by flash upload. This kind of upload is possible in case browser is allowed to use flash player.

Please check if your Chrome and Firefox have flash player disabled. In the request the cookie which contains the version of flash is sc_fv.

Enable flash player in Chrome - Enable Flash in chrome

Enable flash player in FireFox - Enable Flash in Firefox

 

*please let me know if u have any questions on this and i can come back as soon as possible

READ MORE

Creating a sitecore contact programmatically

Tags:     sitecore-contacts, sitecore-xdb

Recently, I get into a sutuation where i need to create a sitecore contact when a user submits a contact form. I find it hard while finding a right solution for this. So i am sharing my solution here.

Here is the SitecoreContactManager.cs which deals with creating and updating contacts in xdb:

public class SitecoreContactManager
    {
        private ContactManager _manager = Sitecore.Configuration.Factory.CreateObject("tracking/contactManager", true) as ContactManager;
        private ContactRepository _repository = Sitecore.Configuration.Factory.CreateObject("tracking/contactRepository", true) as ContactRepository;

        public ContactManager XdbContactManager
        {
            get { return _manager; }
        }

        public ContactRepository XdbContactRepository
        {
            get { return _repository; }
        }

        public Contact GetXdbContact(string identifier)
        {
            var contact = XdbContactRepository.LoadContactReadOnly(identifier);

            if (contact != null) return contact;

            var leaseOwner = new LeaseOwner(GetType() + Guid.NewGuid().ToString(), LeaseOwnerType.OutOfRequestWorker);

            contact = XdbContactRepository.CreateContact(Guid.NewGuid());

            if (contact == null) return null;

            contact.Identifiers.Identifier = identifier;

            //Setting these values allows processing and aggregation to process this contact without erroring.
            contact.System.Value = 0;
            contact.System.VisitCount = 0;

            //MAKE OTHER Contact Changes here too if you need to on Create.

            XdbContactRepository.SaveContact(contact, new ContactSaveOptions(true, leaseOwner));

            return contact;
        }

        public bool UpdateXdbContact(string identifier, string firstName, string lastName, string telephone)
        {

            try
            {
                var currentContact = GetXdbContact(identifier);

                if (currentContact == null)
                    return false;

                var lockResult = XdbContactManager.TryLoadContact(currentContact.ContactId);

                Contact updatedContact = currentContact;

                switch (lockResult.Status)
                {
                    case LockAttemptStatus.Success:
                        var lockedContact = lockResult.Object;
                        lockedContact.ContactSaveMode = ContactSaveMode.AlwaysSave;
                        updatedContact = UpdateContactInfo(lockedContact, firstName, lastName, telephone, identifier);  //this method should do work on lockedContact and return lockedContact.  this then saves it as updatedContact.
                        break;
                    case LockAttemptStatus.NotFound:
                        updatedContact = currentContact;
                        break;
                    default:
                        var exception = new Exception(GetType() + " Contact could not be locked - " + currentContact.ContactId);
                        Sitecore.Diagnostics.Log.Error(string.Format("[{1}:SaveContactInfoToXdb] Can't Lock or Find xDB Contact {0}", currentContact.ContactId, GetType()), exception);
                        break;
                }

                //Save Contact Information - This is the ContactManager method to use
                XdbContactManager.SaveAndReleaseContact(updatedContact);
                return true;
            }
            catch (Exception exception)
            {
                Sitecore.Diagnostics.Log.Error($"Error in updating contact identifier {identifier}", exception);
            }

            return false;
        }

        private Contact UpdateContactInfo(Contact lockedContact, string firstName, string lastName, string telephone, string email)
        {
            try
            {
                var personalInfo = lockedContact.GetFacet<IContactPersonalInfo>("Personal");

                personalInfo.FirstName = firstName;
                personalInfo.Surname = lastName;


                var contactEmails = lockedContact.GetFacet<IContactEmailAddresses>("Emails");

                if (contactEmails != null)
                {
                    IEmailAddress contactEmail = null;

                    if (contactEmails.Entries.Contains("Work E-Mail"))
                    {
                        contactEmail = contactEmails.Entries["Work E-Mail"];
                    }
                    else
                    {
                        contactEmail = contactEmails.Entries.Create("Work E-Mail");
                        contactEmails.Preferred = "Work E-Mail";
                    }
                    contactEmail.SmtpAddress = email;
                }


                var cotactphoneNumbers = lockedContact.GetFacet<IContactPhoneNumbers>("Phone Numbers");

                if (cotactphoneNumbers != null)
                {
                    IPhoneNumber work = null;
                    if (cotactphoneNumbers.Entries.Contains("Work"))
                    {
                        work = cotactphoneNumbers.Entries["Work"];
                    }
                    else
                    {
                        work = cotactphoneNumbers.Entries.Create("Work");
                        cotactphoneNumbers.Preferred = "Work";
                    }
                    work.Number = telephone;
                }
            }
            catch (Exception exception)
            {
                Sitecore.Diagnostics.Log.Error($"Error in updating contact identifier {lockedContact.Identifiers.Identifier}", exception);
                return null;
            }
            return lockedContact;
        }
    }

Code i used on contact form submit:

private void CreateUpdateContactInXdb(FormDetailsModel model)
        {
            try
            {
                //Identify the user
                Tracker.Current.Session.Identify(model.Email);

                //get the contact
                var contact = Tracker.Current.Session.Contact;

                if (contact != null)
                {
                    //Update contact
                    SitecoreContactManager.UpdateXdbContact(model.Email, model.FirstName, model.LastName, model.Phone);
                    Tracker.Current.Interaction.Value += 100;
                }
                else
                {
                    //Create contact
                    Tracker.Current.Interaction.Value = 100;
                    Tracker.Current.Session.Contact = SitecoreContactManager.GetXdbContact(model.Email);
                }
            }
            catch (Exception exception)
            {
                Sitecore.Diagnostics.Log.Error($"error in creating or updating contact for identifier {model.Email}", exception);
            }
        }

Please look at this blogpost which helped me in finding the right solution for more details on this.

READ MORE

Disable unicorn in sitecore

Tags:     sitecore-unicorn, sitecore8.1, Sitecore

It is easy to switch off unicorn if we don't need it.

All it takes is disabling the file Unicorn.Configs.Default.config by renaming it to Unicorn.Configs.Default.config.disabled

Disabling Unicorn.config will throw an error as below:

Server Error in '/' Application.
Could not find configuration node: /sitecore/unicorn/configurationProvider
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Could not find configuration node: /sitecore/unicorn/configurationProvider

 

Happy sitecoring!

READ MORE

I hate programming- really?

Tags:     personal-opinions, programming

 

I have heard this sentence repeatedly, mostly from students in India who are undertaking their undergraduate studies in Computer Science. I don't blame them, but the real question is do you really hate programming or is it the fear of something new that puts you off or makes you hate it?

You cannot hate something unless you try, have you sincerely tried programming? I am asking you again, have you really tried? No baby starts walking on the first attempt, nobody rides a motorcycle on his/her first attempt – anything worth doing will require multiple attempts to get right. So, now I am asking you again, have u really given it a shot to learn?

If you have really tried hard and hated it then this post is not for you. This post is aimed at students who think they hate programming but, they don’t.

I agree most of you are forced into computers, like me, even though u want to pursue your career in something else. That's how it works in India right? I don't want to delve deeper into this topic as there is a huge debate already happening in the Indian community.

Hating something does not help you learn it. If you keep saying “I hate it” multiple times in your mind, your subconscious agrees and stores this information. After some time even if you try hard to learn, your brain says I hate programming as it is trained to think that. So, is programming worth hating?

I agree it can be a bit tricky when getting started, but eventually, you'll start loving it. So, stop forcing yourself to hate it but instead start learning!

Programming is nothing but writing instructions for a computer - imagine you are raising a child, you need to teach every little thing to him/her. They don’t learn immediately but eventually, when they start doing what you taught them, you will find satisfaction and happiness. Wouldn’t you?

Programming is similar, you need to learn little by little and eventually you will find pleasure in programming. You don't need to be a scientist to start programming like I said you just need to write instructions in the language a computer understands.

Can you find the next number in the sequence below?

1 4 9 16 _ 36

Have you guessed the number 25? Yes, the answer is 25. If you guessed it right, you are good at logical thinking and you have the mind of a programmer. Even if you did not guess it correctly, that's alright. Let's try this, can you train your dog to catch a ball? Can you teach your parrot to say hello? Do u know how to instruct your remote-control car to avoid hitting walls? I presume many of you can, and programming is as same. If you can do any of the above, you have the potential to be a programmer.

There is nothing impossible in this world. If someone else can do it, then why not you? Everyone is blessed with the same brain. All that matters is how you are training your brain. Did you know that blind person who read braille have greater sensitivity in their fingertips? It's just because they have trained and use their fingertips to read more often than one with eyes. Their brain notices this and provides more sensitivity to their fingertips. Do you know, the brain which started reading the first sentence of this article is different to the one which is reading now? Yes, it is, it constantly grows in the direction of the things you do. It learns and grows from every situation experienced.

So, nothing is impossible, all we need to do is 3 things

  • Train your brain in a positive direction.
  • Train your brain in a positive direction.
  • Train your brain in a positive direction.

I did not make a typo, yes all three points are one and the same.

There are tonnes of material online to learn to program, start learning and stop hating. Trust me, once you start learning you will find pleasure and happiness in programming and never want to go back.

PS: If you need any help in finding good resources (or where to start) drop me an email or comment here, I will come back as soon as I can.

 

READ MORE

Image uploaded by media dialog from content item is giving 404

Tags:     sitecore-bug, sitecore-media-library

This is identified as a bug in sitecore 8.1 update 3. See the response from sitecore below:

Sitecore contains setting Media.UploadAsVersionableByDefault which defines the way how the media should be uploaded. By default the value of this setting is false. In this case, the dialog has to upload media as unversioned. However, the dialog does not consider this setting and uploads a versioned media.
This behavior has been as a bug in our bug tracking system. Thank you for reporting the problem.
To track the future status of this bug report, please use the reference number 162905. More information about public reference numbers can be found here:
https://kb.sitecore.net/articles/853187

If you are on same vesion number please request a patch from sitecore support.

This has been fixed in n sitecore 8.1 update 5

READ MORE

What is sitecore patching? Why we as sitecore developers need to use it?

Tags:     sitecore-patch, sitecore-best-practises

Sitecore provides us with an opportunity to better control our config changes to Sitecore instance in the way of patching.

Imagine changing Sitecore config files for your project needs. It's all good until you need to upgrade your Sitecore solution. On upgrade, u need to compare all config files u have changed and merge changes on every config file. This is very time-consuming and confusing.

Patching helps us on upgrade, as we have not touched raw Sitecore config files. Any upgrade is easy.

Now how does patching engine works in Sitecore?

To include any patch file, u need to place that config file in /App_Config/Include folder. Patch rendering engine renders the patches in alphabetical order. So I would suggest adding a folder called zzz_projectName and add your patch files inside it. So there are fewer chances of our patches being override.

I am not going in deep here about how to patch here. But there is a great pdf from Sitecore explaining that - Sitecore Patching Guide

If you want to see the final patch version you can go to - http://hostname/sitecore/admin/showconfig.aspx

Let me know if u have any questions and I am happy to come back as soon as possible.

 

READ MORE

The model item passed into the dictionary is of type Sitecore Mvc Presentation RenderingModel, but this dictionary requires a model item of type xxx

Tags:     sitecore-error, GlassMapper

You might have come to this page as you encoutered same error as mine. I am using Glassmapper as ORM for sitecore.

There are many reasons for this error. Below are couple of them.

  • Make sure you are passing in right datsource template for your rendering. If your rendering is not expecting any datasource make sure u have datasource field empty on presentation.
  • If you are in multi-lingual environment, make sure all the items needed by the rendering have this language version, Using language fallback helps in this 

 

READ MORE

Sitecore manual publish an item in all publishing targets and languages

Tags:     sitecore-publishing, sitecore8.1, Sitecore

Some times we need to tigger sitecore publish on an item on all publishing targets and on all languages. Here is the way we can achive it.

To get all publishing target database names:


var publishingTargetDatabaseNames = Sitecore.Publishing.PublishManager.GetPublishingTargets(
                                    Sitecore.Configuration.Factory.GetDatabase("master"))
                                    .Select(x => x["Target database"]).ToArray();

To get all languages for master database:

var publishingTargetLanguages = Sitecore.Configuration.Factory.GetDatabase("master").Languages;

 

To trigger a manual publish in sitecore:


Sitecore.Publishing.PublishManager.PublishItem(itemsToPublish.FirstOrDefault(),	publishingTargetDatabaseNames.Select(Sitecore.Configuration.Factory.GetDatabase).ToArray(),
	publishingTargetLanguages,
	deep: true,
	compareRevisions: true, //Do smart publish for the item	publishRelatedItems: true);

To clear sitecore cache manually:

 Sitecore.Caching.CacheManager.ClearAllCaches();

Adding all together in item publish event: 

 

var publishingTargetDatabaseNames = Sitecore.Publishing.PublishManager.GetPublishingTargets(
                                    Sitecore.Configuration.Factory.GetDatabase("master"))
                                    .Select(x => x["Target database"]).ToArray();
var publishingTargetLanguages = Sitecore.Configuration.Factory.GetDatabase("master").Languages;Sitecore.Publishing.PublishManager.PublishItem(itemsToPublish.FirstOrDefault(),
												publishingTargetDatabaseNames.Select(Sitecore.Configuration.Factory.GetDatabase).ToArray(),
												publishingTargetLanguages,
												deep: true,
												compareRevisions: true, //Do smart publish for the item
												publishRelatedItems: true);
//Clear cache manuallySitecore.Caching.CacheManager.ClearAllCaches();

 

Deep : this parameter specifies whether to include subitems or not. If you only want to publish item go for false.

Compare Revisons: this parameter specifies if this publish is smart publish or republish. Go for true if u want smart publish. Going for republish is necessary sometimes due to media items but keep an eye on performance.

publishingTargetDatabases is array of databases.

READ MORE

Sitecore Hyphenate Item name and set displayname to item name

Tags:     sitecore-events, Sitecore

Its better to make urls in lowercase and hyphenate for SEO purposes. In case of sitecore, urls are generated based on the items created in sitecore.

We can modified the item names and diplay names of a sitecore item on item save event. 

First add your Item event handler to sitecore config like below:


<event name="item:saved">
       <handler type="Sitecoreblog.Tools4geeks.SitecoreCustom.Data.Items.ItemEventHandler, Sitecoreblog.Tools4geeks.SitecoreCustom" method="HyphenateItenName"/>
</event>
<event name="item:saved:remote">
       <handler type="Sitecoreblog.Tools4geeks.SitecoreCustom.Data.Items.ItemEventHandler, Sitecoreblog.Tools4geeks.SitecoreCustom" method="HyphenateItenName"/>
</event>

Here is the way how to do it. Quick mentions:

  • Process only items in Master database.
  • Process only content Items
  • While hyphenating item name change display name to item name. So content editors see nice item names in sitecore.

/// <summary>
        /// Ensures that item names are lower case and hyphenated for URL creation purposes. The original name is saved as the Display Name.        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="args">Event arguments.</param>
        protected void HyphenateItemName(object sender, EventArgs args)
        {
            var item = (Item)Event.ExtractParameter(args, 0);
            // only process items in master database
            if (item.Database.Name != "master")
            {
                return;
            }
            // only process items in the content tree
            if (!item.Paths.IsContentItem)
            {
                return;
            }
            string processedName = item.Name.ToLower().Replace(' ', '-');
            if (item.Name == processedName)
            {
                return;
            }
            item.Editing.BeginEdit();
            try
            {
                item.Appearance.DisplayName = item.Name;
                item.Name = processedName;
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error("Error while hyphenating item name.", ex, this);
            }
            finally
            {
                item.Editing.EndEdit();
            }
        }

 

Hope this helps someone!

READ MORE

RECENT POSTS

  • Extension method to get sitecore start item or ...
  • Sitecore get all base templates for an item rec...
  • Sitecore publish at specific time
  • Value cannot be null Parameter name objectToSwi...
  • Better way of handling sitecore 404 pages
  • Custom name for rendering in presentation detai...

POPULAR POSTS

  • Better way of handling sitecore 404 pages
  • Sitecore publish at specific time
  • Auto publish sitecore items
  • Sitecore manual publish an item in all publishi...
  • Creating a web API in sitecore for ajax calls w...
  • Change sitecore default content editor language

CATEGORIES

  • sitecore-unicorn
  • sitecore-contacts
  • sitecore-xdb
  • sitecore-handling-500-errors
  • sitecore-bug
  • sitecore-media-library
  • sitecore-content-editor
  • sitecore-patch
  • personal-opinions
  • programming
  • sitecore-error
  • sitecore-usermanager
  • sitecore-languge-settings
  • sitecore-content-language
  • microsoft-Bot-Framework
  • Chatbot-Beginners
  • ChatBot-Emulator
  • botFramework
  • FirstChatBot
  • chatbot
  • sitecore-customroutes
  • sitecore-api
  • sitecore-buckets
  • sitecore-wildcards
  • sitecore-layoutdetails
  • sitecore-modals
  • sitecore-client
  • sitecore-deviceeditor
  • sitecore-best-practises
  • sitecore-templates
  • sitecore
  • sitecore-scheduling
  • sitecore-autopublish
  • sitecore-autopublish
  • extension-method
  • sitecore-multisite
  • sitecore-events
  • sitecore-publishing
  • sitecore8.1
  • Rendering Parameters
  • GlassMapper
  • Sitecore

Copyrights © 2023 Blog All rights reserved | Build by Lohitha Palagiri |Template by W3layouts