Sitecore Multiple Publish Queue (Parallel Publishing)


Are you facing a long long queue stuck up with Sitecore publishing and clients are complaining a lot? Then surely you might have think about having multiple publish queue.

See my other post regarding Practical Implementation of Sitecore Parallel or simultaneous Publishing using multiple Publish Queue.

Benefits of Parallel Publishing:

  1. More than one user can do publishing simultaneously, so no more "Publishing Queued".
  2. Less load on CM environment, where actually users are editing or previewing pages as publishing load is now shared with Publish Instances.
  3. Less chances of Publish Queue stuck-up as Publish Jobs threads are now divided between multiple instances.
  4. If one publish instance is stuck-up, then we can move publishing traffic to another instance.

What is expected?

The expectation is to do parallel publishing on two or more Sitecore Instances. Say, our Content Authors' publish should go to PI-1 and clients' publish should go to PI-2. Or, Content Editor publish should go to PI-1 and Page Editor publish should go to PI-2.




This idea is quite possible and easier too. Let's see how can we use multiple publish queue. For that, you might need to understand How Sitecore Publish Instance works.

What is the Challenge?

As we see from the above link, Sitecore allows to add only one PublishInstance name, as per below setting so publishing will be done from that server itself.
        <setting name="Publishing.PublishingInstance">
           <patch:attribute name="value">CM-PI-1</patch:attribute>
        </setting>
So, the problem is well described in below image. See the red balloon points.
Ohhhh.... how to come out from this challenges, as Sitecore does not support this? Well, we have an alternate to overcome this issue.

Solution to Setup Multiple Publish Instances

As we seen, when Sitecore CM sets publish, it is recorded in EventQueue and then the related PI will pickup that publish and start publishing. Can't we do the same process by bypassing EventQueue? I mean we can prepare our own Event Queue.

So, that's really possible now. Also, don't worry about caching updates, means items updated/published on CM or PI-1 or PI-2 will get reflected on other servers immediately using Event Queue. So, our main concern should be publishing only.

There can be two different approaches to achieve this:
  1. Creating own table same as EventQueue
  2. By Invoking Web Service to start Publish

1. Creating own table same as EventQueue

  • We can create own table, which can have fields like below:

    Fields in MyPublishQueue
    ID
    ItemId
    SubItems
    Languages
    PublishType
    PublishInstance

    So, when user sets publish from CM, we will not start publish. We will just make entries in above table, with appropriate Publish Instance Name.
  • Now, both PI will have a our new Sitecore scheduler running. This scheduler should be triggered every few time interval as per our need, same as EventQueue checks. Write down some code to find any publishes for their own instance, and starts publish there itself. Refer Alex Shyba's blog to Create Sitecore Scheduler.

Below image shows how to achieve this approach. Here, as what we expect, Content Editor publish should go to PI-1 and Page Editor publish should go to PI-2, which is maintained by our Custom EventQueue table.

Parallel Sitecore Publish using custom EventQueue

This approach is very useful when we use for Scheduled Publish.

2. By Invoking Web Service to Start Publish

We can create a web service on both PIs, with input parameters as below:
  • ItemId
  • SubItems
  • Languages
  • PublishType (Smart/Republish)
This web service will do publish according to the passed items. So, when user sets publish from CM for PI-1 or PI-2, web service of respective PI should be invoked. So, publishing will be started by the webservice itself.
Below image shows how to achieve this approach.

Parallel Publish using WebService


This approach is very useful when we are not using Scheduled Publish.

Finally, you might be knowing how to start publishing using Sitecore. If No? See the code below:
public void PublishItem(Sitecore.Data.Items.Item item, User user)
{
 using(UserSwitcher(user))
 {
  // The publishOptions determine the source and target database,
  // the publish mode and language, and the publish date
  Sitecore.Publishing.PublishOptions publishOptions =
  new Sitecore.Publishing.PublishOptions(item.Database,
              Database.GetDatabase("web"),
              Sitecore.Publishing.PublishMode.SingleItem,
              item.Language,
              System.DateTime.Now);  // Create a publisher with the publishoptions
  Sitecore.Publishing.Publisher publisher = new Sitecore.Publishing.Publisher(publishOptions);

  // Choose where to publish from
  publisher.Options.RootItem = item;

  // Publish children as well?
  publisher.Options.Deep = true;

  // Do the publish!
  publisher.Publish();
 }
}

Note: Using multiple Publish Instance can be done with 3 Sitecore instances CM, PI-1 & PI-2. We can also do parallel publish with total two instances too CM & PI-1.

Sitecore does not recommend to have Multiple Publishing Instances. But, using above approaches, we implemented Parallel Publishing in 2012 with Sitecore 6.4.1, published millions of items with parallel publish, and still counting.

No need to say but having multiple Sitecore instances requires license accordingly.


No words to say about great Sitecore Architecture!!

Must read posts for Sitecore Publish

- Sitecore Publishing Facts
- Intelligent Publish in Sitecore - The most optimized approach
- Sitecore Setup Separate Publish Instance

Site-wise languages in Sitecore Ribbon for multisite environment

Are you working in Sitecore multisite environment and different sites are built in different languages? Then one idea should come in your mind that can't we set site-wise languages to the Page Editor's Webedit Ribbon and Content Editor's Language Gallery Form.

By default, the ribbon shows all languages defined in /sitecore/system/Languages. In our Sitecore instance, there are 40+ languages and many different culture sites. So, this idea came in our mind and implemented.

See below snap, there are total 5 languages in System.

Default Languages in Content Editor


Default Languages in Page Editor


Expected Behavior

Now, suppose, your site A uses 2 languages and site B uses other 3 languages. Then selecting item of site A, it should show 2 languages and same for site B, it should show 3 languages.

In this example, the site is built in 3 languages: En, hi-IN, de-DE. So, the ribbon should not show other languages en-GB and fr-FR. See below snap what is expected.

Solution

Prepare template to choose site-wise languages

We can have a multilist field to the Home Item of each site, where we can choose site's languages from the whole list from /sitecore/system/languages. See below screen.


Change Languages in Content Editor Gallery

Open file: \Website\sitecore\shell\Applications\Content Manager\Galleries\Languages\Gallery Languages.xml Change the code beside to your custom code. SO, update the file as below:
     <!-- comment below line -->
     <!--<CodeBeside Type="Sitecore.Shell.Applications.ContentManager.Galleries.Languages.GalleryLanguagesForm,Sitecore.Client"/>-->

     <!-- add below line -->
     <CodeBeside Type="SitecoreTactics.GalleryLanguagesForm, SitecoreTactics"/>
Code in the CodeBeside file: Create a custom class, which will be a repilca of the Sitecore.Shell.Applications.ContentManager.Galleries.Languages.GalleryLanguagesForm class. We just need to override OnLoad event in this class. So, this will be look like..
namespace SitecoreTactics
{
 public class GalleryLanguagesForm : Sitecore.Shell.Applications.ContentManager.Galleries.GalleryForm
 {
  // Other methods

   protected override void OnLoad(EventArgs e)
  {
   Assert.ArgumentNotNull(e, "e");
   base.OnLoad(e);
   if (!Context.ClientPage.IsEvent)
   {
    Item currentItem = GetCurrentItem();
    if (currentItem != null)
    {
     // Comment below line from the code
     //foreach (Language language in currentItem.Languages)

     // Instead of all languages, we will pass only selected languages
     LanguageCollection languages  = GetMySelectedLanguages();
     foreach (Language language in languages)
     {
      // Same code which is in foreach block
     }
    }
   }
  }
  
  // Other methods

 }
}

Download above sourcecode

Here is the expected output:

Change languages in Page Editor

For this, we need to update the command from Commands.config file as below:
     <!-- comment below line -->
     <!--<command name="webedit:changelanguage" type="Sitecore.Shell.Applications.WebEdit.Commands.ChangeLanguage,Sitecore.Client"/>-->


     <!-- add below line -->
     


Code in the class file: Create a custom class, which will be a repilca of the Sitecore.Shell.Applications.WebEdit.Commands.ChangeLanguage class. We just need to overrideExecute event in this class. So, this will be look like..
namespace SitecoreTactics
{
 public class ChangeLanguage : WebEditCommand
 {
  // Other methods
 
  public override void Execute(CommandContext context)
  {
   Assert.ArgumentNotNull(context, "context");
   if (context.Items.Length == 1)
   {
    Item item = context.Items[0];
    
    // Comment below line
    //LanguageCollection languages = LanguageManager.GetLanguages(item.Database);
    
    // Instead of all languages, we will pass only selected languages
    LanguageCollection languages  = GetMySelectedLanguages();
    
    SheerResponse.DisableOutput();
    Menu control = new Menu();
    foreach (Language language in languages)
    {
     string id = "L" + ShortID.NewId();
     string languageName = GetLanguageName(language.CultureInfo);
     string icon = LanguageService.GetIcon(language, item.Database);
     string click = "webedit:setlanguage(language=" + language.Name + ")";
     control.Add(id, languageName, icon, string.Empty, click, false, string.Empty, MenuItemType.Normal);
    }
    SheerResponse.EnableOutput();
    SheerResponse.ShowPopup("ChangeLanguageButton", "below", control);
   }
  }
  
  // Other methods
 }
}
Here is the expected output:


Cheers, finally we achieved site-wise selected languages for each site for our multisite Sitecore environment.

Other Language specific customizations - used for multisite environment:
-  Why my Sitecore Media Item created in multiple languages?
-  Upload Sitecore media items in selected languages

Hostname Resolution without editing Host File

Have you ever thought to map hostname without changing host file? If your Sitecore or any other website is working with Load Balanced environment with multiple servers, you surely have thought to point your request to a particular server (IP Address). When you need to check your functionality working/not working on any of these servers, you are surely going to make host entry for individual servers one by one and test the site.

Uffff... it's so boring and lengthy task and chances of human mistakes. Let's see how we can automate this.

Traditional way for Host Name Resolution Using a Hosts File

Find Hosts file in the %SystemRoot%\system32\drivers\etc directory. Read more about Host Name Resolution. Following is an example of the contents of the Hosts file:
#
# Table of IP addresses and host names
#

127.0.0.1 localhost
192.168.220.111 www.patelyogesh.in # Server-1
#192.168.220.112 www.patelyogesh.in # Server-2

How we can bypass hosts file for host name resolution

Traditional way to get response using WebRequest is requesting to http://www.patelyogesh.in but this will get content from any of the servers which Load Balancer choose for our request.

We can get content from particular server by requesting to: http://<IP Address>/<Page> and passing host/domain name as request.Host as below:
      
var request = (HttpWebRequest)WebRequest.Create("http://<IP Address>/<Page>");
request.Host = HostName;
Let us see how whole source code / GetResult function will be to achieve this:
      string strIPAddress = "192.168.220.111";
      string strHostName = "www.patelyogesh.in";

      string result = GetResult(strIPAddress, strHostName);

      public string GetResult(string strIPAddress, string strHostName)
      {
            var request = (HttpWebRequest)WebRequest.Create("http://" + strIPAddress);
            request.Host = strHostName;

            request.Credentials = CredentialCache.DefaultCredentials;
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // Get the stream containing content returned by the server.
            Stream dataStream = response.GetResponseStream();

            // Open the stream using a StreamReader for easy access.
            StreamReader reader = new StreamReader(dataStream);

            // Read the content. 
            string responseFromServer = reader.ReadToEnd();

            // Cleanup the streams and the response.
            reader.Close();
            dataStream.Close();
            response.Close();
            return responseFromServer;
      }
Again, www.patelyogesh.in is mapping to 192.168.220.111. Now suppose we want to request to http://www.patelyogesh.in/AboutUs.html page, we can pass parameters as:

var request = (HttpWebRequest)WebRequest.Create("http://192.168.220.111/AboutUs.html");
request.Host = "www.patelyogesh.in";
Thus we can access all the pages using C# .NET 4.5 bypassing host file using above source code.

Hope you like this idea! Cheers!!

Sitecore bypass login without password

This blog explains how to bypass login for any Sitecore user or how to implement Single Sign-On on Sitecore to automate user login without knowing password.

Practical Use

Suppose, you have a non-Sitecore application, which has different users and all its users also access Sitecore by login into it. Now, switching between these two sites is really repetitive and time-consuming task, need to remember passwords for two websites, etc. If we provide SSO (Single Sign-On) here so that the user logs in only once and access both Sitecore and non-Sitecore websites with single login.

How to achieve

Create a web page in Sitecore website suppose sso.aspx, that will do login in Sitecore with passed userame via querystring. After login, we have to redirect user to welcome.aspx.

So, from the non-sitecore application, we will call url: http://mysitecore.com/sso.aspx?username=user_sitecore. On load of the page, we will login using user_sitecore and redirect user to welcome.aspx.

But make sure you encrypt username before passing in querystring. You can also pass a unique token number (A random generated number/string) along with username and encrypt it to make it more secured.

Login without password is possible using Sitecore.Security.Authentication.AuthenticationManager.Login(username) function. See below source code:

public partial class SSO : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
         string strUserName = Request.QueryString["username"];
         if (Sitecore.Security.Authentication.AuthenticationManager.Login(strUserName))
         {
               Response.Redirect("welcome.aspx", true);
         }
    }
}

Hurrey.... we did login without giving user password.

Sitecore Publish Instance

Are you not getting any direction to improve your Content Management server performance due to publishing? Are you facing slowness while doing publishing or having publishing queue stuck-up issues? Separate Publish Instance is one of the solution.

How to setup Sitecore Publish Instance?

  1. Sitecore PI is nothing but a clone instance of Sitecore CM environment.
  2. On CM server, open \Website\App_Config\Include\Scalability.config file. Update Publish Instance Name as below.
            <setting name="Publishing.PublishingInstance">
               <patch:attribute name="value">PI-Sitecore</patch:attribute>
            </setting>
    
    If the value is empty Sitecore will use the instance name by combining the machine name and the IIS Site name. So for the IIS Site Sitecore on the server PI the instance name would become PI-Sitecore.
    Just to remember, do not configure above setting on Publish Instance.
    Note: Sitecore allows to add only one publish instance name in this setting.
  3. Enable EventQueue on both CM and PublishInstance servers. To enable EventQueues, from web.config, find EnableEventQueues setting. Set its value to true. This setting can also be set from \App_Config\Include\ScalabilitySettings.config, which will be given more precedence over web.config settings.
          <setting name="EnableEventQueues">
            <patch:attribute name="value">true</patch:attribute>
          </setting>
    
  4. Disable any scheduled tasks or agents running if not required.

How Publishing works on Publish Instance?

Below image shows the flow of publishing from CM to PI server.
Sitecore Publish Instance Architecture


Suppose, Sitecore CM Application has Instance named Staging-Sitecore (In image: CM1), and Publish Instance is named PI-Sitecore (In image: CM-PI-1). Now, as per above settings, Sitecore CM1 knows that it's publishing should be done by Publish Instance: PI-Sitecore.

Now, when a user sets a publish job, Sitecore adds its entry in EventQueue table with a flag/column of PublishInstance. It simply adds PI-Name in that column's value. So, every publish job is assigned its Publish Instance Name. Now, as per EventQueue architecture every Sitecore Instance checks for EventQueue every few seconds to fetch which events/jobs it has to perform. Here, when CM1 server checks EventQueue, it will not find any publish job, where PI will find records with its name. So, it will fetch those publish details and start publishing those publish jobs.

Now, as per architecture, all other instances will start updating cache of those published and related items, to get updated.

Can we utilize Publish Instance for other tasks?

- PI is not only PI, which can still be used as CM. So, we can have two Content Management servers. So, we can make less used CM as PI.

- If we are using it only for Publishing, then we can reduce load of CM server by moving all backend tasks or jobs(except Sitecore default) to PI.

How many instances Sitecore supports?

Logically, unlimited. We can have multiple Sitecore Instances working as CM with/without load-balanced environment with one PI.

As per Sitecore architecture, only one PI is recommended. But after reviewing publishing code and my experiements done locally, I concluded that we can have two PIs too. But, that will work with minor architecture change and only one limitation that an item can not be published on both PIs simultaneously.

Must read posts for Sitecore Publish

- Sitecore Publishing Facts
- Intelligent Publish in Sitecore - The most optimized approach
- Sitecore Parallel Publishing using Multiple Publish Instances

Sitecore on Cloud - Windows Azure and Amazon EC2

We investigated a lot of time which cloud service is better for our Sitecore architecture. It is always good to know about reliability, efficiency, scalability and agility for the cloud service we are going to use.

Here are some points I concluded while working on both of Microsoft Azure and Amazon EC2. Azure is a IaaS, while EC2 is PaaS, so surely both are having some pros and cons. Below are few points I collected while working on it.


Points Windows Azure Amazon EC2
Computing Category Infrastructure as Service Platform as Service
Guaranteed Network Availability

99.999%

99.9%
SQL Database
Database Capacity

Allows maximum 150GB size of SQL Azure database. We can then use a cloud VM for using SQL.

No limitation on size of database, as need a separate VM.

Database Backup/Restore

Many limitations in backup/restore on SQL Azure a more mature environment for backup/restore than Azure
Load Balancers
Sticky Session Configuration Does not support Sticky sessions, so session based functionalities will not work Still, they are paid. - Free and allows sticky session configurations.
Multi-region load-balancing Can not support multi-region servers. Can not support multi-region servers, but we can use 3rd party services to handle multi-region servers
Deployment
Quick deployment Sitecore Azure works superbly if you have valid Sitecore and Sitecore Azure versions, which makes automated deployment so easy and quick. Very easy to create multiple sitecore instances using it. Deployment is not automated as they provides a remote console, still automated deployment is possible using WebDeploy.
Troubleshooting It is very difficult to troubleshoot deployment problems faced using Sitecore Azure. Manual deployment does not create any issues
Backup and Monitoring Has some basic monitoring system AWS
Support Paid Support Free Support

If you want to try and experiment cloud services, go to Windows Azure or Amazon EC2. Both of them provides free cloud services for limited time period.

Upload Sitecore media items in selected languages

One day, one idea came in my mind to create media items in specific languages only, which is very useful thought. By default, Sitecore media item gets created in many languages, where we need actually one or two out of them. Want to know why? Then read why sitecore media item gets created in multiple languages.

NOTE: This issues was recreated in Sitecore 7.2 and older versions. Sitecore 8 and later creates media items only in context language.

Specific languages for Unversioned Media Item

How it's benefitial?
In Unversioned media item, item gets created in all languages exist under /sitecore/system/languages/. Creating its version in selected languages is benefitial:
  1. Less insert queries to DB tables, less records fetched while fetching media item details.
  2. DataCache and ItemCache will be created in selected languages only instead of all languages.
  3. Reduced Database size, where millions of media are stored.
  4. So, overall performance improvement while uploading media item and while using/fetching media item.
How to achieve it?
  1. Create your custom class inherited from the Sitecore.Resources.Media.MediaCreator one, say SitecoreTactics.Resources.Media.MediaCreator. In this class you should override CreateItem method to retrieve there the set of the languages from the web.config setting. This set should be used as the languageArray in the foreach cycle. Thus the unversioned mediaitem with certain language versions will be created.

    See how it looks:
    namespace SitecoreTactics.Resources.Media
    {
     using System.Reflection;
     public class MediaCreator : Sitecore.Resources.Media.MediaCreator
     {
      protected override Item CreateItem(string itemPath, string filePath, MediaCreatorOptions options)
      {
       // Override the CreateItem function method and write custom code to create selected language versions
      }
      }
    }
    
    
  2. Create your custom class inherited from Sitecore.Resources.Media.MediaProvider one. In the constructor of this class you should assign an instance of our custom MediaCreator class (from Step#1) to the MediaProvider "creator" private field using the Reflection. So, the MediaProvider will use our own code of MediaCreator.

    How the code looks:
     namespace SitecoreTactics.Resources.Media
     {
      using System.Reflection;
      public class MediaProvider : Sitecore.Resources.Media.MediaProvider
      {
       public MediaProvider() : base()
       {
        typeof(Sitecore.Resources.Media.MediaProvider).GetField("creator", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this, new SitecoreTactics.Resources.Media.MediaCreator());
       }
      }
     }
    
    
  3. Change in config, register our custom mediaprovider class as below:
    <mediaLibrary>
        ...
        <mediaProvider type="SitecoreTactics.Resources.Media.CustomMediaProvider,SitecoreTactics.Resources" />
        ...
    </mediaLibrary>
    
    

Wow, it worked well!

Specific languages for Versioned Media Item

Versioned Media Item does not need an automated way to upload media in selected languages. The reason is, this kind of media does not share Media File. So, it is used only when we need to upload different files for different languages of same media item. If still someone need to achieve it, here is the way:
  1. Add a settings in the web.config file containing set of the pipe separated needed languages. For example:
    <setting name="SetOfLanguages" value="en|ru-RU|en-GB" />
    
  2. Add an extra checkbox "Versionable For Certain Languages" on the Media Folder Upload Form using this file: \Website\sitecore\shell\Applications\Media\MediaFolder\MediaFolder.xml
  3. Create a custom class inherited from Sitecore.Pipelines.Upload.UploadArgs and add here an extra "SpecialVersioned" property. This property will indicate to Sitecore.Pipelines.Upload.Save processor to execute the custom actions.
  4. Create a custom class based on Sitecore.Shell.Applications.FlashUpload.Advanced.UploadTarget one and define it in \Website\sitecore\shell\Applications\FlashUpload\Advanced\UploadTarget.aspx file instead of the default one.
  5. In this class you should assign a value to your custom UploadArgs.SpecialVersioned property similar to the UploadArgs.Versioned one.
  6. Create your custom class based on the Sitecore.Pipelines.Upload.Save one and register it in the web.config <uiUpload> pipeline instead of the default one.
  7. An example of its rewritten Process method is here: Expand Code
It's working great, isn't it?
And yes, many thanks to Yuriy Zakharov from Sitecore Support to provide such a nice solution!!

Sitecore Custom HTTP Handlers

We recently had a requirement that we need thumbnail for PDFs stored in Sitecore, so whenever we need to find PDFs or other media files, we can find out them by its thumbnail. In starting we thought to create a thumbnail for each media file and then store it to another location in Sitecore tree itself while uploading media. It was a good idea, but the problem in this approach would come when a user updates the media? We need to create thumbnail again... Same thing when user deletes/moves/publishes, etc. it needed to generate thumbnail all the time.

Finally we came to a perfect solution of using Custom HTTP Handler, using which we can fulfill this requirement easily.

Overview of Sitecore Custom Handlers?

Search for in your web.config. These are the custom handlers called by Sitecore to serve requests of media, xaml, icon, feed, etc. See below block in web.config:
    
      <handler handler="sitecore_media.ashx" trigger="~/media/" />
      <handler handler="sitecore_api.ashx" trigger="~/api/" />
      <handler handler="sitecore_xaml.ashx" trigger="~/xaml/" />
      <handler handler="sitecore_icon.ashx" trigger="~/icon/" />
      <handler handler="sitecore_feed.ashx" trigger="~/feed/" />
    
Sitecore.Pipelines.HttpRequest.CustomHandlers processor from httpBeginRequest dispatches requests to specialized handlers that are required. These handlers are defined in web.config under configuration/sitecore/customHandlers.

If the current request ends with the a handler’s file (e.g., sitecore_media.ashx), then Sitecore aborts the current pipeline, since this request is specifically targeting a handler directly, which are defined in system.webServer/handlers section as below, which shows that the media request will be served through Sitecore.Resources.Media.MediaRequestHandler. See below block of web.config:
   <handlers>
      <add name="Sitecore.MediaRequestHandler" path="sitecore_media.ashx"  type="Sitecore.Resources.Media.MediaRequestHandler, Sitecore.Kernel" verb="*" />
   </handlers>

Create own Sitecore Custom Handlers to generate thumbnails of media items

Now, we created a new custom handler for creating thumbnail and registered in handlers as below:
    
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>

    <!-- Define Custom Handler -->
    <customHandlers>
      <handler trigger="~/mediathumb/" handler="sitecore_media_thumb.ashx"  />
    </customHandlers>

    <!-- Define Media Prefix -->
    <mediaLibrary>
      <mediaPrefixes>
        <prefix value="~/mediathumb" />
      </mediaPrefixes>
    </mediaLibrary>
  </sitecore>


  <!-- Define Web Handler -->
  <system.webServer>
 <handlers>
     <add verb="*" path="sitecore_media_thumb.ashx" type="SitecoreTactics.ThumbnailManager.PDFThumbnailRequestHandler, SitecoreTactics.ThumbnailManager" name="SitecoreTactics.PDFThumbnailRequestHandler"/>
 </handlers>
  </system.webServer>
</configuration>    
This class will create a thumbnail of the request PDF on-the-fly using GhostScript. We will save this thumbnail as Media Cache, so on the next request, the thumbnail will not be created, but will be served from Media Cache itself, same as Sitecore serves media items.

In this case suppose, the PDF we uploaded has URL:
http://mydomain.com/~/media/files/Docs/mypdf.pdf.
Now, we can get its thumbnail as JPG file using URL, which will be served by our new handler because it contains /~/mediathumb trigger.
http://mydomain.com/~/mediathumb/files/Docs/mypdf.jpg.

Finally, we do not need to take care if media item is moved, deleted, published or updated. I will attach its source code very soon.... :)

What else we can do using Custom Handlers?

- In above approach, we can also generate thumbnail of different size, dimensions passed through querystring, same way Sitecore provides it for images. Read how we can Resize Sitecore Media Images on the fly.
- Even Custom Handlers can be useful to serve different kind of resources like creating sitemap (/~/sitemap/), RSS for page or its children (/~/rss/), stylesheets (/~/css/), javascripts (/~/js/), etc.

Related Posts:
- PDF Thumbnail Handler
- Show PDF Thumbnail Icons in Content Editor

Sitecore media url - remove ashx extension

Have you ever marked all your media items are rendered in our page output as .ashx extension? By default, Sitecore renders all media files or images (.jpg, .gif, .pdf, .xls, etc.) as .ashx extension.

The same thing you will find while getting mediaurl for your media items.

You can render media with its own extension using Media.RequestExtension setting in web.config.
It defines the extension to use in media request URLs. It's default its value is ashx, so all media url are .ashx.
So, media's URL will be: http://mydomain.com/~/media/Images/Banner/image.ashx
<setting name="Media.RequestExtension" value="" />

Setting its value to blank will render media url with relevant extension. Now, all your media items will be rendered in page output with its valid extension like, http://mydomain.com/~/media/Images/Banner/image.jpg
Still, your existing URL http://mydomain.com/~/media/Images/Banner/image.ashx will also work.

Image url can have different querystring parameters. Read more for Image Control and QueryString parameters.

How to speed up Sitecore application startup?

Is your Sitecore application having slow start-up time and want to improve it? The main reason behind slowness is bigger Prefetch Cache. Application's startup time is directly proportional to size of Prefetch Cache set.

If you set Bigger Prefetch, it gives slow startup, but it can give you better performance while opening Content Editor or Page Editor because your items' data are already fetched from Database to Prefetch Cache.

In reverse, if you get Smaller Prefetch, it gives fast startup, but it can give you less performance while opening Content Editor or Page Editor because your items' might not be fetched to Prefetch from database. Thus, setting bigger or smaller amount of Prefetch, both have their own pros and cons.

Prefetch Cache Best Practices

If prefetch is customized according to our use of pages of site, it will give you the best performance. For that you have to findout three things:
- Which are the pages used the most in your site like Homepage.
- Which are the pages, whose children are accessed frequently.
- Which kind of pages(templates) are used the most.
Then, try to Prefetch all those items which are used in these pages.

For example,
- Homepage is the most frequently visited page, then you should prefetch Home item and its immediate children.
- Similarly suppose News pages are visited frequently, then you should prefetch News Template and their children too.

How to configure Prefetch Cache

Open the prefetch cache settings under the App_Config/Prefetch/ folder. For example /App_Config/Prefetch/Web.Config:

  
  300MB

  
  100 

  
  

  
  {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX }

  
  {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}

See how these configurations will work:
  • <cachesize>300MB</cachesize>
    describes Sitecore will do prefetch upto 300MB.
  • <template desc="mytemplate">{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}</template>
    Defines that items of the specified template will be prefetched.
  • <item desc="myitem">{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX }</item>
    Defines that the specified item will be prefetched.
  • <children desc="childitems">{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}</children>
    Defines that specified children of the given item will be prefetched.
  • <childlimit>100</childlimit>
    Defines a limit to the number of children to include in the prefetch cache.

The Sitecore Prefetch Cache is a bit of a gray area. But, the above settings work great and surely help you to improve application startup.

If you are new to Sitecore cache, refer How Sitecore Caching Work and Performance Improvement Techniques.

Enjoy quick start up!!

Why my Sitecore Media Item created in multiple languages?

Have you ever faced that you upload a single media file to Sitecore and it gets created in multiple languages? This is not strange but expected behavior in Sitecore. Let's see how.

Check Media.UploadAsVersionableByDefault setting in your web.config. If it is false, then uploaded media item will be created in all those languages which exist under /sitecore/system/languages. This media item will use unversioned template /sitecore/templates/System/Media/Unversioned/File template, so the Blob(Media) and its common details would be shared across all language versions. See below image.



This option is really helpful when you want to use common media files across multiple languages content pages as the media is shaerd across all languages.


Where, if you have this setting as true, then each media item will be generated in default language only. This media item will use versioned template /sitecore/templates/System/Media/Versioned/File template, so the Blob(Media) and its all details would be distinct across languages. See below image.



This option is really helpful when you want to use different media files for different language content pages.

We can choose whether we need versioned or unversioned media. While doing Upload File (Advanced), Sitecore asks to select Make Uploaded Media Items Versionable checkbox. If the checkbox is selected, it will create versioned media, otherwise will create unversioned media items. See below image:



You want to upload media items in Sitecore with your selected languages only? Refer my new post: Upload sitecore media items in selected languages.

Sitecore partial cache clear programmatically

Sitecore is already smart enough to do cache clearing automatically. But in many cases, we might need to do manually clear different cache for particular items. We can do partial cache clear as below for any item we want whether it is in Prefetch Cache, Data Cache, Item Cache, StandardValue Cache, HTML Cache, etc.

Let's see how we can clear different cache.

Clear Item level Cache - Prefetch Cache, Data Cache, Item Cache, Standard Value Cache

Suppose, we want to clear Prefetch cache, Data Cache and Item Cache for /sitecore/content/Homepage/
    string itemPath = "/sitecore/content/Homepage/";
    Item home = Sitecore.Context.Database.GetItem(itemPath);

    public void ClearItemLevelCache(home)
    {
        // Clear item's Item Cache
        Sitecore.Context.Database.Caches.ItemCache.RemoveItem(home.ID);

        // Clear item's Data Cache
        Sitecore.Context.Database.Caches.DataCache.RemoveItemInformation(home.ID);

        // Clear item's Standard Value Cache
        Sitecore.Context.Database.Caches.StandardValuesCache.RemoveKeysContaining(home.ID.ToString());

        // Clear item's Prefetch Cache
        CacheManager.GetSqlPrefetchCache(home.Database.Name).Remove(home);
    }

    public static Cache GetSqlPrefetchCache(string database)
    {
      return Caching.CacheManager.FindCacheByName("SqlDataProvider - Prefetch data(" + database + ")");
    }

Clear Sitewise Cache

Suppose, we want to clear cache for Context site.
    SiteContext site = Context.Site;

    public void ClearSiteCache(SiteContext site)
    {
        SiteCaches siteCache = site.SiteCaches;

        // Clear HTML Cache
        siteCache.HtmlCache.Clear();

        // Clear Registry Cache
        siteCache.RegistryCache.Clear();

        // Clear ViewState Cache
        siteCache.ViewStateCache.Clear();

        // Clear FilteredItemsCache
        siteCache.FilteredItemsCache.Clear();

        // Clear XSL Cache
        siteCache.XSLCache.Clear();
    }

Clear other Sitecore level Cache

Similarly we can also clear PathCache, AccessResultCache, etc. as below:
    Caching.CacheManager.GetPathCache(Sitecore.Context.Database).Clear();
    Caching.CacheManager.GetAccessResultCache().RemovePrefix(database.Name);
Sitecore.Caching.CacheManager provides all functions for getting and clearing all caches.

Update Sitecore Item programmatically

There are two ways where you can put an item in editing mode and update its fields.

1. Sitecore.Data.Items.ItemEditing

2. Sitecore.Data.Items.EditContext (Deprecated)

If you are saving any item field without using above statements, it might give you exception that Item was not in Edit Mode. Let's see how we can update item

Sitecore.Data.Items.ItemEditing

See below code which allows to save any item.
    Sitecore.Data.Database masterDB = Sitecore.Configuration.Factory.GetDatabase("master");
    Sitecore.Data.Items.Item home = masterDB.GetItem("/sitecore/content/Home");

    //Begin Editing Sitecore Item
    home.Editing.BeginEdit();
    try
    {
        home["Title"] = "Title from Code Behind";

        // This will commit the field value
        home.Editing.EndEdit();
    }
    catch (Exception)
    {
        //Revert the Changes
        home.Editing.CancelEdit();
    }
This function has three variants, means it allows to item save in two different options.
item.Editing.EndEdit()
This save will actually do 3 steps:
  1. Save item
  2. Update item statistics, means updates Modified Date, Modified By User, etc. statistics information of item
  3. It will execute events related to Item Save.
item.Editing.EndEdit(updateStatistics, silent)
This save will allow us to choose which operations to do. This is going to save item same as above. But it will do update statistics or execute events or not depending on the parameters passed.

If we pass updateStatistics as false, it will not update Modified Date and Modified By fields. This is really useful when we need to update items in background. This will update item in read-only mode. If we pass true, then it will update this information.

If we pass silent as true, it will temporarily disable events. So, event this item's save will not be logged in EventQueue.

Below is the code to understand what EndEdit does:
public bool EndEdit()
{
    return this.EndEdit(true, false);
}

public bool EndEdit(bool updateStatistics, bool silent)
{
    bool flag2;
    bool readOnlyStatistics = this.m_item.RuntimeSettings.ReadOnlyStatistics;
    try
    {
        if (!updateStatistics)
        {
            this.m_item.RuntimeSettings.ReadOnlyStatistics = true;
        }
        if (!silent)
        {
            flag2 = ItemManager.SaveItem(this.m_item);
        }
        else
        {
            using (new EventDisabler())
            {
                flag2 = ItemManager.SaveItem(this.m_item);
            }
        }
    }
    finally
    {
        this.m_item.RuntimeSettings.ReadOnlyStatistics = readOnlyStatistics;
    }
    return flag2;
}

Sitecore.Data.Items.EditContext

Note: Sitecore EditContext is deprecated,  and Sitecore team recommends using ItemEditing (1st approach).

The above thing is also possible using Sitecore.Data.Items.EditContext in below manner:

Below is example of Sitecore item save using Default parameters. Here, updateStatistis is by default true and silent is false.
Sitecore.Data.Database masterDB = Sitecore.Configuration.Factory.GetDatabase("master");
Sitecore.Data.Items.Item home = masterDB.GetItem("/sitecore/content/Home");

//Begin Editing Sitecore Item
using (new EditContext(home))
{
    home["Title"] = "Title from Code Behind";
}
We can also pass parameters of updateStatistics and silent in EditContext like below example.
bool updateStatistics = false;
bool silent = true;
using (new EditContext(home,updateStatistics, silent))
{
    home["Title"] = "Title from Code Behind";
}
We can also pass parameters of checking security or not while updating the item like below.
using (new EditContext(home, SecurityCheck.Disable))
{
    home["Title"] = "Title from Code Behind";
}