Debugging JavaScript in the Interactive Service Hub (Part 1)

Those that read regularly my blog and follow my work with Sparkle XRM will know I'm a massive fan of using Fiddler to debug JavaScript. One of the most productive 'superpowers' that Fiddler gives us is the ability to change JavaScript on the disk and not have to upload/publish – we can simply refresh the form and the new script will be used. The Interactive Service Hub (ISH) was first introduced in CRM2016 and has been improved with more support for customisations in CRM2016 Update 1. I see the purpose of the ISH at this stage is not to replace the main User Interface but rather as a testing ground for the principle of bringing the MoCA mobile/tablet native client platform to the web client. I think of it similar to the introduction of the Polaris UI back in CRM2011 – there are many similarities in that they both only support a limited set of entities and have limited customisations features. The main difference is that the ISH is being incrementally improved with each release, where the Polaris UI was more of a throw away proof of concept. At this stage the ISH is only supporting 'case' oriented operations but I'm sure it'll eventually graduate to support all Sales, Service and Marketing features. So why the new approach the UI? Surely it would be better to improve the existing UI incrementally rather than replace it? One of the key drivers for the Dynamics CRM Team over the last few releases has been 'configure once deploy everywhere'. This allows us to configure business rules that can be run on all devices/platforms reliably without having to perform separate testing and perhaps re-write to target different clients. The maintenance of having multiple user interface platforms is considerable so it's a natural step to try and achieve some degree of convergence between the mobile/tablet/web/outlook interfaces. A little background on how the ISH loads metadata I think we are all fairly comfortable with the normal Web 2.0 paradigm of loading resources. This is where with each operation the client requests an html page and then the browser requests all the additional resources (JavaScript, CSS etc. ) that are referenced by that page. JavaScript can then make additional XHR/Ajax requests to the server to display further dynamic content. The CRM2016 UI is very similar on this front as can be seen below. I documented the CRM2013 script loading sequence which hasn't significantly changed even in CRM2016.

Each time you open the web client, the homepage.aspx or Main.aspx has to request the metadata for the specific resource (view or form) and then combine it with the requested data. Although there is browser and server side caching in place, this is still costly in terms of the requests and rendering overhead of the browser. The 'turbo forms' update in CRM2015 Update 1 has really helped with the speed of this since it minimises the resources that requested with each navigation however fundamentally it is still limited by the page per browser request architecture.

ISH works very differently… The ISH is more what we would call a 'single page application'. The sequence is very different in that there is an initial download of metadata and then subsequently all user interactions only request the actual data using the Organization.svc and OrganizationData.svc.

This single page approach has the advantage that it makes navigation super slick but with the rather annoying drawback that there is an initial wait each time the ISH is opened where the metadata changes are checked. The first time you open the ISH all the metadata is downloaded but from then on only the differences from the last open are downloaded. If there haven't been any changes then it's super quick because all the metadata is stored in the browsers indexed Database but if you've done a publish then the next open can take a while. Furthermore, the new metadata won't be downloaded until you close and re-open the ISH - this is different to the Web 2.0 UI and can lead to the client working with stale metadata for a time. The Indexed Database is one of the significant differences between and HTML5 single page app and a more traditional Web 2.0 architecture.

Note: For now the ISH mostly uses the SOAP/Xml based Organization.svc rather than the new JSON based Web API. The speed of the metadata sync can be helped further by using the 'Prepare Client Customizations' button on the solution since this will pre-prepare the download package rather than waiting for the first person to open the ISH to detect the changes in the metadata. The difference between the MoCA client and the ISH is that the MoCA asks if the user wants to download the updates – presumably because you may be on a low bandwidth connection.

So where does that leave us with respect to JavaScript debugging?

If you've been keeping up so far (you have right?) then you'll realise that because the metadata (this includes JavaScript) will be all stored in the browser Indexed DB and not relying on the browser cache. As a result, we can't simply prevent the files from being cached and download the latest version with each page load as we used to do with Fiddler. We're back with the uncomfortable debug cycle of having to make a change to a JavaScript web resource, upload it to CRM, publish, close and re-start the ISH - urgh! To preserve our collective sanity, I've created a little debug utility solution that you can use to clear the cache of specific web resources so that you can quickly make changes to JavaScript on your local disk and then reload it in the ISH without doing a full publish cycle. Here is how: Install the latest build of SparkleXRM Install the Interactive Service Hub Debug Helper Solution Setup Fiddler's Auto Responder to point to your local webresource file as per my instructions. Start the ISH to load your JavaScript Make a local change to your JavaScript Open the ISH Debug Utility Solution configuration page and enter the name of your script name, then click 'Refresh JavaScript Webresource'Note: You can enter only part of the webresource name and it will use a regular expression to match. Use Ctrl-F5 on your ISH Page and when re-loaded the Web Resource will use the new version since the debug utility has forced a new download and updated the Indexed DB storage. Sweet – but what about the MoCA client? Obviously this technique is not going to work for mobile client running on an iPad, iPhone etc. The good news is that you can run the MoCA client in the Chrome browser in the same way you can run the ISH – just navigate to: <crmserver>/nga/main.htm?org=<orgname>&server=<crmserver> Note: You must be pre-authenticated for this to work. OnPremhttp://dev03/nga/main.htm?org=Contoso&server=http://dev03/Contoso OnPrem IFD Online  Since the ISH and the MoCA client are build using the same platform you can now use the ISH Debug Helper from the same browser session to perform the same script refresh! This is actually an excellent way of testing out your Scripts on the MoCA client! For more information, check out the comments in this tip of the day. Looking forwards to the future I'd really encourage you to check out the ISH and use the New CRM Suggestions site to record anything you find that you would like to see in subsequent releases. Whilst I suspect that the existing 'refreshed' UI will be available for some releases to come, it is likely at some point to become the new 'legacy' UI and with on-going investment being made in the ISH style UI. In part 2 we'll look at some limitation of the ISH and how to get around them. Any comments, just tweet me! @ScottDurow

Install Project Service Automation (PSA) – Where’s it gone?

If you have tried to install Project Service recently you might have found that it's disappeared from the 'preferred solution' list in the Office 365 admin portal. So where has it gone?! Although the CRM Online Help hasn't yet been updated to reflect the fact, it has now moved to the recently release and very exciting AppSource! On the 11th of July Michael Kushinsky of Microsoft helpfully posted in the dynamics community the new instructions on how to install and upgrade from a trial installation – and there will be an official blog post about it soon. I thought I would quickly show you how easy it is to use App Source to install PSA: 1. Select Settings -> Dynamics Marketplace

2. Search for 'Project Service' and click 'Try'

3. Accept the T&amp;Cs (you always read them in full right!)

4. Wait for the solution to install and you're off!

I can't wait to see AppSource grow and mature!

Mastering Ribbon Workbench 2016

Since the release of the Ribbon Workbench 2016 I am in the process of updating the documentation to reflect the new user interface. This video series on mastering the Ribbon Workbench 2016 will take you through from installing to performing advanced customisations.

Part 1 - Downloading & Installing

Part 2 - User Interface Overview

Part 3 - Hide Actions

Part 4 - Moving Managed Buttons to a Flyout Menu

Part 5 - Hiding buttons conditional to the form context

More to follow!...

Ribbon Workbench Version Compatibility

I'm pleased to announce that in addition to the managed solution that you can install inside Dynamics CRM, the Ribbon Workbench 2016 is also available in the XrmToolbox (if you hadn't already noticed!). When you open the XrmToolbox you will see that there is the Ribbon Workbench available for download in the plugin store.

Keep checking out the store because tools are being added regularly by some great plugin authors. Thank you to all those who are helping to beta-test, I have been really encouraged by your comments and suggestions. The re-write of the Ribbon Workbench (to remove its dependency on Silverlight) and the XrmToolbox version has been on my 'to-do' list for much longer than I would have liked and so I'm particularly pleased with this release. The new Ribbon Workbench 2016 solution installs alongside the older version – so if you have an upgraded org you might get them all sitting there on your command bar.  I'm keeping there on one of my organisations for posterity but you can safely uninstall older versions without losing any of your customisations.

Here is a version compatibility matrix for users of the older versions:


Ribbon Workbench

Ribbon Workbench 2013

Ribbon Workbench 2016


Latest Version


Requires Silverlight?




XrmToolbox Version?



CRM 2011(Inc. UR12+)



CRM 2013(Inc. SP1+)




CRM 2015(Inc. Update 1+)




CRM 2016(Inc. Update 1+)  




Dynamics 365 – what’s that?

Ever since Microsoft's announcement in 2005 of the 'Dynamics' brand, the strategy (code named 'Project Green') to homogenise the range of ERP products together into a single technology has been all but been forgotten. This has largely been due to Microsoft's investments in 'mash-up' technologies such as Power BI and integration platforms such as Power Apps and Microsoft Flow. The concept of 'metamodeling' your Enterprise to make integration and consolidated reporting easier is clearly not a new concept but now with these technologies and the prevalence of open APIs it has become a reality that's available to practically any organisation. The investment in the 2016 release of Dynamics NAV to integrate more natively with Dynamics CRM Online was a step in the right direction to bring the products closer together and with the recent Dynamics 365 announcement it seems like there is a concrete commitment to use Power BI, Power Apps and Microsoft Flow to make the whole greater than the sum of the parts. The idea of having a single system to drives every single process within a business may seem like nirvana but in reality would become so complex, hard to maintain and inflexible to business changes (such as mergers and acquisitions) that it would quickly prove to be as undesirable as having no system at all. In a previous life as a Microsoft BizTalk Consultant the Service Orientated Architecture was the perceived anti-dote to the monolithic systems of the 1990's. Unfortunately, the fact that the systems being integrated together naturally assumed that they were the only application you ever needed to use and had closed APIs, integration was not only difficult and expensive but also prone to failure. I really welcome Microsoft's Dynamics 365 strategy and hope it will make the Business Solutions sit comfortably within the promised 'common data model' with ease so that we can also include other non-Dynamics parts of the business into the mix. I suspect that the individual product brands will remain intact as products in their own right (On-Premises deployments are not covered by Dynamics 365) and so much of the investment will be as applicable to third-party integration as it will be for the Dynamics brand applications. I really hope that we are starting to see this 'platform first' approach that the Azure team have always taken under Scott Guthrie's leadership now being applied to the Dynamics Business Solutions. Why not check out the technologies that are making Dynamics 365 possible:

Microsoft Power BI Microsoft Flow Microsoft Power Apps


SharePoint Integration Reloaded – Part 5 (belated)

In previous articles in this series we've talked about the differences between Server Side Sync and the old List Component. Since I published the first articles, a new MSDN article on the topic has been posted which I thought would be good to signpost folks to => Important considerations for server-based SharePoint integration. One of the topics that has come up recently for people using Server Side Sync to SharePoint is the 5000 item limit of Document Libraries which has led to a bit of panic amongst some so I thought I'd dispel the rumours!     Here are the facts about the Throttling Limitation 1) You can see how many items you have in a Document Library by opening the site in SharePoint and selecting 'Site Content' from the left hand navigation menu. The number of items will show below the Document Library name – this includes documents and folders. 2) If you have more than 5000 items you can still use Server Side Integration with SharePoint provided that you only use the default sort order of the document view in CRM.The default sort is by Location Ascending. You can change this to sort Descending but if you change the sort to any other column you will receive the error "Throttling limit is exceeded by the operation" 3) If you have a record with only 2 documents in the associated document locations folder you will still not be able to sort by any other column other than Location if the root Document Library has more than 5000 items overall. 3) If the user clicks on 'Open SharePoint' then they will be able to do all the sorting they need since the limitation is not experienced by the native SharePoint interface – only the CRM document lists. I find that this sort limitation is not an issue because I encourage users to use SharePoint freely due to its rich interface. Don't try and hide SharePoint from them because it's important to understand the way in which documents are stored and the additional features that SharePoint has to offer. The documents view in CRM is simply to be used as a quick reference for documents associated with the CRM Record. Hope this helps!

Option-Set, Lookup or Autocomplete

In the constant struggle to improve data quality it is common to avoid using free-text fields in favour of select fields. This approach has the advantage of ensuring that data is entered consistently such that it can easily be searched and reported upon. There are a number of choices of approaches to implementing select fields in Dynamics CRM. This post aims to provide all the information you need to make an informed choice of which to use on a field by field basis. The options are:

Option-Set Field - stored in the database as an integer value which is rendered in the user interface as a dropdown list. The labels can be translated into multiple languages depending on the user's language selection. Lookup Field - a search field that is stored in the database as a GUID ID reference to another entity record. The Lookup field can only search a single entity type. Auto Complete Field - a 'free text' field that has an auto complete registered using JavaScript to ensure that text is entered in a consistent form. The term 'autocomplete' might be a bit misleading since the field is not automatically completed but instead you must select the suggested item from the list. This is a new feature in CRM 2016 that you can read more about in the preview SDK.

The following table provides an overview of the aspects that this post discusses for each option:

Number of items in list – The larger the list and the likelihood that it will grow, the more this becomes important. Filtering based on user's business unit - This is especially important where you have different values that apply to different parts of the business and so the list of options must be trimmed to suit. Adding new Items - Ease of adding values frequently by business users. Removing values – Ease of removing values without affecting existing records that are linked to those values. Multi-Language – Having options translated to the user's selected language Dependant/Filtered options - This is important where you have one select field that is used to filter another such as country/city pairs. Additional information stored against each option - This is important if you have information that you need to store about the selected item such as the ISO code of a country. Mapping between entities - Is the option on multiple entity types? Often the same list of items is added as a field in multiple places and multiple entities. This can be important when using workflows/attribute maps to copy values between different entity types. Number of select fields - The more select fields you have across all your entities, the more important this becomes. Filters, Reports and Advanced Find - When creating advanced finds and views, a user should be able to select from a list of values rather than type free text. Configure once, deploy everywhere – One key design goal should be that once a field is configured, it should be easily used across the web, outlook, tablet and phone clients.

Option-Set Fields Option-Sets are the default starting point for select fields.

Number of items in list (Option-sets) No more than ~100 for performance reasons. All items are downloaded into the user interface which will cause performance problems for large lists – especially where there are lots of option-sets on the same form. Filtering based on user's business unit (Option-sets) Requires JavaScript to filter items dynamically based on the user's role/business unit.

Learn about Javascript methods for manipulating Option-set values

Ease of adding values frequently by business users (Option-sets) Option-Sets require a metadata configuration change and a re-publish that would usually be done out of hours by an administrator. It is best practice to do this on a development environment and then import a solution into production. Adding new values to the list isn't something that can be done by business users. Removing values over time (Option-sets) Removing items causes data loss in old records. Items can be removed using JavaScript to preserve old records, but Advanced Find will still show the values. Multi-Language Options (Option-sets) Each option-set item can be translated into multiple languages.

If you need to have the select field multi-language then an option-set is probably your best choice unless it is going to be a long list, in which case you'll need to make a compromise.

Dependant/Filtered options (Option-sets) Requires JavaScript to filter options.

View the dependant option-set sample in the SDK

Additional information stored against each option (Option-sets) It is not possible to store additional data other than the label and integer value of the option-set. You would need to store somewhere else in a lookup table format. Mapping between entities (Option-sets) Use a global option-set that can be defined once and used by multiple option-set fields.

Learn about global option-set

Number of select fields (Option-sets) You can have as many select fields as your entity forms will allow. The more fields you have the slower the form will load and save.  Search/Filtering (Option-sets) Option-sets are always presented as a drop down in advanced fine and view filters. Configure once, deploy everywhere (Option-sets) Works across all clients including phone and tablet native apps.

Option-sets are the most 'native' choice for select fields and will work in all deployment types without much consideration.

  Lookup Fields with Custom Entity Lookup fields allow selecting a single reference to a custom entity using a free text search.

Number of items in list (Lookup) Unlimited list items subject to database size. Since all list items are not downloaded to the user interface (unlike option-sets) the user can search quickly for the item they need. Filtering based on user's business unit (Lookup) Security Roles can be used in combination with a user owned lookup entity so that lookup records are visible to subset of users.

Read about how Security Roles can restrict access to records.

Ease of adding values frequently by business users (Lookup) New records can easily be added by users using the 'Add New' link. Control over who can add new items can be done using Security Roles.

If you need business users to add values regularly then a Lookup field is a good choice. The Configuration Migration tool in the SDK can be used to easily move values between environments.

  Removing values over time (Lookup) Old items can be easily deactivated and will no longer show in lookup fields (including in advanced finds) however existing records will retain their selected value (unlike when option-set items are removed).

If you need to make changes constantly to the list and remove items without affecting previous records then a lookup field is most likely your best choice.

  Multi-Language Options (Lookup) Not possible without complex plugin server side code to dynamically return the name in the current user's language.

Read my post on creating multi-language lookups.

Dependant/Filtered options (Lookup) Lookup filtering options can be added in the form field properties or can be added via JavaScript for more complex scenarios.

Lookups are the easiest and quickest to setup dependant lists without writing code. This filtering will also work on tablet/mobile clients without further consideration.

Learn about filtering lookups using the form field properties Learn about dynamic JavaScript filtering of lookups

Additional information stored against each option (Lookup) Additional information can be stored as attributes on the lookup entity records. Lookup views can show up to 2 additional attributes within the inline lookup control.

If you are integrating with another system that requires a special foreign key to be provided, lookup entities are good way of storing this key.

  Mapping between entities (Lookup) Lookups can easily be mapped between records using attribute maps/workflows or calculated fields. Number of select fields (Lookup) CRM Online is limited to 300 custom entities.

This is an important consideration and it's unlikely to be a good idea to use Lookup entities for all of your select fields.

If you are using CRM online you'll likely have to always use a combination of lookups and option-sets due to the limit of 300 custom entities. Don't take the decision to make all your select fields as lookups.

  Search/Filtering (Lookup) Lookups are presented as search fields in Advanced Find and Filters. Configure once, deploy everywhere (Lookup) Works across all clients including phone and tablet native apps. If working offline however, all lookup values may not be available. Text Field Auto Completes (CRM 2016) Autocompletes are a free text field with an on key press event added to show an autocomplete. The great thing about autocompletes is that they can show icons and additional action links.See below for an example of how to use autocompletes in Javascript.

Number of items in list (Autocomplete) An autocomplete field can only show as many items as you return at a time but you'll want to put a limit for performance reasons.

If you need the select field to be more like a combo-box where users can type their own values or select from predefined items then autocomplete text fields are a good choice.

  Filtering based on user's business unit (Autocomplete) You can add any search filtering you need using JavaScript.

View the SDK Sample for autocomplete fields

  Ease of adding values frequently by business users (Autocomplete) If the autocomplete is using a lookup entity to store the data displayed then the same considerations would apply as for Lookup Entities. If the values are hard coded into the JavaScript then this would be more akin to the Option-Set solution import. Removing values over time (Autocomplete) Since the actual field is stored as a text field there is no issue with removing values. Existing data will still be preserved. Multi-Language Options (Autocomplete) You can detect the user interface language and return a specific language field to the user via JavaScript however it will be stored in the textbox and other users will not see it in their language (unlike an option-set). One solution to this would be to use the autocomplete for data entry and then use a web resource to present the field value in the local user's language. Dependant/Filtered options (Autocomplete) You can apply whatever filtering you need using JavaScript. Additional information stored against each option (Autocomplete) If you use the autocomplete to search a custom entity you can store additional data as additional attributes. The autocomplete flyout can display multiple values for each result row.

Autocomplete fields have the advantage that they can show an icon that is specific to the record (e.g. The flag of the country). If you need this feature, then Auto completes are a good choice.

  Search/Filtering (Autocomplete) If you use a free text autocomplete it's advisable to additionally populate a backing lookup field to facilitate searching/filtering. This would also allow you to ensure that 'unresolved' values cannot be saved by using an OnSave event to check that the text field matches a hidden lookup field that is populated in the OnChange event. Configure once, deploy everywhere (Autocomplete) Autocomplete does not work on phone/tablet native apps yet. Show me the Code! I have added support for the Auto Complete SDK extensions in CRM2016 to SparkleXRM. To show a country autocomplete lookup, you'd add onload code similar to: public static void OnLoad() { Control control = Page.GetControl("dev1_countryautocomplete"); control.AddOnKeyPress(OnCountrySearch); }

public static void OnCountrySearch(ExecutionContext context) { string searchTerm = Page.GetControl("dev1countryautocomplete").GetValue<string>(); string fetchXml = String.Format(@"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'> <entity name='dev1country'> <attribute name='dev1countryid' /> <attribute name='createdon' /> <attribute name='dev1name' /> <attribute name='dev1longname' /> <attribute name='dev1iso' /> <attribute name='entityimageurl' /> <order attribute='dev1name' descending='false' /> <filter type='and'> <condition attribute='dev1_name' operator='like' value='{0}%' /> </filter> </entity> </fetch>", searchTerm);

OrganizationServiceProxy.BeginRetrieveMultiple(fetchXml, delegate(object state)
        // We use an aysnc call so that the user interface isn't blocked whilst we are searching for results
        EntityCollection countries = OrganizationServiceProxy.EndRetrieveMultiple(state, typeof(Entity));
        AutocompleteResultSet results = new AutocompleteResultSet();

        // The Autocomplete can have an action button in the footer of the results flyout
        AutocompleteAction addNewAction = new AutocompleteAction();
        addNewAction.Id = "add_new";
        addNewAction.Icon = @"/_imgs/add_10.png";
        addNewAction.Label = "New";
        addNewAction.Action = delegate()
            OpenEntityFormOptions windowOptions = new OpenEntityFormOptions();
            windowOptions.OpenInNewWindow = true;
            Utility.OpenEntityForm2("dev1_country", null,null, windowOptions);
        results.Commands = addNewAction;
        results.Results = new List&lt;AutocompleteResult&gt;();

        // Add the results to the autocomplete parameters object
        foreach (Entity country in countries.Entities)
            AutocompleteResult result = new AutocompleteResult();
            result.Id = country.Id;
            result.Icon = country.GetAttributeValueString("entityimage_url");
            result.Fields = new string[] { country.GetAttributeValueString("dev1_name"),                           
            ArrayEx.Add(results.Results, result);                    
        if (results.Results.Count &gt; 0)
            // Only show the autocomplete if there are results
            // There are no results so hide the autocomplete
    catch(Exception ex)
        Utility.AlertDialog("Could not load countries: " + ex.Message, null);


This would result in JavaScript: ClientHooks.Autocomplete = function ClientHooksAutocomplete() { } ClientHooks.Autocomplete.onLoad = function ClientHooksAutocomplete$onLoad() { var control = Xrm.Page.getControl('dev1countryautocomplete'); control.addOnKeyPress(ClientHooks.Autocomplete.onCountrySearch); } ClientHooks.Autocomplete.onCountrySearch = function ClientHooksAutocomplete$onCountrySearch(context) { var searchTerm = Xrm.Page.getControl('dev1countryautocomplete').getValue(); var fetchXml = String.format("<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>\r\n <entity name='dev1country'>\r\n <attribute name='dev1countryid' />\r\n <attribute name='createdon' />\r\n <attribute name='dev1name' />\r\n <attribute name='dev1longname' />\r\n <attribute name='dev1iso' />\r\n <attribute name='entityimageurl' />\r\n <order attribute='dev1name' descending='false' />\r\n <filter type='and'>\r\n <condition attribute='dev1name' operator='like' value='{0}%' />\r\n </filter>\r\n </entity>\r\n </fetch>", searchTerm); Xrm.Sdk.OrganizationServiceProxy.beginRetrieveMultiple(fetchXml, function(state) { try { var countries = Xrm.Sdk.OrganizationServiceProxy.endRetrieveMultiple(state, Xrm.Sdk.Entity); var results = {}; var addNewAction = {}; = 'addnew'; addNewAction.icon = '/imgs/add10.png'; addNewAction.label = 'New'; addNewAction.action = function() { var windowOptions = {}; windowOptions.openInNewWindow = true; Xrm.Utility.openEntityForm('dev1country', null, null, windowOptions); }; results.commands = addNewAction; results.results = []; var $enum1 = ss.IEnumerator.getEnumerator(countries.getentities()); while ($enum1.moveNext()) { var country = $enum1.current; var result = {}; =; result.icon = country.getAttributeValueString('entityimageurl'); result.fields = [ country.getAttributeValueString('dev1name'), country.getAttributeValueString('dev1iso'), country.getAttributeValueString('dev1longname') ]; Xrm.ArrayEx.add(results.results, result); } if (results.results.length > 0) { context.getEventSource().showAutoComplete(results); } else { context.getEventSource().hideAutoComplete(); } } catch (ex) { Xrm.Utility.alertDialog('Could not load countries: ' + ex.message, null); } }); }

ClientHooks.Autocomplete.registerClass('ClientHooks.Autocomplete'); });