Showing posts with label MSCRM. Show all posts
Showing posts with label MSCRM. Show all posts

Monday, June 21, 2010

Improved LinkedIn CRM integration

So some of you may have the original style LinkedIn integration in CRM - it displays in an iframe somewhere on your account/contact/lead and displays any people at a given company that are members on LinkedIn. However, I was recently directed to find (and subsequently found) a better integration (at least it looks cooler and seems to be pretty slick) posted here:
http://marcoamoedo.com/blog/adding-linkedin-to-dynamics-crm/

The code he lists here is for accounts - copy and paste and you have a new cute little icon next to the account name field! If I modify this to work with contacts or leads I will post it here as well.

Wednesday, May 19, 2010

Error when reactivating a case

This same sort of error might occur when reactivating any record, but the specific instance was an error when attempting to reactivate a case in CRM. The error returned in a trace is: The object cannot be updated because it is read-only. This error message doesn't help much, because of course the object is read-only. However, this message is actually being returned by an attribute on the case, and the problem at hand is some javascript.

Thanks to the conversation here, I was able to determine that the problem is in the OnLoad() script, where I was setting a few attributes to ForceSubmit = true. This script is actually firing when I open the read-only record, which normally has no effect because the record cannot be saved. However, upon trying to reactivate, the system is performing a save operation, but the ForceSubmit is trying to submit the value in the attribute before the record becomes read/write again. Hence the above error.

The solution here is to simply wrap the ForceSubmit statements in an IF statement to make sure that they only fire on create or update forms and not on read-only forms.

Tuesday, February 9, 2010

Server 2008 and the Email router with IFD

Today I encountered an interesting issue with Server 2008 and the email router. The email router is running on the same server as CRM, and I had just configured CRM to run in IFD mode. This somehow broke the email router. When browsing the CRM URL from the server, I received the IFD login page. Thinking I had missed a subnet, I confirmed that all appropriate subnets (including the server subnet) were included in the IFD tool, and verified it was working by testing from another server. Everything checked out.

And yet somehow, the request for CRM was coming from another IP not included in the subnet, but only when browsing from the CRM server itself. It turns out that the CRM server, when making the request to itself, was making that request using IPv6 instead of IPv4. The IP address was indeed different and was not in one of the included subnets. To resolve this issue, I disabled IPv6 on the ethernet connection (in the properties, I simply unchecked the IPv6 box) and tested again. Everything worked fine, and the router was fixed.

So if you are using an IFD, keep in mind that Server 2008 may try to make requests in IPv6 to itself, and the CRM server will think these are external requests.

Monday, February 8, 2010

Server 2008 and the IFD tool

Just a quick note today - if you try to run the IFD tool for CRM on a 2008 server, it might not work. You will get no error, no warnings, but the settings will not be applied... unless you run the tool explicitly as an Administrator. UAC strikes again!

Wednesday, December 2, 2009

Rollup 7 breaking workflow publishing?

I can't be certain, but I think Update Rollup 7 for CRM is breaking my ability to publish workflows that contain email steps. So far I have encountered this twice, for two different customers, and it is really annoying. Fortunately, I have found the solution!

The error that appears in the trace file (if you do a trace while attempting to publish the workflow) comes up something like this:
>Crm Exception: Message: Workflow compilation failed:
WF1399: Activity 'SendEmailStep5_policy' validation failed: Property "RuleSetReference" has an invalid value. Rule set is invalid. Rule "main" validation failed. Type System.Globalization.CultureInfo is not marked as authorized in the application configuration file.
WF1399: Activity 'SendEmailStep7_policy' validation failed: Property "RuleSetReference" has an invalid value. Rule set is invalid. Rule "main" validation failed. Type System.Globalization.CultureInfo is not marked as authorized in the application configuration file., ErrorCode: -2147201023

The error actually provides a clue as to the problem, but it wasn't entirely obvious to me. There is a line missing from the web.config file which, when added back, resolves the issue. The line is:
<authorizedtype assembly="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" namespace="System.Globalization" typename="CultureInfo" authorized="True"></authorizedtype>

I've just been adding it to the end of the list of authorized types for mscorlib. You don't even have to restart IIS - it just begins working as soon as you save the file.

UPDATE: I had to put in the HTML escape characters for the brackets to show up in here - my apologies for the line not appearing before!

Tuesday, April 7, 2009

SharePoint folder integration

CRM and SharePoint are natural companions - both are web-based applications, both use the same workflow engine (WinWF), and SharePoint provides a natural completion to CRM's abilities by providing document management. A great way to use this - documents attached to a specific CRM entity, such as agreements on an account, or perhaps customer-delivered documentation attached to specific opportunities. Most recently, I was asked to integrate with CRM campaigns, and use SharePoint as a location to store the creative, graphics, etc. that go along with a specific campaign. This way, when a user accesses a campaign in CRM, all of the relevant files will simply appear in an additional tab on the form, hosted inside an iFrame.

In this case, I decided to store the documents for the campaign inside folders in the SharePoint document library - a folder for each campaign, all within a single library. I also decided that users won't need to access these folders from outside of CRM, so the name of each folder is simply the campaignid GUID to avoid duplicates or other problems. I also set up a special page to use in the iFrame that eliminates the SharePoint "chrome", giving me a bit more screen real estate to display the actual documents.

So now to create the folders dynamically - I need to ensure a folder is created for each campaign automatically. The "standard" method for doing this was grabbed from someone else (sorry, I don't have a link) and involves using an ActiveX FileSystemObject control. This method accesses SharePoint via UNC path, checks to see if a folder by the right name is in the library, and creates one if it is not there. An example is here:


var theIncidentId = crmForm.ObjectId;
var oShell = new ActiveXObject("Scripting.FileSystemObject");

if (! oShell.FolderExists("\\\\Netshare\\IncidentAttachments\\"+ theIncidentId) )
oShell.CreateFolder("\\\\Netshare\\IncidentAttachments\\"+ theIncidentId )
else{
}


Of course, you can use this code to access any fileshare - this is certainly not limited to SharePoint only. However, the fact that SharePoint document libraries are accessible via UNC means you can use this same functionality with SharePoint and it works great...

...Unless you can't get UNC access to SharePoint. This might occur for a few reasons - if SharePoint is not installed on port 80, or maybe if you use a proxy between the end computer and SharePoint, or if using an IFD, or if you are using SharePoint Online. In my case, I couldn't get to SharePoint for an unknown reason via UNC, so rather than troubleshooting SharePoint (which I don't really have control over) I decided to look into an alternative - using the SharePoint web services.

My inspiration came from here: http://blogs.msdn.com/crm/archive/2006/10/23/creating-folders-in-sharepoint-document-libraries.aspx . Their example used a callout, but this proved that the functionality was available in SharePoint. I had another example that involved finding a user's roles using a call to the CRM webservices (one example is here - there are others) . I realized that both ends of the process were there - I simply needed something to bridge the gap and access SharPoint via webservice to create the new folder. Lucky for me, I was able to find a helpful blog by Darren Johnstone that had created all of the javascript needed to talk to SharePoint - I just needed to modify it a bit and use it with CRM. Since Mr. Johnstone had broken the real work out into his classes, I had to do a bit of reverse-engineering to pull it back into an in-line function to make it work with CRM. The end result creates the folder in SharePoint with the webservices just like the FileSystemObject would, but now it will work with SharePoint anywhere! The only issue I haven't worked out yet is how to CHECK for the folder before attempting to create it, so right now I just let it error itself out (SharePoint won't create a duplicate folder) and ignore it. This could become a larger problem later, but it works for me for right now.
The final code is below:

// Only run code on Update forms
if(crmForm.FormType == 2)
{
var objectId = crmForm.ObjectId;
objectId = objectId.replace(/[{}]+/g,'');

// The service URL should go to the site with the document library
var varServiceUrl = "http://sharepoint/SiteName/Marketing/_vti_bin/lists.asmx";

// This function takes the doc library name and the name of the new folder
// You can also pass a third parameter to specify a root folder
var res = createFolder("CRMIntegration", objectId);

// Error catching, which I am currently ignoring
// if (res.status == 200)
// {
crmForm.all.IFRAME_SPDocs.src = "http://sharepoint/SiteName/Marketing/CRMIframe/CampaignCreative.aspx?RootFolder=%2fSiteName%2fMarketing%2fCRMIntegration%2f" + objectId + "&FolderCTID=&View=%7bE2F9780A%2d0546%2d4254%2d92EA%2d31B1A584914E%7d";
// }
// else {

// alert("error creating folder: " + res.statusText);
// }
}


// Functions to support SharePoint folder creation

// Creates batch XML file for new folder
// (Includes optional rootFolder parameter - see Darren Johnstone's blog for usage)
function createFolder(listName, folderName, rootFolder)
{
var batch;
batch = " if (rootFolder != null)
{
batch += " RootFolder='" + rootFolder + "'";
}
batch += ">";
batch += ""
+"1"
+"" + folderName + ""
+"
"
+"
";
return updateListItems(listName, batch);
}

// Delivers message to SharePoint
// (Eliminated dependance on javascript classes)
function updateListItems(listName, updates)
{
var oXMLHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
var result = null;
var resultName;

oXMLHttpRequest.open("POST", varServiceUrl, false);
oXMLHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
oXMLHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/sharepoint/soap/UpdateListItems");

var packet = ["",
"",
"",
"",
"",
listName,
"
",
"",
updates,
"
",
"
",
"
",
"
"
].join("");

oXMLHttpRequest.send(packet);

resultName = "UpdateListItems";
var resBatch;
var status;
var statusText;

status = oXMLHttpRequest.status;
statusText = oXMLHttpRequest.statusText;

if (status == 200)
{
// Check for SharePoint error code
resBatch = oXMLHttpRequest.responseXML.getElementsByTagName(resultName);

var codeEl = oXMLHttpRequest.responseXML.getElementsByTagName('ErrorCode');

if (codeEl != null && codeEl.length > 0)
{
var spStatus = parseInt(codeEl[0].childNodes[0].nodeValue);

if (spStatus != 0)
{
status = 0-spStatus; // Note we make this -ve to prevent confusion with the HTTP code

var messageEl = oXMLHttpRequest.responseXML.getElementsByTagName('ErrorText');
if (messageEl != null && messageEl.length >= 0)
{
statusText = messageEl[0].childNodes[0].nodeValue;
}
}
}
}

result = {
status : status,
statusText : statusText,
responseXML : oXMLHttpRequest.responseXML,
responseText : oXMLHttpRequest.responseText,
resultNode : (resBatch == null || resBatch.length == 0 ? null : resBatch[0])
};
return result;
}

Wednesday, April 1, 2009

Yes, another Microsoft CRM blog

I have been working with Microsoft's CRM product since version 1.2. Over the years, I have been excited with each new release of the product and the capabilities provided to me as a customizer as well as to the businesses that use the software. In its current incarnation, version 4.0, CRM has become a very powerful relationship management tool. More than simply tracking customers and sales (essentially all you could do in v1.2), v4.0 is simply a convenient and easy-to-use interface for a relational database - and you can use this database to track any information your business needs. With this most recent version, the database aspects are, in my opinion, essentially complete - you can map out your data in almost any way you desire. The next version(s) of the product simply need to focus more on displaying and working with that data in even more ways to meet the needs of an even greater segment of the business world.

I am beginning to blog about CRM because I would like to document and share my experiences with the product, both from a business standpoint and from a technical perspective. I think Microsoft CRM is a fantastic product that can do almost anything, and I intend to prove it here!