Quantcast
Channel: Develop 1 Limited Blog
Viewing all 296 articles
Browse latest View live

SharePoint Integration Reloaded – Part 1

$
0
0

Back in the day when CRM2011 was first in beta I blogged about the exciting SharePoint integration and how it works. This post is about the exciting new server side SharePoint integration that is now available as part of CRM2013 SP1 Online.

There has already been some good posts on how to set up SharePoint sever-side Sync but in this series I'm going to explain how the server to server integration works in more detail and run through some scenarios of how it can be used for custom solutions.

CRM List Component Integration

Before CRM2013 SP1 was released the only option for SharePoint Integration was to use the CRM List Component. Each document location was surfaced on a record form record via an IFRAME that placed a SharePoint page inside the CRM page via the list component aspx page. This SharePoint page rendering the document library's default view with a CRM theme and provided the upload/download commands.

  1. The CRM form page is displayed in the browser and including an IFRAME that requested the configured document library page from SharePoint.
  2. The IFRAME shows the SharePoint document library styled to look like CRM. This required the user to be independently authenticated with SharePoint.
  3. Using any of the actions in the page (New/Upload etc.) sends requests directly to SharePoint.

Changing Landscape

This approach worked well but since the user was accessing SharePoint directly within the IFRAME they'd sometimes encounter authentication issues where they must be authenticated with SharePoint first and sometimes SharePoint needed to be configured to allow inclusion of content in IFRAMES. In addition to this the list component required a sandbox host to run but this feature is being phased out in SharePoint Online.

Server to Server SharePoint Integration (S2S)

With the introduction of CRM2013 SP1 a new type of integration has been developed that provides direct server to server integration between SharePoint and CRM thus removing the need for the user to be pre-authenticated with SharePoint on the client.

  1. The Record page includes a standard sub grid that is populated using the CRM entity query object model. CRM converts a RetrieveMultiple request on the SharePoint Document entity into SharePoint CAML (Collaborative Application Markup Language) query and sends it to the SharePoint Web Services. The important part here is that this query is run in the context of the currently logged on user and so they only see the document that they have access to in SharePoint (more on how this works in part 2 of this series).
  2. Documents are rendered inside the CRM Form HTML as a standard sub grid in the same way that any other record might be displayed.
  3. Using the New/Upload command bar buttons sends a request to CRM by way of an Execute Request in the same way that any other command bar buttons might do.
  4. CRM uses the SharePoint Web Service API to execute the requests and refreshes the sub grid.

This server to server integration only works for CRM Online/SharePoint Online combinations that are in the same tenant due to the nature of the server to server authentication and can be turned on in the Document Management Settings using the 'Enable server-based SharePoint integration'. There is a note that states that sandboxed solutions will not be supported in the future for SharePoint online.

Differences between List Component and Server-to-Server

Once S2S integration is enabled you'll see a similar view to the list component but it looks far more CRM2013 like. Apart from a slicker interface there are a few other differences:

Folder Support

The S2S sub grid doesn't support folders within the document library and so all documents are flattened down underneath the document location folder. The Location column does give you folder name which you can sort by to allow grouping by folder.

Custom Views

The great thing about having the documents queried by CRM is that you can create custom views of documents in the same way you would with any other entity in CRM. When using the list component the default view in SharePoint was rendered in the IFRAME meaning that to get new columns you had to have list customisation privileges on SharePoint such that all users would see the changes. With the new server to server integration you can select SharePoint columns to include in your own views and even add in your own filters using the CRM advance find interface. If you think about it – this is very cool!

Item Actions

The List Component was by nature very similar to the SharePoint list user interface and so it had more or less full support of actions that can be performed from SharePoint (with the exception of workflow operations). The server to server sub-grid provides all the main functions but with some options such as Alert Me, Send Short Cut, View History and Download a Copy being unavailable.

The S2S integration gives the following command bar actions:

This is in comparison to the List Component actions that are as shown below.

Inline Dialogs

With CRM2013's single page user experience any pop-out windows are supposed to be kept to a minimum. When using the list component operations (such as check-in/out) a new window would always pop out but with S2S integration an inline dialog shown instead. This really make it feel tightly integrated and slick.

Out of these differences, the lack of folder support is the only one that has had any significant effect on my solutions but actually can be seen as an advantage if using sub-folders to hold child entity documents. In this scenario all documents will be visible from the parent record's document view rather than rely on the user drilling down into each folder to see content.

That's all for now but in the next article in this series I'll show you more of how this functionality works under the covers.

Read Part 2

@ScottDurow


Early Binding vs Late Binding Performance (Revisited)

$
0
0

After having an interesting debate on the CRM Community forums about the performance of Early verses Late Bound entities my friend Guido Preite pointed me at a good blog post on this subject by James Wood named 'CRM 2011 Early Binding vs Late Binding Performance'. I have always been an advocate of Early Bound types but it is true that the SDK still states in the 'Best Practices for Developing with Microsoft Dynamics CRM'

"…use of the Entity class results in slightly better performance than the early-bound entity types"

However it also states that that the disadvantages of using the late-bound Entity types is:

"…you cannot verify entity and attribute names at compile time"

I've seen many bugs introduced into code from the use of late bound types because typos can easily be introduced into the strings that are used to determine the entity and attribute logical names. Due to the productivity gains that come with Early Bound types I always recommend their use if the schema of your entities is known at compile time. There are times when this is not true or you are creating code that must run in a configurable way on many different entities or attributes in which case the late bound entity type is the only choice.

So what about performance?

  1. The SDK state that Late Bound types give 'slightly' better performance and states 'Serialization costs' as the reason.
  2. James' post states a 30% increase in speed for 200 Create operations, and <5% increased for 1500 operations.

So addressing each of these points in turn:

Although before CRM2011 there were serialization costs in using early bound types because they were serialised as part of the web service call, with CRM2011/2013 the early bound types just inherit from the Entity class and the early bound attribute properties simple set/get values from the underlying Attribute collection. The serialization when making SDK calls is effectively the same for both early and late. The main difference is due to the extra work that the OrganizationService Proxy has to do when converting the Early Bound type to the Entity type and then back against when it's received from the server. This is done using Reflection to first search for the Early bound classes and then by searching the classes for the one that matches the logical name received. This obviously will have a cost but it seems to be work that is done once per Service Channel and then cached to avoid any further cost.

James' tests are interesting but perhaps a bit misleading because the initial cost of this additional work is included in his overall speed calculations. This is probably why the overall percentage cost of early binding goes down as the number of records increases.

To remove this initial cost from the equation I adapted his code to introduce a warmup time. In tests I couldn't categorically show that either Late Bound or Early Bound had any performance difference once the OrganizationService was 'warmed up' with all reflection done and cached. In fact sometimes the test showed that early bound was quicker which leads me to believe that the main influencing factor is somewhere else like Database or Server performance. To make the results easier to interpret I have simply shown the average operation time after the warmup period. I also separated out the tests so that the Early Bound types were not compiled and picked in the Late Bound tests.

Each test was a warm up of creating 400 records and a run of creating 500 records.

Conclusions

Whilst it is true that using Early Bound classes incurs some cost of additional 'plumbing' – assuming that you are caching the WCF Service Channels (which the Microsoft.Xrm.Client.Services.OrganizationService does for you) because the difference in speed is so small (< there really is no reason not to use the Early Bound classes unless you have performance related issues and want to eliminate this as the cause.

If you are interesting, here is the code I used (based on James' code)

static void Main(string[] args)
{       
    int warmupCount = 400;
    int runCount = 500;

    CrmConnection connection = new CrmConnection("Xrm");
    var service = new OrganizationService(connection);

    CreateAccounts("Early Bound Test", warmupCount, runCount, () =>
    {
        Account a = new Account();
        a.Name = "Test Early Vs Late";
        service.Create(a);
    });


    CreateAccounts("Late Bound Test", warmupCount, runCount, () =>
    {
        Entity e = new Entity("account");
        e["name"] = "Test Early Vs Late";
        service.Create(e);
    });

    TidyUp();

    Console.WriteLine("Finished");
    Console.ReadKey();
}

static void CreateAccounts(String name, int warmup, int runs, Action action)
{
    Console.WriteLine("\n" + name);
    // Warm Up
    for (int i = 1; i <= warmup; i++)
    {
        if (i % 10 == 0)
            Console.Write("\r{0:P0}     Warmup   ", (((decimal)i / warmup) ));
        action();
    }

    // Run Test
    double runningTotal = 0;
    Stopwatch stopwatch = new Stopwatch();
    for (int i = 1; i <= runs; i++)
    {
        stopwatch.Reset();
        stopwatch.Start();
        action();
        stopwatch.Stop();
        runningTotal += stopwatch.ElapsedMilliseconds;

        double runningAverage = runningTotal / i;
        if (i % 10 == 0)
            Console.Write("\r{0:P0}     {1:N1}ms   ", (((decimal)i / runs)), runningAverage);
    }
}

SharePoint Integration Reloaded – Part 2

$
0
0

Part 1 of in this series described how SharePoint Server to Server SharePoint integration (new to CRM2013 SP1) works from the client interface perspective. In this post I'd like to share a bit more how this all works from the server side.

Authentication

When this feature was introduced my first question was about authentication. Having created a fair number of solutions that integrated SharePoint with Dynamics CRM on the server side I knew that this is a tricky area. Since this feature is only available for CRM Online to SharePoint Online where they are in the same tenant it makes authentication slightly simpler because there is already an existing trust in place between the two servers which allows Dynamics CRM to authenticate with SharePoint and act as the calling user. The HTTP request that comes from the client is inspected by the integration component and the UPN is used to authenticate with SharePoint as the same user rather than the service account. This is acting on behalf of the user is critical because when documents are created, checked in/out or queried, it must be performed under the account of the user and not the system account. Perhaps even more important, when CRM queries for documents it will only return those that the user has access to as configured in SharePoint.

If this feature is made available for On Prem customers I would expect that a configuration would have to be made available to provide the user's SharePoint username and password to use when performing server side operations.

Query SharePoint Documents

The new SharePoint sub grid that is rendered by CRM actually uses exactly the same query mechanism as any other entity – but rather than the query being sent to the CRM Database, it is handled by the SharePoint query handler. If you fire up Advanced Find, you'll see a new Entity named 'Documents' but if you query against this entity you will get the error:

The error is given by the SharePoint FetchXml conversion to CAML only works if a specific regarding object is provided – this means that you can only return records for a specific folder, rather than all documents in all document locations. When the refresh button is clicked on the client sub-grid there is FetchXml similar to the following executed:

<fetch distinct="false" no-lock="true" mapping="logical" page="1" count="50" returntotalrecordcount="true" ><entity name="sharepointdocument" ><attribute name="documentid" /><attribute name="fullname" /><attribute name="relativelocation" /><attribute name="sharepointcreatedon" /><attribute name="ischeckedout" /><attribute name="filetype" /><attribute name="fullname" /><attribute name="modified" /><attribute name="sharepointmodifiedby" /><attribute name="relativelocation" /><attribute name="documentid" /><attribute name="modified" /><attribute name="fullname" /><attribute name="title" /><attribute name="author" /><attribute name="sharepointcreatedon" /><attribute name="sharepointmodifiedby" /><attribute name="sharepointdocumentid" /><attribute name="filetype" /><attribute name="readurl" /><attribute name="editurl" /><attribute name="ischeckedout" /><attribute name="absoluteurl" /><filter type="and" ><condition attribute="regardingobjecttypecode" operator="eq" value="1" /><condition attribute="regardingobjectid" operator="eq" value="{1EF22CCD-9F19-E411-811D-6C3BE5A87DF0}" /></filter><order attribute="relativelocation" descending="false" /></entity></fetch>

The interesting part here is that we can add filters not only by regarding object but we could also add our own filters for name or document type. Initially I was confused because running this Fetch query in the Xrm Toolbox FetchXml tester gave no results but as it turns out this uses the ExecuteFetchRequest rather than RetrieveMultiple and this new SharePoint integration is only implemented on the latter.

Internal Execute Messages

This new server to server functionality is exposed by a set of internal messages that are not documented in the SDK but by using Fiddler (the tool that give you super powers!), you can see these messages being called from the client when operation such as Check In/Check Out are called. Here is a list of these internal messages:

Message Name

Description

RetrieveMultipleRequest
(sharepointdocument)

Returns a list of document names from SharePoint for a specific document location. This query is converted into SharePoint CAML on the server and supports basic filter criteria and sorting.

NewDocumentRequest

Creates a new document location and matching SharePoint folder and is called when the documents sub grid is first shown with no document locations configured.

 

FileName– Name of the file to create including extension (e.g. NewDocument.docx)

RegardingObjectId– Guid of the record that the document location belongs to

RegardingObjectTypeCode– Object type code of the record the document location belongs to

LocationId– The ID of the document location to add the new document to (in case there are multiple)

CheckInDocumentRequest

CheckOutDocumentRequest

DisregardDocumentCheckoutRequest

This performs the check in/out operation on a specific document in SharePoint.

Entity– The document to check in/out with the 'documentid' property populated with the List Id in SharePoint.

CheckInComments

RetainCheckOut

CreateFolderRequest

Creates a new documents location in CRM and the corresponding SharePoint folder.

FolderName– the name to give to the SharePoint folder

RegardingObjectId– Guid of the record that the document location belongs to

RegardingObjectTypeCode– Object type code of the record the document location belongs to

 

Now before you get excited you can't use these requests on the server because you will get a 'The request <Request Name> cannot be invoked from the Sandbox.' (Yes, I did try!) This is expected since the sandbox does not have access to the HTTP context that contains the information about the calling user and so the authentication with SharePoint cannot take place.

I proved this using a Custom Workflow Activity that tried to call 'CreateFolder' and you see the following error.

These requests can however be called easily from JavaScript which opens up some interesting possibilities (if a little unsupported because these messages are not actually documented the SDK at the moment):

  1. Automatically create a document location using a different naming convention to the standard one via JavaScript onload of a record if there isn't one already.
  2. Provide a custom view of SharePoint documents using fetchxml – this could even be filtered to just show a particular file type by adding a condition similar to <condition attribute="filetype" operator="eq" value="jpeg"/>
  3. Provide custom buttons to create documents in SharePoint.

I hope you've found this interesting - Next time I'll show you how to get around the sandbox limitation to perform server side operation on SharePoint from a CRM Plugin or Workflow Activity.

@ScottDurow

Separate Content from Code

$
0
0

Back in the On-Prem CRM 3/4 days I would regularly customise the Dynamics CRM help files to add new topics and amend existing ones to match a company's solution. Context sensitive help was also possible by linking specific entities to specific help topics. With CRM2011 and the advent of the 'getting started pane' help became more of an inline experience with links being displayed above grids of records however customising the online help was no longer supported. CRM2013 removed the 'getting started pane' and so it was much harder to provide a customised help experience. I'm really pleased that the Dynamics CRM team has invested in this area in CRM 2015 in the form of the 'Custom Help URL' feature.

Global Help URL

If you navigate to Settings->Administration->System Settings you'll see the new Custom Help URL options. To use this new feature you must first enable 'Use custom Help for customizable entities'


You then have the option to provide a global help URL that will be used when users click the '?' icon on the top right of any refreshed UI page. It is also used when user click on the 'classic' style help icon on non-refreshed pages.

If the help button is clicked on an entity form then some contextual parameters are passed. For the Case entity the following are added to the Global Help URL:

entrypoint=form&formid=4a63c8d1-6c1e-48ec-9db4-3e6c7155334c&typename=incident&userlcid=1033

Solution Deployment vs. Content Management

You have the option to use either a relative URL that points to a WebResource as shown in the screenshot above but you can also use an absolute URL to content hosted outside of CRM. You can also then create a help URL per entity in the entity customisation properties as shown below.

The disadvantage with this external URL per entity approach is that this URL is likely to be different in your DEV/UAT/PROD environments and since this URL will be transferred with each solution deployment we'll have to update this setting every time we deploy.

I've found that although the software development lifecycle is usually a regulated release process, the help content authoring process tends to be more of an evolving one. With the CRM2011 getting started pane being linked to Web resources I always found it much harder to get this content updated due to the rigor of the deployment process. For this reason I now recommend not coupling your company's help content to your CRM solution's web resources but rather use an external content management system such as SharePoint. This allows you to then regular release updates and new content without having to schedule a new release and the down time associated to this.

But how do we avoid having to update the entity URLs on each DEV/UAT/PROD environment? We use only the Global Help URL in combination with the typename querystring parameter and not use the entity Help URL at all.

To do this in SharePoint:

1. First we must enable embedding of code into SharePoint pages.

2. Edit the SharePoint page that is the default landing page in the Global Help URL (e.g. SitePages/Home.aspx)

3. On the Insert ribbon tab, select 'Embed Code'

And paste the following code:

<script>
function getQueryStringArray(){
    var assoc=[]; 
    var items = window.location.search.substring(1).split('&'); 
    for(var j = 0; j < items.length; j++) { 
       var a = items[j].split('='); assoc[a[0]] = a[1]; 
    }
    return assoc;
}
var qs = getQueryStringArray();

if (qs.typename!=null)
{
  window.location.href = '/SitePages/' + qs.typename + '.aspx';
}
</script>

Now when CRM opens your Global Help URL it will redirect to the specific page for that entity passed on the typename. You can then create pages for each typename in the Site Pages library:

Since the Global Help URL is not solution aware, you can deploy your solution to multiple environments without having to change all those URLS with each deploy.

You can read more about this on Bryce Holmes' post on this feature. Read the Online Help about this feature and read Magnetism's blog on this feature.

Hope this helps!

@ScottDurow

SharePoint Integration Reloaded – Part 3

$
0
0

In Part 1 and Part 2 of this series we have discussed how the new server-to-server integration with SharePoint works under the covers. In this post I'll show you how to integrate with SharePoint directly from a sandboxed workflow activty/plugin rather than relying on the out of the box integration.

Using the standard integration, a new folder will be created for each record underneath the default site. In some solutions you'll find that you want to modify this behaviour so that folders are created in a custom location. You may for example want to have an opportunity folder created under a site that is specific to a particular client rather than all under the same site.

The challenge with integrating with SharePoint using a CRM Online Workflow Activity/Plugin is that you can't reference the SharePoint Assemblies which authenticating and calling the SharePoint web service somewhat harder. Thanks goes to fellow Dynamics CRM MVP Rhett for his blog that provided a starting point for this sample - https://bingsoft.wordpress.com/2013/06/19/crm-online-to-sharepoint-online-integration-using-rest-and-adfs/. The sample code in this post shows how to create a folder in SharePoint and then associate it with a Document Location. The authentication with SharePoint works via ADFS and since the out of the box integration uses a trust between CRM and SharePoint that is not accessible from a sandbox (even if you try and ILMerge it!) we have to provide a username and password that will act as our privileged user that can create folders in SharePoint. I have left a function where you can add your own credentials or implement a method to retrieve from a secure entity in CRM that only administrators have access to. Look in the code for the 'GetSecureConfigValue' function.

The sample contains a custom workflow activity that works in a CRM online 2013/2015 sandbox accepting the following parameters:

  • Site– A reference to the site that you want to create a folder in. You could store a look up to a site for each customer and populate this parameter from the related account.
  • Record Dynamic Url– The 'Dynamic Record Url' for the record that you want the SharePoint document location to be related to. This uses my Polymorphic input parameter technique. You simply need to pass the Record Url (Dynamic) for the record that you wish to create the folder for.
  • Document Library Name– The name of the document location to create the folder underneath. In the out of the box integration this is the entity logical name (e.g. account)
  • Record Folder Name– The name of the folder to create. You could use the client name, client ID etc. – but it will automatically have the GUID appended to it to ensure uniqueness just like the out of the box integration.

Calling the workflow activity might look like:

The workflow activity is deployed using the Developer Toolkit for Dynamics CRM and performs the following:

  1. Checks if the document location already exists for the given site/document library – if so it simply returns a reference to that
  2. Checks if a document location exists for the given document library – if not, one is created
  3. Creates a SharePoint folder using the SpServiceclass. It is worth noting that if the folder already exists, no exception is thrown by SharePoint. The SpService class must first authenticate using the SpoAuthUtilityclass.
  4. Creates a Document Location for the newly created folder.

You could choose to run the workflow in Real Time or asynchronously on create of a record – the down side of real time is that it will increase the time that the record takes to save.

Check out the code in MSDN Samples- you'll need to do a Nuget package restore to pick up the referenced assemblies.

View/Download Code

That's all for now – have a Happy New Year!

@ScottDurow

 

 

Ribbon Workbench 2015 (Happy New Year!)

$
0
0

Happy New Year – I hope 2015 will bring you lots of good things. Dynamics CRM 2015 upgrades are rolling out to Dynamics CRM online organisations with rapidly increasing frequency and so I have updated the Ribbon Workbench for 2015 support. You will see a new update notification when you next open the Ribbon Workbench and you can let the auto update do the download and install for you. If you would rather install manually you can download and install over the top of the previous version.

Automatic update is mandatory after 10th January – why?

You'll have the option to update before the 10th of January and then you'll be forced to update after that. Some people have asked me other the last couple of years why I roll out these mandatory updates. Initially this was about ensuring that the beta version was updated with any bug fixes but since then it has been a way of ensuring that I keep rolling out new version. I set a 6 month release cycle target so that even though I roll out minor updates I have to roll out at least one mandatory update every 6 months! I hope you don't mind being forced to update too much – I certainly feel that the benefits outweigh the downsides and it keeps me on my toes J

Command bar 2015

The good news about the 2015 command bar is that after the big shift from the ribbon in CRM2013 there haven't been any significant changes. All of your ribbon xml customisations in CRM2013 will remain to work in CRM2015 provided that any JavaScript you are calling in your commands doesn't use any deprecated SDK elements. If you compare the RibbonXml XSD between the CRM2013 and CRM2015 SDKs you'll see that they are in fact identical!

All the best for 2015!

@ScottDurow

SparkleXRM for CRM2015 with process API support

$
0
0

I've just committed an update to SparkleXRM with CRM2015 support and the process client API. One of the design decisions I made early on with SparkleXRM was to stick with a CRM2011 solution format to allow installation on both CRM2011 and CRM2013. Now that CRM2015 does not support installing CRM2011 solutions I've had to branch and make both CRM2011 and CRM2015 versions available. The code base still remains the same but they are distributed through two separate solution files depending on your target version. You can download the new CRM2015 SparkleXRM solution from github.

The new client side process control API is such a welcome addition. The latest version of SparkleXRM contains support for this so that you can:

Write code to run when the stage is changed or the user selects a process tab (such as hiding/showing sections on the form.

// Add On Process Stage change
Page.Data.Process.AddOnStageChange(delegate(ExecutionContext context){
    // Stage Stepped forwards backwards
});

// Add On Process Stage change
Page.Data.Process.AddOnStageSelected(delegate(ExecutionContext context)
{
    // Stage Tab Selected
});

Write code to get the current process and stage so that onload functions can show/hide sections on the form.

// Get Current Process
Process process = Page.Data.Process.GetActiveProcess();

Stage stage = Page.Data.Process.GetActiveStage();
Script.Alert("Process = " + process.GetName() + " Stage = " + stage.GetName());

Write code to get the stages and steps of the current process and find the attributes that are referenced – I've not found a use for this yet!

// Get Stages
ClientCollection stages = process.GetStages();

if (stages.GetLength() > 0)
{
    // Get Steps
    Stage stage0 = stages.Get(0);
    ClientCollection steps = stage0.GetSteps();
    steps.ForEach(delegate(Step step, int index)
    {
        Script.Alert(step.GetName() + " " + step.GetAttribute());
        return true;
    });
}

Write code to show/hide or collapse/expand the process ribbon:

// Show/Hide Process
Page.Ui.Process.SetVisible(true);

// Expand/collapse
Page.Ui.Process.SetDisplayState(ProcessExpanded.Collapsed);

Write to advance/step back the stage or change the process/stage completely:

// Change stage
Page.Data.Process.MoveNext(delegate(MoveStepResult result)
{
    Script.Alert(result.ToString());

});

// Change process
Stage currentStage = stages.Get(0);
Page.Data.Process.SetActiveStage(currentStage.GetId(), delegate(SetActiveStageResult result)
{
    Script.Alert(result.ToString());
});

// Change process to the first available process that the user has access to. 
// If the same as the current process, this does nothing.
Page.Data.Process.GetEnabledProcesses(delegate(Dictionary processes)
{
    Page.Data.Process.SetActiveProcess(processes.Keys[0], delegate(SetActiveProcessResult result)
    {
        Script.Alert(result.ToString());
    });
});

Along with the server side branching support for processes – I think this really finishes off this feature nicely. The business process flow feature is now by far my favourite in terms of innovation, business usefulness and developer API. First it was gold in CRM203 RTM, then green in SP1 - now with CRM2015 I especially like the calming cool blue that the process ribbon is now rendered with!

Cheers,

@ScottDurow

 

The cream cracker conundrum (or customising the sub grid command bar)

$
0
0

I still find the streamlined user experience offered by the Command Bar a welcome change from the CRM2011 Ribbon. The sub-grid command bar is the only possible exception with the loss of the ability to add custom sub-grid buttons. There are only at most two buttons on a sub grid – 'Add' and 'Open Associated Sub-Grid'

The user must click on the 'Open associated sub-grid' button to show the full associated view and the full command bar with custom buttons. I say 'possible exception' because in fact there are still the same number of clicks involved (users had to click on the sub grid to show the contextual ribbon before) but it does feel as though we should have the ability to add buttons to the sub-grid command bar. I can think of some good reasons why this design decision may have been made (performance for one!) – but this post details what you CAN do to the sub-grid command bar.

Because the 'Open associated sub-grid' button is a bit of a mouthful, I'll refer to it from now on as the 'cream cracker' because it kind of looks like one and is equally a bit of a mouth full! (Thanks goes to my friends at the British Red Cross who first named it this way!)

Hiding buttons

We have established that you cannot add buttons to the form sub grid, but both the 'Add New' and 'Cream cracker' buttons are customisable in terms of their command and visibility (but you cannot change the image or the tool tip text).

To hide the sub grid buttons you have the following options:

  1. Hiding based on a security role (if the user does not have access to append a record to the parent or create new records of the sub grid type, the 'add new' button will be invisible
  2. Hiding all of the time using a 'Hide Action'
  3. Hiding conditionally using a security role
  4. Hiding conditionally using a custom JavaScript rule.

A common requirement is to hide the Add New button on a sub-gird until a specific criteria is met on the parent form. The standard ValueRule cannot be used because this rule will only work when the command button is added to a form command bar. So to achieve the conditional show/hide we must use a Custom JavaScript Rule.

The first hurdle is to determine which button needs to be customised. The sub grid 'Add New' button calls a different command depending on the nature of the relationship.

If you have a foreign-key attribute on your child entity that is marked as Field Requirement = 'Optional' then the Add New button will be the AddExistingStandard since it allows you to search for an existing record first. If the Field Requirement = 'Business required' then the button will be AddNewStandard

 

 

Once you've identified the right button, you can then open the ribbon workbench and click Customize Command and add the Value Rule as described by my user voice article.

Changing the command actions

Although we cannot add new buttons (did I mention that?!) we can change the command actions that either of those two buttons call. Since we can't customise the button, the only option here is to customise the command and change its behaviour in a very similar way to adding custom enable rules.

  1. Right click the button in the Ribbon Workbench and select Customise Command
  2. Expand the command in the Commands node in the Solution Elements panel and select the command that has been created for you to customise.
  3. Right click Edit Actions and you can simply delete the standard action and add your own custom ones.
  4. Remember to mark all the enable and display rules that you don't want to customise as IsCore=True.

Once such use of this technique is to replace the standard add new button with a custom dialog.

Refreshing the sub grid Command Bar

You will find that when the form is loaded and the sub grid is refreshed for the first time the EnableRules are evaluated. If however the conditions for the EnableRules change (e.g. a value changes on the parent form) the sub grid command bar will not automatically refresh to reflect the new state. Upon adding or deleting rows in the sub grid the command bar is refreshed – but this isn't much use in this case.

The main form command bar can be refreshed using Xrm.Page.ui.refreshRibbon() however this will not refresh sub grid command bars. Instead, we can add an onchange event to the fields that are used in our ValueRule and call:

Xrm.Page.data.save();

This will refresh the sub grids and re-evaluate any of the EnableRules however it will also save any other dirty attributes and so should be used with caution if you do not have auto-save enabled.

Responding to new/deleted rows in the sub grid

Since the sub grid command bar is refreshed when new rows are added or deleted we can use the fact that the EnableRules will be re-evaluated to call custom JavaScript when the sub grid changes. This simulates a sub-gird onchange event and was first described by James Wood's blog post for CRM2011. He states on his blog that this technique doesn't work for CRM2013 – however if we add the custom EnableRule to the existing command (rather than use a new button as James describes) then this technique works well in CRM2013 and CRM2015. So we can customise the command for the Add New or cream cracker and add a Custom JavaScript Enable Rule that always returns true in just the same way that you might use the EnableRule to dynamically show/hide the button but rather we just run our onchange code.

Perhaps in the future there will be more possibilities but for now that about sums up the possibilities for customising the sub grid command bar.

@ScottDurow


The dependent component Attribute (Id=transactioncurrencyid) does not exist. Failure trying to associate it with Workflow

$
0
0

I recently answered a question on this error on the Community forms and coincidently I've just come up against exactly the same issue!

When importing a solution you receive the error 'There was an error calculating dependencies for this component' and on downloading the log you see the full message similar to:

The dependent component Attribute (Id=transactioncurrencyid) does not exist. Failure trying to associate it with Workflow (Id=<GUID>) as a dependency. Missing dependency lookup type = AttributeNameLookup.

Although this message can appear for other attributes, this post is specifically to do with the transactioncurrencyid attribute being referenced.

When you add a money field to an entity CRM automatically adds a N:1 relationship to the currency entity to hold the currency that the value is stored against. The foreign key attribute is always named transactioncurrencyid.

In this increasingly 'agile' software development world attributes are added and remove fairly regularly during the development phase. If a money field is added to an entity and then removed, the transactioncurrencyid attribute is not removed. Because the relationship automatically created by the system it is not created when deploying to another environment via a solution import because there are no money fields. This leads to your development environment having the additional relationship. This wouldn't cause a problem apart from that when you create a workflow with a 'create' or 'update' step, the currency field is usually pre-populated with the default currency. Consequently when you try to import this workflow into another organization that does not have the currency relationship you will see this error.

The solution is to either delete the transactioncurrencyid field from your development environmentm and from the workflow create/update steps or simply add a dummy currency field to your target environment in order to create the relationship to currency.

@ScottDurow

Business Rules &“SecLib::RetrievePrivilegeForUser failed - no roles are assigned to user”

$
0
0

When publishing your Ribbon Workbench solution you may receive the following error:

"SecLib::RetrievePrivilegeForUser failed - no roles are assigned to user."

The first step in diagnosing these issues is to try and export the same solution using the CRM solutions area and then immediately re-import to see what error is shown. This is exactly what the Ribbon Workbench does behind the scenes.

When doing this you might see:

Upon Downloading the Log File you'll see that the error occurs on a 'Process' and the same error message is shown as reported by the Ribbon Workbench. The User ID is also given which can be used in a URL similar to the following to find the user record causing the issue.

https://<orgname>.crm.dynamics.com/userdefined/edit.aspx?etc=8&id=%7b<User GUID>%7d

You will most likely discover that this user has been disabled or has no roles assigned – this could be that they have left the company or changed job role. You will need to find any Workflows or Dialogs that are assigned to them and re-assign to yourself before you can import the solution.

In most cases, you should not include Dialogs or Workflows in the solution that is loaded by the Ribbon Workbench since this only slows the download/upload publish process down. There is one exception to this – and that is Business Rules. Business Rules are associated with their particular entity and cannot be excluded from the solution. Oddly like Workflows and Dialogs they also are owned by a user but it is not shown in the user interface – nor is there an option to re-assign. There is a handy option on a user record that allows you to 'Reassign Records'

You would think that this would reassign any Business Rules but unfortunately you'll get the following error:

"Published workflow definition must have non null activation id."

The only way to reassign these Business Rules is to perform an advanced find for Processes of Type 'Definition' and Category 'Business Rule'

The results can then be reassigned to yourself using the 'Assign' button on the results grid.

@ScottDurow

SparkleXRM now available on NuGet

$
0
0

I've just published the latest version of SparkleXRM on NuGet to make it easier to use from within Visual Studio.

You can simply add a NuGet reference to SparkleXRM and the dependancies will be pulled in as well.

PM> Install-Package SparkleXRM

Once installed, the Sparkle XRM assemblies will be referenced in your project and the latest managed solution can be found in the '\packages\SparkleXRM.7.2.3\content' folder.

Currently there is the CRM2015 version 7.2.3 available but I'll add the 2011/2013 version as well shortly.

Using the Configuration Data Migration Tool with non-unique display name values

$
0
0

Just thought I would share this little tip to prevent someone else having the same issue:

The Configuration Data Migration Tool is a great way of moving records such as custom auto number settings from one organization to another. You can find it in the SDK under SDK\Tools\ConfigurationMigration.

When configuring the data schema, you are prompted to select the entities and the attributes that you would like to export and then eventually import. This seems like this is all that is required before hitting 'Save and Export' – but be warned there is one more important step if your data does not have unique display name values.

In my example I had two records that had the same name and they already existed in the target organization. Normally, the tool will use the display name attribute to perform a lookup rather than use the primary unique identifier for the record. The result will be that the same record will be updated twice in the target, rather than the two separate records being updated accordingly.

To ensure that the primary name field is not used as the lookup on the target system you must select 'Tools->Configure Import Settings'. This then gives you the option to select the primary key field that you want to use other than the display name:

You can no click 'Save' and export your data knowing that the primary unique identifier field will be used to match the records between the source and destination.

The xml schema file created adds the magic 'updateCompare' attribute to the xml and you are good to go!

<field updateCompare="true" displayname="Counter" name="xbitz_counterid" type="guid" primaryKey="true" />

You can learn more about this very useful tool here - https://technet.microsoft.com/en-us/library/dn647422.aspx Hope this helps!

@ScottDurow

Control Async Workflow Retries

$
0
0

The Dynamics CRM Async Server is a great mechanism to host integrations to external systems without affecting the responsiveness of the user interface. Once such example is calling SharePoint as I describe in my series – SharePoint Integration Reloaded.

A draw back of this approach (compared to using an integration technology such as BizTalk) is that any exceptions thrown during the integration will simply fail the workflow job with no retries. You might already know that the Async Server does in fact have a retry mechanism built in as described by this excellent post from the archives - http://blogs.msdn.com/b/crm/archive/2009/03/25/when-do-asynchronous-jobs-fail-suspend-or-retry.aspx. As you'll see from this article there are some built in exceptions where CRM will automatically retry a number of times as defined by the 'AsyncMaximumRetries' deployment property. The interval between these retries is defined by the 'AsyncRetryBackoffRate' property.

So how can we make use of this retry mechanism with our custom code?

There is an undocumented and unsupported way of using this retry mechanism in your custom workflow activities. I first used this technique back in the CRM 4.0 days and I'm pleased to report that it still works with CRM 2015!

Although unsupported, the worst that could happen is that the workflow would stop retrying in future version of Dynamics CRM and revert to simply reporting the exception. However it still wouldn't be a good idea to rely on this functionality for mission critical operations.

So…plz show me the codez…

catch (Exception ex)
{
    // TODO: Rollback any non-transactional operations

    OrganizationServiceFault fault = new OrganizationServiceFault();
    fault.ErrorCode = -2147204346; // This will cause the Async Server to retry
    fault.Message = ex.ToString();
    var networkException = new FaultException(fault);
    throw networkException;
}

When an exception is thrown by your custom workflow activity you'll see the workflow enter into a 'Waiting' state and the 'Postpone until' attribute will be set to the time when the retry will take place. Once the maximum number of retries has been performed, it will enter the 'Failed' status.

You can use this technique to ensure that any temporary integration failures such as service connectivity issues will be dealt with by a graceful retry loop. It only remains to ensure that you before throwing the exception you rollback any operations performed already by your code (such as creating records) so that your retries will not create duplicate records.

Hope this helps!

@ScottDurow

Can I do that (in Dynamics CRM) ?

$
0
0

I'm sure by now that you've seen the new hierarchical visualisations in Dynamics CRM 2015. By clicking on the little icon in lists or on a form you can see the records graphically laid out. In true Dynamics CRM style anything you can do through the user interface is accessible through the SDK and so we now have the ability perform fetchXml queries using the above, eq-or-above, under& eq-or-under conditions. These new search criteria will find records up to a maximum of 100 recursions for a single hierarchical relationship.

Take a look at the excellent SDK topic Query hierarchical data on the subject.

I have found that when some users see the cool looking Dynamics CRM Online logon page they will ask 'Can I do that?!' I thought I would answer that question with a new SparkleXRM sample solution. Take a look:

Installing Network Visualisations (CRM 2015)

  1. Import SparkleXRM - SparkleXRM_7_2_3_managed.zip
  2. Import NetworkView - NetworkView_1_0_4_alpha_managed.zip
Usual MIT license applies!

Known Issues

For a list of known issues and reporting new ones please use the GitHub repository.

Thanks to CRMCAT and Jukka Niiranen for all the help and feedback so far.

Have Fun!

@ScottDurow

Building Rich UI Extensions for Dynamics CRM using SparkleXRM


Building a Refreshed Connection UI using SparkleXRM

$
0
0

I've now published all 5 videos in this series. 

If you find yourself needing to create HTML Webresource in Dynamics CRM then I'm sure you will find something here of interest. Part 5 shows how to use the new SparkleXRM Metadata Server that I'll be publishing a post on very soon.

Part 1 - Creating your first SpakleXRM Project

Part 2 - View Model Form Data Binding & Unit Testing

Part 3 - Adding an Editable Grid

Part 4 - Pulling it all together

Part 5 - Adding multi-language support

Have fun!

@ScottDurow

Introducing SparkleXRM Metadata Server

$
0
0

Metadata Server Logo

Speed up your HTML web resources by caching metadata such as localised field labels and options sets.

If you've developed an HTML web resource for Dynamics CRM that includes field labels, option sets or any other element that is stored in the Dynamics CRM metadata then you’ll know about the delay each time the your UI is rendered whilst information is downloaded from the server. You could of course hard code this information in your JavaScript but you'd suffer higher maintenance costs especially when supporting multiple languages.

The SparkleXRM Metadata Server allows dynamic expressions to be included in JavaScript Web Resources that are evaluated on the server and then cached on the client side.

/*metadata
var fieldLabel = <@contact.fullname.DisplayName@>;
metadata*/

This will output and cache the following on the client (for an LCID of 1033):

var fieldLabel = "Full Name";

Learn more about the SparkleXRM Metadata Server!

Turbo Forms: Get your JavaScript ready for CRM2015 Update 1

$
0
0

With the introduction of 'Turbo Forms' in CRM2015 Update 1 I thought I'd give you a heads up on what you'll need to address in your JavaScript to support this new form rendering engine. The Dynamics CRM Team Blog has a very good article on the changes but there have been some misunderstandings of the statement 'we have parallelized as many operations as possible'. In actual fact the script loading of custom web resource scripts has not really changed since CRM2013 - It remains the same as I describe in my CRM2013 Script Loading Deep Dive. The operations that are parallelized with turbo form rendering are the internal form load operations rather than custom ones. Custom JavaScript loading has always been parallelized since CRM2013.

Turbo Form Page Differences

Before Turbo Forms both global scripts and your custom web resources would have been loaded into a content IFRAME within the main window each time you navigate between records.

The biggest change with Turbo Forms is that the content IFRAME is kept in memory and re-used for performance reasons. This parent content IFRAME is even kept in memory between navigation between different entity types. Any custom JavaScript is then loaded into a child IFRAME (via the ClientApiWrapper.aspx page) so that when you navigate between records they are re-loaded.

SparkleXRM solutions already have a load order mechanism that ensure that your custom scripts are loaded in order that they are needed.

Impact on unsupported code

If your JavaScript is using only supported SDK calls then there will be no impact from this new loading mechanism. Sometimes it is necessary to use unsupported functions or referencing parameters of the Content IFRAME (such as the query string). Since your custom JavaScript is now running in the context of the ClientApiWrapper (rather than the content IFRAME) any reference to window methods such as window.openStdWin or window.location.href will fail. In order to access these objects you will need to reference parent.window.

The ribbon workbench 'how-to' article that shows starting a dialog from a ribbon button does infact use openStdWin to ensure consistency with the out of the box dialog experience. I have updated the code to use the parent window when required.

Footnotes

There are a couple of other notable aspects of Turbo Forms that I thought I'd point out:

IFRAMES that are collapsed by default are not sized correctly.

If you have an IFRAME or HTML Webresource inside a Tab that is collaposed by default you will find that they are not sized correctly when the tab is expanded. This will be fixed in an upcoming update but until then you will need to show the tab by default and collapse when the form is loaded.

entityType vs typename

Turbo Forms have dropped the typename attribute of Lookup values.

In the past, the following code would return the same:

Xrm.Page.getAttribute("parentcustomerid").getValue()[0].typename
Xrm.Page.getAttribute("parentcustomerid").getValue()[0].entityType

With Turbo Forms only the documented entityType is available. The typename attribute was left over from the CRM4 days and just had not been removed until now!

@ScottDurow

 

Refreshed Connections UI Solution

$
0
0

Following on from my recent video series called 'Building a Refreshed Connections UI using SpakleXRM' this post shows you how to use the final solution to add a bit of sparkle to the otherwise slightly neglected connections feature in Dynamics CRM.

Connections first saw an appearance in CRM2011 replacing the customer relationships entity. The big deal about connections was that they could connect any entity to any other entity using the same lookup field and could be viewed from either side of the relationship. This made them very powerful for creating complex relationships between contacts, contacts, cases and opportunities such that the relationships would be equally discoverable from both sides.

  • Two sided - If you add a link between John Smith and Contoso you can see that connection on the Contoso record as well as the John Smith record. Behind the scenes this is achieved by two connections being created with the duplicate having the Connect To and Connect From lookups reversed. The connection that you created is marked as the 'Master' and when opening the non-master connection you are actually shown the Master record.
  • Ubiquitous– A common ask from users is to see all relationships in one place. Since connections support multiple types of records you can have a single list that shows connections between many types of record. The Connection Role is used to determine the type of relationship and the Connection Role Category can be used to group the types of roles together (e.g. Business, Sales Team, Stakeholders etc.)
  • Customisable Intersect Entity – There are existing attributes such as Effective Start and Effective End that are useful when modelling relationships that change over time but you can also add you own fields. The downside of this of course is that those fields will be visible for all connections irrespective of the entities types that are being connected unless you do some hiding/showing dynamically.

I've always loved the connection entity but it hasn't received the 'Refreshed UI' treatment in recent releases which is why I created the Refreshed Connection UI Solution.

Current Experience (CRM2015)

This is what the current experience is on the Opportunity Form:

So, far so good, but if you add a custom connections sub grid you get the following. Perhaps the biggest drawback is that the Add New (+) button cannot be used on the form connections sub-grid.

If you then use the associated sub grid you get the following. Adding connections uses the old style Ribbon Form.

New Experience

Using the new SparkleXRM Refreshed Connections UI this is the experience you'll get:

Installing:

The code is all in github if you want to take look - otherwise you can install the managed solution. 

Configuring:

  1. You can add the component to any form that supports connections by using 'Add Web Resource'

  2. Select the Web Resource named con_/html/Connections.htm
  3. Within the Web Resources properties dialog on the Formatting tab set the height in Rows and un-check 'Display Boarder' and change the Scrolling to 'Never'
  4. Again, within the Web Resource properties dialog on the General tab you can pass the following properties:
    1. entities– (Optional) A Comma separated list of entity logical names that you want to support in the lookup. By default the entities will be account,contact,opportunity,systemuser
    2. pageSize– (Optional) The number of rows to display per page. By default a size of 10 will be used.
    3. view– (Optional) The name of the view to use (e.g. All Stakeholders). By default the Associated view will be used

The usual disclaimer & license applies.

Refreshed UI Solution Links Updated

Viewing all 296 articles
Browse latest View live