08 December 2010

Force CRM 4.0 Record Deletion

As standard CRM 4 deletes records, marked with deletionstatecode=2, on a 24 hourly basis.

Mitch Milam has written, an unsupported, utility that forces the deletion. The utility can be downloaded from his blog: http://blogs.infinite-x.net/2009/08/26/free-utility-run-crm-deletion-service. After installing the utility the exe can be found in \Program Files\CRM Accelerators\Run CRM Deletion Service.

There is also a utility, by Microsoft, that allows you to change the frequency of deletion and re-indexing. This utility is called Scale Group Job Editor. This tool can be downloaded from: http://code.msdn.microsoft.com/ScaleGroupJobEditor

24 November 2010

Import Organisation, change to default, gives Invalid User Authorization Error

When importing an organisation into Dynamics CRM 4.0 and making it default you sometimes come across the "Invalid Authorization error" message even though you're logged in as the System Administrator.

The following TSQL should be run on the SQL instance that hosts the CRM databases. It isn't a supported fix and I take no responsibility if it breaks your system. Remember backups are your friend!

The scripts have been taken from other resouces on the t'internet, but I can't remember where. When I find out I will acknowledge the original authors hard work.

-- Import Organisation, change default, gives Invalid User Authorization error
-- To Resolve this issue it is necessary to change the user's default organisation

--STEP 1
DECLARE @Org nvarchar(50)
set @Org = 'MSCRMORGNAME'

DECLARE @OrgDb nvarchar(150)
set @OrgDb= @Org + '_MSCRM'


DECLARE @UserName nvarchar(50)
set @UserName='DOMAIN\username'

execute ('select OrganizationId from ' + @OrgDb +'..organization where name=''' + @Org +'''')
execute ('select systemuserid, DomainName from ' + @OrgDb +'..SystemUserBase where DomainName='''+ @UserName + '''')


--STEP 2
-- User the systemuserid returned to get the correct GUID
select userid from MSCRM_CONFIG..systemuserorganizations where crmuserid = '[systemuserid]'

-- Use the userid to return the correct
select DefaultOrganizationId from MSCRM_CONFIG..systemuser where id = '[userid]'

--Use the DefaultOrganizationId and userid to update the table
update MSCRM_CONFIG..systemuser set defaultorganizationid = '[DefaultOrganizationId]' where id = '[OrganizationId]'

10 November 2010

CRM 4 ISV Web App Error Out

Sometimes when a web application has been deployed to the ISV folder within CRM it doesn't work instead it gives a Runtime Error.

To resolve this the web application's web.config file needs to be updated to include the following (immediately above the tag: </httpModules>)

<remove name="CrmAuthentication">

Note: You will probably need to comment the line out if you're testing the code through Visual Studio.

This is necessary because the ISV web application is inheriting from the parent CRM web site.

Hosting a virtual app With a Different .Net version

An error will occur if you try and host a web application within a web site and mix the .Net versions. This is because the virtual application will attempt to inherit from it's parent. This can be switched off by editing the parent web.config as follows:

Wrap <system.web> with:
<location path="." inheritinchildapplications="false">

For Example:

<location path="." inheritinchildapplications="false">
<system.web>
...
</system.web>
</location>

29 September 2010

Call CRM Web Service From JavaScript

There are instructions about calling the CRM Web Services from JavaScript in the SDK however it is necessary to define the SOAPXml that will be executed. There are several ways to define this SOAPXml:

1. Download and use the "JavaScript Web Service Calls 4.0" tool available from Stunware.

2. If you are calling the CRMService you can turn on tracing, perform the search manually, and retrieve the SOAPXml from the logs.

3. Calling the MetaDataService will not log in the tracing, an alternative to tracing is to use Fiddler. Generate code that will perform the query, turn on Fiddler and execute the code. Fiddler will show the SOAPXml executed.

The SOAPHeader must be setup correctly, to generate from within CRM JavaScript use the following:

var soapHeader = GenerateAuthenticationHeader();

This should then be injected into the SOAPXml.

Sample code (retrieve the objecttypecode from the metadata service:

//Get the servername that the current page was called from.
var fullUrl = location.href;
var slashPos = fullUrl.indexOf('/', 7);
var serverName = fullUrl.substr(7, slashPos - 7);

//Get the authentication header, CRM in built function
var soapHeader = GenerateAuthenticationHeader();

//Define the fetchXML that will get the object type code
var xmlPost = '';
xmlPost += '';
xmlPost += ' ';
xmlPost += soapHeader;
xmlPost += ' ';
xmlPost += ' ';
xmlPost += ' ';
xmlPost += ' 00000000-0000-0000-0000-000000000000';
xmlPost += ' EntityOnly';
xmlPost += ' ' + entityName + '';
xmlPost += ' false';
xmlPost += '
';
xmlPost += '
';
xmlPost += '
';
xmlPost += '
';

//Open the CRM metadata service
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', 'http://' + serverName + '/mscrmservices/2007/MetadataService.asmx', false);

//Initialise the call to the web service
xmlhttp.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
xmlhttp.setRequestHeader('SOAPAction', 'http://schemas.microsoft.com/crm/2007/WebServices/Execute');

//Call the metadata service
xmlhttp.send(xmlPost);


var objectTypeCode = '-1';

objectTypeCode = xmlhttp.responseXML.selectSingleNode("//ObjectTypeCode").text;

12 May 2010

Remove Advanced Find

I was recently asked if it is possible to remove Advanced Find from the user interface. My initial answer was: no and there's no need as a user can only search on data that they are allowed to. This didn't cut it as with Advanced Find it's possible to add columns which the users aren't supposed to see. There were some other details which I won't go into, suffice to say it had to go!

The following example will remove the advanced find but, it by an UNSUPPORTED METHOD, use it at your own risk!

This change involves adding some JavaScript to loader.aspx, before continuing make sure that it's been backed up first.

1. Open loader.aspx in a text editor of your choice.

2. Add the following JavaScript function just before the tag.

<script language="JavaScript" type="text/javascript">
function HideAdvancedFind()
{
//Check whether the iframe is completely loaded or not
if(document.getElementById("stage").readyState != "" && document.getElementById("stage").readyState!="loading")
{
try
{
document.frames[0].document.getElementById("btn_advfind").style.display="none";
document.frames[0].document.getElementById("_MIopenAdvFind").style.display="none";
}
catch(e)
{
}
}
}
</script>


3. Locate the frame named "menuBar" and just before the end of the tag add:

onreadystatechange="Javascript: HideAdvancedFind()"


4. Save and close loader.aspx and fire up CRM. The Advanced Find button has been removed from the menu-bar and the Tools menu.
Note: An IISReset may be necessary to clear the cache.

The finished result:




10 May 2010

Add a Button To a CRM 4.0 Form

There are some instances where it would be nice to be able to add a button straight on the CRM form. Unfortunately CRM doesn't provide a button control that can be added to the form. The alternative is to include the functionality to the menu-bar via the ISV.Config.

This blog will show you how to add a button to the form. This is a bit of smoke and mirrors really as we'll made a CRM attribute to appear to be button. The finished result looks like this:


The following steps will guide you through how the above was achieved:

1. Add a new text attribute to your entity, for this example it was called new_button. I set the maximum text length to 5, but the length is unimportant to this functionality.

2. Add a new section to the form, change the section formatting to 4 columns.
CRM Section Formatted to 4 columns



3. Add the new text attribute (new_button) to the new section that's just been created.

4. Change the Field Properties for new_button and ensure that Display Label on Form is un-checked.
CRM Attribute properties

5. The next step is to make the text-box look like a button, add the following to the form onLoad :

//Define the button style
var buttonStyle="font-family: Tahoma; font-size: 11px; line-height: 18px; height: 20px; width: 84px; text-align: center; cursor: pointer; border: 1px #3366CC solid; background-color: #CEE7FF; background-image: url('/_imgs/btn_rest.gif'); background-repeat: repeat-x; border: 1px #3366CC solid; padding-left: 5px; padding-right: 5px;";

//Set the style
crmForm.all.new_button.style.cssText=buttonStyle;

6. Now we need to set the button text:

//Set the button text, and don't allow it to be edited
crmForm.all.new_button.DataValue="Upload";
crmForm.all.new_button.contentEditable = false;

7. Finally the button needs to do something so we'll add the following:

//Define what the button is going to do
buttonClicked = function()
{
alert("Upload process started");
}

//Set the button's on-click event
crmForm.all.new_button.attachEvent("onclick",buttonClicked);

The above example will work for CRM 3.0 as well but you'll need to change the style to make it fit in.

As of version 4.0.10 the SDK provided stylesheets to allow any custom development to match the CRM look and feel. Download the SDK here. Once unpacked look at the sdk\stylesheet folder details.

New CRM SDK Released. Includes LINQ

The latest version of the CRM 4.0 SDK, 4.0.12 was released on Friday 8th May 2010.This isn't just a simple update as there are some great new features included.

Download the SDK at: Download SDK

This version of the SDK includes what is being called the Advanced Developer Extensions, this means that you'll be able to interact with the CRM data more simply, including using LINQ query expressions.

Further details, and walkthrough can be found on the Microsoft CRM Team Blog.

12 April 2010

Run Exe From A CRM Button

Right, here's a nice easy one to ease me into this blogging lark.

I was recently asked how you'd go about firing an EXE from a CRM button, now this isn't the best thing to be doing but here's how to do it.

Note: The exe path must be visible from the client machine.

Assuming that you want the button to appear on the entity form add the following to the isv.config:


<Button Icon="/_imgs/ico_18_debug.gif" JavaScript="fireExe();">


Now you need to add the following to the OnLoad event of the entity that displays the button:


fireExe = function()
{
var oShell = new ActiveXObject("WScript.Shell");
//Note: all back-slashes (\) must be doubled up (Single \ is a control character in JavaScript)
var prog = "c:\\WINDOWS\\System32\\notepad.exe";
oShell.run ('"'+prog+'"',1);
}
The above example will fire NotePad whenever the user clicks the button.

Note: Internet Explorer security settings will probably need to be changed for the Internet Zone that CRM is running under to allow ActiveX code to execute.

07 April 2010

About Me, About This Blog

Where to start, well I'm a Dynamics CRM Lead Consultant in the UK. I've worked, as a developer and consultant, with Dynamics CRM since the beta version of V1.0 and implemented one of 5 V1.0 installations in the UK. Thinking back to V1.0 brings me out in a cold sweat now!

The purpose of this blog is to bring my "CRM Green Bible" (those that have worked with me know about this book of tricks) out from the dark ages, well paper based, and digitalise it. There are a couple of reasons for this, one it's a pain to carry around and I never have it when I want it, secondly I hope that other people will find some of the things that I've noted useful.

Over the coming weeks I'll be updating this blog with my existing notes, and I'll be throwing in a few new things that I find.

This blog will provide information for the easy items, hiding tabs, and other things that are more complicated, filtered lookups for example. This is as much to jog my memory as it is to make me look clever (I hope).

Now for the boring disclaimer bit:
  • Some of my posts will provide information based on information that I've found on other blogs, if I can remember where I got the information from I'll acknowledge the fact.

  • Where possible I always try to find supported methods, but that's not always possible. I'll inform you when it's not a supportable solution.

  • Anything that is on this blog has no relation to my employer, and if you implement something it's down to you, if it breaks your system you fix it (I'll do my best to help but I'm not taking responsibility).
Wow that was a long post.....