Date Formats in SharePoint 2013 Advanced Search

On my current project we had a problem with Date formats, when users where searching from Advanced Search page by Last Modified date, they were getting a very helpful error: “We didn’t understand your search terms….”

Search_DateFormat_Error

Looking into the log it pretty much had a similar message and was down to the format of the date used, obviously US Format dates worked fine.

Here are the few things I tried to make search recognize UK date formats:

Regional Settings Changes

So first instinct to fix it was to straight away jump to Regional settings of search center site and change the locale to en-GB, obviously changing it right away did not work (Life cannot be that simple with ShitPoint!) so I went a step further and verified that en-GB is used across all site collections reset the index & re-crawl but that made no difference. So I did couple of more things with regional settings in SharePoint itself:

  1. Change the Regional settings of Central Admin, there is no direct link to it you will have to manually type in the Url http://SERVER/ _layouts/15/regionalsetng.aspx.
  2. Set the default Locale for Web Application from General Settings in Central admin

Reset, re-index….. Still no difference.

Web.config globalization Settings
So after regional settings made no difference, I went to each web.config (Web App, central admin & even the Search WCF service!) and update the globalization as shown below.

Web.Config_Globalization

I had no hope of that actually fixing it but still gave it a shot and I was correct it did not fix the issue.

Server Locale

So first two options did not really help and I thought ensuring Server language is set to en-GB should help, and actually my language (Control Panel –> Language) was set to English (United States) and I thought aha that’s it changing this to English (United Kingdom) should work, so I changed it and I even verified the locale with Get-WinSystemLocale powershell command and it indeed was set to en-GB. Re-started the VM, reset and re-crawl, phew and still no difference.

I found this TechNet article about a registry setting to change the Default locale. It was set to en-US on my VM as shown:

RegisterySetting_Locale

And voilà I thought changing this to en-GB got to work but alas it was in vain.

Setting Language Preference in UserProfile

One of the StackOverFlow thread talked about setting the correct language preference in UserProfile (from Edit Profile page), but this also really did not impact the search behaviour

UserProfileLanguageSettings

Ensuring Request headers have correct language

I read a while ago that SharePoint is looking at the request headers to determine user’s locale which makes sense but looking at the request header it was indeed en-GB (Which is based on the language preference set on the OS)

RequestHeader_en-GB

While I was inspecting the headers in Fiddler I noticed that when you are on Search results page (results.aspx) and perform a search, it was doing an AJAX call and the request body had Culture set to 1033 (which is the Id for en-US) , now that looks like is where the problem is

SearchRequest_XML

As a final assault on SharePoint, I cracked open search.debug.js & search.clientcontrols.debug.js and finally worked out that following script is getting emitted on the page (You can do View source of the results page and look for it)

ExecuteOrDelayUntilScriptLoaded(
    function() {
        Srch.ScriptApplicationManager.get_current().states =
        {
            "browserLanguage": 2057,
            "webUILanguageName": "en-US",
            "webDefaultLanguageName": "en-US",
            "contextUrl": "http://KPSP/search",
            "contextTitle": "Search",
            "supportedLanguages": [ /*Removed for Readability*/],
            "navigationNodes": [ /*Removed for Readability*/],
            "searchCenterUrl": "http://KPSP/sites/search/Pages",
            "showAdminDetails": true,
            "defaultPagesListName": "Pages",
            "isSPFSKU": false,
            "userAdvancedLanguageSettingsUrl": "",
            "defaultQueryProperties": {
                "culture": 2057,
                "uiLanguage": 2057,
                "summaryLength": 180,
                "desiredSnippetLength": 90,
                "enableStemming": true,
                "enablePhonetic": false,
                "enableNicknames": false,
                "trimDuplicates": true,
                "bypassResultTypes": false,
                "enableInterleaving": true,
                "enableQueryRules": true,
                "processBestBets": true,
                "enableOrderingHitHighlightedProperty": false,
                "hitHighlightedMultivaluePropertyLimit": -1,
                "processPersonalFavorites": true
            },
            "showSuggestions": true,
            "showPersonalResults": true,
            "openDocumentsInClient": false,
            "languages": [{ "id": 1033, "label": "English" }] /*THIS IS THE PROBLEM LINE*/
        };
        Srch.U.trace(null, 'SerializeToClient', 'ScriptApplicationManager state initialized.');
    }, 'Search.ClientControls.js');

if you notice some of the language properties are set to en-GB and some to en-US! Anyways after trial and error it seems that 1033 in “languages”:[{“id”:1033,”label”:”English”}], Makes all the difference, I quickly hacked up something in JavaScript to change 1033 to 2057 (en-GB Locale) and boom now it takes UK date format as a valid entry and shows the results, happy days…..But soon I realized that fix only works when SharePoint is performing a search via AJAX. If you load the resuts.aspx page with search QueryString i.e. /Pages/Results.aspx?k=Write<=31/03/2104 it still fails, so obviously there is something else going on when the search is performed on the server side.

Proper Fix!

In order to properly fix it, we need to first make sure 1033 is changed to 2057 in the JavaScript above and also ensure when the search is performed on the server side it still takes en-GB as the locale.

Fixing the Search Query when performed AJAX Call

So I went fishing trying to work out what’s generating above JavaScript in a hope to try and change it’s behaviour. Thanks to JetBrains dotPeek it was fairly easy to work out that Microsoft.Office.Server.Search.WebControls.ScriptApplicationManager is the class rendering that JS. ScriptApplicationManager class has got a States dictionary object which gets serialized into above mentioned JavaScript and to my delight States property is public so it’s easy to change the value for “language” key in it to whatever your want.

Fixing the Search Query when performed on the Server side

That proved to be little more challenging! after spending good few hours going through the Out-of-Box code using dotPeek, I found following internal method of ScriptApplicationManager class which was eventually working out the Effective Language to use.

GetLanguageMethod

I could not really work out which if construct was actually invoked to return 1033, anyways thanks to the Blog Post about changing language token in Content Search WebPart, I learned about DataProviderScriptWebPart and I realized that FallbackLanguage (which is the first thing checked in above internal method) is a public property on DataProviderScriptWebPart  and It is always set to -1, I can set it to 2057 and above internal method will basically use the FallbackLanguage.

Putting it all together (Final Code)

The best way I thought to put the fix together would be to do a custom Search Results web-part which overrides the Locale settings. Following class inherits from ResultScriptWebPart and just changes few things to use the Locale based on SiteSettings ONLY

[ToolboxItem(false)]
    public class SearchResults : ResultScriptWebPart
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (this.AppManager.States.ContainsKey("languages"))
            {
                List<LanguagePreference> preferences =
                    this.AppManager.States["languages"] as List<LanguagePreference>;
                if (preferences != null && preferences.FirstOrDefault() != null)
                {
                    /*This will fix the language id in JavaScript which emitted on the page*/
                    preferences[0].id = (int)SPContext.Current.RegionalSettings.LocaleId;
                }
            }
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (this.AppManager != null)
            {
                if (this.AppManager.QueryGroups.ContainsKey(this.QueryGroupName) &&
                    this.AppManager.QueryGroups[this.QueryGroupName].DataProvider != null)
                {
                    this.AppManager.QueryGroups[this.QueryGroupName].DataProvider.BeforeSerializeToClient +=
                        ChangeLanguage;
                }
            }
        }

        private void ChangeLanguage(object sender, BeforeSerializeToClientEventArgs e)
        {
            DataProviderScriptWebPart scriptWebPart = e.ScriptWebPart as DataProviderScriptWebPart;
            if (scriptWebPart != null)
            {
                /*This will fix the language used when search is performed on page load*/
                scriptWebPart.FallbackLanguage = (int)SPContext.Current.RegionalSettings.LocaleId;
            }
        }
    }
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s