Argos
Phil's CRM Blog | All posts tagged 'net'

Using Unresolved Email Recipients in CRM 4.0

by Phil Adams 11. August 2010 11:00
Unresolved emails are useful in that you can send an email to someone without having them setup as a system record (contact, account, lead, queue, user, ect).  Unresolved email addresses are turned off by default.  They should be used with caution.  If you send an email to an unresolved address it will not track the email to that lead/contact since one does not exist.  It also does not leverage the email "Do not allow" flags to allow people to opt out of emails.  With those considerations, there are still cases where it is very useful to send an email to someone without having to create a contact record.

I will show two example of sending emails to unresolved recipients.  The first will be sent from the email form using JavaScript.  The second will be done in a plugin.  We will add two unresolved email addresses as CC addresses on emails sent from a contact record.

Before we begin.  You must flip the setting to allow unresolved recipients.  Go to Settings --> Administration --> System Settings --> Email Tab.  Set Allow messages with unresolved e-mail recipients to be sent to yes.

 Second, we will add a couple of fields to the contact for secondary contacts email addresses.  These fields will contain our unresolved email addresses.


JavaScript Implementation
When the user clicks "Send Email" from the contact form we want the CC field to default with the two unresolved email addresses.  To do this we add the following Jscript to the onload of the email form.  The code will check to see if the regarding object is of type contact since this form is used for all emails.  Then it adds each email address to an email object that follows the activityparty schema for unresolved email addresses.  It has a type of 9206 and the email is set in the data property.

if (crmForm.FormType == 1 &&
    crmForm.all.regardingobjectid.DataValue != null &&
    crmForm.all.regardingobjectid.DataValue[0].typename == "contact" &&
    window.opener != null &&
    window.opener.document != null) {

    var ar = new Array();
    var emailObj = new Object();

    if (window.opener.document.crmForm.all.new_secondarycontactemail1 != null &&
            window.opener.document.crmForm.all.new_secondarycontactemail1.DataValue != null) {
        emailObj = new Object();
        emailObj['type'] = '9206';
        emailObj['category'] = '3';
        emailObj['data'] = window.opener.document.crmForm.all.new_secondarycontactemail1.DataValue;
        emailObj['name'] = window.opener.document.crmForm.all.new_secondarycontactemail1.DataValue;
        ar.push(emailObj);
    }

    if (window.opener.document.crmForm.all.new_secondarycontactemail2 != null &&
            window.opener.document.crmForm.all.new_secondarycontactemail2.DataValue != null) {
        emailObj = new Object();
        emailObj['type'] = '9206';
        emailObj['category'] = '3';
        emailObj['data'] = window.opener.document.crmForm.all.new_secondarycontactemail2.DataValue;
        emailObj['name'] = window.opener.document.crmForm.all.new_secondarycontactemail2.DataValue;
        ar.push(emailObj);
    }

    crmForm.all.cc.DataValue = ar;

}

Lastly, publish the customization.  When you click "Send Email" from the contact form your addresses will be added to the CC. 

 

 

Plugin Implementation

The same functionality can be added using a plugin.  The benefits of using a plugin versus a client side implementation is that you can implement consistent functionality regardless of whether the email send is triggered from a client portal, another plugin, or through the CRM UI.  The JavaScript implemenation is useful in that the user can see who the email is being sent to before they click Send. 

Install the plugin using the registration tool as a pre-create step for email save.  This will add the CC on the initial save of the email.  It is triggered regardless of whether the user clicks save or send from the email form.  The code also includes duplicate checking logic. So, if the user adds the email address to the CC from the form it will not duplicate the same address in the plugin.

using System.Web;
using System.Net;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using System.Reflection;
using System.Web.Services.Protocols;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Metadata;
using Inetium.CrmPlugins;
using System.Collections;

namespace Inetium.CrmPlugins.Plugins
{
    public class CaseUnresolvedEmailPlugin : IPlugin
    {
        public void Execute(IPluginExecutionContext context)
        {
            ICrmService service = null;
            try
            {
                if (context.InputParameters.Properties.Contains("Target") &&
                    context.InputParameters.Properties["Target"] is DynamicEntity)
                {
                    DynamicEntity entityInput = context.InputParameters.Properties["Target"] as DynamicEntity;
                    if (entityInput.Properties.Contains("regardingobjectid"))
                    {
                        Lookup regardingObjectLookup = entityInput.Properties["regardingobjectid"] as Lookup;
                        service = context.CreateCrmService(true);
                       
                        // Only using this for contacts
                        if (regardingObjectLookup.type == EntityName.contact.ToString())
                        {
                            // Pull parent contact - using helper method
                            DynamicEntity ctn = CRMUtilities.RetrieveById(service, EntityName.contact.ToString(), "contactid", regardingObjectLookup.Value, new ColumnSet(new string[]{"new_secondarycontactemail1", "new_secondarycontactemail2"}));
                            if (!ctn.Properties.Contains("new_secondarycontactemail1") && !ctn.Properties.Contains("new_secondarycontactemail2"))
                                return;

                            string email1 = ctn.Properties.Contains("new_secondarycontactemail1") ? ctn.Properties["new_secondarycontactemail1"].ToString() : "";
                            string email2 = ctn.Properties.Contains("new_secondarycontactemail2") ? ctn.Properties["new_secondarycontactemail2"].ToString() : "";

                            DynamicEntity[] partyArrayExisting = entityInput.Properties.Contains("cc") ? entityInput.Properties["cc"] as DynamicEntity[] : new DynamicEntity[] { };
                            List<DynamicEntity> partyList = new List<DynamicEntity>(partyArrayExisting);

                            if (email1 != "" && !IsEmailAlreadyInCC(email1, partyList))
                            {
                                DynamicEntity party = new DynamicEntity();
                                party.Name = EntityName.activityparty.ToString();
                                party.Properties["addressused"] = email1;
                                partyList.Add(party);
                            }

                            if (email2 != "" && !IsEmailAlreadyInCC(email2, partyList))
                            {
                                DynamicEntity party = new DynamicEntity();
                                party.Name = EntityName.activityparty.ToString();
                                party.Properties["addressused"] = email2;
                                partyList.Add(party);
                            }

                            // Generate CC's list by adding in those from contacts
                            if (partyList.Count > 0)
                                entityInput.Properties["cc"] = partyList.ToArray();

                        }

                    }
                }
            }
            catch (SoapException se)
            {
                throw new Exception(se.Detail.InnerText);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                if (service != null)
                {
                    service.Dispose();
                    service = null;
                }
            }
        }

        private bool IsEmailAlreadyInCC(string email, List<DynamicEntity> activityPartyList)
        {
            bool bAlreadyExists = false;
            foreach (DynamicEntity existingParty in activityPartyList)
            {
                // compare to existing CCs
                if (existingParty.Properties.Contains("addressused") &&
                    (existingParty.Properties["addressused"].ToString() == email))
                {
                    bAlreadyExists = true;
                    break;
                }
            }
            return bAlreadyExists;
        }
    }
}

Source: Andrew Zimmer

Tags: , , , ,

ASP.Net | CRM | MSCRM | mscrm4

CRM Date and Time Conversions

by Phil Adams 28. October 2009 11:04

using System;
using Microsoft.Crm.Sdk;

/// <summary>
/// Converts Crm types to and from system types.  
/// </summary>
/// <remarks>
/// TODO: Move into CRM Assembly.
/// </remarks>
public static class CrmConverter
{
     /// <summary>
     /// Converts CrmDateTime to Local DateTime.
     /// </summary>
     /// <param name="crmDateTime">The CRM date time.</param>
     /// <returns>A DateTime.</returns>
     public static DateTime ToDateTime(CrmDateTime crmDateTime)
     {
         return crmDateTime.UserTime;           
     }

     /// <summary>
     /// Converts CrmDateTime to UTC DateTime.
     /// </summary>
     /// <param name="crmDateTime">The CRM date time.</param>
     /// <returns>A DateTime.</returns>
     public static DateTime ToUtcDateTime(CrmDateTime crmDateTime)
     {
         return crmDateTime.UniversalTime;
     }

     /// <summary>
     /// Converts UTC DateTime to CrmDateTime.
     /// </summary>
     /// <param name="dateTime">The UTC date time.</param>
     /// <returns>An instance of the CrmDateTime class.</returns>
     public static CrmDateTime ToCrmDateTime(DateTime dateTime)
     {
         return new CrmDateTime(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:s}Z", dateTime.ToUniversalTime()));
     }
}

Tags: , ,

CRM | MSCRM | mscrm4

On-Mouse-Over Custom Tool Tips

by Phil Adams 10. August 2009 11:06

Ever want to give users more information into what they should enter into a field? You can either make the label of a given field really long, put a field in a section on it's own, and write the description out in the section title, or you could use scripts to override the tool tip of the field.
Here's the script:
var oPopup = window.createPopup();
var oPopupBody = oPopup.document.body;
oPopupBody.style.fontFamily = 'Tahoma';
oPopupBody.style.border = '1px solid black';
function My_OrgName_showRatingPopup ()
{
oPopupBody.innerHTML = "<div><table style='font-size:xx-small'>";
oPopupBody.innerHTML += "<tr><td><b></b></td><td> Custom Tool Tip Text Here</td></tr></table></div>";
var x_coord = -100;
var y_coord = 20;
var width = 350;
var height = 90;
var documentElement = event.srcElement;
oPopup.show(x_coord, y_coord, width, height, documentElement);
}
//Be sure to modify the field name here so that you attach your OnMouseOver event
//to the correct field. The "_c" indicates the label of the field.
crmForm.all.new_customfield_c.attachEvent('onmouseover', My_OrgName_showRatingPopup);
Now just hover over your field label and you'll see you new custom tooltip pop up.

Tags: , , ,

mscrm4 | CRM | MSCRM | Javascript

User Account SPN for CRM ASP.NET Application Pool account ERROR.

by Phil Adams 30. July 2009 11:19

You should use Setspn -A http\crm crm$. The machine account is the name of
the machine plus a $-sign.

Tags: , ,

mscrm4 | CRM | MSCRM

Render Control into HTML String

by Phil Adams 6. July 2009 13:50

Occasionally there is a need to get string representation of ASP.NET control in other words - render it into string instead of letting it be rendered on the page. The following method renders a control into its HTML string.
Namespaces used:

using System.Text;
using System.IO;
using System.Web.UI;


public string RenderControl(Control ctrl) 
{
    StringBuilder sb = new StringBuilder();
    StringWriter tw = new StringWriter(sb);
    HtmlTextWriter hw = new HtmlTextWriter(tw);

    ctrl.RenderControl(hw);
    return sb.ToString();
}

Tags: ,

Custom Controls | ASP.Net

Using Multiple Programming Languages in the “App_Code” folder

by Phil Adams 14. May 2009 00:50

Most .NET development shops either develop in C# or Visual Basic (VB). However, depending on who you talk to, C# is probably the most preferred server-side programming language in most of these shops. Personally, I prefer C#; but as a Freelance Consultant, it is in my own interest that I stay unbiased in my judgment of programming languages, and rightfully so.

It is important to note that the source code in the “App_Code” folder of a Web Application Project is compiled into a single assembly. Hence, all the files found directly in the “App_Code” folder must be in the same programming language. The truth is, you can develop a web project solution using both languages if you really need to. So let’s say you purchase a 3rd-party application developed in VB.NET (eg. a Content Management System like CommunityServer or Ektron CMS400.NET), but you prefer to develop your custom code/classes using C#, you can go about implementing it as follows.

You will want to create Sub-folders under the ‘App_Code” folder (eg. “VBCode” for your VB classes and “CSCode” for your C# classes) so ASP.NET can treat the sub-folders as separate compile-able units. In other words, the C# compiler will be used to compile your C# code and the VB compiler, for the VB code. The setup doesn’t end there — you will have to create a codeSubDirectories element in the compilation element of the Web.Config file and subsequently, add a reference of the sub-folders like so:

<configuration>
 ...
 <system.web>
   ...
   <compilation debug="false">
       <codeSubDirectories>
              <add directoryName="VBCode" />
              <add directoryName="CSCode" />
        </codeSubDirectories>
   </compilation>
   ...
 </system.web>
  ...
</configuration>

At compile time, ASP.NET infers the right compiler to use in each sub-folder based on the files in the sub-folder.

This is not necessarily a new concept; however the sad part is, if you have an Application which inherits your ‘parent’ site’s Web.Config file, you might have to ‘remove’ or ‘clear’ the codeSubDirectories element from the ‘child’ (or inheriting) Web.Config file to prevent the .NET error “Parser Error Message: The code subdirectory ‘/App_Code/VBCode/’ does not exist.”

The norm is, typically, to specify in the child Web.Config file, something similar to the following:

<codeSubDirectories>
  <remove directoryName="VBCode" />
  <remove directoryName="CSCode" />
</codeSubDirectories>

However, ASP.NET throws the following error “Parser Error Message: Unrecognized element ‘remove’.” if you try to implement the ‘remove’ element. In the same vein, a similar error is raised when you try to use the ‘clear’ element, even though the MSDN documentation insists that this syntax is correct. Unfortunately, this bug is present in all .NET Frameworks, thus far (including the 3.5 framework).

The Workaround?

You will have to create corresponding sub-directories in the child application’s “App_Code” folder for “VBCode” and “CSCode”.

Tags: , ,

mscrm4 | CRM | MSCRM | ASP.Net

EventLog.WriteEntry() Permissions issue

by Phil Adams 12. March 2009 14:47
void Page_Error (object sender, EventArgs e) {
string errorMessage = "Error Occurred:" + Server.GetLastError(); 
Server.ClearError();
if (!(EventLog.SourceExists("myWebApp",".")))
EventLog.CreateEventSource("myWebApp","System",".");
EventLogPermission eventLogPerm = new EventLogPermission(EventLogPermissionAccess.Administer,"."); 
eventLogPerm.PermitOnly();
EventLog evLog = new EventLog();
evLog.Source = "myWebApp";
evLog.WriteEntry(errorMessage, EventLogEntryType.Error); evLog.Close();
}

Tags: , ,

ASP.Net | CRM | CRM Plugin | MSCRM | mscrm4

About Phil

Phil has been working with CRM since the BETA of CRM v1.0 and has seen a lot of the problems arising from installation, maintenance and Development.

Phil specializes in the ISV area of CRM; Creating Add-Ons and Plugins for various clients.

Phil currently works as a Microsoft Dynamics CRM Consultant and Developer for Cambridge Online Systems Ltd In the UK.

Phil also has several Microsoft Certifications in .NET and CRM