One of the great things about developing bespoke solutions for CRM is the ability to make changes to the sitemap navigation. For the uninitiated, the Sitemap is this area within CRM:

Sitemap

The areas and individual buttons can be modified to suit most requirements for organizations, to include links to custom entities, external applications or to an internal HTML/Silverlight web resource. As a result, CRM can be made to look highly bespoke and unique, as if it is a completely different CRM system altogether from the default setup.

We recently had a requirement to create a sitemap area button that would open a specific record. The record in question is one that will be updated frequently, so colleagues within the business require quick and easy access to it from the Sitemap area. We already know that this possible for opening specific entity views, as we have used this a number of times previously (for example, change the default view that opens when you click the ‘Accounts’ button to “Accounts I Follow”). MSDN provides a great outline of how you go about doing this:

To display a list of entity records within the application for a SubArea set the Entity attribute value. This displays the default view for that entity and provides the correct title and icon.

However, if you want to have a SubArea element that uses a specific initial default view, use the following Url pattern.

Url=”/_root/homepage.aspx?etn=<entity logical name >&amp;viewid=%7b<GUID value of view id>%7d”

Source: https://msdn.microsoft.com/en-gb/library/gg328483.aspx#BKMK_DisplayViewInApplicationUsingSiteMap

The question at this stage then is can we adapt the above method in order to open an entity record instead? Let’s give at a go, using our trusty XRMToolbox Sitemap Editor tool. The steps below assume that you already know how to use this tool and to make amends to the sitemap area.

On the above article page, we are told that in order to open an entity record via a URL, we need to provide the following query parameters:

  • etn: The logical name of the entity
  • pagetype: In this instance, should be set to “entityrecord”
  • id: The GUID for the CRM record to open. The best way to obtain this is to export the record to Excel and unhide all the columns; the GUID is then the value in the A column; you will need to change this to Upper Case via an Excel =UPPER function:

ExcelGUID

Then, in order to ensure that the GUID is accepted correctly in the URL, we need to surround it with curly braces. As these character types are not accepted as part of a URL string, we need to provide the following substitute character strings:

{ = %7B

} = %7D

e.g. {E16EE6D6-56B4-E511-80E2-2C59E541BD38} -> %7BE16EE6D6-56B4-E511-80E2-2C59E541BD38%7D

So let the trial and error begin! The most simple way of getting this to work would be to change the SubArea URL value to the full CRM instance URL. So, for example, our CRM Online URL would look something like this:

https://mycrminstance.crm.dynamics.com/main.aspx?etn=test_mycustomentity&pagetype=entityrecord&id=%7BE16EE6D6-56B4-E511-80E2-2C59E541BD38%7D

But if we have separate development/production environments, then this is impractical as the link will not work when moving our solution between environments. Our preferred setup therefore is to look at using a relative URL path that works across different environments.

What happens if we try adapting the URL example for views instead? So, in which case, our URL would be:

/_root/homepage.aspx?etn=test_mycustomentity&pagetype=entityrecord&id=%7BE16EE6D6-56B4-E511-80E2-2C59E541BD38%7D

SitemapURLError

That’s a no then! The next step then is to take a closer look at the example URL’s provided and making some guesswork in regards to how relative URL’s function. If we assume then that our full URL is:

https://mycrminstance.crm.dynamics.com/main.aspx?etn=test_mycustomentity&pagetype=entityrecord&id=%7BE16EE6D6-56B4-E511-80E2-2C59E541BD38%7D

Then our relative URL would be:

/main.aspx?etn=test_mycustomentity&pagetype=entityrecord&id=%7BE16EE6D6-56B4-E511-80E2-2C59E541BD38%7D

And guess what? It works! One comment to make on this though is that the record opens in a brand new window within Internet Explorer & Google Chrome, so I would therefore presume that this is the case across all browsers. There are some additional query string parameters that can be specified in the URL to make this look more like a quick-edit “pop out” window:

  • cmdbar: Setting this to “false” will hide the ribbon on the form
  • navbar: Setting this to “off” will hide the sitemap navigation bar

Our URL string and record window would therefore look this:

/main.aspx?etn=test_mycustomentity&pagetype=entityrecord&id=%7BE16EE6D6-56B4-E511-80E2-2C59E541BD38%7D&cmdbar=false&navbar=off

URLExampleWindow

The user can then make their changes to records, save and then close the window. Suffice it to say, it is good to know that it is possible to do this within CRM and that the trial and error steps involved were fairly minimal.

The Scenario: You are running CRM Online in conjunction with some legacy database/application systems. These systems are setup with a SQL Server Reporting Services instance that is looking to either an SQL Server, OLE DB etc. database.

The Problem: You need to make data from your legacy systems visible within your CRM. The information needs to be displayed on the Entity Form and show specific information from the legacy database that relates to the CRM record.

Admittedly, the above is perhaps somewhat unlikely situation to find yourself in, but one which I recently had to try and address. I suppose the most straightforward resolution to the above is to just say “Get rid of the legacy system!”. Unfortunately, the suggestion didn’t go down to well when I voiced it myself…

So at this point the next best answer looked to be try and utilise what we have within the existing infrastructure: an all singing, all-dancing SSRS and SQL Server database instance.

What if we were to try uploading an .rdl file that includes a FetchXML and our SQL/OLE DB database data source into CRM? Whenever you try to perform this, you will get this error message:

ReportUploadError_NoFetchXML

 

Rats! So there is no way in which we can include a non-fetch XML Data Source to our separate SSRS report instance. So is there anything else within CRM that can be utilised to help in this situation? Let’s first take a quick look at the following nifty little feature within CRM, courtesy of our good friend MSDN:

You can use an IFRAME to display the contents from another website in a form, for example, in an ASP.NET page. Displaying an entity form within an IFrame embedded in another entity form is not supported.

Use the getValue method on the attributes that contain the data that you want to pass to the other website, and compose a string of the query string arguments the other page will be able to use. Then use a Field OnChange event, IFRAME OnReadyStateComplete event, or Tab TabStateChange event and the setSrc method to append your parameters to the src property of the IFRAME or web resource.

You may want to change the target of the IFRAME based on such considerations as the data in the form or whether the user is working offline. You can set the target of the IFRAME dynamically.

Source: https://msdn.microsoft.com/en-gb/library/gg328034.aspx

Having worked extensively with SSRS in the past, I am also aware that you can use an SSRS URL string in order to specify properties about how the report is rendered, its size and – most crucially – what the value of required parameters should be. The friend that keeps on giving has a great article that goes through everything that you can do with an SSRS report URL and also how to use Parameters as part of your URL. So in theory therefore, we can place an IFRAME on our form and then use JScript to access form-level field values and modify the IFRAME URL accordingly.

Here are the steps involved:

  1. Go into Form Editor and add a new IFRAME to the form, specifying the following settings:

Name: The Logical name of the control, this will be required as part of the JScript code used later, so make a note of it.

URL: As this is a mandatory field, you can specify any value here as it will change when the form is loaded by the user. This is not practical as we don’t want this to be displayed if, for example, the field that we are passing to the URL has no value in it. Our JScript code will sort this out in a few moments

Label: This can be anything, and defaults to whatever is entered into the Name field

Restrict cross-frame scripting, where supported: Untick this option

Ensure that ‘Visible by default’ is ticked

Your settings should look something like this:

IFRAMESettings

  1. Create or modify an existing JScript Library for the form, adding in the following function (after modifying the values accordingly):
function onLoad_LoadSSRSReport() {

    //First get the page type (Create, Update etc.)

    var pageType = Xrm.Page.ui.getFormType();
    
    //Then, only proceed if the Form Type DOES NOT equal create, can be changed depending on requirements. Full list of form types can be found here:
    
    //https://msdn.microsoft.com/en-us/library/gg327828.aspx#BKMK_getFormType

    if (pageType != "1") {

        //Get the value that you want to parameterise, in this case we are on the Account entity and need the Account Name

        var accountName = Xrm.Page.getAttribute("name").getValue();

        //In order to "accept" the parameter into the URL, spaces need to be replaced with "+" icons

        accountName = accountName.replace(/ /g, "+");

        //Now, get the the name of the IFRAME we want to update

        var iFrame = Xrm.Page.ui.controls.get("IFRAME_myssrsreport");

        //Then, specify the Report Server URL and Report Name.

        var reportURL = "https://myssrsserver/ReportServer?/My+Reports/My+Parameterised+Report&MyParameter=";

        //Now combine the report url and parameter together into a full URL string

        var paramaterizedReportURL = reportURL + accountName;

        //Finally, if there is no value in the Account Name field, hide the IFRAME; otherwise, update the URL of the IFRAME accordingly.

        if (accountName == null) {
            iFrame.setVisible(false);
        }
        else {

            iFrame.setSrc(paramaterizedReportURL);
        }
    }
}
  1. Add the function to the OnLoad event handler on the form. Now, when the form loads, it will update the IFRAME with the new URL with our required parameter.

And there we go, we now have our separate SSRS instance report working within CRM! A few things to point out though:

  • If the report parameter supplied does not load any matching records, then SSRS will display a standard message to this effect. You would need to modify the report settings in order to display a custom message here, if desired.
  • It is recommended that you have https:// binding setup on your report instance and supply this to as part of the setSrc method. http:// binding works, but you may need to change settings on your Web Browser in order to support mixed mode content. Full instructions on how to set this up can be found here.
  • This may be stating the obvious here, but if your SSRS instance is not internet-facing, then you will get an error message in your IFRAME if you are not working from the same network as your SSRS instance. Fortunately, SSRS can be configued for an Internet deployment.
  • The steps outlined in 1) can also be used to specify a non-parameterised SSRS report within an IFRAME dashboard too. I would recommend using the following SSRS system parameters as part of the URL though:
    • rs:ClearSession=true
    • rc:Toolbar=false

e.g.

https://myssrsserver/ReportServer/Pages/ReportViewer.aspx?%2fMy+Reports%2fMy+Non+Parameterised+Report&rs:ClearSession=true&rc:Toolbar=false

One of the most challenging things about any system migration is ensuring that information from other business systems can be made available, and it is good to know that CRM has supported approaches that can help to bridge the gap.

Working with Dynamics CRM can present some interesting challenges. What you tend to find is that you can pretty much say “Yes!” when it comes to doing most things you would expect from a CRM/database system, but there is a learning curve involved in figuring out the best approach to take. Often, as well, you may  over-complicate matters and overlook a much easier solution to achieve what you need.

Take, for example, modifying the FetchXML queries in a Public View that you have created programmatically. Let’s say you’ve created your own view within CRM using the following C# code snippet (adapted from the SDK sample):

SavedQuery sq = new SavedQuery
     {
        Name = "My New View",
        Description = "My view created in C# for the Account entity",
        ReturnedTypeCode = "account",
        FetchXml = fetchXml,
        LayoutXml = layoutXml,
        QueryType = 0
    };

_customViewId = _serviceProxy.Create(sq);
Console.WriteLine("A new view with the name {0} was created.", sq.Name);

A few things to point out first with the above:

  • In order for this code to work, you would need to declare System.String values for fetchXml and layoutXml, as well as first connecting to CRM using the OrganizationServiceProxy (_serviceProxy).
  • As well as specifying the FetchXML query you would like to use, you also have to specify a LayoutXML as a parameter in order to. Although Microsoft do have dedicated articles on MSDN that goes over the schema for this, there is a potential learning curve involved here for those who are unfamiliar with working with XML.
  • ReturnedTypeCode is your entity logical name, which will need changing depending on the entity you are attempting to query
  • Be sure to add in the appropriate namespace references, otherwise this code will not work.

The code example above is all very well and good if you are just wanting to create a brand new view. But what happens if you need to change it in the future? We can modify the base properties of a view (Name, Description etc.) as well as the column layout via the CRM GUI, but when we attempt to modify the filter criteria (i.e. the FetchXML query), we will notice that the option is not available to use:

View_NoFilterCriteriaButton

The next logical step would therefore be to look at creating some C# code that would take the existing view and modify the fetchXML query property. Unfortunately, Microsoft have not provided code examples on how this can be done, although it is in theory possible via the many methods at your disposal through the SDK.

Rather then spend days and potentially weeks writing a bespoke piece of code to do the job, it was then that I realised that I was being a little dense (as tends to happen) and that the Solution was sitting right in front of me. See what I did there?

Whilst the general rule of thumb is “DON’T DO IT!!” when it comes to modifying an exported solution file, it is possible to do and pretty much anything within a solution file can be changed or modified to suit a particular requirement. And, as luck would have it, modifying Public Views (either ones created by yourself or system ones) is a supported task that you can perform on the solution file:

Definitions of views for entities are included in the customizations.xml file and may be manually edited. The view editor in the application is the most commonly used tool for this purpose. Editing customizations.xml is an alternative method

Source: https://msdn.microsoft.com/en-gb/library/gg328486.aspx

So, in order to modify a custom Public Views FetchXML query, all you would need to do is:

  1. Create a temporary, unmanaged solution file containing the entity with the custom Public View you want to change.
  2. Export as an unmanaged solution, unzip and open the customizations.xml file either in Notepad, Visual Studio or the XML editor program of your choice
  3. Use Ctrl + F to locate the savedquery node of the view you wish to change. It should look like this:
<savedquery>
    <IsCustomizable>1</IsCustomizable>
    <CanBeDeleted>1</CanBeDeleted>
    <isquickfindquery>0</isquickfindquery>
    <isprivate>0</isprivate>
    <isdefault>0</isdefault>
    <returnedtypecode>1</returnedtypecode>
    <savedqueryid>{8e736028-47c7-e511-8107-3863bb345ac8}</savedqueryid>
    <layoutxml>
        <grid name="resultset" object="1" jump="firstname" select="1" preview="1" icon="1">
            <row name="result" id="accountid">
                <cell name="name" width="150" />
                <cell name="statecode" width="150" />
                <cell name="statuscode" width="150" />
                <cell name="ownerid" width="150" />
                <cell name="createdon" width="150" />
            </row>
        </grid>
    </layoutxml>
    <querytype>0</querytype>
    <fetchxml>
        <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='true'>
            <entity name='account'>
                <attribute name='createdon' />
                <attribute name='statuscode' />
                <attribute name='ownerid' />
                <attribute name='name' />
                <attribute name='statecode' />
                <attribute name='accountid' />
                <order attribute='name' descending='false' />
                <link-entity name='email' from='regardingobjectid' to='accountid' alias='ab' link-type='outer'>
                    <attribute name='regardingobjectid' />
                </link-entity>
                <link-entity name='lead' from='parentaccountid' to='accountid' alias='al' link-type='outer'>
                    <link-entity name='email' from='regardingobjectid' to='leadid' alias='lp' link-type='outer'>
                        <attribute name='regardingobjectid' />
                    </link-entity>
                </link-entity>
            <filter type='and'>
                <condition entityname='ab' attribute='regardingobjectid' operator='null' />
                <condition entityname='lp' attribute='regardingobjectid' operator='null' />
                <filter type='or'>
                    <condition entityname='ab' attribute='createdon' operator='olderthan-x-weeks' value='1' />
                    <condition entityname='ab' attribute='createdon' operator='null' />
                </filter>
            </filter>
            </entity>
        </fetch>
    </fetchxml>
    <IntroducedVersion>1.0</IntroducedVersion>
    <LocalizedNames>
        <LocalizedName description="My New View" languagecode="1033" />
    </LocalizedNames>
    <Descriptions>
        <Description description="My view created in C# for the Account entity" languagecode="1033" />
    </Descriptions></savedquery>
  1. Modify the FetchXML query within the <fetchxml> node to your updated query
  2. (Optional) If your FetchXML query is simply making changes to the filter criteria, you can skip this step. Otherwise, if you have new fields that you would like to be displayed as part of the changes, you will also need to modify the <layoutxml> node so that it contains your new fields.
  3. Save the changes back into the solution file and then import the solution back into CRM.
  4. Test your view by opening it within the application and confirm everything looks OK.

I’m sure you’ll agree that this is definitely a much easier and simple way to make changes to your view. Just be careful when working within the solution file that you don’t accidentally delete/overwrite something!

One of our company directors has a big thing for Americanisms and American spellings over the “proper” English spellings/pronunciations. So it came as no surprise that when we first showed him CRM, he immediately pointed out the default names on the Address field composite box:

CompositeAddressControl

Specifically, he didn’t like “State/Province” or “Zip/Postal Code” (for those of you who are not aware, “State/Province is the equivalent to a County over here and we refer to “Zip/Postal Code” generally as Postcode).

Being good and sympathetic fellow Englanders, our team went away to investigate. Changing the display name of each individual address field didn’t work, as you may have expected. It turns out that that the composite address fields can instead be accessed and changed using the Xrm.Page.getControl JScript method to return each individual field and then set the label accordingly. But how do we find out the name of each control to access? Microsoft have a very informative article on MSDN that goes through Composite Controls and how they operate:

From the article:

You can access the individual constituent controls displayed in the flyout by name. These controls use the following naming convention: <composite control name>_compositionLinkControl_<constituent attribute name>. To access just the address_line1 control in the address1_composite control you would use: Xrm.Page.getControl(“address1_composite_compositionLinkControl_address1_line1”).

Source: https://msdn.microsoft.com/en-gb/library/dn481581.aspx

All we need therefore is the attribute logical name for each of the constituent fields on the control and to then add this to the getControl method!

So after a quick 5-10 minutes of coding, we now how the following nice little Jscript function that fires OnLoad for all entity forms that use the Composite Address control for Address 1 and Address 2 fields:

function changeAddressLabels() {

    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_line1").setLabel("Address 1");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_line2").setLabel("Address 2");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_line3").setLabel("Address 3");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_city").setLabel("Town");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_stateorprovince").setLabel("County");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_postalcode").setLabel("Postal Code");
    Xrm.Page.getControl("address1_composite_compositionLinkControl_address1_country").setLabel("Country");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line1").setLabel("Address 1");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line2").setLabel("Address 2");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_line3").setLabel("Address 3");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_city").setLabel("Town");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_stateorprovince").setLabel("County");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_postalcode").setLabel("Postal Code");
    Xrm.Page.getControl("address2_composite_compositionLinkControl_address2_country").setLabel("Country");
}

A victory for our team and for England – cup of tea, anyone?

Does anyone else have any experience doing interesting things with Composite Controls? Please leave a comment below if you have.

Microsoft Dynamics CRM comes with a number of out of the box Security Roles that can be used in order to give users the correct permissions. Whilst this is helpful, they generally won’t be a good fit for most organisations and a custom security role will be required in order to get the correct mix of permissions. These can be either created from scratch or be based off one of the system defaults. Regardless of how you go about it, the dreaded risk of permissions errors is ever present and it can be very difficult at times to figure out which CRM feature relates to what security permission; it doesn’t help as well when some of the system entity logical names are entirely different from their display names!

A good case in point is Server-Side Synchronisation, a brilliant feature that takes a lot of the headache out of setting up your colleague’s e-mail addresses on CRM. But, if you decide to create your own custom security role in Dynamics CRM 2015 or earlier, you may end up running into this very frustrating error message when attempting to test and enable your users’ mailbox:

ServerSideSyncError

Well, at least we’ve got an error message – what does our best friend Google say? Rather annoyingly, there isn’t much that comes back search wise, not even an official page from Microsoft that provides a list of the permissions that are needed in order to use this feature.

A (not so quick) support case with Microsoft in order to find out just what permissions I need to increase/add onto my role will likely result in an answer similar to this:

“In order to resolve the issue, make a copy of an existing security role and then reduce the privileges accordingly, as there are some hidden privileges within these roles that affect this feature.”

“Hidden permissions” you say? That smells suspicious and is something that I have never come across in my working with CRM (though I am of course happy to be stood corrected). Also, what if in reducing the permissions to suit my businesses requirement, I accidentally remove the privileges that are needed for this work? Looks like I’m going to have to find out which privileges are needed the hard way.

So, after some trial and error, I can now provide a complete list of all the permissions that you need to have on your security role in order to Server Side Sync to work successfully. Please note the below assumes that you already have a separate security role setup that gives relevant permissions on the Appointment, Contacts and Activities entities within CRM:

Incoming/Outgoing E-mail

  • Email Server Profile
    • Organization level Read
  • Mailbox
    • User level Create, Read & Write

ServerSideSync_IncomingOutcomingPrivileges

Appointments, Contacts and Tasks

  • Organization
    • Organization level Read
  • Sync to Outlook
    • Full Privileges

ServerSideSync_ACTPrivileges_1

ServerSideSync_ACTPrivileges_2

 

With all of the these privileges assigned, our test and enable of the mailbox works successfully:

ServerSideSync_SuccessAlerts

Hopefully this helps someone who has spent countless hours pulling their hair out on how to get this working.

For those of you that are upgrading to CRM 2016 in the near future, there’s some good news relating to this: an extra button has been added on the error message that lets you expand it and view the system privilege name that is missing:

ServerSideSyncError_2016ErrorDetails

So based on the above message of “prvReadOrganization privilege”, we know that we need to give Read Privilege on the Organization entity! This is definitely a big help and a welcome new feature to have, as you can then go through and gradually add the permissions missing until everything is working. It’s little things like this which is making me more and more excited about upgrading to 2016 in the near future.

Does anyone else have any tips or advice on how to get certain features within CRM and what privileges are needed? Please use the comments below to share.