Monday, March 19, 2012

Unable to reassign some CRM records after upgrade to 2011

I started having a weird issue where records for some CRM entities could not be reassigned to other users (behavior in CRM 4.0), I was able to only reassign the records to users (shown in the screen shot below – notice “Look for” is defaulted to “Team” and its value cannot be changed.)

image

What was odd was that this behavior was not consistent across all entities. It was occurring on the Account, Contact, Opportunity entities. But it was not happening on Lead and some other entities.

When I exported the Lead entity and compared it with the Account entity (compared the customizations.xml file), one thing that jumped out at me was that there was an additional LookupType that was set for the Lead entity:

image image
Lead customization.xml file Account customization.xml

So I copied the LookupTypes from the lead file to the account customization file and voila! it worked.

Some other things that you need to do to complete the fix:

1. as shown above add the LookupType 8 to the LookUpTypes collection under “EntityInfo\Entity\Attributes\Attribute” node.
As I was working with the Account entity, in your XML look for EntityInfo with Name = “Account” and the nested Attribute with PhysicalName = “ownerid”.

             <LookupTypes>
                <LookupType id="00000000-0000-0000-0000-000000000000">8</LookupType>
                <LookupType id="00000000-0000-0000-0000-000000000000">9</LookupType>
             </LookupTypes>

2. Next under “EntityRelationships” for an “EntityRelationship” node that references AttributeName “OwnerId” (this will be “owner_accounts” for the owner entity). You need to update the “Lookuptypes” attribute to support both 8 and 9 values, as shown below:

<field name="ownerid" requiredlevel="systemrequired" imemode="auto" lookupstyle="single" lookupbrowse="0" lookuptypes="8, 9">

image

Import the customization file and you should now be able to select Team or Users.

Note:

If you want to restrict the lookup to only users (and not users and teams), just replace the value of “9” to a value of “8” at both the “Attribute” level and the "EntityRelationship” level.

Saturday, March 17, 2012

CRM 2011 Ribbon: Disabling/Enabling elements based on form fields

If you need to enable disable a ribbon control based on the value within a form field, you need to use the “ValueRule

Gotcha: Don’t use the default value element of the “ValueRule” as that seems to interfere with the null value check.

Here is a very simple example:

1. First create the enable rule:

<EnableRules>
      <EnableRule Id="Mscrm.Isv.opportunity.Form.Actions.MyButton.EnableRule">

        <ValueRule Field="fieldNameOnOpportunityPage" Value="null"
        InvertResult="false" />
      </EnableRule>
    </EnableRules>

2. Next reference the enable rule via its ID in your command definition:

<CommandDefinition Id="Mscrm.Isv.opportunity.Form.Actions.MyCommand">
      <EnableRules>
        <EnableRule Id="Mscrm.Isv.opportunity.Form.Actions.CreateNewProposal.EnableRule" />
      </EnableRules>
      <DisplayRules/>
      <Actions/>
    </CommandDefinition>

3. And in your button, you reference the command definition, by its ID

<Button Id="Mscrm.Isv.opportunity.Form.Actions.MyButton"
            Command="Mscrm.Isv.opportunity.Form.Actions.MyCommand"
            LabelText="$LocLabels:Mscrm.Isv.opportunity.Form.Actions.MyCommand.LocLabel"
            ToolTipTitle="$LocLabels:Mscrm.Isv.opportunity.Form.Actions.MyCommand.LocLabel"
            ToolTipDescription="$LocLabels:Mscrm.Isv.opportunity.Form.Actions.MyCommand.ToolTip.LocLabel"
            Image16by16="/_imgs/ico_18_acct.gif"
            Image32by32="/_imgs/ico_18_acct.gif"
            Image16by16Class="ms-crm-Upgraded-Ribbon-Image16"
            Image32by32Class="ms-crm-Upgraded-Ribbon-Image32"
            Sequence="10" TemplateAlias="o1" />

4. Finally, add the LocLabels for your button and you are done:

<LocLabels>
    <LocLabel Id="Mscrm.Isv.opportunity.Form.Actions.MyCommand.LocLabel">
      <Titles>
        <Title languagecode="1033"
        description="Create NCM Proposal" />
      </Titles>
    </LocLabel>
    <LocLabel Id="Mscrm.Isv.opportunity.Form.Actions.MyCommand.ToolTip.LocLabel">
      <Titles>
        <Title languagecode="1033"
        description="Create a New Proposal" />
      </Titles>
    </LocLabel>
</LocLabels>

 

Here is the complete example:

The demo also shows you how to create a custom group and add it to the “Main Tab” of the “Opportunity” entity. The button that I add “MyDemoButton” is added to the custom group “Demo Group”.

<RibbonDiffXml>
  <CustomActions>
    <!--Raj-->
    <CustomAction Id="Mscrm.Isv.opportunity.Form.Group.Demo.MaxSize.CustomAction"
    Location="Mscrm.Form.opportunity.MainTab.Scaling._children"
    Sequence="150">
      <CommandUIDefinition>
        <MaxSize Id="Mscrm.Isv.opportunity.Form.Group.Demo.MaxSize"
        GroupId="Mscrm.Isv.opportunity.Form.Group.Demo.Group"
        Sequence="21" Size="LargeLarge" />
      </CommandUIDefinition>
    </CustomAction>
    <CustomAction Id="Mscrm.Isv.opportunity.Form.Group.Demo.CustomAction"
    Location="Mscrm.Form.opportunity.MainTab.Groups._children"
    Sequence="115">
      <CommandUIDefinition>
        <Group Id="Mscrm.Isv.opportunity.Form.Group.Demo.Group"
        Command="Mscrm.Isv.opportunity.Form.Group.Demo.Command"
        Title="$LocLabels:Mscrm.Isv.opportunity.Form.Group.Demo.Title"
        Sequence="110" Template="Mscrm.Templates.Flexible2">
          <Controls Id="Mscrm.Isv.opportunity.Form.Group.Demo.Controls">
            <Button Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton"
            Command="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton"
            LabelText="$LocLabels:Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.LocLabel"
            ToolTipTitle="$LocLabels:Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.LocLabel"
            ToolTipDescription="$LocLabels:Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.ToolTip.LocLabel"
            Image16by16="/_imgs/ico_18_acct.gif"
            Image32by32="/_imgs/ico_18_acct.gif"
            Image16by16Class="ms-crm-Upgraded-Ribbon-Image16"
            Image32by32Class="ms-crm-Upgraded-Ribbon-Image32"
            Sequence="10" TemplateAlias="o1" />
          </Controls>
        </Group>
      </CommandUIDefinition>
    </CustomAction>
  </CustomActions>
  <Templates>
    <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
  </Templates>
  <CommandDefinitions>
    <CommandDefinition Id="Mscrm.Isv.opportunity.Form.Group.Demo.Command">

      <EnableRules />
      <DisplayRules />
      <Actions />
    </CommandDefinition>
    <CommandDefinition Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton">
      <EnableRules>
        <EnableRule Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.EnableRule" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton" />
      </DisplayRules>
      <Actions>
        <Url Address="
http://www.google.com" />
      </Actions>
    </CommandDefinition>
  </CommandDefinitions>
  <RuleDefinitions>
    <TabDisplayRules></TabDisplayRules>
    <DisplayRules>
      <DisplayRule Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton">
        <CrmOfflineAccessStateRule State="Offline"
        InvertResult="true" />
      </DisplayRule>
    </DisplayRules>
    <EnableRules>
      <EnableRule Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.EnableRule">
        <ValueRule Field="Demo_fieldOnFormId" Value="null"
        InvertResult="false" /> <!--replace demo_fieldOnFormId with an actual id-->
      </EnableRule>
    </EnableRules>
  </RuleDefinitions>
  <LocLabels>
    <LocLabel Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.LocLabel">
      <Titles>
        <Title languagecode="1033"
        description="This is a demo command" />
      </Titles>
    </LocLabel>
    <LocLabel Id="Mscrm.Isv.opportunity.Form.Actions.MyDemoButton.ToolTip.LocLabel">
      <Titles>
        <Title languagecode="1033"
        description="This is a demo command" />
      </Titles>
    </LocLabel>
    <LocLabel Id="Mscrm.Isv.opportunity.Form.Group.Demo.Title">
      <Titles>
        <Title languagecode="1033" description="Demo Group" />
      </Titles>
    </LocLabel>
  </LocLabels>
</RibbonDiffXml>

Friday, March 16, 2012

Editing CRM 2011 “Customizations.Xml” in VisualStudio

AKA “Enabling intellisense in Visual Studio for editing the Customizations.xml file”

Download the CRM 2011 SDK. In the SDK you will find a folder called “Schemas”.

image

Open the “customizations.xml” from the solutions package that you exported out of CRM in Visual Studio. Press the “F4” button (or go to the “Properties” window). Click in Schemas:

image

Add the “customizationssolution.xsd” schema, and after that you should have intellisense!

image image

Note: in case schema intellisense does not work for you, try adding the following schemas in addition to “customizationsSolution.xsd” file:

fetch.xsd
formxml.xsd
isv.config.xsd
ribboncore.xsd
ribbontypes.xsd
ribbonwss.xsd
sitemaptype.xsd

Wednesday, March 14, 2012

CRM 2011 Ribbon Customization

Siva has written almost 12 different posts on different aspects of CRM 2011 ribbon customization. As I could not find all the links in one place, here is the list:

Ribbon Customization Part 1

Ribbon customization Part 1 – add a custom tab

Ribbon customization Part 2 – understanding the ribbon customization

Ribbon customization Part 3 – Add a custom tab to a specific entity

Ribbon customization Part 4 – Add a custom tab, custom group and custom group and custom buttons to a grid ribbon for a specific entity

Ribbon customization Part 5 – Add a custom tab, custom group and custom buttons to a form ribbon, grid ribbon for a specific entity

Ribbon customization Part 6 – Add a custom group and custom buttons to existing tab for a specific entity

Ribbon customization Part 7 – Hiding a Ribbon button

Ribbon customization Part 8 – Hiding a group of ribbon buttons

Ribbon customization Part 9 – Adding a button to existing group for custom entity’s ribbon

Ribbon customization Part 10 – Hide “Add Existing” button

Ribbon customization Part 11 – Enable, Disable ribbon button based on security role

Ribbon customization Part 12: Custom ribbon tab in CRM is always selected

Siva’s index for Ribbon customization is available here, but its not upto date: http://dynamicscrm2011.wordpress.com/2011/05/05/dynamics-crm-2011-ribbon-customization-index/

Tuesday, March 13, 2012

Unable to run website that uses Microsoft.Crm.Sdk.dll after CRM 2011 upgrade

I was getting the following error when loading a website that used the Microsoft.Crm.Sdk dll (CRM 2011). The error started occurring right after I installed CRM 2011.

System.IO.FileLoadException: Could not load file or assembly 'Microsoft.Crm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'Microsoft.Crm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' ---> System.IO.FileLoadException: Could not load file or assembly 'Microsoft.Crm.Sdk, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'Microsoft.Crm.Sdk, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

Luckily for me, CRM 2011 Update Rollup 6 was available. Installing this rollup and then restarting the CRM server fixed the issue for me.

If you need a manual fix, please check David Jennaway’s post: http://mscrmuk.blogspot.com/2011/02/using-crm-40-assemblies-on-crm-2011.html

Monday, March 12, 2012

Could not load type “System.ServiceModel.Activation.HttpModule”

After my IIS server got some updates, it looked like I started getting the “Could not load type “System.ServiceModel.Activation.HttpModule” from assembly “System.ServiceModel, Version=3.0.0.0, Culture=neutral,PublickKeyToken=b77a5c561934e089”

image

It looked to me as though I needed to reregister Asp.Net 4.0 dlls. And AspNet_RegIIs is the answer.

Run : aspnet_regiis –iru

Aspnet_Regiis can be found in one of the following locations (based on your OS type)

%windir%\Microsoft.NET\Framework\v4.0.30319
%windir%\Microsoft.NET\Framework64\v4.0.30319 (64-bit machine)

PowerShell and EventLog creation

Powershell provides easy cmdlets that allow you to query event logs, as well as create and delete event log sources. But I could not find a simple cmdLet that allows you to check for the existence of an event log source.

Here is what I came up with:

if ( -not [System.Diagnostics.EventLog]::SourceExists($eventSourceName, $computerName))

        Write-Host "Creating event source $eventSourceName on computer $computerName..."
        New-EventLog -ComputerName $computerName -LogName Application -Source $eventSourceName
        Write-Host "Event source $eventSourceName successfully created on computer $computerName"
}

MSMQ–Security descriptor cannot be set on private queue error

If you get the “Security descriptor cannot be set” error on a private queue, then it may have to do with permissions.

image

Here is how I solved it:

First thing to try:

By changing the ownership of the queue:

Right click on the queue and bring up the properties dialog.

On the “Security” tab, click on “Advanced”

image

Go to the “Owners” tab, select your account and click “Ok”. This should set you up as the owner. Now try and change the settings. If this works, you are done. If not read on:

Manually update security (this is hacky and do so at your own risk):

Private queue information is stored on the server where MSMQ is running in the folder “C:\Windows\System32\msmq\storage\lqs

You will have to open all the files to find the one that stores the information for your queue. The name of the queue is stored in Label field:

image

The next thing you will have to do is create a dummy queue with the security setup such that you can edit that queue. After creating that queue, open up the configuration file from the “lqs” folder for that new queue and look for the “security” field. Copy that value from the newly created queue to the old queue which you could not edit. Save that file and restart MSMQ service.

You should be good to go!

Notes:

MSDN: Interpretting the file names in the storage directory of MSMQ

Sunday, March 11, 2012

OptiRoute for Windows Phone 7

Google-Maps-iconDownload OptiRoute from Windows Phone storeLast week my latest Windows Phone 7 app got certified and became available for download on the Windows Phone App Store.

OptiRoute is a route optimization application, that “attempts” to come up with the shortest route around a set of locations that you enter. The reason that I say “attempts” is that OptiRoute uses a heuristic that allows it run very fast, but in doing so, there are cases where it may not find the most optimal route (more on that later – Check the technical background section). (The way I deal with the cases, is that I provide you with functionality that allows you to customize the route after it has been optimized)

Background:

When I was searching for a home to buy, I always found it hard to come up with an optimal route to visit all the homes in my list. I knew about computer algorithms like the travelling sales man problem, but could not find any implementations for use on a mobile platform (at that time I had an iPhone). One of the biggest problems with finding an optimal route around a set of locations is that its computationally intensive. And when you take into account that you are working on a mobile platform, every resource becomes that much more expensive to use.

When Microsoft launched its “30 to launch” initiative, I decided to port some early work I had done as a web-app with routing to the Windows Phone platform – OptiRoute is the result of that initiative.

The OptiRoute UI:

Welcome screen:

The first time you load up OptiRoute you will be presented with a welcome screen, that gives you a brief overview of the functions available within OptiRoute.

image

In addition to giving you a quick introduction to the main UI, this screen also allows you to load sample locations to check out the route optimization software (you need to scroll to the bottom of the page to view the load button):

image

Once you tap the “Load Sample Data” button, you can close out of the “welcome screen” and you will be presented the list of locations. (Tip: the welcome screen is only shown the first time you open OptiRoute. If you want to see it again, you can do so via the help screen). The screen showing you the list of locations is your “Home Screen”. You manage your route via this page.

Home Page (location list) and viewing your route:

02

Lets take a look at the route, without performing optimization. Click on the “Map Route” button image. This will take you to the “Route Viewer” screen with a map of the route as well as detailed information on the route that you need to take.

05

By tapping “image”, you can change the map type from “road” to “aerial”, as well as hide or show the detailed route information. Rotating your phone will show the map in full-screen:

image

Click the “back button” to return to the home screen.

Optimizing your route:

Lets optimize the route by clicking the “Optimize Route” button image. Now when you click on the “Map Route” button, you will find that the route has been optimized:

image image
Route before optimization Route after optimization!

Customizing your Route:

Now what if you want to change the starting location? Go back to the home-screen and tap and hold the location you wish to make the start of your route until a context menu is displayed:

image

Tapping on the “Set as Start” button will make that location the starting point in your route. As you can see from the image above, this same context menu also provides you a way to delete locations from your route.

What if you want to manually change the route? You can do this using the “Reorder handles” that appear to the right of the location (highlighted in red in the image below):

image

Simply tap, hold and drag that location to the correct place in the route list.

Tip: “Map Route” button displays the route and performs the route based on the order of the locations on the home screen. So if you don’t “optimize” your route, you will be presented the route that reflects the order of the locations shown on the home page. This makes OptiRoute a handy tool for viewing your routes without even using the route optimizer.

Searching for locations:

You can add locations to your route via the “Search” screen. Version 1.0 of Optiroute only supports searching for addresses and points of interest (I plan on adding POIs in a later release).

Tap the “Add” button image to visit the “Search” page.

Type in an address and tap the go button to find that location:

image

To add the location to your route, tap on the pushpin that represents the address you wish to visit and then tap the “Select” button. This will add the selected location to your route and take you back to the “home” screen.

image

Tip: If you wish to add your current location, tap the “Current Location” button image.

Screen shots:

Here are some screenshots taken of the different screens within the application:

02 03 04
05 07 08
09 10 12
13 14 Download OptiRoute from Windows Phone store

Technical background:

Route optimization has many different names (eg: Travelling Sales Man problem, etc.). If you have studied computer algorithms then you may know that the travelling sales man problem (TSP) is NP-Complete. OptiRoute uses a heuristic where it lays out all the locations on a grid, divides the grid into 4 quadrants and attempts to visit all the locations within a quadrant before moving on to the next quadrant. At the same time that its trying to visit each location, it attempts to not criss-cross the lines. The algorithm that I use is based on Sierpinski’s space filling curve (check out the video below to see how it fills 4 separate sections of the rectangle, filling each section before moving on to the next)

Sierpinski Space Filling Curve Animation (I created this video many years back and the routing algorithm is based off of this fractal)

Why is the heuristic important?

Determining the actual travelling route (the driving directions) between any 2 locations is an expensive operation. This is especially true on a phone, where bandwidth is limited (and you typically need to use a web-service to determine the driving directions between any pair of locations). In addition, if you had 10 locations to visit and you attempted to brute force find the optimal route, you would need to find out the distance between all 10 locations (which amounts to 45 separate route calculations: n(n-1)/2 ). 

It is generally better to come up with a general idea of your route and then determine the driving directions between each pair of locations in that route (in which case you would need only 10 route calculations – a far cry from the 45 separate routes needed by the brute force implementation). Using the Sierpinski curve, I am able to determine the general route to visit the different locations (this is done on the phone itself) and then I use Bing’s mapping service to find out the actual route between each pair of locations as determined by my algorithm.

The idea of using a space filling curve to determine routing is not something that I invented and I have read about it in papers earlier. Mine is probably the first implementation of such a routing algorithm on the Windows phone platform and maybe any mobile platform.

Note: I had created a silverlight based app a long time ago based on a similar idea. That site has been down for a long time. But if you are interested in reading about it, check out: http://blog.aggregatedintelligence.com/2009/06/optiroute.html

When you may not get the optimal route:

The reason that you may not always get an optimal route is that while performing route optimization the algorithm is not looking at the “travelling distance”, but just the straight line distance between the different locations. This results in sometimes less than optimal routes when one-way streets are involved, or any other impediments are added to the actual route. In addition, the algorithm does not take into account traffic conditions and hence may not be able to find the route with the shortest travel time. But these problems typically occur only when all the locations to visit are concentrated in a small area (eg: downtown city center) and normally do not affect the route when routing on a larger scale.