Tuesday 17 September 2013

Retrieve System Users by a Security Role (CRM 2011)

We have had the need to retrieve an entity collection of system users from a security role in a plugin. We developed a method that accept the role's name and a collection of attributes of System User entity.
In this method we use query expression to retrieve the system user specifying a link to entity "SystemUserRoles" and then a link to the "Role" entity. The linking attribute is the SystemUserId for the first link and the RoleId for the second one.
Finally a condition on the role's name to filter the system users.

public EntityCollection RetrieveUsersInfoByRole(string roleName,
                                                string[] userColumnSet)
{
    try
    {
        serviceProxy = ServiceConnector.
                       GetOrganizationService("<Organization.svc URL>");
 
        QueryExpression query = new QueryExpression("systemuser");
        query.ColumnSet = new ColumnSet(userColumnSet);
        query.Distinct = true;
        query.Criteria = new FilterExpression();
        //Add link to system user roles and role
        query.AddLink("systemuserroles", "systemuserid", "systemuserid").
            AddLink("role", "roleid", "roleid").LinkCriteria.
            AddCondition("name", ConditionOperator.Equal, roleName);
 
        EntityCollection users = serviceProxy.RetrieveMultiple(query);
 
        return users;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

 
Hope it can be useful.
Happy CRM coding

Monday 29 July 2013

Clone an entity record via JavaScript (CRM 2011)

We have had the need to clone a record with client-side Scripting, responding to a click on a button in the entity's Ribbon.
Conceptually the operation is easy and is based on the retrieving of all the record information using the Organization web service, then build an object identical to the first and use the web service to create a new record with this object.
Few things are important to take in mind:
1. In the building of the object the record ID must be "skipped" because, obviously, we can't create a record with an existing Guid
2. Date fields are returned in a format like: "Date("date_string") that it is not accepted in record creation from the Organization web service: depending of the need of every field, we decided to "skip it" or "reformat it". In the sample code we show both of these cases.
3. StateCode and StatusCode should be skipped (or managed) because, natively, Organization web service doesn't allow the creation of a record in a closed state.


function cloneRecord(serverUrl, originRecordType, originRecordGuid) {
    var cloneData = {};
    var activityId;
    var serverUrl = document.location.protocol + "//" + document.location.host
                    + "/"Xrm.Page.context.getOrgUniqueName();
    var oDataUri = serverUrl + "/xrmservices/2011/OrganizationData.svc/" 
                   originRecordType + "Set?$select=*&$filter=<EntityId> eq guid'"
                   + originRecordGuid + "'";
    jQuery.support.cors = true;
    jQuery.ajax({
        type: "GET",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: oDataUri,
        async: false, //Synchronous operation 
        beforeSend: function (XMLHttpRequest) {
            //Specifying this header ensures that the results
              will be returned as JSON.           
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
        },
        success: function (data, textStatus, XmlHttpRequest) {
            if (data && data.d && data.d.results) {
                cloneData = data.d.results[0];
                //Here insert the code to skip/transform fields such as
                  Record Id, Date fields, etc..
                replacer = function (key, value) {
                    if (key == "ModifiedOn" || key == originRecordType + "Id" ||
                        key == "CreatedOn" || key == "StateCode" ||
                        key == "StatusCode") {
                       
                        return undefined;
                    } else if (key == "ActualStart" || key == "ActualEnd") {
                        if (value) {
                            var date =
                            eval('new ' +
                               value.replace("/", "").replace("/", ""));
                            return date.format('yyyy-MM-dd'); //Format the date
                        }
                    }
                    else return value;
                }
                //Create new Activity
                var oDataUri = serverUrl +
                               "/xrmservices/2011/OrganizationData.svc/" +
                               originRecordType + "Set";
                jQuery.support.cors = true;
                jQuery.ajax({
                    type: "POST",
                    contentType: "application/json; charset=utf-8",
                    datatype: "json",
                    url: oDataUri,
                    async: false, //Synchronous operation 
                    data: JSON.stringify(cloneData, replacer),
                    beforeSend: function (XMLHttpRequest) {
                        //Specifying this header ensures that the results will be
                          returned as JSON.           
                        XMLHttpRequest.setRequestHeader("Accept",
                                                          "application/json");
                    },
                    success: function (data, textStatus, XmlHttpRequest) {
                        if (data && data.d) {
                            activityId = data.d.ActivityId;
                        }
                    },
                    error: function (XmlHttpRequest, textStatus, errorThrown) {
                      alert("Error :  has occured during creation of
                              the activity "  + originRecordType.text + ": " +
                             XmlHttpRequest.responseText);
                    }
                });
            } else {
                //No data returned
            }
        },
        error: function (XmlHttpRequest, textStatus, errorThrown) {
            alert("Error :  has occured during retrieving of the activity "
                    + originRecordType.text + ": " +
                    XmlHttpRequest.responseText);
        }
    });
    return activityId;
}
Hope it can be useful
Happy CRM coding

Monday 8 July 2013

Open an HTML web resource page in a modal dialog, return values from it and use them in the caller Entity form via Javascript / jQuery (CRM 2011)

Sometimes it could be useful to open an external page as a modal window from an entity form and return some values to the caller entity form.
In the following example we show a simple HTML page with an option set and a text box; via javascript, we manage the click on an 'Ok' button to return both the entered text and the chosen option value.
The HTML page could be opened from a ribbon button or in the way that better fit our needs. We show the code snippet to open the modal dialog, take and process the return.



HTML Code:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
       <head>
           <title>HTML Web Resource</title>
           <script type="text/javascript"
                      src="../Scripts/jquery_1.9.0.js"></script>
           <script type="text/javascript" language="javascript">

              $(document).ready(function () {
                  var selectedOption;

                  $('#selectmenu').change(function () {
                     // assign the value to variable
                     selectedOption = $('#selectmenu :selected').val();
                  });

                  $('#btnOk').click(function () {
                     var result = {};
                     result.EnteredText = $('#textbox').val();
                     result.SelectedOption = selectedOption;

                     window.returnValue = result;
                     window.close();
                });
            });
           </script>
       </head>
       <body>
        <div>
             <select id="selectmenu">
             <option value="" selected="selected">
                    Select an option
                </option>
                <option value="option1">Option One</option>
                <option value="option2">Option Two</option>
                <option value="option1N">Option N</option>
            </select>
            <br />
            <label>Enter a text: </label>
            <input id="textbox" type="text" />
            <br />
            <input id="btnOk" type="button" value="Ok"/>
        </div>
       </body>
</html>


Code snippet (open dialog):

var retObj = window.showModalDialog('HTML_WebResource_url', null, options);
//Check if the return object is null
if (retObj == null) {
       alert('Error in return');
       return;
}

//Check if returned property 'EnteredText' value is not null

if (retObj.EnteredText) {

    //Use in the form

    Xrm.Page.getAttribute('attribute1_name').setValue(retObj.EnteredText);

}

 
//Check if returned property 'SelectedOption' value is not null
if (retObj.SelectedOption) {
    //Use in the form
    Xrm.Page.getAttribute('attribute2_name').setValue(retObj.SelectedOption);
}
 

Hope it can be useful
Happy CRM coding

Thursday 27 June 2013

Enabling/Disabling "New Activity" ribbon button based on an attribute value of the record with a custom Javascript function and Ribbon Workbench (CRM 2011)

We have had the need to enable or disable the "New Activity" button on the ribbon of the Incident entity based on the value of a record attribute.
We were inspired from an answer on a similar question on the Microsoft social community. We have followed this solution as base and extended it with a custom Javascript function to achieve our purposes.
The question and answers mentioned above are at the following URL:

http://social.microsoft.com/Forums/en-US/a5a0666f-8a07-4b25-92a3-e36ce8861d04/crm-2011-how-to-disable-the-new-activity-ribbon-button-for-a-deactivated-custom-entity
This solution allow the enabling/disabling based on the status of the record (Active / Inactive).

First of all we have developed the Javascript function similar the following:
function addNewActivityEnableRule() {
    //Retrieve the parent window Page object
    var parentPage = document.parentWindow.parent.Xrm.Page;

    //Gets the desired attribute of the parent window page
    var parentPageAttribute = parentPage.getAttribute('parentPage_attributeName');

    //Check if attribute exists
    if (parentPageAttribute) {
      //Get the value of the attribute
        var parentPageAttributeValue = parentPageAttribute.getValue();

        //'value_for_enabling' : replace with the desired value
          (or extend the condition as needed)
        //Return true for the conditions where you want enable
          the ribbon button and false otherwise
        if (parentPageAttributeValue == 'value_for_enabling') {
            return true;
        } else {
            return false;
        }
    }
}

Our solution consists of the following steps:
1. Create a solution with the ‘Activity’ entity in it
2. Open the Solution in the Ribbon Workbench

3. Select 'Activity' entity (activitypointer)
4. Select the 'SubGrid' ribbon in the top right drop down
5. Select the 'New {0}' button and right-click -> Customise Command. Don’t select 'Customise Button'!


 
6. In the 'Solution Elements', expand the 'Display Rules' and for each display rule, set the 'IsCore' property to 'True' since we don’t want to customise these – only the command.
7. In the 'Solution Elements', expand the 'Commands' and select the 'Mscrm.NewRecordFromGrid' command and Right-Click ->'Edit Enable Rules'
8. Click 'Add New' and then 'Add Step'. Select a 'Custom Javascript Rule'

 

9. Set the properties:
      1. InvertResult: False
      2. Default: False


10. Provide the function name and the web resource script library where the function resides
11. Click 'Publish Solution'


Hope it can be useful
Happy CRM coding

Friday 14 June 2013

Copy Rich Text Box field content into a text field with JavaScript (CRM 2011)

Richtextbox fields content (for example the body of an email activity) is a mix of text and tags: when copying its content into another CRM field of type Text, the destination field will contain also the tags.
Because the representation of a Textbox field is a simple string and it is not formatted as in the case of a Richtextbox, the result is that the text showed is similar to an HTML content.
This is not user friendly for CRM users, therefore we should remove all the tags and let only the original content; obviously, we will lose the formatting (bold, underline, etc...).
We can do that with JavaScript, using the regular expressions, as shown below:


var richTextBoxContent =
    Xrm.Page.getAttribute("richtextbox_attribute_name").getValue();
var richTextBoxContentStripped =
    richTextBoxContent.replace(/(<([^>]+)>)/ig, '');

Xrm.Page.getAttribute("text_attribute_name").setValue(richTextBoxContentStripped);


Hope it can be useful.
Happy CRM coding.

Saturday 8 June 2013

Work with Assign SDK message in CRM 2011 plugin - manage logic based on the Assignee Team (CRM 2011)

In the following example I use the Assign message in CRM 2011 to manage some custom logic based on the assignee Team.
The plugin is registered in Pre-Operation stage on the Task entity and uses early-bound entity classes.
However it is trivial reuse the same logic on another type of entity, use late-bound entity classes, or operate on other type of assignee.

protected void ExecutePreTaskAssign(LocalPluginContext localContext)
{
    if (localContext == null)
    {
        throw new ArgumentNullException("localContext");
    }

    IPluginExecutionContext context = localContext.PluginExecutionContext;
    IOrganizationService service = localContext.OrganizationService;

    try
    {
        //Check if context contains the "Target" parameter and that it
          is an EntityReference
        if (context.InputParameters.Contains("Target") && 
                     context.InputParameters["Target"is EntityReference)
        {
            EntityReference targetEntity =    
                     (EntityReference)context.InputParameters["Target"];
            //Check if the entity type is Task
            if (targetEntity.LogicalName != "task")
            { return; }

            //Check if context contains the "Assignee" parameter
            if (context.InputParameters.Contains("Assignee"))
            {
                EntityReference assigneeRef = 
                       (EntityReference)context.InputParameters["Assignee"];
                //Check if the "Assignee" is a team
                if (assigneeRef.LogicalName == "team")
                {
                    Team team;
                    //Retrieve the team with all properties
                    team = service.Retrieve(assigneeRef.LogicalName, 
                        assigneeRef.Id, new ColumnSet(true)).ToEntity<Team>();

                    //Put your logic here based on the retrieved Team informations
                }
            }
        }
    }

    catch (Exception e)
    {
        throw new InvalidPluginExecutionException("An error occured for 
                Assign plugin " + e.Message + e.InnerException);
    }
}


Hope it can be useful.
Happy CRM coding.

Tuesday 4 June 2013

Filter Option Set on values in a string (CRM 2011)

Sometimes it could be useful restricting the available options for an Option Set.
I have developed a function that accept as input the CRM option set control and a string.
This string parameter contains all string values that we want in the option set.
For reading simplicity I use to separate them with a character (';' for example) but technically it is not a need: the function searches every option value as a substring of it.
Another way to achieve this goal it could be to work with a list (an array) of accepted values, but I have found much easier and less complex the use of a string as "accepted values container".
 

function filterOptionSetValues(optionSetControl, filterValues) {
if (filterValues) {
var options = optionSetControl.getAttribute().getOptions(); //Get the list of values
optionSetControl.clearOptions(); //Clear Option set
for (var i = 0; i < options.length; i++) {
var find = filterValues.indexOf(options[i].text); //Search the string value in the string
if (find >= 0) {
if (parseInt(options[i].value) > 0)
{
//Build a new option and add to the control
var opt = new Option();
opt.text = options[i].text;
opt.value = options[i].value;
optionSetControl.addOption(opt);
}
}
}
}
}
 


Usage example:
var acceptedValues = "value1;value2;";
filterOptionSetValues(Xrm.Page.getControl("optionSet_name"), acceptedValues);


Hope it can be useful.
Happy CRM coding.