Sunday 28 September 2014

Sorting Sitecore SubItems programmatically

6 comments
In one of my previous blog post I’ve explained that Sitecore Fast Query doesn’t return hierarchical result set as compared to Sitecore content tree structure and use Sitecore Query instead of Fast Query if your code logic depends on Sitecore content tree structure and you have to render the items in the same order as they are appearing in Sitecore Content tree. Due to performance constraint I’ve to use Sitecore Fast Query as it is more efficient in terms of execution time and performance as compared to Sitecore Query. Now the main catch is that I’ve to retrieve items using Fast Query and display items with respect to the Sitecore content tree hierarchy.
 
Depending on the total number of items and where these items are located in Sitcore Content tree; it can be much faster and efficient to sort items using C# code after getting result using Sitcore Fast Query. In one scenario, I was getting 12 items in 1758ms using Sitecore Query and 70ms using Sitecore Query. Thus sorting results using C# code will be much efficient in this scenario.

Subitems Sorting in Sitecore Content Tree

Firstly we have to figure it out how Sitecore actually sorts these items in content tree. I’ve created few test items under article folder in Sitecore content tree.
By default Sitecore sorts item by their name.  We can also sort these items manually by using the buttons in the Sorting group of the Home strip or right click on the item and choose Sorting.
If you are changing sort order of a field manually then Sitecore will insert a value in the Sortorder (__sortorder) field in the Appearance section of the standard fields. Sortorder field is used to determine the ordering between sibling items.
Sortorder value takes preference over the default sorting by name. The Item's children are sorted first by Sortorder and then by Name.

Sitecore has few inbuilt subitems sorting methods.  To use one of the built in subitems sorting method, right click on the parent item and choose Sorting and Subitems Sorting.
Please note that the Sortorder field is the first criteria for most sorting. To reset the Sortorder field of the subitems, click Reset in Subitems Sorting dialog. This will set the value of all the sortorder fields to 100.
Sitecore has the following six in built in subitems sorting methods:
  1. Created : Sorts the items in ascending order by their created time.
  2. Default : Sorts the items in ascending order by their names where numbers are treated by their ASCII code value instead of their numerical value. Default sorting is case insensitive. For example:
  3. Display Name : Sorts the items in ascending order by their display names. If an item does not have a display name then the item name is used for sorting. This sorting is a logical sorting, where numbers are considered by their numerical value instead of ASCII code value.  For example:
  4. Logical : Sorts the items in ascending order by their names where numbers are treated by their numerical value instead of their ASCII code value. For example:
  5. Reverse : Sorts the items in descending order by their names where numbers are treated by their ASCII code value instead of their numerical value. For example:
  6. Updated : Sorts the items by their updated time. The item that was updated last is sorted first in order.
If you do not specify a child sorting rule for an item then Sitecore applies the default child sorting rule, and users can sort items manually.

Sorting Sitecore SubItems programmatically

Whenever you select one of the built in subitems sorting method by right clicking on the parent item and selecting Sorting and Subitems Sorting then Subitems Sorting (__Subitems Sorting) field in the Appearance section of the standard fields of parent item gets updated with selected sorting method.
As shown in above image; I’ve selected Reverse Subitems sorting method and Subitems Sorting field gets updated with value as ‘Reverse’. I’ve written following C# code to sort items after getting result using Sitcore Fast Query.
// Get Parent Item i.e Article Folder 
Item parentItem = Sitecore.Context.Database.GetItem(new ID("{D6272FF7-9315-4DCC-B50A-BF8F216E5388}"));
// Get all children under Article Folder using Sitecore Query
string query = "fast://*[@@id ='{D6272FF7-9315-4DCC-B50A-BF8F216E5388}']/*";
Item[] items = Sitecore.Context.Database.SelectItems(query);
// Sort the items in the same way they are appearing in Content Tree by calling SitecoreSubItemsSort Method. Pass items array and Parent Item as parameter
items = SitecoreSubItemsSort(items, parentItem);
// Or call overloaded SitecoreSubItemsSort Method by just passing items array as parameter
items = SitecoreSubItemsSort(items);
        /// <summary>
        /// Sort a list of Items the same way that Sitecore does when it displays them in the content tree
        /// </summary>
        /// <param name="itemArray">Item Array to be sorted</param>
        /// <param name="parentItem">Parent Item</param>
        /// <returns>Sorted Item Array</returns>
        public static Item[] SitecoreSubItemsSort(Item[] itemArray, Item parentItem)
        {
            // Sort items by Subitems sorting method
            if (itemArray.Length > 0)
            {
                string sorting = parentItem.Fields["__Subitems Sorting"].Value.ToString();
                IComparer<Item> comparer = ComparerFactory.GetComparer(sorting, parentItem.Database);
                Array.Sort<Item>(itemArray, 0, itemArray.Length, comparer);
            }
            return itemArray;
        }

        /// <summary>
        /// Sort a list of Items the same way that Sitecore does when it displays them in the content tree
        /// </summary>
        /// <param name="itemArray">Item Array to be sorted</param>
        /// <param name="parentItem">Parent Item</param>
        /// <returns>Sorted Item Array</returns>
        public static Item[] SitecoreSubItemsSort(Item[] itemArray)
        {
            // Sort items by Subitems sorting method
            if (itemArray.Length > 0)
            {
                IComparer<Item> comparer = ComparerFactory.GetComparer(itemArray.First().Parent);
                Array.Sort<Item>(itemArray, 0, itemArray.Length, comparer);
            }
            return itemArray;
        }

        /// <summary>
        /// Sort a list of Items the default way that Sitecore does when it displays them in the content tree
        /// </summary>
        /// <param name="itemList">List to be sorted</param>
        /// <returns>Sorted list</returns>
        public static Item[] SitecoreDefaultSort(List<Item> itemList)
        {
            // Sort by sortorder, if sortorder value is empty, assume 100. 
            // Sort items with the same sortorder value by Name. 
            // Sitecore sorts in the same way. 

            itemList.Sort(
                (a, b) =>
                (
                    (a.Fields["__sortorder"].Value == "" ? "100" : a.Fields["__sortorder"].Value)
                     ==
                    (b.Fields["__sortorder"].Value == "" ? "100" : b.Fields["__sortorder"].Value)
                )
                ?
                (string.Compare(a.Name, b.Name))
                :
                (int.Parse(a.Fields["__sortorder"].Value == "" ? "100" : a.Fields["__sortorder"].Value).CompareTo(int.Parse(b.Fields["__sortorder"].Value == "" ? "100" : b.Fields["__sortorder"].Value)))
                );
            return itemList.ToArray();
        } 
Create IComparer object based on Subitems Sorting field by calling GetComparer method of Sitecore.Data.Comparers.ComparerFactory class.  Then sort the items by using IComparer. If the value of Subitems Sorting field is null or empty then items will be sorted by using default sorting. Using Array.Sort method is much efficient. LINQ's OrderBy operator is about five times slower than Array.Sort with large sequences. Comments and suggestions are most welcome. Happy coding!
References:
  1. Create Your Own Subitems Sorting Comparer by Mike Reynolds
  2. Sitecore SDN Post
Read More...

Sunday 7 September 2014

Find all templates which are not referenced

6 comments
Recently I was doing some cleanup of Sitecore content tree and I’ve got into a situation where I have to find all templates that are not being used by any Sitecore item i.e find all templates which are not referenced. The simplest way of seeing these referrer links is to go to that particular template and go to the Navigation Strip and then click on Links. This should show you all the items (referrers) that point to this selected template and all the items that the selected template points to (references).  This activity needs to be done manually for each template thus I’ve decided to write custom code which returns a list of all the templates that have "no referrers".  Below function GetUnReferencedTemplates() returns list of templates that are not being used by any Sitecore item or have no referrers.
public List<Item> GetUnReferencedTemplates()
        {
            List<Item> UnReferencedTemplates = new List<Item>();

            // Get all templates which are based on /sitecore/templates/System/Templates/Template
            //Template id of /sitecore/templates/System/Templates/Template = {AB86861A-6030-46C5-B394-E8F99E8B87DB}

            string queryTemplateId = "fast:/sitecore/templates//*[@@templateId='{AB86861A-6030-46C5-B394-E8F99E8B87DB}']";

            //Alternatively you can also use @@templatename='Template'
            //string queryTemplateName = "fast:/sitecore/templates//*[@@templatename='Template']";

            Item[] getTemplates = Sitecore.Context.Database.SelectItems(queryTemplateId);

            if (getTemplates.Length > 0 && getTemplates != null)
            {
                foreach (Item item in getTemplates)
                {
                    // getting all linked Items that refer to the particular template item
                    ItemLink[] itemLinks = Globals.LinkDatabase.GetReferrers(item);
                    if (itemLinks == null || itemLinks.Length == 0)
                    {
                        UnReferencedTemplates.Add(item);
                    }
                }
            }
            return UnReferencedTemplates;
        }
Below Sitecore Fast Query will get all templates which are based on /sitecore/templates/System/Templates/Template. Template id of /sitecore/templates/System/Templates/Template is {AB86861A-6030-46C5-B394-E8F99E8B87DB}
string queryTemplateId = "fast:/sitecore/templates//*[@@templateId='{AB86861A-6030-46C5-B394-E8F99E8B87DB}']";
I’ve modified Sitecore Fast Query a bit to search all templates under /sitecore/templates/User Defined context node location in order to optimize the performance. Also I don’t want to fetch system templates which are under /sitecore/templates/System location.
string queryTemplateId = "fast:/sitecore/templates/User Defined//*[@@templateId='{AB86861A-6030-46C5-B394-E8F99E8B87DB}']";
Execution of above method might take some time as it depends on structure of your Sitecore Content tree. Comments and suggestions are most welcome. Happy coding!

Related article: Find all the items that are referring to particular item in Sitecore
Read More...

Thursday 28 August 2014

Sitecore Modal Pop-ups are not working in Chrome

19 comments
Recently I’ve faced strange issue while working with Sitecore.NET 7.0. (rev. 130810) in Chrome browser.  I was trying to access presentation details of Sitecore item and layout details pop-up was not coming properly. I’ve checked Chrome console to verify if there is any JavaScript error. I’ve seen below error in Chrome console:
Uncaught ReferenceError: showModalDialog is not defined
This issue is not occurring in Sitecore 7.2 version. After troubleshooting I’ve found that this issue was occurring in Chrome version 37.x. Chrome 37 disables support for showModalDialog by default thus window.showModalDialog() won’t work anymore in Chrome 37 onwards.  For more information refer to the chromium blog. However, Google has added a temporary Group Policy setting EnableDeprecatedWebPlatformFeatures to re-enable deprecated web platform features for a limited time. In May 2015, this setting will be removed and showModalDialog will be completely removed from Chrome. Below section describes the steps to enable EnableDeprecatedWebPlatformFeatures policy setting to Chrome, which administrators can then configure via Windows Group Policy:
  1. Download ADM policy templates for Windows from here.
  2.  Unzip downloaded zip file and navigate to Start > Run: gpedit.msc
  3. Navigate to Local Computer Policy > Computer Configuration > Administrative Templates.
  4. Right-click Administrative Templates and select Add/Remove Templates.
  5. Add the downloaded chrome.adm template of Windows via the dialog for the specific language. In my case; I’ve selected adm templates for en-US locale.
  6. Once added, a Google Chrome folder will appear under 'Classic Administrative Templates' if it's not there already.
  7. Select EnableDeprecatedWebPlatformFeatures setting in right column and edit it.
  8. Edit EnableDeprecatedWebPlatformFeatures as shown in below image.
  9. Restart chrome browser and type chrome://policy/  in address bar and you will see that setting is enabled now. 
  10. Above procedure will create/update windows registry value. 
This setting doesn't need to be configured manually! Starting with Chrome 28, policies are loaded directly from the Group Policy API on Windows. Policies manually written to the registry will be ignored. Starting with Chromium 35, policies are read directly from the registry if the workstation is joined to an Active Directory domain; otherwise the policies are read from Group Policy Objects (GPO).
Order of Precedence for Chrome Policies:
Related articles:

1.) SDN Forum : showModalDialog disabled by default in Chrome 37
2.) Sitecore Knowledge Base Article

Comments and suggestions are most welcome. Happy coding!
Read More...

Saturday 9 August 2014

Sitecore Page Editor Mode renders raw/json value as content

Leave a Comment
Recently I have faced one strange issue while working with Sitecore Page Editor Mode. Sitecore Page Editor Mode was rendering some raw json value as content. In this post I am going to narrate my findings while doing the troubleshooting. Please note that I am working with Sitecore7.0 with MVC enabled.
  • John West has already written great blog related to this issue. Please refer to John’s blog which describes resolutions to issues that can cause the Page Editor in the Sitecore to render a jumble of JSON as part of the content of a page. 
  • Ensure that webedit.css file is getting loaded and accessible. You’ll find below setting in web.config file:
    <setting name="WebEdit.ContentEditorStylesheet" value="/webedit.css"/>
    Value attribute specifies the location where webedit.css is located. By default the webedit.css file is located at the root of website. In my case, webedit.css was getting loaded perfectly.
    If webedit.css is not getting loaded into DOM then add a reference to the webedit.css in <head> of layout to resolve the problem.
  • Ensure that Sitecore.MvcExperienceEditor.config file is enabled if you are working with Sitecore MVC. You can find this file at \App_Config\Include folder. Sitecore.MvcExperienceEditor.config file is responsible to render all required javascript/css files in page editor mode.
  • Verify that you are not getting any jQuery conflict related errors. If yes, then override $ function by calling "jQuery.noConflict()".
    <script type="text/javascript" src="jquery-1.8.3.js"></script>
    <script type="text/javascript">
    var jq = jQuery.noConflict(true);
    $jq(document).ready(function() {
     // Your jQuery related code go here. Use $jq for all calls to jQuery.
          });
    </script>
    
  • Ensure that Layout HTML is not causing any issue in page editor mode. Following code was written in BaseLayout.cshtml file to render IE version specific conditional css classes on body tag:
    <!--[if lt IE 8]>      <body class="lt-ie9 lt-ie8"> <![endif]-->
    <!--[if IE 8]>         <body class="lt-ie9"> <![endif]-->
    <!--[if gt IE 8]><!-->
    <body>
    <!--<![endif]-->
    
    Bingo! It was the culprit who was causing the main issue. I’ve removed code of loading conditional css classes on body tag and I was no longer getting jumbled json in Sitecore Page Editor Mode.
Drop a comment below if you are aware of any additional solution for resolving this issue. Comments and suggestions are most welcome. Happy coding!
Read More...

Sunday 3 August 2014

Sitecore Fast Query v/s Sitecore Query: Interesting Observation

Leave a Comment
I’ve been working with Sitecore Fast Query and Sitecore Query for a long time. Sitecore Fast Query is translated to SQL queries that be executed by the database engine. Fast Query is more efficient in terms of execution time and performance as compared to Sitecore Query. Few days ago I’ve noticed that Sitecore fast query doesn’t return hierarchical result set as compared to Sitecore content tree structure. In other words, Sitecore Fast Query retrieves a plain list of the Sitecore items regardless of the content tree hierarchy.
  • Sitecore Content Tree Structure:  Below image is part of Sitecore content tree. I’ve to find out all descendants of AccordionBottom item (item id = {2FCB17C4-8EB2-427E-97FE-12A0BECE4044}) that are based on the template with the specified Id.
  • Fast Query: Below Fast Query returns result set in jumbled order.
    fast://*[@@id = '{2FCB17C4-8EB2-427E-97FE-12A0BECE4044}']//*[@@TemplateId = '{39ACE8CB-4EE3-4989-9B73-5CAFEC21B70C}']
  • Sitecore Query: Below Sitecore Query returns result set in hierarchical order as per content tree structure.
    //*[@@id = '{2FCB17C4-8EB2-427E-97FE-12A0BECE4044}']//*[@@TemplateId = '{39ACE8CB-4EE3-4989-9B73-5CAFEC21B70C}']
Use Sitecore Query instead of Fast Query if your code logic depends on Sitecore content tree structure and you have to render the items in the same order as they are appearing in Sitecore Content tree. 
Comments and suggestions are most welcome. Happy coding! 
Read More...

Error realted to Item Id while working with Glass Mapper in Sitecore page editor mode

Leave a Comment
Today I was working on Sitecore Glass Mapping framework for my Sitecore MVC project. I’ve installed Glass Mapper from nuget and configured it for Sitecore MVC project. For more information on the Glass.Sitecore.Mapper visit the official website. Thanks Mike and Tom for this great framework. Below are the few details about my development environment:
  • MVC Version:  4Sitecore Version:  7.0
  • Glass.Mapper version:  3.0.10.23
  • Glass.Mapper.Sc version:  3.2.0.39
  • Glass.Mapper.Sc.Mvc version:  3.2.0.35
  • Glass.Mapper.Sc.Razor version:  3.0.9.13
I am using SimpleInjector IOC container to create objects that are used by Glass.Mapper rather than default Castle Windsor or default inbuilt Glass.Mapper method. Using other IOC container with Glass.Mapper is very easy and I got my webpage up and running in Sitecore normal mode easily. However I got below error while working in Sitecore Page Editor Mode.
You can not save a class that does not contain a property that represents the item ID. Ensure that at least one property has been marked to contain the Sitecore ID. Type: SitecoreRamblings.Models.NewsModel
   at Glass.Mapper.Sc.Configuration.SitecoreTypeConfiguration.ResolveItem(Object target, Database database)
   at Glass.Mapper.Sc.GlassHtml.MakeEditable[T](Expression`1 field, Expression`1 standardOutput, T model, Object parameters, Context context, Database database, TextWriter writer)
Below is the implementation of Model class:
using Glass.Mapper.Sc.Configuration.Attributes;
using SitecoreRamblings.Service.Interface;

namespace SitecoreRamblings.Models
{
    [SitecoreType(AutoMap = true)]
    public class NewsModel
    {
        private readonly INewsService _service;       
        public virtual string Title { get; set; }
        public virtual string Body { get; set; }
        public virtual string Abstract { get; set; }

        public NewsModel(INewsService service)
        {
            _service = service;
        }     
    }
}
After investigation; I’ve realized that I forgot [my stupidity on peak :( ] to add an additional property in NewsModel class to represent the Sitecore ID. I’ve added below property in NewsModel class:
[SitecoreId]
public virtual Guid Id { get; set; }
Sitecore ID is required to allow Glass.Mapper to link your model to the actual Sitecore item in Page Edit mMode. After building solution, I was no longer getting error in Page Editor Mode.
Comments and suggestions are most welcome. Happy coding! 
Read More...

Tuesday 22 July 2014

Disable Schedule Email Reminder Task for multiple items in Sitecore

3 comments
Today I have noticed a post in Sitecore SDN forum regarding how to disable Schedule Email Reminder task for multiple items in Sitecore. I thought it'd be better to turn it into a Blog post with more detail. It might come useful to someone. :)

Occasionally an author may wish to be reminded of something regarding the item he is working on. Sitecore Reminders are made just for that. They also make the Administrator's work easier, by providing a suitable way of notifying Editors and other staff members about changes needed to be made. Reminder settings for an item are set in the Tasks section:
You can also set reminder settings for an item by clicking on Set Reminder in Review Strip of Sitecore Content Editor Ribbon.

Disabling Schedule Email Reminder task

Schedule Email Reminder task for Sitecore item can be disabled by following ways:
  1. Using Sitecore Content Editor: You can disable reminder settings for an item by clicking on Clear Reminder in Review Strip of Sitecore Content Editor Ribbon.
    You can also disable reminder settings for an Item by clicking on Clear link in the Tasks section.
    This approach will work only for one item at a time.
  2. Updating tasks table of Core Database: You can disable EmailReminderTask for multiple items by modifying tasks table of Core database using below SQL query:
    Update [Tasks] set Disabled=1 where taskType='Sitecore.Tasks.EmailReminderTask,Sitecore.Kernel'
    Take a backup of Core db before executing this query.
  3. Disabling TaskDatabaseAgent in web.config: In web.config, you will find below entry related to TaskDatabaseAgent
      <!-- Agent to process tasks from the task database (TaskDatabase) -->
          <agent type="Sitecore.Tasks.TaskDatabaseAgent" method="Run" interval="00:10:00" />
    
    TaskDatabaseAgent gets all pending tasks ( for example: EmailReminderTask, ArchiveItem, ArchiveVersion) from the tasks table of Core db and executes them one by one. So basically TaskDatabaseAgent runs scheduled tasks from the Core database.
    So in order to suspend EmailReminderTask you have to disable TaskDatabaseAgent by setting its value to 00:00:00 ( 00:00:00 means that Agent/Job is disabled).
    Disabling TaskDatabaseAgent will also suspend execution of other pending tasks like ArchiveItem or ArchiveVersion.
  4. Using Custom Code: Below function ClearReminder(Item item) takes Sitecore item as input parameter and clear reminder for specific item. Call this function iteratively for multiple items.
  5. Item item = Sitecore.Data.Database.GetDatabase("master").GetItem(new ID("{D833E825-4948-4264-9D77-68CE50C72F94}"));
                ClearReminder(item);
    public void ClearReminder(Item item)
            {
                if (item[FieldIDs.ReminderText].Length > 0 || item[FieldIDs.ReminderRecipients].Length > 0 || item[FieldIDs.ReminderDate].Length > 0)
                {
                    using (new Sitecore.SecurityModel.SecurityDisabler())
                    {
                        item.Editing.BeginEdit();
                        item[FieldIDs.ReminderText] = string.Empty;
                        item[FieldIDs.ReminderRecipients] = string.Empty;
                        item[FieldIDs.ReminderDate] = string.Empty;
                        item.Editing.EndEdit();
                        string[] strArrays = new string[] { AuditFormatter.FormatItem(item) };
                        Log.Audit(this, "Clear reminder: {0}", strArrays);
                    }
                }
            }
    
Comments and suggestions are most welcome. Happy coding!
Read More...

Monday 21 July 2014

ReferenceError: Sys is not defined while working in Sitecore Content Editor

Leave a Comment
Today I was working on clean installation of Sitecore CMS 7.0 rev. 130918 and configured it to support MVC. Somehow I started getting below error while working with rich text editor in Sitecore Content Editor.
Uncaught ReferenceError: Sys is not defined.

It seems that embedded scripts were not getting loaded by Webresource.axd and Scriptresource.axd handlers. While troubleshooting, I’ve checked for WebResource.axd in IgnoreUrlPrefixes setting to ensure that necessary javascripts are getting loaded. Below entry was present in web.config:
<setting name="IgnoreUrlPrefixes" value="/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing"/>
I’ve also added Scriptresource.axd in IgnoreUrlPrefixes settings but it didn’t work in my case then I started to check MVC routes. The routes for the MVC Web Application are defined in the RouteConfig.cs under the App_Start folder of the MVC Project. In static function RegisterRoutes(RouteCollection routes) below route was defined:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
The route with the pattern {resource}.axd/{*pathInfo} was included to prevent requests for the web resource files such as WebResource.axd or ScriptResource.axd from being passed to a controller. I’ve deleted above route entry and build the solution. Bingo! I was no longer getting any error message. Please drop a comment if anybody knows more detail about this error and let me know your opinion.

Comments and suggestions are most welcome. Happy coding!
Read More...

Saturday 19 July 2014

Configuring Sitecore Item Buckets with different bucket folder path : Part 2

1 comment
In my previous post, I’ve explained about changing the bucketing strategy by using predefined bucketing rules. In this post I am going to explain how to implement bucketing strategy by writing custom code.

Bucketing Strategy: Using Custom Code

Now I demonstrate how newly created bucketable items will be auto organized into content tree based on item name with dictionary index hierarchy with up to two levels. I’ll write custom code to achieve this behavior.
In Sitecore, create a template field titled Enable Custom Dynamic Bucket Folder Path as a checkbox field to /sitecore/templates/System/Templates/Sections/Item Buckets.
I’ve created a bucket folder named Article and enable checkbox for Enable Custom Dynamic Bucket Folder Path field in Item Buckets section.
I’ve written a CustomBucketFolderPathResolver class which implements IDynamicBucketFolderPath interface. In the function GetFolderPath, I am checking value of Enable Custom Dynamic Bucket Folder Path checkbox. If it is true then I am creating custom dynamic bucket folder path else path will be auto generated based on Bucketing Rule Context or by the creation date of the item.
Below is the code of CustomBucketFolderPathResolver class:
using Sitecore;
using Sitecore.Buckets.Rules.Bucketing;
using Sitecore.Buckets.Util;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Rules;
using Sitecore.StringExtensions;
using System;

namespace Sitecore.Ramblings
{
    public class CustomBucketFolderPathResolver : IDynamicBucketFolderPath
    {
        public CustomBucketFolderPathResolver()
        {
        }

        public string GetFolderPath(Database database, string itemName, ID templateId, ID itemId, ID parentItemId, DateTime creationDateOfNewItem)
        {
            Assert.ArgumentNotNull(database, "database");
            Assert.ArgumentNotNull(itemId, "itemId");
            Assert.ArgumentNotNull(parentItemId, "parentItemId");

            string resolvedPath = null;

            Item parentItem = database.GetItem(parentItemId);

            if (parentItem != null)
            {
                if (parentItem.Fields["Enable Custom Dynamic Bucket Folder Path"].Value == "1")
                {
                    if (itemName.Length == 1)
                    {
                        resolvedPath = itemName;
                    }
                    else
                    {
                        resolvedPath = itemName.Substring(0, 1) + "/" + itemName.Substring(0, 2);
                    }
                    return resolvedPath;
                }
            }

            BucketingRuleContext bucketingRuleContext = new BucketingRuleContext(database, parentItemId, itemId, itemName, templateId, creationDateOfNewItem)
            {
                NewItemId = itemId,
                CreationDate = creationDateOfNewItem
            };
            BucketingRuleContext bucketingRuleContext1 = bucketingRuleContext;
            Item item = database.GetItem(Sitecore.Buckets.Util.Constants.SettingsItemId);
            Assert.IsNotNull(item, "Setting Item");
            Item[] itemArray = new Item[] { item };
            RuleList<BucketingRuleContext> rules = RuleFactory.GetRules<BucketingRuleContext>(itemArray, Sitecore.Buckets.Util.Constants.BucketRulesFieldId);
            try
            {
                if (rules != null)
                {
                    rules.Run(bucketingRuleContext1);
                }
            }
            catch (Exception exception)
            {
                Log.Error(string.Format("BucketFolderPathResolver: Cannot resolve bucket path for item {0}. Parent = {1}", itemId, parentItemId), exception);
            }

            resolvedPath = bucketingRuleContext1.ResolvedPath;
            if (resolvedPath.IsNullOrEmpty())
            {
                resolvedPath = creationDateOfNewItem.ToString(BucketConfigurationSettings.BucketFolderPath, Context.Culture);
            }
            return resolvedPath;
        }
    }
}
Go to App_Config\Include\Sitecore.Buckets.config file and set value of BucketConfiguration.DynamicBucketFolderPath setting as below:
<!--<setting name="BucketConfiguration.DynamicBucketFolderPath" value="Sitecore.Buckets.Util.BucketFolderPathResolver, Sitecore.Buckets"/>-->
<setting name="BucketConfiguration.DynamicBucketFolderPath" value="Sitecore.Ramblings.CustomBucketFolderPathResolver, SitecoreRamblings "/>
The value format is “<namespace>.<class name>, <assembly name>”. For example:Sitecore.Ramblings is namespace, CustomBucketFolderPathResolver is class name and SitecoreRamblings is assembly name.

Create a new item under Article bucket folder and it will auto organized into content tree based on item name with dictionary index hierarchy with up to two levels. Comments and suggestions are most welcome. Happy coding!
Read More...

Configuring Sitecore Item Buckets with different bucket folder path : Part 1

Leave a Comment
An item bucket acts as a repository or container in the content tree where you can store other content items. Also the parent to child relationship between the content items in an item bucket is completely removed and instead the items are automatically organized into folders. Auto organizing content items at the time of creation is based on bucketing strategy. By default, the items are organized according to the date and time of when the item was created, but this can be configured to use different behavior, such as the item’s globally unique identifier (GUID).
As you can see in above image; ACE item is auto-organized by the creation date of the item i.e. Date 14 July 2014, Time 20:00 Hrs. In this blog post; I am going to explain few different bucketing strategies while using Sitecore Item bucket.  Note that I am working on Sitecore 7.2 release. We can change the bucketing strategy by two ways:
  • Using predefined bucketing rules. I’ll be explaining this approach in this blog post.
  • Writing custom code for bucketing strategy. This scenario will be explained in next blog post.
  1. Bucketing Strategy – Using new item creation date: Newly created bucketable items will be auto organized into content tree based on item creation date in yyyy/MM/dd format. I’ve created a new template NEWS and made it as bucketable.  
    Navigate to item bucket settings stored at /sitecore/system/Settings/Buckets location and create a new rule (Bucketing Strategy: Item Creation Date) for resolving the bucket folder path.


    Create a new news item based on News template under bucket folder and it will auto organized by item creation date in yyyy/MM/dd format.
  2. Bucketing Strategy – Using new item name: Newly created bucketable items will be auto organized into content tree based on item name with up to three levels. I’ve created a new template PRODUCT and made it as bucketable. Navigate to item bucket settings stored at /sitecore/system/Settings/Buckets location and create a new rule (Bucketing Strategy: Item Name) for resolving the bucket folder path.
    Create a new product item based on Product template under bucket folder and it will auto organized by item name format.
  3. Bucketing Strategy – Using new item id: Newly created bucketable items will be auto organized into content tree based on item id with five levels. I’ve created a new template MEMBER and made it as bucketable. Navigate to item bucket settings stored at /sitecore/system/Settings/Buckets location and create a new rule (Bucketing Strategy: Item Id) for resolving the bucket folder path.
    Create a new member item based on Member template under bucket folder and it will auto organized by item id format with five levels.
Comments and suggestions are most welcome. Happy coding!
Read More...

Sunday 13 July 2014

Check user’s access rights on Sitecore item programmatically

1 comment
Recently I’ve got into a situation where I have to identify programmatically whether given user is having read or write access on specific Sitecore item. I’ve thought of sharing my solution via blog post. It might come in handy for someone else. Below function CheckReadAccess(string itemId, string UserName) accepts User Name and Item Id of Sitecore item as input parameter and returns true or false after assessing access rights.
public bool CheckReadAccess(string itemId, string UserName)
        {
            bool ReadAccess = false;

            if (Sitecore.Data.ID.IsID(itemId))
            {
                Item item = Sitecore.Context.Database.GetItem(Sitecore.Data.ID.Parse(itemId));
                if (item != null)
                {
                    Sitecore.Security.Domains.Domain domain = Sitecore.Context.Domain;
                    string domainUser = domain.Name + @"\" + UserName;
                    if (Sitecore.Security.Accounts.User.Exists(domainUser))
                    {
                        Sitecore.Security.Accounts.User user = Sitecore.Security.Accounts.User.FromName(domainUser, false);
                        // UserSwitcher allows below code to run under a specific user 
                        using (new Sitecore.Security.Accounts.UserSwitcher(user))
                        {
                            ReadAccess = item.Access.CanRead();
                        }
                    }
                }
            }
            return ReadAccess;
        }
Explanation: I’ve used the Sitecore.Security.Accounts.UserSwitcher class to cause a block of code to run in the context of a specific user, regardless of the context user. The Sitecore.Security.Accounts.UserSwitcher constructor sets the context user to the specified user. The code within the using statement block has the effective rights of the user specified by the first parameter passed to constructor of the the Sitecore.Security.Accounts.UserSwitcher class. Sitecore.Security.AccessControl.ItemAccess class is responsible to check various access rights on given item. In my code, I am checking read access rights on Sitecore item by calling item.Access.CanRead(). ItemAccess class is having below inbuilt functions:
namespace Sitecore.Security.AccessControl
{
    public class ItemAccess
    {
        public ItemAccess(Item item);

        public virtual bool CanAdd(BranchId branchId);
        public virtual bool CanAdd(TemplateID templateID);
        public virtual bool CanAdmin();
        public virtual bool CanCopyTo(Item destination);
        public virtual bool CanCreate();
        public virtual bool CanDelete();
        public virtual bool CanDuplicate();
        public virtual bool CanMoveTo(Item destination);
        public virtual bool CanRead();
        public virtual bool CanReadLanguage();
        public virtual bool CanRemoveVersion();
        public virtual bool CanRename();
        public virtual bool CanWrite();
        public virtual bool CanWriteLanguage();
    }
}
Write a comment if you are aware of some other ways of checking access rights on Sitecore item programmatically. Comments and suggestions are most welcome. Happy coding!  
Read More...

Saturday 12 July 2014

Impact of Sitecore Aliases on SEO

Leave a Comment
Today I’ve noticed a post in Sitecore SDN forum regarding impact of Sitecore Aliases on SEO. While answering to the post; I thought it'd be better to turn it into a Blog post with more detail. :)

From SEO point of view, using aliases in Sitecore is not good option. The main reason is multiple URLs will be created for a single page URL. This in turn shows up in Google search engine as two pages and will consider as duplicate content. Search engines try to index pages with distinct and unique content. Duplicate content affects adversely the hits for the page in the results rankings. So it will be better to avoid aliases in Sitecore whenever possible.

No redirect (no 301 or 302 HTTP status code) happens when you are using aliases in Sitecore. You can monitor HTTP status code and web traffic using Fiddler.  Suppose you’ve created aliases for a Sitecore item /sitecore/content/Home/sample/sample1/sample2 as sampleitem. If you open www.yourwebsite.com/sample/sample1/sample2 then you will see HTTP status code as 200 and if you open www. yourwebsite.com/sampleitem then also you will see HTTP status code as 200 which means that we are having two URLs which is generation same content (duplicate content).

Set canonical URL link into the head of the page whenever you are using aliases in Sitecore. Using canonical URLs to improve link and ranking signals for content available through multiple URL structures is recommended practice.

How to implement Canonical URL link in Sitecore:

Override AliasResolver processor in HttpRequest pipeline and write custom logic to handle aliases and generate canonical link. In web.config you will see below line related to AliasResolver processor:
<processor type="Sitecore.Pipelines.HttpRequest.AliasResolver, Sitecore.Kernel" />
Below is the custom code for CustomAliasResolver:
using Sitecore;
using Sitecore.Links;
using Sitecore.Pipelines.HttpRequest;
using System.Web;

namespace Sitecore.Ramblings
{
    public class CustomAliasResolver : Sitecore.Pipelines.HttpRequest.AliasResolver
    {
        public CustomAliasResolver()
        {
        }

        public override void Process(HttpRequestArgs args)
        {
            base.Process(args);
            if (Context.Item != null)
            {
                string href = "{0}://{1}{2}";
                args.Context.Items["CanonicalUrl"] = string.Format(href, HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Host, LinkManager.GetItemUrl(Context.Item));
            }
        }
    }
}
Modify web.config as below:
<!--<processor type="Sitecore.Pipelines.HttpRequest.AliasResolver, Sitecore.Kernel" />-->
<processor type="Sitecore.Ramblings.CustomAliasResolver, SitecoreRamblings" />
The type format is “<namespace>.<class name>, <assembly name>”. For example: Sitecore.Ramblings is namespace, CustomAliasResolver is class name and SitecoreRamblings is assembly name.

Now in the header control or in layout of your website check whether HttpContext.Current.Items["CanonicalUrl"] is not null and set canonical URL into the head of page. Below is the sample code to set canonical URL in layout of Sitecore MVC website.
@if (Context.ApplicationInstance.Context.Items["CanonicalUrl"] != null)
    {
 <link rel="canonical" href="@Context.ApplicationInstance.Context.Items["CanonicalUrl"].ToString()" />
    }
Comments and suggestions are most welcome. Happy coding! 
Read More...

Wednesday 9 July 2014

Find all the items that are referring to particular item in Sitecore

9 comments
In this blog post I am going to explain how to get all the items (referrers) which are referring to a particular item (may be template, sublayout or rendering etc.) in Sitecore. There are few ways to achieve this goal:
  1. Using Sitecore Content Editor UI
  2. Writing custom C# code
  3. Using Sitecore Powershell Console Module
  • Using Sitecore Content Editor UI: The simplest way of seeing these referrer links is to go to that particular item and go to the Navigation Strip and then click on Links. This should show you all the items (referrers) that point to this current item and all the items that the current items points to (references).
  • Writing custom C# code: Below function GetReferrers(string itemId) takes item id as input parameter and return the list of items which are pointing to this particular item.
    public Item[] GetReferrers(string itemId)
            {          
                Item item = Sitecore.Data.Database.GetDatabase("master").GetItem(new Sitecore.Data.ID(itemId));
                // getting all linked Items that refer to the Item
                ItemLink[] itemLinks = Globals.LinkDatabase.GetReferrers(item);
                if (itemLinks == null)
                {
                    return null;
                }
                else
                {
                    ArrayList items = new ArrayList(itemLinks.Length);
                    foreach (ItemLink itemLink in itemLinks)
                    {
                        // comparing the database name of the linked Item
                        if (itemLink.SourceDatabaseName == "master")
                        {
                            Item linkItem = Sitecore.Data.Database.GetDatabase("master").Items[itemLink.SourceItemID];
                            if (linkItem != null)
                            {
                                items.Add(linkItem);
                            }
                        }
                    }
                    return (Item[])items.ToArray(typeof(Item));
                }
            }
    
  • Using Sitecore Powershell Console Module: Download Sitecore Powershell Console Module from Sitecore MarketPlace and install the package. Go to Start > Development Tools > PowerShell ISE.

    Enter below powershell script in script field and execute.
    $props = @{
       InfoTitle = "Referrers"
        InfoDescription = "Lists all items that are using this item"
        PageSize = 25
    }
    
    function Get-ItemReferrers {
        $item = Get-Item -Path "/sitecore/templates/Launch Sitecore/Job Function"
        $linkDb = [Sitecore.Globals]::LinkDatabase
        $links = $linkDb.GetReferrers($item)
        foreach($link in $links){
            $linkedItem = Get-Item -Path master:\ -ID $link.SourceItemID
            $linkedItem
        }
    }
    
    $items = Get-ItemReferrers
    $items | Show-ListView @props -Property @{Label="Name"; Expression={$_.DisplayName} },
                @{Label="Updated"; Expression={$_.__Updated} },
                @{Label="Updated by"; Expression={$_."__Updated by"} },
                @{Label="Created"; Expression={$_.__Created} },
                @{Label="Created by"; Expression={$_."__Created by"} },
                @{Label="Path"; Expression={$_.ItemPath} }
    
    Close-Window
    
Above approaches require that Link Database should be up to date. Link database should be updated automatically but you can force rebuild command manually. Go to Start > Control Panel > Databases > Rebuild Link Database
Sitecore Powershell References:
  1. Blog by Adam Najmanowicz 
  2. Blog by Michael West 
  3. How to create custom report using PowerShell blog by Mike Reynolds
Comments and suggestions are most welcome. Happy coding!
Read More...