Remove attributes from form which cannot be removed

There are some attributes on CRM default forms which you cannot remove. This post will show you how you can do this anyway. While doing so, please keep in mind that Microsoft must have good reasons to fix attributes on the form. Removing this might have implications, as always have a good backup ready.

The steps you need to follow to remove are:
- Export customizations for the specific entity
- Modify the form in the customizations xml
- Import the modified customizations

Since most of you are well known with CRM I will skip the Export and Import of customizations. If you need assistance with this, then don't try this change anyway :) To modify the customizations, open the file in notepad or another XML editor. In this file look for this path:

ImportExportXml - Entities - Entity - FormXml - forms - entity - form - tabs

Within tabs look for the tab with the correct name and similar for the sections. Get a bit familiar with the section xml once you have found the correct section. You'll find that there are a couple of lines that you need to remove. This is the example for the subjectid on the case entity:


<cell id="{a9859c32-0cdc-41b5-8e7e-3eb173cab4a8}">
<labels>
<label description="Onderwerp" languagecode="1043" />
<label description="Subject" languagecode="1033" />
</labels>
<control id="subjectid" classid="{270BD3DB-D9AF-4782-9025-509E298DEC0A}" datafieldname="subjectid" />
</cell>


Attributes which are locked on the form includes the following:
case - subjectid
case - contractid
case - contractdetailid

Note: Some of the attributes are used in hidden javascript codes. For instance the case contract contractid is used in the script for the customer onchange. You can also look at removing those scripts from the customizations file.



Changing default organisation

You can change the default organisation by opening the configuration manager. In this tool you can rightclick on the organisation which you want to set as default and click "set as default". You'll see that the name of the organisation has changed to: Orgname (default).

After you perform an IISReset you would expect the new default organisation to open up when you browse to your CRM server. Unfortunately that doesn't work immediately. My fellow MVP Marco Amoedo wrote this post in the community to explain why and how to change that behavior:

Hi Tony,

The default organisation will no change for the already created users, this change will only be applied for new users created after the change. This means that this changes is not applied in a retroactive fashion, only the new users that you create will have that organisation as default.

Each user record have stored the default organisation, so you would need to change them for each one of the already created users. You have two mechanisms. Either use this tool provided by Microsoft Support

Or directly modify the CRM Database to change the DefaultOrganizationId on the SystemUser table (like it is proposed here http://chicagotech.net/netforums/viewtopic.php?t=6046)

In either case, make a backup of the databases before just in case.

Hope it helps

Marco Amoedo



Change the CRMAppPool Identity

Most likely this is recorded in a thousand places, but I had a hard time finding info about what steps need to be taken to change the identity of the CrmAppPool. All you need to do is:
- Create a user in Active Directory
- Make sure the user has Domain User rights
- Add the user to the PrivUserGroup of CRM
- Make sure the user has 'log on as a service' priviledge

The last step can be executed on two approaches:
- Go to 'Local Security Policy' in the administrative tools ('Domain Controller Secuirty Settings' on a Domain Controller) and add the user to 'Log on as a service'
- Add the user to the CRM_WPG group in AD. This group is member of the 'Log on as a service' policy.

Hope this saves you some time, at least now that I have this documented here I can find it back *smile*



Automatic provisioning of Organizations

Microsoft CRM does have a separate webservice for doing deployment related activities. There is a Deployment SDK especially for this webservice. You can download that here. One of the activities you can execute is automatic provisioning of new organizations. In this post I'll dive into the code required for this as well as some of the common issues while doing so.

Basically following the next steps should be enough:
- create an application
- add a web reference to: http:///mscrmservices/2007/crmdeploymentservice.asmx
- write the following code:


Organization org = new Organization();
org.UniqueName = txtName.Text;
org.FriendlyName = txtName.Text;
org.SqlServerName = ConfigurationManager.AppSettings["SqlServerName"];
org.SrsUrl = ConfigurationManager.AppSettings["SrsUrl"];
org.BaseCurrencyCode = ConfigurationManager.AppSettings["BaseCurrencyCode"];
org.BaseCurrencyName = ConfigurationManager.AppSettings["BaseCurrencyName"];
org.BaseCurrencySymbol = ConfigurationManager.AppSettings["BaseCurrencySymbol"];

CreateRequest request = new CreateRequest();
request.Entity = org;

CrmDeploymentService crmDeployService = new CrmDeploymentService();
crmDeployService.Credentials = System.Net.CredentialCache.DefaultCredentials;

try
{
crmDeployService.Execute(request);
}
catch (SoapException se)
{
lblError.Text = String.Format("Creation of Organization failed with error:<br>{0}<br>Detail:<br>{1}", se.Message, se.Detail.InnerText);
}
catch (Exception ex)
{
lblError.Text = String.Format("Creation of Organization failed with error:<br>{0}", ex.Message);
}

When you don't want to use the default credentials, then you would supply the username and password like this:

crmDeployService.Credentials = new System.Net.NetworkCredential("administrator", "pass@word1", "litwareinc");

In some cases this might lead to a 401 Unauthorized error message though. This has to do with kerberos. When you do force your application to use NTLM though, then it works. You can do this by using this code:

CredentialCache credential = new System.Net.CredentialCache();
NetworkCredential netCred = new NetworkCredential("administrator", "pass@word1", "litwareinc");
credential.Add(new Uri("http://localhost:5555"), "NTLM", netCred);
crmDeployService.Credentials = credential;

Some other errors you might run into with their respective answers.

0x8004b001 Create new Organization (Name=orgname, Id=10d59133-ae34-de11-a5a9-0003ffab19fc) failed with Exception: System.Security.SecurityException: Requested registry access is not allowed.

This error occurs because the identity of the application pool which is used by Dynamics CRM has no rights to update the records in the registry hyve HKLM\Software\Microsoft\MSCRM. By default this is set to "Network Service". To get rid of the error, open the registry editor (regedit), browse to the MSCRM hyve, rightclick on the hyve and choose "Permissions". You can then give the correct user rights for updating the values.

Most likely you will now run into this error message:

Server was unable to process request.
Detail:
0x8004b001 Create new Organization (Name=orgname,
Id=2015e8d7-b434-de11-a5a9-000b2924ac91) failed with Exception: System.NullReferenceException: Object reference not set to an instance of an
object.

This message does show up because you are working with the Network Service as the Identity for the Application Pool. You should change the identity of the CrmAppPool (see my other blog post). Then do an iisreset and you should get passed this error message.

Do you run into this error message?

Server was unable to process request.
Detail:
0x8004b001 Create new Organization (Name=orgname, Id=2015e8d7-b434-de11-a5a9-0003ffab19fc) failed with Exception: Microsoft.Crm.CrmException: Invalid user auth.

Then you most likely have forgot to do an iisreset. Try that or even a complete server reboot.

Now your provisioning should be starting, but it won't finish due to this error message:

The operation has timed out

Creating a organization can take quite some time. The timeout should be increased so that the creation of the organization can finish successfully before the timeout has expired. Adding this line of code would help:

crmDeployService.Timeout = 6000000;


That should solve your issues regarding the automatically provisioning of organizations for Dynamics CRM.

There could be ofcourse many other issues like out of memory exceptions, but that you can 'easily' solve by adding memory. If you find any other issues with the respective solutions to this topic, please add them to the comments!



Walk through all elements on a form

In some cases it is required to set all attributes on a form to disabled or do some other logic like checking which attribute has been changed. To do this you do need to walk through each attribute of the CRM form. My fellow MVP Mitch Milam has posted the technique to do this more than a year ago:
http://blogs.infinite-x.net/2007/11/21/disabling-all-fields-on-a-crm-form/

The most important piece of this code is:


var iLen = crmForm.all.length;
for (i = 0; i < iLen; i++)
{
o = crmForm.all[i];
switch (o.tagName)
{
case "INPUT":
case "SELECT":
case "TEXTAREA":
case "IMG":
case "IFRAME":
if (o.id != "leadqualitycode")
{
o.disabled = true;
}
break;
default:
break;
}
}

This example does set all attributes to disabled, but another option would be to replace the o.disabled = true; line with something like:

if (o.IsDirty)
{
alert("The value of " + o.name + " has changed.");
}

This allows you to determine which attributes have been changed on the form.



401 Unauthorized when accessing the webservice

While there are many situations in which you can retrieve the error message 401: Unauthorized, there are some more difficult to find than others. Of course you have After you have checked the basic settings including:
- Are the default credentials used
- Does the user have an account in CRM
- Does the user have enough priviledges in CRM
- Is Windows Authentication enabled for the CRM website
- Is anonymous access disabled for the CRM website

If these settings do not solve the issue, then it's getting intereseting. One of the reasons I have experienced, has to do with two steps. In the first step, I've been using the CrmDiscoveryService to find the url's for the CrmService as well as the CrmMetadataService. The second step was using SPN's to allow the server to pass on the kerberos ticket to the server, even though it is a single server install.

Let me get a bit more in detail. The code which I used for the first step is:


CrmDiscoveryService disco = new CrmDiscoveryService();
disco.Credentials = cred;
disco.Url = server + "/MSCRMServices/2007/AD/CrmDiscoveryService.asmx";
RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
RetrieveOrganizationsResponse orgResponse = (RetrieveOrganizationsResponse)disco.Execute(orgRequest);
foreach (OrganizationDetail orgdetail in orgResponse.OrganizationDetails)
{
if (orgdetail.OrganizationName == org)
{
orgdetail.CrmServiceUrl;
}
}

This returns the CrmServiceUrl. Apparently the URL for the web service can have different servername than the url you have used in the code for accessing the CrmDiscoveryService. In my particular example there was the servername (srv-crm01), a dns name (crm) and of course the localhost that could be used to access the server. For accessing the CrmDiscoveryService the dns name was used. The service did return the servername as url for the crm web service.

With the help of a system engineer we found out that this server did have kerberos as authentication method specified. In the list of service principal names (SPN's) the servername was not listed for the http protocol. After this has been added for the servername as well as the fully qualified domain name, the error was gone. To add the SPN's execute the following commands:

setspn -A http/servername
setspn -A http/servername.fullyqualified.name

Don't forget to change the servername and servername.fullyqualified.name to the correct values of your environment.

If you happen to have the same error but find another solution, please share this by adding a comment to help your peers.



Hiding elements: the next evolution

One of my colleagues, Richard McCormick, has written a blog post for the Avanade internal employees. He has asked me to make this post publically available since it might be interesting for more people. Read on and get great code samples on how to hide attributes, elements and sections.

Hiding Links & Controls in CRM 4

Before I go on, I should point out that this solution is technically unsupported by Microsoft as it goes outside the CRM SDK, however the customization is small and easy to reverse to get into a state where Microsoft is supported.

With that said, this post aims to provide a quick JavaScript solution for hiding both controls on a CRM page and links in the left-hand navigation page on an Entity Form.

The code below provides three functions that can be used to show/hide specific items on a CRM form:


var HIDE = 'none';
var SHOW = 'block';

// Function to show/hide CRM controls on a CRM form
// such as text boxes, lookups, pick-lists etc.
function SetCrmControlVisible(elementName, visibility)
{
SetElementVisible(elementName + '_c', visibility);
SetElementVisible(elementName + '_d', visibility);
}

// Fuction to show/hide specific elements on a CRM form
// such as the left-hand link items (More Addresses, Workflows
// and even custom ISV links
function SetElementVisible(elementId, visibility)
{
var elem = document.getElementById(elementId);
if (elem != null)
{
elem.style.display = visibility;
}
}

// Function to show/hide Navigation "Sections" in the left-hand
// links (Such as Sales, Marketing and Service)
function SetParentElementVisible(elementId, visibility)
{
var elem = document.getElementById(elementId);
if (elem != null && elem.parentElement != null)
{
elem.parentElement.style.display = visibility;
}
}

Add the above code to the Entity OnLoad (and ensure that you enable the event) and after that you are ready to show/hide elements on the CRM form.

By using the IE Developer toolbar, you can retrieve the id of the element to be hidden (note in IE 8 this come built in and is not a seperate download). Open an entity record (in this case a Contact), press CTRL+N to open it in a new window, and then select IE Developer Toolbar.

Using the controls, select the item you want to hide and get the element id. In this case I have selected the “Opportunities” link and the id is “navOpps



Using this, I can call the SetElementVisible function like this to hide the Opportunities link:

SetElementVisible('navOpps', HIDE);

To hide the entire “Sales” section from the left-hand navigation pane, I can once again find the id, but this time is of the parent item which would result in the following call to hide it:

SetParentElementVisible('_NA_SFA', HIDE);

Lastly, to hide an element on the form such as the “Job Title” field, you once again should use the IE Developer Toolbar to retrieve the element id and then call the SetCrmControlVisible function:

SetCrmControlVisible('jobtitle', HIDE);

By selecting either the textbox or the label, for the Job Title, the returned id will be “jobtitle_c” or “jobtitle_d” – remove everything including and after the “_” and pass that to the SerCrmControlVisible function and you are done!

Happy coding!