Microsoft CRM Customizations
February 06, 2012
XRM 2011 JavaScript: Another 101 Lesson in Microsoft Dynamics CRM 2011
When you’re learning JavaScript, you’ll often come across the need to do something to every field on the form. When you do, it’s important to have efficient code to make such widespread changes so the user doesn’t have to wait for the JavaScript to finish. This lesson consists of several parts, some of which you’ll probably already know but hopefully there are some things in here for everyone.
Setting up our Environment
First thing first, open your CRM 2011 development environment and browse to an account form. Once the account form is open, hit F12 on your keyboard. A window should popup that looks like this:
If instead, you see the window in the bottom of your browser page. Click the little “Unpin” button shown here:
This is IE Developer Tools and if you haven’t used this in the past, then I hope you enjoy not having to save/publish/refresh/test nearly as often. To start, we’re going to see how many fields we have on the form. When inside of the CRM form, we’d use something like:
Xrm.Page.data.entity.attributes.getLength()
But when we’re inside Developer Tools, we actually have a different context. So we need to click on the “Console” tab and execute:
frames[0].Xrm.Page.data.entity.attributes.getLength()
Anything we want to execute on the form, we can do by simply prepending “frames[0].” in Developer Tools to get the proper context. This allows us to write and test code instantaneously, which is a huge timesaver.
Iterating Fields
Now that we are playing in our Developer Tools sandbox, let’s iterate through some fields to see which fields are required. To use multiple lines, click the double up arrow in the bottom right corner.
Here’s some slightly modified code from the SDK that we can just copy and paste into our Console:
var message = "The following fields are required:\n";
frames[0].Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
if (attribute.getRequiredLevel() == "required") {
message += " \u2219 " + attribute.getName() + "\n";
}
});
alert(message);
Note: I can’t stress enough how awesome the SDK is. Everyone I’ve met who partakes in the SDK creation, maintenance, etc. have been top notch people. If you aren’t using the SDK, you are missing out in tons of awesome tips, features and customizations.
You’ll notice that the above code quickly goes through each field and checks to see if the field is required. We could also check if the field is dirty (meaning the user changed the value), if the field is enabled, or whatever. It’s just a simple way to make mass changes quickly.
About “forEach”
For the experienced developers, you may be thinking did IE get ForEach? No, but the CRM developers were gracious enough to add this into the Xrm variable. For those of you unfamiliar, this was an old Mozilla thing that made life a lot easier when dealing with arrays. Expanding further, the Xrm forEach approach is faster than most arrays people would generate using for loops. You can write some JScript that is faster and I’ll get into that one day, but at this point you are better off using the forEach approach.
I’ve said it several times, but I really like the Xrm object due to its attention to performance. In fact the method here is about a hundred times faster than iterating the crmForm.all object. I’ll back that up with some pretty graphs and such in a later post.
Checking for Changed (Dirty) Fields
var message = "The following fields are dirty:\n";
frames[0].Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
if (attribute.getIsDirty()) {
message += " \u2219 " + attribute.getName() + "\n";
}
});
alert(message);
Notice how easy it is to change what we are checking for. Whether we are looking for a specific value, dirty fields, required fields, or whatever, doing so inside the forEach function makes it fast and easy.
Back to the Web Resource
To go from the Developer Tools back to the Web Resources, we need to replace all of the “frames[0].” references with blanks. IE Developer Tools is a great way to create and test our code without affecting anyone else and there is a lot more to developer tools than the console. I hope you enjoy!
Posted by Paul Way on February 06, 2012 at 08:25 AM in CRM Best Practices, CRM Development, CRM Javascript, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (1) | TrackBack (0)
February 01, 2012
Using a No-Operation Plugin to Examine the IPluginExecutionContext in Microsoft Dynamics CRM
If you have ever had the privilege of developing a plugin for CRM 2011 you know that one of the most important aspects of plugin development is understanding what is available in the context that is passed to the plugin by CRM. After creating a few plugins you generally have a good idea of what is available for the common operations of CRM such as Create, Retrieve, Update, and Delete, but some of the more uncommon request to the OrganizationService may still be cloudy.
One of the methods that I use to understand what is available in the IPluginExecutionContext of a plugin is to use a no-operation plugin registered for the message that I will be handling that will trace the context out to the event log. This is a great way to see a list of all of the data contained in the context’s InputParameters, OutputParameters, SharedVariables, and images. Once you have a trace of what is available in the context of the plugin, the plugin development seems like a much less arduous task.
Since tracing the plugin context is something that I commonly do when developing plugins for messages that I am not familiar with, I thought I would share the no-operation plugin that I use to trace the context for a plugin. The plugin that I use can be registered for any message, entity, or stage and it will trace the output to the event log on the server with a source of MSCRMServices. Simply register the plugin for the desired message, perform an action in CRM that will trigger the plugin, and voila, an event log entry will show up in the event log on the server.
Since the plugin requires access to the event log it cannot be used in a sandboxed environment and therefore it will not work online, but that shouldn’t be a big hurdle as I am sure if you are developing plugins you will have access to a test environment in which you can register the plugin for testing purposes.
You can download the plugin here. Good luck with your plugin and hopefully, the no-op plugin will have you on your way to a speedy plugin development.
Posted by Nick Doriot on February 01, 2012 at 09:48 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (0) | TrackBack (0)
January 30, 2012
Removing Query Data Cached by the OrganizationServiceContext in Microsoft Dynamics CRM
One of the great set of tools provided by the CRM 2011 SDK are the SDK Extensions which provide the OrganizationServiceContext for accessing data in CRM. The OrganizationServiceContext provides a way to access CRM and provides features such as change management, exposing an IQueryable interface, implementing a LINQ query provider, and providing caching services. It is the caching services that I would like to take a look at today.
Most of the time, the caching provided by the OrganizationServiceContext is exactly what you are looking for as a developer. It provides faster access to the data exposed by the OrganizationService and reduces the load on the network. However, there are times, when the retrieving data that you would like to have a current set of data rather than a set of cached data. There are a couple of methods that can be employed if you would like to retrieve a set of current data using the OrganizationServiceContext. One of these is to modify the configuration in the web.config or app.config file that is used to specify how the client context is initialized. Another is to instantiate the OrganizationServiceContext yourself and pass an instance of a non-caching OrganizationService when constructing the context.
While these methods will work and have their purposes, most of the time I would like to use a caching instance of the OrganizationService but still be able to control the caching so that I can retrieve a current set of data if necessary (I like to have my cake and eat it too). The great news is that it is possible to control the data caching of the OrganizationServiceContext to some extent if you understand the structure of the context class that is being used.
If you are using early binding and the CrmSvcUtil.exe to generate an OrganizationServiceContext to be used when accessing data in CRM, the class that gets generated derives from the CrmOrganizationServiceContext. If you are using late binding you will more than likely be using the CrmOrganizationServiceContext directly. The CrmOrganizationServiceContext implements the IOrganizationServiceContainer interface that exposes a Service property. If you have not specified a type for the service to be used by the context, this object will be an instance of the CachedOrganizationService. In addition, the CachedOrganizationService class exposes a Cache property that is an IOrganizationServiceCache instance. It is this instance of the IOrganizationServiceCache that exposes the methods that allow items to be removed from the cached used by the context.
Now that we have an understanding of the structure of the context it is simple to write an extension method that will remove cached data items. The following snippet of code can be used to remove entity data from the set of data cached by the context.
1: public void RemoveCachedData(OrganizationServiceContext context, string entityLogicalName, Guid? id) {
2:
3: var serviceContainer = context as IOrganizationServiceContainer;
4: if (serviceContainer == null) {
5: return;
6: }
7:
8: var cachedOrgService = serviceContainer.Service as CachedOrganizationService;
9: if (cachedOrgService == null) {
10: return;
11: }
12:
13: var orgServiceCache = cachedOrgService.Cache as IOrganizationServiceCache;
14: if (orgServiceCache == null) {
15: return;
16: }
17:
18: orgServiceCache.Remove(entityLogicalName, id);
19: }
As you can see, we simply need to cast the context to an IOrganizationServiceContainer and drill down through the object until we get at the actual OrganizationServiceCache. Once we have an instance of the OrganizationServiceCache we can use one of the Remove methods to remove the cached data. There are several other overloaded Remove methods that allow data to be removed and I have only shown one of them that will allow data to be removed for a specific entity. In the snippet I have shown, if the id argument is not supplied, any cached data for the specified entity will be removed.
Now all that needs to be done is call the method to remove the cached data before executing your query to retrieve data. If you would like to remove other pieces of cached data you can browse the CRM 2011 SDK and have a look at the Remove methods exposed by the OrganizationServiceCache class.
Posted by Nick Doriot on January 30, 2012 at 09:46 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (0) | TrackBack (0)
January 13, 2012
XRM 2011 - Microsoft Dynamics CRM 2011 Style Buttons
Adding a button to a Form is a great way to add additional functionality right where the user is already focused. Today we are looking at a few different approaches to adding a button. First, we’re looking at handling the upgrade for those of you who have already used the 4.0 button inside of 2011 or if you had a 4.0 environment and are upgrading. If you have a clean 2011 environment, then feel free to skip down to the “From Scratch” section.
There is already some code floating around to create a 4.0 style button inside of CRM 2011. The button we’re creating will instead create a CRM 2011 style button (i.e. “Example Button”).
The CRM 2011 button starts gray and then when the user hovers over it, the button will look like:
Creating our Web Resource
First,name your Web Resource whatever you’d like. In my case, I’m planning to re-use it in various ways, so I’m throwing it in our JS root directory.
1: // CRM 2011 Style Button
2: // Creates a button from a form field
3: // Paul Way - 1/3/2012
4: function ConvertToButton(fldName, btnLabel, btnWidth, evt){
5: var btn = '<button id="btn_' + fldName + '" ' +
6: ' style="width:' + btnWidth + '" ' +
7: ' class="ms-crm-Button" ' +
8: ' onmouseover="Mscrm.ButtonUtils.hoverOn(this);" ' +
9: ' onmouseout="Mscrm.ButtonUtils.hoverOff(this);" ' +
10: '>' + btnLabel + '</button>';
11:
12: var ctrl = Xrm.Page.ui.controls.get(fldName)._control;
13:
14: // Add the new button
15: ctrl.get_element().innerHTML += btn;
16:
17: // Hide the textbox
18: ctrl.get_element().firstChild.style.display = 'none';
19:
20: // Hide the label (optional)
21: Xrm.Page.ui.controls.get('pager').setLabel('');
22:
23: // Add Event to the newly created button
24: ctrl.get_element().childNodes[1].attachEvent('onclick', evt);
25:
26: }
Modifying our Form
To use this new function, you’ll first need to add the newly created web resource to the form. You’ll also want to have a separate JS web resource to call the ConvertToButton function. In my case, I already have a web resource for contact specific JavaScript. If I didn’t, I would need to create a new web resource and then place my contact specific code there. Here’s an example screenshot:
By having two web resources for this, you’ll have only one version of your ConvertToButton function throughout your CRM 2011 environment. Let’s say down the road you’ll need to update the function to change the label to “Button”, then you only need to modify one file. In CRM 4.0, you probably had a lot of duplicate code. With CRM 2011, your browser can cache the JavaScript for better performance and it is easier to maintain when organized appropriately.
In our other web resource, the one we setup with the OnLoad, we need the following code.
1: function contactsOnLoad(){
2: convertToButton('pager', 'Example Button', '150px', function(){alert("test")});
3: }
Why is this so different then the 4.0 code?
If you’ve used the CRM 4.0 code, you’ll notice the code here is a lot shorter. It’s actually pretty different as well.
- For one, instead of modifying the input element we are actually creating a button HTML element. This just means that we can now use about any kind of field instead of just pure textboxes. Not a huge deal, but opens up the email address 3 and other attributes.
- Both are really unsupported but the other uses the deprecated crmForm.all.
- Finally, the button style was meant for 4.0 whereas now we have the 2011 look-n-feel.
From Scratch
So far we’ve mainly focused on if you already were using the 4.0 code and were upgrading. But what if you don’t have the existing code structure? I’d argue against creating a new attribute just to have a button. When adding a new attribute, you are also adding the attribute to the underlying SQL tables and views. Using an existing field isn’t really a great option either because there is always the chance you will need the field or it will overlap with an application from the MS marketplace.
Instead, I’d recommend creating a button place holder web resource and then embedding the web resource on the page. The web resource is just a JPG image like this:
We then place the image wherever we want on the form. Make sure to set the formatting to one column and one row.
Finally, we need a little bit of code added to the /js/formButton.js web resource:
1: // CRM 2011 Style Button
2: // Creates a button from a form field
3: // Paul Way - 1/3/2012
4:
5: function convertWebResourceToButton(fldName, btnLabel, btnWidth, leftMargin, evt){
6: var btn = '<button id="btn_' + fldName + '" ' +
7: ' style="margin-left:' + leftMargin + ';width:' + btnWidth + ';" ' +
8: ' class="ms-crm-Button" ' +
9: ' onmouseover="Mscrm.ButtonUtils.hoverOn(this);" ' +
10: ' onmouseout="Mscrm.ButtonUtils.hoverOff(this);" ' +
11: '>' + btnLabel + '</button>';
12:
13: var ctrl = Xrm.Page.ui.controls.get(fldName)._control.get_element().childNodes[1];
14:
15: // Replace image with buttom
16: ctrl.innerHTML = btn;
17:
18: // Add Event to the newly created button
19: ctrl.firstChild.attachEvent('onclick', evt);
20:
21: }
And as for our OnLoad function, we’ll need to use this instead:
1: function contactsOnLoad(){
2: convertWebResourceToButton('WebResource_btnProfInfo', 'Another Approach', '150px', '119px', function(){alert("test2")});
3: }
Notice that this will require an additional parameter for the left margin.
Summary
As you can see, there are several different approaches to adding a button onto the CRM form for 2011. With 2011, I’d opt for using the button place holder image method. No matter which approach you take, the end goal is enhancing the user’s experience. I hope you enjoy!
Posted by Paul Way on January 13, 2012 at 08:53 AM in CRM Best Practices, CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
January 12, 2012
CRM 2011 - iFrames & Saving
After spending time on several of the CRM forums, I noticed a few people having trouble with iFrames and saving data inside of the iFrame. While a forum post is a little difficult to write a full response, I wanted to share some insight on capturing the form save event to then trigger a save event inside of your iFrame.
From a business case, this is a fantastic way to tie multiple systems together. If all you are doing is syncing data, then look first at a plugin or scribe. However, if you are looking for the user to interact with multiple systems simultaneously, then an iFrame is a wonderful way to integrated with an existing system. A good example might be where CRM is only storing the summary information and the iFrame contains the details. The user may update the details which should save both the CRM record and the details of the web site.
Prerequisites
First, let’s assume you have an iFrame inside an entity.
Secondly, we need to uncheck the “Restrict cross-frame scripting” check box on the iFrame properties.
Lastly, it’s a good idea to make sure our iFrame is using HTTPS to prevent mixed mode security warnings.
Talking Directly
If you try to talk directly to the iFrame via something like:
Xrm.Page.ui.controls.get('IFRAME_opp').getObject().contentWindow.document
Unless you are On-Premise, you may encounter the “Access is Denied” warning. This will show itself when dealing with URLs on a different domain (or subdomain). Cross domain scripting is a security feature within the browser. Now an easy fix is to disable the browser settings, but this isn’t a good solution.
Messaging
Instead of talking directly, we need send a messages to our iFrame. To send a message, we’d do something like:
Xrm.Page.ui.controls.get('IFRAME_opp').getObject().contentWindow.postMessage('test', "*")
To receive the message on the iFrame, you need to listen for the message event. Let’s take a look at the JavaScript on our example iFrame page:
1: function receiveMessage(e) {2:3: if (e.origin == 'https://way.crm.dynamics.com'){4:5: if (e.data == 'test'){6: document.getElementById('msgBox').innerHTML = 'testing...<br />' + e.origin + '<br />' + e.data;7: }8:9: if (e.data == 'save'){10: saveForm();11: }12: }13:14: }15:16: saveForm = function() {17: document.getElementById('msgBox').innerHTML = 'saving...';18: }19:20: window.attachEvent("onmessage", receiveMessage);
It is very important to check the origin and the message if you are dealing with publicly facing sites.
If the idea of messaging is new to you, here are some additional resources:
- http://msdn.microsoft.com/en-us/library/cc197015(VS.85).aspx
- http://msdn.microsoft.com/en-us/library/cc511311(v=vs.85).aspx
The OnSave
To initiate the message from the CRM 2011 form, you need to register an OnSave event from within the “Form Properties”.
How about .NET Pages?
If your iFrame is an ASPX page, all of this still applies. The only difference is that you want to have an ASP.Net button on your page to call the server side code. Instead of the saveForm event, simply call the click event of the button.
When dealing with server side saves though, you can either post a message back to say “I’m done” which will then complete the CRM save or you can put a set timeout in the onSave on the CRM side.
Conclusion
Hopefully you have seen how easy it is to integrate with another web site while inside of CRM 2011. If you have had problems with iFrames in the past, I really encourage you to use iFrames for your integration needs as they can really enhance your users experience and can allow for improved data across systems. I hope you enjoy!
Posted by Paul Way on January 12, 2012 at 01:22 PM in CRM Javascript, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Online, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (2) | TrackBack (0)
January 03, 2012
Microsoft Dynamics CRM 2011 – Pre-Populated Emails (Alternative Approach)
Recently, we came across the need for a single-click email feature on the ribbon of a custom entity. The idea was to be able to share CRM data as quickly and easily as possible. Another interesting caveat was the client wanted to have the email come directly from Outlook.
For those of you old enough to have used the internet before the iPad, you may recall that fancy-dancy “mailto” feature. (Granted I’m only 29 but my Omron body age says I’m 50 ).
Back to the point at hand though, CRM already has a few built in email features. Our situation calls for something a little different.
What’s Installed By Default
CRM already has the feature of sending a link to the CRM entity you’re working with.
CRM also has a really nice Email Template feature.
Which can automatically mail-merge information pertaining to the entity to eliminate any of that “automated” feel.
What We Need
Our situation is somewhat unique, but really we want both features combined. We want to open a new message like the link feature: inside outlook and with one click. However, we need more than just the link. We want to have a brief summary of the call report with a link to it.
Here’s an example email of what we’d like to send:
Adding the Button
The first thing we need to do is add the button to the ribbon like so:
If you are unfamiliar with the ribbon, Microsoft offers some really nice walkthroughs. I’d start with http://msdn.microsoft.com/en-us/library/gg334341.aspx.
For our situation, the XML that matters is our Action
<Actions> <JavaScriptFunction Library="$webresource:cei_example.js" FunctionName="emailContact" /></Actions>
Adding our JavaScript
At this point, the button should be on the form. From here, we need to add the JavaScript to open our populated email message.
To do this, add to the Form OnLoad events the following function:
emailContact = function() {
if (Xrm.Page.data.entity.getId() != null) {
var sRptID = Xrm.Page.data.entity.getId().replace('{', '').replace('}', '');
var sLink = "http://paul.customereffective.com/userdefined/edit.aspx?id=" + sRptID + "&etc=10011";var sSubject = 'Contact - ' + Xrm.Page.getAttribute("firstname").getValue() + ' ' + Xrm.Page.getAttribute("lastname").getValue();
var sCustomer = '';
if (Xrm.Page.getAttribute("parentcustomerid").getValue().length > 0){
sCustomer = Xrm.Page.getAttribute("parentcustomerid").getValue()[0].name;
}
var sBody = '---\n\nCustomer:\t' + sCustomer + '\n\n' + sLink + '\n\n---';
var sMailTo = 'mailto:?subject=' + escape(sSubject) +
'&body=' + escape(sBody);parent.location=sMailTo;
} else {
alert('You must save the contact before emailing.');
}
}
Note: If you change the name of the function, make sure it matches the name inside your Ribbon XML.
What about Window.Open
If you use Window.Open to trigger the email (or OpenStdWin), the user will get a blank IE page inside of Outlook. By using the parent.location for your mailto, you’ll have a consistent user experience across IE and Outlook.
Seeing the Final Results
Now whenever a user wants to share a call report, they can easily hit the “Email Call Report” button. Add any of the fields you want.
For our needs I ended up with the following; however, this technique can add whatever field you’d like.
Well this isn’t a tool in the toolbox I’d use daily, it can be valuable when the situation calls for it. I hope you enjoy!
Posted by Paul Way on January 03, 2012 at 01:58 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM for Outlook, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
December 23, 2011
Dynamically Generating URLs for Cached Web Resources for Microsoft Dynamics CRM
CRM 2011 supports several methods for accessing web resources. These includes using the $webresource: directive when referencing web resources from the site map or ribbon, a relative URL, or an absolute URL. While this allows a web resource to be accessed in virtually any situation, if a scenario arises in which an absolute URL must be used, the benefit and speed of retrieving the cached web resource is lost. The reason for this has to do with the caching mechanism used by CRM for web resources. Take the following URL that could be generated by CRM when using the $webresource: directive or when a web resource is included on a CRM form as an example.
https://yourorgname.crm.dynamics.com/%7B634594655730000000%7D/WebResources/cei_/test.htm
Notice the magic number that is highlighted in the example URL. CRM automatically inserts this token into the URL and the CRM 2011 SDK indicates that this magic number is a GUID value that ensures the latest cached version of the requested web resource is used. If you use a tool like Fiddler to examine the response returned by the GET request for the web resource, you will notice that it has caching headers that set it to be cached for one year. If the magic GUID value is removed from the URL so that the URL would resemble an absolute URL to the web resource, the response returned by the GET request for the web resource, depending upon your version of CRM, will be a non cached version or a version that is cached for a short amount time such as 24 hours.
This caching functionality provided by CRM is great, but what if you need to dynamically generate the URL for a large web resource? It would be preferable to access the cached version, but in order to do that you need the magic GUID value. Fortunately, the magic GUID value is not magic at all, and not actually a GUID. The caching token inserted into the URL for the web resource by CRM is merely a timestamp that represents the modified time of the last web resource to be modified. The timestamp is represented as ticks in UTC time as generated by the .NET Framework.
Now in order to get the URL for a web resource, it is merely necessary to generate this timestamp. The example function below shows exactly how to generate the timestamp for the caching token using a quick call to the REST service in CRM and a simple conversion from the JavaScript Date object to the timestamp value.
1: function getWebResourceUrl(webResourceName) {
2:
3: var dotNetMillisecondsAt_1970_01_01 = 62135596800000,
4: ticksPerMillisecond = 10000,
5: lastWebResourceModifiedOn = null,
6: webResourceCachingToken = null,
7: webResourceUrl = "/WebResources/" + webResourceName;
8:
9: // Retrieve the modified date of the last web resource to be modified
10: $.ajax({
11: async: false,
12: beforeSend: function (xhr, settings) {
13: xhr.setRequestHeader("Accept", "application/json");
14: },
15: contentType: "application/json; charset=utf-8",
16: datatype: "json",
17: global: false,
18: success: function (data, textStatus, xhr) {
19:
20: // Extract the modified date from the results
21: lastWebResourceModifiedOn = +(/\/Date\((\d*)\)\//.exec(data.d.results[0].ModifiedOn)[1]);
22:
23: // Convert the JavaScript Date value to the caching token value
24: webResourceCachingToken = (lastWebResourceModifiedOn + dotNetMillisecondsAt_1970_01_01) * ticksPerMillisecond;
25: },
26: type: "GET",
27: url: Xrm.Page.context.prependOrgName("/XRMServices/2011/OrganizationData.svc/WebResourceSet()?$select=ModifiedOn&$orderby=ModifiedOn%20desc&$top=1")
28: });
29:
30: // If we have a caching token value then prepend it to the current URL.
31: if (webResourceCachingToken !== null) {
32: webResourceUrl = "/%7B" + webResourceCachingToken + "%7D" + webResourceUrl;
33: }
34:
35: // Return the URL to the web resource prepended with the organization name if necessary
36: return Xrm.Page.context.getServerUrl().replace(/\/$/,"") + Xrm.Page.context.prependOrgName(webResourceUrl);
37: }
Notice the function takes the name of a web resource, e.g. cei_/mypage.htm, and prepends it with the caching token, organization name if necessary, and the server URL. Now this function can be used to dynamically generate the URL for a web resource and still retrieve the cached web resource. Of course, there could be some improvements made, such as wrapping the call to retrieve the caching token in a closure so that it is only retrieved the first time a URL is generated, but I will leave that up to you.
**Note, that generating a URL in this manner is not supported by Microsoft and the caching mechanism used by CRM for web resources could be changed in the future.
Posted by Nick Doriot on December 23, 2011 at 09:28 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
December 16, 2011
Setting Lookup Field Values on Microsoft Dynamics CRM Forms with Silverlight
Recently, I have found myself developing a lot of Silverlight customizations for CRM 2011. If you happen to find yourself in the realm of Silverlight and CRM you will more than likely have the need to interact with the CRM form in context. The tool that Silverlight provides for interacting with the hosting web page is the HTML Bridge. The types and methods exposed by the HTML Bridge allow access to the hosting pages DOM and JavaScript objects. By utilizing the types and methods of the HTML Bridge it is possible to access the Xrm JavaScript object of the CRM SDK to set field values so let’s take a look at how the HTML Bridge can be leverages to do so.
There are a couple of different methods that can be used when accessing the Xrm object of CRM. One is to create an object model around the Xrm object that mirrors the Xrm object in JavaScript and the other is to use a dynamic object in Silverlight. I am going to use a dynamic object in this instance as it requires much less code, however, the downside is that there is not any compile-time type checking and errors may not be discovered until runtime.
1: // Get a reference to the Xrm object
2: dynamic Xrm = HtmlPage.Window.GetProperty("Xrm");
3:
4: // Set a string field value
5: Xrm.Page.getAttribute("cei_stringfield").setValue("Hello World!");
6:
7: // Set a floating point field value
8: Xrm.Page.getAttribute("cei_floatfield").setValue(3.14159);
Notice in the previous example I simply use the HtmlPage.Window object to get a reference to the Xrm property on the hosting page. Also, notice that the local Xrm object is declared as a dynamic variable. Declaring the local Xrm object as a dynamic object allows the object to bypass static type checking. At this point, it is simply a matter of using the Xrm object just as if it were being used from JavaScript. In line 5 of the previous code snippet I have done just that. A reference to the cei_stringfield attribute is obtained and the value is set to a “Hello World!”. In line 8, the same approach is used to set a numeric field.
After seeing how the previous snippet of code works, you may think, as I did, that setting a lookup field’s value could be done using the same technique with an anonymous array object that mimics a lookup fields value as the argument to the setValue function. However, calling the setValue function for a lookup field will not work from within Silverlight due to the way the HTML Bridge marshals managed objects to JavaScript. Fortunately, there is another method that can be used, although it is not quite as elegant. The following snippet shows how a lookup field can be set from within Silverlight.
1: private void SilverlightSetLookupValue(string fieldName, Guid? id, string entityLogicalName, string name)
2: {
3: // Define eval statements for setting lookup to a value and null
4: string setLookupJscript = @"Xrm.Page.getAttribute(""{0}"").setValue([ {{ id: ""{1:B}"", typename: ""{2}"", name: ""{3}"" }}])";
5: string setLookupToNullJscript = @"Xrm.Page.getAttribute(""{0}"").setValue(null)";
6: string evalStatement = null;
7:
8: // Set the statement to be evaluated based upon the value of the id argument
9: if (id.GetValueOrDefault().Equals(Guid.Empty))
10: {
11: // Setting the lookup to null
12: evalStatement = string.Format(setLookupToNullJscript, fieldName);
13: }
14: else
15: {
16: // Setting the lookup to a value
17: evalStatement = string.Format(setLookupJscript, fieldName, id, entityLogicalName, name);
18: }
19:
20: // Set the lookup
21: HtmlPage.Window.Eval(evalStatement);
22: }
That’s right, I broke out the big dog, the EVIL EVAL function. As I said previously, it is not the most elegant solution, however, sometimes the heavy hitters like the eval function need to be dusted off and it works beautifully. As you can see, the method in the snippet builds the JavaScript necessary to set a lookup field dynamically as a string and then evaluates the string on the hosting page using the HtmlPage.Window.Eval method within Silverlight.
Posted by Nick Doriot on December 16, 2011 at 09:55 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
December 08, 2011
Using the CRM 2011 REST Services and Deep Insert to Create Activities
One of the nice features of CRM 2011 is the REST based OrganizationData service. Lately, I have found myself using the REST service more often as it provides a simpler and cleaner method of accessing data from client side JScript. The REST service also provides the ability to do deep inserts of entity and related records. This capability can be leveraged to provide an elegant solution to creating activity records using client side JScript.
In my particular use case, I needed to create a new email activity from the currently logged in user with the regarding record set to a new custom entity record. It would have been possible to issue a couple of calls to the REST service that created the regarding record and then the email, but it is just as easy to create the email and related records in one call avoiding the need to clean up any records should one of the calls fail.
The first thing that needs to be done is to create the activity party record for the from field on the email. The activity party is a separate entity that stores all of the parties related to an activity, i.e. sender, recipient, etc. The activity party data needs to be specified as an array of activity party entities. The following snippet creates an activity party record for the from field on the email.
1: // Create the activity party records
2: var emailPartyEntities [{
3: PartyId: { LogicalName: "systemuser", Id: Xrm.Page.context.getUserId() },
4: ParticipationTypeMask: { Value: 1 }
5: }];
As I mentioned earlier, I also needed to create a custom entity record that will be set as the regarding on the created email. In my case, this custom entity is used to track an email thread. Since this is a N:1 relationship for the email entity, the record created does not need to be stored in an array and is merely an object with properties specifying the fields of my custom entity.
1: // Build the activity thread entity
2: var activityThreadEntity = {
3: cei_Department: { Value: 29669000 },
4: cei_Subject: "Activity Thread for Account",
5: };
Finally, I need to build the email entity that will be passed to the REST service during the create request. In order for the deep insert to work, the activity party array and the activity thread object created earlier will need to be set to properties on the created object that specify the relationships to the related entities as shown below.
1: // Build the email record
2: var emailEntity = {
3: Subject: "Test Email",
4: cei_activitythread_Emails: activityThreadEntity,
5: email_activity_parties: emailPartyEntities,
6: };
In the previous snippet, notice that the activityThreadEntity object and the emailPartyEntities array are set to their corresponding relationships, i.e. cei_activitythread_Emails is the name of the N:1 relationship for my custom entity to the regarding field on the email entity, and email_activity_parties is the name of the 1:N relationship from the email entity to the activity party entity.
Now that all of the required objects have been created we can issue the call to the REST service to create the email and related records. The following snippet utilizes a jQuery plugin from our custom JScript library. It is merely doing a POST request to the REST service with the data for the POST being the JSON serialized emailEntity object that was created.
1: // Create the email by POSTing the data to the REST service
2: var createResult = $.xrm.orgData.buildRequest().
3: async(false).
4: dataset("Email").
5: data(emailEntity).
6: create();
7: if (createResult.errorExists) {
8: alert("There was an error creating the email.");
9: } else {
10: alert("Success!");
11: }
That’s it! It really is that simple. The email, activity party, and regarding records were all created in a single call.
In this example, I utilized the deep insert capabilities of the REST services to create an email, but the deep insert feature can be used on any entity in order to create multiple records in a single call. The biggest hurdle that you will probably run into is determining whether to create the related records as JScript objects or arrays of objects. Just remember that 1:N relationships require and array of objects and that N:1 relationships require a single object.
Posted by Nick Doriot on December 08, 2011 at 08:05 AM in Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (1) | TrackBack (0)
December 01, 2011
CRM 2011–Excessive Sub-Gridding
Sub-grids enhance the user experience by conveniently providing associated information. Within a sub-grid the user can easily view, create, or modify a related entity. By default, Microsoft will auto-populate up to four sub-grids.
One thing to note however is that if the form contains more than four sub-grids, then sub-grids 5+ will contain a message inside the sub-grid “To load x records, click here”. To populate the sub-grid, the user has to click the refresh button or the link.
CRM 2011 does this intentionally to make sure the User is presented with the Form as quickly as possible. Each sub-grid is another Fetch call to the database, so reducing those inherently speeds up the form.
Now what if you only need a few more sub-grids and you are ok with the potential performance trade-off? Well, let’s add a little JavaScript to auto load all of our sub-grids that didn’t auto load already:
function getLinksWithClassName(classname) {
var bdy = document.getElementsByTagName("body")[0];
var els = [];
var re = new RegExp('\\b' + classname + '\\b');
var lnks = bdy.getElementsByTagName("a");for(var i=0,j=lnks.length; i<j; i++)
if(re.test(lnks[i].className))
els.push(lnks[i]);return els;
}
var lnksUnloaded = getLinksWithClassName('ms-crm-List-LoadOnDemand');for (var i = 0; i < lnksUnloaded.length; i++){
lnksUnloaded[i].click();
}
Now adding the bottom four lines to our OnLoad event will cause all of our sub-grids to auto-load. Hope you enjoy!
Posted by Paul Way on December 01, 2011 at 08:00 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (3) | TrackBack (0)
November 01, 2011
Controlling Tabs and Sections with JScript in Microsoft Dynamics CRM 2011
The options for managing Microsoft Dynamics CRM forms dynamically has increased tremendously in CRM 2011 – we can now, more than ever, tailor the look of a form to the specific type of record displayed.
Here are 2 functions I use to manage the form at runtime to show or hide sections or tabs based on data in the form.
The first function, “toggleTabDisplayState” hides or shows a tab and can show it collapsed or expanded. Call the function by passing in the ‘name’ of the tab, desired ‘display state’ [either “expanded” or “collapsed”] and whether the tab should be visible [ true or false ].
The second function, “toggleSectionDisplayState” hides or shows a section by setting its isVisible attribute to [ true or false ].
In this Example, I’ve named the “Prospect” tab: “tabProspect” in the form configuration - and based on whether a (previously set) variable “typeProspect” is true or not, I want to hide or show this tab – and when it’s displayed, I want it expanded. – I also want to hide/show the Customer tab based on whether the record is for a prospect or not.
// If the 'Prospect type' == true, // then Show and expand the Prospect tab and hide the Customer section // Otherwise, // Show the Customer Section and hide the Prospect Tab if (typeProspect == true) { toggleTabDisplayState("tabProspect", "expanded", true); toggleTabDisplayState("sectionCustomer", false); } else { toggleTabDisplayState("tabProspect", "collapsed", false); toggleTabDisplayState("sectionCustomer", true); } function toggleTabDisplayState(tabName, tabDisplayState, tabIsVisible) { //Hide/Show and/or Expand/Collapse tabs var tabs = Xrm.Page.ui.tabs.get(); for (var i in tabs) { var tab = tabs[i]; if (tab.getName() == tabName) { tab.setVisible(tabIsVisible); tab.setDisplayState(tabDisplayState); } } } function toggleSectionDisplayState(sectionName, sectionIsVisible) { //Hide or Show Sections var tabs = Xrm.Page.ui.tabs.get(); for (var i in tabs) { var tab = tabs[i]; tab.sections.forEach(function (section, index) { if (section.getName() == sectionName) { section.setVisible(sectionIsVisible); } }); } }
Posted by Scott Sewell on November 01, 2011 at 02:34 PM in CRM Development, Microsoft CRM Customizations, Microsoft CRM Implementation, Microsoft CRM Tricks and Tips | Permalink | Comments (0) | TrackBack (0)
October 26, 2011
CRM 2011 and Windows Identity Foundation
This is a quick reminder that if you are deploying custom applications on servers other than where you install CRM, you need to manually install WIF (Windows Identity Foundation). These pieces are not included in the .Net Framework 4. Here is a sample error message you may receive if you run a custom application without WIF installed.
You can download and install the appropriate WIF from here. I also noticed that Microsoft upgraded the download site for this piece. You no longer need to know what OS you are on to download the appropriate file.
Posted by Sean Shilling on October 26, 2011 at 03:30 PM in CRM Development, Microsoft CRM Customizations, XRM | Permalink | Comments (0) | TrackBack (0)
October 25, 2011
Microsoft CRM 2011 -- When to Use an iFrame vs a Web Resource
Sit back and relax a bit because we aren’t going to get into any code today. Instead we are going to take a higher level look at some interesting caveats within CRM 2011.
Even if you aren’t a developer, if you customize the form with Web Resources or iFrames, this should be helpful to you.
Silverlight
First of all, I came across this due to a colleague using Silverlight. Like many of you out there, we do some crazy stuff with Silverlight. Silverlight is awesome and can be leveraged to do some serious heavy lifting. I’m actually more of a JavaScript guy, but until JavaScript supports multi-threading, Silverlight cannot be overlooked.
When using Silverlight, you have three options to put Silverlight on your form:
- Embed a Web Resource and select the XAP file (Figure A – 1)
- Embed a Web Resource and select an HTML wrapper (Figure A – 3)
- Embed an iFrame and enter an HTML wrapper (Figure A – 4)
Figure A (Before)
If you are embedding the XAP file (1), then all is good in the world and everything should behave like any other control. Problems only arise with the magnitude of what you are doing. Let’s say you have a couple of JavaScript resources you need to support your Silverlight control to talk to the form. If that’s the case, setup and maintenance is a little more cumbersome.
To make life easier for you and your customer, you could upload your own HTML file with everything self-contained. Now that you have everything nicely contained in an HTML web resource, we can either insert a web resource and select our HTML file (3) or we can use an iFrame and point to our HTML file (4). What’s the difference? Watch what happens when I select a Related entity (e.g. Activities) and then come back to my form. Notice how the Web Resource is blank? That’s because the form reloaded my web resource.
Figure B (After)
HTML
Ok, some of you out there may be saying, “but I don’t use Silverlight”. That’s fine, but this actually applies to HTML files as well. When embedding an HTML page, again you can either use a web resource or an iFrame. If you choose a web resource however, the page will reload when toggling to a new related entity. If you choose an iFrame, it will NOT reload (look back at Figure A&B paying attention to 2 and 5).
Well why?
This is all about your purpose. If you have a HTML page or Silverlight control that is read-only (e.g. graphs, charts, or accessing another system), then a Web Resource would be the better method most of the time. If you are expecting users to input data, then you’ll want to use an iFrame instead of a Web Resource so that the data is persistent. For some of you this could be a gotcha and for others it can be a feature. Hopefully none of your users are on the wrong end of this! Enjoy.
Posted by Paul Way on October 25, 2011 at 07:43 AM in CRM Best Practices, CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
October 05, 2011
Cases and Resolution Activities in Microsoft Dynamics CRM 2011
Many CRM users use the Case entity in CRM 2011 to track issues. These issue range from customer complaints to tracking actual software development bugs. The Case entity does a good job of allowing users to track a wide variety of issues and work on them them through resolution. What seems odd though is that when a user closes a case he/she cannot see those “resolution” activities anywhere.
The funny part of this problem is that a “resolution” is nothing more than another type of activity. Microsoft Dynamics CRM is just filtering out these activities from the activities views (see example 1) by setting the “Is Regular Activity” to “Yes.”
Example 1
You can probably see where we are going with this, but one of the easiest ways that we have found to show these on cases is to create a new view called “Resolution Activities” (See examples 2) and set the type of activity to resolution.
Example 2
As a result of those changes (Example 3), it is then very easy, especially under CRM 2011, to show the new view in a sub-grid on the case form. Users can now view a closed case and see what the resolution.
Example 3
Posted by Sean Shilling on October 05, 2011 at 01:36 AM in Microsoft CRM Customizations, Microsoft CRM Reporting, Microsoft CRM Tricks and Tips | Permalink | Comments (0) | TrackBack (0)
September 30, 2011
Hiding Ribbons in CRM 2011
I was recently involved in implementing Microsoft CRM for a large organization, and one of their requirements was a simplified, stripped down UI. One of the ways that we accomplished this goal was by hiding ribbon buttons that were not used in their deployment.
First, I found a great tool that made it very easy to add or remove ribbon buttons. I would recommend giving this tool a try. It was created by Pragma and did a pretty good job; and, it can be found on CodePlex.
Here is an example screenshot of hiding buttons for the case entity.
However, when you are editing the ribbon, either by using a tool or editing an entity xml manually, you can make mistakes that cause problems with your customization. In my case, I accidentally created two "hide" entries for the same item in the xml. As a result of this, the solution could not be imported.
Here is sample XML associated with hiding a ribbon entry. I placed big **** next to my duplicate records.
Once I deleted one of the duplicate rows from the xml file and reimported the solution, everything worked fine.
Lesson learned: It is fine to use tools and utilities to help customize your environment, but it is important that you still understand what is being changed "under the hood," as you may need to edit the xml file in case something doesn't work right.
Posted by Sean Shilling on September 30, 2011 at 08:35 AM in CRM Development, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (0) | TrackBack (0)
September 29, 2011
How to get the Microsoft CRM “Closed Activities” Nav Bar Link Back (if you delete it)
As we have covered earlier on the CEI blog, Microsoft Dynamics CRM 2011 introduces some great new form customization capabilities. One of my favorites is the ability to remove Navigation bar links from a form.
Prior to 2011, the only way to remove nav bar links was to use jscript to hide them. This complicated form customization, and also made the form appear to “dance” when you first opened it, as all of the hidden nav bar links would appear momentarily and then hide.
Now, with CRM 2011, you can go to the form customization and select the “Navigation” node from the ribbon.
Then from the left side of the form customization, you can select visible navigation bar links and remove them by either clicking delete on your keyboard, or clicking “Remove” from the ribbon.
If you want to add a link to the navigation bar that is not currently displayed on the navbar, you can select the link from the “Relationship Explorer” on the right side of the customization form, and drag and drop it to where you want to display it on the navigation bar. So if you remove one, then want to add it back, just move it over from the Relationship Explorer.
This will work with most of the relationships in CRM. One of the few for which it doesn’t work however is the “Closed Activities” link.
"Closed Activities" is basically a link to the same entity as the “Activities” nav bar link, just filtered to show a different view—Closed activities.
If you delete the Closed Activities nav bar link, you will not be able to add it from the relationship pane. It will no longer exist.
To get the link back, create a solution containing the entity from which the link was deleted and export the solution.
Then from another CRM environment, export the same entity, extract the solution .zip file, and edit the customization.xml file. Search for “<NavBarByRelationshipItem.“ You will find a section in the XML that looks like this:
<NavBarByRelationshipItem RelationshipName="Incident_ActivityPointers" Id="navActivityHistory" TitleResourceId="Tab_Label_History" Icon="/_imgs/ico_18_history.gif" ViewId="21E2B905-6FDB-470d-8517-AD69B4C01268" Sequence="20" Area="Info">
<Privileges>
<Privilege Entity="" Privilege="ReadActivity" />
</Privileges>
<Titles>
<Title LCID="1033" Text="Closed Activities" />
</Titles>
</NavBarByRelationshipItem>
Note this section may be different based on the entity in question. In this example, I’m using the case entity.
Copy this section, then go to your exported solution customization.xml for the environment where you deleted the closed activity link. Paste this section in the appropriate place, then zip up your solution and import into your environment.
You will once again see the “Closed Activities” navigation bar link.
Posted by Joel Lindstrom on September 29, 2011 at 04:41 PM in CRM Best Practices, Dynamics CRM 2011, Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)
August 29, 2011
CRM 2011 Form Customization (Part 2)
A while back we looked at customizing the CRM 2011 header and footer. I got some really positive feedback, but some people were tripped up. Troubleshooting via blog is difficult, so whenever you have problems feel free to tweet me (@paul_way). Today, I’m going to go into great detail of changing the form again, but this time we’re going to add a new twist. Take a look at the before and after:
Before
After
Now some of you may be wondering what I changed. First, we still have the light blue shading on the header. Secondly, we moved the footer to the left navigation. User’s may never request changing the color of the forms, but that was intended to show you that you could style the forms any which way you wanted. Moving the footer to the left hand navigation is quite practical. Some users may be faced with smaller screen resolutions and this will really free up space if you intend to put a lot of information in the footer (especially if you are adding to the header already).
Step-by-Step
Setting up our Web Resources
The first thing you’ll need to do is create a solution. I called mine “Form CSS Customization”. In it we are going to add two web resources.
For our initial web resource we’ll create is a CSS file:
.ms-crm-Form-HeaderContainer {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffB3CAEC, endColorstr=#fff6f8faE)
}
.ms-crm-Form-Footer {
display: none;
}
.ftLabel {
color: #999;
font-style: italic;
padding-left: 4px;
}
.ftValue {
padding: 2px 2px 8px 12px;
text-align: left;
}
#myCustomNav { padding: 2px }
.myNavFoot {
margin-left: 4px;
border: solid 1px #999;
width: 99%;
}
Take note of the name of your CSS file, as we’ll use this in a minute. The CSS file contains the shading on the header as well as some custom table formatting that we’ll use with our JavaScript file.
Now, let’s add our JavaScript file:
function load_css_file(filename) {
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
document.getElementsByTagName("head")[0].appendChild(fileref)
}function myFormOnLoad() {
load_css_file('/WebResources/cei_/css/case.css'); // don’t forget to change the file!
moveFooterToNav();
}function moveFooterToNav() {
var tbl = document.getElementById('crmNavBar');
var row = tbl.insertRow(tbl.rows.length);
var cFoot = row.insertCell(0);
var txt = document.createTextNode('testing...');
cFoot.appendChild(txt);
cFoot.id = 'myCustomNav';
document.getElementById('myCustomNav').innerHTML = "<table class='myNavFoot' cellspacing='0' cellPadding='0'>" +
"<tr><td class='ftLabel'>Created</td></tr>" +
"<tr><td class='ftValue'>" + document.getElementById('footer_createdby_d').childNodes[0].innerHTML +
"<br />" + document.getElementById('footer_createdon_d').childNodes[0].innerHTML + "</td></tr>" +
"<tr><td class='ftLabel'>Modified</td></tr>" +
"<tr><td class='ftValue'>" + document.getElementById('footer_modifiedby_d').childNodes[0].innerHTML +
"<br />" + document.getElementById('footer_modifiedon_d').childNodes[0].innerHTML + "</td></tr>" +
"</table>";}
The JavaScript file is doing two important things. First, it is injecting the CSS file into the header of the page. This basically tells the browser to pickup the CSS file as if it was always part of the page. After loading the CSS file, the JavaScript will append a table to the left area nav bar. This table is completely custom and you can style it any which way you want. Be careful using Xrm.Page.getAttribute() here, because even though the field is in the footer it may not be accessible to the Xrm object. In my case, the attributes I’m using were not on any of my tabs.
Changing the JavaScript
You must change the “load_css_file” function to contain your specific web resource. To find the web resource name, it’s easiest to look at the CSS web resource inside of CRM:
Configuring the Form
Now we need to go to the form we are customizing. The first step is to add everything we want to onto the footer.
Next, we’ll edit the form properties.
When editing the Form Properties, we’ll need to add the JavaScript library and add our onLoad function to the Form OnLoad. In our example, the function we’re using is called “myFormOnLoad”.
After this, we simply hit OK, Save, and Publish.
Reviewing the Finished Product
As you can see, we made subtle changes to improve the overall look and feel for the user. We also provided a real-life situation where you may need to squeak out some additional room for the user. Hope you enjoy!
Posted by Paul Way on August 29, 2011 at 03:58 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (0) | TrackBack (0)
August 23, 2011
CRM 2011–Changing the Form Header and Footer Colors
CRM 2011 has a beautiful default theme. Microsoft has already organized the CRM 2011 folder structure to support themes, but currently only supplies the Silver theme. Office 2010 on the other hand contains Silver, Blue, and Black.
Today we aren’t going to modify the entire theme, but modify the form header and footer. The code I’m going to include is a small gradient, but you could choose a solid background or any color you’d like. I was going to use a Clemson, I mean Customer Effective, Orange, but instead focused more on the blue theme look.
Before
After
Adding our CSS Web Resource
Granted this change was very minor, but you could do about anything you wanted to with it. To accomplish this basic change you must first upload a CSS web resource (I named mine new_header.css) with the following styling:
.ms-crm-Form-HeaderContainer{
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffB3CAEC, endColorstr=#fff6f8faE);
}.ms-crm-Form-Footer{
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffB3CAEC, endColorstr=#fff6f8faE);
background-image: none;
}
Note: The filter property is an IE specific CSS attribute. It isn’t supported in any other browsers (they will just ignore it), but Internet Explorer 7+ supports it just fine.
Modifying the OnLoad
Our next step is to modify the onLoad to inject the CSS file. We’ll do this by adding the following JavaScript to a web resource:
function load_css_file(filename){
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
document.getElementsByTagName("head")[0].appendChild(fileref)
}function myFormOnLoad(){
load_css_file('/WebResources/new_header.css');
}
Changing the Form Properties
Finally we need to change the form properties to include this JavaScript web resource in the library and add our “myFormOnLoad” function to the OnLoad Event for the Form:
Conclusion
After going through this blog, hopefully you will see how easy it is to change the form header, footer, or anything else. By embedding a CSS file, our ideas are our only limits. Hope you enjoy!
Posted by Paul Way on August 23, 2011 at 08:00 AM in Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (4) | TrackBack (0)
Technorati Tags: CRM, header, Microsoft CRM, ribbon, ribbon color
August 16, 2011
Hiding the Form Assistant in CRM 2011
In CRM 2011, the Form Assistant is enabled by default for the following entity forms:
- Case (incident)
- Product
- Service Activity (serviceappointment)
For those of you unfamiliar with the Form Assistant, it is designed to help you fill out lookup fields. It is actually pretty nice, but with CRM 2011 it may be a little overkill considering how nice the lookup fields already are.
Hiding via Form XML
The SDK lays out how to hide the Form Assistant by setting the “enablerelatedinformation” attribute to false. http://msdn.microsoft.com/en-us/library/gg334527.aspx
To modify the Form XML, you will need to add the form to a solution and then export. Once you have unzipped the solution, you can modify the Form XML and re-import the solution.
Hiding via JavaScript
Unfortunately, there doesn’t appear to be a way to grab the Form Assistant via the Xrm object. Instead you will have to go old-school and use the document.getElementById. The code to disable the Form Assistant is:
document.getElementById('tdRelatedInformationPane').style.display = 'none';
Either way you go is fine. Personally, I prefer the JavaScript method since it is so easy to revert back. Anyhow, I hope this helps!
Posted by Paul Way on August 16, 2011 at 10:21 AM in Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (1) | TrackBack (0)
August 02, 2011
Printing in CRM 2011
Microsoft CRM Dynamics 2011 offers a nice print preview that will handle almost all of your needs; however, without the ability to execute JavaScript on the print preview, it’s can sometimes fall short. Generally this isn’t a problem because you can create an SSRS report and be on your merry way; however, today we’re going to look at a different option – using the print preview.
The Scenario
Let’s say you have someone that really wants the customer’s URL added to the header. They also want the text to always be blue, whether on the form and when printed. So you add it to the header and add some JavaScript to change the style, no problem.
You take a look at the print preview and find that the color isn’t blue like the customer strongly prefers. Talking to the customer they love this printout except the color has to be blue. You could go through and change this to an SSRS report, but why create another thing for the customer to maintain on something so simple as a blue URL? If only we could execute just a tiny piece of JavaScript on the report… but wait you can!
Executing JavaScript on the Print Preview
To execute JavaScript on the Print Preview, you’ll need to embed a web resource. You will want to make sure it is not visible by default since it really is only there for the JavaScript. I also pass in the record object-type, because chances are you’ll need this for something other than a blue URL.
Now, let’s take a look at this mysterious web resource:
<HTML>
<HEAD>
<SCRIPT type=text/javascript>
if (top.location.href.indexOf("/print/print.aspx") != -1){
top.frames[0].document.getElementById('header_websiteurl_d').firstChild.style.color = '#0000FF';
}
</SCRIPT>
<META charset=utf-8>
</HEAD>
<BODY contentEditable=true>
<FONT size=2 face="Tahoma, Verdana, Arial">Testing...</FONT>
</BODY>
</HTML>
Breaking Down the JavaScript
Looking back at the web resource, the only thing that really matters is the JavaScript. The web resource exists in a hidden iFrame, so we’ll just go ahead and execute the code once the iFrame gets called.
The first thing we’ll check for is if we are in the print preview. We only want to execute the JavaScript inside this web resource if we are on the print preview. So we check the URL of the window to see where we are at:
if (top.location.href.indexOf("/print/print.aspx") != -1){
Next we’ll grab the element we want and change the color of the text to blue:
top.frames[0].document.getElementById('header_websiteurl_d').firstChild.style.color = '#0000FF';
To get to the page being printed, we have to use top.frames[0]. We then will find the id we want changed (and in this case actually need to change the firstChild’s CSS). Any of your “document.getElementById” code on the main form should work so long as you prepend it with top.frames[0].
Seeing the Final Result
Now we see a pretty, blue link on our print preview form and printed document.
Understanding the Drawbacks
This scenario worked perfectly; however not all scenarios will. One of the biggest problems is the inability to access the XRM variable. You can get to an XRM variable on the “printMain” iFrame; however, that XRM variable is very limited. Instead, you can really only use document.getElementById.
Summary
Modifying the Print Preview is quite easy with JavaScript. You won’t get all of the advantages that you get with the normal form, but you can do everything on that print preview that you can do on the form. So whether you plan to resize an iFrame, modify a label, or whatever, you can easily do so while printing as well. Hope you enjoy!
Posted by Paul Way on August 02, 2011 at 02:54 PM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (1) | TrackBack (0)
July 25, 2011
Using FetchXML Aggregation to Create a Rollup Plugin in Microsoft Dynamics CRM 2011
One of the most common requirements for a CRM implementation is for a rollup of data on a set of child records to the parent record. CRM has several built in entities, e.g. Orders and Quotes, that implement rollups, but it does not natively support rolling up of fields for custom entities or for customizations of existing entities. Fortunately, it is quite easy to implement a rollup using a simple plugin. Generally, implementation of the rollup involves retrieving all of the child records related to the parent when one of the child records is created, updated, or deleted, calculating the rollup value and updating the parent. This can be done by iterating through all of the child records after retrieving them using the organization service, but why not make things a little easier and use the new aggregation features of FetchXML to calculate the rollup value.
Aggregation support was added to FetchXML with the release of CRM 2011 and it supports the basic aggregation operations of sum, average, minimum, maximum, and count. The query can be built dynamically in the plugin which allows us to create a generic configurable plugin that will work for most standard rollups.
The first piece of code that we will need is a method to build the aggregation query dynamically. The following method will allow us to build the needed query.
1: public string BuildRollupQuery(string rollupEntity, string rollupAttribute, string aggregateOperation, EntityReference parentLookup) {
2:
3: return string.Format(
4: "<fetch distinct='false' mapping='logical' aggregate='true'>" +
5: "<entity name='{0}'>" +
6: "<attribute name='{1}' aggregate='{2}' alias='{1}_rollup' />" +
7: "<filter type='and'>" +
8: "<filter type='and'>" +
9: "<condition attribute='{3}' operator='eq' value='{4:B}' />" +
10: "</filter>" +
11: "</filter>" +
12: "</entity>" +
13: "</fetch>",
14: rollupEntity,
15: rollupAttribute,
16: aggregateOperation,
17: parentLookup.Name,
18: parentLookup.Id);
19: }
This method will allow us to dynamically create the rollup query given the parameters that define the child entity and attribute that is being rolled up, the rollup operation to perform, and the lookup field to the parent of the child. The query will aggregate the rollup attribute for all of the child records related to the parent.
Using this query we can call the following method to execute the query using the organization service and return the rollup value.
1: public object GetRollupValue(IOrganizationService orgService, string rollupQuery, string rollupAttribute)
2: {
3: return orgService
4: .RetrieveMultiple(new FetchExpression(rollupQuery))
5: .Entities
6: .First()
7: .GetAttributeValue<AliasedValue>(rollupAttribute + "_rollup")
8: .Value;
9: }
As you can see, this method executes the query by passing it to the RetrieveMultiple method of the organization service and retrieves the rollup attribute returned from the first record returned by RetrieveMultiple. Notice that the attribute value is returned by RetrieveMultiple as an AliasedValue. This is how all aggregate attributes are returned by the RetrieveMultiple method. I have made a few assumptions with this method for simplicity. For example, if the attribute being rolled up, the Value property of the returned AliasedValue attribute would be returned as a Money object. If that were the case it would be necessary to return the Value property of the Money object. As I mentioned, for simplicity I have assumed that the attribute being rolled up is a simple type, i.e. decimal, int, or double.
We now have the value for our rollup and simply need to update the parent object. Finally, we can make a call to the organization service to perform the update using the following method.
1: public void UpdateParentRollupField(IOrganizationService orgService, EntityReference parentLookup, string parentRollupField, object rollupValue)
2: {
3: Entity rollupParentEntity = new Entity(parentLookup.LogicalName);
4: rollupParentEntity.Id = parentLookup.Id;
5: rollupParentEntity[parentRollupField] = rollupValue;
6:
7: orgService.Update(rollupParentEntity);
8: }
At this point we have all of the tools needed to create the rollup plugin. In order for the plugin to operate properly and efficiently it is necessary to add a few checks and images to the plugin. For example, it is only necessary to update the rollup if the parent lookup field was changed or the field being rolled up on the child record was changed. These values can be checked by examining the InputParameters object that is passed to the plugin. It will also be necessary to check to see if the parent lookup field for the rollup has changed. If it has changed it will be necessary to update the rollup field on the old parent and the new parent. The old parent lookup field value can be retrieved from a Pre Image entity that is registered for the update and delete messages. The following snippet shows an example of the required checks and assumes that the Pre Image registered for the plugin is called “Target”.
1: Entity targetInput = context.InputParameters["Target"] as Entity;
2: Entity targetPreImage = context.PreEntityImages["Target"] as Entity;
3:
4: bool parentLookupChanged = targetInput.Contains(parentLookupFieldName);
5: EntityReference parentLookupOldValue = targetPreImage.GetAttributeValue<EntityReference>(parentLookupFieldName);
6:
7: bool rollupFieldChanged = targetInput.Contains(rollupFieldName);
All that remains at this point is to wrap the code up in a plugin implementation and call the methods as needed. The plugin can be made configurable by setting a configuration when registering the plugin using the plugin registration tool supplied with the SDK and retrieving the configuration string from the configuration argument passed to the plugin constructor. Happy coding!
Posted by Nick Doriot on July 25, 2011 at 08:48 AM in CRM Development, Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips, XRM | Permalink | Comments (3) | TrackBack (0)
June 30, 2011
Activity Relationships in Microsoft Dynamics CRM 2011
One of the hidden surprises in CRM 2011 customization is the new ability to create 1:N relationships from activity entities. In previous versions, you could only create N:1 relationships from activity entities such as phone calls and appointments. This meant that you could have look-up fields on activity forms, but you could not relate other records to the activity.
This new capability will greatly enhance CRM activity management and make it easier to enhance the out of the box processes to better fit unique activity management scenarios. A couple of example use cases:
1. Trip reports
In industries such as Insurance and Financial Services, it is a common request to create a trip reports when an account manager visits a client. These trip reports commonly include details such as discussion points, follow up items, and products discussed.
In CRM 4.0, since we could not create 1:N relationships with activities, the trip report usually had to be created using a custom entity. While this approach worked, it created a disconnect between the appointment for the meeting and the trip report record. The user would have to manage the appointment on their calendar, and then go to an unrelated entity to create trip reports.
Now that we can add relationships to activities, we can use the appointment record to contain the trip report details, and link it to related records, such as products discussed, or follow-up items, which can be other activities, such as tasks. Appointment and trip reports can be managed in one place.
2. Follow-up activities
In CRM 4.0, there was a button on the phone call form called “Follow up.” When you pushed this button, a new activity would be created that includes the details from the original call, and this was a useful way to quickly create and schedule a follow up call.
In CRM 2011, this functionality has been removed. So what can you do if you want to quickly create follow-up activities?
The new activity relationship functionality is a great replacement for the follow-up button.
In 2011, I can create a self referential activity relationship. For example, I can customize the phone call entity and create a 1:N relationship for phone call to phone call. When I do this, I set it to use a custom label for the navigation link, and call it Follow Up Calls.
Then from the mappings settings, I can map the fields that I want to be copied from the originating call to the follow up call.
Once I publish, I will now see a navigation bar link for “Follow up calls.” When I create a phone call, I can now create a related follow up activity that contains the data from the originating call.
This also give us some new capabilities that the old follow up call button did not:
1. Follow up activities are related to their originating call. The 4.0 follow-up activity button would create a new call, but it would have no relationship to the originating call. With the new approach, related activities are joined together, so you can group conversations together.
2. You can now customize what maps when the follow up activity is created. Given that you set the mappings of the 1:N relationship, you can choose what fields to map, and which not to map, even with custom fields.
A few considerations with this approach:
1. You cannot map the party-list type fields, such as the “to” or “attendee” fields. These fields are a unique type of field that actually display the contents of multiple related records (activityparty), so the contents of these fields will not map to the follow-up activity.
2. You have to create a follow-up activity prior to closing the activity, since related records cannot be created from inactive records.
Posted by Joel Lindstrom on June 30, 2011 at 04:08 PM in Dynamics CRM 2011, Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)
June 28, 2011
MS CRM 2011 Developer JavaScript PDF
If you are like many of us trying to get up to speed on CRM 2011 new JavaScript objects, I hope this helps. I came across this the other day. The original post and credit goes to Daniel Cai. You can find his blog here.
However, I found that someone uploaded the same PDF to this free E-Book Browse site.
http://ebookbrowse.com/mscrm-2011-javascript-development-cheat-sheet-pdf-d126038641
Here is a sample screen shot.
Hope it helps!
Posted by Sean Shilling on June 28, 2011 at 03:00 PM in CRM Development, Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)
Modifying Quote Templates in CRM 2011
Since we all don’t work for Adventure Works, Inc., organizations that use the quote entity to send quotes to customers will ask their administrators to modify the out-of-the-box quote template. It’s not as straight-forward as it seems and you could easily corrupt the original mail merge template so I wanted to give instructions to help you along.
Standard instructions will probably tell you to click on ‘Settings’, ‘Templates’, ‘Mail Merge Templates’, then change the view to see all templates and double-click the template you wish to modify.
Yes, you can go about it this way, but editing the mail merge template is certainly an iterative process so I’m going to suggest go to another location to modify your templates. By going to the quote entity and opening a quote, you save some steps as you save your changes and then run through the mail merge of the quote to see how they look. So with a quote open, do the following:
1. Click on the ‘Print Quote for Customer’ button.
2. Select the type of mail merge template document and click the lookup icon. (I suggest using one ‘Organizational mail merge template’ to insure uniformity in the documents you send out to your customers.)
3. Select the out-of-the-box template named, “Quote for Customer”, and click ‘Properties’.
4. The following window will pop up. Click ‘Edit Template in Word’.
5. Next, click ‘Open’.
6. Microsoft Word will open.
a. If you are using Word 2010 it is very likely you will see the following across the top of the screen. Click ‘Enable Editing’.
b. Next, you may get prompted by another similar message. Click ‘Enable Content’.
7. The document tells you, “To start the mail merge, click CRM.” However, you actually need to click on the ‘Add-Ins’ menu item and click ‘CRM’. Note: Depending on the version of Word you have, you may have to click ‘Mailings’ or ‘Add-Ins’ to get to the ‘CRM’ button.
8. Next, the following screen will pop up. Click ‘OK’.
9. Now you can begin to edit your template. If it looks like the screenshot below, hit ‘Alt+F9’.
It will display more of the editable areas of the template.
Below, outlined in green, are changes I made to the logo, the address tables and at the bottom of the template. Along with the ability to remove merge fields using the delete button, you can add merge fields by clicking the ‘Insert Merge Field’ button on the ribbon, outlined in blue with the arrow pointing to it. Lastly, I suggest avoiding making changes in the section outlined in red unless you absolutely must. This is where the quote product data is entered and calculations take place. I don’t see a need to edit this section.
10. Upon completing your changes, click on ‘File’, ‘Save As’.
IT IS OF THE UTMOST IMPORTANCE THAT YOU CHOOSE ‘SAVE AS’ INSTEAD OF ‘SAVE’ TO AVOID OVERWRITING AND CORRUPTING YOUR ORIGINAL MAIL MERGE TEMPLATE!!! You may ask why we don’t save a copy of the template before we start any of this. The problem with that is the original file is a “.doc” file and if you make a copy of it, all of the back-end functionality of the mail merge is lost.
You’ll notice that it will save the file as “.xml” instead of a “.doc” file, which is the type of your original template. Be sure to save it as an XML file.
11. Now go back and repeat steps 1 & 2 to get to the screen below where you can choose your template except this time click on ‘New’.
12. The following window will pop up. Fill in the information outlined in red. Then browse for your newly saved template and click ‘Attach’.
13. Once the screen refreshes, you’ll notice your file name changed to blue. You will also have a new button called ‘Actions’ on the menu bar. In that menu, select ‘Make Available to Organization’ to make your template viewable by everyone. And fear not, once you make it available to the organization, you can always go back to ‘Actions’ and click the ‘Make Personal’ menu item to make it just yours again.
14. You’ll end up back at this screen. Choose your new mail merge template and click ‘OK’.
15. Click ‘OK’ at the next screen.
16. Microsoft Word will open.
a. If you are using Word 2010 it is very likely you will see the following across the top of the screen. Click ‘Enable Editing’.
b. Next, you may get prompted by another similar message. Click ‘Enable Content’.
17. The document tells you, “To start the mail merge, click CRM.” However, you actually need to click on the ‘Add-Ins’ menu item and click ‘CRM’. Note: Depending on the version of Word you have, you may have to click ‘Mailings’ or to get to the ‘CRM’ button.
18. Next, the following screen will pop up. Click ‘OK’.
19. Click on ‘Next: Preview Your Directory’ in the lower, right corner of the Word window.
20. Click on ‘Next: Complete the merge’ in the lower, right corner of the Word window.
21. Click on ‘To New Document…’ in the top, right of the screen.
22. Click ‘OK’ at the next pop up screen.
23. And behold the culmination of your hard work!
Don’t be surprised if you don’t get everything looking right the first time. In that case, go back and repeat steps 1 – 10, but this time be sure to edit the new mail merge template you created instead of the original. Next, repeat steps 1 – 3. In step 4, remove the old file from the bottom and attach the new file you just created. I name my files with an incremental number after each one as I develop the quote in case one of my attempts ends up rendering the template inoperable. So in this case, I would replace the file “CustEffective Quote.xml” with “CustEffective Quote1.xml”. Repeat this last paragraph as many times as needed to get your quote looking beautiful.
Posted by Chris Weilandt on June 28, 2011 at 10:01 AM in Microsoft CRM Customizations | Permalink | Comments (2) | TrackBack (0)
June 21, 2011
Adding WebResource to CRM 2011 Header
A customer of ours had a recent requirement to add information to the CRM 2011 header. While I did find some information on the topic, it was difficult to find all the pieces to puzzle.The following steps should allow you to recreate the same results.
Here is the result…
Steps required to accomplish it…
1.) Create Web Resource using the sample code from the Microsoft CRM SDK. You may want to modify the html to format the page the way you want it to look.
http://msdn.microsoft.com/en-us/library/gg327945.aspx
Note:
In the sample HTML page from MS, I made the following changes..
Added – Font size/color for description text.
oTRowTD00.style.color = "#818A96";
oTRowTD00.style.fontSize = "11px";
oTRowTD00.style.fontFamily = "Segoe UI";
Added – Font size/color for result text.
oTRow1TD01.style.color = "#000000";
oTRow1TD01.style.fontSize = "14px";
oTRow1TD01.style.fontFamily = "Segoe UI";
2.) During onload of form use FetchXML to get data and pass it to the new web resource you created and pass results to the following JS
//Sample data
var HeaderData = ‘’;
HeaderData = "?data=DBA%3d" + sDBA + "%26Address%3d" + sAddress + "%26City/State/Zip%3d" + sStateZip + "%26Phone%3d" + sPhone + "%26City%3d" + sCity;
document.getElementById("WebResource_Test").src = document.getElementById("WebResource_Test").src + HeaderData
Note: The following two objects did NOT work when referring to web resources in the header. One object only works against resources in the body. The other object I tried just kept saying it doesn’t exist:
Xrm.Page.ui.controls.get("WebResource_Test") – Only works in body
Xrm.Header.ui.controls.get("WebResource_Test") – No documentation and Null results
Posted by Sean Shilling on June 21, 2011 at 10:08 AM in Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)
March 30, 2011
Preparing for 2011: Filtered Lookup in Microsoft Dynamics CRM 2011
One of the most commonly requested features in Microsoft Dynamics CRM has always been filtered lookup fields. For example, say you replace country and state text fields with lookup fields—it is a very common request to filter the available states based on the country selected.
There are several unsupported ways to filter lookup fields in CRM 4.0; however, many of these approaches added complexity or broke standard CRM functionality, like the auto resolution of lookup fields. A good supported approach is the Stunnware Filtered Lookup add-on; however, for some implementations that just needed simple filtered lookups in one or two places, it could be hard to justify purchasing add-ons.
CRM 2011 has taken a major step forward with the introduction of filtered lookup fields. In this example, I want to filter the Case form “Responsible Contact” lookup to only include the contacts associated with the selected Case account.
From the case form customization view, I’m going to select the “Contact” field.
By clicking the “Change Properties” button, I will see the field properties for the contact lookup.
Under the Related Records Filtering section of this form, I can check a check box that will filter the available values of this lookup field to records related to the value selected in another lookup field. In this example, I said to only show contacts contained in the case account.
Note that I can select whether or not to allow users to turn off the filter—this gives me the ability to choose whether my filter is “hard,” limiting my users to just the filtered records, or “soft,” allowing users to select something outside of the filter. This is important because some filters are there to prevent users from selecting invalid record combinations, while others are there for user convenience, but may need to be overridden for exceptions.
Considerations
I’m very excited to see solid filtered lookup functionality in Microsoft Dynamics CRM 2011, and I think it will be very useful to make forms more user friendly and less cumbersome. New functionality like this and also the ability to delete form navbar items has made it possible to reduce much of the form jscript in many crm 4 to 2011 upgrades.
I also don’t expect the out of the box functionality will solve every filtered lookup challenge. The functionality works great for standard filtering where you want the value of one lookup on the form to filter the options available in another lookup field; however, in cases where the filtering logic are more complex (filter the options in one lookup based on a combination of variables from multiple other lookups, or based on attributes on a related record), there will still be value in products like the Stunnware Filtered Lookups.
Posted by Joel Lindstrom on March 30, 2011 at 05:18 PM in Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)
February 18, 2011
How To: Customize Wunderbar Icons in CRM 2011
Another useful feature of Web Resources in CRM 2011 is that once created they can be reused throughout the environment. Other than allowing for greater customization options, and client satisfaction, a benefit of using web resources in this way is once a single web resource is cached in your browser it can quickly be called within the site when needed; allowing for faster page loads. In this example we will add the dynamics logo as a PNG web resource and insert it into the Site Map to change the out of the box ‘Workplace’ logo.
Step 1: Add a new Web Resource
- Go to Settings –> Customization –> Customize the System
- Create a new Web Resource with a content type of PNG Format
- Browse and upload your PNG file
- Save the Web Resource
- Copy the unique URL that is generated
Step 2: Modify the Site Map
- Export your unmanaged solution containing the Site Map client extension, extract the contents of the ZIP file, and edit the customizations.xml file.
- Locate the <SiteMap> section of the file, then the Workplace area, and update the icon attribute with the unique URL of your web resource that was copied earlier. The first line of your Workplace area should now look something like this:
<Area Id="Workplace" ResourceId="Area_Workplace" ShowGroups="true" Icon="https://customereffectiveinc.crm.dynamics.com//WebResources/new_CRM_LOGO" DescriptionResourceId="Workplace_Description">
3. Save the customizations.xml file and repackage the solution folder into a ZIP file.
Step 3: Upload the new Site Map
- Go to Settings –> Customization –> Solutions
- Click the
icon to import the updated solution.
- Browse for the ZIP file containing the modified customizations.xml file.
- If the solution imports successfully, click
to publish out the Site Map modifications to all users.
- Hit F5 on the main page to refresh your CRM session and you should see the new icon for the Workplace section of the wunderbar:
Note - We never have to resize the PNG file to fit any predefined dimensions as you need to do when adding custom icons to a custom entity or the Ribbon. This is a nice time saver to anyone not familiar with image customization.
Posted by Andrew Magnotta on February 18, 2011 at 08:47 AM in Dynamics CRM 2011, Microsoft CRM Customizations, Microsoft CRM Tricks and Tips | Permalink | Comments (2) | TrackBack (0)
November 17, 2010
Managed and Unmanaged Solutions in Microsoft Dynamics CRM 2011: Know the Difference
Previous versions of CRM were know for their extensibility. CRM 2011 takes it a step further and introduces us to the concept of "managed" and "unmanaged" solutions. The big picture view of this is simple: Managed solutions allow you to bundle customizations and set rules and boundaries for your solution. There are three types of 'Solutions'.
Default Solution
Yup, you got it. The standard MS Dynamics CRM software, plus any pre-deployment customizations that your organization has added to it is brilliantly labeled 'Default Solution'. You'll see it, and when you drill down into it you can see all of the Managed Components it's helping to keep warm.
Unmanaged Solutions
All solutions start out as Unmanaged. When it is in the unmanaged state, you can add, remove, update, test any of the components of the solution. You can delete components of your unmanaged solutions, while leaving it available for use in the rest of the system. Some on the MS CRM dev team have likened this to your 'source' code of your system. The great thing about an Unmanaged Solution is that during development, you can create restrictions (like 'not customizable') on the components as they evolve.
Managed Solutions
When your unmanaged solution is ready for the show, you simply export it to 'Managed'. You could think of this as 'compiling' you code. You set the restrictions (i.e. prevent customizations on certain components) and the end user lives by those rules. But remember, they can still customize the components of the solution that are unrestricted. You cannot add or remove components of a solution, even if the component is unmanaged.
Once you have packaged the Managed Solution, it can be installed into another organization. They can also be deployed across multiple deployment types (Online, Partner Hosted, On-Premise) and all CRM Clients (web, Outlook, Mobile Express, and Offline via Outlook Client).
Posted by Brad Koontz on November 17, 2010 at 08:29 AM in Dynamics CRM 2011, Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)
September 30, 2010
Copying Dynamics CRM form values to the clipboard with jscript
If you use Dynamics CRM, you may be familiar with the options for mail merge, bulk email, export to Excel, and other options to get data out of CRM. These options work great, but what if you want to just export certain values from a single record? You could export to Excel, but that is kind of overkill for one record.
Consider this real-world scenario: an office manager periodically mails individual pieces of mail to a specific recipient. When he does this, he prints the mailing label using a Dymo label printer. Rather than typing the address in, he wants to get it from CRM.
One of our genius developers put together an easy jscript function to give users the ability to copy the address fields from the contact record form to the Windows clipboard, and then they can easily paste the address into their label printer software. This function can be adapted to copy any CRM entity field displayed on the record form.
First, add the function to the OnLoad event
CopyAddressToClipboard =
function () {
// Construct the address to suit your needs.
var sAddress = crmForm.all.firstname.DataValue + " " + crmForm.all.lastname.DataValue + "\n" +
crmForm.all.address1_line1.DataValue + "\n" +
crmForm.all.address1_city.DataValue + ", " + crmForm.all.address1_stateorprovince.DataValue + " " + crmForm.all.address1_postalcode.DataValue;
// Get the hidden copy area (creating it if it doesn't yet exist).
var oCopyArea = document.getElementById("copyArea");
if (oCopyArea == null) {
oCopyArea = document.createElement("input");
oCopyArea.setAttribute("id", "copyArea");
oCopyArea.setAttribute("type", "hidden");
document.getElementById("crmForm").appendChild(oCopyArea);
}
// Copy the address to the hidden copy area.
oCopyArea.innerText = sAddress;
// Copy from the hidden copy area to the clipboard.
var oCopyTextRange = oCopyArea.createTextRange();
oCopyTextRange.execCommand("Copy");
};
Then create a toolbar button via IsvConfig:
<Entity name="contact" >
<ToolBar>
<Button JavaScript="CopyAddressToClipboard();" Icon="/_imgs/ico_16_132.gif" ValidForCreate="1" ValidForUpdate="1" AvailableOffline="true">
<Titles>
<Title LCID="1033" Text="Copy Address" />
</Titles>
<ToolTips>
<ToolTip LCID="1033" Text="Copy this contact's name and address to the clipboard." />
</ToolTips>
</Button>
</ToolBar>
</Entity>
Now you will have a button on the toolbar that you can push to copy the name and address.
Posted by Joel Lindstrom on September 30, 2010 at 03:34 PM in Microsoft CRM Customizations | Permalink | Comments (4) | TrackBack (0)
September 15, 2010
Anticipating the Microsoft Dynamics Marketplace
At the Worldwide Partner Conference in July, Microsoft announced the Microsoft Dynamics Marketplace. When completed, this service will allow developers of all Dynamics platforms to showcase their applications to users. First at bat from the Dynamics roster of products is the CRM Marketplace, which will allow partners, like Customer Effective, to share their specialized vertical CRM solutions. The Marketplace will allow users to download apps directly from CRM 2011, as well as directly connect with partners. Users will be able to search for applications by Business Need (Marketing, HR, Finance), Industry Focus (Financial Services, Manufacturing) and geography. Users will also be able to evaluate and rate the solutions on the Marketplace.
The Marketplace is set to launch this month (September).
In an interview with Channel9, Microsoft's's Dan Bien says:
Initially, it will be a search able repository of download-able custom solutions and extensions to help accelerate and/or extend the value of Microsoft Dynamics CRM Online and on-premises deployments. Publishers of solutions can get also more detailed web analytical information about their listings. In the future, Marketplace will provide e-commerce and offer financial transaction capabilities for Microsoft Dynamics CRM Online and on-premise, as well as Microsoft Dynamics ERP solutions.
The Marketplaceis set to launch this month (September). Check back to CE Blog for frequent updates.
Posted by Brad Koontz on September 15, 2010 at 11:03 AM in CRM Development, Customer Effective News, Dynamics CRM 2011, Microsoft CRM Customizations | Permalink | Comments (0) | TrackBack (0)




Recent Comments