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 !!

Advertisement

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

    1. Thanks Michel for your comment. We can use Xrm.WebApi however, this is asynchronous in nature for which we need to refresh the ribbon using Xrm.Page.ui.refreshRibbon() after Xrm.WebApi returns results.

      Regards,
      Ajit

      Like

  1. Hi Ajit,

    I am facing a weird situation with Promise object. I am returning true / false and I can see the same when it is being returned from resolve part. however the promise is not working as expected. I can see the button when promise is returning false. Any suggestions , please help.

    Thanks

    Like

      1. Hi Ajit,

        Here is my code :

        function ShowHideControl() {

        var globalContext = Xrm.Utility.getGlobalContext();
        var userRoles = globalContext.userSettings.securityRoles;

        return new Promise(function (resolve, reject) {
        Xrm.WebApi.retrieveMultipleRecords(EntityLogicalNames.Setting, “?$select=abcd,createdon&$orderby=xyz&$filter=contains(prqs, ‘” + requiredRole + “‘)”).then(function (result) {
        if (result.entities.length > 0) {
        var allowAction = false;
        var userData = result.entities[0].abcd;
        var userAllowedRoles = userData.split(‘~’);

        userRoles.forEach(function (item) {

        userAllowedRoles.some(function (element) {
        if (item.toLowerCase() === element.toLowerCase()) {
        allowAction = true;
        }
        });
        });

        resolve(allowAction);
        }
        }, function (error) {
        reject(error.message);
        });
        });
        }

        Somehow it is not honoring the promise result when it should be hidden the button is not.

        Like

  2. Hi Ajit,
    I am trying the same on Hme Page grid of Contact, but using Xrm.WebApi.retrieveMultipleRecords. when user select a record I need to show/ Hide a button, code is working fine and returning resolve() according, but button is not appearing. However, button appears when no item is selected. following is the ode:

    // Xrm.WebApi.retrieveMultipleRecords(“contact”, fetchXml).then(
    // function success(result) {
    // var oButtonEnabled = false;
    // var isUserInRole = doesUserHaveMembershipRole();
    // if (result.entities.length == 0 && isUserInRole == true ) {
    // oButtonEnabled = true;
    // }
    // // return true or false
    // resolve(oButtonEnabled);
    // // oSelectedControl.refreshRibbon();

    // },
    // function (error) {
    // reject(oButtonEnabled);

    // }

    any suggestions. Thanks

    Like

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 )

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.