Scripting Archives - Aric Levin's Digital Transformation Blog http://aric.isite.dev/tag/scripting/ Microsoft Dynamics 365, Power Platform and Azure Thu, 12 May 2022 03:23:46 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.3 Creating a NAICS Code PCF Control using WebApi http://aric.isite.dev/powerapps/post/create-naics-pcf-control-webapi/ Fri, 18 Oct 2019 06:43:00 +0000 https://aric.isite.dev/index.php/2019/10/18/creating-a-naics-code-pcf-control-using-webapi/ In many of the projects that I have worked over the past years, either government of private sector, while working with other businesses it has become a common practice to request that Industry information from the companies that you work with, and furthermore the NAICS code. While not every company knows their NAICS code, drilling down to get it using the Industry (2-digit code), Subsector (4-digit code) and NAICS title (6-digit code) might be the way to go, sometimes you might want to bypass this complex search if you know ahead of time your business’ NAICS code and have it auto populated for you.

The post Creating a NAICS Code PCF Control using WebApi appeared first on Aric Levin's Digital Transformation Blog.

]]>
In many of the projects that I have worked over the past years, either government of private sector, while working with other businesses it has become a common practice to request that Industry information from the companies that you work with, and furthermore the NAICS code. While not every company knows their NAICS code, drilling down to get it using the Industry (2-digit code), Subsector (4-digit code) and NAICS title (6-digit code) might be the way to go, sometimes you might want to bypass this complex search if you know ahead of time your business’ NAICS code and have it auto populated for you.

In this article I will demonstrate the prep work that I did in order to have the NAICS codes stored within the Model-Driven app, and how to create the PowerApps Component Framework custom control that will retrieve the NAICS code.

The first thing that I needed to do was create the entities that will store the NAICS information. I created three separate entities for Industry, subsector and NAICS codes/titles. The image below shows that entities that are used as part of this solution. The account entity is used to display the custom control.

Entity List
Next, I will show the data that makes up these three entities. You will see the 4-digit and 6-digit NAICS codes have lookups to the parent entities to simplify the entry and search process.

Industries [2-digit NAICS]
Industries

Subsectors [4-digit NAICS]
Subsectors

NAICS Titles [6-digit NAICS]
NAICS title

Once we have created the entities and the fields in the 3 lookup entities, we will create the fields in the account entity where they can be viewed from. The two images below will show the fields and the form design.

Account Entity NAICS related fields:
Account Entity fields
Account Entity form design:
Account Entity form design
Now that we have done all the prep work, we are ready to start creating our new PowerApps Component Framework custom control. In this post, I will go through a step by step explanation create the PCF control and deploy it to your Model-Driven app/CDS environment.

I will be using Visual Studio Code for the development of the PCF control, and Developer PowerShell for Visual Studio 2019. You can use the Command Prompt for Visual Studio 2017/2019 as well.

The first thing that we need to do is download and install Node.js and the PowerApps Command Line Interface. For Step by step instructions, please view the PCF Control Guide.

To start we will create a folder on our workstation where we want to store the PowerApps Component Framework custom control project. In our case we used the D:PowerPlatformPCFNAICSGenerator folder. Once this folder has been created, we open Visual Studio Code, and from the Start page, we select Open Folder and point it to the folder that we created for this solution.

Next, we select Terminal from the View menu in order to allow us to run PowerShell commands to add the required components, test and build the solution. The first command that we will need to run is the pac pcf init command, which will create the manifest and typescript files for us.
PAC PCF Init

After the project has been created, you can navigate to the project directory, and you will notice that a subfolder with the name of the component has been created. The subfolder contains the ControlManifest.Input.xml and index.ts files.

We can now go ahead and install all the required dependencies that are required. This steps can take a few minutes to complete. The command to install the dependencies is npm install. While this task is executing you will see progression on your command prompt window, and you might see a few warnings or errors.
NPM Install

You will notice in Visual Studio code that a node_modules folder was created with a lot of subfolders containing script files. This contains a large variety of available modules that can be added to your typescript project.
Next, we will need to update the manifest file to include the required control and properties. The below shows the final view of the manifest file.

<?xml version="1.0" encoding="utf-8" ?>
<manifest>
  <control namespace="PCFControls" constructor="PCFNaicsCodeSplitter" version="0.1.1" display-name-key="PCFNaicsCodeSplitter" description-key="PCFNaicsCodeSplitter description" control-type="standard">
    <property name="NAICSCode" display-name-key="PCFNaicsCodeSplitter_NAICSCode" description-key="NAICS Code Field" of-type="SingleLine.Text" usage="bound" required="true" />
    <property name="NAICS2Control" display-name-key="PCFNaicsCodeSplitter_NAICS2Control" description-key="NAICS2 Control Field Name" of-type="SingleLine.Text" usage="input" required="true" />
    <property name="NAICS4Control" display-name-key="PCFNaicsCodeSplitter_NAICS4Control" description-key="NAICS4 Control Field Name" of-type="SingleLine.Text" usage="input" required="true" />
    <property name="NAICS6Control" display-name-key="PCFNaicsCodeSplitter_NAICS6Control" description-key="NAICS6 Control Field Name" of-type="SingleLine.Text" usage="input" required="true" />
    <resources>
      <code path="index.ts" order="1"/>
      <css path="pcfcontrols.css" order="1" />
    </resources>
    <feature-usage>
      <uses-feature name="WebAPI" required="true" />
      <uses-feature name="Utility" required="true" />
    </feature-usage>
  </control>
</manifest>

You will notice in the above file that the control has 4 properties. The first property contains the bound control that contains the 6 digit NAICS code that the user will type. The next 3 properties are text fields that contain single like of text, which contain the names of the three lookup controls. Lookup controls are currently not available for use as bound controls within PCF solutions.

You will also see that we include the WebApi and the Utility features. These features allow us to call the classes of the Xrm namespace which contain the methods for accessing the WebApi and Utility methods.

We not start making changes to the typescript file in order to provide the functionality that we are looking for. Note that the typescript contains several parameters that were used for testing and debugging purposes, and can be removed if so required.
We will first look at the variable declarations of the class.

    private _context: ComponentFramework.Context<IInputs>;
    private _notifyOutputChanged: () => void;
    private _container: HTMLDivElement; 
    
    private _naicsCodeElement: HTMLInputElement;
    private _naicsCodeErrorElement: HTMLElement

    private _naicsCodeChanged: EventListenerOrEventListenerObject;
    private _naics6id: string;

    private _naics2FieldName: string | null;
    private _naics4FieldName: string | null;
    private _naics6FieldName: string | null;

The variable declarations include the default context, notifyOutputChanged and container parameters, as well as the HTML elements, event listener object and other string variables. These variables will be initialized in the init function.

    public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container:HTMLDivElement)
    {
        // Add control initialization code
        this._context = context; 
        this._notifyOutputChanged = notifyOutputChanged; 
        this._container = container;

        this._naicsCodeChanged = this.naicsCodeChanged.bind(this);

        // textbox control
        this._naicsCodeElement = document.createElement("input");
        this._naicsCodeElement.setAttribute("type", "text");
        this._naicsCodeElement.setAttribute("placeholder", "Enter a 6 Digit NAICS Code");
        this._naicsCodeElement.setAttribute("class", "pcfinputcontrol");
        this._naicsCodeElement.addEventListener("change", this._naicsCodeChanged);

        this._naicsCodeErrorElement = document.createElement("div");
        this._naicsCodeErrorElement.setAttribute("class", "pcferrorcontroldiv");
        var naicsErrorChild1 = document.createElement("label");
        naicsErrorChild1.setAttribute("class", "pcferrorcontrolimage")
        naicsErrorChild1.innerText = "";

        var naicsErrorChild2 = document.createElement("label");
        naicsErrorChild2.setAttribute("id", "errorelementlabelid");
        naicsErrorChild2.setAttribute("class", "pcferrorcontrollabel");
        naicsErrorChild2.innerText = "Invalid NAICS Code Entered";

        this._naicsCodeErrorElement.appendChild(naicsErrorChild1);
        this._naicsCodeErrorElement.appendChild(naicsErrorChild2);
        this._naicsCodeErrorElement.style.display = "none";

        this._container.appendChild(this._naicsCodeElement);
        this._container.appendChild(this._naicsCodeErrorElement);   

        this._naics2FieldName = context.parameters.NAICS2Control.raw;
        this._naics4FieldName = context.parameters.NAICS4Control.raw;
        this._naics6FieldName = context.parameters.NAICS6Control.raw;
    }

The init function contains the variable initialization, creation of child elements (such as error element), and reading of the names of the fields used for the 3 lookup controls.
The naicsCodeChanged function below retrieves the data from the HTML element, and calls the RetrieveNAICSReferences function passing to it the 6 digit NAICS code. After this is complete the nofifyOutputChanged function is called.

    public naicsCodeChanged(evt: Event):void
    {
        var naicsCode = this._naicsCodeElement.value;
        console.log(naicsCode);
        this.RetrieveNAICSReferences(naicsCode);
        this._notifyOutputChanged();
    }

The RetrieveNAICSReferences function call the WebApi to retrieve the values of the NAICS 2, 4 and 6 lookups from the NAICS6 – Title entity.

    private RetrieveNAICSReferences(naicsCode : string) {
        var entityName = "cdsp_naics6title";
        var retrieveMultipleOptions = "?$select=_cdsp_naics2_value,_cdsp_naics4_value,cdsp_naics6titleid,cdsp_title&$filter=cdsp_number eq '" + naicsCode + "'";
        var thisRef = this;
        this._context.webAPI.retrieveMultipleRecords(entityName, retrieveMultipleOptions).then(function (results: any) {
            for (let entity of results.entities) {
                var naics2Value = entity["_cdsp_naics2_value"];
                var naics2Text = entity["_cdsp_naics2_value@OData.Community.Display.V1.FormattedValue"];

                var naics4Value = entity["_cdsp_naics4_value"];
                var naics4Text = entity["_cdsp_naics4_value@OData.Community.Display.V1.FormattedValue"];
                
                var naics6Value = entity["cdsp_naics6titleid"];
                var naics6Text = entity["cdsp_title"];

                // @ts-ignore
                Xrm.Page.getAttribute(thisRef._naics2FieldName).setValue([{ id: naics2Value, name: naics2Text, entityType: "cdsp_naics2industry" }]);

                // @ts-ignore
                Xrm.Page.getAttribute(thisRef._naics4FieldName).setValue([{ id: naics4Value, name: naics4Text, entityType: "cdsp_naics4subsector" }]);

                // @ts-ignore
                Xrm.Page.getAttribute(thisRef._naics6FieldName).setValue([{ id: naics6Value, name: naics6Text, entityType: "cdsp_naics6title" }]);
                break;
            }
        }, function (error) {
            thisRef._context.navigation.openAlertDialog(error.message);
            return [];
        });
    }

We retrieve each of the three values and call the setValue function on each of the lookup fields. The reason for using this is the bound attributes are not yet available for lookup controls.
We call the updateView to display any errors that might have occurred. In this solution we have not really captured any errors.

    public updateView(context: ComponentFramework.Context<IInputs>): void
    {
        // Add code to update control view

        // Display Error
        if (this._naicsCodeErrorElement.style.display != "none")
        {
            var message = "The NAICS COde  is not valid.";
            var type = "ERROR";  //INFO, WARNING, ERROR
            var id = "9443";  //Notification Id
            var time = 5000;  //Display time in milliseconds
            
            // @ts-ignore 
            Xrm.Page.ui.setFormNotification(message, type, id);
            
            //Wait the designated time and then remove the notification
            setTimeout( function () {
            // @ts-ignore 
                Xrm.Page.ui.clearFormNotification(id);
            }, time );

        }       
    }

Finally in the getOutput we return the value of the 6 digit search code:

    public getOutputs(): IOutputs
    {
        return {
            NAICSCode: this._naics6id
        };
    }

The last two method which are not commonly used are the construction and destructor as shown below:

    constructor()
    {

    }

    public destroy(): void
    {
        // Add code to cleanup control if necessary
        this._naicsCodeElement.removeEventListener("change", this._naicsCodeChanged);
    }

Once all the code is done we can test the application and run it. Since this application uses WebApi, this cannot be tested properly from within the context of the PowerApps Component Framework Test environment, but we can still do this by passing some sample data.

The command to build to solution is npm run build, and in order to run it within the Test environment it is npm start.

npm run build, npm start

We not open Developer PowerShell for VS2019, create a directory for the solution (can be called solution or something similar) and will run various commands within that new empty directory. You can create a powershell script file so that you don’t have to redo this, as every time that you will have to run this, it will save you some time.

Build Solution

Once the solution has been we are ready to deploy it. Again here, there are a couple of calls that have to made in order to have the completed deployment.

We will call the pac auth create and pac pcf push to deploy the solution to our CRM environment.

pac auth create –url https://pcfapps.crm.dynamics.com
pac pcf push –publisher-prefix PCFC

Once the solution is deployed, we will open that account form, and configure the control. Within the account form, select the control, and double click or click and choose properties. This will open the control’s property window. Within the property window we will click on the Controls tab, and choose Add Control, and then add the control that we created. The window should look similar to what is displayed below.

Field Properties

The next three images show us how the controls look on the form and the result.

NAICS PCF Step 1

NAICS PCF Step 2

NAICS PCF Step 3

You can download the lab instructions, source code and solution using the links below:

Dynamics 365 Solution file

PCF Control Source Code files

The post Creating a NAICS Code PCF Control using WebApi appeared first on Aric Levin's Digital Transformation Blog.

]]>
Changing the details page link url for Entity Lists http://aric.isite.dev/dynamics/post/change-entity-list-details-page-link/ Thu, 12 Jul 2018 03:26:00 +0000 https://aric.isite.dev/index.php/2018/07/12/changing-the-details-page-link-url-for-entity-lists/ When displaying entity lists on a web page, there is usually a default column in a view which is displayed as a hyperlink. The link to the page is displayed based on the Web Page for Details View and the ID Query String Parameter Name attributes on the Entity List form (as shown in the image below). The problem is, what happens if my page that is displaying the Entity List already has query string parameters, and you want to keep those parameters on the next page that you are going to display.

The post Changing the details page link url for Entity Lists appeared first on Aric Levin's Digital Transformation Blog.

]]>
When displaying entity lists on a web page, there is usually a default column in a view which is displayed as a hyperlink. The link to the page is displayed based on the Web Page for Details View and the ID Query String Parameter Name attributes on the Entity List form (as shown in the image below). The problem is, what happens if my page that is displaying the Entity List already has query string parameters, and you want to keep those parameters on the next page that you are going to display.

Entity List Attributes

The Options tab on the Entity List form contains a custom JavaScript section, which we can control what happens when the document is loaded or when the grid refreshes/loads. We can use the $(“.entitylist.entity-grid”).on(“loaded”, function () { }) to process anytime the grid is reloaded, sorted or paged through. The image and code snippet below provide us with an example on how this can be accomplished.

Entity List Options (Javascript)

When the grid is loaded, we retrieve the existing query string and place it into a variable. We then loop through all the results of the table (entity list), by using the jquery each function. We get the current url that is in the anchor attribute, and append the existing query string to it. We finally write back to the anchor attribute the new url. The code below shows everything withing the document ready function.

$(document).ready(function () {
  $(".entitylist.entity-grid").on("loaded", function () { 
    var url = window.location.href;
    var queryStrings = url.substring(url.indexOf("?") +1);
    $("[href^='/business-info']").each(function(){
        var currentUrl = ($(this).attr('href'));
        var targetUrl = currentUrl + "&" + queryStrings;
        $(this).attr("href", targetUrl);
    });  
  });  
});  

If your page was start-business?id=999, when you click on the link the page you will be redirected to would be business-info?slid=12345678-1234-1234-1234-1234567890AB&id=999. The querystring from the previous page is now appended to the new page.

The post Changing the details page link url for Entity Lists appeared first on Aric Levin's Digital Transformation Blog.

]]>
Adding masking to form controls in Dynamics Portals http://aric.isite.dev/dynamics/post/dynamics-portals-textbox-masking/ Thu, 21 Jun 2018 13:21:00 +0000 https://aric.isite.dev/index.php/2018/06/21/adding-masking-to-form-controls-in-dynamics-portals/ It is a pretty known practice today, that when creating a web application that requests data from customers, certain fields are masked so that the system can prevent the entry of incorrect data. This has been done in desktop applications for a long time and is now also very common in web based applications.

The post Adding masking to form controls in Dynamics Portals appeared first on Aric Levin's Digital Transformation Blog.

]]>
It is a pretty known practice today, that when creating a web application that requests data from customers, certain fields are masked so that the system can prevent the entry of incorrect data. This has been done in desktop applications for a long time and is now also very common in web based applications.

Microsoft Dynamics Portals does not provide a way to implement this out of the box, but the path to implement this is simple and straight forward. The first thing that we need to do in order to get this working is get a jQuery mask plugin. The most common plugin that I have seen is the jQuery Mask Plugin created by Igor Escobar. It is available for download from github in the following link:

http://igorescobar.github.io/jQuery-Mask-Plugin/

Next we need to add that plugin to our Dynamics Portals application. In order to do this, I would refer you to a previously published article that I wrote called JavaScript Web Files in CRM Portals. You can read it on the Dynamics Community or my business web site using one of the links below:

https://community.dynamics.com/crm/b/briteglobalsolutions/archive/2018/05/02/javascript-web-files-in-crm-portals or https://www.briteglobal.com/blogs/community/portals-web-files/

Add the script file to the Tracking Code Content Snippet and upload it as a Web file so that it can be accessed across the application. At this point, the hard part is done. In the sample below, on the On my Entity Form Custom JavaScript, I will add the following code to mask my Tax Id and Social Security Numbers.

jQuery(function($){   
   $("#new_ein").mask("99-9999999");      
   $("#new_ssn").mask("999-99-9999"); 
});

The github site above contains plenty of examples for masking. The above was just a simple example. Finally, in the form that I would like to open, what I will see when I click on the control is the masking of the Tax Id number.

Dynamics Portal Masking

Next as I fill it out, it will only allow me to fill the numbers that I have specified in my jQuery function.

Dynamics Portals Masking

The post Adding masking to form controls in Dynamics Portals appeared first on Aric Levin's Digital Transformation Blog.

]]>
Using Placeholders in Portal Entity Form Text Controls http://aric.isite.dev/dynamics/post/using-placeholders-in-portal-text-controls/ Fri, 15 Jun 2018 23:32:00 +0000 https://aric.isite.dev/index.php/2018/06/15/using-placeholders-in-portal-entity-form-text-controls/ Recently I was working on a project, where the client needed all fields to have placeholders for accessibility reasons. When looking at the attribute metadata of the Entity Form, we are allowed to enter some text above or below the textbox control (or above the label), but the option is not available to prepopulate the control with some text when there is no data in it. After seeing a few community questions on how to implement this, I thought to share the solution.

The post Using Placeholders in Portal Entity Form Text Controls appeared first on Aric Levin's Digital Transformation Blog.

]]>
Recently I was working on a project, where the client needed all fields to have placeholders for accessibility reasons. When looking at the attribute metadata of the Entity Form, we are allowed to enter some text above or below the textbox control (or above the label), but the option is not available to prepopulate the control with some text when there is no data in it. After seeing a few community questions on how to implement this, I thought to share the solution.

Using JQuery we are able to easily add this functionality by using the attr function. The code sample below add placeholders for the Tax Id and Social Security Number fields. This code can be placed on either the Entity Form JavaScript section or on the Web Page JavaScript section.

$(document).ready(function () {
 // Add Placeholders for Tax Information
 $("#new_ein").attr("placeholder", "Enter your Tax Id Number" );
 $("#new_ssn").attr("placeholder", "Enter your Social Security Number" );
});

The screenshot below shows the result of displaying the placeholder on the Portal Form.

Dynamics CRM Portal Placeholder for Textbox

The post Using Placeholders in Portal Entity Form Text Controls appeared first on Aric Levin's Digital Transformation Blog.

]]>
Changing Portal Lookup Control to Hyperlink Style Control http://aric.isite.dev/dynamics/post/portal-lookup-control-hyperlink-style/ Sat, 19 May 2018 01:24:00 +0000 https://aric.isite.dev/index.php/2018/05/19/changing-portal-lookup-control-to-hyperlink-style-control/ Recently we had a requirement to change the Lookup Control in the Portal to look like a hyperlink, so that when the user clicked on the Hyperlink it would pop up the Lookup Dialog.
The Lookup control was displayed as part of an entity form, so the only way to really do this was with jquery.

The post Changing Portal Lookup Control to Hyperlink Style Control appeared first on Aric Levin's Digital Transformation Blog.

]]>
Recently we had a requirement to change the Lookup Control in the Portal to look like a hyperlink, so that when the user clicked on the Hyperlink it would pop up the Lookup Dialog.
The Lookup control was displayed as part of an entity form, so the only way to really do this was with jquery.

The first thing that we wanted to do was customize the label above the lookup control, so that it looked like a hyperlink, so we set a couple of styles on the label and modified the text.

$(“#accountid_label”).css(“color”, “#0039e6”);

$(“#accountid_label”).css(“cursor”, “pointer”);

$(“#accountid_label”).text(“Lookup Company”);

We then wanted to hide the actual lookup control

$(“#accountid_name”).parent().hide();

Then we added an onclick attribute to the label of the lookup control

  $(“#accountid_label”).attr(‘onclick’, ‘showLocationLookup()’);

The onclick attribute that we added called a function that would perform the click event of the lookup control to display the dialog.

function showLocationLookup()

{

$(“#sbs_site_locationid_name”).parent().find(‘.input-group-btn’).children().first().next().click();

}

Finally we added an onChange event to the lookup control so that we can retrieve the values from it, and display the control if we wanted to.

$(“#accountid”).change(function(){

    var accountId = $(“#accountid”).val();

    var accountName = $(“#accountid_name”).val();

  $(“#accountid_name”).parent().show();

    $(“#accountid_name”).parent().find(‘.input-group-btn’).children().first().next().hide();

});  

The image below shows the end result. Notice the Lookup Company label at the bottom of the page…

Portal Lookup Hyperlink

The post Changing Portal Lookup Control to Hyperlink Style Control appeared first on Aric Levin's Digital Transformation Blog.

]]>
Portals: Pass multiple parameters between Entity Forms http://aric.isite.dev/dynamics/post/pass-parameters-between-entity-forms/ Sat, 05 May 2018 01:03:00 +0000 https://aric.isite.dev/index.php/2018/05/05/portals-pass-multiple-parameters-between-entity-forms/ Recently we had the need to create multiple Entity forms in our portal, and be able to pass multiple parameters between the different pages of the portal. As it seems, you are able to pass a single parameter (such as the id of the created record) to the next form, but passing multiple does not seem to be the case. For this particular scenario, using Web Forms was not an option. 

The post Portals: Pass multiple parameters between Entity Forms appeared first on Aric Levin's Digital Transformation Blog.

]]>
Recently we had the need to create multiple Entity forms in our portal, and be able to pass multiple parameters between the different pages of the portal. As it seems, you are able to pass a single parameter (such as the id of the created record) to the next form, but passing multiple does not seem to be the case. For this particular scenario, using Web Forms was not an option.

The requirement was as follows: Gather the Business Information of the Signed in Contact in a Registration form, and pass some of the information from the Registration form to the Application form.

The first screenshot bellows shows the Business Registration form, which includes the Account (which was needed to be passed to the redirected form, along with other variables).

Registration Portal Page

The solution included various steps. The first thing that needed to be done was to make sure that the Registration Id was part of the application form, so that the application form could query that data, so the field was added to the solution. The second thing was that in the Registration form, in the On Success Settings, we would set the On Success to Redirect, Check the Append Record ID to Query String and populate the Record ID Query String Parameter Name with the name of the parameter we want to use.

Entity Form (Registration)

The next thing that we needed is to allow the Registration Entity to be available for querying using oData. In the Active Entity Lists, we created a new record, where we specified the view, Page Size, and oData Feed. The image below shows the oData Feed Settings.

oData Configuration

Since we don’t want people to be able to query our Registration database by providing an oData query, we set the Page Size to 1, so that only a single record can be retrieved.

Finally, in the Application page, we need to create custom JavaScript, that retrieved the query string parameter(s), and retrieved the Registration Information from the OData query, and populated it in the Application Entity form page (as shown in the code example below):

 1
$(document).ready(function(){

	var qsDictionary = parseQueryStringToDictionary(document.location.search);
  	var registrationId = qsDictionary['registrationid'];

    if (!isNullUndefinedOrEmpty(registrationId))
    {
        // get oData Registration Info
      	$("#xxx_registrationid_name").attr("value","Auto Populated");
      	$("#xxx_registrationid").attr("value",registrationId);
	    $("#xxx_registrationid_entityname").attr("value","xxx_registration");
        populateFields(registrationId);
    }
});

function populateFields(registrationId)
{
  var oDataUrl = "https://xxx.dynamics365portals.us/_odata/RegistrationSet?$filter=xxx_registrationid%20eq%20guid%27" + registrationId + "%27";
  
  var oDataResponse = getODataResponse(oDataUrl);
  
  if(oDataResponse != null){
  	$.each(oDataResponse, function (index, responseVal) {
      // Fields Populate Here
      
      var registrationNumber = getStringValue(responseVal.xxx_registration_number);
      $("#xxx_registrationid_name").attr("value",registrationNumber);
      
      var accountId = getStringValue(responseVal.xxx_accountid.Id);
      var accountName = getStringValue(responseVal.xxx_accountid.Name);
      setAccount(accountId, accountName);
      
  	});                 
  }           
}

function setAccount(accountId, accountName)
{
    if (!isNullUndefinedOrEmpty(accountId))
    {
    	$("#xxx_portal_accountid_name").attr("value",accountName);
    	$("#xxx_portal_accountid").attr("value",accountId);
		$("#xxx_portal_accountid_entityname").attr("value","account");
	      
    }  
}

function getODataResponse(oDataUrl) {         
	var response = null;
    $.ajax({
		type: "GET",
        url: oDataUrl,
        dataType: "json",
        async: false
        }).done(function (json) {
			response = json.value;                       
            });                
		return response;
}

function getStringValue(o)
{
  if (o)
    return o.toString();
  else
    return '';
}

function isNullUndefinedOrEmpty(o)
{
   return (typeof (value) === "undefined" || value === null || value === '');
}

The end result is that the that Registration and Account (and any other fields) are populated in the Application page.

Filled out information on Application Page

The post Portals: Pass multiple parameters between Entity Forms appeared first on Aric Levin's Digital Transformation Blog.

]]>
JavaScript Web Files in CRM Portals http://aric.isite.dev/dynamics/post/portals-web-files/ Thu, 03 May 2018 04:31:00 +0000 https://aric.isite.dev/index.php/2018/05/03/javascript-web-files-in-crm-portals/ As in any project, either CRM or Web application, the requirement to have JavaScript libraries that can be accessed across multiple files is common. In Dynamics 365 Portals, the use of Web Files is how we have the ability of create files that will be shared across the entire portal, or possibly only sections of the Portal. These common files are stored in the Web Files entity. This issue is that when we try to add a JavaScript web file, we get an error that the attachment is blocked. Web Files use the Notes entity to store the actual files that we add to the Web File entity. 

The post JavaScript Web Files in CRM Portals appeared first on Aric Levin's Digital Transformation Blog.

]]>
As in any project, either CRM or Web application, the requirement to have JavaScript libraries that can be accessed across multiple files is common. In Dynamics 365 Portals, the use of Web Files is how we have the ability of create files that will be shared across the entire portal, or possibly only sections of the Portal. These common files are stored in the Web Files entity. This issue is that when we try to add a JavaScript web file, we get an error that the attachment is blocked. Web Files use the Notes entity to store the actual files that we add to the Web File entity.

The error that we get when we attempt to do this is:

Attachment blocked: The attachment is not a valid file type.

How do we resolve this? The standard Dynamics 365 System Settings is configured to block a large set of extensions for uploading attachments for both email and notes, and this includes of course the .js attachment. The reasoning behind this is that JavaScript extensions could be used to execute unsafe code. Since the JS files that we want to add was created by us, and most likely will not cause any harm, we can modify this System Setting to allow us to upload js files.

If we navigate to Settings -> Administration, and click on System Settings, the System Settings dialog will pop up. On the General tab, under the section Set blocked file extensions for attachments, there is a long list of file extensions that are blocked from being uploaded to notes (annotations).

System Settings - Attachment Extensions

Find the js extension and remove it (include the semicolon that follows it – js;). Remember that it appears after the jar extension. Press OK to Save your changes.

Once you have made the change, you should be able to upload your JavaScript file again to the Web Files entity. If you created the Web File record previously simple update it and add the new attachments. You should keep only a single attachment per web file to simplify things.

After the Web File has been added, you can add the files to your code, by modifying the Tracking Code Content Snippet. Navigate to Content Snippets entity, and Find Tracking Code record. Open the record, and in the Value enter the following code:

<script src="/folder/filename.js"></script>

At this point, the js file should be accessible on the portal, and the functions of the js file can be accessed from within web templates or web pages.

The post JavaScript Web Files in CRM Portals appeared first on Aric Levin's Digital Transformation Blog.

]]>
Creating Web Resource with a Lookup Control http://aric.isite.dev/dynamics/post/creating-web-resource-with-lookup-control/ Sun, 11 Mar 2018 05:45:00 +0000 https://aric.isite.dev/index.php/2018/03/11/creating-web-resource-with-a-lookup-control/ A few years ago we had some requirements where we needed to pop up an html web resource where users could select a value from a related entity (in a manner similar to a lookup control). We originally developed this as a regular drop down and retrieving the values using Rest messages, but later on decided to change this and have the user click on a "lookup" style control inside the web resource to get this working.

The post Creating Web Resource with a Lookup Control appeared first on Aric Levin's Digital Transformation Blog.

]]>
A few years ago we had some requirements where we needed to pop up an html web resource where users could select a value from a related entity (in a manner similar to a lookup control). We originally developed this as a regular drop down and retrieving the values using Rest messages, but later on decided to change this and have the user click on a “lookup” style control inside the web resource to get this working.

In the recent weeks I saw a few posts in the community of people asking how to do this. In this article I will demostrate the logic of implementing this.

The first thing is we are going to need to create an Entity for the purpose of this solution, and add a button to the entity form to popup the message. We created an entity called Area Type and added a button to the entity called Change Owner using Ribbon Workbench. The button itself will call a JavaScript function that is called changeOwner.

Area Type and Change Owner

The Change Owner function contains the code snippet below which calls the dialog and also processes the changes from the callback of the dialog. I am using the Xrm.Internal.openDialog to open the web resource, and yes “I am aware that this is not supported”, but this is still working in Dynamics 365 v9, and you can feel free to modify the calling of the web resource. Once the button is clicked the popup window will open.

function changeOwner()
{
    var DialogOption = new Xrm.DialogOptions;
    DialogOption.width = 600; DialogOption.height = 550;
    Xrm.Internal.openDialog("/WebResources/xrm_dialog.htm", DialogOption, null, null, changeOwnerCallback);
}

function changeOwnerCallback(returnValue) {
    if (returnValue != null) {
		
		// If need to return multiple values;
	    var returnValues = returnValue.split(':');
        var ownerId = returnValues[0];
        var ownerName = returnValues[1];
		// Perform Actions on return values
		setLookup("ownerid", ownerId, ownerName, "systemuser");
    }
}

Since this was used in a previous project that we had, there is a text box for comments and the lookup control that will need to be popped up.

Web Resource/Dialog Window with Lookup Control

When the user clicks on the lookup icon in the dialog window, an additional Lookup Record window will pop up on top of the original dialog window.

function openOther() {

	var imgButton = $("#crmOtherLookup").attr("src");
	if (imgButton == "/_imgs/btn_off_lookup.png") {


		var objectTypeCode = 8;
		var url = "/_controls/lookup/lookupinfo.aspx?AllowFilterOff=0&DefaultType=8&DefaultViewId=%7bE88CA999-0B16-4AE9-B6A9-9EDC840D42D8%7d&DisableQuickFind=0&DisableViewPicker=1&IsInlineMultiLookup=0&LookupStyle=single&ShowNewButton=0&ShowPropButton=0&browse=false&dType=1&mrsh=false&objecttypes=8";

		var DialogOption = new Xrm.DialogOptions;
		DialogOption.width = 500; DialogOption.height = 400;
		Xrm.Internal.openDialog(url, DialogOption, null, null, openOtherCallback);
	}
	else {
		alert("Please select the radio button to the left of Other in order to select a system user.");
	}
}

function openOtherCallback(returnValue) {
	if (returnValue != null)
	{
		if (returnValue.items.length > 0) {
			var guid = returnValue.items[0].id;
			var name = returnValue.items[0].name;
			$("#crmOtherLookup_ledit").val(name); // text 
			$("#crmOtherLookup_lId").val(guid); // hidden                    
		}
	}
}

Select User

Select the User record that you want, and click the Add button to populate the values from the lookup window (as shown below):

Populated Lookup Control

Finally press the OK button to have the data from the lookup returned to the form, and update the original lookup field that is on the form (not required).

function applyChanges() {
	var returnValue = "";

	var message = $("#crmRoutingMessage").val();
	if (message != '') {

		var otherId = $("#crmOtherLookup_lId").val();
		var otherName = $("#crmOtherLookup_ledit").val();
		returnValue = otherId + ':' + otherName; 

		Mscrm.Utilities.setReturnValue(returnValue);
		closeWindow(true);
	}
	else
	{
		alert('Please enter a Routing Message');
	}
}

Final Result

The full source code and a sample solution (containing the test entity, customizations and all web resource files) is available here on github: https://github.com/ariclevin/WebResourceLookup

The post Creating Web Resource with a Lookup Control appeared first on Aric Levin's Digital Transformation Blog.

]]>
Disable Delete icon on subgrid http://aric.isite.dev/dynamics/post/disable-delete-icon-on-subgrid/ Mon, 05 Feb 2018 04:36:00 +0000 https://aric.isite.dev/index.php/2018/02/05/disable-delete-icon-on-subgrid/ We recently had a requirement that users wanted to hide the Delete icon on the subgrid to prevent users from deleting records. Of course it is possible to remove the Delete privilege on the entity in Security Roles, but the requirement was different. When the status of the parent record was Active, the Delete operation should be allowed, however when the parent record was Inactive, the Delete operation should not be allowed.

The post Disable Delete icon on subgrid appeared first on Aric Levin's Digital Transformation Blog.

]]>
We recently had a requirement that users wanted to hide the Delete icon on the subgrid to prevent users from deleting records. Of course it is possible to remove the Delete privilege on the entity in Security Roles, but the requirement was different. When the status of the parent record was Active, the Delete operation should be allowed, however when the parent record was Inactive, the Delete operation should not be allowed.

The first thing that of course we are aware of, is that if a record is inactive, the subgrid Delete functionality is still available, as shown in the image below.

Normal Subgrid Delete button visible on Inactive parent record

We then went ahead and hid the button.

Hide Delete button

This caused the expected result that the Delete button no longer showed up on the subgrid, but of course it did not show up for both enabled or disabled parent records.

Delete button does not show up.

Finally we came up with a resolution, the did not hide the delete button, but allowed us to prevent the Delete action for the disabled records. We went ahead and unhid the Delete button, and then selected the Customize Command option. This populated the Mscrm.DeleteSelectedRecord command under the Command in Ribbon Workbench. We added another Enable rule called RestrictDeleteFromSubgrid, as shown below:

Restrict Delete from Subgrid Command

The result of this action is that the Delete button will still be enabled on the subgrid (and associated grid), but when the Delete button is pressed, if the parent record is Inactive, a message will pop up displaying the you cannot delete a record if the parent record is Inactive.

Delete button clicked on Inactive record

The code on the RestrictDeleteFromSubgrid JavaScript method is very simple. It checks the status of the form and returns true or false whether the record can be deleted.

function RestrictDeleteFromSubgrid() {
    var formType = Xrm.Page.ui.getFormType();
    if (formType == 3 || formType == 4)
    {
        alert('You cannot remove an Inactive record');
        return false;
    } 
    else 
    {
        return true;
    }
}

The post Disable Delete icon on subgrid appeared first on Aric Levin's Digital Transformation Blog.

]]>
Migrating your Client API to Dynamics 365 CE (v9) – Part III – Controls http://aric.isite.dev/dynamics/post/migrate-client-api-to-dynamics-365-ce-iii/ Fri, 19 Jan 2018 18:15:00 +0000 https://aric.isite.dev/index.php/2018/01/19/migrating-your-client-api-to-dynamics-365-ce-v9-part-iii-controls/ In this third post of the series, we will review the changes between the properties and methods of the control based on the getControls Collection or the Xrm.Page.getControl method. The tables below will show the base methods of the control, as well as specific method for specific control types.

The post Migrating your Client API to Dynamics 365 CE (v9) – Part III – Controls appeared first on Aric Levin's Digital Transformation Blog.

]]>
In this third post of the series, we will review the changes between the properties and methods of the control based on the getControls Collection or the Xrm.Page.getControl method. The tables below will show the base methods of the control, as well as specific method for specific control types.

Xrm.Page.getControl

The following table show the most commonly used properties and methods in the Dynamics CRM Xrm.Page.getControl and Xrm.Page.data.entity.control classes, and the method calls in Dynamics 365 v9.

Dynamics CRM Client API Dynamics 365 v9 Client API
Xrm.Page.getControl(attr).getControlType formContext.getControl(attr).getControlType
Xrm.Page.getControl(attr).getAttributes formContext.getControl(attr).getAttribute
Xrm.Page.getControl(attr).getDisabled formContext.getControl(attr).getDisabled
Xrm.Page.getControl(attr).getLabel formContext.getControl(attr).getLabel
Xrm.Page.getControl(attr).getName formContext.getControl(attr).getName
Xrm.Page.getControl(attr).getParent formContext.getControl(attr).getParent
Xrm.Page.getControl(attr).setDisabled formContext.getControl(attr).setDisabled
Xrm.Page.getControl(attr).setFocus formContext.getControl(attr).setFocus
Xrm.Page.getControl(attr).setLabel formContext.getControl(attr).setLabel
Xrm.Page.getControl(attr).setVisible formContext.getControl(attr).setVisible
Xrm.Page.getControl(attr).setNotification formContext.getControl(attr).setNotification
Xrm.Page.getControl(attr).clearNotification formContext.getControl(attr).clearNotification

The next table will show additional properties and methods of the Xrm.Page.getAttribute and Xrm.Page.data.entity.attribute classes, that are specific to particular data types.

Attribute Type Dynamics CRM Client API Dynamics 365 v9 Client API
DateTime Xrm.Page.getControl(attr).getShowTime formContext.getControl(attr).getShowTime
DateTime Xrm.Page.getControl(attr).setShowTime formContext.getControl(attr).setShowTime
Lookup Xrm.Page.getControl(attr).addCustomFilter formContext.getControl(attr).addCustomFilter
Lookup Xrm.Page.getControl(attr).addCustomView formContext.getControl(attr).addCustomView
Lookup Xrm.Page.getControl(attr).addPreSearch formContext.getControl(attr).addPreSearch
Lookup Xrm.Page.getControl(attr).getDefaultView formContext.getControl(attr).getDefaultView
Lookup Xrm.Page.getControl(attr).removePreSearch formContext.getControl(attr).removePreSearch
Lookup Xrm.Page.getControl(attr).setDefaultView formContext.getControl(attr).setDefaultView
OptionSet Xrm.Page.getControl(attr).addOption formContext.getControl(attr).addOption
OptionSet Xrm.Page.getControl(attr).clearOptions formContext.getControl(attr).clearOptions
OptionSet Xrm.Page.getControl(attr).removeOption formContext.getControl(attr).removeOption
Web Resouce/IFrame Xrm.Page.getControl(attr).getData formContext.getControl(attr).getData
Web Resouce/IFrame Xrm.Page.getControl(attr).getInitialUrl formContext.getControl(attr).getInitialUrl
Web Resouce/IFrame Xrm.Page.getControl(attr).getObject formContext.getControl(attr).getObject
Web Resouce/IFrame Xrm.Page.getControl(attr).getSrc formContext.getControl(attr).getSrc
Web Resouce/IFrame Xrm.Page.getControl(attr).setData formContext.getControl(attr).setData
Web Resouce/IFrame Xrm.Page.getControl(attr).setSrc formContext.getControl(attr).setSrc

Click here to access the reference to the formContext.getControl properties and methods in Dynamics 365 CE.

The post Migrating your Client API to Dynamics 365 CE (v9) – Part III – Controls appeared first on Aric Levin's Digital Transformation Blog.

]]>