Friday, 10 August 2018

Remove optionset values dynamically using Javascript



Remove Optionset Value or Two option set Value Using Javascript


Hi all,

In CRM community I read a question that how to remove the option from Two optionset field based on conditions.

Long back I tried to remove values from Optionset field but I never tried to remove from Two option set field.

So now I changed my code for Two option set as well.

Using this code, we can remove multiple values at a time.

So here is the code

var Seperator = ",";

function RemoveOptionsByText(Field, Options) {


    //------------------------------------------------------------------------
    // Parameters Field - Optionset field
    //              Options - Array or list of text of options to be removed (case insensitive)
    // Action Locates and removes the specified options
    // Returns true if successful, otherwise false
    //------------------------------------------------------------------------

    var result = false;

    if (Field!=null) {
        var obj = Xrm.Page.getControl(Field);
        var Text = new String();
        var chk = new String("");
        var ary;
        var i;

        Options = ListToArray(Options);
        if (Options != null) {
            for (var o = 0; o < Options.length; o++) {
                Text = Options[o];
                if (Text != null) {
                    Text = Text.toUpperCase();
                    ary = Xrm.Page.getAttribute(Field).getOptions();
                    for (i = 0; i < ary.length; i++) {
                        chk = new String(ary[i].text);
                        if (chk.toUpperCase() == Text) {
                            obj.removeOption(ary[i].value);
                            result = true;
                            break;
                        }
                    }
                }
            }
            if (!result && DEBUGGING) alert("No text matches found");
        }
        else {
            if (DEBUGGING) alert("No text provided for match in RemoveOptionsByText");
        }
    }
    return result;
}

function ListToArray(list) {

    //------------------------------------------------------------------------
    // Parameters list - delimited string or array
    // Action Converts list to an array
    // Returns array of items
    //------------------------------------------------------------------------

    if (!Array.isArray(list)) {
        if (list + "" == "") return null;
        list = list.toString();
        list = list.split(Seperator);
    }

    return list;
}

function OnLoad(){
// Your Condition
RemoveOptionsByText("new_testtwooptions","yes");   // Here I am removing Yes from Two options field
//RemoveOptionsByText("new_testtwooptions","no");   // Here I am removing No from Two options field
//RemoveOptionsByText("new_optionsetfield",["A","B"]);   // Here I am removing A,B Options from optionset field ()
}

Thursday, 19 July 2018

Filter Activities based on users

Filter Activities based on User


                                Recently I got a requirement to filter emails in activities tab based on users. By default in Activities pane, it will show all emails regarding the current record. So there is no OOB functionality to achieve. And unfortunately, apply a custom filter on Activities pane using JavaScript also not possible as it's not a grid. So we need to register a Plugin on Retrieve Multiple.

How Query looks like?
                                
                              Here in Activities, we only will get Activity related information.so we need to Link related entity of ActivityParty. In this entity, we will get only two fields which are Participation Type and Party. Here Participation Type can be From/To/CC/BCC. This will be based on your requirement. Party is the MultiLookup which you can choose All Entity Records which entities allowing Emails in Communication & Collaboration section while creating an entity. This Acts as Activity Party.

                                By default, CRM will have a Plugin Step Retrieve Multiple. So either we can append our custom query or we can completely remove existing CRM query and add our custom query. But appending our custom query will be the best practice.

Here is the Query

QueryExpression qe = (QueryExpression)Context.InputParameters["Query"];
                    LinkEntity linkEntity = new LinkEntity("activitypointer", "activityparty", "activityid", "activityid", JoinOperator.Inner);

                    linkEntity.LinkCriteria.AddCondition(new ConditionExpression("partyid", ConditionOperator.Equal, Context.InitiatingUserId));

                    FilterExpression filter1 = new FilterExpression(LogicalOperator.Or);
                    //filter1.Conditions.Add(
                    filter1.Conditions.Add(new ConditionExpression("participationtypemask", ConditionOperator.Equal, 2));
                    filter1.Conditions.Add(new ConditionExpression("participationtypemask", ConditionOperator.Equal, 3));
                    filter1.Conditions.Add(new ConditionExpression("participationtypemask", ConditionOperator.Equal, 4));
                    linkEntity.LinkCriteria.AddFilter(filter1);
                    qe.LinkEntities.Add(linkEntity);
                    Context.InputParameters["Query"] = qe;
                   
As explained, the above query will retrieve activities where Participation Type will be either To/CC/BCC. I am showing emails to the user whether he involves any of the above participation types. And Party should be User and who is calling this Plugin. So who is new to CRM should care that while registering your step you need to select Run in Users context as Calling User.


Here is the full code:

public void Execute(IServiceProvider serviceProvider)
        {
 
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
            QueryExpression qe = (QueryExpression)Context.InputParameters["Query"];
            AppendLog("Activity.Retrieve Multiple-Started");
            if (qe.EntityName == "activitypointer"&& Context.MessageName == "RetrieveMultiple")
            {
                FilterActivities(userService);
               
            }
            AppendLog("Activity.Retrieve Multiple-Finished");
        }


public void FilterActivities(IOrganizationService service ,IPluginExecutionContext Context)
        {
            try
            {
               
                if (Context.InputParameters["Query"] is QueryExpression)
                {

                    QueryExpression qe = (QueryExpression)Context.InputParameters["Query"];
                    LinkEntity linkEntity = new LinkEntity("activitypointer", "activityparty", "activityid", "activityid", JoinOperator.Inner);

                    linkEntity.LinkCriteria.AddCondition(new ConditionExpression("partyid", ConditionOperator.Equal, Context.InitiatingUserId));

                    FilterExpression filter1 = new FilterExpression(LogicalOperator.Or);
                    //filter1.Conditions.Add(
                    filter1.Conditions.Add(new ConditionExpression("participationtypemask", ConditionOperator.Equal, 2));
                    filter1.Conditions.Add(new ConditionExpression("participationtypemask", ConditionOperator.Equal, 3));
                    filter1.Conditions.Add(new ConditionExpression("participationtypemask", ConditionOperator.Equal, 4));
                    linkEntity.LinkCriteria.AddFilter(filter1);
                    qe.LinkEntities.Add(linkEntity);
                    Context.InputParameters["Query"] = qe;
                    return;
                }
            }
            catch (Exception ex)
            {
               
                throw;
            }
        }

Plugin step Details
Message Name: Retrieve Multiple
Event Pipe Line Stage: Pre-Operation
Primary Entity: Activity Pointer

Why Pre Create?

As I already mentioned, for every entity CRM will have to Retrieve Multiple Step and whenever we want to use advanced find then based on the query we passed, it will call the Plugin Step with the provided query. So before the operation of Plugin Step completed, we need to inject our query also. So this can be possible before the operation. So we need to register on Pre-Operation.

                Once we registered this Plugin and open any record which is having Activities Pane, you can see his related Activities only.


Hope it Helps!

Wednesday, 21 February 2018

Console Application Service for Latest Versions

Retrieve service using the console in Dynamics 365 V9.0



While Retrieving service we will be getting an error in Dynamics 365 V9.0. To avoid that error, we need to add one additional line to our code for the Retrieve Service.

But What can be the issue?
Microsoft Introduced Transport Security Layer 1.2 (TSL 1.2) for SDK assemblies. So an updated version of CRM we are having TSL 1.2

.net Framework Version 4.6 and above will support TSL by default. While coming to lower FW versions we are getting connection issues.

What can be the solution?

Add the following line of code while retrieving service:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

And followed by the complete code here:
  public static IOrganizationService RetrieveService(Uri organizationURL, string userName, string password)
 {
   var url = organizationURL;
   ClientCredentials cre = new ClientCredentials();
   cre.UserName.UserName = userName;
   cre.UserName.Password = password;
   ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
   using (OrganizationServiceProxy proxy = new OrganizationServiceProxy(url, null, cre, null))
      {
         proxy.EnableProxyTypes();
         IOrganizationService service = (IOrganizationService)proxy;
         return service;
       }
   }

Thursday, 20 July 2017

Sharing and unSharing records using custom workflow

Sharing and Revoke Records to Team using Custom Workflow


Hi all,

Recently I got a requirement for Sharing records using Workflow. As of now I never tried sharing records. By default, we don't have any OOB functionality for sharing records. We have the only option for Assigning records. 

So I decided to create a custom workflow. Here I am pasting my code.

using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;

namespace SharingAccountRecordstoTeam
{
    public class Class1 : CodeActivity
    {
        protected override void Execute(CodeActivityContext executionContext)
        {
            //Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
            Entity account = (Entity)context.InputParameters["Target"];

            // Create the request object and set the target and principal access
            GrantAccessRequest grantRequest = new GrantAccessRequest()
            {
                Target = new EntityReference("account", account.Id),
                PrincipalAccess = new PrincipalAccess()
                {
                    Principal = new EntityReference("team", Team.Get<EntityReference>(executionContext).Id),
                    AccessMask = AccessRights.WriteAccess | AccessRights.ReadAccess | AccessRights.ShareAccess                                     // Mention here what Access you want to provide.
                }
            };

            // Execute the request.
            GrantAccessResponse granted = (GrantAccessResponse)service.Execute(grantRequest);

// Revoke The shared recorde from Team
 RevokeAccessRequest revokeRequest = new RevokeAccessRequest()
            {
                Target = new EntityReference("account", account.Id),
                Revokee = new EntityReference("team", Team.Get<EntityReference>(executionContext).Id),
            };

            // Execute the request.
            RevokeAccessResponse revoked = (RevokeAccessResponse)service.Execute(revokeRequest);
      }
        [Input("Team")]
        [ReferenceTarget("team")]
        public InArgument<EntityReference> Team { get; set; }
    }
}


This is the code for Sharing and revoking records to Team. Once Completed the code, then register your workflow to Plugin Registration Tool. then your workflow will be available in processes. Just add your condition and after that call this Workflow.

Note:   Pass Team Id as a parameter.

This is for the new users who want to create a custom workflow.

Hope it will help you.

Thanks,
Shivaram.


Sunday, 12 February 2017

Hide +New button in Subgrid programmatically.

Hide +New button in Subgrid programmatically.


Hi all,

             Recently I got one requirement that Hide +New button from subgrid based on Condition. By default we can hide button using Right Click --> Hide button. But based on condition if we want to hide then we need to write our custom function. 

            Here I am posting this for how to achieve this. For achieve this, we should have Ribbon Workbench Solution. you can download that using following link


or you can down load latest XRM tool which is providing Ribbon workbench Plugin directly. 


Now Create one web Resource Javascript in your CRM solution.



In this scenario, I am hiding New Button, if Form is having value for Name field. If Name field is not having value then New button field should be visible. For that I wrote following code.

function RemoveNewButton(){
// Checking Name field is having Value or not
if(Xrm.Page.getAttribute("rbn_name").getValue()!=null)
           return false;
else
          return true;
}

If you want to Hide/Show button based on user role, first get the user role using following code.

function CheckUserRole() {
    var currentUserRoles = Xrm.Page.context.getUserRoles();
    for (var i = 0; i < currentUserRoles.length; i++) {
         var userRoleId = currentUserRoles[i];
    var userRoleName = GetRoleName(userRoleId);
        if (userRoleName == "System Administrator") {
            return true;
        }
    }
    return false;
}
//Get Rolename based on RoleId
function GetRoleName(roleId) {
    //var serverUrl = Xrm.Page.context.getServerUrl();
    var serverUrl = location.protocol + "//" + location.host + "/" + Xrm.Page.context.getOrgUniqueName();
    var odataSelect = serverUrl + "/XRMServices/2011/OrganizationData.svc" + "/" + "RoleSet?$filter=RoleId eq guid'" + roleId + "'";
    var roleName = null;
    $.ajax(
        {
            type: "GET",
            async: false,
            contentType: "application/json; charset=utf-8",
            datatype: "json",
            url: odataSelect,
            beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
            success: function (data, textStatus, XmlHttpRequest) {
                roleName = data.d.results[0].Name;
            },
            error: function (XmlHttpRequest, textStatus, errorThrown) {
alert('OData Select Failed: ' + textStatus + errorThrown + odataSelect);
}
        }
    );
    return roleName;
}


After adding this Javascript to your web resource, then open your ribbon workbench and Import which solution you are working on. Now select which entity Form deactivate button you need to hide.Here I want to hide Contact Entity Deactivate Button.


Now click on Customize Command button in above options. (Above Image Accidentally I highlighted Customize Button please observe)

Then Add New Enable rule. 

Using following Steps.

Right Click -> Add New-> right side you can see Steps Lookup -> Click on that -> Add New.
Observe following images







Then you will get following options



Fill all those details like above and then click Ok.

Now you successfully created Enable Rule.

Now go to Commands section and click On deactivate command. And select Edit Enable Rule. like following


Then You will get following popup


Click Ok and Publish your customizations. Then Deactivate button will visible only for System Admin.

But I will suggest you one thing in your Js code. Don't use hardcoded GUID Value in code. Because it might be vary from one environment to another Environment (Dev to Test)

Hope it helps you.

Thanks.