Creating Web Resources using Dynamics CRM Themes

Very often there is a requirement to create Web Resources to be used in Dynamics CRM that provide some form of user interface.  Sometimes it may just be to display some data that you otherwise could not display using QuickView Forms, sometimes its to provide some buttons to trigger integrations that need to be on a Dashboard.

I have seen instances where the standard Windows Grey buttons are used, and also seen Web Resources that are graphically styled like they have come straight out of the Web 2.0 UI style guide.  These often look inconsistent with how Dynamics CRM looks, and can either be distracting, or look like a dog’s dinner.

Often, most good CRM developers will create a standard style sheet, upload it to their solution, and reference it from any Web Resource that needs it.  I would say that this is still a good thing to do, but I wanted to enhance it a bit so that a UI provided by a Web Resource would blend in with CRM without any additional work.  This has especially become crucial when a lot of companies will brand their Sandbox environments different to their Production environments (sometimes using garish colour schemes) to instantly alert their users as to which environment they are on.  CRM provides Custom Themes for this task, and although they are still quite limited, I wanted to be able to tap in to this feature.

Changing the colour scheme of CRM

So, I had the idea of creating a mechanism that would automatically retrieve the Theme and style up any User Interface elements using those settings.

Retrieving the theme

To retrieve the theme, you can use a simple Web API call to retrieve the JSON representation of the Theme.  I wanted to have it so that the script does this without having any dependencies on any other scripts, so I kept it very simple.


document.onreadystatechange = function () {
    if (document.readyState == "complete") {

        if (typeof Xrm == 'undefined')
        {
            Xrm = window.parent.Xrm;
        }
        if (typeof Xrm != 'undefined' && Xrm != null) {
            var req = new XMLHttpRequest();
            req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/themes?$select=backgroundcolor,controlborder,controlshade,defaultcustomentitycolor,defaultentitycolor,globallinkcolor,headercolor,hoverlinkeffect,_logoid_value,logotooltip,name,navbarbackgroundcolor,navbarshelfcolor,processcontrolcolor,selectedlinkeffect&$filter=isdefaulttheme eq true", true);
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
            req.onreadystatechange = function () {
                if (this.readyState === 4) {
                    req.onreadystatechange = null;
                    if (this.status === 200)
                    {
                        var results = JSON.parse(this.response);
                        var theme = results.value[0];
                    }
                    else {
                        alert(this.statusText);
                    }
                }
            };
            req.send();
        }
    }
}

Creating the style sheet dynamically

Creating the style sheet from this was fairly straight forward. I defined a variable at the top of my script to hold the style sheet (although I could have come up with some clever way of retrieving the style sheet from a Web Resource, I wanted to keep it simple), with a number of specific replacement tokens that I could do a string replace on.


var STYLE_SHEET = "\
                    html, body\
                                    {\
                                        background-color: #fff;\
                                        font-family: 'Segoe UI', Tahoma, Arial;\
                                        font-size: 12px;\
                                    }\
                    button          {\
                                        background: {NAVBARBACKGROUNDCOLOR};\
                                        color: white;\
                                        border-color:{NAVBARBACKGROUNDCOLOR};\
                                        cursor:pointer;\
                                    }\
                    button:hover    {\
                                        opacity:0.4;\
                                    }\
                    a               {\
                                        color:{GLOBALLINKCOLOR};\
                                        text-decoration:none;\
                                    }\
                    a:hover         {\
                                        color:{GLOBALLINKCOLOR};\
                                        text-decoration:underline;\
                                    }\
                    h1              {\
                                        color:{HEADERCOLOR};\
                                    }\
                    h2              {\
                                        color:{HEADERCOLOR};\
                                    }\
                    h3              {\
                                        color:{HEADERCOLOR};\
                                    }\
                    h4              {\
                                        color:{HEADERCOLOR};\
                                    }\
";

Next, was to have a chunk of JavaScript to do the string replace and create the actual style sheet.


function createStylesheet(theme)
{
    var backgroundcolor = theme["backgroundcolor"];
    var controlborder = theme["controlborder"];
    var controlshade = theme["controlshade"];
    var defaultcustomentitycolor = theme["defaultcustomentitycolor"];
    var defaultentitycolor = theme["defaultentitycolor"];
    var globallinkcolor = theme["globallinkcolor"];
    var headercolor = theme["headercolor"];
    var hoverlinkeffect = theme["hoverlinkeffect"];
    var _logoid_value = theme["_logoid_value"];
    var _logoid_value_formatted = theme["_logoid_value@OData.Community.Display.V1.FormattedValue"];
    var logotooltip = theme["logotooltip"];
    var name = theme["name"];
    var navbarbackgroundcolor = theme["navbarbackgroundcolor"];
    var navbarshelfcolor = theme["navbarshelfcolor"];
    var processcontrolcolor = theme["processcontrolcolor"];
    var selectedlinkeffect = theme["selectedlinkeffect"];

    STYLE_SHEET = STYLE_SHEET.replace(/{NAVBARBACKGROUNDCOLOR}/g, navbarbackgroundcolor).
                                replace(/{GLOBALLINKCOLOR}/g, globallinkcolor).
                                replace(/{GLOBALLINKCOLOR}/g, backgroundcolor).
                                replace(/{GLOBALLINKCOLOR}/g, controlborder).
                                replace(/{GLOBALLINKCOLOR}/g, controlshade).
                                replace(/{GLOBALLINKCOLOR}/g, defaultcustomentitycolor).
                                replace(/{GLOBALLINKCOLOR}/g, defaultentitycolor).
                                replace(/{GLOBALLINKCOLOR}/g, hoverlinkeffect).
                                replace(/{GLOBALLINKCOLOR}/g, navbarshelfcolor).
                                replace(/{GLOBALLINKCOLOR}/g, processcontrolcolor).
                                replace(/{GLOBALLINKCOLOR}/g, selectedlinkeffect).
                                replace(/{HEADERCOLOR}/g, headercolor);
    var head = document.head || document.getElementsByTagName('head')[0];
    var style = document.createElement('style');

    style.type = 'text/css';
    if (style.styleSheet) {
        style.styleSheet.cssText =STYLE_SHEET;
    } else {
        style.appendChild(document.createTextNode(STYLE_SHEET));
    }

    head.appendChild(style);
    
}

(The full script can be found at the bottom of this post)

Using the new script

To actually use the script, all that needs to be done is to reference the script within a Web Resource (being careful to choose the correct path). The script will run on load of the Web Resource, download the theme, and apply it as a style sheet. Hopefully, the UI elements of the Web Resource will then take on the correct theme. Obviously, the style sheet held in the variable can be expanded on as you see fit. There is a small loading delay at the moment with the script. This could easily be cached in browser storage to speed it up for future requests if required.

Using the default theme in a Web Resource
Using a modified theme in a Web Resource

The full theme.js script



var STYLE_SHEET = "\
                    html, body\
                                    {\
                                        background-color: #fff;\
                                        font-family: 'Segoe UI', Tahoma, Arial;\
                                        font-size: 12px;\
                                    }\
                    button          {\
                                        background: {NAVBARBACKGROUNDCOLOR};\
                                        color: white;\
                                        border-color:{NAVBARBACKGROUNDCOLOR};\
                                        cursor:pointer;\
                                    }\
                    button:hover    {\
                                        opacity:0.4;\
                                    }\
                    a               {\
                                        color:{GLOBALLINKCOLOR};\
                                        text-decoration:none;\
                                    }\
                    a:hover         {\
                                        color:{GLOBALLINKCOLOR};\
                                        text-decoration:underline;\
                                    }\
                    h1              {\
                                        color:{HEADERCOLOR};\
                                    }\
                    h2              {\
                                        color:{HEADERCOLOR};\
                                    }\
                    h3              {\
                                        color:{HEADERCOLOR};\
                                    }\
                    h4              {\
                                        color:{HEADERCOLOR};\
                                    }\
";


document.onreadystatechange = function () {
    if (document.readyState == "complete") {

        if (typeof Xrm == 'undefined') 
        {
                Xrm = window.parent.Xrm;
        }
        if (typeof Xrm != 'undefined' && Xrm != null) {

            var req = new XMLHttpRequest();
            req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/themes?$select=backgroundcolor,controlborder,controlshade,defaultcustomentitycolor,defaultentitycolor,globallinkcolor,headercolor,hoverlinkeffect,_logoid_value,logotooltip,name,navbarbackgroundcolor,navbarshelfcolor,processcontrolcolor,selectedlinkeffect&$filter=isdefaulttheme eq true", true);
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
            req.onreadystatechange = function () {
                if (this.readyState === 4) {
                    req.onreadystatechange = null;
                    if (this.status === 200)
                    {
                        var results = JSON.parse(this.response);
                        createStylesheet(results.value[0]);
                    }
                    else {
                        alert(this.statusText);
                    }
                }
            };
            req.send();
            
        }
    }
}

function createStylesheet(theme)
{
    var backgroundcolor = theme["backgroundcolor"];
    var controlborder = theme["controlborder"];
    var controlshade = theme["controlshade"];
    var defaultcustomentitycolor = theme["defaultcustomentitycolor"];
    var defaultentitycolor = theme["defaultentitycolor"];
    var globallinkcolor = theme["globallinkcolor"];
    var headercolor = theme["headercolor"];
    var hoverlinkeffect = theme["hoverlinkeffect"];
    var _logoid_value = theme["_logoid_value"];
    var _logoid_value_formatted = theme["_logoid_value@OData.Community.Display.V1.FormattedValue"];
    var logotooltip = theme["logotooltip"];
    var name = theme["name"];
    var navbarbackgroundcolor = theme["navbarbackgroundcolor"];
    var navbarshelfcolor = theme["navbarshelfcolor"];
    var processcontrolcolor = theme["processcontrolcolor"];
    var selectedlinkeffect = theme["selectedlinkeffect"];

    STYLE_SHEET = STYLE_SHEET.replace(/{NAVBARBACKGROUNDCOLOR}/g, navbarbackgroundcolor).
                                replace(/{GLOBALLINKCOLOR}/g, globallinkcolor).
                                replace(/{GLOBALLINKCOLOR}/g, backgroundcolor).
                                replace(/{GLOBALLINKCOLOR}/g, controlborder).
                                replace(/{GLOBALLINKCOLOR}/g, controlshade).
                                replace(/{GLOBALLINKCOLOR}/g, defaultcustomentitycolor).
                                replace(/{GLOBALLINKCOLOR}/g, defaultentitycolor).
                                replace(/{GLOBALLINKCOLOR}/g, hoverlinkeffect).
                                replace(/{GLOBALLINKCOLOR}/g, navbarshelfcolor).
                                replace(/{GLOBALLINKCOLOR}/g, processcontrolcolor).
                                replace(/{GLOBALLINKCOLOR}/g, selectedlinkeffect).
                                replace(/{HEADERCOLOR}/g, headercolor);
    var head = document.head || document.getElementsByTagName('head')[0];
    var style = document.createElement('style');

    style.type = 'text/css';
    if (style.styleSheet) {
        style.styleSheet.cssText =STYLE_SHEET;
    } else {
        style.appendChild(document.createTextNode(STYLE_SHEET));
    }

    head.appendChild(style);
    
}


The test file


<html><head></head><body>



    
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <title></title>

    <insert path to script here>

    <button>This is a button</button>
    
<h1>This is a Header One</h1>


<h2>This is a Header Two</h2>


<h3>This is a Header Three</h3>


<h4>This is a Header Four</h4>

    <a href="#">This is a hyperlink</a>

</body></html>

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.