Control Activate and Deactivate buttons by Security Role using a JScript web resource and enhancing the RibbonDiffXml

The security model in Microsoft Dynamics CRM 2011 is extremely flexible and with the inclusion of field level security, is almost ultimately configurable. However, there is a small deficiency in the security model whereby the ability to activate and de-activate records is not independently configurable. Several of our customers have expressed a desire to allow their users to write to a record but not allow the activation or de-activation.

I have posted a product enhancement suggestion on Microsoft Connect here, but in the meantime, here is how to configure an entity to only allow users with a defined security role to activate or de-activate a record:



A user may activate or de-activate records from list views and within the record’s form so we must enhance the RibbonDiffXml of our chosen entity to disable these buttons depending on the result returned by a JScript web resource which evaluates the user’s security role.


The following example will disable the activate and de-activate buttons on both the list view and form of the ‘Account’ entity for all users except the System Administrator.


Step 1.


Create a JScript web resource (I have called my example gap_jscript/common.js) and add the following script (Replace ‘System Administrator’ or add extra roles to the if(UserHasRole condition to grant access to those security roles who need to activate or de-activate the records) :


/*GAP-RB-14-06-2011 Functions to facilitate activate/deactivate button disable */
function callMain() {
if(UserHasRole("System Administrator"))
{
return true;
}
else
{
return false;
}
}
 
function UserHasRole(roleName) {
    var serverUrl = Xrm.Page.context.getServerUrl();
    var oDataEndpointUrl = serverUrl + "/XRMServices/2011/OrganizationData.svc/";
    oDataEndpointUrl += "RoleSet?$top=1&$filter=Name eq '" + roleName + "'";
    var service = GetRequestObject();
 
    if (service != null) {
        service.open("GET", oDataEndpointUrl, false);
        service.setRequestHeader("X-Requested-Width", "XMLHttpRequest");
        service.setRequestHeader("Accept", "application/json, text/javascript, */*");
        service.send(null);
 
        var requestResults = eval('(' + service.responseText + ')').d;
 
        if (requestResults != null && requestResults.results.length == 1) {
            var role = requestResults.results[0];
            var id = role.RoleId;
            var currentUserRoles = Xrm.Page.context.getUserRoles();
 
            for (var i = 0; i < currentUserRoles.length; i++) {
                var userRole = currentUserRoles[i];
 
                if (GuidsAreEqual(userRole, id)) {
                    return true;
                }
            }
        }
    }
    return false;
}
 
function GetRequestObject() {
    if (window.XMLHttpRequest) {
        return new window.XMLHttpRequest;
    }
    else {
        try {
            return new ActiveXObject("MSXML2.XMLHTTP.3.0");
        }
        catch (ex) {
            return null;
        }
    }
}
 
function GuidsAreEqual(guid1, guid2) {
    var isEqual = false;
 
    if (guid1 == null || guid2 == null) {
        isEqual = false;
    }
    else {
        isEqual = guid1.replace(/[{}]/g, "").toLowerCase() == guid2.replace(/[{}]/g, "").toLowerCase();
    }
 
    return isEqual;
}
 


Step 2.


Create a solution that contains only the entity that you wish to apply this ribbon change to. Export the solution (keep a backup in a separate folder) and extract the zip file. Open the ‘Customisations.xml’ file, ideally using an xml editor (such as XML Notepad or Visual Studio), alternatively, you can use Notepad.


Search for the ‘RibbonDiffXml’ element.


By default, the RibbonDiffXml element will look like this:


<RibbonDiffXml>
  <CustomActions />
  <Templates>
    <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
  </Templates>
  <CommandDefinitions />
  <RuleDefinitions>
    <TabDisplayRules />
    <DisplayRules />
    <EnableRules />
  </RuleDefinitions>
  <LocLabels />
</RibbonDiffXml>
 


Replace the RibbonDiffXml with the following (ensure the highlighted text matches the name of your WebResource):


<RibbonDiffXml>
  <CustomActions />
  <Templates>
    <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
  </Templates>
  <CommandDefinitions>
    <CommandDefinition Id="Mscrm.Form.Activate">
      <EnableRules>
        <EnableRule Id="Mscrm.CustomcheckRole" />
        <EnableRule Id="Mscrm.CanWritePrimary" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="Mscrm.CanWritePrimary" />
        <DisplayRule Id="Mscrm.PrimaryIsInactive" />
        <DisplayRule Id="Mscrm.PrimaryEntityHasStatecode" />
        <DisplayRule Id="Mscrm.PrimaryIsNotActivity" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="changeState" Library="/_static/_forms/form.js">
          <StringParameter Value="activate" />
          <CrmParameter Value="PrimaryEntityTypeCode" />
          <StringParameter Value="6" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
    <CommandDefinition Id="Mscrm.HomepageGrid.Deactivate">
      <EnableRules>
        <EnableRule Id="Mscrm.CustomcheckRole" />
        <EnableRule Id="Mscrm.VisualizationPaneNotMaximized" />
        <EnableRule Id="Mscrm.SelectionCountAtLeastOne" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="Mscrm.CanWriteSelected" />
        <DisplayRule Id="Mscrm.SelectedEntityHasStatecode" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="Mscrm.GridRibbonActions.deactivate" Library="/_static/_common/scripts/RibbonActions.js">
          <CrmParameter Value="SelectedControl" />
          <CrmParameter Value="SelectedControlSelectedItemReferences" />
          <CrmParameter Value="SelectedEntityTypeCode" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
    <CommandDefinition Id="Mscrm.Form.Deactivate">
      <EnableRules>
        <EnableRule Id="Mscrm.CustomcheckRole" />
        <EnableRule Id="Mscrm.CanWritePrimary" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="Mscrm.CanWritePrimary" />
        <DisplayRule Id="Mscrm.PrimaryIsActive" />
        <DisplayRule Id="Mscrm.PrimaryEntityHasStatecode" />
        <DisplayRule Id="Mscrm.PrimaryIsNotActivity" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="changeState" Library="/_static/_forms/form.js">
          <StringParameter Value="deactivate" />
          <CrmParameter Value="PrimaryEntityTypeCode" />
          <StringParameter Value="5" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
    <CommandDefinition Id="Mscrm.HomepageGrid.Activate">
      <EnableRules>
        <EnableRule Id="Mscrm.CustomcheckRole" />
        <EnableRule Id="Mscrm.VisualizationPaneNotMaximized" />
        <EnableRule Id="Mscrm.SelectionCountAtLeastOne" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="Mscrm.CanWriteSelected" />
        <DisplayRule Id="Mscrm.SelectedEntityHasStatecode" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="Mscrm.GridRibbonActions.activate" Library="/_static/_common/scripts/RibbonActions.js">
          <CrmParameter Value="SelectedControl" />
          <CrmParameter Value="SelectedControlSelectedItemReferences" />
          <CrmParameter Value="SelectedEntityTypeCode" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
  </CommandDefinitions>
  <RuleDefinitions>
    <TabDisplayRules />
    <DisplayRules />
    <EnableRules>
      <EnableRule Id="Mscrm.CustomcheckRole">
        <CustomRule FunctionName="callMain" Library="$webresource:gap_jscript/common.js"></CustomRule>
      </EnableRule>
    </EnableRules>
  </RuleDefinitions>
  <LocLabels />
</RibbonDiffXml>
 
Save the Customizations.xml file and add to a zip file along with the [Content_Types].xml and solution.xml that were extracted along with the customizations.xml file originally.


Import your solution and publish.


Log-in to your crm organisation as a user who does not have the system administrator security role and navigate to the accounts list view.

The ‘Activate’ and ‘Deactivate’ buttons on both the list view and form will be disabled.


Rob Boyers