Sunday, 23 August 2015

Change sort order of languages in the language menu

Leave a Comment
Lately I have been playing more with language menu in Sitecore 8 content editor. Check this post how to enable flag icon in language drop down menu in Sitecore. If you have Sitecore site with many languages then probably you will find one thing most annoying – order of languages in language menu. By default, Sitecore displays the languages in the language selector ordered by the created date but I want to sort languages based on language name in languages menu. Below steps illustrates how to change sort order in which the languages are displayed in the languages menu:
  1. Create a new class and inherit Sitecore.Data.SqlServer.SqlServerDataProvider class.
  2. Override function GetLanguages() and sort languages  using the IComparer interface using a helper class by providing your own implementation for sort logic.
    using Sitecore.Collections;
    using Sitecore.Globalization;
    using System;
    using System.Collections.Generic;
    
    namespace SitecoreCustomizedLanguageOrder
    {
        public class LanguageSortOrder : Sitecore.Data.SqlServer.SqlServerDataProvider
        {
            public LanguageSortOrder(string connectionString) : base(connectionString) { }
    
            private class LanguageComparer : IComparer<Language>
            {
                #region IComparer<Language> Members
                LanguageSortOrder provider;
                public LanguageComparer(LanguageSortOrder dataProvider)
                {
                    this.provider = dataProvider;
    
                }
                public int Compare(Language x, Language y)
                {
                    return string.Compare(x.CultureInfo.EnglishName, y.CultureInfo.EnglishName);
                }
    
                #endregion
            }
            
            public override LanguageCollection GetLanguages()
            {           
                Language[] languages = base.GetLanguages().ToArray();           
                Array.Sort(languages, new LanguageComparer(this));
                LanguageCollection result = new LanguageCollection();
                foreach (Language language in languages)
                {
                    result.Add(language);
                }
                return result;
            }
        }
    }
    
  3. Compile source code and placed dll in bin folder. 
  4. Create new Sitecore Config patch file (CustomDataProvider.config) and provide following data provider definition:
    Old data provider definition
    <main type="Sitecore.Data.$(database).$(database)DataProvider, Sitecore.Kernel">
            <param connectionStringName="$(1)"/>
            <Name>$(1)</Name>
          </main>    
    New data provider definition
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <dataProviders>      
          <main type="SitecoreCustomizedLanguageOrder.LanguageSortOrder,SitecoreCustomizedLanguageOrder" patch:instead="main">
            <param connectionStringName="$(1)"/>
            <Name>$(1)</Name>
          </main>
        </dataProviders>
      </sitecore>
    </configuration>
  5. Deploy CustomDataProvider.config file in \Website\App_Config\Include folder.
  6. Create a custom event handler which will clear the cache when the language sort order is changed. Here’s the code to achieve this:
    using Sitecore;
    using Sitecore.Caching;
    using Sitecore.Data.Items;
    using Sitecore.Events;
    using System;
    
    namespace SitecoreCustomizedLanguageOrder
    {
        public class NotifyLanguageSortOrderChanged
        {
            public void OnItemSortorderChanged(object sender, EventArgs e)
            {
                Item item = Event.ExtractParameter(e, 0) as Item;
                if (item.TemplateID == TemplateIDs.Language)
                {
                    foreach (Cache cache in CacheManager.GetAllCaches())
                    {
                        if (cache.Name == "LanguageProvider - Languages")
                        {
                            cache.Clear();
                        }
                    }
                }
            }
        }
    }
    
    
  7. Create new Sitecore Config patch file (LanguageSortOrderChanged.config ) and add the new event handler.
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <events>
          <event name="item:sortorderchanged" >
            <handler type="SitecoreCustomizedLanguageOrder.NotifyLanguageSortOrderChanged,SitecoreCustomizedLanguageOrder" method="OnItemSortorderChanged" />
          </event>
        </events>
      </sitecore>
    </configuration>
    
  8. Deploy LanguageSortOrderChanged.config file in \Website\App_Config\Include folder.


Source code can be downloaded from github. Comments and suggestions are most welcome. Happy coding!
Read More...

Sunday, 9 August 2015

Display flag icon in language dropdown in Sitecore 8

Leave a Comment
Sitecore UI and UX have been changed drastically with version 8 and Sitecore 8 provides a slick and powerful experience.  One thing you’ve surely noticed that you can’t see flag icon in language drop down in Sitecore 8 Content Editor.
The absence of the flags in Sitecore 8 is by-design. The list of all the flags in the world is changed quite frequently. That's why Sitecore decided that it's better not to show any flags, than having incorrect or not full list of flags. In my opinion, flag icons provides better UI experience and helps content editors to select item in a specific language easily. Visual content influences an individual’s brain in a faster and more comprehensible way than textual content.  I’ve configured flag icon manually for a specific language by going to configure tab and selecting appropriate icon but still I am not able to see flag icon in language drop down.
I’ve checked source code of Gallery Languages.xml file ( Website\sitecore\shell\Applications\Content Manager\Galleries\Languages) and able to see that display is set to none for Languages div.
div#Languages img {
        display: none;
        }
I’ve commented out display:none line but no success. Check below image to see how language drop down looks after commenting out line:
 I’ve overridden the OnLoad method of the GalleryLanguagesForm class and added the following code:
using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Data.Managers;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Security.AccessControl;
using Sitecore.Shell;
using Sitecore.Shell.Applications.ContentManager.Galleries;
using Sitecore.Shell.Applications.ContentManager.Galleries.Languages;
using Sitecore.Web;
using Sitecore.Web.UI.HtmlControls;
using Sitecore.Web.UI.Sheer;
using Sitecore.Web.UI.XmlControls;
using System;
using System.Globalization;

namespace SitecoreFlagIcon
{
    public class GalleryLanguagesForm : GalleryForm
    {
        /// <summary></summary>
        protected Scrollbox Languages;

        /// <summary></summary>
        protected GalleryMenu Options;
      
        /// <summary>
        /// Raises the load event.
        /// 
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        /// <remarks>
        /// This method notifies the server control that it should perform actions common to each HTTP
        ///             request for the page it is associated with, such as setting up a database query. At this
        ///             stage in the page lifecycle, server controls in the hierarchy are created and initialized,
        ///             view state is restored, and form controls reflect client-side data. Use the IsPostBack
        ///             property to determine whether the page is being loaded in response to a client postback,
        ///             or if it is being loaded and accessed for the first time.
        /// 
        /// </remarks>
        protected override void OnLoad(EventArgs e)
        {
            Assert.ArgumentNotNull((object)e, "e");
            base.OnLoad(e);
            if (Context.ClientPage.IsEvent)
                return;
            Item currentItem = GalleryLanguagesForm.GetCurrentItem();
            if (currentItem == null)
                return;
            using (new ThreadCultureSwitcher(Context.Language.CultureInfo))
            {
                foreach (Language language in currentItem.Languages)
                {
                    string languageIcon = string.Empty;
                    ID languageItemId = LanguageManager.GetLanguageItemId(language, currentItem.Database);
                    if (!ItemUtil.IsNull(languageItemId))
                    {
                        Item obj = currentItem.Database.GetItem(languageItemId);
                        if (obj != null)
                        {
                            languageIcon = obj["__icon"];
                        }
                        if (obj == null || !obj.Access.CanRead() || obj.Appearance.Hidden && !UserOptions.View.ShowHiddenItems)
                            continue;
                    }
                    XmlControl xmlControl = ControlFactory.GetControl("Gallery.Languages.Option") as XmlControl;
                    Assert.IsNotNull((object)xmlControl, typeof(XmlControl));
                    Context.ClientPage.AddControl((System.Web.UI.Control)this.Languages, (System.Web.UI.Control)xmlControl);
                    Item obj1 = currentItem.Database.GetItem(currentItem.ID, language);
                    if (obj1 != null)
                    {
                        int length = obj1.Versions.GetVersionNumbers(false).Length;
                        string str1;
                        if (length != 1)
                            str1 = Translate.Text("{0} versions.", (object)length.ToString());
                        else
                            str1 = Translate.Text("1 version.");
                        string str2 = str1;
                        CultureInfo cultureInfo = language.CultureInfo;
                        xmlControl["Header"] = (object)(cultureInfo.DisplayName + " : " + cultureInfo.NativeName);
                        xmlControl["Description"] = (object)str2;
                        xmlControl["Click"] = (object)string.Format("item:load(id={0},language={1},version=0)", (object)currentItem.ID, (object)language);
                        xmlControl["ClassName"] = !language.Name.Equals(WebUtil.GetQueryString("la"), StringComparison.OrdinalIgnoreCase) ? (object)"scMenuPanelItem" : (object)"scMenuPanelItemSelected";
                        var lang = LanguageDefinitions.GetLanguageDefinition(language);
                        if(lang!=null)
                            languageIcon = lang.Icon;
                        
                     xmlControl["Icon"] = languageIcon; 
                    }
                }
            }
            Item obj2 = Sitecore.Client.CoreDatabase.GetItem("/sitecore/content/Applications/Content Editor/Menues/Languages");
            if (obj2 == null)
                return;
            this.Options.AddFromDataSource(obj2, string.Empty);
        }

        /// <summary>
        /// Gets the current item.
        /// 
        /// </summary>
        /// 
        /// <returns>
        /// The current item.
        /// </returns>
        private static Item GetCurrentItem()
        {
            string queryString1 = WebUtil.GetQueryString("db");
            string queryString2 = WebUtil.GetQueryString("id");
            Language language = Language.Parse(WebUtil.GetQueryString("la"));
            Sitecore.Data.Version version = Sitecore.Data.Version.Parse(WebUtil.GetQueryString("vs"));
            Database database = Factory.GetDatabase(queryString1);
            Assert.IsNotNull((object)database, queryString1);
            return database.GetItem(queryString2, language, version);
        }

        /// <summary>
        /// Handles the message.
        /// </summary>
        /// <param name="message">The message.</param>
        public override void HandleMessage(Message message)
        {
            Assert.ArgumentNotNull(message, "message");
            if (message.Name == "event:click")
            {
                return;
            }
            base.Invoke(message, true);
        }
    }
}
Compile the solution and put dll in bin folder of website root. I’ve also duplicated Gallery Languages.xml file and copied it at Website\sitecore\shell\Override\Applications\Content Manager\Galleries\Languages location and also updated Gallery Languages code-beside class to use our code-beside class instead of Sitecore's default class and that’s it! I am able to see flag icon for languages in Sitecore Content tree.
Below is the source code of Gallery Languages.xml.
<?xml version="1.0" encoding="utf-8" ?> 
<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense" xmlns:shell="http://www.sitecore.net/shell">
  <Gallery.Languages>
    <Gallery>
    <CodeBeside Type="SitecoreFlagIcon.GalleryLanguagesForm,GalleryLanguagesForm"/>

    <!--  <CodeBeside Type="Sitecore.Shell.Applications.ContentManager.Galleries.Languages.GalleryLanguagesForm,Sitecore.Client"/> -->
      <Script>
        window.onload = function() {
          var activeLanguage = document.querySelector('.scMenuPanelItemSelected');
          activeLanguage.scrollIntoView(false);
        }
      </Script>
      

      <Stylesheet Key="GalleryLanguages">
        .scMenuPanelItem, .scMenuPanelItem_Hover, .scMenuPanelItemSelected_Hover, .scMenuPanelItemSelected {
        padding-left: 0;
        padding-right: 0;
        padding-top: 8px;
        padding-bottom: 8px;
        }
        .scGalleryGrip {
        position: absolute;
        bottom: 1px;
        left: 1px;
        right: 1px;
        height: 10px;
        }
        .scLanguagesGalleryMenu {
        overflow: hidden;
        vertical-align: top;
        border-bottom: 12px solid transparent;
        -moz-box-sizing: border-box;
        box-sizing: border-box;
        width: 100%;
        height: 100%;
        border-collapse: separate;
        }
        div#Languages img {
        //display: none;
        }
      </Stylesheet>

      <Border Width="100%" Height="100%">

        <GalleryMenu ID="Options" Class="scLanguagesGalleryMenu">

          <MenuPanel Height="100%">
            <Scrollbox ID="Languages" Class="scScrollbox scFixSize scFixWidthInsideGallery" style="padding-top:0 !important;" Height="100%" Width="100%" />
          </MenuPanel>

        </GalleryMenu>

        <Gallery.Grip />

      </Border>

    </Gallery>
  </Gallery.Languages>
</control>

Source code can be downloaded from github.  Comments and suggestions are most welcome. Happy coding!
Read More...