Monday, March 22, 2010

Creating a SharePoint Document Library in CRM 4.0

After reading a few posts on SharePoint and CRM 4.0 integration, and learning of a new business requirement that someone came up with for the CRM deployment I am working on, I managed to get a plug-in created that will create a document library in SharePoint when a new custom entity is created in CRM 4.0.

I basically performed these steps:

1) Added a new attribute to my custom entity, in this case it was called new_projectspdocliburl of type nvarchar with a length of 200.

2) Added a new tab to the custom entity form, and added a new iframe called IFRAME_SharePoint, unchecking Restrict cross-frame scripting.

3) Added the new field to the tab (the field has to be on the tab for some JavaScript used later).

4) Saved and published the changes to the entity.

5) Create a new VB.Net Class Library project in Visual Studio 2008, calling it ProjectCreateSPDocLibPlugin, and renaming Class1.vb to plugin.vb

6) Added a web reference to SharePoint's Lists.asmx for the site you want the document library to be created in (http://servername[:port]/site_name/_vti_bin/Lists.asmx) and using a reference name of SharePoint.

7) Added a reference to System.Web.Services (found in the .Net tab of the add reference dialog) and a reference to micrsoft.crm.sdk.dll and microsoft.crm.sdktypeproxy.dll (which can be found in the sdk/bin folder of the CRM 4.0 SDK)

8) Used the following code in plugin.vb:

Imports Microsoft.Crm.Sdk
Imports Microsoft.Crm.SdkTypeProxy
Imports ProjectCreateSPDocLibPlugin.SharePoint 'This is the namespace of this project + the Reference name of the SharePoint web reference

Public Class plugin
 Implements IPlugin

 Public Sub Execute(ByVal context As Microsoft.Crm.Sdk.IPluginExecutionContext) Implements Microsoft.Crm.Sdk.IPlugin.Execute
     'Get the name of the target entity and its GUID
     Dim dynamicEntity As DynamicEntity = CType(context.InputParameters.Properties("target"), DynamicEntity)
     Dim entityName As String = dynamicEntity("new_projectname").ToString 'replace new_projectname with the name of your custom entity's primary attribute
     Dim entityId As String = context.OutputParameters.Properties("id").ToString

     'SharePoint Document Library is type 101
     Dim spDocLibListType As Integer = 101

     'Use the SharePoint's Lists.asmx to create a new list object
     Dim listService As New Lists()
     listService.Credentials = New System.Net.NetworkCredential("username", "password", "domain") 'Replace these with your own
     listService.Url = "http://servername[:port]/site_name/_vti_bin/Lists.asmx" 'Replace this with your own
     Dim listResult As System.Xml.XmlNode = listService.AddList(entityName, entityName + " Documents", spDocLibListType)
     Dim returnXML As String = listResult.InnerXml.ToString()

     'Update the Custom Entity to store the URL of the new document library
     Dim customEntity As New DynamicEntity()
     customEntity.Name = "new_customentityname" 'Replace this with your own entity name
     customEntity("new_customentityid") = New Key(New Guid(entityId)) 'Replace this with your entity's ID attribute name
     customEntity("new_projectspdocliburl") = "http://servername[:port]/site_name/" + entityName + "/Forms/AllItems.aspx"

     'Use the IPlugin interface to create a reference to the CrmService
     Dim crmService As ICrmService = context.CreateCrmService(True)
     crmService.Update(customEntity)
 End Sub
End Class
9) Compiled the project and used the Plug-in Registration tool to register the new plugin and then registered a new step with a Message of Create, the Primary Entity being the custom entity and making sure it's a Post Event step (it will be triggered after the core processing of the entity creation in the event pipeline).

10) Added the following JavaScript code to the OnLoad method of the custom entity's form:

var CRM_FORM_TYPE_CREATE = 2;
if (crmForm.FormType == CRM_FORM_TYPE_CREATE)
{
var spDocLibUrl = crmForm.all.new_spdocliburl.DataValue;
crmForm.all.IFRAME_SharePoint.url = spDocLibUrl;
}
11) Save the changes and once again publish the custom entity.

Now, all things going to plan, when you create a new object from your custom entity, once it is created, your plug-in will kick in and create a new document library in SharePoint using the name of the created object, and update the IFRAME on the form so the users can see the document library.

Obviously, the users need to already have permission to see the library in SharePoint. Another thing to consider is that the view we used, AllItems.aspx, is potentially going to show a full SharePoint page in the IFrame. It might look a bit off to have all the standard SP navigation inside the CRM frame. I don't know enough about SharePoint to explain how to work around that.

But this should have given you a good enough start into integrating SharePoint and CRM to give better document handling than the standard attachments to a CRM object.

5 comments:

Unknown said...

How do you pass in the current users credentials without hard coding them in your example: listService.Credentials = New System.Net.NetworkCredential("username", "password", "domain"). I tried using the CredentialCache.DefaultCredentials and CredentialCache.DefaultNetworkCredentials without any luck. Looked arround at other blogs with no answer as well.

Unknown said...

Any ideas why the same code wouldn't run in an update Message? The line that seems to be throwing the error is the "Dim entityName As String = dynamicEntity("new_projectname").ToString" assingment line.

I read somewhere that the context is different when passed as an update, but the poster didn't elaborate on that.

Adam said...

@David - I believe this has something to do with delagted trust between the CRM and the SharePoint server. By default, web service calls do not support this form of authentication, it has to be explicitly defined. System.Net.CredentialCache.DefaultCredentials is going to try and use the credentials of the current user, and .DefaultNetworkCredentials is going to try and use the credentials of the worker process, in this case the account running CRM. Neither of these will work in this expamle unless delegated trust is set up.

I don't know the specific ins and outs of this, so I can't help you beyond that I am afraid.

Adam said...

@Adam P - The line you commented on - Dim entityName As String = dynamicdynamicEntity("new_projectname").ToString will fail on an update (and I'm guessing it fails on "The given key was not present in the dictionary" because the target property bag that is used to create the dynamicEntity object does not contain a property called new_projectname.

On an Update message, only the attributes of the entity which have changed are present. The context itself is the same. As far as I know, all plug-ins use the same signature, which requires an IPluginExecutionContext.

I hope that makes sense.

Unknown said...

Actually it does - insofar as all of the CRM object model does. ;)

You're totally right - update doesn't pass the name value unless it explicitly changes.

Thanks!

Post a Comment