Strip HTML characters using Liquid in PowerApps/ Dynamics 365 Portals

I have been working lately a lot on PowerApps portals and one of the requirement that we got from customer is to render some HTML text as text in Portals. The customer had a field in Dynamics 365 which was storing rich text.

We would retrieve the field value using fetchxml in liquid and the requirement was to strip the HTML characters and show the text in plain text. We did that initially in JS, only to realize later that there is a specific filter available in liquid – “strip_html”.

And guess what it worked like a charm.

Input:

{{ “<span>This liquid filter <b>can work wonders</b> with stripping HTML<br />” | strip_html}}

Output:

This liquid filter can work wonders with stripping HTML

As I am working more and more on liquid, I am simply falling in love with it. Simple yet quite powerful language. And as I keep exploring, I will post some interesting stuffs over the next few weeks.

Hopes this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Working with custom DateTime formats in PowerApps/ Dynamics 365 Portals

By default in PowerApps portals, the date and time is showed in the format as configured in CDS/ Dynamics 365. However you can override the default date/ time format to be used on portals with the below site settings.

  • DateTime/DateFormat: The date format used on the portal.
  • DateTime/TimeFormat: The time format used on the portal.
  • DateTime/DateTimeFormat: The format for full date and time used on the portal.

We have a requirement to display the date field in particular format such as 20-May-2020 in Power Apps Portals. This can be achieved by creating site setting in the below format.

Now let us create a site setting

Name – DateTime/DateFormat

Value – dd-MMM-yyyy

Screenshot for reference

clip_image002[4]

Once you clear your portal cache, you should see the date time values now reflected in the portal accordingly.

clip_image004[4]

It displays time as my date field is of type date-time format.  For the benefit of readers, sharing some other format which can be of help.

1. yyyy-MM-dd – 2020-05-16

2. MMM dd yy – May 13 20

3. dd/MM/yyyy – 27/05/2020

4. dd/MM/yyyy HH:mm – 14/05/2020 12:27

5. dd/MM/yyyy hh:mm:ss – 12/05/2020 12:30:52 – 12 Hour format

6. dd/MM/yyyy HH:mm:ss – 27/05/2020 19:32:02 -24 Hour format

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Automatically sign-in with Azure AD in PowerApps/ Dynamics 365 Portals on Sign in click.

Recently I had this requirement where the customer wanted to automatically sign in using the default Azure AD option on clicking Sing in the header menu in PowerApps portals.

image

So when the Sign In link is clicked, the user should be automatically signed in without being redirected to the Sign In page and then clicking on the Azure AD button.

It’s actually quite easy to do the same. And it can be accomplished using couple of site settings.

The first site setting is Authentication/Registration/LoginButtonAuthenticationType. To set the value of the site setting, we need to get the GUID of the identified.

Click on Azure AD button. And copy the URL from browser where you are being redirected to. The URL shall be in the format as shown in the below screenshot.

image

Take the first guid after login.microsoftonline.com and use this value for the previously mentioned setting. The value will be in the format – https://login.windows.net/<guid>/.

image

Don’t forget to put the trailing slash after the GUID.

The next step is to basically check for another site setting – Authentication/Registration/ExternalLoginEnabled. If this site setting is not there, it’s fine. If it is there, set it to False or disable it.

Make sure you refresh the portal cache. Sometimes, to reflect authentication related settings, you may need to restart the portal as well to reflect it immediately.

Now once the user clicks on Sign in link, it automatically signs the user in with Azure AD.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Get Transaction currency name for logged in user in Dynamics 365

Recently Microsoft have released quite a few updates to it’s client API and one such is the update to API for getting the currency name of the logged in user.

All this time, Microsoft had an API to get the transaction currency id of the logged in user using the API – Xrm.Utility.getGlobalContext().userSettings.transactionCurrencyId. You needed to run a separate query to fetch the currency name based on currency id. However it is being deprecated now and now the replacement API is Xrm.Utility.getGlobalContext().userSettings.transactionCurrency.

This wonderful api returns the name of the currency as well as the id and entityType.

Below is the sample output

{id: “e7dd9bc6-d239-ea11-a813-000d3a35b14a”, entityType: “transactioncurrency”, name: “US Dollar”}

Wonderful isn’t it?

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)


Out of the box API to get Logged In user’s security role names in Dynamics 365 is finally there. Is the long wait finally over?

Checking for logged in user’s security role is a requirement I haven’t missed in any of my implementations so far. From enterprise implementations to projects spanning couple of months, this requirement I had everywhere. And as much crazy it may sound, until recently there was no way to get the security role names directly using client API. Even the unified interface introduced Xrm.Utility.getGlobalContext().userSettings.securityRoles which unfortunately had the same perennial problem – we shall get a the GUID of the roles user is having and then we have to fire a query to the server side to get the role names.

Thankfully Microsoft have slipped in an update to get the role names. Since this one came without much publicity, it didn’t get the media attention it deserved. Microsoft have deprecated – Xrm.Utility.getGlobalContext().userSettings.securityRoles and instead suggested to use Xrm.Utility.getGlobalContext().userSettings.roles. Use the getAll() function to take the output as array.

image

As seen from the developer console, it’s a relief certainly. We no longer need to query the separately to get the role names. Wonderful isn’t it. Finally a solution to a long standing problem? Well not exactly.

This method works just fine if you have the security roles directly assigned to the user. However if the user is added to a team and the team have a security role, the name comes as undefined.

image

Above is a screenshot for a user. The user is assigned to a team and the team is having Sales Manager role. Although the API is indeed returning the GUID of all the roles, the name of the security role coming through team association is coming as undefined.

Honestly I was bit surprised as it is kind of quite basic and this making the final cut with this issue is something I was not expecting.

I hope this is fixed quick by Microsoft.

Till then we wait with bated breath to use this for all scenarios.

Hope this helps!

{Dynamics CRM + Role Privilege} Record Privilege Checker for Dynamics CRM

Recently I had a requirement where our client’s support and maintenance team wanted a feature through which they can view the privileges for a record for all active users in the system. Ok, let me take a moment to explain here.

Suppose the support team wants to run a regular security check and identify what privilege does a specific user have on specific account records. You might be thinking that is that this hard? After all we can go to the user and identify the security roles of the user. And then determine what access privilege does the security role have on the account

Simple isn’t it? Well it is not. And specially with CRM 2016, a user might end up getting a specific privilege on record through any of the following.

  • Security roles
  • Sharing
  • Access teams
  • Owner team association
  • Hierarchical relationship.

It can be really complex depending on how your system is configured. Then what is the other way of finding out without user intervention. The customer did not want any external utility. They wanted something within CRM from where they could view the information.

So I developed a quick solution and then I thought why not share the community so that anybody in the future having the same requirement can benefit from this.

The tool is available for download for free at codeplex site – https://recordprivilegechecker.codeplex.com/releases/view/629363

 

Download the managed solution specific to your CRM version. Currently it supports

  • For CRM 2016 Version 8.1
  • For CRM 2016 Version 8.0
  • CRM 2015 Version

Once you install the managed solution, open any record form for any customizable entity. You should see a button called ‘Check User Access’ on the form.

image

Click on the button. Once you click on the button, a popup will show like the one below where you can view the privilege of all the active users on that particular record

image

 

And voila!,  it shows the privilege that each user has on the record. For Organization Owned entities it would show you the privilege as well.

image

 

Since ‘Assign’ and Share privileges are not valid for organization owned entities, you can see those privileges are blank.

The results might take some time to pop-up depending on the number of active users and access complexity in your environment.

And finally to round off, you can sort on the ‘FullName’ and ‘User Name’ columns. Also you have paging and searching to slice and dice your data.

image

 

Liked this tool? You can make a small donation at paypal account – debajit.prod@gmail.com. Your support and encouragement will help me to make many other tools in the future.

 

Till then you read one of my blog posts, happy CRM’ing.

{Dynamics 365/ CRM forms} Programmatically access controls from a system form of an entity in Dynamics CRM

It’s been sometime I have published a blog article as I am in neck deep work in some tool development. But still I could not resist myself from penning down this interesting stuff which I came across recently and share with all my readers.

Recently I came across a customer requirement where they needed to fetch all the controls in a tab in on a form for contact entity and show them in one of their external applications which is interacting with Dynamics CRM. I think an example here would explain the scenario.

I have a tab with display name – ‘DEMO TAB’ and name – ‘DEMO_TAB’ on the ‘Contact’ form of the contact entity.

image

The DEMO TAB has the following controls of the specified type

First Name Single line of text (type = simple)
Last Name Single line of text (type=simple)
Lead Source Optionset
Birthday DateTime
Originating Lead Lookup
Do not allow faxes two options
Credit Limit Currency

 

I haven’t taken all the data types and surely at the end of this blog you will get an idea on how to proceed with other data types as well.

So let’s see what are the  steps here to achieve the customer requirement.

  • Query the system form entity for contact entity with form name = ‘Contact’
  • Get the formxml
  • Parse the formxml to get all the controls within the DEMO TAB of the form
  • Finally render the controls in the custom application as per the control type. So First name should be rendered as textbox, Birthday probably as DatePicker control and Lead Source as dropdown.

For readers who are aware of ADX portals and how they work, you might have a question coming, isn’t is available OOB? Well it is and ADX is much more than this. We tried to put our point through for it but this is one off requirement for the customer and they have been using their custom application for pretty long time and hence did not want to discontinue the same.

Well, so we are back to our custom development.

Let’s complete our requirement step by step. So lets first write the piece of code to get the controls within the ‘DEMO TAB’ of the ‘CONTACT’ form of contact entity.

 

var systemForm = GetFormByName("contact", 2, proxy);
var controls = GetTabControls("DEMO_TAB", systemForm.GetAttributeValue<string>("formxml"), proxy);

private static Entity GetFormByName(string formName, int entityTypeCode, IOrganizationService service)
        {
            var query = new QueryExpression("systemform");
            query.Criteria.AddCondition("name", ConditionOperator.Equal, formName);
            query.Criteria.AddCondition("objecttypecode", ConditionOperator.Equal, entityTypeCode);
            query.ColumnSet.AddColumn("formxml");

            var results = service.RetrieveMultiple(query);

            return results.Entities[0];
        }

        private static List<XElement> GetTabControls(string tabName, string formXml, IOrganizationService service)
        {
            var controlsList = new List<XElement>();
            var formDoc = XDocument.Parse(formXml);
            var tabs = formDoc.Descendants("tab");

            foreach (var tab in tabs)
            {
                if(tab.Attribute("name").Value.Equals(tabName, StringComparison.OrdinalIgnoreCase))
                {
                    // get all the controls.
                    controlsList = tab.Descendants("control").ToList();
                    break;
                }
            }

            return controlsList;
        }

 

The first method retrieves the form for the contact entity and the second method extracts all the controls within the ‘DEMO_TAB’.

I have used System.Xml.Linq and believe me, it is much cleaner way to parse your xml than using the traditional System.Xml.Xmldocument methods.

So we now have the controls. Now how to get the field name and the type of the control? To explore this, lets see what the form xml looks like. I have pasted a portion of the tab xml

<tab name="DEMO_TAB" id="{596d8090-3b20-77ad-3c36-77442a42835c}" IsUserDefined="0" locklevel="0" showlabel="true" expanded="true">
            <labels>
                <label description="DEMO TAB" languagecode="1033" />
            </labels>
            <columns>
                <column width="100%">
                    <sections>
                        <section name="tab_4_section_1" showlabel="false" showbar="false" locklevel="0" id="{a64c108b-c1d1-1d90-ebd0-e2c4ab7ced3d}" IsUserDefined="0" layout="varwidth" columns="11" labelwidth="115" celllabelalignment="Left" celllabelposition="Left">
                            <labels>
                                <label description="Section" languagecode="1033" />
                            </labels>
                            <rows>
                                <row>
                                    <cell id="{2b74bc94-1e03-4ab7-428e-8283732c57ca}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="First Name" languagecode="1033" />
                                        </labels>
                                        <control id="firstname" classid="{4273EDBD-AC1D-40d3-9FB2-095C621B552D}" datafieldname="firstname" disabled="false" />
                                    </cell>
                                    <cell id="{b6d61c95-0c1d-4cae-2e7a-1210e4d7a1cd}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Lead Source" languagecode="1033" />
                                        </labels>
                                        <control id="leadsourcecode" classid="{3EF39988-22BB-4f0b-BBBE-64B5A3748AEE}" datafieldname="leadsourcecode" disabled="false" />
                                    </cell>
                                </row>
                                <row>
                                    <cell id="{b06bd53b-c215-d372-0265-e3da223d9bd5}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Last Name" languagecode="1033" />
                                        </labels>
                                        <control id="lastname" classid="{4273EDBD-AC1D-40d3-9FB2-095C621B552D}" datafieldname="lastname" disabled="false" />
                                    </cell>
                                    <cell id="{6780cace-3301-540e-0425-543755db1a18}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Originating Lead" languagecode="1033" />
                                        </labels>
                                        <control id="originatingleadid" classid="{270BD3DB-D9AF-4782-9025-509E298DEC0A}" datafieldname="originatingleadid" disabled="false" />
                                    </cell>
                                </row>
                                <row>

See for the section highlighted in yellow. Let’s see what we can get from there.

  • description – the display name for the field
  • datafieldname – the logical name of the field
  • disabled – value to indicate whether the field is enabled or disabled on the form.

So far so good. We have all the necessary information. But wait? How do we get the type of the field and decide whether to render it as textbox or dropdown or datepicker?

You have two methods here. One is to query the metadata based on the field name (datafieldname) and then determine the type and render the control accordingly. If you ask me, this is probably the best way to determine to do since it would be independent of any version changes or upgrades in CRM.

But one disadvantage is the amount of time it takes to query the metadata is significant and your page rendering time could go for a toss depending on how many controls you are querying for.

The other and probably faster way to do this is to use the classid attribute of the control. Check for the stuff highlighted in green. If you observe carefully both first name and last name have the same data type and their classid is same as well. It’s more like each control type being identified by a separated id. However this method has major caveats

  • You would need to identify the classid of all type and probably use them in your code, perhaps as switch-case.
  • If for some reason, Microsoft changes the classid of the control types, your code would go for a toss.
  • Finally, with new releases, Dynamics is coming up with new field types and hence you need to keep your code changing for the new types as they come.

Wondering how to get the attributes of the controls. Well below is the code to help you on that.

foreach (var control in controls)
            {
                var isDisabled = control.Attribute("disable").Value;
                var classId = control.Attribute("classid").Value;
                var datafieldName = control.Attribute("datafieldname").Value

                // parse the other attributes
            }

 

Isn’t it easy with System.Xml.Linq?

 

Hope you find this useful.

{Dynamics CRM/ 365 + Views}–Create a view to show security roles assigned to user along with roles the user inherits from the team

The title might be confusing. So let’s straightway jump to the requirement here. Our client a role lookup on custom entity form and on click of the lookup, the user should be presented with a view with a view which shows the security role directly assigned to the user along with the security roles the user inherits from the team.

For e.g.

User A has security role – Role 1

User A is a member of the team which has security role – Role 2

So the view would show both the roles – Role 1 + Role 2

Very simple requirement right? However can you achieve this using OOB view? Well unfortunately the answer is BIG NO.

We went ahead with the design that when a user clicks the lookup we would call dynamically a fetchxml using the OOB custom view. Now the big task is creating the fetch xml of the custom view.

After much deliberation, I finally came up with the below Fetch XML which serves exactly the purpose. So let’s see our protagonist in the play.

 

var fetchString = string.Format(@"<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’true’>
                                            <entity name=’role’>
                                                <attribute name=’name’ />
                                                <attribute name=’businessunitid’ />
                                                <attribute name=’roleid’ />
                                                <order attribute=’name’ descending=’false’ />
                                                <link-entity name=’systemuserroles’ from=’roleid’ to=’roleid’ link-type=’outer’>
                                                    <link-entity name=’systemuser’ from=’systemuserid’ to=’systemuserid’ alias=’aj’ link-type=’outer’>
                                                        <attribute name=’systemuserid’ />
                                                    </link-entity>
                                                </link-entity>
                                                <link-entity name=’teamroles’ from=’roleid’ to=’roleid’ visible=’false’ intersect=’true’ link-type=’outer’>
                                                    <link-entity name=’teammembership’ from=’teamid’ to=’teamid’ visible=’false’ intersect=’true’ link-type=’outer’>
                                                        <link-entity name=’systemuser’ from=’systemuserid’ to=’systemuserid’ alias=’al’ link-type=’outer’ >
                                                            <attribute name=’systemuserid’ />
                                                        </link-entity>
                                                    </link-entity>
                                                </link-entity>
                                                    <filter type=’or’>
                                                        <condition entityname=’aj’ attribute=’systemuserid’ operator=’eq’ value='{0}’ />
                                                        <condition entityname=’al’ attribute=’systemuserid’ operator=’eq’ value='{0}’ />
                                                    </filter>

                                            </entity>
                                        </fetch>", userid);

I have highlighted the important part of the query. The first thing to notice here is the alias that I provided while fetching the roles from the systemuserroles table and the teamroles table.

The next part is using the alias and constructing the filter condition with the alias as the entity name. userid is the GUID of the user whose security roles needs to be determined.

 

Please note that this type of construct is actually possible from CRM 2013 and onwards. Although this works, unfortunately this construct is not supported through the UI.

Small trick but it can consume days if not know.

Hope this helps! Till you read one or more of my blog posts, happy CRM’ing