Automatic Portal (xRM CE) cache refresh with CRMQuickDeploy

This feature of CRMQuickDeploy is for xRM Portal CE only and does not work for D365 Portal (on cloud). The context of this post therefore is xRM Portal CE only.

When updating portal artefacts (such as web templates, web files, etc.), you need to refresh the portal cache for the changes to be picked up by the portal. During development, this means either triggering a request to cache.axd via a bookmarklet, or clicking the Clear Cache button on the /_services/about page of the portal.

While CRMQuickDeploy allows you to quickly deploy portal artefacts to CRM from Visual Studio, you still needed to manually trigger a portal cache refresh after each deployment. Not only this means extra work for you as the developer, it also has an impact on your productivity as the portal typically takes 3-8 seconds to recover after each cache refresh.

I am pleased to announce that a new feature has been added to CRMQuickDeploy to address these pain points. With CRMQuickDeploy 3.7, you can now specify the location of cache.axd for your portal as a solution property in Visual Studio. Each time a portal artefact is deployed, CRMQuickDeploy will automatically fire a request to the specified cache.axd to refresh the cache. Unlike the bookmarklet or the Clear Cache button however, which invalidate the entire portal cache, CRMQuickDeploy will invalidate only the specific CRM records that were updated by your deployment. This therefore significantly reduces the page load time in portal following a deployment.

Do note however that your browser may cache JS/CSS/HTML rendered by the portal, even if server-side cache has been refreshed by CRMQuickDeploy. You therefore may need to press Ctrl + F5 (as oppose to F5), or disable browser cache during development, to reload the page with all changes.

OK, so to make use of this feature, you will need to do two things:

  1. Enable cache.axd for your targeted portal
  2. Specify the cache.axd URL in Visual Studio

Enabling cache.axd for your portal

cache.axd is a special HTTP handler that can be used to invalidate server-side cache for your portal. This HTTP handler may not be enabled for your portal by default. To check if this handler is enabled, open the web.config for your portal and search for a line similar to this:

<add name="CacheInvalidation" verb="*" path="Cache.axd" preCondition="integratedMode" type="Adxstudio.Xrm.Web.Handlers.CacheInvalidationHandler, Adxstudio.Xrm"/>

If the above is not found, you can add it to the <handlers> section underneath <system.webServer>, for example:

<configuration>
   <system.webServer>
      <handlers>
         <add name="CacheInvalidation" verb="*" path="Cache.axd" preCondition="integratedMode" type="Adxstudio.Xrm.Web.Handlers.CacheInvalidationHandler, Adxstudio.Xrm"/>
      </handlers>
   </system.webServer>
</configuration>

Important: Once enabled, this HTTP handler can be invoked by anyone that has access to your portal website, and can be used maliciously. You should enable this handler only in the development environment.

Specify cache.axd URL in Visual Studio

A new solution property, namely Portal Cache.axd Invalidation Handler URL, has been added to the solution node in Visual Studio. Specify the full URL to cache.axd for your portal website in this property, e.g. http://myportal/cache.axd if your website is at the root path, or http://myserver/portal/cache.axd if your website is not at the root path.

Download CRMQuickDeploy

You can download CRMQuickDeploy here from the Visual Studio Marketplace.

Wait… what about D365 Portal (on cloud)?

D365 Portal (on cloud) uses a completely different approach to invalidate portal cache. Unfortunately it does not seem to expose any hooks for us to be able to trigger the cache refresh programmatically. This means that unfortunately it is not possible to implement the same feature for D365 Portal at this stage.

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

Loading solution user options with AsyncPackage (Visual Studio extension)

User-specific options at the solution level are stored in the .suo file. Back when it was the norm to have your extension inherits from the Package class, and therefore loaded synchronously by Visual Studio, you can have these options loaded by overriding the OnLoadOptions method. This method is automatically called by the platform for each option key that you previously specified (via Package.AddOptionKey). The OnLoadOptions method would look something like the below.

protected override void OnLoadOptions(string key, Stream stream)
{
	if (key.Equals("MyOption", StringComparison.InvariantCultureIgnoreCase))
	{
		_myOption = new StreamReader(stream).ReadToEnd();
	}
	else
	{
		base.OnLoadOptions(key, stream);
	}
}

Since synchronous loading of packages has been deprecated, your extension now should inherit from AsyncPackage (and have AllowsBackgroundLoading enabled and PackageAutoLoadFlags set to BackgroundLoad if applicable). When your package is loaded asynchronously in the background however, it may not have been loaded when the platform processes the .suo file, and therefore OnLoadOptions may not be called on your package, which results in user-specific options not being restored for the user.

A solution for this is to initiate the loading of solution user options in your own code (rather than relying on the platform). You can use the IVsSolutionPersistence.LoadPackageUserOpts method and pass in the option key to do this. Here are the steps:

  1. Make your package class implements IVsPersistSolutionOpts.
public sealed class MyExtensionPackage : AsyncPackage, IVsPersistSolutionOpts
  1. Add the following code to the package’s InitializeAsync method:
var solutionPersistenceService = GetService(typeof(IVsSolutionPersistence)) as IVsSolutionPersistence;

solutionPersistenceService.LoadPackageUserOpts(this, "MyOption");
solutionPersistenceService.LoadPackageUserOpts(this, "MyOption2");
//Other option keys

That’s it. Your OnLoadOptions method does not need to change.

Note that this will change the UX of your package slightly. When the user opens a solution in Visual Studio, they would be able to start using Visual Studio and work with the solution before your package finishes loading. This means that for a few seconds, the user options may not have been loaded, and this may confuse some users.

Posted in Visual Studio Extension | Leave a comment

Outputting clean HTML with Liquid in Dynamics 365 Portal

Recently a colleague taught me a trick that I did not know about Liquid in Dynamics 365 Portal: use the {%- -%} syntax (as oppose to {% %}) to keep the HTML output clean. I don’t think this is documented in Microsoft’s documentation, but it is described here: https://shopify.github.io/liquid/basics/whitespace/. I have tested this an it works on both OnPrem and OnCloud portals.

In most cases I think you’d want to use the {%- -%} syntax to keep your HTML clean and compact. To illustrate the point, let say I have the code below to list all the accounts, with a different CSS class for odd and even items.

Without the {%- -%} syntax, this produces the following HTML:

As you can see, there are multiple blank lines between each item. While this does not impact how the browser renders the page, it does make the page’s source messy and increase the page’s download size.

Here is what the output look like when the {%- -%} is used:

As you can see, this is a lot cleaner.

The Shopify documentation also mentioned the {{- -}} syntax (as oppose to {{ }}). I have not tried this, but if it does work, it probably would be something you’d want to use more often than not too.

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

View actual XML of FetchXML query in Dynamics 365 Portal

In Dynamics 365 Portal you can write FetchXML query in Liquid using the fetchxml tag. As this query can be dynamically constructed, there may be scenarios where you will need to debug it. There’s an undocumented feature that allows you to view the actual XML that Portal executes against CRM.

Here’s an example. Let say I have a page that takes a query string and uses Fetch to list all accounts where account name starts with that string. I would like to view the actual FetchXML being executed in order to debug the page. I can do this by simply accessing the xml property of the query object. Below is the Liquid code for this.

Here is how the page renders:

As you can see, this can be a very useful tool for debugging. Note that in the Liquid code, the escape filter is used to escape the XML tags. Without this, the XML will still be output, but will not be displayed on the page when viewing in browser.

This works for both OnPrem and On Cloud Portals.

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

Auto-deployment of linked items in new version of CRMQuickDeploy

Since a couple of versions back, CRMQuickDeploy has a “link item” feature that allows you to share source code (Liquid/HTML, CSS and JavaScript) between multiple Portal artefacts (web pages, entity forms and web forms). When you deploy the source item however, the linked items are not automatically deployed and you had to trigger their deployment separately. If you forget to do this, then the linked artefacts would become out of sync.

I have released a new version of the extension that eliminates this issue. Now when you deploy an item, the extension automatically adds all linked items that reference that item to the deployment list. This means that you will no longer need to separately trigger deployment of linked items, and that your linked artefacts will automatically stay in sync.

Download CRMQuickDeploy

You can download CRMQuickDeploy from the Visual Studio Marketplace.

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

Use web templates as functions in Dynamics 365 Portal Liquid

In Dynamics 365 Portal, web templates are most often used to define reusable page layouts. Web templates however can also be used to create “functions” that “return” values, which you can then use to streamline your Liquid code.

I have put “functions” and “return” in quotes above because technically you can’t define functions with web templates. In fact, there are no ways to define a true function in Liquid that I am aware of! With some creative workaround however, you can come pretty close to achieving the same goals/benefits of functions with web templates. I will detail the approach in this post.

There are 3 main concerns that we need to address when it comes to function:

  • invoking the function,
  • input parameters, and
  • return values.

To invoke a web template from a web page, you can use the following liquid:

{% include “Name of the Web Template” %}

To pass input parameters to the web template, we can update the call above to be as follow:

{% include “Name of the Web Template” param1: value1, param2: value2 %}

Return values requires a bit of a workaround. Web templates predominantly render content and do not return values. However, any Liquid variables you create and assign value to in a web template are also available for use on the calling web page, i.e. outside of the web template. We can use this workaround to return values to the calling web page.

As an example, let say you want to list all the open Opportunities for a given Account on a page. On some other pages, you need to display them in a carousel. You therefore would like to encapsulate the retrieval of open Opportunities into a reusable function.

Create a web template function

First, create a web template, e.g. Get Open Opportunities For Account, with the following code:

{% fetchxml query %}
	<fetch mapping="logical" distinct="false">
		<entity name="opportunity">
			<attribute name="name" />
			<attribute name="customerid" />
			<attribute name="estimatedvalue" />
			<attribute name="statuscode" />
			<attribute name="opportunityid" />
			<order attribute="name" descending="false" />
			<filter type="and">
				<condition attribute="statecode" operator="eq" value="0" />
				<condition attribute="parentaccountid" operator="eq" value="{{ accountId }}" />
			</filter>
		</entity>
	</fetch>
{% endfetchxml %}

{% assign opportunities = query.results.entities %}

This simple “function” expects an input parameter called accountId. It executes a Fetch query and returns the resulting records in a variable called opportunities.

Create the web page

Create a web page with the following code:

{% include "Get Open Opportunities for Account" accountId: request.params["accountid"] %}

{% for record in opportunities %}
	{{record.name}}<br />
{% endfor %}

This simple web page invokes our function and pass in the account ID from the query string. It uses the returned result (in the opportunities variable) to render the list of Opportunities.

Other things to note and limitation

By creating multiple variables in your web template, you can return multiple values to the calling web page. In the example above for instance, you can create a separate return value to hold the total count of matching Opportunities.

In terms of limitation, the biggest I see is the readability of the code (the syntax isn’t the most natural for functions), and the discoverability of the input parameter(s) and return value(s). I’d suggest you workaround this by using the {% comment %} tag in your function web template.

One more thing…

Do a lot of coding with Dynamics 365 Portal? You should check out my Visual Studio extension CRMQuickDeploy. This extension allows you to deploy HTML, Liquid, CSS, and JavaScript to a wide range of Portal artefacts (e.g. web templates, web pages, web files, entity forms, etc.) seamlessly from Visual Studio. Not only this would allow you to leverage the powerful editing features of Visual Studio, but also to source control your code – and facilitate multiple developers working on the project concurrently.

So there you have it…

Function is not a native concept in Liquid, but with a simple workaround, you can still encapsulate your logic into reusable blocks and use them throughout your code-base as if they were functions.

 

Posted in CRM, CRM Portal | Leave a comment

Fixed bug with web file deployment in CRMQuickDeploy 3.5.3

The previous version of CRMQuickDeploy introduced a new feature where it was possible to use folders in the Visual Studio project to specify the targeted language for Portal web page deployment. This means that you no longer have to name the root and localised web pages differently in CRM in order for the tool to target the right record for deployment.

Unfortunately this introduced a bug for web file deployment. In the DeploymentSettings.xml deployment configuration file for web files, you need to specify the name of the targeted parent web page for the web files. When you enable the “Use Folders to Determine Web Page Language” feature, the name of the root and the corresponding localised web pages in CRM are typically the same. Web files however should be attached to root pages only, and the tool did not filter out localised web pages when searching for the parent page during deployment.

I however could not simply just add the filter to look for root web pages. This is because this would not work when targeting Portal v7.x. The ability to localise web pages (and hence the concept of root web pages) were introduced from Portal v8.x.

To continue to support all versions of Portal from v7.x onward, I have added a new configuration attribute to the DeploymentSettings.xml config file.

This attribute is targetPortalVersion7=”false|true” and can be placed on the root node of the configuration file, i.e. DeploymentSettings. This attribute is optional, and the default value is false.

<?xml version="1.0" encoding="utf-8" ?>
<!-- targetPortalVersion7: Optional. Specifies whether the target Portal is v7.x. Default is false. This setting impacts how the parent web page is located for a web file. Localisation of web pages was added to Portal from version v8.x. This means there could be multiple web pages in the system with the same name (one is the root page, others are the localised pages). When this attribute is true, the tool will not look for root pages when retrieving the parent web page for a web file. When this attribute is false, only root web pages will be considered when retrieving the parent web page for a file. -->
<DeploymentSettings targetPortalVersion7="false">
	<WebFiles>
		...
	</WebFiles>
</DeploymentSettings>

Setting this attribute to false causes CRMQuickDeploy to apply a filter for root web pages when retrieving the parent web page for a web file deployment.

Configuration file update required for existing developers targeting Portal v7.x

Since the default value for this attribute (including when it is not present) is false, developers currently targeting Portal v7.x needs to update their DeploymentSettings.xml to set this attribute to true.

Download

You can download CRMQuickDeploy from the Visual Studio Marketplace.

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

Improved password management in new version of CRMQuickDeploy

In previous versions of CRMQuickDeploy, if your connection string requires a password, then the password needs to be specified as clear text in the connection string. While the password is not checked in to source control, and is not saved to disk in clear text, it can still lead to some pretty awkward situations.

With the latest version (3.5.3) of CRMQuickDeploy you now have the option of not specifying the password in the connection string. When a deployment is triggered, the tool examine the connection string to see if a password is required. If a password is required, and is not specified in the connection string, then a dialog is shown to prompt you to provide your password. The password that you provide is not written to any file and is cached for the remaining of the Visual Studio session.

The tool determines that a password is required if it encounters one of the following parameters in the connection string:

  • UserName
  • User Name
  • UserId
  • User Id

You can still specify the password in the connection string if you wish, although this is generally not recommended (unless you are using a dummy account for deployment). If you do this, CRMQuickDeploy will work as it always has been and you will not receive the prompt to enter your password on deployment.

Download

You can download CRMQuickDeploy from the Visual Studio Marketplace.

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

Better support for localised Portal web pages in new version of CRMQuickDeploy

Background

When deploying Portal web pages, CRMQuickDeploy uses the web page’s name in CRM (and the filename of the project item) to identify the target web page for deployment. Since version 8 of CRM Portal however, we have the ability to localise web pages. This created a small problem for CRMQuickDeploy, since by default the name of the localised pages are the same as the name of the root page. Below is how the root and localised pages would look by default:

The workaround (before now)…

The way we have worked around this was to edit the name of the localised web pages to make them unique. We appended the language name to the name of each localised web page, e.g. Contact Us (English) and Contact Us (Spanish), and named our Visual Studio project items accordingly.

Issue with CRM Portal v9…

The above workaround was a bit annoying but it worked well and did not take up too much time. It recently came to my attention however that CRM Portal v9 (i.e. the cloud version) automatically reverts the name of the localised web pages back to be same name as their root pages.

Yes, in CRM Portal v9, you can edit and save the name of the localised web pages. However, if you later on reopen the localised web page record in CRM’s web client, then the form automatically reverts the name back to be the same as the root page. This means that the workaround described above is no longer viable.

Improved web page language targeting in CRMQuickDeploy

To address this issue I have released a new version of CRMQuickDeploy that allows you to define the target language for web pages by placing the Visual Studio project items into a folder. The name of the folder is the name of the target language.

So for the above example, the structure of the Visual Studio project would look like this:

Under this approach, English\Contact Us.html for example would be deployed to the Contact Us web page where the language of that page is set to English. Note that only the immediate parent of the VS project item is used to determine the target language.

Enabling web page language targeting

To prevent this new approach from breaking your current project structure, this new feature of web page language targeting must first be enabled in Visual Studio. You can do this by going to Tools \ Options \ CRMQuickDeploy \ Deployment Options and set Use Folder to Determine Web Page Language to True.

When this option is enabled, each web page must be placed in a folder (underneath the PortalWebPages folder). The name of the immediate containing folder will be used as the target language for its children web pages.

Download

You can download CRMQuickDeploy from the Visual Studio Gallery.

So there you have it…

This new feature allows you to target web pages more efficiently, and works against both on-cloud and on-prem (v8) CRM Portal.

As always, would love to hear from you with feedback and suggestion!

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

How to set value for date-time field by JavaScript in CRM Portal

Date-time fields in CRM Portal is a bit tricky when it comes to setting value dynamically using JavaScript. Behind the scene there are two HTML input fields that we need to worry about.

First is the field that actually contains the value that will be submitted to the server. This field has the CRM attribute name as its HTML ID. This field is not displayed on the page. The second is the field that holds the display value and enables the date-time picker. Below is what the HTML of these fields would look like:

We’d need to set both of these fields in order for the date-time field to work correctly.

The trick is the value that you set for these fields need to be in a particular format. For the submit field, the value should be a UTC date-time string in the format 2018-07-24T02:13:36.0000000. Note that the fractional second part (the last part) requires 7 digits.

For the display field, the value should be a local date-time string in the format determined by the current page (based on the page’s localisation). You can find this format in the data-date-format attribute of the HTML element for the display field.

Luckily CRM Portal uses moment.js by default and we have access to this library on our pages. This makes converting and formatting date-time easy. Below is the complete code to set value for a date-time field. Please review the inline comments.

//fieldId: ID (CRM attribute name) of the field we are setting.
//dateValue: The date-time value to set to. This should be of type Date. 
function setDateTimeFieldValue(fieldId, dateValue) {
	//Get the submit field
	var $submitField = $("#" + fieldId);

	//Get the display field
	var $displayField = $submitField.nextAll(".datetimepicker").children("input");

	//Get the display date format
	var dateFormat = $displayField.attr("data-date-format");

	//Set the submit field. Remember this needs to be in UTC and the format must be exact.
	$submitField.val(moment.utc(dateValue).format("YYYY-MM-DDTHH:mm:ss.SSSSSSS"));

	//Set the display field using the page's date format
	$displayField.val(moment(dateValue).format(dateFormat));
}
Posted in Adxstudio, CRM, CRM Portal | 3 Comments