Retrieve contents of File Data Type field in Dynamics 365/ CDS using JavaScript/ client code.

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

Few months back I wrote an article on File Data type in Dynamics 365/ CDS where I explained the details of this data type and explained how you download the content of the field using C# code.

After that I received loads of request on how I can do the same using JavaScript. And here is this blog where I show in detail on how you can do that.

For this demo, I have created a file data type field on contact entity – File Content (cr947_filecontent)

image

Then I go ahead an upload a file on one contact record.

image

Ok now the tough part. How to download this file content from your client script if required? Well, let’s see how you can do this.

First things first. Below is the URL Format to download the file content for a file field for a contact record.

https://<crmurl>/api/data/v9.1/contacts(<contact_id>)/cr947_filecontent?size=full – This will download the content in base64 format string.

https://<crmurl>/api/data/v9.1/contacts(<contact_id>)/cr947_filecontent/$value?size=full – This will download the content as byte array.

If your file size is less than 16 MB, then depending on how you want to download the file, you can use the appropriate URL.

However if your filesize is more than 16MB, it wont’ work. This is because you if you use the same URL to download the content for a filesize more than 16MB, you will get an error – “Maximum file size supported for download is [16] MB. File of [17 MB] size may only be downloaded using staged chunk download.”

So let’s see how we can have a common function to handle all these scenarios. Below is the code for the same. I have decided to go ahead with the base64 content.

Also if you observe here, I am downloading the file content in chunk of 4MB. Also observe I am using the “Range” header to set the bytes count that I want to download.

function makeRequest(method, url, startBytes, increment) {
    return new Promise(function (resolve, reject) {
       var request = new XMLHttpRequest();
       request.open(method, url);
       request.setRequestHeader("Range", "bytes=" + startBytes + "-" + (startBytes + increment - 1));
       request.onload = resolve;
       request.onerror = reject;
       request.send();
    });
}

async function PrepareFile() {
    var startBytes = 0;
    var increment = 4194304;
    var url = "https://<crmurl>/api/data/v9.1/contacts(8da6e5b9-88df-e311-b8e5-6c3be5a8b200)/<field_name>?size=full";
    var finalContent = "";
    var fileSize = 0;
    var fileName = '';

   while (startBytes <= fileSize) {
       debugger;
       var result = await makeRequest("GET", url, startBytes, increment);
       var req = result.target;

      if (req.status === 206) {
          finalContent += JSON.parse(req.responseText).value;
          startBytes += increment;

         if (fileSize === 0) {
             fileSize = req.getResponseHeader("x-ms-file-size");
             fileName = req.getResponseHeader("x-ms-file-name");
          }
       }
    }
}

PrepareFile();

I am calling PrepareFile() method which is doing the loop through. “finalcontent” is the variable which will have the entire base64 content.

How to use the base64 content and download it through browser? I am leaving that for a homework.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Custom integration between Dynamics 365/CDS and SharePoint using C# and SharePoint REST API ? Learn how to create a SharePoint Add-in and generate authentication token–Part 2

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

If you are directly on this blog, I suggest you go through the first blog of this series.

In our previous blog, we created the SharePoint Add-in. In this blog we shall be providing this APP permissions on sitecollection.

To apply permissions to the generated Add-In either you can visit SharePoint Tenant Administration Site or via AppInv.aspx page

Visit SharePoint Admin center to do this. Go to <admin_site_url>/_layouts/15/appinv.aspx

image

Use the App Id that you created as per directions in previous blog and click on Lookup button to retrieve the APP Details.

image

The important thing is the App’s permission.

If you want to provide full control at the tenant level use the following permission XML

<AppPermissionRequests AllowAppOnlyPolicy="true">

<AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />

</AppPermissionRequests>

image

Once you click Create you will be presented with permission dialog. Click Trust It to grant permissions

clip_image002

Now your Add-In is ready for use. As we granted tenant-level permissions you might be wondering if I need do the same for specific site collection. No worries this can he handled as well. Create Add-In app in your Site Collection using Blog Post 1 and follow the similar approach but modify the permissions XML as below:

<AppPermissionRequests AllowAppOnlyPolicy="true">

<AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl"/>

<AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="FullControl"/>

</AppPermissionRequests>

In this way we create Add-Ins and grant permissions to Add-In. In Part-3 of my blog I will show you how to generate token from Add-in.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Custom integration between Dynamics 365/CDS and SharePoint using C# and SharePoint REST API ? Learn how to create a SharePoint Add-in and generate authentication token–Part 1

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

Before I go ahead, while this blog depicts on how you can generate the Authentication token to integrate between Dynamics 365 and SharePoint, this can be applied to any application trying to authenticate with SharePoint online to consume SP REST API’s. The methodology presented here can be applied for all applications.

Your day as a consultant can throw a lot of surprises and this was one of them. We had a custom integration between Dynamics 365 online and SharePoint. online And the connection was being completed using username and password authentication by consuming Sharepoint REST API services.

And one fine day this stopped working. There were some access policy changes on Azure and now the code was not able to generate a request digest using username-password credentials. It was always coming as null and authentication was failing. Moreover client was also not okay to use a service or end-user account for authentication.

If you are on the same boat, don’t be worried. This blog is explicitly on how you can handle this scenario. And that is using SharePoint add-in.

There are multiple ways to register SharePoint add-in such as using Visual Studio, the Seller Dashboard, or an AppRegNew.aspx page. In this blog post we will register add-in using AppRegNew.aspx in tenant which is one of the recommended approaches.

To do this we need global administrator privileges which is SharePoint Administrator role in SharePoint site. Let us now try to register your add-in in the following steps:

Step 1: Go to <site-colleciton-url>/_layouts/15/AppRegNew.aspx using browser.

clip_image002

Step 2: Fill up the details to create add-in

 

· Client Id – Add in ID basically a Guid that can be generated (Generated button is used to generate Guid) or paste Guid into AppRegNew.aspx

· Client Secret – Add in Secret basically a string that can be generated using Generated button. Usually the secret expires in one year.

· Title – Name of the add in

· App Domain – The host name of the remote component of the SharePoint Add-in. he add-in domain must match the URL bindings you use for your web application. Do not include protocol (“https:”) or “/” characters in this value. If your web application host is using a DNS CNAME alias, use the alias. Examples: www.localhost.com, www.contoso.com:3333 and www.scholarship.com

· Redirect Uri –The endpoint in remote application. The value must be a complete endpoint URL including the protocol, which must be HTTPS. For example: https://www.localhost.com, https://www.contoso.com/Default.aspx

Step 3: Provide all the details and click Create.

clip_image004
clip_image005

Now that we have created the add-in, it’s now time for providing the app the required permissions to your sitecollection. In the next blog will shall be doing the same. Make a copy of your application details that you just created. We shall need it in the next blog.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Using EntityCollection as Output parameter of Dynamics 365 Action while calling Action from Microsoft Power Automate (Flow)

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

In that blog I explicitly mentioned on the problem of using EntityCollection as output parameter and suggested a possible solution to that. After multiple request from my blog readers, I am writing this blog on how you can use EntityCollection as output parameter of your action that you are invoking through flow. Well may not be exact EntityCollection output parameter but you can accomplish the same purpose.

So first – What’s all the problem with EntityCollection as output parameter? This is because when you set output parameter of type EntityCollection, the action would need an output of type Array. If you use EntityCollection as output parameter, you would get an error similar to the one below.

The API operation ‘PerformUnboundAction’ requires the property ‘body’ to be of type ‘Array’ but is of type ‘Object’.

Well Entity Collection contains the list of entity but after all in CRM it is not an array whereas in Flow it is expected as array.

So what is the other option. Well there can be many different ways but the easiest is to get the JSON representation of your entity collection and then parse in back in the flow. So without wasting more time, let’s jump inside the flow.

Microsoft Power Automate - YouTube

First of all, I create an action in Dynamics 365 and use and Output parameter named – EntCollString of type string. Observe I am not using the parameter of type EntityCollection but string.

image

For this demo, I have written a plugin on Post-operation of this plugin. Below is plugin sample code.

var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
         var sf = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

  var s = sf.CreateOrganizationService(context.UserId);

        QueryExpression exp = new QueryExpression
         {
            EntityName = "account",
            TopCount = 10,
            ColumnSet = new ColumnSet("name")
         };

        var results = s.RetrieveMultiple(exp);

        var entResults = new List<Tuple<string, Guid>>();
         results.Entities.ToList().ForEach(x =>
         {
            entResults.Add(new Tuple<string, Guid>(x.GetAttributeValue<string>("name"), x.Id));
         });

        var memoryStream = new MemoryStream();
         DataContractJsonSerializer dataContractSerializer = new DataContractJsonSerializer(typeof(List<Tuple<string,Guid>>));
         dataContractSerializer.WriteObject(memoryStream, results.Entities.ToList());
         memoryStream.Position = 0;

        var sr = new StreamReader(memoryStream);
     context.OutputParameters["EntCollString"] = sr.ReadToEnd();

If you observe the code, I am taking a list of 10 account, getting their name and id and then passing it back as the output parameter of the action by serializing them into JSON.

Nothing much in that. Offcourse we have less attributes here but you can apply the same with more attributes.

Now comes the important part here. That is the follow. I invoke the action, I get the data in output collection and then parse the JSON.

image

To parse the JSON, I am using the ParseJSON data operation. For the schema you would need a sample data which you can easily get by doing a test run of your code, getting the JSON result and then generating the schema using “Generate from sample” button.

image

And then iterating through each item of the JSON array. In the code I have used C# Tuples. And hence the property names came as m_item1 and m_Item2. However if you use a strongly typed class, you shall get the names with the exact property names you have specified in the class.

image

And that’s all. Now you have same stuff that would you have done using entitycollection as output parameter. Yes a little bit of workaround but certainly worth it.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Skip Setting a Business process flow during record create in Dynamics 365/ CDS? This simple trick may help you

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

We all love Business Process flows. And why not? Their introduction have solved the age old problem of visualizing a record as and when it progress through multiple stages. Typically lead and opportunity moving through multiple stages, a product moving through multiple stages of development and numerous other examples can be cited.

And yet there can be scenarios where you need to skip applying business process when the record is created. Typical example may be bulk importing records from some other sources where you are not sure about what business process need to be applied and you want the decision to be left to end-users when they work with the respective records. Really there can be scenarios which you never thought.

I've never thought about it like that before | Honest about my faith

In scenarios like this, how do you plan to do the same. After all the default business process flow configured for an entity is automatically applied when you create new record for the entity.

We know for every entity there is a field called ProcessId. You may be wondering, isn’t it deprecated. Yes it is deprecated. However as much surprising it is, the same will come to help in scenarios like this.

All we need to do in this case is set the ProcessId field to Guid.Empty. That’s all.

And how are you going to do it. Well there can be many ways but in our scenario if it is bulk import, we can have a plugin in pre-operation stage where we will set the value of ProcessId to Guid.Empty.

Something like this shall work.

public class SetProcessIdToBlank : IPlugin
    {
       public void Execute(IServiceProvider serviceProvider)
       {
          var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
          var target = (Entity)context.InputParameters["Target"];

         target["processid"] = Guid.Empty;
       }
    }

And now the records shall be created without any Business process flow record.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Stop Assign functionality based on Business Logic using Client API in Dynamics 365

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

Assign – an age old functionality which is there since the time I started working in CRM which dates way back to 2011. So much we have seen about Assign that whenever I talk about Assign it’s like going back to our school days. But sometimes stuff from our school days we miss out which can be quite interesting when we discover it in our later years.

So here was this requirement. When a user clicks on the Assign and try to assign a record to a user or team, there should be some custom business validations based on which we will decide whether to go ahead with the Assign. If not then we will throw a custom message to the user.

So let’s see the tried and tested options and see why they won’t fit here.

Writing a plugin

For me it’s the best solution because it handles server side scenarios as well. However while the plugin shall still be there, the error dialog throw from plugins is not acceptable by the end-users here. They want more of like an alert dialog available in Dynamics 365.

Show hide the Assign button using enable rules

While this works, it is absolutely essential for this validation to be as real time as possible. Also the customer wanted that the end-user should understand why he/ she is not able to assign the record and then take corrective action.

So what’s the other way? We all know that when Assign operation is performed, the record is saved and any save event you have registered fires as well. And here is secret. There is a specific save code for Assign which is 47.

Using the specific save code we can understand whether Assign is currently being executed by the user and take necessary action.

function record_onsave(e) {
    var saveMode = e.getEventArgs().getSaveMode();

   if (saveMode === 47) { // assign
       // perform your custom logic here
       // and show alert using Xrm.Navigation.openAlertDialog if assign is invalid.

      // also you can stop the save using code below.
       e.getEventArgs().preventDefault();
    }
}

Using this simple trick you can actually handle your assign event. One very important point to note here. If you expose the owner field on the form and the end-user updates the owner field, that is not considered as Assign operation and would be treated as regular save (savemode = 1). So your above code shall fail.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Scope of getSharedVariable and setSharedVariable functions in Dynamics 365 Client API

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

These functions have been introduced way back in 2011 but till date I believe this is one of the least used features of Dynamics 365 Client API’s. And partly because of the fact their limited scope when it comes to sharing variables.

I wonder many may have tried it but had to move over to other methods because these methods didn’t meet their needs.

Nonetheless I get this question every time I do a technical training on Dynamics 365 – What are these API’s used for. So I decided to dedicate a blog for this. So let’s start with the Microsoft definition of setSharedVariable function to understand the scope.

Requirements Scope Statements — Business Analyst Learnings

“Sets the value of a variable to be used by a handler after the current handler completes.”

The first impression you get from this definition is – if you have multiple handlers for the onload event of a form and if I set a variable using setSharedVariable method, the other handlers for the same event shall get the value using getSharedVariable function.

function pageLoad(e) {
    console.log("Inside Page Load.");
    e.setSharedVariable("key1", "From pageLoad function.");
    console.log(e.getSharedVariable("key1"));
}

function f1(e) {
    console.log(e.getSharedVariable("key1"));
    e.setSharedVariable("key1", "From f1 function.");
}

function f2(e) {
    console.log(e.getSharedVariable("key1"));
}

So let’s look at the code above. We have three methods – pageLoad, f1 & f2. All these three functions I have registered on the onload event in order. So basically these are three handlers for the same onload event.

image

And then I set a variable using setSharedVariable function.

Now as you can see, I am trying to get the same value in functions f1 & f2.

Let’s see what’s the output.

image

As you can see, the shared variable is available inside the method pageLoad. However it is not available for functions f1 & f2.

So it’s kind of heartbreaking right! So where it is useful and what is the scope of the variable using setSharedvariable function.

I change my pageLoad function to below. As you can see, I am calling an async function to get the record details of an account. By the time, the account details are retrieved, the function have exited. However on-success of retrieve method I am trying to access the value back.

function pageLoad(e) {
    console.log("Inside Page Load.");
    e.setSharedVariable("key1", "From pageLoad function.");

   Xrm.WebApi.retrieveRecord("account", "a8a19cdd-88df-e311-b8e5-6c3be5a8b200", "?$select=name,revenue").then(
       function success(result) {
          console.log("Retrieved values: Name: " + result.name + ", Revenue: " + result.revenue);
          console.log("Printing Shared variable: ", e.getSharedVariable("key1"));

       },
       function (error) {
          console.log(error.message);
          // handle error conditions
       }
    );

}

And here is the output in the console. As you can see, I am still able to access this variable.

image

So if you have some async/ callback functions, these methods will work good for you. However if you trying to persist values across lifetime of the form or even pass data to multiple handlers of the same event, this does not work.

That limits the scope to a great extent. And now I guess you understand why it is used pretty less.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Configure conflict detection for mobile offline synchronization in Dynamics 365

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

In my previous article, I described in detail about Mobile Offline filters. Unlike the previous blog, this one is going to be real short. In this blog I am just going to discuss about a setting for conflict resolution when you have enabled Mobile Offline synchronization for your organization.

If you navigate to Advanced settings –> Mobile Offline, you will find an option – “Mobile Offline Settings”.

Once you click it, it basically opens the System settings –> Mobile client tab. There your have the below option.

image

By default, the value is set to No. So what is this conflict all about? When there is a mismatch of data between client and server, conflict errors occur.This setting help in resolution of these conflicts.

Suppose the user made some changes in offline mode and in the meantime the record has been modified on the server as well. If you set this value as No, the client wins over the server and whenever you go online, the changes made offline shall be synced. Client wins over the server.

If this setting is set to Yes, the server wins over the client and the offline changes are discarded if simultaneously the record the record has been changed on server as well.

Hope this helps!

Debajit Dutta

(Microsoft MVP)

Configuring Mobile Offline profiles in Dynamics 365 for Offline Sync on mobile devices

Follow my blog for more interesting topics on Dynamics 365, Portals and Power Platform. For training and consulting, write to us at info@xrmforyou.com

Trust me we all know about the offline feature of Dynamics 365 Mobile App. And if you ask even experienced Dynamics consultants on how to enable an entity for offline, the answer you get is you need to open the entity customizations and enable the below options.

image

In organization data download filter, you can define the filter criteria for data sync in mobile offline. So for the below filter, only accounts which are active and created in last 20 days shall be downloaded and available for mobile offline.

image

Till this point, almost everyone is aware. What is missed out is the Mobile Offline Profiles. Surprisingly it’s so much less discussed that many are not aware of it. But the fact remains that you need to create mobile offline profiles for users to configure filters that determine how much of an entity’s data (and related entities’ data) will be available to the user while offline.

So let’s see, how you can accomplish the same.

Go to Advanced Settings –> Mobile Offline

image

You may already find some profiles there but let’s start with a simple profile.

I enter a name for the profile – “Sample Offline Profile” and save the record. Here I will only show for account record. However you should do the following steps for all the entities which you want to enable for mobile offline sync.

Click on plus icon in “Mobile Offline Profile Item Details” to create a new Mobile Offline Profile item.

image

Observe I am using “All Records” as option here for data which will be downloaded. However one very important point here – The actual data available for mobile offline sync shall be the intersection of the filters specified in Offline Profile + Filter in organization data filter. If you remember the screenshot above, we have specified the filter there as active accounts created in last 2o days. Hence that shall be respected.

image

You have other options like “Other data filter” and “Custom data filter” using which you can specify custom filter rules.

I save the record. Now the “Mobile Item Profile Offline Association Details” grid becomes activated. In this grid, you can define the related data for account which can be enabled for mobile offline. For example, you want to download the active contact records for each account, we can define that using the below configuration.

image

You can create additional filters on the related records as well.

So we are all set for Account Entity here.

Now the final step is to assign users to this Offline profile. You can add users using the Users grid on the right.

The user “Chandana” is added to the profile. Please note that a user can be added to only one mobile offline profile.

image

And before I end, you remember the Organization data filters that you configured using the entity customization screen. Well, the same is present on the “Mobile Offline Profile Item” record for Account Entity that we just configured.

You can manage the same from here as well. Please note that to remove an entity from Mobile offline capabilities, you need to remove them from Mobile Offline filters where it is being referred.

image

Hope this helps!

Debajit Dutta

(Microsoft MVP)

How to get device location in Dynamics 365 for Mobile

Follow my blog for interesting topics on Dynamics 365, Portals and Power Platform.

The world is into mobile devices and Dynamics 365 is not left far behind. With unified interface, user’s are now getting seamless experience across all devices starting from web browser in desktop clients, to phones and tablets.

Dynamics 365 is well and try mobile now.

Dynamics 365 for phones, Power Apps, and Resco: What are the ...

And when you are in mobile platform, you may feel the need to know device location. And believe me, Microsoft have provided you an API which can give you just that. Following is the code to do that.

Xrm.Device.getCurrentPosition().then(
    function success(location) {
       console.log("Latitude", ":", location.coords.latitude);
       console.log("Longitude:", location.coords.longitude);
    },
    function (error) {
       console.log("error in determining location :", error.message);
    }
);

Please note that the above code will only run on mobile platforms (tablets & Phones). Trying to run it on Web browser in desktop will give you an error – “Operation is not supported by platform.”

Hope this helps!

Debajit Dutta

(Microsoft MVP)