Custom Dynamics CRM 2011 ToolTips

Dynamics CRM 2011 does not support custom tooltips or any other built-in method for delivering helpful information to the user when filling out a form. The code snippet below will popup a tooltip and display a custom tooltip on as many of the CRM form fields as you like. Just attach this code to you CRM form onload event.

function OnLoad() {
CreateToolTip("img_equipmentneeded", "Provide a brief history of why the services or equipment are needed.");
}

var Tooltip = null;

function CreateToolTip(controlId, toolTip) {
var control = document.getElementById(controlId);

control.ToolTip = toolTip;

control.attachEvent("onmouseover", ShowToolTip);

control.attachEvent("onfocus", ShowToolTip);

control.attachEvent("onmouseout", HideToolTip);
}

function ShowToolTip() {
var control = event.srcElement;

Tooltip = window.createPopup();

var ToolTipHTML = "<DIV style='width:100%;height:100%;border:1px solid gray;background-color: #d8e8ff;filter: progid: DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffff,EndColorStr=#cecfde);padding-left:2px;font:12px tahoma'>" + control.ToolTip + "</DIV>";

Tooltip.document.body.innerHTML = ToolTipHTML;

var Width = control.offsetWidth;

var Height = 30;

var Position = GetControlPostion(control);

var Left = Position.X + 1;

var Top = Position.Y + 1;

Tooltip.show(Left, Top, Width, Height, null);
}

function GetControlPostion(control) {
var Position = new Object();

var controlHeight = control.offsetHeight;

var iY = 0, iX = 0;

while (control != null) {
iY += control.offsetTop;

iX += control.offsetLeft;

control = control.offsetParent;
}

Position.X = iX + screenLeft;

Position.Y = iY + screenTop + controlHeight;

return Position;
}

function HideToolTip() {
if (Tooltip)

Tooltip.hide();
}

OnLoad();

 

Dynamics CRM 2011 Security Roles Best Practices

Security roles in Microsoft Dynamics CRM 2011 are deceptively simple in CRM.  Just click a few bubbles and you good.  Truth is they can become very difficult to manage if not setup and maintained properly.  Here are some best practices to keep in mind.

  • Never use the Out-Of-Box Security roles, rather always clone them.
  • Business Units are data security related and not a business hierarchy.
  • Security Roles are roles not job titles.
  • Limit sharing to a minimum. If you must share, share to teams.
  • Do not let anyone function as system administrator or system customizer roles. IT and Admins should log in with special accounts to make changes. Example CRM Admin account (Which would have System Administrative access)
  • Keep the number of security roles as minimal as is practical.
  • Use meaningful roll names.

 

The SQL below will give you a listing of each entity and role interaction and setting.

 

SELECT DISTINCT
                      FilteredRole.name, EntityView.PhysicalName AS [Entity Name],
                      CASE Privilege.AccessRight WHEN 1 THEN 'READ' WHEN 2 THEN 'WRITE' WHEN 4 THEN 'APPEND' WHEN 16 THEN 'APPENDTO' WHEN 32 THEN 'CREATE' WHEN 65536
                       THEN 'DELETE' WHEN 262144 THEN 'SHARE' WHEN 524288 THEN 'ASSIGN' END AS [Access Level],
                      CASE PrivilegeDepthMask WHEN 1 THEN 'User' WHEN 2 THEN 'Business Unit' WHEN 4 THEN 'Parent: Child Business Unit' WHEN 8 THEN 'Organisation' END AS [Security Level]
FROM         RolePrivileges INNER JOIN
                      FilteredRole ON RolePrivileges.RoleId = FilteredRole.roleid INNER JOIN
                      PrivilegeObjectTypeCodes ON RolePrivileges.PrivilegeId = PrivilegeObjectTypeCodes.PrivilegeId INNER JOIN
                      Privilege ON RolePrivileges.PrivilegeId = Privilege.PrivilegeId INNER JOIN
                      EntityView ON EntityView.ObjectTypeCode = PrivilegeObjectTypeCodes.ObjectTypeCode
WHERE     (FilteredRole.roletemplateid IS NULL)
ORDER BY FilteredRole.name, [Entity Name]

How to get Microsoft CRM 2011 field attributes

How to get CRM field attributes

Say you need to know what all the Required Fields are or list of other field attributes like MaxLength or MinValue, for a set of entities in Microsoft Dynamics CRM 2011, you could open each entity one at a time and then open each field attribute to determine its requirement level but if you have a large solution that process is time consuming and tedious.

A quick simple solution is to query the MetadataSchema.Attribute and the MetadataSchema.Entity found  in the Microsoft Dynamics CRM 2011 Organization database.  Using the SQL I have provided below you can get a listing of just about anything you might want to know about your solutions entity attributes and field attributes.

 

SELECT MetadataSchema.Attribute.Name,   MetadataSchema.Attribute.AttributeRequiredLevelId,   MetadataSchema.Attribute.PhysicalName, MetadataSchema.Attribute.Length,

MetadataSchema.Attribute.MaxLength,   MetadataSchema.Attribute.DefaultValue,   MetadataSchema.Attribute.VisibleToPlatform,   MetadataSchema.Attribute.IsPKAttribute,

MetadataSchema.Attribute.PrecisionValue,   MetadataSchema.Attribute.MinValue, MetadataSchema.Attribute.MaxValue,   MetadataSchema.Attribute.AttributeId,

MetadataSchema.Entity.Name,   MetadataSchema.Entity.OriginalLocalizedName

FROM    MetadataSchema.Entity INNER JOIN

MetadataSchema.Attribute ON   MetadataSchema.Entity.EntityId = MetadataSchema.Attribute.EntityId

 

 

Dynamic JSON Converter and CRM 2011

The Dynamics CRM 2011 REST API is a powerful integration option for XRM developers.  In a past post I attempted to demonstrate a simplified example of a console app grabbing some SQL data from a non-CRM source and posting that data to CRM using the REST API.  Retrieving data back from CRM is just as straightforward but since it returns as JSON you need a strategy to handle that. I wanted to find a way I could deserialize JSON on the fly dynamically, below are my results.  I have greatly parsed down the example, hopefully making it easier for readers to comprehend my approach but at the end of this post I will include the full DynamicJsonConverter class for your use.

The JavaScriptSerializer class is used internally by the Dynamics CRM 2011 asynchronous communication layer to serialize and deserialize the data that is passed between the browser and the Web server. You cannot access that instance of the serializer. However, this class exposes a public API. Therefore, you can use the class when you want to work with JavaScript Object Notation (JSON) in managed code.

To serialize and deserialize types that are not natively supported by JavaScriptSerializer, implement custom converters by using the JavaScriptConverter class. Then register the converters by using the RegisterConverters method which is where our DynamicJsonConverter class comes in.

Step 1) First we get some data from a XRM proxy service based on the concepts found here Dynamics CRM 2011 REST API

publicdynamic GetListofEntities(string entityName, string entityField)

        {

         

   XRMServiceClient xrm = newXRMServiceClient();

          string json = xrm.GetAllEntititiesSpecifyFields(entityName, entityField);

 

 

 

Step 2)

We declare a JavaScriptSerializer dynamic object and register our custom converter.

          var serializer = newJavaScriptSerializer();

          serializer.RegisterConverters(new[]{newJsonExtension.DynamicJsonConverter()});

 

          dynamic data = serializer.Deserialize(json, typeof(object));

 

          return data.Items;

         }

 

 

 

The sample code above would then be implemented by code similar to below.  In this example (Step 3 below) we are just passing in a email address of a contact and getting back the corresponding contact entity record from CRM.  Next we can store our contact in local variables or as a class which I also show below.

Notice here that using these methods we are able to use dot notation to access entity attributes (entityRecord.telephone1).  That’s pretty cool considering our GetListofEntities method above has no concept of a contact or any of the specific attributes, it only knows how to fetch JSON based data and convert it. Even the keyword Items is dynamic and resolved only at runtime.

Step 3)

foreach (var entityRecord in GetListofEntities(“contact”, “emailaddress1”))

{

first = newContact() { FirstName = entityRecord.firstname };

last = newContact() { LastName = entityRecord.lastname };

phone = newContact() { Phone = entityRecord.telephone1 };

 

}

 

publicclassContact : IEntity

  {

publicstring FirstName { get; set; }

publicstring LastName { get; set; }

publicstring Phone { get; set; }

  }

 

 

Here’s the JSONConverter class in sound working order.  However the example code above is not necessarily Intended for direct use.

using System;

using System.Collections;

using System.Collections.Generic;

using System.Collections.ObjectModel;

using System.Dynamic;

using System.Linq;

using System.Text;

using System.Web.Helpers;

using System.Web.Script.Serialization;

namespace JSONConverter

{

    publicclassJsonExtension

    {

        publicclassDynamicJsonConverter : JavaScriptConverter

        {

            publicoverrideobject Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)

            {

                if (dictionary == null)

                    thrownewArgumentNullException(“dictionary”);

 

                return type == typeof(object) ? newDynamicJsonObject(dictionary) : null;

            }

 

            publicoverrideIDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)

            {

                thrownewNotImplementedException();

            }

 

            publicoverrideIEnumerable<Type> SupportedTypes

            {

                get { returnnewReadOnlyCollection<Type>(newList<Type>(new[] { typeof(object) })); }

            }

 

            privatesealedclassDynamicJsonObject : DynamicObject

            {

                privatereadonlyIDictionary<string, object> _dictionary;

 

                public DynamicJsonObject(IDictionary<string, object> dictionary)

                {

                    if (dictionary == null)

                        thrownewArgumentNullException(“dictionary”);

                    _dictionary = dictionary;

                }

 

                publicoverridestring ToString()

                {

                    var sb = newStringBuilder(“{“);

                    ToString(sb);

                    return sb.ToString();

                }

 

                privatevoid ToString(StringBuilder sb)

                {

                    var firstInDictionary = true;

                    foreach (var pair in _dictionary)

                    {

                        if (!firstInDictionary)

                            sb.Append(“,”);

                        firstInDictionary = false;

                        var value = pair.Value;

                        var name = pair.Key;

                        if (value isstring)

                        {

                            sb.AppendFormat(“{0}:\”{1}\””, name, value);

                        }

                        elseif (value isIDictionary<string, object>)

                        {

                            newDynamicJsonObject((IDictionary<string, object>)value).ToString(sb);

                        }

                        elseif (value isArrayList)

                        {

                            sb.Append(name + “:[“);

                            var firstInArray = true;

                            foreach (var arrayValue in (ArrayList)value)

                            {

                                if (!firstInArray)

                                    sb.Append(“,”);

                                firstInArray = false;

                                if (arrayValue isIDictionary<string, object>)

                                    newDynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);

                                elseif (arrayValue isstring)

                                    sb.AppendFormat(“\”{0}\””, arrayValue);

                                else

                                    sb.AppendFormat(“{0}”, arrayValue);

                            }

                            sb.Append(“]”);

                        }

                        else

                        {

                            sb.AppendFormat(“{0}:{1}”, name, value);

                        }

                    }

                    sb.Append(“}”);

                }

 

                publicoverridebool TryGetMember(GetMemberBinder binder, outobject result)

                {

                    if (!_dictionary.TryGetValue(binder.Name, out result))

                    {

                        // return null to avoid exception.  caller can check for null this way…

                        result = null;

                        returntrue;

                    }

 

                    var dictionary = result asIDictionary<string, object>;

                    if (dictionary != null)

                    {

                        result = newDynamicJsonObject(dictionary);

                        returntrue;

                    }

 

                    var arrayList = result asArrayList;

                    if (arrayList != null && arrayList.Count > 0)

                    {

                        if (arrayList[0] isIDictionary<string, object>)

                            result = newList<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => newDynamicJsonObject(x)));

                        else

                            result = newList<object>(arrayList.Cast<object>());

                    }

 

                    returntrue;

                }

            }

        }

    }

}

 

 

 

 

 

 

Dynamics CRM 2011 REST API

Dynamics CRM 2011 offers a multitude of integration options. There are number of additional items exposed to enable delivery of more dynamic integrated soluions. CRM 2011 supports both Early and Late-bound interfaces allowing access to business data in CRM 2011. Additonally, Microsoft has introduced REST endpoint as an alternative to SOAP-based Web Services that allows executing requests using a service that is based on URI. This new interface allows developing client applications using ASP.NET AJAX or Microsoft Silverlight as well as other technologies such as Drupal, PHP, Ruby on Rails,etc. In this post, we will highlight some tips that should give you a good starting point to work from.

Sample Project

Challenge: To explore this let’s use a fictional company we will call Northwind Real Estate. Northwind would like to capture leads and applications on their public website and feed them in to their new Dynamics CRM 2011 back-office system. Northwind has had an ASP.NET website for years and it runs on the .NET 3.x Framework. Unfortunately the CRM 2011 SDK requires .NET 4.0 so we can’t use traditional CRM SDK methods to integrate Northwind’s public facing website with CRM.

Solution: Utilizing CRM 2011 REST API, Northwind can continue to operate their Web site in the .NET 3.x Framework and yet consume CRM REST interface to interact with their new CRM 2011 back-office system. Bottemline, we should be able to achieve the following steps:

  1. Get some data that should be sent to CRM 2011
  2. Format the data in to JavaScript Object Notation (JSON)
  3. Pass it to a method which will make the REST call

GetData is just a simple SQL DataReader method example that grabs some columns from our database and passes the results to second method that formats the results to JSON format.

static void GetData() {
string connectionString = GetConnectionString();
string queryString =
"SELECT FirstName, LastName, Email, PrimaryPhone FROM Leads;";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand command = connection.CreateCommand();
command.CommandText = queryString;
try {
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read()) {
string json = Buildjson(reader[0].ToString(), reader[1].ToString(), reader[2].ToString(), reader[3].ToString());
HttpPost(json);
}
reader.Close();
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
}

Next create some simple JSON Objects

A JSON object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).

static private string Buildjson(string firstName, string lastName, string email, string phone) {
  string json = "{\'new_firstname\':\'" + firstName + "\'";
  json += ",\'new_lastname\':\'" + lastName + "\'";
  json += ",\'new_email\':\'" + email + "\'";
  json += ",\'new_phone\':\'" + phone + "\'";
  json += "}";
  return json;
}

Now we are ready to look at the code that executes the REST call

The HttpWebRequest object in .NET allows us to POST to the CRM 2011 REST API (we could use cURL for a similar PHP function), it’s important to choose a content type of “application/json” and a method of POST as some of the other REST methods don’t seem to be fully supported.

static string HttpPost( string json) {
   //The service call will require a URL, active CRM account name, password and domain
   string domain = ConfigurationManager.AppSettings["domain"];
   string url = ConfigurationManager.AppSettings["restEndPoint"] + endpoint.ToString();
   string user = ConfigurationManager.AppSettings["crmUser"];
   string pwd = ConfigurationManager.AppSettings["crmPWD"];
     // HttpWebRequest exposes common HTTP header values sent to the Internet resource //as properties, set by methods, or set by the system;
   HttpWebRequest req = WebRequest.Create(new Uri(url)) as HttpWebRequest;
   req.Method = "POST";
      // Set the content type of the data being posted.
   req.ContentType = "application/json";
   // Set credentials to use for this request.
   req.Credentials = new NetworkCredential(user, pwd, domain);
     // Encode the parameters as form data:
   byte[] formData = UTF8Encoding.UTF8.GetBytes(json);
   req.ContentLength = formData.Length;
   // Send the request:
   using (Stream post = req.GetRequestStream()) {
   // Write out the response just for testing and debugging
     post.Write(formData, 0, formData.Length);
   }
   // Pick up the response:
   string result = null;
   using (HttpWebResponse resp = req.GetResponse() as HttpWebResponse) {
     StreamReader reader = new StreamReader(resp.GetResponseStream());
     result = reader.ReadToEnd();
   }
   return result;
}

Dynamics CRM 2011 Rollup 12 Cross-Browser Support

Dynamics CRM 2011 Rollup 12 offers cross-browser support.  Microsoft claims a wide range of support which is shown in the table below.  My own findings were not as successful.  While Firefox seems to run Dynamics CRM 2011 very smoothly Google Chrome seems to have trouble, parts of some basic forms don’t display at all.  I was able to successfully load Dynamics CRM 2011 pages on a Mozilla browser running on my Samsung S3 which was pretty cool.  Even if the Chrome has issues this still seems to be a major step forward for Dynamics CRM 2011.

 

OS Internet Explorer Firefox Chrome Safari
Windows 8 IE 10 (Desktop mode) 16+ 21+ Not Supported
Windows 7/Vista IE 8/9 16+ 21+ Not Supported
Windows XP IE 7/8 16+ 21+ Not Supported
Mac OS-x 10.7 (Lion) Not Supported Not Supported Not Supported 6+
Mac OS-x 10.8 (Mountain Lion) Not Supported Not Supported Not Supported 6+

Firefox

ff