D365: Enable Rule for button with asynchronous API request using promise

Many times, we get requirement to show/hide a button based on certain condition for which we define Enable Rule in Ribbon Workbench.

If we need javaScript code for evaluation then we use Custom Enable Rule mentioning library, method to execute, passing PrimaryControl CRM parameter and additional parameters if necessary.

Sometimes, the evaluation is done real time(based on the logic written) and we return true/false based on which the button remains visible/hidden. At times, we need to make additional API request which is asynchronous in nature e.g. show/hide button based on security role and in this case the result of the method/operation doesn’t affect the visibility of the button.

In this post, we’ll see how we can use promise to fix this issue and the scenario that we’ll take is hide Delete button on Document subgrid if the logged in user doesn’t have “System Administrator” security role.

Since we will make async API request to achieve this, returning appropriate boolean value as shown in below code will not affect visibility of the button:

DXC.ShowHideDeleteDocumentButton = function (selectedControl) {
	//we wanted to hide Delete button on Document subgrid for a particular entity for which we passed SelectedControl CRM Parameter in Ribbon Workbench to check the relationship name
    if (selectedControl.getRelationship().name === "tri_patientclaimssharing_SharePointDocuments") {
        var userSettings = Xrm.Utility.getGlobalContext().userSettings;
        var securityRoles = userSettings.securityRoles;

        if (securityRoles === null) return false;

        var globalContext = Xrm.Utility.getGlobalContext();        
		var req = new XMLHttpRequest();
		req.open("GET", globalContext.getClientUrl() + "/api/data/v9.1/roles?$select=roleid&$filter=name eq 'system%20administrator'", 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=\"*\"");
		req.onreadystatechange = function () {
			if (this.readyState === 4) {
				req.onreadystatechange = null;
				if (this.status === 200) {
					var results = JSON.parse(this.response);
					//returns GUID of System Administrator security role in all the Business Units
					for (var i = 0; i < results.value.length; i++) {
						var roleid = results.value[i]["roleid"].toLowerCase();
						if (securityRoles.includes(roleid) === true) {
							return true;							
						}
					}
					return false;
				} else {
					return false;
				}
			}
		};
		req.send();
    }
    else {
		//For all other entities don't hide Delete button on Document subgrid
        return true;
    }
};

Below is the code for achieving what we want by using promise.

DXC.ShowHideDeleteDocumentButton = function (selectedControl) {
	//we wanted to hide Delete button on Document subgrid for a particular entity for which we passed SelectedControl CRM Parameter in Ribbon Workbench to check the relationship name
    if (selectedControl.getRelationship().name === "tri_patientclaimssharing_SharePointDocuments") {
        var userSettings = Xrm.Utility.getGlobalContext().userSettings;
        var securityRoles = userSettings.securityRoles;

        if (securityRoles === null) return false;

        var globalContext = Xrm.Utility.getGlobalContext();
        return new Promise(function (resolve, reject) {
            var isAdmin = false;
            var req = new XMLHttpRequest();
            req.open("GET", globalContext.getClientUrl() + "/api/data/v9.1/roles?$select=roleid&$filter=name eq 'system%20administrator'", 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=\"*\"");
            req.onreadystatechange = function () {
                if (this.readyState === 4) {
                    req.onreadystatechange = null;
                    if (this.status === 200) {
                        var results = JSON.parse(this.response);
						//returns GUID of System Administrator security role in all the Business Units
                        for (var i = 0; i < results.value.length; i++) {
                            var roleid = results.value[i]["roleid"].toLowerCase();
                            if (securityRoles.includes(roleid) === true) {
                                isAdmin = true;
                            }
                        }
                        resolve(isAdmin);
                    } else {
                        reject(this.statusText);
                    }
                }
            };
            req.send();
        });
    }
    else {
        //For all other entities don't hide Delete button on Document subgrid
		return true;
    }
};

NOTE: If the promise does not resolve within 10 seconds, the rule will resolve with a false value.

References: https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/customize-dev/define-ribbon-enable-rules#custom-rule

Hope it helps !!

4 thoughts on “D365: Enable Rule for button with asynchronous API request using promise

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

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