-
Rabbits don't follow orders
My friend Bruce W. dropped off some supplies at my house this evening and when I went out to get them, I found one of my highly-trained attack rabbits setting on the front lawn.
"Attack," I ordered. But, it just set there like a brown, furry, lawn ornament.
I guess I should have believed the guy at the attack rabbit training school….
Meanderings 1,665 views -
JavaScript: How to Close Browser Window (window.close()) Without Warning
If you've ever tried to close a browser window from ASP.NET by using the following code:
<input type="button" class="inputfields"
onclick="javascript:window.close();" value="Close" /></td>
You will probably receive the following message:
After digging around for 20 minutes or so, I finally found the answer on Anatoly Lubarsky's blog:
<input type="button" class="inputfields" onclick="javascript:window.opener='x';window.close();" value="Close" /></td>
Customization, Development, Dynamics CRM 8,416 views -
Creating a legal filename in JavaScript
I am working on a project this weekend ( yeah, I know ) where I will be creating a folder in the file system based on the Account Name for an Account within CRM. Since it is possible to have characters within an Account Name that would be invalid in a file or folder name, they must be replaced.
The following JavaScript snippet will replace any invalid character within the Account Name with an underscore.
// Create a dummy account name for testing purposes. crmForm.all.name.DataValue = "this?is/\ a* test"; if (crmForm.all.name != null) { // The replaceChar should be either a space // or an underscore. var replaceChar = "_"; var regEx = new RegExp('[,/\:*?""<>|]', 'g'); var Filename = crmForm.all.name.DataValue.replace(regEx, replaceChar); // Show me the new file name. alert(Filename); }
Customization, Development, Dynamics CRM 2,032 views -
Error 0×80040237: Operation failed due to a SQL integrity violation
I ran to the above error today assisting another Partner with a custom solution they developed.
Problem
The custom ASP.NET Web application is not configured to use client impersonation. By default, impersonation is disabled in a typical ASP.NET Web application created with Microsoft Visual Studio .NET. Calls to the Microsoft Dynamics CRM Web services must provide valid domain credentials to the Microsoft CRM Web service. If When you do not specify credentials, or when you use impersonation, the correct credentials are not provided to the Microsoft Dynamics CRM Web services and the exception occurs.
Resolution
In the Web.config file of the custom ASP.NET Web application, add the following element to the <system.web> element:
When you set the impersonate attribute of the identity element to true, you configure the application to use client impersonation on each request to the application server.<identity impersonate="true"/>
Customization, Development, Dynamics CRM 2,382 views -
Conversational highlight from this past weekend
My friend Roger was telling us about his week which started off with, "So I took the kids, my wife and her boyfriend to Chuck E Cheese…"
[ They are separated but it is just so funny to hear him say that. Guess you had to be there. ]
Meanderings 1,106 views -
New Poll Added: Would Lead address verification be useful?
Greetings Everyone,
One of my new customers is purchasing leads from a listing service, as many companies do these days. One of their direct mailings experienced a very high rate of returned mail so I am investigating possible solutions while also looking at the commercial potential of such a solution.
I am curious about the potential market for a solution that would integrate with Dynamics CRM that would perform the following functions:
1) Validate addresses.
2) Convert the U.S. Zipcode to its proper Zip+4 format.
3) Retrieve additional information such as County, Latitude, Longitude, etc.
The Poll is now active at the bottom of the navigation section of this blog ( right side ) so if you have a second, I'd like to know what you think.
Additional comments can be sent via email: mitch at infinite-x dot net.
Have a great weekend.
Mitch
Dynamics CRM 1,318 views -
Assigning vs. Changing Ownership
I noticed something interesting this week while creating workflow rules for various CRM entities related to how CRM treats the assignment of the owner of a record internally.
When you Assign a record to another party within CRM, it actually follows an assignment process internally.
When you change the owner of a record, it physically appears to be the same process as Assigning but is it not. It is merely changing the Owner attribute of the record.
I mention this because if you have workflow rules that are based on the Assign action, and the user merely changes Owner, the Assign workflow rule will not be activated.
Well that's a simple work around: just have the user perform an Assign instead of changing the owner. Good idea, but you can't Assign a record until that record has been saved. Saving is an extra step and it takes time. Users generally don't like extra steps or time wasted. Those are just more things that interfere with their already hectic day. This means they won't do what you ask and that carefully developed solution you hand-crafted is sitting around unused.
So, when creating automated solutions that utilize workflow, pay special attention to how people are using the system. Just to make sure that your solution actually increases their productivity and doesn't decrease their productivity.
Dynamics CRM 1,782 views -
Integrating Customization Changes into CRM 3.0
When you are developing a custom solution for CRM, you will undoubtedly be required to restart part or all of the system to get your changes to take effect. Here are some guidelines that I use to determine the minimum amount of work I must perform to implement my changes.
Entity Customizations
Publish the entity in question or Publish All Entities just to be safe. Usually, a CRM system restart is not required. This includes changes to the CRM schema, and any Form or Attribute Events you may have implemented.
Callouts
Require an IISRESET. Should you need to implement a update to a callout already within the system, performing an IISRESET will unload the callout, allowing you to replace the old DLL with the new version.
Workflow Assemblies
Stopping and Starting the CRM Workflow Service will release any loaded workflow assemblies and allow you to make changes to the workflow config file.
Sitemap
The sitemap is implemented immediately after it has been imported. When you doubt, perform an IISRESET.
ISV.CONFIG Changes
When implementing solutions that involving changing the isv.config file, I always like to perform an IISRESET afterwards, just to be on the safe side. However, I have noticed lately that changes made to Entity toolbars, for example, will be picked up and displayed the next time the Entity record is opened. Still, an IISRESET is always a safe bet.
Custom Web Pages ( linked to CRM via ISV.CONFIG )
If you are making changes to a web page that you have linked to CRM via the isv.config file, you may not always see your changes implemented. This is due to the fact that Internet Explorer caches the pages locally and it will retrieve the cached copy first. To circumvent this issue, simply empty the cache ( Tools, Options, Delete Temporary Files ).
Well, I think that is all I have noticed recently, but if anyone has additional best practices, please let me know.
Customization, Dynamics CRM 2,091 views -
CRM 3.0 SDK: Phone number format example bug fix
In the CRM 3.0 SDK, you will find an example of how to use an Attribute's OnChange event to format a phone number. We found a bug in this code that was quite interesting.
Reproduction Steps:
1) Enter a valid phone number and exit the field so that the OnChange Event fires and the phone number is formatted.
2) Change focus back to the phone number field and erase the entire phone number.
3) Press Tab to leave the field.
At this point you should get a JavaScript error in the OnChange event complaining about the DataValue property.
Cause
It would appear that since the field had once contained valid data, it was not undefined and not null. Both of these two conditions were checked by the code and since both were not true, it continued on with the format routine. The problem seems to be that when we removed the data from the field, the DataBalue property was disposed of, which later caused an error when we attempted to access the contents of the property.
Solution
I added an extra check to make sure the DataValue property was not null before I started my validation and formatting operation. Code is highlighted below:
// Attempt to auto-format basic US phone numbers. This method supports // 7 and 10 digit numbers. Example: (410) 555-1212 // Get the field that fired the event var oField = event.srcElement; // If we have the field and all is well if (typeof(oField) != "undefined" && oField != null) { if (oField.DataValue != null) { // Remove any non-numeric characters var sTmp = oField.DataValue.replace(/[^0-9]/g, ""); // If the number is a length we expect and support, //format the number switch (sTmp.length) { case "4105551212".length: oField.DataValue = "(" + sTmp.substr(0, 3) + ") " + sTmp.substr(3, 3) + "-" + sTmp.substr(6, 4); break; case "5551212".length: oField.DataValue = sTmp.substr(0, 3) + "-" + sTmp.substr(3, 4); break; } } }
Customization, Dynamics CRM 5,051 views -
Working with CRM Emails
Last Thanksgiving, while waiting to have dinner with some friends, I decided to set down and worth through the process of:
1) Sending an email from within CRM, using the CRM web services.
2) Using an email template.
3) And including an attachment to the email.
Pretty simple, I thought. After all, the SDK has some pretty good examples of doing all of these things; all I had to do was to just stitch the pieces together to make it work. Yeah, right.
Three hours later, I finally figured out the last of the issues with my process and will present the solution here today. As mentioned, most of what you're seeing is merely modified versions of the CRM SDK examples.
There is a lot to cover on this topic so the article will be pretty long.
Creating a CRM Email Template
This section is pretty much straight out of the example found in instantiatetemplate.cs in the SDK.
Basically, as with most CRM Entities, you create the properties required by the object, add them to the object instance, then instruct CRM to create the Entity.
1: // STEP 1: CREATE A TEMPLATE
2: // Define the body and subject of the email template
3: // in XML format.
4: string bodyXml =
5: "<?xml version=\"1.0\" ?>"
6: + "<xsl:stylesheet xmlns:xsl="
7: + "\"http://www.w3.org/1999/XSL/Transform\""
8: + " version=\"1.0\">"
9: + "<xsl:output method=\"text\" indent=\"no\"/>"
10: + "<xsl:template match=\"/data\">"
11: + "<![CDATA["
12: + "001: This message is to notify you that a new account "
13: + "has been created."
14: + "]]></xsl:template></xsl:stylesheet>";
15:16: string subjectXml =
17: "<?xml version=\"1.0\" ?>"
18: + "<xsl:stylesheet xmlns:xsl="
19: + "\"http://www.w3.org/1999/XSL/Transform\" "
20: + "version=\"1.0\">"
21: + "<xsl:output method=\"text\" indent=\"no\"/>"
22: + "<xsl:template match=\"/data\">"
23: + "<![CDATA[001: New account notification]]>"
24: + "</xsl:template></xsl:stylesheet>";
25:26: string presentationXml =
27: "<template><text><![CDATA["
28: + "This message is to notify you that a new account "
29: + "has been created."
30: + "]]></text></template>";
31:32: string subjectPresentationXml =
33: "<template><text><![CDATA[New account notification]]>"
34: + "</text></template>";
35:36: // Create the template object and populate
37: // the required properties
38: template template = new template();
39: template.title = "New account";
40: template.body = bodyXml;41: template.subject = subjectXml;42: template.presentationxml = presentationXml;43: template.subjectpresentationxml = subjectPresentationXml;44:45: // The type of Entity this template references.
46: template.templatetypecode = new EntityNameReference();
47: template.templatetypecode.Value = EntityName.account.ToString();48:49: // Is this a personal or system template
50: // true = personal, false = system.
51: template.ispersonal = new CrmBoolean();
52: template.ispersonal.Value = false;
53:54: // specify the owner of the template
55: template.ownerid = new Owner();
56: template.ownerid.Value = user.UserId;57: template.ownerid.type = EntityName.systemuser.ToString();58:59: // Specify who created the template
60: template.createdby = new Lookup();
61: template.createdby.Value = user.UserId;62: template.createdby.type = EntityName.systemuser.ToString();63:64: // Add the template to CRM
65: templateID = myCRMService.Create(template);66:Creating an Email based on a Template
The next step in our process is to actually create an email based on either the template we created in Step 1, or based on an existing template ( using the template's GUID ).
1: // STEP 2: CREATE AN EMAIL BASED ON THE TEMPLATE
2: // Create a template instantiation request.
3: InstantiateTemplateRequest instTemplate =4: new InstantiateTemplateRequest();
5:6: // The email template to use.
7: // You can either specify the ID of an existing
8: // template within your system or utilize the
9: // template you created in step 1.
10: instTemplate.TemplateId =11: new Guid("0AA4C2A0-247B-DB11-8667-00142A05B544");
12: //instTemplate.TemplateId = templateID;
13:14: // The object that this email is regarding.
15: instTemplate.ObjectId = accountID;16: instTemplate.ObjectType = EntityName.account.ToString();17:18: // Execute the request to create an email message
19: // from the template.
20: InstantiateTemplateResponse instTempResp =21: (InstantiateTemplateResponse)myCRMService.Execute(instTemplate);22:23: // Extract the newly created email object from the
24: // BusinessEntities collection.
25: email email =26: (email)27: instTempResp.BusinessEntityCollection.BusinessEntities[0];28:29: // Specify who the email is FROM
30: activityparty from = new activityparty();
31: from.partyid = new Lookup();
32: from.partyid.Value = user.UserId;33: from.partyid.type = EntityName.systemuser.ToString();34:35: // Specify who the email is TO
36: activityparty to = new activityparty();
37: to.partyid = new Lookup();
38: to.partyid.Value = accountID;39: to.partyid.type = EntityName.account.ToString();40:41: // Add the TO and FROM parties to the email
42: email.from = new activityparty[] {from};
43: email.to = new activityparty[] {to};
44:45: // Direction specifies if the email is Outgoing ( true )
46: // or Incoming ( false ).
47: CrmBoolean direction = new CrmBoolean();
48: direction.Value = true;
49: email.directioncode = direction;50:51: // Create the email in CRM
52: Guid emailID = myCRMService.Create(email);Things I learned
I learned a couple of things working with the SDK examples here:
1) The SDK sample, sendemail.cs, uses the following code to send an email
1: // Create an email message.
2: email email = new email();
3: email.torecipients = "someone@example.com";
4: email.subject = "Please disregard";
5: email.description = "This is a test message. Please disregard";
While this is perfectly functional code, in most likelihood, it will cause issues for your users. When you specify the email's recipients via the torecipients property, you haven't actually associated the email to anything or anyone within CRM. This means when you look at your activities, all you see is an email like this:
If you specify ActivityParties for the To and From properties, you will see a normal CRM lookup field with the values you specified displayed, like this:
2) Sendmail.cs also has the following code that it uses to send the email:
1: TargetCreateEmail targetCreate = new TargetCreateEmail();
2: targetCreate.Email = email;3:4: CreateRequest request = new CreateRequest();
5: request.Target = targetCreate;6:7: CreateResponse response = (CreateResponse)service.Execute(request);8: Guid emailID = response.id;Again, functional, but this code does the same thing:
1: // Create the email in CRM
2: Guid emailID = myCRMService.Create(email);Adding an Attachment to an Email
Adding attachments is very similar to any other CRM operation, as long as you understand which objects need to be attached to what objects and where.
1: // STEP 3: Add an attachment
2:3: // Create a new Attachment object.
4: // Attach it to the email we created in Step 2.
5: activitymimeattachment attachment =6: new activitymimeattachment();
7: attachment.activityid = new Lookup();
8: attachment.activityid.Value = emailID;9: attachment.activityid.type = EntityName.email.ToString();10:11: // This is the first attachment.
12: attachment.attachmentnumber = new CrmNumber();
13: attachment.attachmentnumber.Value = 1;14:15: // Create the Accountment in CRM.
16: Guid attachmentId = myCRMService.Create(attachment);17:18: // variable to hold our attachment data.
19: string data;
20:21: // Create an instance of StreamReader to read text from a file.
22: // The using statement also closes the StreamReader.
23: using (StreamReader sr = new StreamReader("temp.txt"))
24: {25: data = sr.ReadToEnd();26: }27:28: // Encode the data using base64.
29: byte[] byteData = new byte[data.Length];
30: byteData = System.Text.Encoding.UTF8.GetBytes(data);31: string encodedData = System.Convert.ToBase64String(byteData);
32:33: // Create the request object to upload the file to CRM.
34: UploadFromBase64DataActivityMimeAttachmentRequest upload =35: new UploadFromBase64DataActivityMimeAttachmentRequest();
36:37: // Set the properties of the request object.
38: upload.ActivityMimeAttachmentId = attachmentId;39: upload.FileName = "temp.txt";
40: upload.MimeType = "text/plain";
41: upload.Base64Data = encodedData;42:43: // Upload the file to CRM.
44: UploadFromBase64DataActivityMimeAttachmentResponse uploaded =45: (UploadFromBase64DataActivityMimeAttachmentResponse)46: myCRMService.Execute(upload);47:48: bool success2 = false;
49:50: activitymimeattachment attachmentCheck =51: (activitymimeattachment)myCRMService.Retrieve(52: EntityName.activitymimeattachment.ToString(),53: attachmentId,54: new AllColumns());
55:56: if (attachmentCheck.filename != null)
57: {58: success2 = true;
59: }Things I learned
1) You need to create an attachment object then assign the activityid for the attachment to the ID of the email we created in Step 2.
2) Each attachment added must have a unique number that identifies it, and I'm assuming, its location within the list of attachments. To my knowledge, CRM doesn't automatically assign values to the attachment number property. I think that I did a little research into the ability to determine the number of attachments for a specific CRM Entity, I don't recall the results of my research. It would be nice to have a GetNextAttachmentNumber function or something like it, built into the CRM API.
3) Files must be Uploaded into CRM before they can actually be available. As part of the Upload object creation process, you instruct CRM where the uploaded file should reside using the ActivityMimeAttachmentId property as seen on line 38.
Sending the Email
Finally we get to the actual sending email part, which as you can see from the code, is actually the easiest part of the whole operation:
1: // STEP 4: Send the email.
2: SendEmailRequest req = new SendEmailRequest();
3:4: // ID of the email
5: req.EmailId = emailID;6:7: // You may specify your own tracking token
8: // or if blank, CRM will add the normal tracking token
9: req.TrackingToken = string.Empty;
10:11: // When IssueSend is set to true, the email will be sent.
12: // Otherwise, it will be placed in Activities for later
13: // sending.
14: req.IssueSend = true;
15:16: // Send the email message.
17: SendEmailResponse res =18: (SendEmailResponse)myCRMService.Execute(req);19:20: #region check success
21:22: if(!res.Subject.Equals(""))
23: {24: success = true;
25: }26:27: #endregion
28:Things I learned
As you can see from the comments within the code, there are a few items of interest here.
1) You can provide your own TrackingToken, or use the one CRM provides. However, you CAN NOT leave that property set to a null value. That will result in an error. Just make it blank ( which of course, is not null ).
2) If IssueSend is set to True, it will send the email. Otherwise, it will just place it into your Activities list.
Conclusion
Well, that's about it. I originally started this process because I needed to create a Workflow Assembly that would allow me to specify not only a pre-defined email template but to also allow me to attach a file to the email. It took me a whole lot longer that I expected because there were a lot more little "gotchas" than I expected. Hopefully, I've saved you a bit of time should you need to create a similar solution.
If anyone has any questions or comments, please let me know.
Customization, Dynamics CRM 7,397 views




