CRM 2011 Integration with SharePoint: Custom Document Management

The previous post on CRM and SharePoint integration focused on looking at the workings of the default CRM 2011 functionality to see how the underlying structure of SharePoint Site and SharePoint Document Location records could cater for much more than the basic ‘out-of-box’ Document Management.

This article aims to describe how we can leverage this flexibility via custom development to define a more bespoke Document Management solution between SharePoint 2010 and CRM 2011, using different arrangements of Document Libraries, Folders and an initial look at Metadata.

Developing a Plugin to integrate CRM to SharePoint

Towards achieving a custom format of Document Management between CRM and SharePoint, we can look at creating a Plugin which will automatically integrate a new CRM Record with a new SharePoint Document area for a particular entity or entities in CRM.

This is similar to how custom Document Management Integrations were often implemented between CRM 4 and SharePoint 2007, however with the advent of SharePoint 2010 we are able to use the SharePoint Client Object Model to make this development simpler and less code-heavy. The aim here being to focus on the desired business logic of our Document Management and not get bogged down with the coding for the SharePoint development.

However our first difficulty here is incorporating the SharePoint Client Object Model DLL in the CRM Plugin to allow us to communicate remotely with our deployment of SharePoint 2010. CRM Plugins are standalone DLLs which are then registered to the Database which makes referencing external DLLs such as the SharePoint Client Object Model difficult – however this can be done via a variety of methods:

(1) Register the Plugin DLL via Disk, and then store both the Plugin DLL and SharePoint Client Object Model DLL in the Bin folder of the CRM Website.

This is functional solution to the problem but in impossible for Online or IFD Deployments of MSCRM.

(2) Merge the SharePoint Client Object Model DLL into your Plugin DLL to form a composite Plugin.

This is almost a functional solution, however the merge makes the Plugin DLL very large and often too large to correctly register with CRM – and is a bit of a hammer to crack to a nut really.

(3) Place the SharePoint Client Object Model DLL in the GAC for the Plugin DLL to reference.

This is the most practical solution as allows us to register our Plugin to the Database – but we have no access to the GAC for Online and usually limited access to the GAC for IFD Deployments of CRM.

(4) Develop the Plugin to call out to either a Webservice which can then access SharePoint.(either one of the standard SharePoint Webservices or a custom Webservice which can then invoke the SharePoint Client Object Model)

This is a workable solution for CRM Online deployments but does require SharePoint to be publically facing to accept the Webservice calls – the best bet in this scenario would be to have CRM Online communicating to Public Webservice which in turn communicates to SharePoint Online.

For this example, we will look into option (3) of using the GAC, as this is likely the most appropriate option of On-Premise deployments of CRM.

Adding the SharePoint Client Object Model DLL to the GAC

Assuming we have access to our CRM and SharePoint Servers, adding the Client Object Model DLL to the GAC is easily done.

First of all we can find the SharePoint DLL on the following location of our SharePoint Server where can take a copy for use on the CRM Server – C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI.

image

The location of the SharePoint Client Object Model on a SharePoint 2010 Server

There is then various ways of adding these DLLs to the GAC, but the simplest is simply to use Windows Explorer to add the files to the following location on the CRM Server – C:\Windows\Assembly.

image

The SharePoint Client DLLs added to the GAC

With these DLLs copied into the GAC, we should be ready to develop a new CRM Plugin which incorporates the Share Point Client Object Model as a reference.

Referencing SharePoint DLLs in Visual Studio

Referencing the SharePoint Client Object Model DLLs in Visual Studio

Developing the Plugin

For the Plugin to create a new Document Library for each new Account, the logic of the Plugin’s execute method will need to carry out the following steps.

(1) Check whether the Account (or other record in CRM) already has a Document Location associated.

(2) If not, then create a new Document Library in SharePoint

(3) Associate the Account in CRM to the newly created Document Library by creating a new SharePoint Document Location record in CRM.

These steps will involve methods that communicate to both CRM and SharePoint, such that the Plugin will break down into three distinct sets of code: methods to retrieve/insert data to CRM, methods to insert data to SharePoint and the core Plugin Execute call that uses these methods to carry out the logic.

If initially we look at how the code could implement this core logic:

#region Plugin Business Logic

string recordName = string.Empty;
Guid recordId = Guid.Empty;

if ( crmAccount.Attributes.Contains(entityIdField) )
{
    recordId = (Guid)crmAccount.Attributes[entityIdField];
}
else
{
    throw new Exception("CrmConsultancy.CRM2011.CustomDocumentManagement.SharePointIntegration :: Could not find a '" + entityIdField + "' attribute for the " + crmAccount.LogicalName + " record");
}

bool createDocumentLibrary = false;

if (crmMethods.DoesCRMRecordHaveSharePointLocation(recordId) == false)
{
    createDocumentLibrary = true;
}

if (createDocumentLibrary == true)
{
    if (crmAccount.Attributes.Contains(entityNameField))
    {
        recordName = (string)crmAccount.Attributes[entityNameField];
    }
    else
    {
        throw new Exception("CrmConsultancy.CRM2011.CustomDocumentManagement.SharePointIntegration :: Could not find a '" + entityNameField + "' attribute for the " + crmAccount.LogicalName + " record");
    }

    SPSite connectedSite = crmMethods.GetDefaultSPSite();
    if (connectedSite != null)
    {
        // create the Document Library in SharePoint
        SharePointMethods sharePointMethods = new SharePointMethods(connectedSite.AbsoluteUrl, "mySharePointUserAccount", "myPassword", "myDomain");

        string documentLibraryName = sharePointMethods.CreateDocumentLibrary(recordName, "Document Library for CRM Record");

        Guid newSharePointLocationId = crmMethods.AddRootSharePointLocation(connectedSite.Id, entityLogicalName, recordId, recordName, documentLibraryName);

    }
    else
    {
        throw new Exception("CrmConsultancy.CRM2011.CustomDocumentManagement.SharePointIntegration :: CRM is not configured for SharePoint Document Management");
    }

}
else
{
    // CRM Record is already connected to a SharePoint Document Location
    // likely no need to create and link to a new location, take no further action
}

#endregion

We can see here the basic Plugin logic for determining the Record Id and Name from the Account via a Post Image, and using these fields to carry out the logic via calls to either CRM Methods or SharePoint Methods.  The highlighted lines in the code above then refer to the points where these calls are made, if we look at these in turn:

DoesCRMRecordHaveSharePointLocation(recordId)

Method to determine whether the CRM Record already has a related Share Point Document Location associating the record to an area in SharePoint. This is essentially simple query in CRM to return a true/false flag depending on whether a record is found.

internal bool DoesCRMRecordHaveSharePointLocation(Guid recordId)
{
    try
    {
        ColumnSet cs = new ColumnSet(new string[] { "sharepointdocumentlocationid", "name" } );

        ConditionExpression regardingCondition = new ConditionExpression("regardingobjectid", ConditionOperator.Equal, recordId);
        ConditionExpression stateCondition = new ConditionExpression("statecode", ConditionOperator.Equal, 1);

        FilterExpression f = new FilterExpression(LogicalOperator.And);
        f.Conditions.Add(regardingCondition);
        f.Conditions.Add(stateCondition);

        QueryExpression q = new QueryExpression("sharepointdocumentlocation");
        q.Criteria = f;
        q.ColumnSet = cs;

        EntityCollection crmLocations = _service.RetrieveMultiple(q);

        if (crmLocations.Entities.Count > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    catch (Exception ex)
    {
        throw new Exception("CrmMethods -> DoesAccountHaveSharePointLocation (" + ex.Message + ")");
    }
}

GetDefaultSPSite()

Method to retrieve the details of the Default SharePoint Site that is connected to CRM via the Document Management area of CRM – this provides the Plugin with the SharePoint URL and ‘root location’ to invoke for calling SharePoint, again however, this is a fairly simple query in CRM to retrieve the relevant record.

internal SPSite GetDefaultSPSite()
{
    try
    {
        ColumnSet cs = new ColumnSet(SPSite.ColumnSet);

        ConditionExpression c = new ConditionExpression("isdefault", ConditionOperator.Equal, true);

        FilterExpression f = new FilterExpression(LogicalOperator.And);
        f.Conditions.Add(c);

        QueryExpression q = new QueryExpression("sharepointsite");
        q.Criteria = f;
        q.ColumnSet = cs;

        EntityCollection crmSites = _service.RetrieveMultiple(q);

        if (crmSites.Entities.Count > 0)
        {
            return new SPSite(crmSites[0]);
        }
        else
        {
            // no SharePoint Sites defined in CRM
            throw new Exception("CRM does not have any default SharePoint Sites");
        }
    }
    catch (Exception ex)
    {
        throw new Exception("CrmMethods -> GetDefaultSPSite (" + ex.Message + ")");
    }
}

new SharePointMethods(connectedSite.AbsoluteUrl, “administrator”, “myPassword”, “MyDomain”)

This constructor methods creates the initial connection to SharePoint using the Absolute Url field from the [GetDefaultSPSite] method alongside valid security credentials. These credentials could be hard-wired in the code, or added to the SharePoint Site entity in CRM as custom fields.

internal SharePointMethods(string spSiteUrl, string spUsername, string spPassword, string spDomain)
{
    try
    {
        _siteUrl = spSiteUrl;
        _clientContext = new ClientContext(_siteUrl);

        _clientContext.Credentials = new System.Net.NetworkCredential
            (spUsername, spPassword, spDomain);
    }
    catch (Exception ex)
    {
        throw new Exception("SharePointMethods.Constructor --> [" + ex.Message + "]");
    }
}

CreateDocumentLibrary(recordName, “Document Library for CRM Record”)

This method is where the Plugin calls out to SharePoint using the Client Object Model to create a new Document Library.

public string CreateDocumentLibrary(string documentLibraryName, string documentLibraryDescription)
{
    try
    {
        Web web = _clientContext.Web;

        _clientContext.Load(web);
        _clientContext.ExecuteQuery();

        ListCreationInformation lci = new ListCreationInformation();
        lci.Title = documentLibraryName;
        lci.Description = documentLibraryDescription;
        lci.TemplateType = 101;

        List newDocumentLibrary = web.Lists.Add(lci);

        newDocumentLibrary.ContentTypesEnabled = true;
        newDocumentLibrary.Update();
        _clientContext.ExecuteQuery();

        return (_siteUrl + "/" + documentLibraryName);
    }
    catch (Exception ex)
    {
        throw new Exception("SharePointMethods.CreateDocumentLibrary('" + documentLibraryName + "') (General Exception: " + ex.Message + ")");
    }
}

AddRootSharePointLocation(connectedSite.Id, entityLogicalName, recordId, recordName, documentLibraryName)

This method adds a new Share Point Document Location record into CRM that associates the regarding object (in this case the Account record) to the new SharePoint Document Library via setting the Relative Url property the name of the new Document Library.

internal Guid AddRootSharePointLocation(
    Guid siteId,
    string regardingEntityType,
    Guid regardingRecordId,
    string regardingRecordName,
    string relativeUrl)
{
    try
    {
        Entity sharepointLocation = new Entity("sharepointdocumentlocation");

        sharepointLocation.Attributes.Add("name", "SharePoint Location for " + regardingRecordName + "");

        EntityReference lookupSharePointSite = new EntityReference(SPSite.EntityName, siteId);
        sharepointLocation.Attributes.Add("parentsiteorlocation", lookupSharePointSite);

        EntityReference lookupRegarding = new EntityReference(regardingEntityType, regardingRecordId);
        sharepointLocation.Attributes.Add("regardingobjectid", lookupRegarding);

        sharepointLocation.Attributes.Add("relativeurl", relativeUrl);

        return _service.Create(sharepointLocation);
    }
    catch (Exception ex)
    {
        throw new Exception("CrmMethods -> AddSharePointLocation (" + ex.Message + ")");
    }
}

This combination of the Plugin Business Logic, CRM Methods and SharePoint Methods then provides a basic Plugin for automatically integrating CRM and SharePoint each time a new Account (or other CRM entity) is created.

The full Visual Studio Solution and code listing can be found here.

Testing the Plugin

With the Plugin registered against CRM, we can then create a new Account in CRM and immediately have a new Document Library created in SharePoint (via the Client Object Model call) and linked into CRM without any further user action:

image

Creating the new Account in CRM 2011

Therefore upon saving the Account, we can browse to the Documents area to view the Documents and Folders in SharePoint via the newly created Document Location record:

image

Automatically created Document Location for the new Account

With this Document Location looking at a new Document Library in SharePoint as opposed to the standard folder inside a pre-existing Library:

image

Document Library within SharePoint 2010

This gives us a method of effectively controlling how we want our documents managed in SharePoint – in that we are no longer fixed into the default behaviour of creating a new Folder for each Account in a single Document Library, instead the custom Plugin has implemented a new system of creating a separate Document Library per Account.

However whilst this control of the Document Management structure is useful, this simply changes the way SharePoint behaves alongside CRM – our next step could be to bring more sophisticated SharePoint functionality into our Plugin as way of leveraging SharePoint’s strong Document Management features alongside CRM.

Namely Content Types.

CRM, SharePoint and Content Types

To provide a brief summary, Content Types are SharePoint’s method for allowing different kinds of Document to be stored with varying Metadata fields based on the Content Type involved.  In a way similar to CRM’s concept of different entities but with a heavier focus on inheritance.

So a Contract Document may have Metadata fields describing the Contract Type, Sent Date, Effective Dates and Compliance Contact; whereas a Proposal may have different fields for Proposal Type, Revision Number and so on – but both would have standard Document fields for Title, Last Modified and Last Modified By.

In SharePoint terms, this would be expressed as two Content Types for Contract and Proposal inheriting from the base Document Content Type.

Normally Document Libraries created in SharePoint have the Content Type functionality deactivated until explicitly activated in the Advanced Settings of the Document Library.

image

Therefore any Document Libraries created by the CRM 2011 SharePoint integration will not initially have this enabled – however with our Plugin now creating the Document Libraries via code, this can be changed to activate Content Types for each new Document Library created via the Plugin.

newDocumentLibrary.ContentTypesEnabled = true;
newDocumentLibrary.Update();
_clientContext.ExecuteQuery();

This would allow the Plugin to create new Document Libraries that are able to be attached to different Content Types. If we then create some Content Types in SharePoint, we can then extend this code to attach the Content Types we want to be accessible in this Document Library:

image

With this done we can alter the code that creates the Document Library to automatically make use of these Content Types for uploading documents:

newDocumentLibrary.ContentTypes.AddExistingContentType(someContentType);
newDocumentLibrary.Update();
_clientContext.ExecuteQuery();

To do this for the Contract Type and Proposal Content Types we have added, our code would have to loop through the Content Types defined in the SharePoint Site and, if found, add them to the Document Library in the same fashion:

_clientContext.Load(web.ContentTypes);
_clientContext.ExecuteQuery();

ContentType proposalContentType = null;
ContentType contractContentType = null;
for (int n = 0; n != web.ContentTypes.Count; n++)
{
    if (web.ContentTypes[n].Name == "Proposal")
    {
        proposalContentType = web.ContentTypes[n];
        _clientContext.Load(proposalContentType);
    }
    if (web.ContentTypes[n].Name == "Contract Type")
    {
        contractContentType = web.ContentTypes[n];
        _clientContext.Load(contractContentType);
    }
}

_clientContext.ExecuteQuery();

if (proposalContentType != null)
{
    newDocumentLibrary.ContentTypes.AddExistingContentType(proposalContentType);
}
if (contractContentType != null)
{
    newDocumentLibrary.ContentTypes.AddExistingContentType(contractContentType);
}

newDocumentLibrary.Update();
_clientContext.ExecuteQuery();

This would have the effect of associating each new Document Library with the predefined Content Types – presenting CRM Users with the option of a different set of metadata fields when adding or uploading a document in CRM:

image

Creating a new document of a different Content Type to SharePoint through CRM

The key here, as with many of SharePoint’s features, being the tight integration with Office to provide the user a seamless transition between CRM and managing the document:

image

Creating a new Document in Office directly from CRM with the SharePoint Metadata fields

With the normal SharePoint functionality for uploading existing documents as specific Content Types as well:

image

Entering the Metadata when uploading a document to SharePoint through CRM

image

Viewing the list of documents with different Content Types in CRM

This gives us a basic custom structure for how different types of documents may be stored using Document Libraries between CRM and SharePoint – essentially a new custom document management configuration for the CRM Solution through a fairly small amount of custom development.

Conclusion

This concept of using CRM Development to control the Document Management between CRM and Sharepoint allows us a great deal of flexability in how documents for a CRM Solution could be structured.

As we have seen here this allows a Solution Architect to consider other methods of goverance when storing documents provide the initial folder-per-entity structure that CRM provides by default. The inclusion of SharePoint Metadata then allows this to be taken one step forward towards SharePoint best practise for managing volumes of documents – essentially providing methods for taking unstructured document data and streamlining this into a structure that fits the business. (or in a more snappy way, working the way you do, and not you working the way the software does)

Moving beyond this, for the next article in this series I will aim to look into how the concept of SharePoint Metadata can be used alongside CRM for SharePoint Views and Templates to further extend a custom document management configuration, and begin to looking at how we could handle the migration of legacy data into CRM and SharePoint.

Source Code

Download from Public-facing Skydrive

About these ads
This entry was posted in CRM 2011, Development, MSCRM, SharePoint and tagged , , , , . Bookmark the permalink.

33 Responses to CRM 2011 Integration with SharePoint: Custom Document Management

  1. Pingback: CRM 2011 Offers Custom Document Management for SharePoint : Beyond Search

  2. Garrett Stoll says:

    This was a huge help to me! Thanks for posting all of this integration code.

    However, in the code snippet for DoesCRMRecordHaveSharPointLocation(recordId) you have the criteria:
    ConditionExpression stateCondition = new ConditionExpression(“statecode”, ConditionOperator.Equal, 1);

    A statecode of 1 = inactive where a statecode of 0 = active in CRM 2011. This would limit the query to only check inactive CRM sharepointdocumentlocations.

  3. bjp says:

    How are you avoiding the .NET 4.0 framework with this code? In my attempts to use direct Sharepoint 2010 calls from a CRM 2011 plugin I always get the following error without fail:

    System.PlatformNotSupportedException
    Microsoft SharePoint is not supported with version 4.0.30319.1 of the Microsoft .Net Runtime.

    It seems to me that you’d also get this error as any assemblies called directly by a plugin get executed in the same process space and hence .NET 4.0.

    • Hi, the example here is using .NET 4 which should be okay with the SharePoint Client Object Model DLLs. The Client Object Model essentially just converts the code into messages which are then communicated to the SharePoint Server in a traditional Client/Server format, which de-couples the version of .NET that the Plugin is using from the version that the SharePoint Server-side code is using.

      In this fashion, if I remember rightly the SharePoint Server-side Object Model is restricted to earlier versions of .NET but the Client Object Model should be compatible with .NET 4.

  4. Chris says:

    Thank you very much posting the code. It’s very elaborate!
    I do seem to have run into a slight problem. I keep getting the “Plugin has no PreImage or Target entity” error. Yesterday actually it was working fine, but now I keep getting this error. It pops up when I try to save the newly created Account.
    Should I register the plugin as pre-operation or post-operation? It’s probably something silly, but I can’t seem to figure it out.
    Thanks again for posting the code!

    • Hi Chris,

      When you are registering the Plugin using the CRM 2011 Plugin Registration Tool – make sure you are registering the Plugin Step as a Pre-Operation. If the Plugin code is executing as a Pre-Operation then the Plugin should include a Target entity.

      For the PreImage entity, make sure in the Plugin Registration Tool you add an Image named ‘PreImage’ after the initial Step registration.

      Kind Regards, Paul.

      • Chris says:

        Thanks for your reply Paul. I forgot to add the Image. So that solved that problem. I appreciate it!
        Now I have something else though. When I run the code, the folder gets automatically created in Sharepoint, no problem, but the method that add the new SharePoint Location record into CRM crashes on this line: “_service.Create(sharepointLocation)”.
        Only thing is the error that is thrown only says “General SQL error”. Sound very specific. :) Do you have any idea? Thank you very much Paul!

  5. Dave says:

    Hi,
    i have followed #3 and added the Sharepoint assemblies to the GAC at C:\Windows\Assembly but when I run my plugin I get the following

    Could not load file or assembly ‘Microsoft.SharePoint.Client, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’ or one of its dependencies. The system cannot find the file specified.

    I have also tried adding the assemblies to the .NET 4 GAC

    Any advice?

    • Hi Dave,

      Strange, normally works fine if the DLL is registered into the GAC. I usually just the ‘simple’ method of including in the GAC of copying the files to Windows Assembly, however could be worth looking into the ‘gacutil’ command-line option for registration if this is not working.

      Also could be worth trying to register your Plugin as a Disk-based DLL and copying both your DLL and the Microsoft.SharePoint.Client DLL to the ‘assembly’ folder of your CRM Server and seeing if this non-GAC alternative works as a test-case.

      Let me know how it goes, Paul.

  6. Paul Erickson says:

    I’ve run into the same message about SharePoint.Client.ServerRuntime not found. SharePoint is installed on another server, so we have installed the SharePoint Client Object model runtime components on the CRM server via Microsoft’s MSI. The DLL’s are in the gac of the CRM Server, as well as C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI. We had to copy them manually to the ISAPI folder. If we run the same code on an environment where CRM and SharePoint are on the same server, it works fine.

  7. Randy says:

    Great job. This gets us part way to meeting our needs. I can create the folder using our customer number instead of name. How would we change the funcationallity back to storing documents for related entities back under the accounts folder? I am having trouble in the AddRootSharePointLocation method.

  8. Pingback: File storage and CRM: what you should know | Surviving CRM

  9. Fridge77 says:

    Hi, thanks for taking the time to provide this example. I don’t understand the settings for registering the plugin… I am entering “preMessageImage” without quotes in the Primary and Secondary Entity boxes but I get an error message stating, “Invalid Primary Entity or Secondary Entity supplied. Please re-enter the data.” when I click on Register New Step. Any idea what I am doing wrong?

  10. spinder30 says:

    Thanks for very nice and handy example. I have a question though regarding combination SharePoint, CRM 2011 and several business units. Let’s say I have 2 BU’s (A and B) that will only have accounts and opportunity’s as folders created based on entity Account. From SP perspective, users within BU A are not allowed to see these documents from BU B and vice versa. I’m thinking about using site collections for the distinction between BU’s in SharePoint. In CRM this has also been set separately with rights and roles functionally. Now I’ve created two default sites from the standard document management setup CRM – SharePoint but only 1 is really the default site.
    This has a result that when working from CRM point of view to upload/check/find documents the folder will be created in the default site.
    I know that this can be altered manually by the user, but is not very user friendly IMO.

    Having read this post I was wondering if it is possible with the check to let the folder be created in the correct sharepoint default site? And could this script be also used when importing (=creating) 1000′s of accounts?

    Hope to hear from you soon.
    KR,

    Erik

    • Hi Erik,

      If I understand correctly, you are looking to have two different CRM Business Units where each is connected to a different SharePoint Site.

      As I think you have seen, the out-of-the-box (i.e. no-code) integration between CRM and SharePoint only really allows for a single default SharePoint Site, meaning the users would have to choose which SharePoint Site to view or upload documents for a particular CRM Account or Opportunity.

      There are two methods round this limitation:

      If the same Accounts and Opportunities are shared between the two Business Units (A and B) with the different sets of Users both accessing the same CRM records – this could be accomplished using either Javascript (which could set the right SharePoint view based the User’s BU) or role-based forms and iFrames.

      If however the behaviour is for different Account and Opportunity records to exist in different BUs each with their own SharePoint Site (so Accounts in Business Unit A would use SharePoint Site A, and Accounts in Business Unit B would use SharePoint Site B) then this would be simpler to achieve using a custom Plugin which would set-up the SharePoint Site Locations in the relevant SharePoint Site based on the Business Unit for the record. (bit convoluted but hopefully that makes sense!)

      Feel free to reply back and I can provide further detail if helpful.

      Kind Regards, Paul.

  11. Tim says:

    This sounds exactly what we are looking at doing Paul. Does option 3 also alow for specific site templates to be specified for different Entities (e.g. Opportunity entity and different business units). And can the base template be the SharePoint Project Site template

    • The SharePointSite and SharePointDocumentLocation model within CRM 2011 is incredibly flexible in that you could quite easily have multiple SharePoint sites available (say A and B) and then have new CRM records linked to either SharePoint Site depending on the Business Unit or some other factor.

      So in this example we could have two SharePoint Sites created and available, both of these set up as SharePointSite records in CRM (so CRM is aware of the different sites) and then our custom plugin firing when a new Opportunity or other record is created. The Plugin would then examine the Business Unit for the new record, determine which site should be used (A or B) and create the SharePointDocumentLocation record accordingly. Voila, we have different CRM records linking to different SharePoint Sites – potentially on completely different servers if need be. (security permitting!)

      In terms of Site Templates – you could use the SharePoint Client Access Model to create new Sites using a specific Template and then link these to CRM. The usual SharePoint method of developing the template and saving into the list of available site templates, and developing the code to create new sites from this template can work fine; however the usual SharePoint cavetts apply in governance of creating lots of SharePoint Sites, security and hitting a problem if we need to make a global change to all of them through the template!

      In terms of developing the code for creating the new Site from a Template, look into the WebCreationInformation class in the Client Object Model. This class has a property Webtemplate that you can use to specify the site template. MSDN has a good snippet of example code for this at http://msdn.microsoft.com/en-us/library/ee537538.aspx

      Hope that helps.

  12. Pingback: Dynamics CRM & SharePoint 2010 Integration – Useful Links | daveymcglade.net

  13. Mark says:

    Hi Paul, thanks for this post. I’ve found it really helpful. I’m very new to CRM and not sure exactly how to register the plugin. I just had a crack at registering it and a step but I can’t get the code to fire, it’s still creating the documents in the out of the box locations. I’ve registered a step with a message of “Create” against the Primary Entity of “sharepointdocumentlocation” but it’s still creating the documents under “/lead”, “/account” etc. Am I on the right track? Any help appreciated!

    Thanks,
    Mark

  14. jaysil says:

    Hi, This post is really awesome and gave me a very clear idea about CRM integration with sharepoint. I have one question that is it possible to write plug-ins or Custom workflow to copy all attachments of incoming mail to sharepoint location and crm will show the link to the location .
    If yes, then which one is more appropriate Plug-ins or Custom Workflow.

  15. Pingback: CRM 2011 integration with SharePoint 2010 | SharePoint and CRM Revealed

  16. Pingback: CRM 2011 Integration with SharePoint: Custom Document Management | Dynamics CRM Guru

  17. Jhon Medina says:

    Thanks for very nice and handy example. I have an issue regarding with using of SharePoint Record Center capability. As you know, Sharepoint’s Record Center do not use folders to store documents but it distribute automatically the documents differentes sites and libraries, so it is not posible to have an only folder in sharepoint to keep all the documents belonging to a Dynamics CRM record like is the architecture of the native integration.
    Do you think tha it is a good alternative do not use the native integration you explain and instead of this to use the sharepoint 2010 with record center and built from zero the integracion between CRM 2011 and Sharepoint 2010 (wich imply to throw the native integration to the garbage !)? Or it is better to use and to extend the native integration and do not use the sharepoint Record Center capability? Wich and why is the better integration architecuture between this two platforms?

  18. Hey! I know this is kinda off topic however I’d figured I’d ask.
    Would you be interested in trading links or maybe guest writing a blog article or vice-versa?
    My website addresses a lot of the same topics as yours and I
    think we could greatly benefit from each other. If you’re interested feel free to send me an email. I look forward to hearing from you! Terrific blog by the way!

  19. Lee says:

    Thank you for the example. I am having a problem with the SharePoint site connection. The error Message I am receiving is: SharePointMethods.Constructor –> [request failed.]. I know that this is trying to use the SharePoint clientcontext and I am unable to find the cause of the failure. Any help would be greatly appreciated.

    • Lee says:

      I realized that this was due to registering the plug-in with sandbox isolation mode. I have re-registered the plug-in without sandbox isolation and am no longer receiving the error above. Thanks again!

  20. Mike Kolling says:

    Nice one Paul!

  21. Mark says:

    Hi – thanks for the example. I’m new to writing solutions for CRM, and I’m trying to adapt this idea to creating a document folder for each new CRM Incident, and one issue I’m encountering with this example is when I try to update the new document library (doing the .ExecuteQuery() after the newDocumentLibrary.Update(); line), I’m getting an exception that reads “Access denied. You do not have permission to perform this action or access this resource.” I’ve tried different credentials, for myself, the admin user, or no credentials at all, but all seems to result in the same exception.

    I’m trying this code in a separate Visual Studio forms solution before attempting to incorporate it into a Plugin.

    • Hi Mark,

      The permissions between CRM, Active Directory and SharePoint can be tricky – we have hit some similar problems when building these concepts into a CRM-SharePoint Framework. When debugging the logic, the user credential/context that the CRM Plugin is running as can muddy the waters when attempting to communicate with SharePoint, so to help debugging it can be useful to build a mini console application (or web application) to test how your code is communicating to the SharePoint Client Object Model. This also allows you to debug by Visual Studio which can be quite helpful.

      I’ve been doing a lot of work integrating CRM and SharePoint over the last year, so if you are still encountering the error, then it could be worth posting the full Error Trace as I might be able to spot which end the credential error is occurring from.

      Kind Regards, Paul.

  22. Maarten says:

    Hi Paul,

    I’m trying to get this to work in a Sandboxed Plugin in CRM Online and I want to communicate with SharePoint Online (both have version 2013). For that to work, I merged the SharePoint Client and Runtime DLL’s with ilmerge.exe into my plugin DLL. But as soon as the code:

    _context = new ClientContext(spSiteUrl)
    {
    Credentials = new SharePointOnlineCredentials(username, securePassword)
    };

    tries to create the SharepointOnlineCredentials it throws an exception because of the Sandbox limitations:

    System.Security.SecurityException: Request for the permission of type ‘System.Security.Permissions.RegistryPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′ failed.

    Did you find a solution for this? If so, can you reply here or mail it to me?
    Thanks in advance!

    Kind regards,

    Maarten

    • Hi Maarten,

      I imagine this post is well out of date now! (been submerged in a long project for some time now, so am suffering on the blog front!)

      The On-Premise method of placing the files in the GAC works well (if awkward as the GAC tends to encourage DLL bloat) but obviously for Online that is not going to be possible.

      The ILMerge.exe should in theory work fine but could see this hitting problems as the SharePoint DLLs are quite intrusive. (even the Client Object Model)

      A way round this my be to setup a ‘middle-tier’ webservice that the CRM Plugin could communicate with, and then the Webservice calls out to SharePoint Online – keeping the Plugin ‘DLL-lite’ and having the Webservice reference the SharePoint Client DLLs; however this approach defeats some of the no-server benefits of using both CRM Online and SharePoint Online.

      Otherwise you could continue to investigate the error and determine why the ‘mscorlib’ DLL (which is one of the supporting DLLs) is attempting to look at the registry.

      Be interested to know if you found success to this problem in the end.

      Kind Regards, Paul.

      • Hi Paul,

        I saw no other option than to create the ‘middle-tier’ webservice. It was the only way to get it working.
        Hopefully CRM 2013 Online and SharePoint 2013 Online integration will mature one day and my webservice will become obsolete.

        Kind regards, Maarten

  23. Sethuraman R says:

    Thanks for sharing this information…ROC Software

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s