Close Canvas App window using PCF button

Canvas Apps can be launched from within the Dynamics CE using a ribbon button and calling a javascript funtion to open the Canvas App in a new window using window.open.

However, once a Canvas app window is launched, you can’t close the window. Microsoft provides Exit() and Exit(true) functions. Exit() navigates away from the current page and redirects to the list of canvas apps, while Exit(true) signs out the user and navigates to the login page. In either case, the window should be closed manually by the user.

I found a way to close this window using PCF button embedded within a canvas app. This a two step process:

  1. Creating and deploying PCF button control to the Dynamics CE/CDS/Dataverse environment
  2. Adding this PCF button control to the Canvas App
  1. Creating and deploying the PCF button control to the Dynamics CE environment:

You can follow this blog to create and deploy the PCF button control with one additional changes. In the submitClicked(event:Event) handler, add the following code

2. Adding the PCF button control to the Canvas App

To enable PCF control to be used within the Canvas App, you need to:

  • Enable power apps component framework for canvas app in the power platform admin center
  • Enable Components in Preview Features of the Canvas App settings

Enable power apps component framework for canvas app in the power platform admin center

  • Go to admin.powerplatform.microsoft.com
  • Select the Environment where you want to embed the PCF control
  • Click Settings -> Product -> Features
  • Enable the Power Apps component framework for canvas apps

The next step is to Enable Components in Preview Features of the Canvas App settings

  1. Open the canvas app
  2. Select File -> Settings -> Advance Settings
  3. Under Preview Features -> Enable Components

Now that the PCF control components is enabled for your CDS/Dataverse environment and the Canvas App, let’s embed this control in your Canvas App.

  • Go to the canvas App
  • Select the Insert tab -> Custom -> Import component
  • You will notice two tabs, Canvas and Code. Select the “Code” tab
  • Select your PCF control (this is visible only if your pcf control is deployed to this CDS environment, see step 1 – creating and deploying the pcf control) from the list, and click on Import
  • Importing this component will enable the Code components section in your Insert tab in the left navigation and display your PCF control
  • Drag this control across to your Canvas App
  • Save and Publish

Test this out by opening the canvas app from the ribbon button. Now, clicking on this button should close the Canvas App.

Create a new PCF Button Control

  1. Create a new folder in your directory

2. Open Visual Studio Developer 2019 command prompt and navigate to the NewPcfButton folder, and type the following pcf command:
pac pcf init –namespace CanvasAppClosePcfControl –name CanvasAppClose –template field

You should see a pcf project getting created in the NewPcfButton folder.

3. Open Visual studio code, and open this project by navigating to the NewPcfButton folder

4. Now, we will need to install the project dependencies. Click on Terminal in the visual studio code ribbon and select New Terminal. Type the command in the terminal – npm install

You’ll notice a new folder “node_modules” created in the NewPcfButton folder
5. Next, type npm run build to build the solution

6. Type npm start to watch the pcf control in your local browser. You can debug and test your pcf control here

Now that we have the pcf project created, built, and run successfully, let’s go ahead and customize and add a new PCF button control. In this post, we will have not have either a bound/unbound control. Therefore, in your ControlManifest.Input.xml file, command out the following line:

<!–<property name=”sampleProperty” display-name-key=”Property_Display_Key” description-key=”Property_Desc_Key” of-type=”SingleLine.Text” usage=”bound” required=”true” />–>

Next, In your index.ts file, define the following global variables and an eventSubmitClicked event

private _context:ComponentFramework.Context<IInputs>;
private theNotifyOutputChanged:() => void;
private _container:HTMLDivElement;
//Html Elements - Creating a text box and a button

private _eleMainContainer:HTMLDivElement;
private _eleButton:HTMLButtonElement;
private eventSubmitClicked:EventListenerOrEventListenerObject;

Next, inside the init function, write the following code:

this._container = container;
this._context = context;
this.theNotifyOutputChanged = notifyOutputChanged;
//The assignment of the event listener to the function should be done before creating the UI        

this.eventSubmitClicked = this.submitClicked.bind(this);
//Create UI        

//Main Container
this._eleMainContainer = document.createElement("div");
this._eleMainContainer.className = "mydiv";
//Define a button
this._eleButton = document.createElement("button");
this._eleButton.className = "canvasAppButton";             
this._eleButton.innerHTML = "Finish";
this._eleButton.addEventListener("click", this.eventSubmitClicked);
//Add the button inside the main container, and the main container inside the container

this._eleMainContainer.appendChild(this._eleButton);
this._container.appendChild(this._eleMainContainer);

Let’s give this button some css styling. Notice, I have a css class “canvasAppButton” attached to this button. The idea here is to apply css to the button control, the same css styling as that of the Canvas App buttons. Create a new subfolder “css” under the main project folder and add the canvas.css file.

Inside the css file, apply the css properties to the class

.canvasAppButton
{
    background-color:#2d5297;
    color:white;
    width:150px;
    height:40px;
    border-radius: 8px;
    border-color:transparent;
    font-family: “Open Sans”, sans-serif;
    font-weight: 600;
    font-size: 16pt;
}

Reference this css file under the ControlManifest.Input.xml file in the <resources> section.

Now, that the button is created, we bind the button to the submitClicked event. Write the following function below your init function.

//Custom event handler – for the button
 private submitClicked(event: Event): void{  alert(“Button is clicked”);    }

Next, build the project using npm run build, and npm start to see the button in action

Click on the button.

The pcf button control works!!

Deployment:

Let’s work on deploying this pcf control to CRM

The first step is to create a solution folder inside the NewPcfControl project folder

The next step is to create a publisher and publisher prefix. This will bind the control to the given publisher and the publisher prefix. When this is imported into CRM, a new publisher and the publisher prefix is created with the given names and the pcf control is bound to that prefix. You could also make use of your existing publisher and publisher prefix if you don’t want to create a new one. Type the following command in the Developer Command prompt for VS 2019

pac solution init –publisher-name MyCompany –publisher-prefix new

The next step is add the pcf control project solution reference to the solution folder. Type the following command, but make sure you replace the path with your project path

pac solution add-reference –path “C:\Users\HameedHussain\Downloads\NewPcfButton”

Next, build the solution directory, type the following command:

msbuild /t:build /restore

This command will build the CRM zip file, that you will use to import your solution into CRM. Let’s look at the Solution and see where we would find the solution zip file

The CRM zip file is found inside the bin folder

Import the Solution.zip file into CRM, and use the PCF control as you see fit in any entity.

Additional Notes:

By default, the solution imported will have the display name and Name of the solution as “Solution”. The default solution version is 1.0 This is because the solution.xml contains the unique name, and description of the CRM solution as “Solution”. You can change the Unique name and Name of the solution as per your solution naming convention. See below.

If you make changes to the Pcf control code and wish to import the new changes, just run the build command (npm run build) and make directory (msbuild /t:build /restore), and import the solution. It’s a good practice to update the solution version on every build.

‘The ‘version’ attribute is invalid – The value ‘1.0’ is invalid according to its datatype ‘versionType’ – The Pattern constraint failed.’

While implementing the PCF control, after building the solution, if you run into the following issue while importing the custom control solution

String”>An error has occurred. Try this action again. If the problem continues, check the Microsoft Dynamics 365 Community for solutions or contact your organization’s Microsoft Dynamics 365 Administrator. Finally, you can contact Microsoft Support. : Microsoft.Crm.Tools.ImportExportPublish.ImportCustomControlException: CustomControl with name failed to import with error: The import manifest file is invalid. XSD validation failed with the following error: ‘The import manifest file is invalid. XSD validation failed with the following error: ‘The ‘version’ attribute is invalid – The value ‘1.0’ is invalid according to its datatype ‘versionType’ – The Pattern constraint failed.’.”‘.” —> Microsoft.Crm.CrmException: The import manifest file is invalid. XSD validation failed with the following error: ‘The import manifest file is invalid. XSD validation failed with the following error: ‘The ‘version’ attribute is invalid – The value ‘1.0’ is invalid according to its datatype ‘versionType’ – The Pattern constraint failed.’.”‘.”at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.ValidateSchema()at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.Validate(ExecutionContext context)at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.Install() — End of inner exception stack trace — at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.Install()at Microsoft.Crm.Tools.ImportExportPublish.ImportCustomControlsHandler.ImportItem()

It is most likely due the version attribute being invalid. Make sure the version is 1.0.0 in your ControlManifest.Input.xml file

Power Automate Flow – Flow client error returned with status code “Bad Request”

In Power Automate Flow, you may encounter the error below when tryin to save the flow

Request to XRM API failed with error: ‘Message: Flow client error returned with status code “BadRequest” and details “{“error”:{“code”:”InvalidOpenApiFlow”,”message”:”Flow save failed with code ‘InvalidTemplate’ and message ‘The template validation failed: ‘The repetition action(s) ‘Apply_to_each’ referenced by ‘inputs’ in action ‘Create_a_new_record’ are not defined in the template.’.’.”}}”. Code: 0x80060467 InnerError: Type: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]

There issue may happen when you’re trying to copy a value (example, lookup) from one flow to the other because the flow may have different collection (list of records for example) names. Therefore, when you’re copying one value from one flow to the other, you’re also copying the collection name.

For example, in one flow, your collection name is

outputs(‘Run_a_Child_Flow_-_Retrieve_Records’)?[‘Body’]?[‘new_personcategoriesid’]

In the other flow, if the collection name is different, and you’re copying the above value and pasting in the flow, you’ll see the “Bad Request” error.

To fix the issue, I deleted the value, selected the lookup field from the existing flow using dynamic content. It let me save the flow. This is a simple issue, often that people may figure it out, but I wanted to put it out there if someone run into this issue.

Azure DevOps Pipeline for CRM – Solution Packager

The first step in implementing the Azure DevOps pipeline process for Dynamics 365 is to extract/decompose the CRM solution into its individual components. This is done through the solution packager provided by Microsoft. You can download and install the solution packager from this link

After you install the the solution packager on your disk, you will see the following list

You can find the SolutionPackager.exe file in the CoreTools

Open windows powershell command prompt and navigate to the SolutionPackager.exe folder – D:\NuGetTools\Tools\CoreTools

Before we go to the next step, in the CRM instance, create a new empty solution and export the solution, this solution file is a zip file containing the customizations and solutions xml

We will try to extract this solution using the solution packager and see what it looks like. Go back to the powershell command, and type the following

.\SolutionPackager.exe /action:Extract /zipfile:”SolutionfolderPath/SolutionName.zip” /folder “OutputFolder”

Below, I specified where my solution is and the output folder where I want the extract the zip folder contents.

.\SolutionPackager.exe /action:Extract /zipfile:”D:\Other\DevOps\NewDemoSolution_1_0_0.zip” /folder “D:\Other\DevOps\devopsfiles”

Let’s see what the output folder looks like:

Because, our solution was empty, we did not find the Enities, Plugins, Webresources folders. Let’s add few components to our Dynamics CRM solution

Export this solution, and run the SolutionPackager.exe from the powershell again, you’ll notice extraction of the different components

After you navigate to the output folder, you’ll see the different folders

Navigating inside the Entities folder, you’ll notice that the entities are split into their own folders and files containing the forms, views, charts etc.

Now, you can upload the root folder to the Git/GitHub/TFS, or any other repo, and take the next step in the integration of Azure Dev Ops pipeline for CRM.

Retain Overridecreatedon and createdby in Dynamics 365 during data import

In Dynamics 365 data import, the data imported will create records with today’s date and the data created will be logged against the user who is currently logged in even though the original createdon and createdby user could be different.

In many cases, when you’re importing an existing or legacy data in your Dynamics 365, you want to retain the original createdon date and the user who created that record. If you want to import your data with the date when the data was created, you can use overridecreatedon to override the data with the createdon date. You can also create this record against the original createdby user through impersonation. There are few ways to achieve this in Dynamics 365. You can use the kingswaysoft SSIS package to achieve this. However, in this blog, you will see how overridecreatedon and created by can achieved through xRM solution.

We have to use the OrganizationServiceProxy class to impersonate the createdby user. The class object has a property called “CallerId” that will be used to impersonate a user.

Here’s the code:

using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using System;
using System.Net;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk.Query;namespace DynamicsProxy
{
  class Program
  {
     static void Main(string[] args)
     {
       try
       {
         string crmUserName = "xxxx@onmicrosoft.com";
         string crmPassword = "xxxxxx";
         string crmOrgHost = "https://orgname.crm.dynamics.com/";            
          ClientCredentials clientCredentials = new ClientCredentials();
          clientCredentials.UserName.UserName = crmUserName; 
                                                             
            clientCredentials.UserName.Password = crmPassword; 
                                                               
            // For Dynamics 365 Customer Engagement V9.X, set Security Protocol as TLS12
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;


            string crmApiPath = "XRMServices/2011/Organization.svc";

            Uri crmUri = new Uri(crmOrgHost + crmApiPath);

            OrganizationServiceProxy orServiceProxy = new OrganizationServiceProxy(crmUri,null,clientCredentials,null);
            orServiceProxy.EnableProxyTypes();
            //Caller Id: this is the guid of the user we want to impersonate - 4b591077-9fb8-e911-a86e-000d3a372124
            orServiceProxy.CallerId = new Guid("47d57787-433b-eb11-a813-000d3a31c841");
            Entity ent = new Entity("contact");
            ent["lastname"] = "Hameed";
            ent["overriddencreatedon"] = new DateTime(2021, 1, 19);
            orServiceProxy.Create(ent);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: "+ex.Message);
        }
    }
}
}

During the code execution, you may run into an error:

{“Principal user (Id=xxxx-xxx-xxx-xxx-000d3a31c841, type=8, roleCount=5, privilegeCount=811, accessMode=0), is missing prvOverrideCreatedOnCreatedBy privilege (Id=d48cf22f-f8c2-xxxx-89eb-49f8281dxxxx)

To resolve this issue, you need to enable the override createdon privilege on the security role that the user is currently assigned to

Power Automate – Invalid type. Expected Object but got Array

In Power automate, in the parse json, if the response doesn’t align with the schema of the parse json, you’ll notice different errors, one of them being “Invalid type. Expected Object but got Array”.

The Parse json schema was expecting an object as a response.

But, instead, received an array of objects, which results in the following error

This error happens when you’re passing a list of array oject records from the child workflow or list records from the previous steps.

So the parse json was expecting an object in the format of

{

“Var1″:”var1_value”,

“Var2″:”var2_value”

}

But instead receiving an array of objects:

[

{

“Var1″:”var1_value”,

“Var2″:”var2_value”

}

]

The output value outputs(‘Get_List_record_of_Person_Engagement_Event’)?[‘body/value’] returns an array/list of objects

To solve this problem, you need to return just the first object value from the result. Use the following expression (in the above Response body value) to return the first value from the array

first(outputs(‘Get_List_record_of_Person_Engagement_Event’)? [‘body/value’])

This will return the following object

{

“Var1″:”var1_value”,

“Var2″:”var2_value”

}

Which is now in-line with the format that we were expecting in the parse json

An undeclared property ‘new_bulkadjustment’ which only has property annotations in the payload but no property value was found in the payload.

Recently, I was running into an issue on xrm.webAPI.createRecord, when trying to update the lookup attribute to the entity object.

The oData naming convention for lookup attribute and the documentation around it asks you to use the schema name of the field with @odata.bind:

entity[“new_BulkAdjustment@odata.bind”] = “/new_revcontacts(00000000-0000-0000-a000-000d0a000a00)”;

This did not resolve the issue. I tried few other combinations using the REST builder, but unfortunately, nothing worked.

The way to set the lookup is:

entity[“FieldSchemaName_EntityName@odata.bind”]=”/LookupEntityName(guid)”;

entity[“new_BulkAdjustment_new_revcontacts@odata.bind”]=”/new_revcontacts(00000000-0000-0000-a000-000d0a000a00)”;

new_BulkAdjustmnet is a lookup field on new_revcontact entity.

USD Outbound call – Triggering an UII action from a Session Overview

In USD session overview xaml, you can trigger an event or UII action through a command parameter. You can pass the replacement parameters as parameters to this UII action.

In this example, we will trigger an UII action. Additionally, we will pass the telephone number and agent Id to this UII action. The xaml for the telephone number attribute and the command parameter looks like this:

<Image Style=”{DynamicResource ImageLogo}” Source=”{Binding Source=msdyusd_Phone16, Converter={StaticResource CRMImageLoader}}” />

    <TextBlock  TextWrapping=”Wrap” Padding=”5,0,0,5″ FontSize=”12″ Text=”Mobile: ” Foreground=”#262626″  VerticalAlignment=”Center”>

  <Hyperlink Command=”CCA:ActionCommands.DoActionCommand” CommandParameter=”http://uii/CTIConnector/MakeCall?tel:%5B%5Bcontact.mobilephone%5Du+x%5D%26agentid:%5B%5Bsystemuser.edw_agentid%5Du+x%5D”&nbsp; FontWeight=”Regular” AutomationProperties.Name=”Telephone Number [[contact.mobilephone]+x]”  Foreground=”#FF3B79B7″ FontSize=”12″>[[contact.mobilephone]+x]</Hyperlink>

       </TextBlock>

</StackPanel>  

     </Grid>

</Grid>

NOTE: To pass more than one parameter in the command parameter URL, always use encoded format. In the above example, & is encoded as %26. You can’t pass & but the encoded format of ‘&’ which is %26

MakeCall is the custom UII action. I am overriding this action in the CtiConnector custom code. The code will perform the outbound call through a telephony system.

From the session overview on USD, click on the mobile phone link

Couple of things to note:

In this example, we have one command parameter. If you want to add an additional command parameter in the xaml, you need to add an additional hyperlink element. This is not recommended.

If you want to trigger or perform multiple actions through this command parameter, the best way to do it is adding a new event and fire that event through the command parameter. For example, in the same example, if you want to trigger a phone call and also create a phone call record, you can handle this through the event.

You can fire the event through this command:

CommandParameter=”http://uii/Patient/FireEvent?name=FireOutboundCall

FireOutboundCall is the event that is being triggered. You can create this new event on the hosted control Patient, and add action calls