Hardcore CRM: Dynamic provisioning of the ribbon

At the moment I’m working on a series of articles about document integration within CRM. I have this big hairy audicious goal that I want to achieve… which is to provide a better experience than the standard SharePoint integration.

Set-a-big-hairy-audacious-goal

In my last article I came to a point in which I got stuck. I managed to get the ribbon definition from CRM, but failed to publish it back to CRM. Fortunately I ran into a MSDN sample in which Microsoft explained how to work with exporting and importing and publishing solutions. I managed to adapt the code to my needs.

In this article I want to share my experience and code with you. From this point on I can return to the Document Integration series of articles. 

The hard part is that I want to be able to manipulate the ribbon dynamically. The hard part was figuring out in what order the steps needed to be executed.

In order to manipulate the ribbon, I need to do the following steps:

  • Create or Retrieve an “Empty” solution
    Check if the publisher exists, if not create him.
    Check if the solution already exists. If not, then create it.
    Export the solution and return its bytes
  • Get the specific ribbon I want to modify
    Export a specific entity ribbon from CRM and unzip it.
  • Modify the definition of the specific ribbon xml (not in this article)
    Out of scope in this article. But sooner or later we need to tinker with the ribbon definition.
  • Create a “Filled” solution that I can use for import
    Use the files from “Empty solution” and combine it with the altered ribbon definition and create a new solution file (in memory only).
  • Import the “Filled” solution
    Import the new solution file
  • Publish the changes in CRM
    Publish the modifications in CRM to make them visible.

Below I have the main routine that coordinates all steps:

        public void Execute()
        { 
          var entityName = "account";
          try
          {
              var connection = CrmConnection.Parse(_connectionUrl);
              // Connect to the Organization service. 
              IOrganizationService service = new OrganizationService(connection);
              // create or retrieve a basic solution and unzip the solution and contenttypes file
              var emptySolution = CreateOrRetrieveEmptySolution(service);
              var unzippedSolution = unzipFile(emptySolution, "solution.xml");
              var unzippedContentTypes = unzipFile(emptySolution, "[content_types].xml");
              // get the specific ribbon
              OrganizationRequest req = new OrganizationRequest("RetrieveEntityRibbon");
              req.Parameters.Add("EntityName", entityName);
              req.Parameters.Add("RibbonLocationFilter", RibbonLocationFilters.All);
              var response = service.Execute(req);
              var zipped = (byte[])response.Results["CompressedEntityXml"];
              var ms = new MemoryStream(zipped);
              // unzip the ribbon
              var unzippedRibbon = unzipFile(ms.ToArray(), "RibbonXml.xml");
              var text = System.Text.Encoding.UTF8.GetString(unzippedRibbon, 0, unzippedRibbon.Length);
              // TODO ... DO THE RIBBON MODIFICATION...
              // convert string back to byte array
              var unzippedModifiedRibbon = System.Text.Encoding.UTF8.GetBytes(text);
              
              // make a solution zip
              var zipfile = CreateImportSolutionZipFile(unzippedSolution, unzippedContentTypes, unzippedModifiedRibbon);
               
              // import solution
              var importRequest = new OrganizationRequest("ImportSolution");
              importRequest.Parameters.Add("CustomizationFile", zipfile);
              importRequest.Parameters.Add("ImportJobId", Guid.NewGuid());
              importRequest.Parameters.Add("PublishWorkflows", true);
              importRequest.Parameters.Add("OverwriteUnmanagedCustomizations", true);
              service.Execute(importRequest);
              // publish changes
              var publishRequest = new PublishXmlRequest();
              publishRequest.ParameterXml = "<importexportxml>" +
              "    <entities>" +
              "        <entity>" + entityName + "</entity>" +
              "    </entities>" +
              "</importexportxml>";
              service.Execute(publishRequest);
          }
          catch (Exception e)
          { 
            // error handler...
          }
        }

For now it looks like I have cracked the toughest nut. I still need to tinker with the ribbon, but that’s just a matter of inserting multiple blocks of code in an XML string.

For your convenience I put up the complete class file (with all helper methods) for download:  File Attachment: RibbonSolution.zip (3 KB)

Leave a Reply

Your email address will not be published. Required fields are marked *