Sunday, June 7, 2015

Keys and Dynamics CRM 2015 Online - Update 1

Recently, Microsoft released Update 1 to its online subscribers.  This included a number of features and enhancements, and one I wanted to talk about today is Keys. I read through the release notes and somehow completely missed this feature as did many people in the CRM world that I work with, so I figured that making a post on the topic wasn't a bad thing.

So, keys.  When I first heard the term I thought back to my database days and thought that it would be related to records being unique...but CRM already had the duplicate detection engine so in my mind it had to be something else.  After some digging and playing with the new feature though, CRM Keys exactly what I thought:  Unique values on an entity.  It can be based off one attribute or multiple (compound keys for the SQL readers out there).  So if you start using Keys, you don't have to re-implement that plugin enforcing unique-SSNs requirement for the 10th time.  So how do we do this?

First, open up a solution or click on Customize the System.  Pick an entity in your solution, and you'll see a new Keys option:


Inside that setting, you create a new key, give it a unique name like you were creating a new attribute, then select which attributes on the entity should be involved in the Key [I removed my entity prefix from the screenshots]:


And that's all there is to it!  Test it out, and you'll get a message similar to the following when you try to create a duplicate record according to the new Key you just created...and you'll notice that it's blazing fast:



But the question remains, how does this work?  What's it actually doing?  Well, if you try to create a key that would be invalid (say, unique on first name), you get a message similar to the following:


As you can see, the name of the actual database key that the system is trying to create is displayed...ndx_for_entitykey_<keyname>.  So at the end of the day, these new CRM Keys really are just database keys/indexes.

One thing I'd like to get around to playing with is are these actual keys?  Can these be utilized not only for making sure records are unique, but also to create database keys on tables to speed up processing?  Hopefully I'll get to that in the future, and when I do I'll post my findings here.

Happy keying!

Thursday, January 1, 2015

Microsoft Unified Service Desk (USD) Configuration 102 - Non-Entity Information

In this next installment of an introduction to configuring USD, I'll go through some elements that aren't entities used in configuring but are still critical in knowing how to use USD to it's fullest. Below are the items that'll be discussed:
  • Sessions
  • Replacement Parameters
  • Debugger

Sessions


One of the benefits of USD is that you can have multiple groups of hosted controls open at the same time.  A session is a set of forms and data related to one thing; that 'thing' usually being an account or a contact.  For instance, in a call center environment you can have someone in multiple chat sessions at the same time.  Each session in USD would be related to one of those chat sessions.  This way you can have multiple customers' forms open at the same time, and also any CRM forms related to those specific callers without losing track of what's related to what.

Replacement Parameters


Replacement parameters are used to store session-based information within USD hosted controls. In programming terms, think of them as key/value pairs.  These are used to pass information between CRM, any systems that you're integrating with USD, and any custom USD components.  These values can be based off a specific session or be global.

For example, you can make a custom search page that'll search an external system and return social security numbers.  From there you can search CRM for that social security number and start a session around that specific customer.  To pass that information around, you would use replacement parameters.  Additionally, once the CRM form for that customer is open, you can also pass information from the search page into that form using replacement parameters (use the Event of BrowserDocumentComplete to call an Action that will read Replacement Parameters).

To access replacement parameters use two leading brackets and 2 trailing brackets in your code.  For instance, [[Contact.FullName]] would be replaced in USD by a contact's full name.When accessing replacement parameters, there are many trailing parameters that can be used.  Some of the more common ones (from my experience) are:
  • g - Global.  If a value is global, put a 'g' after the first ending bracket.  Ex:  [[SearchControl.SSN]g].
  • + - Optional.  If the value doesn't exist, the action calls and other code will continue running.  Otherwise the action call will fail and any sub action calls won't be fired (that this could be a desired result!).  For instance, [[SearchControl.MiddleName]+].
  • u - URL Encoding will be used when retrieving the value.  so a space would be replaced by "%20".
Additional Replacement Parameter parameters can be found on the MSDN site:

http://msdn.microsoft.com/en-us/library/dn864934.aspx

Debugger


The debugger is used to see what's going on in the background of USD, and also manually fire off action calls and events for testing.  There are many pages to the debugger, depending on what you're looking for.  Personally, I use it for three main purposes:
  • Viewing Replacement Parameters
  • Viewing Events/Action Calls
  • Manually firing off events and action calls.
To view the replacement parameters, click on the Data Parameters tab in the debugger:


Here you can see all the replacement parameters for the given session and also any global values. Additionally you can use the Copy button on top to get the code needed to access that specific replacement parameter through code or USD configuration.

The action calls that have been run since USD has started up will be displayed under Action Calls.


As you can see above, everything has been captured from events firing, hosted controls closing, even the opening of the debugger itself (the last line above).

Additionally, you can manually run actions:


First, click on the dropdown arrow to bring up additional debugging options.  Once you do that, you'll see the 2nd image above where you can pick a specific action call to fire.  If you'd like to enter data into a specific action, click on Direct Action and from there you can pick which Hosted Control you want to use along with which specific Action you want to fire.  You can also use this to 'hide' certain actions from users and use them for debugging and development purposes.  For instance you might be pulling information from another application but never actually visually display that system.  If you're suddenly having troubles pulling data from that system, you can add an action to make that application visible.

Tips and Tricks (And hidden, lesser known items)


USD can be cached.  Every time USD opens, many many records are pulled down from CRM.  You can set USD to use caching though though Options (Options will be gone over in my next post).  If you have an Option named 'ClientCacheVersionNumber', the USD client will NOT pull all information from the server every time a user connects.  The only time all USD data will be retrieved is when this Option's value changes, making the locally stored value different from what's on the server.  If this option doesn't exist, the client will always pull all USD information down from the server.  I recommend that once you're in production you use this option, and even in your development environment to speed up actual development.

If you want to turn off the caching mentioned above for specific users (say you as a programmer always want the values loaded from CRM and not cached), you can do that through User Settings.  They'll be covered in the next section, but they're basically options on a per-user basis.  That said, just create a new User Setting record, set the Name to DisableCaching, the User field to the individual that you don't want to use caching, and you're all set.

You can fire USD Events from within CRM itself.  Let's say you have multiple values stored in replacement parameters and want to use different ones based off dropdown values changing on a CRM form.  If you want to pull specific values, you'd have to use an Action Call, but how do you fire them?  You'd first have to make a custom hosted control to accept a DoAction call (I'll cover this in a future post, but for now you can check out the MSDN site on making a custom control).  Then, you can call the DoAction event from a specific hosted control by calling http://uii/<hostedcontrol>/<actioncallname>.  USD will intercept that call and instead of displaying a webpage, it'll fire a DoAction event and pass in the given action.

------------

Next time I'll finish what the first USD post started by finishing the review of the entities that are involved in configuring USD.

Microsoft Unified Service Desk (USD) Configuration 103 - Other Entities

In our 3rd post around configuring CRM, I'll go over the rest of the entities involved in setting up and using USD.  This post will be a great starting point and also a good reference point to definitions of some USD records that aren't used very often, but are important nonetheless.  Personally, I'd bookmark it and come back to it to remind yourself what each thing is used for and what configurations are possible out of the box.  Before actually getting your hands dirty with USD, a lot of these entities might seem pointless/useless, but that'll change over time as you get used to USD and want to configure more advanced features.  That said, let's get to work.

The entities we'll cover today:
  • Option
  • Window Navigation Rule
  • Entity Search
  • Entity Type
  • Configuration
  • User Setting
  • Scriptlet
  • Additional Agent Script Components


Additional USD Entities


Options - Key/value pairs for use with USD.  Say you have use a web service to pull some data from another internal application that you're hosting.  In your development environment, you connect to the development web service.  In production, you connect to the production web service.  You can easily change where your USD components connect to using Options.

Window Navigation Rules - Determine how USD will handle moving from one hosted control to another.  For instance if you're on an Account page and click on a phone call record in a sub-grid, what happens?  Does the phone call record open up right in that tab?  Does a new tab open?  Does the phone call record open outside USD?  This is determined by the Window Navigation Rules.

Entity Searches - Contain FetchXML and are used in conjunction with Window Navigation Rules. You can use replacement parameters here (we'll get to those shortly) so the Fetch is dynamic.

Entity Types - Used as a link between USD records and entities themselves.  A lot of records, Entity Searches for instance, need to be related to an entity.  Entity Types are these relationships.

Configurations - Used to limit what USD records are loaded per user.  This way you can easily have the USD application look and work differently for different user roles.  For instance, you can create a toolbar button to email a contact.  Maybe you only want that button visible to supervisors.  This way you can create the appropriate USD records and only have them visible to supervisors by assigning a Supervisor configuration, while users on the phones would have a Basic User configuration.

User Settings - Used to set values on a per-user basis.  They're just like Options, but are set per user.

Scriptlets - JavaScript to be used in a USD setting.  Their main use over the usual CRM web resources is to utilize replacement parameters.

Agent Script Components


In my first USD Entity post I touched on agent scripting, here I'll go into more details.  When you create agent scripting, it's separated into three components:

  • Agent Script Task
  • Agent Script Category
  • Agent Script Answer

Agent Script Task - These were talked about in the last post; they're the main steps in the scripts. Each script contains two pieces: Text and Instructions.  The Text contains what the user should say to the person they're interacting with and the instructions are notes to the user stating what they should do.  To go from one task to another, Agent Script Answers are used.

Agent Script Answers - These are answers to script tasks that can lead the user down different paths through the application.  For example, in the case of a phone call the user will need to do vastly different things based off why the person on the phone is calling in.  If the caller has an issue with a product, the user can click a button to open a Case.  If they have a quick question that can be answered easily, another answer can bring up FAQs for the user to look through.  If the caller wants to talk to a supervisor, another button can route the call.

Agent Script Task Category - These are used to group Agent Scripts records together to make them easier to find.  It's not actually used by the USD client at all.

Using all of the entities covered in this post and my 101 post, you can create a very rich experience for your USD users.  The out of the box example from Microsoft is just this.  There's no custom code, no DLLs to install, nothing 'special' that was added to make all of that work.  If you do want to create custom components, it's definitely an available option and something I'll be covering in the future.

And as always, if you have any questions about what I've covered today, let me know!

Sunday, December 28, 2014

Microsoft Unified Service Desk (USD) Configuration 101 - Usage and Entities

With a new year right around the corner, I figured I could talk about one of the fairly new areas/products of Microsoft CRM: the Unified Service Desk (USD).  I've had the chance to work with early versions of this product and have been involved in a deployment to a call center, so I have first-hand knowledge and a production deployment under my belt with this offering from Microsoft. Hopefully I can pass some of that knowledge onto others.

From what I've seen online, there's a lot of documentation and articles out there about what USD is and at length about how to use pieces of it, but most of it isn't very user friendly or easy to read.  I'm hoping to change that here.  I won't go into gruesome details about CCA, CCD, UII, and all the technical components in this post; hence the 101 designation in the post title.  I believe that seeing USD in action is the best way to understand how it works, so if you can, I recommend getting a demo site of CRM set up, installing USD, and going back and forth between your installation and this post as you read about the different components.  Of course, you can just read this start to finish and you'll get a lot out of it (I hope), but you'll get more if you get your hands dirty.  Just to give a quick figure, there are literally hundreds of records that are created from this demo data being installed.  You can even use it as a base to install your actual USD implementation.

Before I get into explaining how USD is installed and configured, a quick word on what USD actually is.  USD is a client-hosted application that captures, groups, and displays the web-based Microsoft Dynamics CRM forms.  At the end of the day you're still connecting to your CRM website, but you're doing it through a desktop executable.  Why would you do this?  USD provides many benefits that the website cannot do on its own, listed below:

  • It is easily integrated with Microsoft Dynamics CRM out of the box.  No coding is required to have the desktop application display and interact with CRM.  This makes it a perfect starting point as opposed to making something on your own to integrate with CRM.
  • It can keep track of many sessions at the same time, so users can do work for many different clients at once and their data will stay separated on the screen.
  • It integrates and hosts other applications easily.
  • It provides an easy way to automate processes in both CRM and other systems, as well as transfer information between those systems.
USD does not need to be pigeon-holed into a call center only product, although it does lend itself very well to that use case.  If a CRM implementation also has to interact with many other systems at the same time (ERP, websites, phone software), USD is a great candidate to connect all those applications.  Even if your users are using multiple systems that aren't related to CRM it can be a benefit, as that's where automation comes into play.  For example, say you own a car repair shop.  A caller calls in and says they're still having an issue with their steering alignment.  With one button click in USD, the application can:

  • Bring up customer information
  • Show recent cases in CRM
  • Open a new case for the client
  • Display their open balance in an ERP system
  • Display the manual for that client's car to help a technician fix the problem.


Yes, unlike CRM and most products from the last 5 years, USD is not web-based.  The configuration records are on the web in CRM, but the interpretation of those records is done on the client.  If you don't have any custom integrations, all deployments/updates are done on the server.  If you do have custom components, they're compiled into DLLs and placed in the USD folder.

USD Installation

Installing USD is fairly simple if you follow the installation instructions provided by Microsoft.  I highly recommend installing the demo data that it comes with as it gives you a ton of records to play with.  The installation is in two parts, one for the server and one for the client.  And honestly, using the term 'installation' for both of these pieces is being pretty generous.  The server installation is just a solution install and data import.  The client installation is just copying a bunch of files to a folder of your choosing.  There aren't any files actually 'installed' anywhere else as we know it in terms of most Windows applications.

Configuring USD

Configuring USD is done through creating records within CRM.  When USD loads on a user's machine, after logging in the server will be contacted to retrieve records from the USD entities.  This configuration is made up of 15-20 different entities, many of them obvious but some of them a little more convoluted and harder to understand.  Here I'll go through some of the more basic ones.  Since a call center is touted as the main use of USD, I'll use that as a running theme.

First, what I consider the main four entities:
  • Hosted Controls - The Objects of USD.  If you know Object Oriented Programming, these are the objects.  Most of the 'things' you make in CCD are hosted controls.  This includes your tabs, the debugger, any phone integration visuals, custom components, and even the global manager of USD.  When you have a new USD installation, you need to at least have a Global Manager setup.  Again, though, that's beyond the surface of USD so I'll leave the details of that for another post.
  • Events - Simply, things that happen.  This can be a web page finishing loading, a user clicking a button, or a phone call coming in and being picked up by USD.
  • UII Actions - Reactions to events.  For example, if someone clicks on a button, what happens?  Does a form open?  Is data saved somewhere?  Does a page close?  All of that is handled through actions.
  • Actions - These are the links between Events and UII actions.  You have an Event of clicking a button and a UII Action of saving a record, the Action record would link those two things up so that when the button is clicked, the record is saved.  This way you can use the same action multiple times, maybe you want the record saved automatically if someone closes a form, a button is pressed, or agent scripting is used (more on agent scripting later).



So you have the items, what they do, and how they respond.  You can have multiple actions fire off of one event happening, such as saving a record and then closing a tab.  You can even have actions fire off of other actions.  So if you want to retrieve information and then use that information after it's retrieved, you can set that up.  If you fired both events in parallel there's a chance the data won't be available and the call would fail.

Tying this to the call center example, your hosted control would be a CRM form, say, a Contact entity.  Your Event could be clicking on an activities button to bring up activities related to that contact, the Action would be to show a grid of activities, which would call the UII Action to do the actual display.  Note that the Activities button I mentioned is a USD button, not the one within CRM.  This is  done through toolbars, explained below.

Visual Components:
  • Toolbars - These should look familiar, they're like any toolbar in windows applications.  They're used to hold toolbar buttons which are used to fire events.
  • Toolbar Buttons - As mentioned above, these are used to fire events in USD.  Some common uses are to open Advanced Find in CRM, link to external websites, or open the debugger.
  • Agent Scripting - Helps guide users though the use of USD and CRM.  It gives a script for users to say to users if they're communicating with someone, and they can also help drive business logic through options.  For example, if someone is calling in to order a product vs. asking for support, you would have totally different things happen in terms of what records are created or screens are shown.
  • Session Lines - A 'mini area' of USD, usually used to show often-used information about a customer in the current session (more on sessions later).  If you're in a financial implementation, you can place company name, market cap, stock ticker here.  If you're selling homes, you can place the price of the home and number of bedrooms/bathrooms that a client is interested in.



To keep with the theme of a healthcare call center, in the top left of your USD screen would be a client's name, coverage plan, maybe their deductibles.  That's the session line for this client.  Above your phone call form would be a toolbar and on that toolbar you can have multiple toolbar buttons:  Save, close, create email, open a URL to an external website...anything you'd like.  If on this phone call the user is to determine why the user is calling (maybe they're asking about eligibility, maybe they're asking about changing their primary care physician), you would use Agent Scripting.  The script could contain language telling the user to ask why the caller is calling and then give options.  If a user clicks on one of those options, say the caller is calling about an existing issue, USD can open up a grid showing all service requests that are related to that customer, and possibly open a help website or if there is only one open service request, open that record.

I'll pause here as that should be enough to digest for one post.  I mentioned this at the start but it's that important:  Install the data that comes with the USD download and use it.  Explore it.  Play with it.  Everyone that I've worked with that knows this product, myself included, has learned and gotten used to the product from using it and not just reading about it.

Next time I'll go through more of the entities and components of USD:  Options, Window Navigation rules, Scriptlets, the debuggger, what Replacement Parameters are, and some of the smaller entities involved in configuring USD.  I'll also have future posts on making a custom hosted control and integrating with other applications.

As I'm sure you can tell, this is just scratching the surface of USD. If you have any specific questions or topics you'd like me to go over, feel free to send me an email!

Sunday, November 2, 2014

CRM 2013 SP1 - Debugging Custom Workflows

The topic for today is creating and then debugging a custom workflow for CRM 2013 SP1.  Sounds simple enough, right?  If you have done this type of work in the past it should be straightforward:  You make your DLL though Visual Studio, you deploy your DLL, profile the workflow, make a debug-text file from the profiling, then use the plugin registration tool and debug.

There are a few problems though, given the current (6.1.1) SDK.

First, if you're adding plugins through the plugin registration tool don't use version 6.1.1.  If you do, the tool won't detect your workflows so you can't register them nor debug them (if you managed to get your steps in using Visual Studio).  Find a version of 6.1.0 online and use that instead.

Version 6.1:



Version 6.1.1:



Once you've gotten past that hurdle, you can add the step to your solution and start profiling.  You'll hit another snag later though:

Version 6.1.1:



The profiler won't/can't find which plugin/workflow step you're trying to debug.

Version 6.1.0:



And the details of the error:

"Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Could not load file or assembly 'Microsoft.Xrm.Sdk.Workflow, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified."


This error basically means that the plugin profiler can't find the Microsoft.Xrm.Sdk.Workflow file.  Personally, I've tried placing the file in the location of my DLL, of the plugin registration tool, even where the debug file is (and even putting everything in the plugin registration folder).  To get around this, add the file to your local GAC (global assembly cache).   In case you're not familiar with this, perform the following steps:

  • Open a command prompt as an administrator.
  • Browse to where gacutil.exe file is on your machine.
    • For Windows 7 on my machine, this is at C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools.
  • Register the Workflow DLL that was used to make your workflow step (which should also be the same DLL in the plugin registration folder.
    • Type gacutil.exe -i <location of the file>


And then you should be all set to debug!  Remember to use version 6.1.0 of the plugin registration tool from the SDK, or hopefully 6.1.2 / 6.2 when they come out.

Monday, September 29, 2014

Bulk Delete From Multiple Entities With 1 Button

Over the years I've had the need to delete all records from multiple entities, such as when testing out data migrations or working with the new Unified Service Desktop.  You can use advanced find to find/delete the records or go into the bulk deletion module but then you have to do each entity one at a time and it can quickly become a hassle.

I created a very simple JavaScript web resource to fix this issue which has saved me a lot of time over the last few months as there are a lot of entities that USD works with.  All you need to do is create a web resource that makes a bulk-delete call and call that function with a button in the ribbon (for CRM 2011 that is).

Here's what the button looks like and the popup that's shown, plus the resulting bulk deletion job that's created:




Here are screenshots of the Ribbon Editor tool with the pieces needed to add this customization.  The screenshots below show the setup of this button used to delete data.

First the position of the button and the text:


Here are the button properties:



The command pointing to a JavaScript web resource:




And the rules indicating that the button should only be on the customization and solution pages:

 



Here is the JavaScript called by the button.  You can update the JobName and alert message at the end to be whatever you'd like.

-------------

function getServerUrl()
{
    try
    {
        var ServicePath = "/XRMServices/2011/Organization.svc/web";
        var serverUrl = "";
        if (typeof GetGlobalContext == "function")
        {
            var context = GetGlobalContext();
            serverUrl = context.getClientUrl();
        }
        else
        {
            if (typeof Xrm.Page.context == "object")
            {
                serverUrl = Xrm.Page.context.getClientUrl();
            }
            else
            {
                throw new Error("Unable to access the server URL");
            }
        }
        if (serverUrl.match(/\/$/))
        {
            serverUrl = serverUrl.substring(0, serverUrl.length - 1);
        }
        return serverUrl + ServicePath;
    }
    catch (e)
    {
    }
}

function RemoveData()
{
    try
    {
        var requestMain = ""
        requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
        requestMain += "  <s:Body>";
        requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
        requestMain += "      <request i:type=\"b:BulkDeleteRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
        requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>QuerySet</c:key>";
        requestMain += "            <c:value i:type=\"a:ArrayOfQueryExpression\">";
        requestMain += "              <a:QueryExpression>";
        requestMain += "                <a:EntityName>[Entity1]</a:EntityName>";
        requestMain += "              </a:QueryExpression>";
        requestMain += "              <a:QueryExpression>";
        requestMain += "                <a:EntityName>[Entity2]</a:EntityName>";
        requestMain += "              </a:QueryExpression>";
...
...
        requestMain += "            </c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>JobName</c:key>";
        requestMain += "            <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">Data Bulk Delete</c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>SendEmailNotification</c:key>";
        requestMain += "            <c:value i:type=\"d:boolean\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">false</c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>ToRecipients</c:key>";
        requestMain += "            <c:value i:type=\"d:ArrayOfguid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>CCRecipients</c:key>";
        requestMain += "            <c:value i:type=\"d:ArrayOfguid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>RecurrencePattern</c:key>";
        requestMain += "            <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\" />";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "          <a:KeyValuePairOfstringanyType>";
        requestMain += "            <c:key>StartDateTime</c:key>";
        requestMain += "            <c:value i:type=\"d:dateTime\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">0001-01-01T00:00:00</c:value>";
        requestMain += "          </a:KeyValuePairOfstringanyType>";
        requestMain += "        </a:Parameters>";
        requestMain += "        <a:RequestId i:nil=\"true\" />";
        requestMain += "        <a:RequestName>BulkDelete</a:RequestName>";
        requestMain += "      </request>";
        requestMain += "    </Execute>";
        requestMain += "  </s:Body>";
        requestMain += "</s:Envelope>";
        var req = new XMLHttpRequest();
        req.open("POST", getServerUrl(), true)
        req.setRequestHeader("Accept", "application/xml, text/xml, */*");
        req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
        req.send(requestMain);
        alert("Delete command received.  Data has been queued to be deleted and should start processing shortly.");
    }
    catch (e)
    {
    }
}

----------

And finally, if you can't use the ribbon editor tool and have to rely on old-fashioned XML editing, see below:

Note: [Annotations are in bold] and should be removed/replaced in an actual implementation.
Note:  The org that I’m importing to uses a prefix of ‘hac’ so you see it many times below.


<?xml version="1.0" encoding="utf-8"?>
<RibbonDiffXml>
  <CustomActions>
    <CustomAction Id="mcs.hac.ApplicationRibbon.RemoveCCDData.Button.CustomAction" Location="Mscrm.BasicHomeTab.Tools.Controls._children" Sequence="6">
      <CommandUIDefinition>
        <Button Alt="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.Alt" Command="hac.ApplicationRibbon.RemoveCCDData.Command" Description="Main Text of the Button" Id="hac.ApplicationRibbon.RemoveCCDData.Button" Image32by32="$webresource:hac_/shared/images/recycleBin.png" [This is a custom image I added as a web resource] LabelText="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.LabelText" Sequence="6" TemplateAlias="o1" ToolTipTitle="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipTitle" ToolTipDescription="$LocLabels:hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipDescription" />
      </CommandUIDefinition>
    </CustomAction>
  </CustomActions>
  <Templates>
    <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
  </Templates>
  <CommandDefinitions>
    <CommandDefinition Id="hac.ApplicationRibbon.RemoveCCDData.Command">
      <EnableRules />
      <DisplayRules>
        <DisplayRule Id="hac.ApplicationRibbon.OnlyInSolutionArea.DisplayRule" /> [This is where I’m setting the button to be shown only when looking at solutions, see the DisplayRule nodes below]
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="RemoveCCDData" Library="$webresource:hac_/shared/RemoveCCDData.js" /> [This is the web resource with the code to execute]
      </Actions>
    </CommandDefinition>
  </CommandDefinitions>
  <RuleDefinitions>
    <TabDisplayRules />
    <DisplayRules> [If you want to have this button shown on other pages, indicate that here]
      <DisplayRule Id="hac.ApplicationRibbon.OnlyInSolutionArea.DisplayRule">
        <OrRule>
          <Or>
            <PageRule Address="/tools/Solution/home_solution.aspx" />
          </Or>
          <Or>
            <PageRule Address="/tools/systemcustomization/systemcustomization.aspx" />
          </Or>
        </OrRule>
      </DisplayRule>
    </DisplayRules>
    <EnableRules />
  </RuleDefinitions>
  <LocLabels>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.Alt">
      <Titles>
        <Title description="Bolded Title of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.LabelText">
      <Titles>
        <Title description="Sub Text of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipDescription">
      <Titles>
        <Title description="Tooltip Description of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
    <LocLabel Id="hac.ApplicationRibbon.RemoveCCDData.Button.ToolTipTitle">
      <Titles>
        <Title description="Tooltip Title of the Button" languagecode="1033" />
      </Titles>
    </LocLabel>
  </LocLabels>

</RibbonDiffXml>

-------------

Hope this helps save you some time!

Wednesday, April 2, 2014

CRM 2011 - Setting Privacy / Don't Send to Microsoft

When deploying a new CRM implementation, most of the time clients want the default error reporting turned off.  The error message that's displayed to the user is generic and not helpful, and usually the error has nothing to do with Microsoft so the 'Send to Microsoft' button isn't very helpful.

What I've seen done is either one of two things:

A - An Admin will go into the database and write some scripts to automatically set every user's 'choice' to 'Never send an error report to Microsoft.'  This requires direct database access so isn't an option at all for Online, plus it's not a supported modification.

B - Instruct users to go to their options and set this value.  This obviously isn't the best option; Users don't like having to do setup of a system, and some just won't do it.

But then there's the easy way to do this, which most people don't know about since it's tucked away in an area that most people never look:  Privacy Settings.




Once there, click on the 'Error Reporting' tab and you can instantly update the setting for all users (and any future users that are added).



I hope this saves some people deployment time and headaches.  Just remember to turn this off if you actually need to view error logs on a page!