Tuesday, July 22, 2008

The Stunnware Tools Framework for Microsoft Dynamics CRM 4.0 - Professional Edition

STUNNWARE Tool is a must for CRM Developers!

As previously announced there will be a professional edition of the Stunnware Tools for Microsoft Dynamics CRM 4.0. It's almost complete, besides the documentation, but I wanted to show you some of the features you can expect when going for the professional edition.

I'm starting with the query builder, formerly known as the FetchXml wizard. What you have so far is the following:

  1. Create a query using a visual designer
  2. Paste an existing query, parse it and edit in the designer window
  3. Analyze the query and create .NET code executing the same query with a QueryExpression. Available for C# and VB.NET

The professional edition adds the following new features:

  1. Create .NET code for any available .NET language. Though C# and VB.NET are the most common ones, you can also create code for C++, J# or whatever you prefer.
  2. Execute the .NET code and run the query. It allows you to make changes and test before using the code in your own applications.
  3. Tracking the SOAP messages being sent and received. If you add additional .NET code, like Create, Update or Delete statements, you will get a list of all SOAP messages.
  4. Creates the JavaScript code you need in your CRM forms to access the CRM service. Again, the code is available for each request you made.

The video

I think it's easier to show the application than talking about it, so here's a short video created with Jing (awesome tool). It requires the Adobe Shockwave Player to be installed on your machine though.

 

Converting HTML E-mail To Plain Text

Posted: Thursday, July 10, 2008 10:34 PM by Simon Hutson

OK, I admit it. I've caught the CRM development bug. What started as a harmless bit of fun working on document library integration between CRM & SharePoint has now developed into an obsession. In this post I will describe how to build a plug-in that examines the body of any e-mail promoted promoted from Outlook or the e-mail router and converts the HTML into plain text.


After a bit of searching, I found a good article which showed how you could use regular expressions to remove unwanted HTML tags leaving just the plain text - Convert HTML to Plain Text. Converting this from C# to VB (my preferred choice of language) and stripping out some of the bits I didn't need, I came up with the following code which forms the basis of this plug-in.



Private Function ConvertHTMLToText(ByVal Source As String) As String
 
    Dim result As String = Source
 
    ' Remove formatting that will prevent regex from running reliably
    ' \r - Matches a carriage return \u000D.
    ' \n - Matches a line feed \u000A.
    ' \f - Matches a form feed \u000C.
    ' For more details see http://msdn.microsoft.com/en-us/library/4edbef7e.aspx
    result = Replace(result, "[\r\n\f]", String.Empty, Text.RegularExpressions.RegexOptions.IgnoreCase)
 
    ' replace the most commonly used special characters:
    result = Replace(result, "&lt;", "<", RegexOptions.IgnoreCase)
    result = Replace(result, "&gt;", ">", RegexOptions.IgnoreCase)
    result = Replace(result, "&nbsp;", " ", RegexOptions.IgnoreCase)
    result = Replace(result, "&quot;", """", RegexOptions.IgnoreCase)
    result = Replace(result, "&amp;", "&", RegexOptions.IgnoreCase)
 
    ' Remove ASCII character code sequences such as &#nn; and &#nnn;
    result = Replace(result, "&#[0-9]{2,3};", String.Empty, RegexOptions.IgnoreCase)
 
    ' Remove all other special characters. More can be added - see the following for more details:
    ' http://www.degraeve.com/reference/specialcharacters.php
    ' http://www.web-source.net/symbols.htm
    result = Replace(result, "&.{2,6};", String.Empty, RegexOptions.IgnoreCase)
 
    ' Remove all attributes and whitespace from the <head> tag
    result = Replace(result, "< *head[^>]*>", "<head>", RegexOptions.IgnoreCase)
    ' Remove all whitespace from the </head> tag
    result = Replace(result, "< */ *head *>", "</head>", RegexOptions.IgnoreCase)
    ' Delete everything between the <head> and </head> tags
    result = Replace(result, "<head>.*</head>", String.Empty, RegexOptions.IgnoreCase)
 
    ' Remove all attributes and whitespace from all <script> tags
    result = Replace(result, "< *script[^>]*>", "<script>", RegexOptions.IgnoreCase)
    ' Remove all whitespace from all </script> tags
    result = Replace(result, "< */ *script *>", "</script>", RegexOptions.IgnoreCase)
    ' Delete everything between all <script> and </script> tags
    result = Replace(result, "<script>.*</script>", String.Empty, RegexOptions.IgnoreCase)
 
    ' Remove all attributes and whitespace from all <style> tags
    result = Replace(result, "< *style[^>]*>", "<style>", RegexOptions.IgnoreCase)
    ' Remove all whitespace from all </style> tags
    result = Replace(result, "< */ *style *>", "</style>", RegexOptions.IgnoreCase)
    ' Delete everything between all <style> and </style> tags
    result = Replace(result, "<style>.*</style>", String.Empty, RegexOptions.IgnoreCase)
 
    ' Insert tabs in place of <td> tags
    result = Replace(result, "< *td[^>]*>", vbTab, RegexOptions.IgnoreCase)
 
    ' Insert single line breaks in place of <br> and <li> tags
    result = Replace(result, "< *br[^>]*>", vbCrLf, RegexOptions.IgnoreCase)
    result = Replace(result, "< *li[^>]*>", vbCrLf, RegexOptions.IgnoreCase)
 
    ' Insert double line breaks in place of <p>, <div> and <tr> tags
    result = Replace(result, "< *div[^>]*>", vbCrLf + vbCrLf, RegexOptions.IgnoreCase)
    result = Replace(result, "< *tr[^>]*>", vbCrLf + vbCrLf, RegexOptions.IgnoreCase)
    result = Replace(result, "< *p[^>]*>", vbCrLf + vbCrLf, RegexOptions.IgnoreCase)
 
    ' Remove all reminaing html tags
    result = Replace(result, "<[^>]*>", String.Empty, RegexOptions.IgnoreCase)
 
    ' Replace repeating spaces with a single space
    result = Replace(result, " +", " ")
 
    ' Remove any trailing spaces and tabs from the end of each line
    result = Replace(result, "[ \t]+\r\n", vbCrLf)
 
    ' Remove any leading whitespace characters
    result = Replace(result, "^[\s]+", String.Empty)
 
    ' Remove any trailing whitespace characters
    result = Replace(result, "[\s]+$", String.Empty)
 
    ' Remove extra line breaks if there are more than two in a row
    result = Replace(result, "\r\n\r\n(\r\n)+", vbCrLf + vbCrLf)
 
    ' Thats it.
    Return result
 
End Function

All that remains is to implement the IPlugin.Execute method. In order to be able to modify the e-mail message before the e-mail activity gets created in the database, I had to figure out which event(s) to intercept. Through a bit of trial and error, I observed that any e-mail promoted from Outlook triggers the "DeliverPromote" event, whereas any incoming e-mail handled by the e-mail router triggers the "DeliverIncoming" event. Interestingly enough, the "Create" event was also called as a child pipeline for these events, but modifying the message here didn't have any effect, even in the pre-processing stage.


Because plug-ins have the potential to introduce significant performance and scalability issues into your environment, it is important to ensure that the code is as efficient as possible. To that end I added additional checks to ensure that the even if registered on multiple events, the main code will only run if the plug-in:



  1. is running on the 'DeliverPromote' or 'DeliverIncoming' messages

  2. is running synchronously

  3. is running against the 'Email' entity

  4. is running in the 'pre-processing' stage of the pipeline

  5. is running in a 'Parent' pipeline


Public Class ConvertHtmlToText
    Implements IPlugin
 
    Public Sub Execute(ByVal context As IPluginExecutionContext) Implements IPlugin.Execute
 
        ' Exit if any of the following conditions are true:
        '  1. plug-in is not running synchronously
        '  2. plug-in is not running against the 'Email' entity
        '  3. plug-in is not running in the 'pre-processing' stage of the pipeline
        '  4. plug-in is not running in a 'Parent' pipeline
        If Not (context.Mode = 0) Or Not (context.PrimaryEntityName = "email") Or Not (context.Stage = 10) Or Not (context.InvocationSource = 0) Then
            Exit Sub
        End If
 
        If (context.MessageName = "DeliverPromote") Or (context.MessageName = "DeliverIncoming") Then
 
            For Each item In context.InputParameters.Properties
 
                If (item.Name = "Body") Then
                    context.InputParameters.Properties.Item("Body") = ConvertHTMLToText(CStr(item.Value))
                End If
 
            Next
 
        End If
 
    End Sub
 
End Class

As always, I have include the source code to my project here. Please do bear in mind that I haven't included any error handling or logging, so it's not production-ready. However, it should provide you with a good head-start.


This posting is provided "AS IS" with no warranties, and confers no rights.

Microsoft Dynamics CRM Online Video Gallery

Please see the link below to review a gallery of how-to videos using Microsoft Dynamics CRM Online. The videos include topics such as end-to-end scenario’s for Sales, Marketing, and Customer Service. There are also video’s for customizations, imports, and workflow.

All of the videos are the work of the CRM Online Technology Specialist team. Keep checking back for more as we will continue to add new videos. Feel free to ping us if there is something you would like to have covered.

www.democrmonline.com

CRM Online Customer Evidence Video – Total Structures

Another great testimonial from a Microsoft Dynamics CRM Online customer.

image

Total Structures manufactures structural staging systems to be used in rock concerts, trade shows, etc.  The vice-president of this company discusses the advantages to the company of an integrated CRM system, why he chose Microsoft Dynamics CRM Online and describes their successful implementation process and how Microsoft Dynamics CRM Online helps them keep the promises they make to their customers.

MSCRM 4.0 User Guide from Microsoft

Download Here
The Microsoft Dynamics CRM 4.0 User’s Guide includes all of the basic end-user information available in Help, including documentation on sales, marketing, and customer service features, as well as step-by-step instructions on working with Advanced Find and workflow. And it’s in an easily printable format.

Microsoft Partners and MVPs have told us that they want to provide customized documents for their customers. To make it easy to customize the document, we’re making a version available in Microsoft Office Word 2007 (.docx) format.

We’ve also talked to Microsoft Dynamics CRM customers, such as salespeople and customer service reps, who’ve expressed frustration at having to print out each Help page individually. Based on this feedback, we’ve included an Adobe Acrobat (PDF) version that serves as an all-in-one document you can distribute in your organization.

You can download both versions from the Microsoft Download Center.

Although the User’s Guide is currently available only for Microsoft Dynamics CRM 4.0 (on-premise) and only in English, we have plans to release the document for Microsoft Dynamics CRM Online and for additional languages. (We also plan on improving the formatting of the document over time; Word doesn’t seem to like 499 pages.)

Monday, July 21, 2008

Adding An I_Frame For An N:N Relationship

by Danny Varghese 07.10.08

I've mentioned in a previous post how to add an I_Frame to a related entity: http://crowechizek.com/cs/blogs/crm/archive/2008/03/18/adding-an-i-frame-that-contains-a-view-of-related-entity.aspx.  The steps mentioned work for any entity that has a 1:N or N:1 relationship in both CRM 3.0 and now the new CRM 4.0 (Titan).  As you all know, Titan now has the ability to create N:N relationships!  With any new feature comes some new challenges, but not to worry, you can add an I_Frame for N:N related entities.  Thanks to a user who posted a comment on my blog article, I did a little digging and found that an additional parameter is needed in the url.


The additional parameter is shown in red and has been added to the original code from my other post:

 var urlAct = ""; urlAct =  "areas.aspx?oId=" + crmFormSubmit.crmFormSubmitId.value + "&oType=" + crmFormSubmit.crmFormSubmitObjectType.value + "&security=" + crmFormSubmit.crmFormSubmitSecurity.value +"&tabSet=areaActivityHistory" + "&roleOrd=2";document.getElementById('IFRAME_History').src = urlAct;

Although I don’t have confirmation of what this parameter is, I believe it may stand for "Role Ordinal."  If you look at the definition of the word "ordinal," it means to define the order or succession of something.  This parameter appears to define which side/order of the N:N relationship to display.  In the example above, the value is "2," which represents which side of the relationship you want to view.  So if you create an N:N relationship say from the Account entity, and you're asked to fill in the "Other Entity" section, that would represent the variable "2."  Another way to look at it is, if you're on the Account form, and you want to create an I_Frame pointing to the related entity of the N:N relationship, then the "roleOrd" is 2.  The best way to find the value of this parameter is if you view the source of the page you're on, do a find for "roleOrd," and see what the value is for the related entity.


Either way, I've seen this example work.  If anyone has any comments on this parameter, please do comment.  Thank you!

CRM Custom RSS Feed in less than 30 minutes

by Mitchell Kett 07.11.08


One of the best ways to improve a client's business is to keep users better informed and up-to-date on the information provided by CRM.  A workflow could be created (and maintained) to send out an email to the appropriate parties when a specific event happens (create, update, delete of an entity), but what if we could go one step further and provide the same up-to-date information without emails (and maintaining who gets what) or without the need for a user to look in CRM?  What about using an RSS feed?

 

Thanks to a very useful tutorial provided by Jeff at uberasp.net, creating an RSS feed for CRM can be done in a matter of minutes.  For a very quick crash course in XML and the syntax for RSS, see  http://www.w3schools.com/rss/rss_syntax.asp .

 

Say I'd like to create an RSS Feed for a specific entity in CRM.  Whenever a new record is created for this entity, I want to see it in my RSS Feed.  For this example, I created a custom entity in CRM called "new_rssfeed".  The only attribute I added to new_rssfeed was an ntext field called "new_description" which will contain text describing the new record.  After publishing my new entity type, I opened up Visual Studio 2005 and started a new ASP.Net Web Site.  I renamed the Default.aspx file generated by VS to "RSS_Feed.aspx" and changed the code to the following:

 

//RSS_Feed.aspx

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="RSS_Feed.aspx.cs" Inherits="_Default" EnableViewState="false" %>

<%@ OutputCache Duration="300" VaryByParam="none" %>

 

Yup, that is all you should see in your .aspx file.  No need for any html tags or DOCTYPE declarations.  What will happen is that when a user navigates to the RSS_Feed.aspx file, the Page_Load event will generate a stream of XML code which the web browser will interpret as an RSS feed.  So there is no need for any HTML.

 

Within the code-behind file, RSS_Feed.aspx.cs, I added the following code to generate the XML for the feed within the Page_Load event.  You can use this code as a template for your own feed.

 

//RSS_Feed.aspx.cs

protected void Page_Load(object sender, EventArgs e)

    {

Response.Clear();

Response.ContentType = "text/xml";

XmlTextWriter objX = new XmlTextWriter(Response.OutputStream, Encoding.UTF8);

objX.WriteStartDocument();

objX.WriteStartElement("rss");

objX.WriteAttributeString("version","2.0");

objX.WriteStartElement("channel");

objX.WriteElementString("title", "Practice CRM RSS Feed");

objX.WriteElementString("link","http://localhost:5555/RSS/RSS_Feed.aspx");

objX.WriteElementString("description","Live, up-to-date information coming from CRM!");

objX.WriteElementString("copyright","(c) 2008. All rights reserved.");

objX.WriteElementString("ttl","5");

SqlConnection objConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["crmConnectionString"].ToString());

objConnection.Open();

string sql = "SELECT TOP 10 new_name, new_description, new_rssfeedid, createdon FROM new_rssfeed ORDER BY createdon DESC";

SqlCommand objCommand = new SqlCommand(sql, objConnection);

SqlDataReader objReader = objCommand.ExecuteReader();

while (objReader.Read())

{

objX.WriteStartElement("item");

objX.WriteElementString("title",objReader.GetString(0));

objX.WriteElementString("description",objReader.GetString(1));

objX.WriteElementString("link", "http://localhost:5555/MicrosoftCRM/userdefined/edit.aspx?id=" + objReader["new_rssfeedid"].ToString() + "&etc=10008");

objX.WriteElementString("pubDate", objReader.GetDateTime(3).ToString("R"));

objX.WriteEndElement();

}

objReader.Close();

objConnection.Close();

 

objX.WriteEndElement();

objX.WriteEndElement();

objX.WriteEndDocument();

objX.Flush();

objX.Close();

Response.End();

    }

 

Notice the bolded text within the code.  These are snippets that will differ in your code.  For my RSS feed, I gave it the title of "Practice CRM RSS Feed".  The link element is for the URL used to get to the aspx file.  For my connection to CRM, I simply created a web.config file with a connection string to my CRM DB.  Throw in your own custom SQL Query to grab the necessary info to populate the "title", "description", "link", and "pubDate" for the feed <item> element.  The above code, in a nut shell, will grab the 10 most recently added New_rssfeed elements and format them for the feed.  I built and published the web site project and the last thing to do was configure IIS to make the feed accessible.

 

In IIS, all that I needed to do was create a new virtual directory with the alias "RSS" under the Microsoft CRM web site and point it to the folder with the compiled web code.  It automatically saw the web.config file, so no other adjustments had to be made.  Do an IIS reset and navigate to the aspx page.   You should see a basic page with the feed title and a description of how to subscribe to the feed.  You will also see the feed articles listed below and search options to the right (I used IE7 -- other browsers may render differently or re-direct to an RSS Reader like Google Reader).

 

Example of RSS Feed rendering in IE7 (click to view larger image)

 

Just think of what you could use this for!  You could integrate workflows and plugins with an RSS feed in order to provide up-to-date info on what's happening in CRM to other users (or anyone within the local network).  Inform sales people of new opportunities and leads, give executives updates by the minute as opportunities close and new ones come in.  Create one generic feed and register a plugin for multiple actions which could generate a variety of updates to the feed.  You could even create multiple feeds/aspx files and give users the option of how much/ little they'd like to get updated on.  We could even throw in a couple parameters  like entity type and GUID and we've got an RSS feed for one specific record in CRM. 

 

I merely scratched the surface of RSS (you can add images and other content as well), so be creative and think of how you might be able to use this to keep users (and developers) better informed of what's going on in CRM.