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.
Hope it helps !!
Reblogged this on Nishant Rana's Weblog.
LikeLiked by 1 person
Why not using this pattern: https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/xrm-webapi/retrievemultiplerecords
LikeLike
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
LikeLike
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
LikeLike
Hi Manish, please send the code snippet to troubleshoot.
LikeLike
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.
LikeLike
Hi Manish,
Apologies for the late reply. You can now use “Xrm.Utility.getGlobalContext().userSettings.roles” for getting security roles of logged in user. Please refer https://ajitpatra.com/2020/09/11/d365-ce-get-logged-in-users-security-roles-using-javascript/ for more details.
Thanks,
Ajit
LikeLike
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
LikeLike
Hi,
You probably need to have a look at the selected record count rule in Ribbon WorkBench if the result is resolved properly in promise.
LikeLike