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

Windows7, IE 8 and CRM: popups being blocked

by Phil Adams 13. August 2010 13:36

One of our clients is having a really frustrating problem: on a new Windows 7 pc with IE 8 installed, popups from CRM are always being blocked with the usual "A Microsoft Dynamics CRM window was unable to open, and may have been blocked by a pop-up blocker." error message.
The Outlook CRM client isn't installed. 
When we get the above error message, it also causes additional iexplorer.exe processes to appear in task manager that won't dissapear after closing IE.

What we have already tried:

  • adding the site to the popup blocker exception list
  • disabling the popup blocker altogether
  • adding the site to the trusted zone
  • enabling/disabling protected mode
  • disabling all IE plugins
  • resetting security zone settings
  • enable protected mode for all zones
  • resetting all IE 8 parameters
  • checked if IE 8 is the default browser
  • cleared IE cache

I will update when we have found a resolution.

Tags: , , , , ,

CRM | IE8 | Javascript | mscrm4

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

Date field lookup cut off in IE8

by Phil Adams 29. April 2010 12:25

This seemed strange to me, as I knew that I had used right hand date fields in CRM 4 previously, and it had worked.  I tested on another machine, this time with IE 7, and the calendar lookup was not cut off.

IE 7    IE 8

Internet Explorer 7                                          Internet Explorer 8

Turns out that it’s not really a bug in CRM, but rather a side effect from some changes in Internet Explorer 8.  The CRM team worked with the IE team and came back with a fix for this issue.  The fix is to install IE update 974455 and make the following registry change on the client:

HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl
Create a new Key, and name it as follows:
FEATURE_FORCE_POPUPS_ONTO_MONITOR_KB974537
Within this key, point to New , and then click DWORD Value
Type iexplore.exe for the name of the key
Right-click iexplore.exe , click Modify and then type 1 in the Value data field

Read more about this fix at the UK MSCRM Support Blog.

Tags: , , , , ,

CRM | IE8 | Javascript | MSCRM | mscrm4

CRMScape - Mark Kovalcson's MS CRM Development Blog

by Phil Adams 16. August 2009 20:39

 

The email templates in MS CRM can be leveraged as a powerful building block when generating emails programmatically.

Tips:

  • Generate your HTML in another editor and then paste it into the template editor to add data slugs.
  • Associate your template with the entity that you want the most information from.
  • Use Template Naming conventions that organize things and make programmatic template selection easier.

For an example lets say that we have product specific Quote Letters.

Using my helper method from a previous blog article, grab an email template matching a specific name based on the product.

// Get appropriate Template
BusinessEntityCollection templates = 
   h.GetAllEntitieswithFilter(EntityName.template.ToString(), new[] { "templateid" }, new[]{"title"}, new[]{emailTemplateTitle});

Then create a new InstantiateTemplateRequest using the template id and point it at an entity with the information that you want merged in. This must be the same type that you associated your template with when you created it.

var instTemplate = new InstantiateTemplateRequest
     {
         TemplateId = thisTemplate.templateid.Value,
         ObjectId = quoteId,
         ObjectType = EntityName.quote.ToString()
     };


// Execute the request to create an email message from the template.
var instTemplateResponse = (InstantiateTemplateResponse)h.service.Execute(instTemplate);

var newEmail = (email)instTemplateResponse.BusinessEntityCollection.BusinessEntities[0];

The result of the InstantiateTemplateResponse is an email entity collection. For this example we just have one email.

From here you can regard the email to a completely different entity if you want. It is only important that the ObjectId and ObjectType be set for the merge process. After that is complete this is just another email with a bunch of fields pre-filled. Now you can have your way with it like any other email that you created from scratch. This is critical to making the best use of email templates.

Additional Merging
If you are calculating other numbers or pulling information from another data source outside of CRM, there is no reason to limit merging to what can be represented as data slugs.

In this example I am using putting additional information into the email body with a simple string replace using an agreed upon naming convention to represent additional merge fields.

// If template had #EmbedLicense# in body replace it with the license information
newEmail.description = newEmail.description.Replace("#EmbedLicense#", embedFile);

From here you can add attachments as shown here.

Proofing before Email is Sent
Another useful thing to do if you are doing this in a web application or a client Winform application is save the email and then open it in a new window for final review before it is sent. To do that generate a URL like the following with your orgname and email id passed in. You can get the CRM server base URL from the registry as shown here which you need for your web service reference, and then use the following: CrmServerBaseUrl = CrmServiceUrl.Substring(0, CrmServiceUrl.ToLower().IndexOf("mscrmservices"));

 // Open newly created email activity at this Url.
var emailUrl = h.CrmServerBaseUrl + "/"+orgname+"/activities/email/edit.aspx?id={" + emailId + "}";

From a WinForm application you can pop a CRM screen up like this.

For a web application register some JavaScript in the code behind that sets the value of the new email's URL.

 // Sets Javascript variables to envoke Window Open to new Explorer Window with body onload event
const string SETQUERYSTRING_SCRIPT = "Email_String";

if (!Page.ClientScript.IsClientScriptBlockRegistered(SETQUERYSTRING_SCRIPT))
{
   Page.ClientScript.RegisterClientScriptBlock(GetType(), SETQUERYSTRING_SCRIPT, string.Format(@"<script language=""javascript"">emailRecordUrl = '{0}';</script>", emailUrl));
}

Then in the aspx file use the following JavaScript code to open the new window. Call the script from the onload of the body.

<script type="text/javascript"> var emailRecordUrl = ""; function UpdateCRMFormGenerateEmail() { if (emailRecordUrl != "") { window.open( emailRecordUrl,
"_blank", "toolbar=no,scrollbars=yes,resizable=yes"); emailRecordUrl = ""; } } </script> </head> <body onload="UpdateCRMFormGenerateEmail();">

CRMScape - Mark Kovalcson's MS CRM Development Blog

Tags: , , ,

CRM | Javascript | mscrm4 | MSCRM

CRMScape - Mark Kovalcson's MS CRM Development Blog

by Phil Adams 16. August 2009 20:37

 

If you write custom web applications in iframes that depend on the data in the parent form, it is important to run on up to date information to avoid user confusion.

Important! This requires that the Restrict cross-frame scripting box for your iframe is NOT checked.

image

Accessing attribute values on the Parent Form can be done simply by adding a parent.document in front of your normal attribute names. This gets you the values currently on the form, but doesn't guarantee those values have been saved.

var myAttributeValue = parent.document.crmForm.all.new_attribute.DataValue;

Parent Form Save "if modified" This is a nice method to call because if there are no modified values on the form it won't do anything and your application can continue on it's merry way.

parent.document.crmForm.Save();

Force a Parent Save Sometimes you want to force a save so that a Plug-in is called to do some additional calculations even though no attributes have been modified on your current form. The recalculate on the Opportunity, Quote, Order, and Invoice forms is a good example.

Note: The SubmitCrmForm method is not a MS supported method, but it hasn't changed in the last couple CRM revisions.

SubmitCRMForm( Mode, Validate, ForceSubmit, closeWindow)

Modes:
1 = Save, 2 = SaveAndClose, 7 = Send,
58 = SaveAsCompleted, 59 = SaveAndNew

Example: In this example I am forcing a save so that a plug-in is called on the Pre Update for the quote to create a new total value from the quote details that were filled in when the Quote was created from an Opportunity. These new fields were  mapped between the detail products see Mapping Note below. The iframe contains a button to do something that requires all of the data on the quote be up to date.

function RecalculateQuote() 
{ 
   if ( parent.document.crmForm.all.new_totalamount.DataValue == null)
   {    
    parent.document.crmForm.SubmitCrmForm(1, true, true, false);   
    return false;      
    } 
    
    parent.document.crmForm.Save();
}

The above JavaScript is made to work with an OnClientClick so that a return false will not generate a postback.

Why do we need a return false option?  If the validation fails, a message is generated for the user, the save is not performed, and you do not want your action to run.

The data has already been filled in, then the crmFrom.Save() will check to make sure that any modified fields on the form have been saved.

<asp:Button ID="RunMyNeatFeature" CssClass="button" runat="server" Text="Go" OnClick="RunMyNeatFeature_Click"
OnClientClick="javascript:return RecalculateQuote();"/>

For the button the OnClick will PostBack and run the functionality I care about only if the RecalculateQuote() is not false.

This safeguards the user from running the functionality with different data than what is on the screen when they press the "Go" button.

Mapping Note: For mapping details open SQL Management Studio and look at the EntityMapBase table to find the EntityMapId that has the SourceEntityName and TargetEntityName that you are interested in. Add your guid to the end of the URL below and it will bring up a form to map attributes between the Source and Target Entities.

http://yourcrmservername:5555//tools/systemcustomization/relationships/mappings/mappinglist.aspx?mappingId=

CRMScape - Mark Kovalcson's MS CRM Development Blog

Tags: , ,

CRM | Javascript | MSCRM | mscrm4

Javascript Snippets for Working with Grids in CRM

by Phil Adams 14. August 2009 14:20

 

Javascript Snippets for Working with Grids in CRM

Here are a few useful examples of getting some data from the grid in CRM. I’ve used these in my ISV.config customizations in order to grab certain data that i want to pass to my custom pages. For the most part, a lot of this stuff gets passed automatically when you specify PassParams = 1, but I often have to construct page links dynamically in ISV.config using Javascript, so the PassParams argument does not work.

First, here’s how to get the Id of the displayed view and the Object Type Code of the records it returns:

//    get guid and object type code of view being displayed (otc 1039 = savedquery, system view)
var sViewId = document.all['crmGrid'].GetParameter('viewid');
var sViewType = document.all['crmGrid'].GetParameter('viewtype');

You may notice that the code is slightly different from what I’ve presented before. The method above is a more robust way of getting the data, as it also works with the Associated Grids on an entity’s form.

To get the Object Type Code of the records that are in the grid, use the following:

var sOtc = document.all['crmGrid'].GetParameter('otc');

The following snippet will allow you to retrieve an array containing the Id values of the selected records in the grid:

//    get array of selected records
var a = document.all['crmGrid'].InnerGrid.SelectedRecords;
var selectedItems = new Array(a.length);
for (var i=0; i < a.length; i++)
{
    selectedItems[i] = a[i][0];
}
alert(selectedItems);

To get all of the records in the grid (ie. “All Records on Current Page”):

//    array of all records on current page
var iTotal = document.all['crmGrid'].InnerGrid.NumberOfRecords;
var o = document.all['crmGrid'].InnerGrid;
var allItems = new Array;
var ii = 0;
for (var i=0; i < iTotal; i++)
{
    allItems[ii] = o.rows[i].oid;
    ii++;
}
alert(allItems);

If the grid you are working with is displaying records from an Associated View, you can use the following to retrieve the Id and Object Type Code of the main record:

//    get object id and type code of main record when grid is an associated view
var oId = document.all['crmGrid'].GetParameter('oId');
var oType = document.all['crmGrid'].GetParameter('oType');

For kicks and giggles, here is some code that will retrieve the Grid XML. you can use this for researching other types of properties that are available from the grid object that I have not presented here (and there are a few):

var gridXml = document.all['crmGrid'].gridXml;

Bonus Snippet!

I’ve seen a bunch of questions on how to retrieve the query used in an Advanced Find that has not yet been saved. Well, here ya go:

//    Advanced Find fetchXml
alert(window.top.resultRender.FetchXml.value);

This piece of code will display the Fetch XML that is used by the Advanced Find to query CRM.

Customer Effective Blog: Javascript Snippets for Working with Grids in CRM

Tags: , , , ,

CRM | Javascript | MSCRM | mscrm4

On-Mouse-Over Custom Tool Tips

by Phil Adams 11. August 2009 13:30

 

On-Mouse-Over Custom Tool Tips

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.

Dynamic Methods Microsoft CRM Blog: On-Mouse-Over Custom Tool Tips

Tags: , ,

CRM | Javascript | 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

Open new Modal Window from onLoad

by Phil Adams 10. August 2009 11:02

There are times where administrators and/or executives/managers want to alert users opening a given record about something important on a given record. If people need to be warned that they shouldn't call the contact because they are marked as "Do Not Call" or an Account is marked as being "On Hold". Those can typically be done through alerts. But if logic is needed off of related objects, such as only allowing 3 items per Order, or if a number of Cases have been opened within a given period of time, that's not as easy to do with JScript.
That means that you would need to take your logic to a webpage on the load of the given entity. But since you are opening another web page (through the window.open method) users would be able to click around it and ignore it, making the work of the web page useless.
If you use the window.showModalDialog method then you can change all that. A Modal Dialog requires users to take action on the newly opened window before they can get to the record they are trying to open. The method is very similar to the window.open method, allowing you to control the size of the page, what bars show (status, menu, etc) on the web page. Here's the code:

if(crmForm.ObjectId != null)
{
var url = "/ISV/Test/OrderDetailCheck.aspx?id=" + crmForm.ObjectId;
//window.open(url);
window.showModalDialog(url, "Order Detail Check","dialogWidth:200px;dialogHeight:150px;center:yes;status:no");
}

Just put this in the OnLoad of your entity and you now have a custom page that you can fully customize, modify and design that users must look at take action on before they can access a record.

Tags: , ,

CRM | Javascript | 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