Gotcha! Does Library name really matter while registering event handler in Dynamics 365

Registering an event handler for your form events or field events – Probably the most mundane stuffs that you would perform everyday if you a CRM consultant. But sometimes the simplest things have the greatest mysteries to unfold.

Now here I was in a training session emphasizing the importance of namespaces while you write your JavaScript files. And I was explaining how can putting the same method name in multiple files can actually lead to different event handler being called for your event instead of the desired event handler registered.

And then I get this question – “How is that possible? After all we specify the library name while specifying the event handler. Isn’t it?” Well the participants were pretty experienced in CRM and honestly I was also into split thought after I heard.

image

After all from the basic concept of the Javascript I know,  the first function which matches the name will be called. The best way to know something is trying and that’s what we did.

So I created two files here

file1.js with the below content

// JavaScript source code
function formLoad() {
    var alertStrings = { text: "Alert from file1.js", title: "Alert" };
    var alertOptions = { height: 200, width: 300 };

    Xrm.Navigation.openAlertDialog(alertStrings, alertOptions);
}

file2.js with the below content

// JavaScript source code
function HelloWorld() {
    var alertStrings = { text: "Alert from file2.js", title: "Alert" };
    var alertOptions = { height: 200, width: 300 };

    Xrm.Navigation.openAlertDialog(alertString, alertOptions);
}

Now comes the important part.

I go ahead to the form properties of the Lead form and register the below event handler.

image

Observe here carefully. I have put the file name to new_/scripts/file2.js and the function name I have put is “formLoadwhich is actually not there in the file2.js at all.  Also I have included the both the files in the form libraries as shown in the below screenshot.

image

Now when I open a lead record – Gotcha! I can see the alert

image

Even though I was handling participants who were pretty much experienced but believe me I could literally find an absolute taken aback expression once the alert dialog popped up. In the world of Power Apps and Azure, sometimes the tiniest and mundane stuffs actually leaves us in awe.

So this is one stuff. But what we get from it. Well now you understand that if we have the same method name in both file1 and file2 and you registered an onload event for the method in file1, there is no guarantee which method of which file gets called.

And that’s the whole I repeatedly stress on the use of namespaces while writing JavaScript files.

-Debajit Dutta

(Dynamics MVP)

For consultation/ training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Advertisements

Get Api Version Dynamically For WebApi Requests

Loads of my blog readers wanted to know..here is the perfect link to it.

Passion Dynamics

In this post I will quickly cover the requirement of dynamically getting API version of dynamics CRM in JavaScript rather then hard coding it , so that in next releases your JavaScript doesn’t breaks. I am referring to this:

1

For Dynamics CRM version 8.2 or below, do this:

var apiVersion = Xrm.Page.context.getVersion();

For Dynamics CRM version 9 or above, do this:

var apiVersion = Xrm.Utility.getGlobalContext().getVersion();

2

It will give you an expended version which includes minor and major versions. Now, all you have to is crop the number to get only initial number such as 9.1 in this case:

var shortVersion= apiVersion.substring(3, myStr.indexOf(“.”) -1);

then finally use it in your request as:

    req.open("GET",Xrm.Utility.getGlobalContext().getClientUrl()+ "/api/data/"+shortVersion+"/systemusers

Hope this helps!

View original post

{knowhow} Invoking an action from with EntityCollection Input parameter using WebApi in Dynamics 365

Xrm.WebApi methods introduced by Microsoft are great! But not much can be said about Xrm.WebApi.execute method which is used to invoke custom actions from client side code.

To be honest, ever since I have written blogs on Xrm.WebApi.execute like this one- https://debajmecrm.com/2018/07/20/calling-bound-actions-entity-actions-using-xrm-webapi-execute-in-dynamics-v9/, I have been getting huge number of questions on how this topic.

And one of the question that is coming time and again is – How do I pass an input parameter of type EntityCollection. I was baffled a bit as to why this question has popped up time and again but when I really go and search the docs and community, I really find very little information on the same and this answers my curiosity. Well, I have decided to pen this down. Actually it’s pretty simple but in case you don’t know, it can cost you days.

So I created an Action with two input parameters – one with Type entity reference and the other of type Entity Collection. Screenshot for reference.

image

The entity reference is of type Account as well. And in the entity collection also we are going to pass a collection of accounts.

So here is the call to the action which would work just fine.

var request = {};
request.entity = { entityType: "account", id: "475B158C-541C-E511-80D3-3863BB347BA8" };
request.EntRef = { "@account.id": "475B158C-541C-E511-80D3-3863BB347BA7" };
request.EntColl = [
    {
        "@account.id": "475B158C-541C-E511-80D3-3863BB347BA6", "name": "acc1", "@odata.type": "Microsoft.Dynamics.CRM.account"
    }
    ,
    {
        "@account.id": "475B158C-541C-E511-80D3-3863BB347BA5", "name": "acc2", "@odata.type": "Microsoft.Dynamics.CRM.account"
    }

];
request.getMetadata = function () {
    return {
        boundParameter: "entity",
        operationType: 0,
        operationName: "new_ActionProcess",
        parameterTypes: {
            "entity": { typeName: "mscrm.account", structuralProperty: 5 },
            "EntRef": {
                typeName: "mscrm.account",
                 structuralProperty: 5
            },

            "EntColl": {
                typeName: "Collection(mscrm.crmbaseentity)",
                structuralProperty: 4
            }

        }
     };
};

Xrm.WebApi.execute(request).then(
    function (result) {
        debugger;
    },
    function (error) {
    }
);

Highlighted the entity collection areas for easy readability.So as you can see, you need to pass the typeName as “Collection(mscrm.baseentity)”.

This is what we need to do. But the question is how did I find that I need to put this as the type name. After all this name is not something which can come out of intuition.

My suggestion here is, whenever you get stuck with names, fallback to the metadata.

I go to Settings –> Customizations – Developer Resources and download the OData Metadata. Once downloaded when I search for the action name this is what I find.

image

After that it’s just piece of cake.

Hope this helps and saves you some precious time.

-Debajit Dutta

(Dynamics MVP)

For consultation/ training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

{knowhow} Sequencing Xrm.WebApi methods in Dynamics 365 leveraging JavaScript promise.

Come version 9.0 of Dynamics, Microsoft have introduced the Xrm.WebApi methods which have significantly eased out the task of making web api calls from the client side. However as we are all using this wonderful feature, many of us are unaware of the fact that these API’s are based on Javascript promises.

This article is not to explain what is JavaScript promise. Sharing this wonderful link in case you are unaware – http://www.javascriptkit.com/javatutors/javascriptpromises.shtml.

A great article to get started indeed.

OK. So let’s assume you are already aware of JavaScript promises. Let’s take a sample method to retrieve a record. Below is the Microsoft documentation for retrieving a record.

Xrm.WebApi.retrieveRecord(entityLogicalName, id, options).then(successCallback, errorCallback);

Concentrating on the highlighted part, the first question is – Is it absolutely necessary to define the then part of the API syntax? Can we just call Xrm.WebApi.retrieveRecord(entLogicalName, id, options) without the then part of it?

The answer is – It’s perfectly alright to call. the successCallback and errorCallback are just the event handlers when the promise is fulfilled or rejected.

Now the next part comes is – If it uses javascript promise, then it must allow sequencing. Surprisingly I have seen many implementations of Xrm.WebApi so far but I don’t see much of chaining example. Well, it can be done.

Let’s understand a scenario here. We perform a retrieve on accounts based on some query and then use the accounts to create a contact and set the parentcustomerid field to each accounts retrieved during the retrieve multiple call.

Xrm.WebApi.retrieveMultipleRecords("account", "?$select=name&$filter=accountcategorycode eq null").then(
    function success(result) {
         var ids = [];
        for (var i = 0; i < result.entities.length; i++) {
            var ent = result.entities[i];
             var accountId = ent.accountid;
            ids.push(accountId);
        }

        // pass the id’s to the next sequence
        return ids;
    },
    function (error) {
        console.log(error.message);
        // handle error conditions
    }
).
then(function (ids) {
    for (var x = 0; x < ids.length; x++) {
        var contact = {};
        contact["firstname"] = "sample";
        contact["lastname"] = "contact" + (x + 1);
        contact["parentcustomerid@odata.bind"] = "/contacts(" + ids[x] + ")";

        Xrm.WebApi.createRecord("contact", contact);
    }
});

Just focus on the highlighted part in green. As you can see here, in the successCallback of the retrieveMultiple, we first create an array of account id’s and then use the next sequence highlighted in yellow to process the Id’s and create a contact for each account retrieved. Also observe the createRecord call without the then part of it.

In this way you can chain methods as much as you want. Much better way to write than the nested callbacks.  

Hope this helps!

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit
http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

{knowhow} How to edit the Dynamics 365 App for Outlook screen

Recently there was a requirement to change the Dynamics 365 App for Outlook  screen. Basically when you set up the default App for Outlook, the Sitemap contains just the Activities and the Dashboards.

image

However the customer wanted to show the Accounts and some other custom entities there as well in the menu. So how do we include it?

It’s quite obvious that the change needs to be in the SiteMap. However how can I change the Sitemap of the App for Outlook screen? Strangely enough that’s one unexplored area and surprisingly even consultants working day in day out on CRM tends to miss out on this.

To modify the app UI, perform the below steps.

Open up your solution that you are working on and go to Model driven apps.

image

Add the Dynamics 365 App for Outlook app from the list of Apps.

image

Now it’s just like modifying any other app. I just quick edit the sitemap to include the account and then publish it.

Re-open the outlook and you could see your changes in there.

image

Easy wasn’t it?

Hope this helps.

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit
http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

Create and expose portal content easily in Dynamics 365 Portals using Portal Management feature

The true potential of a portal is realized only when we expose the Dynamics 365 content on the portals. However as we all know, to expose the content end-end, we need to create the following

  • Entity List
  • Entity Form
  • Web  Page for exposing both the entity list and the entity form
  • Separate entity forms for create and update.

Well things like this are really easy for anybody who are hands on in the portal development. However they take some time to spin up. And specially when you have some entities to set-up in one go.

Well no more worries. The new version of portals comes with the Portal Management editor that helps you do all these within minutes.

Open your CRM and go to Portals –> Administration –> Portal Management

image

Next click on “Create Portal Content”. Page like the one below would pop-up initially.

image

Make the “Display organization entity in the portal” to Yes.

image

The rest is very simple. You get to select as many views as you want just like you get when you configure an entity list. In the below screenshot i have put “Allow Record Creation” to Yes as well.

image

That’s all. Now all we need to do is expose the “cases” page in the navigation.And then you see something like this.

image

As you can see, the Create button is there and the Case title is clickable so that it redirects you to the case detail form.

Hope this helps!

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit
http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

Getting Form Context from Sub-grid ribbon button in Dynamics V9.0 Gotchas!

After my last blog on getting the formContext and the client URL from a ribbon button on the form in Dynamics Version 9.0, I was simply taken back by the comments and was truly satisfied knowing that it had helped so many blog readers.

https://debajmecrm.com/2018/11/16/xrm-page-is-deprecated-with-version-9-0-how-do-i-get-the-clienturl-and-form-data-in-ribbon-actions/

However one of the questions was on how to get the formContext when a sub-grid button was clicked. So basically the requirement here is when the sub-grid button is clicked, the UI elements on the form needs to be accessed like the tabs, fields, sections, formSelector etc

And now with V9.0, since Xrm.Page has been deprecated, we need to use the formContext to access and any form-related values.

So I decided to give this a try.

I created a custom ribbon button on the account sub-grid and then on the click of the function, I added couple of CrmParameters

<Actions>
              < JavaScriptFunction FunctionName="subgridEvent" Library="$webresource:new_contactformload.js">
                <CrmParameter Value="PrimaryControl" />
                < CrmParameter Value="SelectedControl" />

              </JavaScriptFunction>

</Actions>

One is PrimaryControl and the other is SelectedControl. Well, PrimaryControl was very helpful last time for a form button, since it gave me the much desired FormContext with which I was able to play with the entire form.

Somehow I was reluctant to use the PrimaryControl still I included it.

SelectedControl is very much necessary because I know it gives me the Grid Context which would help me to play with the Grid. And I was pretty sure that it would give me something to access the FormContext.

All set and good. I clicked on the Test Button in the Account sub-grid.

image

While debugging, I got my two parameters – PrimaryControl and SelectedControl.

As I expected PrimaryControl was useless and SelectedControl gave me the Grid Context.

image

I kept on searching for something in the SelectedControl and to  my horror nothing was there to get the FormContext.

I stated thinking otherwise. How about storing the formContext in a global variable when the form loads? Well that does not work either. When you move to associated view, your form refreshes and unfortunately you won’t find that script global variable in there when the sub-grid button is clicked.

So is there no other way? Searched the heck out of google and could not find a permanent solution. Searched in docs. Still no luck.

So back to where I started. No supported way I realized or may be I could not find?

I started digging now into the parameter – PrimaryControl and found a method – get_crmFormControl(). And finally from this I was able to get the formContext.

Code below.

function subgridEvent(primaryControl, selectedControl) {
    var formContext = primaryControl.a.get_crmFormControl().getFormContext();
    var grid = selectedControl.getGrid();

    // interact with the form using formContext now
}

Please note that this is not a documented approach and hence unsupported. However I could not find anything else from this and no where this scenario is mentioned as well.

Would really like to hear if you get a solution working which is supported.

Hope this helps!

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit
http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

Firing on-load event every time when record is saved. Exploring the data onload event in Dynamics v9.0

Recently I was working with a customer and came across a requirement where they needed to fire an event on client side every time once the data is saved successfully to process some post-save operations.

As we all know from version CRM 2013 and onwards the formload event does not fire when save is completed. So what is the other way around?

Well there is way to do that. We need to use the formContext.data.addOnLoad to add an event which fires every time when the data is refreshed.

The below code adds the onLoad event of data refresh in the form load event of the account entity.

function formLoad(e) {
    var formContext = e.getFormContext();

    formContext.data.addOnLoad(dataOnLoad);

    console.log("form load called.");
}

function dataOnLoad(e) {
    // write your code here.
    console.log("data onload fired");
}

formLoad is the event which is registered through Form events. In the formload we are adding the event for data refresh using the formContext.data.addOnLoad method.

The function dataOnLaod fires on the initial page load as well as every time the user clicks on save. And now you have a way to perform your post-save operations in client side whenever the record is saved.

Also please note that the function also fires if formContext.data.refresh() function is called.

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit
http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

Getting logged in user roles in client side in Dynamics V9.0

You may be asking? Why this mundane post? After all we have been here close to 8 years since 2011 released and we have millions of time retrieved it using Xrm.Page.context.getUserRoles().

So what’s the fuss in it?

So if you are working on CRM version below 9.0, then it’s no fuss. But if you are working on V9.0 and above, may be this is an interesting read for you.

Xrm.Page is deprecated. So you should no longer be using Xrm.Page.context.getUserRoles()

So what’s the other way. Well you need to do it the V9.0 way. Below is the  code to do the same.

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

And the userRoles variable here contains the list of GUID’s of the roles the user have. Wasn’t that easy?

Also now with the unified interface coming in, you should be very careful about every piece of Javascript you write. There are some properties which works only in UCI and some in Web

For e.g – not only you can get the security roles but also the privilege id’s for all the security roles associated with the user.

var userRolesPrivileges = Xrm.Utility.getGlobalContext().userSettings.securityRolePrivileges;

Unfortunately, the above line only works in UCI and not on the web version.

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit http://www.xrmforyou.com or reach out to us at info@xrmforyou.com

{Solved} Call Web API from within a plugin in Dynamics 365 online

Well, this is one question I cam across countless number of times. And in a recently conducted training boot camp, i was asked the same question. To be honest after understanding OAuth and how various types of grants work in Azure Active Directory, I was thinking this should not be a very tough problem.

To my surprise, when I started searching the community I came across many links which suggested it can’t be done and many links which suggested ways but no complete guidance on how to do it.

So decided to challenge myself in it for my blog readers and came out pretty quick within couple of hours. So here we start out journey.

Before we go ahead and discuss on how to do this, let’s understand the problem faced.

The first thing that we need to connect to Web API instance is an access token. And getting an access token now is just piece of cake. You reference Active Directory Authentication Library (you can easily get this from Nuget with the keyword : ADAL) in your project and then use some 4-5 pre-defined lines to get the token.

So what’s the problem with that? The problem is when we try to run the same code in our plugins and try to access the token, we get a security exception. The problem is our online plugins runs in sandbox mode.

So the solution to this would be not to use ADAL. So what are the other options? Off-course we can use plain simple HttpRequest to get the token right? After all internally ADAL might be doing the same right? Not sure about ADAL but certainly this is harder than you think.

The first point to consider here is when the plugin is executing, we do not have the password for the user.

In my blog link – https://debajmecrm.com/2018/04/30/headless-authentication-with-dynamics-crm-online-and-external-web-app-which-requires-client-secret/ I have explained in detail on how to get the access token without using ADAL. The problem here is, I am  using the password here to get the token. In plugins I will not have that.

So how to tackle this.

If you observe carefully, there are various types of grants in Oauth and one of them “grant_type=password” I have used in the above link. Well since I can’t use that, there is another type called grant_type=client_credentials”. And this is what we are going to use here.

So let’s now jump to the solution.

Step 1:

Register an application of type WebApp/Api in your Azure active directory with proper permission to Dynamics Application and then create an application user in Dynamics with the Application ID of your registered App. Give access to your application user with proper security roles to gain necessary access to entities.

If you are not aware of registering App or the concept of Application User in Dynamics, it’s explained in great detail in the below link

https://debajmecrm.com/2018/08/16/step-by-step-guide-query-dynamics-crm-web-api-using-server-to-server-authentication-with-application-user/

Just set-up the APP and the application user. Don’t try to use the code since it will not work in plugins.

Step 2:

Plugin code

public class WebApiSample : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            // getting the pipeline context.
             var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var ent = (Entity)context.InputParameters["Target"];

            var accessToken = GetTokenWithoutADAL().GetAwaiter().GetResult();
             var accountJson = RetrieveAccounts(accessToken).GetAwaiter().GetResult();

            ent["vvbv_description"] = accountJson;
        }

        private async Task<string> GetTokenWithoutADAL()
        {
            string azureAdDomain = "xrmforyou62.onmicrosoft.com";
            string loginUrl = $"
https://login.microsoftonline.com/{azureAdDomain}/oauth2/token";
             string resource = "https://xrmforyou62.crm.dynamics.com";
              string clientId = "9a3590d8-c89e-4de5-bb50-8dfa3d9796a9";
            string clientSecret = "<put your client secret here>";

            HttpClient client = new HttpClient();
            var postData = $"client_id={clientId}&client_secret={clientSecret}&resource={resource}&grant_type=client_credentials";

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, loginUrl);
            request.Content = new StringContent(postData, Encoding.UTF8);
            request.Content.Headers.Remove("Content-Type");
            request.Content.Headers.TryAddWithoutValidation("Content-Type", $"application/x-www-form-urlencoded");

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var responseMessage = await client.SendAsync(request);
            var jsonResponseString = await responseMessage.Content.ReadAsStringAsync();

           var jsonContent = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonResponseString);

            return jsonContent["access_token"];
        }

        private async Task<string> RetrieveAccounts(string token)
        {
             string webApiUrl = "
https://xrmforyou62.api.crm.dynamics.com/api/data/v9.1";
             string url = $"{webApiUrl}/accounts?$select=name";

            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
           
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            HttpResponseMessage response = await client.GetAsync(url);

            string jsonContent = await response.Content.ReadAsStringAsync();

            return jsonContent;
        }
    }

You can do a better job at handling the client_id and client_secret. But this is just sample code and hence I am using this directly.

Also to parse the JSON, I have used the Newtonsoft.json dll which you need to ilmerge with the plugin assembly before uploading to the plugin registration tool. Check for the highlighted code.

And I have registered this plugin on the Pre-create of a test entity. I set the description of the field in this plugin to the account JSON and voila it works!

image

Eureka! I can see the account JSON in the description field after I create the record.

Isn’t it great guys.

Hope this helps. And hope you just had something interesting to read in CRM world today.

Debajit Dutta

(Dynamics MVP)

For consultation/ training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com