Welcome

Welcome to my blog. This blog is mostly intended for things that I find related to my development. As most of my time is spent developing for Microsoft Dynamics CRM, most of my blog posts will be directly related to that. Once in a while, I might have some other random posts that might cross my mind.

Advertisements

Retrieving Xrm.Page and Xrm.Utility in iframes on the mobile tablet app in CRM 2016

With the release of CRM 2016, we now have the ability to use iframes in the tablet mobile application. This is a big deal, as it will allow us to include external web pages and web resources in our forms for mobile users to interact with. One of the biggest benefits would be the creation of HTML and JavaScript web resources that interact with the entity data by retrieving and updating values from the entity form or reading and updating data directly in the CRM. This is accomplished by referencing Xrm.Page and Xrm.Utility from the web resource, an issue that turned out to be tricky on the tablet mobile app.

Take a very simple web resource example. In an iframe on the account form, reference an HTML resource that shows the current account email and gives the user the ability to change the email address in the iframe and update it immediately on the account page. While this would be an unnecessary feature in a production CRM, it does demonstrate the functionality in question.

First we create the HTML page in question. Well name it simply: EmailUpdate.html.

<html>
<head>
<meta charset="utf-8" />
<title>Testing</title>
<script>
 var Current = null;
 var Error = null;
 var New = null;
 var Xrm = null;

 function onLoad()
 {
 Current = window.document.getElementById("current");
 Error = window.document.getElementById("error");
 New = window.document.getElementById("new");
 Xrm = window.parent.Xrm;

 if (!Xrm) { Error.innerHTML = "{unable to find parent Xrm}"; return; }

 var field = Xrm.Page.getAttribute("emailaddress1");
 if (!field) { Error.innerHTML = "{unable to find email field}"; return; }

 var value = field.getValue();
 if (!value) { Current.innerHTML = "{no email specified}"; }
 else { Current.innerHTML = value; }
 }

 function onChangeClick()
 {
 var value = New.value;
 var field = Xrm.Page.getAttribute("emailaddress1");
 if (!field) { Error.innerHTML = "{unable to find email field}"; return; }

 field.setValue(value);
 New.value = "";
 window.document.location.reload();
 }

 function onStaticClick()
 {
 New.value = "tester@test.corp";
 onChangeClick();
 }
 </script>
</head>
<body onload="onLoad();">
The current email address is: <span id="current">{unknown}</span> <span id="error" style="color: red"></span>
Change email address: <input type="text" id="new" /> <input type="button" id="change" value="Change" onclick="onChangeClick();" />
<a href="#" onclick="onStaticClick();">Click here to change email to tester@test.corp</a>
</body>
</html>

Once added to the CRM, we can then check the box to Enable for Tablet and publish the changes. The WebResource page will tell us the URL of our WebResource, which we can copy to use on the Account form.

WebResource1

On the account form, we add the appropriate iframe to the form. Again, keeping things simple, we’ll name it EmailUpdate. In the iframe configuration, we also need to specify the URL. It won’t let us specify a relative URL or a WebResource directly, so we have to add the full URL as shown on the WebResource page. This doesn’t automatically make it visible to tablets, though, as we need to scroll down the page and make sure Enable for Tablets is checked. In addition, since scripts will be communicating between the iframe and the account form, we will need to uncheck Restrict cross frame scripting.

IFrame1

We’re done with the iframe, and can publish the changes. We go to a web browser and test the results, satisfied that they are working. Then you grab your iPad and test using the mobile tablet app, only to find that nothing is happening. The script in the onLoad event is failing silently, with no indication of what the problem is.

What is happening is that the call to parent.Xrm is failing with a security error. The reason for the security error is that the page, as expected, is running on the org domain, as you had entered into the iframe on the account form. The problem is, the mobile app is not running in the same domain. In the online instance I was using, it added a -d after the org name. This mismatch prevents the WebResource from retrieving Xrm.Page or Xrm.Utility. As a result, more needed to be done to resolve this issue.

First, we want to create a simple function that will generate the URL of the WebResource dynamically, instead of it being static.

function setFrameResource(frameName, webResourceName)
{
var frame = Xrm.Page.getControl(frameName);
var clientUrl = Xrm.Page.context.getClientUrl();
var resourceUrl = clientUrl + "/WebResources/" + webResourceName;
frame.setSrc(resourceUrl);
}

By using getClientUrl() we’re able to retrieve the correct domain that the CRM is running under. What this also allows is a greater flexibility when porting between a development sandbox and a production instance, as we are now domain agnostic.

We will upload this simple JavaScript file and attach it to the account form. On the iframe settings, we’ll change the URL to about:blank to cause the iframe to be blank when first loading. Then, in the account form onLoad, we would add the call to the function that we created, passing in just the parameters “IFRAME_EmailUpdate” and “new_EmailUpdate.html”. Note that I’m only passing the name, not the full WebResource URL.

FormOnLoad1

Save an publish the changes, then test it again. This simple function will work as intended in both applications. It would be nice if Microsoft gave us the ability to reference WebResources instead of a static URL, but since that isn’t an option, always set your WebResource to load dynamically, whether it is from the onLoad event, or another triggering event in your business process.

There are additional considerations and warnings that Microsoft gives us when using WebResources from tablets, and I encourage you to read through them and write your code accordingly: https://msdn.microsoft.com/en-us/library/dn858562.aspx

 

CRM 2011 Entity Object Type Codes

I found this list of Entity Object Type Codes for CRM 2011 that was compiled by Sureshkumar at the Microsoft Dynamics Community Forums. I am including it here for reference.

EntityLogicalName DisplayName OTC
account Account 1
accountleads 16
activitymimeattachment E-Mail Attachment 1001
activityparty Activity Party 135
activitypointer Activity 4200
annotation Note 5
annualfiscalcalendar Annual Fiscal Calendar 2000
applicationfile Application File 4707
appointment Appointment 4201
asyncoperation System Job 4700
attachment Attachment 1002
attributemap Attribute Map 4601
audit Auditing 4567
bulkdeletefailure Bulk Delete Failure 4425
bulkdeleteoperation Bulk Delete Operation 4424
bulkoperation Quick Campaign 4406
bulkoperationlog Bulk Operation Log 4405
businessunit Business Unit 10
businessunitmap Business Unit Map 6
businessunitnewsarticle Announcement 132
calendar Calendar 4003
calendarrule Calendar Rule 4004
campaign Campaign 4400
campaignactivity Campaign Activity 4402
campaignactivityitem Campaign Activity Item 4404
campaignitem Campaign Item 4403
campaignresponse Campaign Response 4401
clientupdate Client update 36
columnmapping Column Mapping 4417
commitment Commitment 4215
competitor Competitor 123
competitoraddress Competitor Address 1004
competitorproduct Competitor Product 1006
competitorsalesliterature 26
connection Connection 3234
connectionrole Connection Role 3231
connectionroleassociation 3232
connectionroleobjecttypecode Connection Role Object Type Code 3233
constraintbasedgroup Resource Group 4007
contact Contact 2
contactinvoices 17
contactleads 22
contactorders 19
contactquotes 18
contract Contract 1010
contractdetail Contract Line 1011
contracttemplate Contract Template 2011
customeraddress Address 1071
customeropportunityrole Opportunity Relationship 4503
customerrelationship Customer Relationship 4502
dependency Dependency 7105
dependencynode Dependency Node 7106
discount Discount 1013
discounttype Discount List 1080
displaystring Display String 4102
displaystringmap Display String Map 4101
documentindex Indexed Article 126
duplicaterecord Duplicate Record 4415
duplicaterule Duplicate Detection Rule 4414
duplicaterulecondition Duplicate Rule Condition 4416
email E-mail 4202
emailhash E-Mail Hash 4023
emailsearch E-Mail Search 4299
entitymap Entity Map 4600
equipment Facility/Equipment 4000
fax Fax 4204
fieldpermission Field Permission 1201
fieldsecurityprofile Field Security Profile 1200
filtertemplate Filter Template 30
fixedmonthlyfiscalcalendar Fixed Monthly Fiscal Calendar 2004
goal Goal 9600
goalrollupquery Rollup Query 9602
import Data Import 4410
importdata Import Data 4413
importentitymapping Import Entity Mapping 4428
importfile Import Source File 4412
importjob Import Job 9107
importlog Import Log 4423
importmap Data Map 4411
incident Case 112
incidentresolution Case Resolution 4206
integrationstatus Integration Status 3000
internaladdress Internal Address 1003
interprocesslock Inter Process Lock 4011
invaliddependency Invalid Dependency 7107
invoice Invoice 1090
invoicedetail Invoice Product 1091
isvconfig ISV Config 4705
kbarticle Article 127
kbarticlecomment Article Comment 1082
kbarticletemplate Article Template 1016
lead Lead 4
leadaddress Lead Address 1017
leadcompetitors 24
leadproduct 27
letter Letter 4207
license License 2027
list Marketing List 4300
listmember Marketing List Member 4301
lookupmapping Lookup Mapping 4419
mailmergetemplate Mail Merge Template 9106
metric Goal Metric 9603
monthlyfiscalcalendar Monthly Fiscal Calendar 2003
msdyn_postalbum Profile Album 10000
msdyn_postconfig Post Configuration 10001
msdyn_postruleconfig Post Rule Configuration 10002
msdyn_wallsavedquery Wall View 10003
msdyn_wallsavedqueryusersettings Filter 10004
notification Notification 4110
opportunity Opportunity 3
opportunityclose Opportunity Close 4208
opportunitycompetitors 25
opportunityproduct Opportunity Product 1083
orderclose Order Close 4209
organization Organization 1019
organizationstatistic Organization Statistic 4708
organizationui Organization UI 1021
owner Owner 7
ownermapping Owner Mapping 4420
phonecall Phone Call 4210
picklistmapping List Value Mapping 4418
pluginassembly Plug-in Assembly 4605
plugintype Plug-in Type 4602
plugintypestatistic Plug-in Type Statistic 4603
post Post 8000
postcomment Comment 8005
postfollow Follow 8003
postlike Like 8006
postregarding Post Regarding 8002
postrole Post Role 8001
pricelevel Price List 1022
principalattributeaccessmap 43
principalentitymap 41
principalobjectaccess 11
principalobjectaccessreadsnapshot 90
principalobjectattributeaccess Field Sharing 44
privilege Privilege 1023
privilegeobjecttypecodes Privilege Object Type Code 31
processsession Dialog Session 4710
product Product 1024
productassociation Product Association 1025
productpricelevel Price List Item 1026
productsalesliterature 21
productsubstitute Product Substitute 1028
publisher Publisher 7101
publisheraddress Publisher Address 7102
quarterlyfiscalcalendar Quarterly Fiscal Calendar 2002
queue Queue 2020
queueitem Queue Item 2029
quote Quote 1084
quoteclose Quote Close 4211
quotedetail Quote Product 1085
recordcountsnapshot 91
recurrencerule Recurrence Rule 4250
recurringappointmentmaster Recurring Appointment 4251
relationshiprole Relationship Role 4500
relationshiprolemap Relationship Role Map 4501
replicationbacklog Replication Backlog 1140
report Report 9100
reportcategory Report Related Category 9102
reportentity Report Related Entity 9101
reportlink Report Link 9104
reportvisibility Report Visibility 9103
resource Resource 4002
resourcegroup Scheduling Group 4005
resourcegroupexpansion Resource Expansion 4010
resourcespec Resource Specification 4006
ribboncommand Ribbon Command 1116
ribboncontextgroup Ribbon Context Group 1115
ribboncustomization Application Ribbons 1120
ribbondiff Ribbon Difference 1130
ribbonrule Ribbon Rule 1117
ribbontabtocommandmap Ribbon Tab To Command Mapping 1113
role Security Role 1036
roleprivileges 12
roletemplate Role Template 1037
roletemplateprivileges 28
rollupfield Rollup Field 9604
salesliterature Sales Literature 1038
salesliteratureitem Sales Attachment 1070
salesorder Order 1088
salesorderdetail Order Product 1089
salesprocessinstance Sales Process Instance 32
savedquery View 1039
savedqueryvisualization System Chart 1111
sdkmessage Sdk Message 4606
sdkmessagefilter Sdk Message Filter 4607
sdkmessagepair Sdk Message Pair 4613
sdkmessageprocessingstep Sdk Message Processing Step 4608
sdkmessageprocessingstepimage Sdk Message Processing Step Image 4615
sdkmessageprocessingstepsecureconfig Sdk Message Processing Step Secure Configuration 4616
sdkmessagerequest Sdk Message Request 4609
sdkmessagerequestfield Sdk Message Request Field 4614
sdkmessageresponse Sdk Message Response 4610
sdkmessageresponsefield Sdk Message Response Field 4611
semiannualfiscalcalendar Semiannual Fiscal Calendar 2001
service Service 4001
serviceappointment Service Activity 4214
servicecontractcontacts 20
serviceendpoint Service Endpoint 4618
sharepointdocumentlocation Document Location 9508
sharepointsite SharePoint Site 9502
site Site 4009
sitemap Site Map 4709
solution Solution 7100
solutioncomponent Solution Component 7103
statusmap Status Map 1075
stringmap String Map 1043
subject Subject 129
subscription Subscription 29
subscriptionclients Subscription Clients 1072
subscriptionmanuallytrackedobject Subscription Manually Tracked Object 37
subscriptionsyncinfo Subscription Synchronization Information 33
subscriptiontrackingdeletedobject Tracking information for deleted entities 35
systemform System Form 1030
systemuser User 8
systemuserbusinessunitentitymap SystemUser BusinessUnit Entity Map 42
systemuserlicenses 13
systemuserprincipals System User Principal 14
systemuserprofiles 1202
systemuserroles 15
task Task 4212
team Team 9
teammembership 23
teamprofiles Team Profiles 1203
teamroles 40
template E-mail Template 2010
territory Territory 2013
timezonedefinition Time Zone Definition 4810
timezonelocalizedname Time Zone Localized Name 4812
timezonerule Time Zone Rule 4811
transactioncurrency Currency 9105
transformationmapping Transformation Mapping 4426
transformationparametermapping Transformation Parameter Mapping 4427
unresolvedaddress Unresolved Address 2012
uom Unit 1055
uomschedule Unit Group 1056
userentityinstancedata UserEntityInstanceData 2501
userentityuisettings User Entity UI Settings 2500
userfiscalcalendar User Fiscal Calendar 1086
userform User Dashboard 1031
userquery Saved View 4230
userqueryvisualization User Chart 1112
usersettings User Settings 150
webresource Web Resource 9333
webwizard Web Wizard 4800
wizardaccessprivilege Web Wizard Access Privilege 4803
wizardpage Wizard Page 4802
workflow Process 4703
workflowdependency Process Dependency 4704
workflowlog Process Log 4706
workflowwaitsubscription Workflow Wait Subscription 4702

Source: http://community.dynamics.com/crm/f/117/t/99386.aspx#.Ufy7tZLVBaw

Creating a Multi-Entity Lookup in CRM 2011

Important Note: This feature only works in CRM 2011. This is using unsupported customization which broke in 2013 and beyond.

In Microsoft Dynamics CRM 2011, there are certain lookups that will allow you to select from one of several different entity types. For example, on activities, there is the Regarding field, which allows you to choose from any entity that has activities enabled, as shown in the screenshot below.

Regarding Lookup

The regarding lookup is an example of the multi-entity lookup included in the CRM.

The ability to choose a single record from different entities is a very useful feature. The problem is, Microsoft did not include the ability for the CRM customizer or administrator to add lookups that reference multiple entities. When you create a lookup field, it can only reference the single entity is was created to map to.

The Requirement

While it is most likely not possible that you would need as an extensive a list as what is used in the Regarding lookup on activities, it is quite possible that we would need to reference multiple entities from a single lookup. Let’s say, for example, that you want to identify a referral source when creating a lead. This referral source could be an account, a contact, or even another lead. So, in our case, we would want a lookup that will allow us to choose from any of those three entities.

The Problems

First thought would be to create a lookup to one entity only. From that, we would add JavaScript to allow you to look at the other entities. While that is part of the solution, it isn’t complete. If you try to save the record with a different entity type, you will receive an error.

The next thought would be to create hidden text fields to store the entity logical name, the entity id, and the name. Using JavaScript you would populate the lookup and the fields on load, save, and change. Again, this is part of the solution, but it also has a flaw. When you populate a lookup on load, and the lookup entity type differs, the save button does not function at all. It acts as if there were no changes to the form. In my search for a solution, I found several blogs that did exactly this. Perhaps it worked on earlier versions of the CRM, or they never had to go back and change any values on the form. Whatever the situation, it was not an ideal solution.

The Solution

The solution to the issue requires creating multiple lookups, one to each entity that would need to be referenced in the multi-entity lookup. Using JavaScript, we will show or hide the appropriate lookup fields, populate and clear data from the lookup fields as appropriate, and create the multi-entity lookup.

Lead Lookup Fields

Three lookup fields, one to each entity, all with the same label.

The first step in creating our “Referred by” multi-entity lookup is to create three new lookup fields. For simplicity sake, I have named the fields “Referral Account,” “Referral Contact,” and “Referral Lead.” These three fields are then added to the desired position on the lead form. It is important to make sure they are grouped together as displayed.

Once the three fields have been added to the form, you will want to edit the properties of each of the fields to rename the labels. In this case, I have changed all three fields to “Referred By.” In addition, I have hidden each of the fields by default, as we will use JavaScript to show the appropriate field.

The next step will be to create the JavaScript file and enter the appropriate code. There is one global variable and five functions required for the multi-entity lookup to function, display and save properly. I will break apart each function to explain their purpose.

var changedControl = null;

The first line of our JavaScript file contains our global variable, which is set to null. This variable will hold the name of the lookup that was changed. As any one of the three fields can be displayed, we need to know which lookup fields to update when the value of the visible lookup is changed.

function BuildLookup(lookupId, lookupEntityName, lookupType, lookupName)
{
    var lookup = new Array();
    lookup[0] = new Object();
    lookup[0].displayClass = "ms-crm-Lookup-Item";
    lookup[0].keyValues = new Object();
    lookup[0].values = new Object();
    lookup[0].onclick = "openlui()";
    lookup[0].id = lookupId;
    lookup[0].entityType = lookupEntityName;
    lookup[0].typename = lookupEntityName;
    lookup[0].name = lookupName;
    lookup[0].type = lookupType;
    return lookup;
}

The BuildLookup function is next in our JavaScript. This helper function will create the necessary lookup object that will populate both the hidden lookup field and the visible multi-entity lookup field.

function BuildMultiLookup(control)
{
    var lookupIcons = "/_imgs/ico_16_1.gif:/_imgs/ico_16_2.gif:/_imgs/ico_16_4.gif";
    var lookupNames = "account:1,contact:2,lead:4";
    var lookupTypes = "1,2,4";

    document.getElementById(control).setAttribute("lookuptypes", lookupTypes);
    document.getElementById(control).setAttribute("lookuptypeIcons", lookupIcons);
    document.getElementById(control).setAttribute("lookuptypesnames", lookupNames);

    var accountLookup = Xrm.Page.getAttribute("new_referralaccount").getValue();
    var contactLookup = Xrm.Page.getAttribute("new_referralcontact").getValue();
    var leadLookup = Xrm.Page.getAttribute("new_referrallead").getValue();
    var newLookup = null;

    if (!IsNull(accountLookup))
    {
        newLookup = BuildLookup(accountLookup[0].id, "account", accountLookup[0].type, accountLookup[0].name);
    }
    else if (!IsNull(contactLookup))
    {
        newLookup = BuildLookup(contactLookup[0].id, "contact", contactLookup[0].type, contactLookup[0].name);
    }
    else if (!IsNull(leadLookup))
    {
        newLookup = BuildLookup(leadLookup[0].id, "lead", leadLookup[0].type, leadLookup[0].name);
    }

    if (!IsNull(newLookup))
    {
        Xrm.Page.getAttribute(control).setValue(newLookup);
    }
}

The BuildMultiLookup function is another helper function that is used to create our multi-entity lookup. The top half of the function allows the lookup to reference the account, contact, and lead entities. The bottom half of the function builds the lookup data and displays the data in the multi-entity lookup field.

The lookupIcons, lookupNames, and lookupTypes might appear a little confusing at first glance. The question becomes, how do you know what to put there? You will notice that each of the entities have a certain type, which is their type code. You can easily change or expand this list using the same format, as long as you know the type code for the entity in question. To get the type code, simply open up the entity in your browser, and look at the URL querystring. The number that is after the etc is the type code for that entity. This will work for both the out-of-the-box entities, as well as any custom entities you create.

The image files for custom entities require special handling. The default icon path for a custom entity is as follows: /_Common/icon.aspx?iconType=GridIcon&objectTypeCode=10007 If you have added your own custom icon to your custom entity, you can find the path to your icon file from your web resources.

function Referral_OnChange(context)
{
    changedControl = context.getEventSource().getName();
    var lookup = Xrm.Page.getAttribute(changedControl).getValue();
    if (lookup != null)
    {
        var lookupId = lookup[0].id;
        var lookupName = lookup[0].name;
        var lookupType = lookup[0].type;

        if (changedControl != "new_referralaccount")
        {
            Xrm.Page.getAttribute("new_referralaccount").setValue(null);
        }
        if (changedControl != "new_referralcontact")
        {
            Xrm.Page.getAttribute("new_referralcontact").setValue(null);
        }
        if (changedControl != "new_referrallead")
        {
            Xrm.Page.getAttribute("new_referrallead").setValue(null);
        }

        if (lookupType == "1") // account
        {
            Xrm.Page.getAttribute("new_referralaccount").setValue(BuildLookup(lookupId, "account", lookupType, lookupName));
        }
        else if (lookupType == "2") // contact
        {
            Xrm.Page.getAttribute("new_referralcontact").setValue(BuildLookup(lookupId, "contact", lookupType, lookupName));
        }
        else if (lookupType == "4") // lead
        {
            Xrm.Page.getAttribute("new_referrallead").setValue(BuildLookup(lookupId, "lead", lookupType, lookupName));
        }
    }
}

The OnChange event is what will be called when any of the three referral lookups are changed. The context must be passed into the function in order to get the name of the control that triggered the event. The next task the function does is to clear all the other lookup fields, aside from the one that had triggered the function. Finally, the function will populate the data from the multi-select lookup into the appropriate lookup field (Account to account, contact to contact, lead to lead).

function Referral_OnLoad()
{
    var contactLookup = Xrm.Page.getAttribute("new_referralcontact").getValue();
    var leadLookup = Xrm.Page.getAttribute("new_referrallead").getValue();

    if (contactLookup != null)
    {
        Xrm.Page.ui.controls.get("new_referralcontact").setVisible(true);
        BuildMultiLookup("new_referralcontact");
    }
    else if (leadLookup != null)
    {
        Xrm.Page.ui.controls.get("new_referrallead").setVisible(true);
        BuildMultiLookup("new_referrallead");
    }
    else
    {
        Xrm.Page.ui.controls.get("new_referralaccount").setVisible(true);
        BuildMultiLookup("new_referralaccount");
    }
}

The OnLoad event is triggered when the form is loaded. This function will determine which of the lookup fields has data in it, and will display only that one lookup. If none of the fields have data, then it will display the referralaccount field by default. It will then call the BuildMultiLookup function earlier, to change the lookup into a multi-edit lookup.

function Referral_OnSave()
{
    if (changedControl != null)
    {
        var accountLookup = Xrm.Page.getAttribute("new_referralaccount").getValue();
        var contactLookup = Xrm.Page.getAttribute("new_referralcontact").getValue();
        var leadLookup = Xrm.Page.getAttribute("new_referrallead").getValue();

        if (changedControl == "new_referralaccount" && (contactLookup != null || leadLookup != null))
        {
            Xrm.Page.getAttribute("new_referralaccount").setValue(null);
        }

        if (changedControl == "new_referralcontact" && (accountLookup != null || leadLookup != null))
        {
            Xrm.Page.getAttribute("new_referralcontact").setValue(null);
        }

        if (changedControl == "new_referrallead" && (accountLookup != null || contactLookup != null))
        {
            Xrm.Page.getAttribute("new_referrallead").setValue(null);
        }
    }
}

The OnSave function performs some cleanup to the lookup fields before saving can occur. Because the multi-entity lookup is based on a single entity, it is not able to save the value if the entity type is a different type. For that reason, if the type differs, the lookup field is cleared prior to saving.

Custom Multi-Entity Lookup

The completed multi-entity lookup dialog.

Once all the pieces of the JavaScript file have been added and saved, it is necessary to make the final modifications to the form. In the form properties, for the event OnLoad, you will want to create an event that calls Referral_OnLoad. You will also want to create and event that calls Referral_OnSave for the event OnSave. Finally, edit the properties of each of the three fields. If you haven’t renamed and hidden them, do so at this time. You will also want to create an event that calls Referral_OnChange. When you create the event, it is important that you check the box for “Pass execution context in the first parameter.” As mentioned previously, this is necessary for the JavaScript to know which control had triggered the OnChange event.

With those changes done, it is time to save and publish your changes. Your new multi-entity lookup is now ready for use.

Connection Strings in CRM 2011

When working with plugins, authentication for the CRM is handled automatically. The user’s credentials and appropriate permissions are received right from the context. What if you need to write a service or application to connect to the CRM, though? For that, CRM 2011 SDK makes it easy to do with a simplified connection strings. Below is an example of an app.config file that contains the connection strings.

<?xml version="1.0"?>
<configuration>
  <connectionStrings>

      <!-- Online using Office 365 -->
      <!-- <add name="Server=CRM Online, organization=contoso, user=someone" connectionString="Url=https://contoso.crm.dynamics.com; Username=someone@contoso.onmicrosoft.com; Password=password;"/> -->

      <!-- Online using Microsoft account (formerly Windows Live ID) -->
      <!-- <add name="Server=CRM Online, organization=contoso, user=someone@example.com" connectionString="Url=https://contoso.crm.dynamics.com; Username=someone@example.com; Password=password; DeviceID=11hfn41bbqrg580vyvoea05abc; DevicePassword=fuqNIlx%e$.l*+ax_#8O4abc;"/>-->

      <!-- On-premises with provided user credentials -->
      <!-- <add name="Server=myserver, organization=AdventureWorksCycle, user=administrator" connectionString="Url=http://myserver/AdventureWorksCycle; Domain=mydomain; Username=administrator; Password=password;"/> -->

      <!-- On-premises using Windows integrated security -->
      <!--<add name="Server=myserver, organization=AdventureWorksCycle" connectionString="Url=http://myserver/AdventureWorksCycle;"/>-->

      <!-- On-premises (IFD) with claims -->
      <!--<add name="Server=litware.com, organization=contoso, user=someone@litware.com" connectionString="Url=https://contoso.litware.com; Username=someone@litware.com; Password=password;"/>-->

  </connectionStrings>
</configuration>

Adding to your app.config file is an easy way to manage, but if you’re building an application, you might want the user to log in from the application. If that is the case, you can put the connection strings directly into your code, having the user pass in the appropriate parts.

This, of course, is only part of the issue. Once you have determined which connection string, and the appropriate parameters to pass in the connection string, you will need to add this into your code. First thing you will need to do is add the following assemblies from the CRM 2011 SDK into your solution:

  • Microsoft.Crm.Sdk.Proxy.dll
  • Microsoft.Xrm.Client.dll
  • Microsoft.Xrm.Sdk.dll

These assemblies will give you access to the methods that you will need to perform the authentication from your application. In order to simplify writing the code, you should add the following using directives:

using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

Once you have obtained the correct connection string, whether it was retrieving it from the app.config settings, or dynamically in your code, connecting to the CRM is a very simple matter.

CrmConnection connection = CrmConnection.Parse(connectionString);
OrganizationService orgService = new OrganizationService(connection);

Assuming the connectionString you’re passing in is valid, you can now use orgService to issue your service commands to the CRM, such as Create, Delete and Execute.

One other thing to note, creating the service itself will not tell you whether or not you have logged in successfully. You will not receive an error until you try to perform an action against the CRM directly. To address this issue, I prefer to add a simple call to WhoAmI to see if I’ve been successful in logging in. If I haven’t an exception will be thrown by the code that can be caught.

try
{
    CrmConnection connection = CrmConnection.Parse(connectionString);
    OrganizationService orgService = new OrganizationService(connection);
    WhoAmIRequest request = new WhoAmIRequest();
    orgService.Execute(request);

    // Code to perform queries, create, update, delete, etc.
}
catch (Exception e)
{
    // Error Handling
}

That’s all there is to it. For more information on this simplified connection to the CRM, as well as all the possible connection string parameters, check out the article on MSDN: Simplified Connection to Microsoft Dynamics CRM

Filtering Lookups and PartyLists in CRM 2011

I had a requirement to filter the Recipient (To) Party List based on the value in the Regarding (RegardingObjectID) field, and the converse to filter the Regarding lookup field based on the value of the Recipient field. In this case, there was a N:N relationship between Accounts and Contacts, and the Recipient always had to be a Contact filtered based on the Account shown in the Regarding Field, and the Regarding Field had to be filtered based on the Contact that was in the Recipient field.

My searching for the solution brought me to this blog: http://sliong.wordpress.com/2012/04/18/crm-2011-filtered-lookup-using-javascript/

var viewId = "{1DFB2B35-B07C-44D1-868D-258DEEAB88E2}"; //Random GUID
var entityName = "account";
var viewDisplayName = "Accounts beginning with Abc";

var fetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
"<entity name='account'>" +
"<attribute name='name' />" +
"<attribute name='primarycontactid' />" +
"<attribute name='telephone1' />" +
"<attribute name='accountid' />" +
"<order attribute='name' descending='false' />" +
"<filter type='and'>" +
"<condition attribute='name' operator='like' value='Abc%' />" +
"</filter>" +
"</entity>" +
"</fetch>";

var layoutXml = "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" +
"<row name='result' id='accountid'>" +
"<cell name='name' width='300' />" +
"<cell name='primarycontactid' width='150' />" +
"<cell name='telephone1' width='100' />" +
"</row>" +
"</grid>";

Xrm.Page.getControl("new_accountid").addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);

Everything worked flawlessly, until I got to adapting it to the party field. At first, I thought the party field might have worked differently. Turned out that errors in the way that I adapted the code were to blame. This code worked just as well for the Party List field, as it had for the Lookup field. I just had to make sure I was using the correct FetchXML, passing the correct IDs, and setting it to the correct entity.

Getting the Opportunity Entity in Plugin when Closing as Won

I had an interesting situation that I had to overcome when writing a plugin for Microsoft Dynamics CRM 2011. When an Opportunity was Closed as Won, I needed my plugin to generate a new Account Number if one didn’t already exist on the account. I thought this would be fairly straightforward to do. Just register the plugin on the SetState and SetStateDynamicEntity, add a Post Image for the Opportunity, and go from there. To my surprise, it didn’t work. My plugin wasn’t getting fired at all on those messages. So, what message was I supposed to use?

After searching the issue, I found this blog entry: http://mscrmshop.blogspot.com/2008/06/opportunity-setstate-plugin-problems.html

I tried a lot of different things and at the end I registerfed my pluggin for win and lose message instead of setstate or setstatedynamicentity message. It worked this time.

So, there was a Win and a Lose message. As I was only concerned about the Win, I altered my plugin to be registered on the Win message. That was when I encountered my second issue. I could not register a post image on the Win message. How was I going to find the Account ID if I couldn’t get to the entity? So, I threw in a bunch of tracing information to dump the information from the context, to see what values I had available to me, and forced the plugin to throw an error so I could get to the tracing information. What I discovered was that, in the Input Parameters, there was an OpportuntityClose entity in there, that contained the Entity Reference to the original Opportunity Entity. That was just what I needed. So, my next step was to write a simple function where I could retrieve the OpportuntityId from the OpportunityClose Input Parameter.

private Guid GetOpportunityId(IPluginExecutionContext context)
{
    Guid opportunityId = Guid.Empty;

    if (context.InputParameters.Contains("OpportunityClose") &amp;&amp;
        context.InputParameters["OpportunityClose"] is Entity)
    {
        Entity entity = (Entity)context.InputParameters["OpportunityClose"];

        if (entity.Attributes.Contains("opportunityid") &amp;&amp;
           entity.Attributes["opportunityid"] != null)
        {
            EntityReference entityRef = (EntityReference)entity.Attributes["opportunityid"];

            if (entityRef.LogicalName == "opportunity")
            {
                opportunityId = entityRef.Id;
            }
        }               
    }

    return opportunityId;
}

With the Opportunity Id in hand, I was able to load the Opportunity Entity, read the Customer ID (to get the Account), and generate the required Account Number if necessary.

Authentication Failure with Plugin Registration Tool on CRM Online

Apparently, Microsoft changed the way the Plugin Registration is done when they integrated the CRM with Office 365. Up until now, I had been connecting to the old CRM Online instances, which always worked fine. Then, when I tried to connect to the new instance, I was getting an Authentication Failure before it even listed the organization names. I spent over 30 minutes trying to find the solution to this issue, which eventually led me to a Microsoft community forum in the UK that explained the Discovery URL had changed.

In the older way of connecting, that I was used to, I would create a new connection with the Discovery URL set to: https://orgname.crm.dynamics.com

In the new way of connection, you have to change that Discovery URL to: https://disco.crm.dynamics.com

If you have a doubt as to what the exact Discovery URL you should be using for your Plugin Registration, log into the CRM Online instance directly, go to Settings > Customizations > Developer Resources. On that page, you will see the Discovery URL (among other useful URLs) that you will need.

Once I switched to the new Discovery URL, I was able to log in without a problem and do my Plugin Registration.