Get current entity logical name and record ID in CRM Portal forms using JavaScript

Use the following JavaScript to retrieve the current entity logical name and record ID in CRM Portal forms:

For Entity Form:

Entity logical name: $(“#EntityFormControl_EntityFormView_EntityName”).val()

Record ID: $(“#EntityFormControl_EntityFormView_EntityID”).val()

For Web Form Step:

Entity logical name: $(“#EntityFormView_EntityName”).val()

Record ID: $(“#EntityFormView_EntityID”).val()

Tested on xRM Portal CE.

Advertisements
Posted in CRM, CRM Portal | Leave a comment

Leveraging built-in services in CRM Portal

On a recent project we decided to build a custom entity list using jQuery DataTables as the OOTB entity list did not meet our requirements. As part of this we needed to build a delete list item functionality that should work similarly to the OOTB counterpart. To achieve this we needed a service that we can call from JavaScript to delete a given record (i.e. a list item).

After some investigation, we discovered that there are several built-in services in CRM Portal that we can invoke using JavaScript. These are the same services used by the OOTB components. Using these services means that we did not have to develop/deploy a custom service to support our custom UI component. It also means that the entity permissions we have configured in CRM are automatically honoured, which is a huge bonus.

In this post I will write about these built-in services, in particular the ‘delete list item’ service, and how you can call them from your JavaScript.

Important: this investigation was done against xRM Portals Community Edition (i.e. the on-prem version of CRM Portal). The findings may or may not apply to CRM Portal on cloud that is hosted by Microsoft. References to source code refer to the source code of XRM Portals Community Edition, which can be downloaded from https://github.com/Adoxio/xRM-Portals-Community-Edition).

The built-in ‘delete list item’ service

This service lives at [portalUrl]/_services/entity-grid-delete/[recordId to delete]. You can invoke this service by performing a POST to this URL. The service accepts an EntityReference parameter to identify the record to delete, and it expects to find this in the POST’s body as JSON, e.g.:

{
"Id":"[guid]",
"LogicalName":"new_myentity"
}

This particular service route maps to the Delete method of the class Site.Areas.Portal.Controllers.EntityGridController, which is defined in the file \Areas\Portal\Controllers\EntityGridController.cs of the Portal project.

A side note: other built-in services

The route mapping of the ‘delete list item’ service is defined in the RegisterArea method of the Site.Areas.Portal.PortalAreaRegistration class, which is defined in the file \Areas\Portal\PortalAreaRegistration.cs of the Portal project.

Reviewing this file quickly shows that there are actually many more built-in services in CRM Portal that you should be able to invoke from JavaScript. Below is a screenshot to illustrate:

Invoking the ‘delete list item’ service from JavaScript

There is a slight complication in invoking this service from JS, and that is it has anti-forgery enabled. This means that our requests to the service must contain a __RequestVerificationToken header with a valid token value.

Fortunately CRM Portal has a built-in JavaScript object that helps us with this. Every page in the Portal automatically includes a reference to the JS file \js\antiforgerytoken.js. This JS defines a global object called shell, which has a method called ajaxSafePost.

ajaxSafePost is essentially a wrapper around jQuery’s ajax method, and is responsible for ensuring the request contains a valid __RequestVerificationToken header before sending it off to the server. This method returns a promise object as it may relies on an async request to retrieve the token (more details later).

Below is an example of how you could use the ajaxSafePost method to invoke the ‘delete list item’ service.

var recordId = "4ca2e5d1-d0fe-4700-b383-59cee9890392";

var entityReference = {};
entityReference.LogicalName = "new_myentity";
entityReference.Id = "4ca2e5d1-d0fe-4700-b383-59cee9890392";

var ajaxPromise = shell.ajaxSafePost({
	type: "POST",
	contentType: "application/json",
	url: "/_services/entity-grid-delete/" + recordId,
	data: JSON.stringify(entityReference)
});

ajaxPromise.done(function () {
	alert("Record deleted!");
});

ajaxPromise.fail(function () {
	alert("Something has gone wrong...");
});

How does ajaxSafePost retrieve the token?

This method first attempts to retrieve the token from an input field on the page. If one is not found, it makes an async request to the URL [portalUrl]/_layout/tokenhtml to request a token.

So… is this supported?

Strictly speaking, since it’s not documented (as far as I’m aware), I’d say this is unsupported. Since the service is fairly loosely coupled to our code however, switching over to a custom service when the need arises should not incur much overhead. You however need to make good judgement based on your own scenario.

So there you have it…

The built-in services are hidden gems that could really streamline your customisation efforts. There are built-in support facilities that make invoking them relatively easy. You however need to consider your scenario carefully as these are not documented, and therefore may change without notice.

I have not had a chance to test whether they also work on CRM Portal online. That is a to-do for the near future.

 

Posted in Adxstudio, CRM, CRM Portal | 4 Comments

Changing Quick Find to perform ‘contains’ search in CRM

OOTB CRM Quick Find automatically appends a wildcard character (*) to the end of your query, and therefore effectively performs a ‘begins with’ search. If you searched for ‘Canberra’ for example, you’d find ‘Canberra Hospital’, ‘Canberra College’, etc., but you won’t find ‘The Awesome Pub of Canberra’.

To perform a ‘contains’ search, you would need to explicitly include the wildcard character at the start of your query, e.g. ‘*Canberra’. I suspect it was done this way for performance reasons. This however may not be the best user experience in some scenarios.

I have added a new feature to the Enhanced Quick Find solution that allows you to automatically add the wildcard character to the start of users’ Quick Find queries. This turns the query to ‘*Canberra*’ for example, and effectively becomes a ‘contains’ search.

What is this Enhanced Quick Find solution?

This is a CRM solution that I have previously developed that allows you to perform Advanced Find-like queries using text in Quick Find. Essentially it allows you to configure an XML that defines how the query should be interpreted and transformed prior to fetching the results. You can read more about this solution here.

So how do I enable this ‘contains’ search thing for Quick Find?

A new attribute, namely prependWildcardToQueryText, has been added to the QueryTransformation element in the configuration schema. Set this attribute to true to have the wildcard character appended to all Quick Find queries for the target entity. Below is a simple example:

<QuickFindConfiguration>
	<QueryTransformation prependWildcardToQueryText='true'/>
</QuickFindConfiguration>

Where will this work?

This will work in Quick Find for all the entities that you have created a Quick Find Configuration entity for. This will also work when searching within the lookup field/dialog for those entities. This however does not work with the multi-entity search.

Is there any performance impact?

The solution intercepts and transforms search queries. This in its own should have minimal or no performance impact. Depending on the size of your data, and the number of Quick Find columns you have configured for a given entity however, ‘contains’ searches may have some performance impacts comparing to ‘begins with’ searches. You therefore should consider your scenario and deploy this feature selectively.

Download

You can download the solution ZIP here:

Posted in CRM | Leave a comment

Querying many-to-many relationship in CRM Portal using JavaScript

How do you programmatically query an N:N relationship in CRM Portal using JavaScript without developing additional services?

You can do this with FetchXml and Liquid, and a small hack. Here is a good walkthrough of using FetchXml in Liquid to provide a data service in CRM Portal. I will explain how to adapt this to work for N:N relationships and the hack that would be required.

One thing you should understand is that when you create an N:N relationship in CRM, a “Relationship Entity” is also created behind the scene. This relationship entity is the “joining” entity that you would typically find in a normalised database. You can specify the name of this relationship entity when creating the N:N relationship in CRM as shown below.

To query an N:N relationship in CRM Portal, we will use FetchXml/Liquid to query this relationship entity instead of the actual entities on either end of the relationship.

For example, say I have a Degree and a Subject entities with an N:N relationship, and I want to query all the Subjects for a given Degree (retrieving Subject Name and Subject Code). First thing first, the FetchXml would look as follow (bnh_degree_bnh_subject is the name of my relationship entity):


  
    
      
    
    
      
      
    
  

If you use this FetchXml in Liquid as described by the link earlier on in the post however, you will not get any result. This is because FetchXml in Liquid (as of v8) uses Entity Permission. You need to grant Entity Permission for the relationship entity. (I would guess that Entity Permission for Degree and Subject would also be required – although I have not tested it without).

Here is where the small hack comes in. OOTB you cannot create Entity Permission for a relationship entity. To workaround this:

  1. Create an Entity Permission for a random entity. Set the Scope to Global and grant it the Read privilege.
  2. Add appropriate Web Roles to it.
  3. Create a workflow to update the field adx_entitylogicalname of this Entity Permission record to the name of the relationship entity (bnh_degree_bnh_subject in this example).
  4. UPDATE 02/01/2018: Unfortunately you cannot use a workflow to update the field above. This is because the field is read-only on the form, and therefore the workflow editor does not allow you to set a value for it. Some options to workaround this:
    1. Write a simple C# script
    2. Use a Chrome extension to effectively hack the form and edit the field. Below are some options I would recommend:
      1. Dynamics CRM Power Pane
      2. Level Up

That’s it! Your FetchXml/Liquid should now return results!

Is this hack supported?

Strictly speaking, probably not.

Is it likely to stop working in future updates to CRM and CRM Portal?

I’d say no, but you need to make your own judgement.

So in conclusion…

You can query an N:N relationship in CRM Portal using JavaScript without developing additional services. You just need to query the relationship entity, and apply a small hack to grant the required permission.

 

Posted in Adxstudio, CRM, CRM Portal | 1 Comment

Reduce code duplication between Insert/Edit forms in CRM Portal with CRMQuickDeploy

CRM Portal is an interesting product, in a good way. One thing it doesn’t do very well however, is that a single Entity Form (or Web Form) cannot be used to handle both inserting and editing records for an entity.

This means you’d need a separate form to handle insert, and a separate form to handle edit – even if the two functionalities should work exactly the same way. This creates an issue as you’d need to duplicate any configuration and code associated with this form.

For a while now CRMQuickDeploy has been allowing you to work with code (JS, CSS, HTML, Liquid) for Portal artefacts in Visual Studio. A new feature has now been added to version 3.3 of this tool to help address the code duplication issue in this particular scenario.

New “link item” in CRMQuickDeploy 3.3

As a quick overview, you can now create a “link item” in your Visual Studio project that points to another item. When this “link item” is deployed, the content of the referenced item is used to update the Portal artefact in CRM.

Sound like “Add as Link” that comes OOTB with Visual Studio? Well, yes.. it is pretty much the same idea. “Add as Link” however does not work within the same project OOTB.

Creating “link item” and referencing source item

You can create a “link item” by creating a normal item for a Portal artefact like you normally would, and then append the “.link” extension to it. You specify the source item within the content of the link item using the INI file format. The INI key is sourceItemPath, and the value is the project-relative path to the source item.

So for example, a link item that holds the JavaScript for an Entity Form could be:

Form1 – Create.js.link

To specify its source content (in this case Form1 – Update.js), specify the following in the content of the link item:

sourceItemPath=PortalEntityForms\Form1 – Update.js

A more detailed example

Let say you have two Entity Forms to create and update Application with the following names in CRM:

  • Application Form – Create
  • Application Form – Update

Because OOTB a Web Page can only host one Entity Form, you also have two Web Pages with the following names in CRM:

  • Application Page – Create
  • Application Page – Update

Imagine you have some JavaScript attached to the Entity Form, and also some HTML/Liquid attached to the Web Page. Because the two functionalities (Create and Update Application) should function the same way, you’d need to duplicate the JavaScript and HTML/Liquid across these Entity Forms and Web Pages.

Using link items, your Visual Studio project would look like this:

Application Form – Create.js would contain the actual JavaScript to be deployed as normal.

Application Form – Update.js.link would contain:

sourceItemPath=PortalEntityForms\Application Form – Create.js

When a deployment is triggered on Application Form – Update.js.link, the target artefact will be deduced from the file name of the link item, in this case Application Form – Update. The content however will be drawn from the referenced source item, i.e. Application Form – Create.js.

Likewise, Application Page – Create.html would contain the actual HTML/Liquid to be deployed for the Web Pages.

Application Page – Update.js.link would contain:

sourceItemPath=PortalWebPages\Application Page – Create.html

Which artefact types are supported with link item?

The following artefact types are supported:

  • Web Page (HTML/Liquid, CSS, JS)
  • Entity Form (JS)
  • Web Form Step (JS)

Download CRMQuickDeploy

You can download CRMQuickDeploy from the Visual Studio Marketplace.

 

Posted in Adxstudio, CRM, CRM Portal, CRMQuickDeploy | Leave a comment

Fixing issues with migrating Adxstudio/CRM Portal v8 using Configuration Migration tool

The Configuration Migration tool does a pretty good job at migrating Adxstudio/CRM Portal configuration in v7. There are some changes in Portal v8 however that are causing issues with this tool. This is true at least in the case for OnPrem (Community Edition). For Online, I have not tested but suspect that the behaviour would be the same.

Issue #1 – Value of Partial URL field of the Home Page of your website can only be /. Any other value will lead to issues in your Portal.

The import log produces the above error when processing the adx_webpage entity. In Portal v8 there is a new validation that checks the Partial URL of web pages that the code determines to be a homepage. The code determines a web page as a homepage when it does not have a value in the Parent Page field.

As the Parent Page field is a reference field to the same entity (i.e. adx_webpage), I suspect the Configuration Migration tool first saves the record without a value for this field, and then fixes it up in a 2nd pass. Creating the web page without a Parent Page however causes the validation to see it as a homepage, and hence the Partial URL fails the validation.

This validation occurs in a plugin. To workaround this issue we need to disable the following plugin steps prior to running the import:

  1. Plugin assembly: Adxstudio.Xrm.Plugins
    • Plugin: Adxstudio.Xrm.Plugins.MultiLanguage.WebPageValidationPlugin
      1. Step: Adxstudio.Xrm.Plugins.MultiLanguage.WebPageValidationPlugin: Update of adx_webpage
      2. Step: Adxstudio.Xrm.Plugins.MultiLanguage.WebPageValidationPlugin: Create of adx_webpage

Issue #2 – Unexpected exception from plug-in (Execute): Adxstudio.Xrm.Plugins.MultiLanguage.WebPageUpdatePlugin: System.NullReferenceException: Object reference not set to an instance of an object

The “Object reference not set to an instance of an object” is probably the bane of all developers as it is often quite hard to pinpoint the root cause without debugging. Unfortunately this is the case in this scenario, and I don’t quite know the root cause of this issue. You can however workaround it by disabling the following plugin step prior to running the import:

  1. Plugin assembly: Adxstudio.Xrm.Plugins
    • Plugin: Adxstudio.Xrm.Plugins.MultiLanguage.WebPageUpdatePlugin
      1. Step: Adxstudio.Xrm.Plugins.MultiLanguage.WebPageUpdatePlugin: Update of adx_webpage

Issue #3 – Duplicate web pages

One of the changes in Portal v8 is the introduction of localised web pages. When you create a web page (Portal refers to this as a Root page), a separate localised web page is automatically created for the corresponding root page and is linked to it. So if you create a web page called Dashboard for example, a second web page would be automatically created for you. This page, also named Dashboard by default, would hold the localised content of the Dashboard page you explicitly created.

The localised page is created by a plugin, which means that you’d end up with duplicate web pages when importing data via the Configuration Migration tool. This is because the migration tool would import both the root page and the localised page, and the plugin would trigger on the creation of the root page, which in turns would create a duplicate localised page.

To workaround this issue we need to disable the following plugin step prior to running the import:

  1. Plugin assembly: Adxstudio.Xrm.Plugins
    • Plugin: Adxstudio.Xrm.Plugins.MultiLanguage.WebPagePlugin
      1. Step: Adxstudio.Xrm.Plugins.MultiLanguage.WebPagePlugin: Create of adx_webpage

(Potential) Issue #4

OK, this is not one that I have actually encountered, but one that I just thought about while describing issue #3 above.

When the localised web page is automatically created, it has the same name as the root page by default. The Configuration Migration tool however uses the primary name field of an entity to determine uniqueness while importing if no duplicate detection (uniqueness) condition is specified for that entity. I wonder if this could lead to the migration tool applying updates to incorrect records during the import?

In our implementation we always append the language name to the name of localised web pages, e.g. Dashboard is the root page and Dashboard (English) is the localised page. We therefore have not encountered this issue, but it is something that we should check.

 

So in summary…

When migrating configuration for Portal v8 you should disable four plugin steps prior to the import (and remember to enable them again afterward).

I would suggest always append the language name to the name of localised web pages to avoid confusion for yourself, and possibly also for the Configuration Migration tool.

Posted in CRM, CRM Portal | 4 Comments

Auto deploy on save from Visual Studio for Adxstudio Portal artefacts in new version of CRMQuickDeploy

I have released a new version of CRMQuickDeploy with the following improvements:

  • Option to auto deploy on save for Portal artefacts
  • Ability to specify MIME Type and Display Order for Portal web files

Auto deploy on save for Portal artefacts

You can now enable this option to have Visual Studio automatically deploys a Portal artefact file when you save it from the code editor.

This option is disabled by default, but you can enable it by going to Tools \ Options \ CRMQuickDeploy.

Note that this option is only applicable to Portal artefacts.

Ability to specify MIME Type and Display Order for Portal web files

Configuration settings for web files are defined in the DeploymentSettings.xml file. This file now allows you to specify MIME Type and Display Order for web files. Below is an annotated example XML.

<?xml version="1.0" encoding="utf-8" ?>
<DeploymentSettings>

	<!-- Define a collection of items that will be deployed as 
Web Files and associated with a particular website. -->
	<WebFiles>

		<!-- Separately define each local files to be 
deployed as Web Files.

localPath: Required. Relative to the root PortalWebFiles 
folder of the project. Value should not start with '\'. 

targetName: Optional. The name of the web file once deployed.
Default to the same value as partialUrl. 

parentPage: Required. The name of the parent web page in CRM.

partialUrl: Optional. The partial URL of the web file once 
deployed. Default to the file name of the Visual Studio item 
with all spaces removed. 

mimeType: Optional. The MIME type of the file. If not 
specified, the MIME type will be determined based on the 
file extension. 

displayOrder: Optional. The display order of the file. 
Must be an integer. 

The file extension to MIME type mapping is as follow: 

txt: text/plain 
css: txt/css 
js: txt/javascript 
htm, html: text/html 
gif: image/gif 
jpg, jpeg: image/jpeg 
png: image/png 
svg: image/svg+xml 
ico: image/x-icon 
xml: application/xml 
json: application/json 
all other extensions: application/octet-stream -->
		<File localPath="common.js" parentPage="Home" />
		<File localPath="scripts\script1.js" parentPage="Home" />
		<File localPath="styles\styles.css" targetName="Portal Styles" parentPage="Home" partialUrl="styles.css" mimeType="text/css" displayOrder="1"/>

		<!-- Define local files to be deployed as Web files 
using the folder approach. When this approach is used, default 
value will be used for targetName and partialUrl for each item 
under the folder. mimeType for each item will be determined based 
on the file extension, unless the item is specified in the 
folder's MimeTypeOverride child element. A Folder element only 
covers the files that are found directly underneath that folder. 
Separate Folder elements must be defined for each sub-folders. 

localPath: Required. Relative to the root PortalWebFiles folder 
of the project. Value should not start with '\'. To refer to 
files directly underneath the root PortalWebFiles, specify an 
empty string for this attribute, i.e. localPath="". 

parentPage: Required. The name of the parent web page in CRM 
that will be applied to all items under this folder. 

targetNamePrefix: Optional. The specified value will be added 
to the targetName for all items under this folder. -->
		<Folder localPath="scripts" parentPage="Home" />
		<Folder localPath="styles" parentPage="Home" targetNamePrefix="My Portal - ">
			
			<!-- Define MIME type for selected items underneath 
this folder. This element is optional. -->
			<MimeTypeOverride>
			
				<!-- Define MIME type for items underneath 
this folder. The MIME type for an item will be automatically 
determined based on the file extension if the item is not 
defined here. 

name: Required. The filename of the item. Value should not start 
with '\'. 

mimeType: Required. The MIME type for the item. -->
				<File name="styles.css" mimeType="text/css" />
			</MimeTypeOverride>
			
			<!-- Define display order for selected items 
underneath the folder. This element is optional. -->
			<DisplayOrderOverride>
			
				<!-- Define display order for items underneath 
this folder. The display order for an item will be blank if the 
item is not defined here. 

name: Required. The filename of the item. Value should not 
start with '\'. 

displayOrder: Required. Must be an integer. -->
				<File name="styles.css" displayOrder="1" />
			</DisplayOrderOverride>
		</Folder>
	</WebFiles>
</DeploymentSettings>

Download

You can download this extension from the Visual Studio Marketplace.

I hope you find this addition useful!

Posted in Adxstudio, CRM, CRM Portal, CRMQuickDeploy | Leave a comment