31 January 2008

Install CRM 4.0 and Outlook client on the same box



As you may know that Exchange Server isn't the only mail option in Microsoft Dynamics CRM v4.0, Furthermore in CRM 4.0, you can install Outlook client and CRM Server on the same box, it doesn't have limitation anymore. Plus, you don't have to have an Outlook profile setup before you install the Outlook client.


So, it's pretty easy now, let's start building a CRM 4.0 all-in-one-box VPC.



  1. Build and setup a Windows Server 2003 R2 with SQL Server 2005 VPC

  1. Install CRM 4.0 Server

  1. Install Outlook 2007

  1. Install CRM 4.0 Client for Outlook

During the installation, if you get a pre-check error message say:"Microsoft Dynamics CRM for Outlook cannot be installed on a computer where Microsoft Exchange Server versions prior to Exchange Server 2007 are installed." It does mean as it's displayed. But how about if you don’t have Exchange 2003 installed on that box? Well, in that case you may run 'regedit.exe', and go to [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\], find the [Exchange] folder, export it & delete it. And then try the installation again.


  1. After the installation, open Outlook(if you don't have a Outlook profile setup yet, do it now), you will see a brand new toolbar, click it, go to 'Next', it will alert you to close Outlook, do it, and then click 'Next'

  1. On the next window, you may select either 'My company' or 'An online service provider', select yours, click 'Next'

  1. Fill out the CRM server URL, then click 'Next', it will check the system requirement, click 'Next' again when it's finished, and then it will run the installation process.

  1. After the installation, every time you open Outlook, it will initialize some tasks (you can see it from the CRM Toolbar), so just leave it to finish.

26 January 2008

CRM 4.0 : get UserId, BusinessUnitId, OrganizationId on client-side JScript (WhoAmIRequest)

I'm sure it's in SDK, but to be clear I made it as a JScript function, so you can easly get UserId, BusinessUnitId and OrganisationId from client-side WhoAmIRequest, it's a good example.


function GetCurrentUserInfo()
{
var SERVER_URL = "http://CRM";
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
xmlhttp.open("POST", SERVER_URL + "/mscrmservices/2007/crmservice.asmx", false);
xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");

var soapBody = "<soap:Body>"+
"<Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<Request xsi:type='WhoAmIRequest' />"+
"</Execute></soap:Body>";

var soapXml = "<soap:Envelope " +
"xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' "+
"xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "+
"xmlns:xsd='http://www.w3.org/2001/XMLSchema'>";

soapXml += GenerateAuthenticationHeader();
soapXml += soapBody;
soapXml += "</soap:Envelope>";

xmlhttp.send(soapXml);
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.loadXML(xmlhttp.responseXML.xml);

var userid = xmlDoc.getElementsByTagName("UserId")[0].childNodes[0].nodeValue;
var buid = xmlDoc.getElementsByTagName("BusinessUnitId")[0].childNodes[0].nodeValue;
var orgid = xmlDoc.getElementsByTagName("OrganizationId")[0].childNodes[0].nodeValue;

alert("UserId: " + userid + "\r\nBusinessUnitId: " + buid + "\r\nOrganizationId: " + orgid);

}

19 January 2008

CRM 4.0 : Check current user's security role using JavaScript

It's a common question about how to show/hide fields based on user's security roles.
Ronald Lemmen had a very popular post on his blog about how to use 'RemoteCommand' to achieve that in CRM 3.0. Because 'RemoteCommand' is for internal use and unsupported, it doesn't work in CRM 4.0.
Michael H?hne also had a great post about how to access web service using client-side JavaScript. Since CRM 4.0 web service EndPoint changed, some people get 401 authorization error.

Here's code which works great in CRM 4.0, the function UserHasRole("ROLE_NAME") returns true if the current has the role, returns false if it doesn't. GetCurrentUserRoles() function generated by Michael H?hne's tool with some changes. Thanks for Regan who point out that using GenerateAuthenticationHeader() instead of hard-coding the Organization's Name.



//check if the current user has the 'System Administrator' role
alert(UserHasRole("System Administrator"));

function UserHasRole(roleName)
{
//get Current User Roles, oXml is an object
var oXml = GetCurrentUserRoles();
if(oXml != null)
{
//select the node text
var roles = oXml.selectNodes("//BusinessEntity/q1:name");
if(roles != null)
{
for( i = 0; i < roles.length; i++)
{
if(roles[i].text == roleName)
{
//return true if user has this role
return true;
}
}
}
}
//otherwise return false
return false;
}

function GetCurrentUserRoles()
{
var xml = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>role</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>name</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkCriteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:LinkCriteria>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";

var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");

xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction"," http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);

var resultXml = xmlHttpRequest.responseXML;
return(resultXml);
}



Due to Bloger's format issue, I have rewrite the code, it should work for everyone now.:)

12 January 2008

Dynamic Picklist, load values from a XML file



There are some dynamicpicklist examples on the Internet. You may find it from CRM SDK as well, and there's A (slightly) different approach to dynamic picklists from Greg Owens.

But none of those can meet my client's requirement. We have 2 picklists(which is normal), but picklist2 has hundreds of items. And those picklists repeat 3 times on all activites. Furthermore, they don't want to use another entity. So I create a XML file to keep the data, and using it dynamicly fill out picklist2 when picklist1 has value selected. It works great, and you only need to maintain one XML file.

This is a very simple XML to do the demonstration, put it ('section.xml') into the root folder of CRMWeb. The idea is: when user selects type1 in picklist1, then picklist2 only shows item1, item2, item3; when user selects type2 in picklist1,the picklist2 only shows item4, item5, item6.


<?xml version="1.0" encoding="utf-8" ?>
<Section>
<type1>
<item>item1</item>
<item>item2</item>
<item>item3</item>
</type1>
<type2>
<item>item4</item>
<item>item5</item>
<item>item6</item>
</type2>
</Section>


Suppose there are two picklists on crmForm, picklist2's value depends on picklist1's selection.
In my example:
picklist1 is: new_type1, it has two values: 'typeA/typeB' and 'typeC/typeD'
picklist2 is: new_section1, it doesn't have any value, and I add another nvarchar attribute: new_section1Text to save it's value(see example)



/*
Form.onLoad() event
GetItems() is a global function to get section's list items based on type's selection
typeValue: picklist1's DataValue
section: picklist2(object)
sectionText: picklist2.SelectedText
*/

GetItems = function(typeValue, section, sectionText)
{
//clean the section object
section.length = 0;

//it is the index of picklist2.SelectedText in XML file
var sectionTextIndex = 0;

//get the typeName, used for XML node
var typeName = 0;

switch(typeValue)
{
case "1" : typeName = "type1"; break;
case "2" : typeName = "type2"; break;
case "0" : return;
}

//load XML file
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.load("/section.xml");

//get all items under this type
xmlDoc = xmlDoc.getElementsByTagName(typeName)[0];
var items = xmlDoc.getElementsByTagName('item');

//insert all items into section object
for(var i=0; i<items.length; i++)
{
section.AddOption(items(i).firstChild.nodeValue, i+1);
if((sectionText != null)&&(sectionText.DataValue == items(i).firstChild.nodeValue))
{
sectionTextIndex = i+1;
}
}

return sectionTextIndex;
}

GetPicklist(crmForm.all.new_type1, crmForm.all.new_section1, crmForm.all.new_section1text);

function GetPicklist(type, section, sectionText)
{
if(sectionText.DataValue != null)
{
//select the right one
section.DataValue = GetItems(type.DataValue, section, sectionText);
}
}

/*
Form.onSave() event, save the current new_section1's selectedText
Becasue we need to add this option to new_section1 on Form.onLoad()
*/

SetPicklist(crmForm.all.new_section1 , crmForm.all.new_section1text);

function SetPicklist(section, sectionText)
{
if(section.SelectedText != "")
{
sectionText.DataValue = section.SelectedText;
section.length = 0;
}
}

/*
new_type1.onChange() event to call GetItem() funciton, passing new_type1.DataValue and new_section1, sectionText as parameters
*/
if(crmForm.all.new_type1.DataValue != null)
{
GetItems(crmForm.all.new_type1.DataValue, crmForm.all.new_section1, null);
}
else
{
crmForm.all.new_section1.options.length = 0;
}

06 January 2008

Microsoft Dynamics CRM 4.0 SDK Released!

Microsoft Dynamics CRM 4.0 SDK

This package contains the complete software development kit for Microsoft Dynamics CRM 4.0.


Microsoft Dynamics CRM 4.0 Language Pack Readme

This document provides important late-breaking information.


Microsoft Dynamics CRM 4.0: Planning and Deployment Guidance for Service Providers

These guides and tool provide additional information for Service Providers to plan and deploy Microsoft Dynamics CRM 4.0.


Microsoft Dynamics CRM 4.0

Microsoft Dynamics CRM 4.0 for released languages.


Microsoft Dynamics CRM 4.0 Data Migration Manager

Using the Microsoft Dynamics CRM 4.0 Data Migration Manager, you can convert and upload data from another CRM system to Microsoft Dynamics CRM 4.0.


Microsoft Dynamics CRM 4.0 Trial Versions

Microsoft Dynamics CRM 4.0 90-day trial versions for released languages.


Microsoft Dynamics CRM for Outlook (For On-Premise and Hosted Editions)

Install Microsoft Dynamics CRM 4.0 for Outlook and Microsoft Dynamics CRM 4.0 for Outlook with Offline Access. For on-premise and hosted editions of Microsoft Dynamics CRM 4.0 only.


Microsoft Dynamics CRM 4.0 E-mail Router (On-Premise and Hosted Editions)

The E-mail Router is an interface between the Microsoft Dynamics CRM system and an e-mail system.


Examples of how to configure the Microsoft Dynamics CRM 4.0 on-premise E-mail Router in different deployment scenarios

This document lists steps to configure Microsoft Dynamics CRM 4.0 e-mail in different deployment scenarios.


Microsoft Dynamics CRM 4.0 for Outlook Readme (On-Premise and Hosted Editions)

This document provides important late-breaking information.


Microsoft Dynamics CRM 4.0 Implementation Guide

This guide contains comprehensive information about how to plan, install, and maintain Microsoft Dynamics CRM 4.0.


Microsoft Dynamics CRM 4.0 Server Readme

This document provides important late-breaking information for Microsoft Dynamics CRM 4.0 Server.


Microsoft Dynamics CRM 4.0 E-mail Router Readme (On Premise)

This document provides important late-breaking information for the Microsoft Dynamics CRM 4.0 E-mail Router (on-premise and hosted editions).


Microsoft Dynamics CRM 4.0 Data Migration Manager Readme

This document provides important late-breaking information.


Microsoft Dynamics CRM 4.0 Internet Facing Deployment Scenarios

This document covers how to set up the Microsoft Dynamics CRM 4.0 Web site to make it available from the Internet.