Sitecore – Remove Unreferenced / Unused media items

Hey Guys,

  • Do you have too many media items and you are not sure which items are unused or unreferenced?
  • Do you want to list-out/delete such unused or unrferenced media items?
  • Do you want a simple and easy way to do this?

Here is the tool available on the Sitecore Market Place which list-out all the unused / unreferenced media items: UnRef-Media. All the versions:  Sitecore  8.0 onwards are supported. It also supports Sitecore 7.2.

https://marketplace.sitecore.net/en/Modules/U/UnRefMedia.aspx

Best thing about this package/tool is that no extra configuration / dll is needed to run this. The package contains single aspx page.

  • Choose your database
  • Choose the root folder of Media
  • Hit the “Go” button

And you will get the list of unreferenced media.

  • You can sort this list based on the item-name or item-path
  • You can also search for perticular item by its name or path
  • You can delete / move single/multiple items to recycle bin.
  • Preview on hover

If you are confused whether to delete the media item just by its name then hover on the media name and you will get preview of the image. Click on the media item name to view full image.

Below is the demo how this tool works:

Unused / Unreferenced Media

 

How to know current date time of Sitecore Content Management Server

What if you are accessing a Sitecore Content Management (CM) server which is located in a different timezone than your local machine.

Sometimes (Especially working with log files) it will be very difficult to get the actual time  of the Sitecore CMS server and map it  to your local machine (By adding timezone difference). But what if you are not aware of Sitecore CMS server timezone.

  • One way will be having an aspx page on Content Management Server  which display the Current Time

But what if you don’t have that page and you quickly want to know the current time of CM server?

It’s simple and quick (tip/trick):

  • Go to any item which has field of DateTime type. Click on the “Now” link above it. And you will have the current time of the CM server. 😉
    Once you get the current time, discard your changes by not saving that item.
  • If you don’t have any DateTime field then create one temporarily.currenttime

That’s it.

Happy Coding 🙂

Solving Experience Editor Slowness part-2

In part-1, we have analyse the load time of Experience Editor with different options available like Optimized Compilation, Sheer UI and disabling Suggested Test Count. We have also found that the Suggested Test Count request is time consuming.

So, Instead of disabling Suggested Test option from the header ribbon, what if we disable the Suggested Test Count request call?

First it is better to know how the Suggested Test Request works while Experience Editor loads.

As per the name, Suggested Tests are the tests suggested by Sitecore to run. Sitecore continually analyzes the tests run on the website and the usage of the website in general, and uses this analysis to suggest new tests to run. Go through Test Lists document by sitecore for more information.

This suggested Tests are stored in sitecore_suggested_test_index and displays them in the list and as a count in the ribbon. As per default configuration, a crawler uses /sitecore/content as the root location in the content tree to start from when it populates this index. It would be better to change this path to the navigable content. The configuration file for sitecore_suggested_test_index is at App_Config\Include\ContentTesting\Sitecore.ContentTesting.Lucene.IndexConfiguration.config or Sitecore.ContentTesting.Solr.IndexConfiguration.config file. While Experience editor loads, total number of suggested test (Tests suggested by Sitecore) is calculated and displayed in the ribbon.

So now back to our issue: let’s disable suggested test count call while Experience editor loads. Sitecore support suggest an awesome solution for this.

This Suggested Test Count request is executed from .js file: ShowSuggestedTestCommand.js located at #RootFolder#\sitecore\shell\client\Sitecore\ContentTesting\ShowSuggestedTestCommand.js

In this file, locate the below code:

 canExecute: function (context) {
      var testCount = context.app.canExecute("Optimization.SuggestedTests.Count", context.currentContext);
      var outputEl = $("a[data-sc-id='Suggested-Tests'] span");
      var counterSpan = "<span> (" + testCount + ")</span>";

Now we will change values of two variables: testCount and counterSpan. Assign 0 to testCount and “” to counterSpan. Final code will look like following:

 canExecute: function (context) {
      var testCount = 0;
      var outputEl = $("a[data-sc-id='Suggested-Tests'] span");
      var counterSpan = "";

Or else you can download ShowSuggetedTestCommand.js and replace your copy with it.

And finally, here is the impact of our action.

Environments Normal Page Load Time Applying Optimized Compilation – OC OC + Sheer UI Speak UI + Disabling Suggested Test Count Request Just Removing Count figure(ShowSuggestedTestCommand.js)
Env 1 1m 9s 32.04s 16s 29.98s 28s
Env 2 2m 8s 1m 30s 40s 56s 54s

 

It works, right? It takes less time than disabling the Suggested Test Count. And on top of it, it has Suggested Test functionality available in the ribbon of Speak UI.

That’s it.

Happy Coding 🙂

 

Solving Sitecore Experience Editor Slowness part-1

We were facing an issue of Experience editor load time. It is taking too much time to load than usual. We have followed the guidelines suggested in the following kb article:
https://kb.sitecore.net/articles/672774
Optimized Compilation and using the Sheer UI instead of Speak makes things better. See the below table:

Environments Normal Page Load Time Applying Optimized Compilation – OC OC + Sheer UI
Env 1 1m 9s 32.04s 16s
Env 2 2m 8s 1m 30s 40s

But what if we want to keep Speak UI?
We have started observing the different components load time and we found that Suggested Test Count is causing the issue.
ExpEditor-SuggestedTestCountLoadTime

Check which component/request is causing the slowness in your case. If it is My Items, then you can refer following kb article from Sitecore: https://kb.sitecore.net/en/Articles/2015/12/04/14/31/549951.aspx

Suggested Test Count takes more than 70% of Page load time. So, using the following link we have disabled the Suggested Test Count.
https://blog.horizontalintegration.com/2015/07/05/sitecore8-experience-editor-slow/

And after disabling the Suggested Test count we are getting improved result with Speak UI.

Environments Normal Page Load Time Applying Optimized Compilation – OC OC + Sheer UI Speak UI + Disabling Suggested Test Count Request
Env 1 1m 9s 32.04s 16s 29.98s
Env 2 2m 8s 1m 30s 40s 56s

So, it helps us to minimize the load time but yes, then Suggested Tests will not be visible on the ribbon.

Exp.Editor Image without SuggestedTest Count Label
But what if we need Suggested Test functionality in the ribbon? Yes, that is possible. Here is How..

Sitecore: Include HTML tags in indexes for rich text field

Usually, html tags are striped out while indexing the Rich Text field content and it makes sense.  But what if you want to index the html format with the content? Answer is yes, you can by doing some code change.

Suppose We have a field named “Long Description” in sitecore having “Rich Text Field” as its Field type. and we want to index its value with html format in the index.

To get this, we need to change Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config . ( I am using Lucene indexing here, if you are using solr then open Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config) located at App_Config/Include folder.

map the following code in the config:

<fieldReaders type="Sitecore.ContentSearch.FieldReaders.FieldReaderMap, Sitecore.ContentSearch">
<param desc="id">defaultFieldReaderMap</param>
<mapFieldByTypeName hint="raw:AddFieldReaderByFieldTypeName">
<fieldReader fieldTypeName="checkbox" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.LuceneProvider.FieldReaders.CheckboxFieldReader, Sitecore.ContentSearch.LuceneProvider" />
<fieldReader fieldTypeName="date|datetime" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.LuceneProvider.FieldReaders.DateFieldReader, Sitecore.ContentSearch.LuceneProvider" />
<fieldReader fieldTypeName="image" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.ImageFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="single-line text|multi-line text|text|memo" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="html|rich text" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.RichTextFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="multilist with search|treelist with search" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DelimitedListFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="checklist|multilist|treelist|treelistex|tree list" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.MultiListFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="icon|droplist|grouped droplist" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="name lookup value list|name value list" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.NameValueListFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="droplink|droptree|grouped droplink|tree" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.LookupFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="tracking" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.NullFieldReader, Sitecore.ContentSearch" />
</mapFieldByTypeName>
</fieldReaders>

Let’s focus on the fieldReader: <fieldReader fieldTypeName=”html|rich text” . It uses RichTextFieldReader as fieldReaderType and in Sitecore 8.1 it strips all html tags using HtmlField.GetPlainText().

We will add another mapField section for our field which uses DefaultFieldReader (and it just read the text as it is without stripping out html tags).

<mapFieldByFieldName hint="raw:AddFieldReaderByFieldName">
<fieldReaderfieldName="Long Description"fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
</mapFieldByFieldName>

This would be added at the same level of <mapFieldByTypeName>. By this we are just defining that for the field having name “Long Description” use DefaultFieldReader.

The resulted code would look like following:

<fieldReaders type="Sitecore.ContentSearch.FieldReaders.FieldReaderMap, Sitecore.ContentSearch">
<param desc="id">defaultFieldReaderMap</param>
<strong><mapFieldByFieldName hint="raw:AddFieldReaderByFieldName">
<fieldReader fieldName="Long Description" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
</mapFieldByFieldName></strong>
<mapFieldByTypeName hint="raw:AddFieldReaderByFieldTypeName">
<fieldReader fieldTypeName="checkbox" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.LuceneProvider.FieldReaders.CheckboxFieldReader, Sitecore.ContentSearch.LuceneProvider" />
<fieldReader fieldTypeName="date|datetime" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.LuceneProvider.FieldReaders.DateFieldReader, Sitecore.ContentSearch.LuceneProvider" />
<fieldReader fieldTypeName="image" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.ImageFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="single-line text|multi-line text|text|memo" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="html|rich text" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.RichTextFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="multilist with search|treelist with search" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DelimitedListFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="checklist|multilist|treelist|treelistex|tree list" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.MultiListFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="icon|droplist|grouped droplist" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="name lookup value list|name value list" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.NameValueListFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="droplink|droptree|grouped droplink|tree" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.LookupFieldReader, Sitecore.ContentSearch" />
<fieldReader fieldTypeName="tracking" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.NullFieldReader, Sitecore.ContentSearch" />
</mapFieldByTypeName>
</fieldReaders>

You can add more than one field for which you want to prevent html tags in indexing.  Not suggested but if you want to prevent html tags for all the Rich Text, from being stripped out while indexing, you can just update the fieldReaderType of “html|rich text” fieldReader to DefaultFieldReader.

That’s it. Check the field in the index file using Luke or any other tool and you can find html tags in the field’s value.

Happy Coding 🙂

Field level fallback is not working in template inheritance

An issue regarding the field level fallback language in Sitecore 8.1 update 1. We need to enable field level fallback for all the fields of all the template. While working on that we found that field level fallback is not working in inheritance. i.e. if you enable field level fallback in Template Field’s standard value (/sitecore/templates/System/Templates/Template field/__Standard Values), then it should get enabled for all the fields. But unfortunately it is not.

 

Following are the steps to reproduce the issue:

  1. In the Sample Item, on Title Field I have enabled field level fallback. and when I create the pt-BRversion of the home item, the field level fallback works perfectly as expected.

EnableFieldLevelFallback

EnableFieldLevelFallback2

  1. But we have lot many template and lot many fields. So what I have tried is  instead of enabling field level fallback on individual fields, I have enabled it on theStandard valuesof Template field (/sitecore/templates/System/Templates/Template field/__Standard Values) so that all fields have that fallback enabled.

EnableFieldLevelFallback3

 

  1. So, after resetting the Title Field, the value of Enable field level fallback is now taking value from the standard values of theTemplate Field template.

 

EnableFieldLevelFallback4

 

  1. Now when I am creating the Portuguese version of the same item, as per the field level fallback it should show the value of en version but unfortunately it is not showing it.

EnableFieldLevelFallback5

So in short, if we enable the Enable Field level fallback for individual fields, it is working fine but if we enable it from Template Field’s standard value (in inheritance), though it display as marked in field, it is not working.

 

Reason:

There is a issue in IsValidForLanguageFallback processor of getFieldValue pipeline.

 

IsValidForLangaugeFallback processor has Process method which calls the LanguageFallbackFieldValuesManager’s IsValidForFallback method and it calls the IsValidForFallback method of LanguageFallbackFieldValuesProvider and the issue resides in this method.

public virtual bool IsValidForFallback(Field field)
{
	SiteContext context;
	bool? currentValue = Switcher&amp;amp;amp;lt;bool?, LanguageFallbackFieldSwitcher&amp;amp;amp;gt;.CurrentValue;
	if (currentValue == false)
	{
		return false;
	}
	if ((currentValue != true) &amp;amp;amp;amp;&amp;amp;amp;amp; (((context = Context.Site) == null) || !context.SiteInfo.EnableFieldLanguageFallback))
	{
		return false;
	}
	bool flag = true;
	Item item = field.Item;
	string key = string.Concat(new object[] { field.Database.Name, item.ID, field.ID, field.Language.Name });
	object obj2 = field.Database.Caches.IsLanguageFallbackValidCache.GetValue(key);
	if (obj2 != null)
	{
		return (((string) obj2) == &amp;quot;1&amp;quot;);
	}
	Language language = LanguageFallbackManager.GetFallbackLanguage(item.Language, item.Database, item.ID);
	if ((language == null) || string.IsNullOrEmpty(language.Name))
	{
		flag = false;
	}
	else if (field.Name.StartsWith(&amp;quot;__&amp;quot;))
	{
		flag = false;
	}
	else if (field.Shared)
	{
		flag = false;
	}
	else if (StandardValuesManager.IsStandardValuesHolder(item))
	{
		flag = false;
	}
	else if ((field.ID == FieldIDs.EnableLanguageFallback) || (field.ID == FieldIDs.EnableSharedLanguageFallback))
	{
		flag = false;
	}
	else if (!field.SharedLanguageFallbackEnabled)
	{
		if (Settings.LanguageFallback.AllowVaryFallbackSettingsPerLanguage)
		{
			Item innerItem;
			using (new LanguageFallbackItemSwitcher(false))
			{
				innerItem = field.InnerItem;
			}
			if ((innerItem == null) || (innerItem.Fields[FieldIDs.EnableLanguageFallback].GetValue(false, false) != &amp;quot;1&amp;quot;))
			{
				flag = false;
			}
		}
		else
		{
			flag = false;
		}
	}
	field.Database.Caches.IsLanguageFallbackValidCache.Add(key, flag ? &amp;quot;1&amp;quot; : &amp;quot;0&amp;quot;);
	return flag;
}

In the last section of else if, it checks for if sharedLanguageFallback is not enabled. In our case it is not enabled so, the debugger comes into that condition, then it checks for if AllowvaryFallbackSettingsperLanguage is true or not. In our case it is not so debugger comes into else and in else there is no code/Condition it is just set flag to false and IsValidForFallback always return false which should not.

 

Support ticket had been raised for the same and sitecore found that it is a bug. They have provided a updated dll and config for the same. In the dll’s code we found the following difference in the last else if block.

else if (!field.SharedLanguageFallbackEnabled)
{
	if (Settings.LanguageFallback.AllowVaryFallbackSettingsPerLanguage)
	{
		Item innerItem;
		using (new LanguageFallbackItemSwitcher(false))
		{
			innerItem = field.InnerItem;
		}
		if ((innerItem == null) || (innerItem.Fields[FieldIDs.EnableLanguageFallback].GetValue(false, false) != &amp;quot;1&amp;quot;))
		{
			flag = false;
		}
	}
	else
	{
		Item item3;
		using (new LanguageFallbackItemSwitcher(false))
		{
			item3 = field.InnerItem;
		}
		if ((item3 == null) || (item3.Fields[FieldIDs.EnableSharedLanguageFallback].GetValue(true, false) != &amp;quot;1&amp;quot;))
		{
			flag = false;
		}
	}
}

If field.SharedLanguageFallbackEnabled is false and AllowVaryFallbackSettingPerLanguage is also false then it comes to the highlighted else section.

Here, field item is set and then GetValue method is called for its EnabledSharedLanguageFallback (EnabledSharedLanguage refers to Field Level Fallback property) property with (true,false) parameter. The magic is here in the passing the parameter.  That’s it. And the other code is same as it is.  So what happening in GetVaue method is:

 

This GetValue(true,false) actally calls the method Sitecore.Data.Fields.Field.GetValue(bool allowStandardValue, bool allowDefaultValue) .

 

From this method a call is made to GetValue(bool allowStandardValue, bool allowDefaultValue, bool allowFallbackValue)  with the parameter true, false ,true:  .GetValue(true, false , true)

 

This method calls GetValue(bool allowStandardValue, bool allowDefaultValue, bool allowFallbackValue, allowInheritValue) with param true, false, true, true:GetValue(true, false , true,true)

 

And from this method the last overloaded method called:  GetValue(bool allowStandardValue, bool allowDefaultValue, bool allowFallbackValue, bool allowInheritValue, bool allowInnerValue)  with parameter true,false,true,true,true: GetValue(true, false , true,true,true)

 

And in this method, the GetFieldValueArgs are assigned and hence for the AllowFallbackValue  is set to true

 

GetFieldValueArgs args = new GetFieldValueArgs {
	Field = this,
	AllowStandardValue = allowStandardValue,
	AllowDefaultValue = allowDefaultValue,
	AllowFallbackValue = allowFallbackValue,
	AllowInheritValue = allowInheritValue
};
CorePipeline.Run(&amp;quot;getFieldValue&amp;quot;, args);
this.containsStandardValue = args.ContainsStandardValue ? 1 : 0;
this.inheritsValueFromOriginalItem = args.InheritsValueFromOriginalItem ? 1 : 0;
this.containsFallbackValue = args.ContainsFallbackValue ? 1 : 0;
this.fallbackValueSource = args.FallbackSource;
return args.Value;

And finally IsValidForFallback method returns true. And now the things are working fine.

Field level fallback is now working fine for inherited value as well.

Patch provided by Sitecore Support:

Sitecore Support provides following patch files which can solve the issue: a config and a dll file

Download the files and

1. Place the  Sitecore.Support.105327.dll assembly into your /bin folder.
2. Place the Sitecore.Support.105327.config file into your /App_Config/Include folder.

Happy Coding 🙂