Wednesday, 3 April 2013


 Branding a CRM 2011 application by  Mani Ramachandran

The Dynamics CRM 2011 application, along with the XRM framework, enables us to model a varied requirement set with a high degree of customization. But there are instances when CRM is used adjacent with another LOB application, either as sections inside of CRM 2011, or outside of it. In such a case, the user experience should be as streamlined as possible. Dynamics CRM approaches this issue by providing the style sheets that crm uses as part of the sdk (sdk\resources\styleguide), so that other applications can be brought in line with CRM. In situations where other LOB applications take precedent (or their UI cannot be modified), what are the options for the CRM customizer?

Unsupported modifications to the rescue!
A few assumptions/ caveats before we go down this road:
  1. The crm application is installed on-premise (you have access to the web files)
  2. The changes are unsupported. Future rollups/ updates from MS could wipe out the changes
  3. Many of the changes could be global in nature, so test the new styles completely before moving it to production
Dynamics CRM uses css to style the various pieces in the application. Some of the styles are available in the css files, and some are generated during run-time by the code-behind. The approach is to figure out the element that I want to re style, note down the css class and the webfile it is a part of, and make the necessary changes to those css webfiles. Here are some examples of the changes, and the sections I edited to get it to work.

1. Border color for selected dashboard item

        File \CRMWeb\ _forms\controls\form.css.aspx

TABLE.ms-crm-Form-SubGrid-Layout-Selected,
TABLE.ms-crm-Form-AssociatedGrid-Layout
{
border:                                                 2px #8ab728 solid;
}
TABLE.ms-crm-Form-Chart-Layout-Selected
{
border:                                                 1px #8ab728 solid;
}






















2. Active header panel background color for view

     File \CRMWeb\ _forms\controls\form.css.aspx

TR.ms-crm-Form-SubGrid-viewRow
{
background-color:   #bef14f;
}











3. Background color and text color for view sort columns, with modified divider color

     File \CRMWeb\_common\styles\theme.css.aspx

.ms-crm-List-Header
{
<% = this.GetStyleCss(CrmTheme.Current.Grid.Header) %>
background-image: url("null");
background-color:   #76d3ee;
color: #3e4d6b;
}
File \CRMWeb\_grid\ AppGrid.css.aspx (for text color):


label.ms-crm-List-Sortable
{
text-overflow:ellipsis;
overflow:hidden;
white-space:nowrap;
<% if (CrmStyles.IsRightToLeft) { %>
text-align:right;
<% } %>
color: #3e4d6b;
}


    File \CRMWeb\_common\styles\global.css.aspx  – for white color dividers between the columns.
             .ms-crm-ImageStrip-resize{background:#ffffff;width:2px;height:14px;overflow:hidden;}
 .ms-crm-ImageStrip-bar_line{background:#ffffff;width:2px;height:14px;overflow:hidden;}




4. View module panel background color

     File \CRMWeb\_common\styles\theme.css.aspx

        .ms-crm-List-Title
        {
        <% = this.GetStyleCss(CrmTheme.Current.Grid.TitleArea) %>
        background-color:   #84b38e;
        color: #3e4d6b;
        }
        span.ms-crm-View-Name
        {
        <% = this.GetStyleCss(CrmTheme.Current.Grid.ViewName) %>
        color: #3e4d6b
        }





5. Navigation section header background and text color
       File \CRMWeb\appnav\appnavbar.css
div.ms-crm-NavBar-Title
{
border-bottom: 1px dotted #A5ACB5;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#2b73c0, endColorstr=#2361a1);
}
nobr.ms-crm-NavBar-Header-Title
{
vertical-align: middle;
font-weight:    <%= WebUtility.GetFontResourceForStyle("General.Bold.font_weight")%>;
font-size:      <%= WebUtility.GetFontResourceForStyle("General.14px.font_size")%>;
color                      :          white;
overflow              :       hidden;
text-overflow:  ellipsis;
display  : block;
}























6. Navigation section items background color and selected color
       File \CRMWeb\appnav\appnavbar.css
LI.ms-crm-NavBar-Subarea
{
height : 16px;
line-height : 16px;
margin-left: 3px;
margin-right: 3px;
padding: 1px;
background-color: #c4d1f5;
}
LI.ms-crm-NavBar-Subarea-Hovered
{
padding: 0px;
border: 1px solid #61A6E4;
}
LI.ms-crm-NavBar-Subarea-Selected
{
padding: 0px;
border: 1px solid #A7CDF0;
background-color: #2361a1;
}


































7. Active List Tool background color in dashboards
       File CRMWeb\_static\css\1033\Cui.css
.ms-cui-cg-lb.ms-cui-cg-s .ms-cui-cg-t{
color:#3e4d6b;
background-color:#bef14f;
}









8. Active/ Default background color tabs

       For selected tab:
.ms-cui-tt-s > .ms-cui-tt-a{
border-color:#b6babf;
background-color: #c2f259;
color:#601704 !important;
}

       For all tabs:

.ms-cui-tt-a{
margin-top:20px;
color:#6c6e70 !important;
border:1px solid transparent;
border-bottom:0px;
display:block;
cursor:default;
background-color: #0578bc;
color:#f8fa5b !important;
}










Here is a sample re-branded landing page. With changes to about 5 files, we are able to get our dynamics crm user interface aligned with an external application's UI. The image below also has changes to the logo, which is an important part of user experience (here is a good example for changing the logo).
















There is a feature request in MS Connect (ID 480469) that talks about custom logos, and is a possibility in a future release. So if you have some time, please go to MS Connect and vote for it!

Before I bid adieu, let me reiterate that these changes are unsupported, meaning a rollup update could not only replace the files, but might change the way the css is rendered. Thanks for reading!

Monday, 1 April 2013


Microsoft CRM 2011 Timeouts and Limits:

Timeouts

Limits

  • Fetch Aggregate Limit
  • Query Result Limit
  • Infinite Loop Detector
  • Duplicate Detection Rules
    • Default is 5 published rules per entity
  • Excel Export
  • Managed Solutions
    • you cannot change your publisher if you share an updated version of a previous solution you´ve deployed under another publisher (so keep in mind your prefix)
    • you cannot start auditing for any of your entities just by providing them within a managed solution
    • you cannot set the default dashboard by providing a managed solution
    • you cannot set the default public view by providing a managed solution
  • Data Import
    • CRM 2011 Online: single file must be less than 8MB
    • you can zip many files to one .zip-file. This file has to be less than 32MB
    • when you upload the zip file, CRM 2011 unzips it on server and process the files individually. Because of the 8MB limit if any single file is greatet then 8MB limit this file will fail
  • Sub-Grid
    • The first four sub-grids can be populated with data in a form when it loads
    • If more than four sub-grids exist on a form, the remaining sub-grids require some user or form script action to retrieve data
  • Outlook Reading Pane
    • Form events do not occur in the reading pane so form script event handlers are never called
    • If you are using multiple forms for an entity, only the default entity form will be used for the reading pane
    • The reading pane does not display IFrames, Subgrids or Web Resources

Friday, 29 March 2013

Managed VS Un managed Solution in MSCRM 2011

Think of unmanaged solutions as source code and think of managed solutions as compiled, versioned and signed assemblies.
How to delete any of its entities, attributes, relationships, reports and Option Sets from Managed Solution  in MSCRM 2011:


 Hide instead of delete. The easiest solution is to simply create the new attribute with the correct data type and completely hide the old attribute from all views, forms, reports, etc. In many cases, deleting an attribute in production is unacceptable because of data loss so this alternative is the safest and most conservative as you get to keep the old data. The disadvantage is that you will have useless columns in your database and the old attribute will continue to appear in Advanced Find.


2. Use a holding solution to delete any component from your deployed managed solution. This is my favourite approach as it has the advantage of completely deleting from the system the unwanted components without losing any data. This is how it works: Assume you have a solution XXX which contains a custom entity and one of the attributes is of type “Single Line of Text” but you want to change it to “Multiple Lines of Text”. In your target environment you should have solution XXX as managed and in your development environment you should have solution XXX as unmanaged.

  1. In your development environment create a new solution “XXX_holding”. The publisher must be the same as the publisher of your XXX solution.
  2. Add all the components of the XXX solution to your XXX_holding solution*.
  3. Export XXX_holding as managed.
  4. Import XXX_holding to your target environment.
  5. Delete XXX solution from your target environment. No data is lost because all the customizations remain in the holding solution.
  6. In your development environment delete the attribute (you will need to adjust views, forms, etc.).
  7. Export XXX as managed.
  8. Import XXX to your target environment
  9. Delete XXX_holding from your target environment. The attribute will now be deleted from all environments. There is no data loss except for the data that was saved in the old attribute which was deleted.
  10. Now that the attribute is deleted, if you want the attribute back but with a different data type you just need to re-create it in dev and promote the solution to your target environment.
* Note: You can avoid steps 1-3 if you simply open your XXX solution zip file and in the solution.xml file you update the solution unique name to “XXX_holding”. This way you guarantee that you have all the components of XXX also in XXX_holding.


3. Uninstall and re-install solution. If you absolutely need to wipe out the old attribute from the system, you could also uninstall the entire managed solution, make the appropriate changes in your development environment and then re-deploy a clean and correct version of your managed solution. The big disadvantage here is that you will lose your data if it relies on the customizations of your solution, which is usually unacceptable unless you have no data to retain or the data is not important.


4. Use unmanaged solutions instead of managed solutions. While it is true that with unmanaged solutions it is simpler to manually delete any component, it is also true that deploying unmanaged solutions to a production environment has multiple disadvantages and is not a good practice in my perspective. It might also be too late for you to switch to unmanaged solutions.

Tuesday, 26 March 2013

MSCRM  Fetch XML 5000 Records Limitation:


Have you ever tried to write a code which will get you all records from a specific entity? It's harder then you think it is! Everybody who is a bit aware of the CRM SDK thinks it should be a fetch statement like this:

<fetch mapping='logical'><entity name='account'><attribute name='accountid'/></entity>

WRONG!
This would only give you the first 5000 records in the database! It is written down in the SDK with small letters, but it could drive you crazy..

There are two solutions for this issue.
1) Add a registery setting to specify not to implement MaxRowsPerPage
2) Modify the fetch statement and merge several results

Here are the details for each solution
1st solution
Search in the SDK for the word "TurnOffFetchThrottling". You should add this as DWORD registery setting to HKLM\Software\Microsoft\MSCRM. Set the value to 1. You will now not have the 5000 records limit.

2nd solution
Modify your fetch statement to include paging and count numbers. Store all the data in an DataSet and perform that series of code over and over again as long as there is data coming.

Here's the script you should use to get all accountid's (for clarity and the ease of use I have added a function called "FetchDataSet").


private DataSet FetchAllAccountIds(){
int i=1;
bool bFinished = false;
DataSet dsAllData = new DataSet();
while (bFinished == false)
{
StringBuilder sbFetch = new StringBuilder();
sbFetch.AppendFormat("<fetch mapping='logical' page='{0}' count='5000'>", i);
sbFetch.Append("<entity name='account'>");
sbFetch.Append("<attribute name='accountid'/>");
sbFetch.Append("<attribute name='new_12_accountid'/>");
sbFetch.Append("</entity>");
sbFetch.Append("</fetch>");
DataSet dsTempResult = FetchDataSet(sbFetch.ToString());
dsAllData.Merge(dsTempResult);
if (dsTempResult.Tables[0].Rows[0]["morerecords"].ToString() == "0")
{
bFinished = true;
}
else
{
i++;
}
}
return dsAllData;
}

private DataSet FetchDataSet(string fetchXml)
{
string strResult = service.Fetch(fetchXml);
DataSet ds = new DataSet();
System.IO.StringReader reader = new System.IO.StringReader(strResult);
ds.ReadXml(reader);
return ds;
}