Setting a CRM 4.0 Lookup field in JavaScript – part deux
Yes, some time has passed since I last posted a blog article and yes, I could say that it won’t happen again but I don’t want to make any promises. Since my last post a lot of things have changed in my life, so my apologies. That said, we aren’t here to discuss me. We’re all about the CRM. If I can keep my focus I would expect many new blogs posts about CRM 2011 as well as some CRM 4.0 blog posts that I’ve been meaning to get out the door. For this post however I just wanted to drop a little something for everyone, so here goes.
You may recall that I had previously posted and explained how to set the value of a Lookup field in CRM 4.0. Well, I’m going to quickly show how to take that to the next level to keep your life (actually, your code) clean and concise.
First, let me post a refresher of the SDK code to set the value of a Lookup field:
var lookupObject = new Object; var lookupObjectArray = new Array(); lookupObject.id = "21EC2020-3AEA-1069-A2DD-08002B30309D"; lookupObject.name = "John Doe"; lookupObject.typename = "systemuser"; lookupObjectArray[0] = lookupObject; crmForm.all.new_userid.DataValue = lookupObjectArray; crmForm.all.new_userid.ForceSubmit = true;
Now that code comes from the SDK, and is intended to allow people to follow along and understand what’s happening. Long story short, there’s another notation called JSON (JavaScript Object Notation)
Read more about JSON by visiting http://JSON.org
I won’t waste your time explaining what the JSON site can explain to you, but we can shorten our code by using JavaScript Object Notation. I’ll post the same code block getting smaller each time so that you can see what I’m doing. First, you don’t have to use the “new Array()” syntax to create an array. In fact, many JavaScript gurus recommend against it. Instead, you can simply use the square brackets, like this:
var lookupObject = new Object; var lookupObjectArray = []; lookupObject.id = "21EC2020-3AEA-1069-A2DD-08002B30309D"; lookupObject.name = "John Doe"; lookupObject.typename = "systemuser"; lookupObjectArray[0] = lookupObject; crmForm.all.new_userid.DataValue = lookupObjectArray;
JavaScript also allows to use the curly braces to declare an object, as shown here:
var lookupObject = {};
var lookupObjectArray = [];
lookupObject.id = "21EC2020-3AEA-1069-A2DD-08002B30309D";
lookupObject.name = "John Doe";
lookupObject.typename = "systemuser";
lookupObjectArray[0] = lookupObject;
crmForm.all.new_userid.DataValue = lookupObjectArray;
Looking at the code like this may make you consider whether we actually need the lookupObjectArray variable at all, and in fact we don’t. We could simply do this:
var lookupObject = {};
lookupObject.id = "21EC2020-3AEA-1069-A2DD-08002B30309D";
lookupObject.name = "John Doe";
lookupObject.typename = "systemuser";
crmForm.all.new_userid.DataValue = [lookupObject];
Now to use JSON, we can declare our object in one statement. All you C# folks may find that it looks a lot like automatic initializers. Here goes:
var lookupObject = {
id: "21EC2020-3AEA-1069-A2DD-08002B30309D",
name: "John Doe",
typename = "systemuser"
};
crmForm.all.new_userid.DataValue = [lookupObject];
Finally, if we got this far you may have guessed that we don’t really need lookupObject and we can instead simply declare it inline, like this:
crmForm.all.new_userid.DataValue = [{
id: "21EC2020-3AEA-1069-A2DD-08002B30309D",
name: "John Doe",
typename = "systemuser"
}];
There we have it. Doesn’t that look more clean and concise than what we started with? I think so, and I hope you do too. Cheers!
“The specified file is not a registry script” – How encoding can ruin your morning
If you’re like me, you spend a lot of time dealing with files and text files are no exception. Doing anything powerful while editing a text file usually isn’t handled by basic programs like Notepad. What editor to use is a bit of a holy war that I won’t get into since it has little to do with this blog entry. Having said that, I prefer to use a powerful editor when dealing with text files because I find that all too often Notepad just doesn’t get the job done as quickly, so I use TextPad. It has several features that I find myself using all too often, so I’ve set it as my default text editor.
This morning I was doing some development and I had to quickly create a (.reg) registry file with which I could quickly set some values in the registry when I needed to. This seemed like a simple enough operation. For the sake of speed, I already had regedit open to the path that I was interested in, so I chose to export it from regedit. Doing so saved a .reg file where I specified and I was free to edit it. I opened up TextPad and make the changes that I needed. In my case I was only changing a single key so I cut everything else out and saved the file. To test things out, I double-clicked on the .reg file and got the typical prompt:
I just made this file, so I did in fact trust it and by clicking “Yes” I expected to get a valid message like this:
However, instead I got this:
What the heck? That’s not good! This simple was dirt simple. After all it only had 3 lines in it. What could have happened? Turns out that encoding can ruin your morning. Taking a look at the message I saw the “… only import binary registry files from …” and I thought “Why does this thing think it’s binary?”. Why, indeed. I opened the file back up in TextPad and rather than just hitting Ctrl+S or Save, I chose "Save As". Doing so presented this menu, and I’ve highlighted my problem:
That’s right. Without me doing anything, TextPad was going to save this file as Unicode. Regedit expects to get .reg files that are ANSI encoded. So I changed the Encoding to ANSI, saved the file, ran it again, and everything worked fine. It caused me some frustration for a good 20 minutes, so hope this helps someone else out.
Microsoft Internet Explorer provided by me? Why not!
It’s been a busy summer for me, but I wanted to make sure I got something out there so I thought I’d drop a quick one for you guys and hopefully it helps someone else who possibly annoyed by it.
I had a friend who bought a Dell laptop (great laptop by the way), but it came with one annoyance that he wanted help with. Here’s a picture of his browser, let’s see if you can pick out the issue. Go ahead, I’ll wait.
If you said the annoyance was that the homepage was set to http://www.dell.com that is a bit of an annoyance, but that’s easy enough to deal with. If you said that the window title for every web page you go to ends with ” – Microsoft Internet Explorer provided by Dell, Inc.” you’d be right. It is annoying isn’t it? The good news is that it’s pretty easy to fix if you have the appropriate rights on your box.
Specifically, you’ll need to have the ability to modify your registry. You can screw your machine up pretty quickly by tinkering around in the registry, but this is very straightforward. Basically, all you have to do is follow these steps:
1. Run regedit.exe to view the contents of your registry. The machine’s registry is a hierarchical structure details all sorts of stuff that you can mess up so be careful!
2. Browse through the hierarchy to Computer\HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main
3. Once you’re there on the hierarchy on the left, you’ll see a list of keys and thier values on the right. Find the key labeled “Window Title”. When I saw his, it read “Microsoft Internet Explorer provided by Dell, Inc.”. Change that value to whatever you want.
4. If you have any Internet Explorer windows open, close them all and re-open IE. Voila! The text you had has been replaced. No muss, no fuss!
Hope this helps someone.
Improving readability of disabled fields in CRM 4.0
In talking with others apparently this is a common item that people ask for, and I finally ran across it so I figured it would be best to document my approach.
Background:
So we really do some neat things to push the envelope with CRM and this case was no exception. I will dispense with the gory details but long story short we were retreiving some data from an external source (using jQuery, Ajax, and JSON from a WCF service – how cool is that?) on a CRM form and we wanted the fields to be disabled once the field’s data was populated.
Sounds simple enough right? It could have been as simple as this:
crmForm.all.fieldname.value = "foo"; crmForm.all.fieldname.Disabled = true;
It could have been that simple and it was until our users wanted to look at the results. Enter problem stage left.
It appears that I’ve been given the gift of normal vision but not all the users had. It turns out that when you set the Disabled property to true on a CRM field one of the things it does is to change the field’s background and its border to muted color which signifies that the field is disabled. The users were fine with that. The other thing it can do is change the text color of the field to gray. Here’s an example:
This isn’t so good when your users want to read the contents and don’t have perfect vision. Trust me, we even have to scale back our minimum resolution requirements so that all the users can see.
The request was to keep the text color black and just have the background change. So how did I accomplish this?
It really only took a couple of steps. First, I wanted to make sure that the field was still effectively disabled so that the users couldn’t change the data on the field. Since CRM is an Internet Explorer only application, the issue was that when IE sees that a field is disabled it decides to be nice and change the text color to gray. Instead there is another property that you can use called readonly. Setting a field’s readonly property to true does in fact prevent the user from changing the value, so there’s our first part of the problem solved. You can make a field readonly in JavaScript like this:
crmForm.all.fieldname.readOnly = true;
The problem is that we didn’t wanted to leave it like that because when you make a field readonly it looks like this:
That’s functionaly fine, but as you can see it does give the user the impression that they can still edit it. So, let’s make them think it’s disabled by changing the background color, like this:
crmForm.all.fieldname.style.backgroundColor = "#eaf3ff";
Doing so produces the following result:
Alright, we’re almost there. For any perfectionists out there you may notice 2 differences from the first image:
1. The text is black, not gray, which is what we’re going for.
2. The border color is different.
So, we can quickly fix that by setting the border color using a little JavaScript too:
crmForm.all.fieldname.style.borderColor = "#c5c5c5";
Taking one last look we see that the results are exactly what we wanted:
So, in summary, 3 lines of JavaScript gets you exactly where you wanted to be:
crmForm.all.fieldname.readOnly = true; crmForm.all.fieldname.style.backgroundColor = "#eaf3ff"; crmForm.all.fieldname.style.borderColor = "#c5c5c5";
Hope this helps. Cheers!
Setting a CRM 4.0 Lookup field in JavaScript
It seems that the days are getting busier as I’m finalizing a couple of projects and preparing for TechEd 2010. Still, I want to still try and drop at least a couple of trinkets off here so you’ll keep reading this blog.
So what is today’s pearl of wisdom? I get asked all the time how to set the value of a lookup field in CRM via JavaScript.
It’s not quite as straightforward as setting the value of a textbox or picklist, but it’s not terrible. Basically, it boils down to a few steps.
The first thing to remember is that the value of a lookup field is an array of objects. My understanding is that by default you can’t actually create a CRM lookup field that accepts multiple values. However, a good example of this is in the CRM email entity’s “To” or “CC” fields. These fields allow you to set multiple recipients for an email.
The second thing to remember is that despite the fact that you can’t set multiple values, you can set a single value but that value is a complex type, an object with individual properties, not just an intrinsic type like a string or integer.
So how do we accomplish that in JavaScript? First, we’ll create an object:
var lookupObject = new Object;
Now that we have our object, we can set some values. Typically, you’ll want to set 3 main properties: ID, name, and typename. The ID is the GUID of the object whose value you’re setting. The name is the string that your users will see. Finally, the typename is the CRM entity name for the value you’re setting, in this case systemuser. You can set them like this:
lookupObject.id = "21EC2020-3AEA-1069-A2DD-08002B30309D"; lookupObject.name = "John Doe"; lookupObject.typename = "systemuser";
OK, so now you’ve set the values for the object that you’ll be setting as the value of your lookup. As I mentioned though the value of a lookup field is an array of objects, even if you can only set a single value. So we can create the array like so:
var lookupObjectArray = new Array();
We can only set one object, so we’ll set our new object as the first member of our newly created array.
lookupObjectArray[0] = lookupObject;
Alright, now that we have our array and its first member is our object, let’s set the array as the lookup field’s DataValue:
crmForm.all.new_userid.DataValue = lookupObjectArray;
Finally, we want to make sure that our newly submitted value gets saved back to the database when we save the record, and we can accomplish that quite simply by setting ForceSubmit to true:
crmForm.all.new_userid.ForceSubmit = true;
There we have it! We created an object, a container array, set the appropriate values and assigned it to out lookup field. Here’s all the code together in one block:
var lookupObject = new Object; var lookupObjectArray = new Array(); lookupObject.id = "21EC2020-3AEA-1069-A2DD-08002B30309D"; lookupObject.name = "John Doe"; lookupObject.typename = "systemuser"; lookupObjectArray[0] = lookupObject; crmForm.all.new_userid.DataValue = lookupObjectArray; crmForm.all.new_userid.ForceSubmit = true;
Cheers!
WCF IIS App_Web_xxxxxx_WTF?
This is very frustrating. We are now on my 5th try saving this post without losing everything. Here goes!
This post is probably a great example of how I like to write a blog entry for myself as well as the reader. I had been tinkering with embedding Silverlight into CRM 4.0 and it has been an enlightening experience (don’t worry, more posts on that to follow). At any rate, I had finally gotten it working how I wanted and I checked back the next day only to find that it was broken. The worst part being that I hadn’t changed anything! Now before you unsubscribe from this blog or decide to move on to the next search result, it turned out that I really hadn’t done anything, so read on.
OK, so what was this magical error that just suddenly popped up out of nowhere when I hadn’t done anything wrong? Well, Silverlight was giving me some “none too helpful” information (gotta figure out how to get better info there). I did notice that it was after the control loaded and looked to be at the point that the app reached out to my WCF service for some data. So, i decided to try and access the WCF service directly and that’s when I got the error. It looked something like this:
Server Error in ‘/ISV/MyApp’ Application.
Could not load file or assembly ‘App_Web_xxxxxxxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IO.FileNotFoundException: Could not load file or assembly ‘App_Web_xxxxxxxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Assembly Load Trace: The following information can be helpful to determine why the assembly ‘App_Web_xxxxxxxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’ could not be loaded.
WRN: Assembly binding logging is turned OFF. To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1. Note: There is some performance penalty associated with assembly bind failure logging. To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
Stack Trace:
[FileNotFoundException: Could not load file or assembly 'App_Web_xxxxxxxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.] System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0 System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +54 System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +211 System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +141 System.Reflection.Assembly.Load(String assemblyString) +25 System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses) +154 System.ServiceModel.HostingManager.CreateService(String normalizedVirtualPath) +516 System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +31 System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +498 [ServiceActivationException: The service '/ISV/SilverlightService.svc' cannot be activated due to an exception during compilation. The exception message is: Could not load file or assembly 'App_Web_xxxxxxxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified..] System.ServiceModel.AsyncResult.End(IAsyncResult result) +4413209 System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +183 System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(HttpApplication context, Boolean flowContext) +205 System.ServiceModel.Activation.HttpHandler.ProcessRequest(HttpContext context) +77 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +358 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +64
If you’re anything like me, your initial reaction was probably something like “App_Web_xxxxxxxx, WTF is that?”. If that sounded familiar, then you and I were in the same boat.
First response: start and stop the site. Result: no change.
Second response: rebuild and publish the site. Result: no change!
Third response: iisreset. Result: no change! grrrrr…
Fourth response: reboot the server. Result: no change! what the heck?
Now there are probably a few ASP.NET gurus that will (and are welcome to) comment on this and probably suggest that I’m foolish for not realizing what was going on. It stumped me for a few minutes, and that’s what I’m documenting. Anyway, my eyes shot to the Stack Trace and the secret for me was the exception. It’s a FileNotFoundException, which means that it was trying to find a physical file and couldn’t. Furthermore, I could see from the calls, like System.Reflection.Assembly.InternalLoad, that this wasn’t likely to be my code. Then it hit me. Part of the miracles of ASP.NET 2.0′s Site Pre-Compilation, your app gets compiled down and stored in the Temporary ASP.NET Files directory, typically at C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files.
We keep all of our custom files in the ISV virtual directory of the CRM 4.0 web site just as the guidance to do so describes. When I went back to check, CRM was behaving perfectly, as were all my custom pages except for my WCF services. This is where I begin speculating, but my guess is that at some point the WCF Service Host died a potentially horrible death at which point it cleaned up after itself including deleting its temporary compiled files. The ASP.NET web app on the other hand figured “nothing changed about me, so I’ll just leave those files in my temp directory and use them next time. Again, I don’t know if this is what actually happened, but it’s my best guess for now.
Deleting all the files from my Temporary ASP.NET files did in fact fix the issue, and I have yet to have it happen again. Knock on wood!
As it turns out, a bit more research revealed that there is in fact a hotfix, though people’s mileage has varied on this one. I didn’t apply it yet, but I will let you know if I have any more issues with this.
Hopefully this saves you some grief!
Cheers!
CRM deployment errors courtesy of SQL
Quick little post here, but not every CRM developer is a SQL developer and sometimes errors that come through the CRM interface aren’t actually CRM errors at all. A great example of this came my way during a deployment when I was importing some customizations. Now normally that’s my “push and pray” moment. Fortunately I have to pray less these days thanks to Hyper-V (take snapshot, got problems? revert and you’re A-OK – gotta love it!), but back to the story. So, I pressed the button and didn’t get the “all clear” from CRM. Uh-oh. Instead I got an error message that baffled me for a minute.
Cannot alter the table 'accountExtensionBase' because it is being used for replication.
What? There’s some replication in CRM that I didn’t know about? Then I thought for a second. This isn’t CRM at all. This was in fact an error message brought back from SQL who wasn’t happy about the fact that I was changing the schema of a table that it was replicating. For the CRM developers out there who aren’t familiar with SQL, it’s best said by the MSDN library:
Replication is a set of technologies for copying and distributing data and database objects from one database to another and then synchronizing between databases to maintain consistency.
Basically we use it to make sure that when changes are made we save the changes to another server almost real-time. This can save our behinds should something really bad happen. You can find more detail about the error here.
To solve this problem, it was a simple matter of tearing down replication, importing again, and replication could be setup again. Hopefully this helps anyone else who gets caught off guard by this message.
http://msdn.microsoft.com/en-us/library/aa226361%28SQL.80%29.aspx
VS2010 Launch – The Big Tease
First off for anyone who thinks this is a post to bash Microsoft, you’re reading the wrong blog.
So what’s this about? It’s a little about VS2010, a little about bad design, and a little about JavaScript. Ready? Good.
Alrighty then, today’s the big day when a few products were launched. Nothing big, just most of Microsoft’s developer toolset. Let’s see, there’s:
- .NET Framework 4
- ASP.NET 4
- Silverlight 4
- Visual Studio 2010
ASP.NET MVC 2 released about a month ago, but those guys are a little off, right Phil?
But I digress. The main lead-up to this was a launch event that is happening in several places around the world and perhaps most notably in Las Vegas in conjunction with DevConnections. Go to Microsoft’s website more than an hour before the keynote and you were presented with a countdown timer to help build the suspense. I for one was in suspense. I had held my laptop in a pristine state since I didn’t want to worry about any hassles associated with previously installed betas or release candidates. So I watched the countdown go to 0, and nothing happened. OK, I’ve seen this happen before and I know how conferences go, so I check back every 5 minutes or so. 5 minutes later, nothing. 10 minutes later, nothing. 45 minutes later, nothing! What the heck?
How could I be watching this timer for over an hour and not the keynote? About then my brain kicked in where someone else’s didn’t. Since this thing was straight HTML and not Silverlight or Flash, I decided to view the page source and see if my suspicions were right. Now, I had the page source ready to show you, but unfortunately my browser crashed while watching the keynote (ironic, I know), so I lost that. However, the gist of it is this. Whoever made this page decided to embrace jQuery (smart move #1) and avoid writing some ad-hoc code when something else existed (smart move #2). Unfortunately that’s where Smart Move Rd. intersected with Not Quite Blvd.
The code that they chose to make use of was a simple jQuery plugin to count down the time left until a specified date. The JavaScript/jQuery code (per Keith Wood’s site and my memory) was setup something like this:
var keynoteTime = new Date();
keynoteTime = new Date("April 12, 2010 08:30:00");
$('#countdown').countdown({until: keynoteTime});
That seems straightforward enough, doesn’t it? Of course it does, and it is. This plugin is great for certain uses, there was just one little detail that they didn’t consider. You see, the conference was in Las Vegas and I’m in Florida. JavaScript is a client side technology, and my client was on Eastern Time Zone time, hours ahead of Las Vegas time. So, I would have been staring a “0 hours, 0 minutes, 0 seconds” for another 3 hours before finally viewing the launch webcast. It sounded like a great idea at the beginning though, didn’t it? Oh well, I only had to wait another 5 hours to have Akamai serve me the VS2010 bits, but I’ve got ‘em!
NOTE: I don’t know for sure that the code used was Keith Wood’s Countdown jQuery plugin, just a hunch. Either way, you get the idea.
Making Silverlight content region occupy all available browser space
I know, I know. It’s been a while and I’ve been busy. I will do what I can to make up for it. Here’s the first.
So as I’ve mentioned, I’m starting from ground zero on Silverlight and I’m documenting stuff I ran into that I found frustrating so that it may help some other poor soul and so I have a place to check when it happens again (and it probably will). This little bugger was particularly frustrating because I wasn’t focused at the time. Writing good multi-threaded code does not mean that you can write good code when you’re multi-threading.
So what was I trying to do? I had already written my XAML and my code behind written and they were working fine. All I needed to do now was to make my Silverlight control take up all the space available in the browser window. Simple, right?
Well, it should have been. I tried setting the LayoutRoot’s height and width to Auto. No good. Set height and width to 100% on the <object> tag. Nope. Time to reference the library. Pro Silverlight 3 in C#, surely that must know. It says:
“… you might prefer to remove the Width and Height attributes from the <UserControl> start tag. That way, the page will be sized to match the Silverlight content region, which in turn is sized to fit the browser window, and your Silverlight content will always fit itself into the currently available space.”
Tricky wording there. It will fit itself into the currently available space, but it isn’t sized to take all the available space, which is what I wanted. So what was the key? When you have Visual Studio create a new Silverlight test page, you’ll notice that it adds a style, a style that I didn’t have. It looks like this:
<style type="text/css">
html, body {
height: 100%;
overflow: auto;
}
body {
padding: 0;
margin: 0;
}
#silverlightControlHost {
height: 100%;
text-align:center;
}
</style>
As soon as I added that to my page, I got just what I was looking for. I should mention here that this makes the assumption that your silverlight <object> tag is nested in a div whose id is silverlightControlHost. Give it a shot and I hope this helps!
html, body {
height: 100%;
overflow: auto;
}
body {
padding: 0;
margin: 0;
}
#silverlightControlHost {
height: 100%;
text-align:center;
}
</st<style type=”text/css”>
html, body {
height: 100%;
overflow: auto;
}
body {
padding: 0;
margin: 0;
}
#silverlightControlHost {
height: 100%;
text-align:center;
}
</style>yle>
Easy Access to the crmForm.ObjectId from CRM 4.0 entities
Note: The content in this post, like most of my posts, is likely unsupported.
Warning: This is a lot of content for a very few lines of code. I ramble so that you can learn the how and the why. More advanced developers and/or less patient readers may want to go straight for the code.
If you’re anything like me, you spend a lot of time in CRM. I love it, as does most anyone I’ve met who’s tried it. Having said that, there are a few things about it that are intentionally hidden from the user’s view and with good reason.
A great example of that is the address bar. For most of us who deal with CRM, we know that CRM keeps track of entities by using GUIDs. Furthermore, we know that the easiest built-in way to get the current entity’s GUID is to get it from the URL. One quick way to do that is to hit F11 which will cause the current browser window to go full screen and the address bar is available by moving your mouse to the top of the window. It’s a little annoying because it usually takes me 3 steps:
- You have to hit F11 and move the mouse to the top of the window to show the address bar.
- You have to click in the address box, which usually makes the address bar hide itself, so you have to click in it again.
- You have to highlight only the GUID before copying it, or copy and paste the whole URL and remove everything but the GUID.
Now if you haven’t been exposed to that method, you’re welcome, but hang on because this post should save you the trouble. 3 repetitive steps that annoy me and are preventable are usually enough for me to search for a programmatic solution. I’m a developer and I live by DRY. (Don’t repeat yourself)
So there are many ways to accomplish this I’m sure, but here’s my way. I should mention here that I tend to deal with the database side of things a great deal as well, so I knew from the beginning that I wanted to copy the GUID to my clipboard.
Initially my thought was to add a button to the toolbar, or maybe even add a copy command to the actions menu, but while I was making this little snippet I realized that often times I’m running database queries against these GUIDs and it would be nice to see the GUID on the screen at all times. I decided to tap into the status bar. For all the entities I checked it’s always down there, and it seems to always have room enough for some other text, say… a GUID.
My approach: This is actually pretty straight-forward. Each entity’s form has a crmForm.ObjectId (yes, case sensitive) that is the GUID for the current record. So, I figured I would add some JavaScript to embed the ObjectId in a UI element on the form. Nowadays, anytime I hear JavaScript I think jQuery. So, how to get jQuery on a CRM form? This has been documented in several places on the interwebs. Before I post it here online, I need to ask the person I got it from if I can share this particular approach.
Now that you’re referencing jQuery, you have a little horsepower behind you. So, let’s add the line that will show the GUID in the status bar:
$("#crmRenderStatus").closest("td").append("<span id='GetObjectId'>" + crmForm.ObjectId + "</span>");
Huh? What’s all that? Let’s break it down:
- First, it finds the element whose id is crmRenderStatus.
- Next, it finds the closest td tag (in this case, its parent td)
- Then, it appends the HTML that I’m specifying while injecting the crmForm.ObjectId
Here’s the result:
What you wind up with is quite simply the GUID nested in a span tag, positioned right next to the Status on the status bar. It’s important to note that the fact that I’ve tucked the content in a span tag with a specific ID property is both intentional and required. Great! Now I can see the GUID, but I can’t copy it. How can I fix that? Easy enough. Just use good old jQuery again and wire up a double click event handler to the GetObjectId span that contains the GUID, like this:
$("#GetObjectId").dblclick(function() {
window.clipboardData.setData('Text', this.innerHTML);
});
Does that look like Greek? OK, let’s break that down:
- First, we’ll find our newly added span element with the id of GetObjectId.
- Next, we’ll attach a handler to the double click event (jQuery .dblclick method).
- Finally, we’re defining an anonymous function that details what we want to do when the double click event fires.
Now this works, but you don’t get any visual indication that you’ve copied the GUID. To handle this, I’m changing the “UI modification” line to include a span that just has the text ” Copied…”. This line starts out invisible since you haven’t copied the GUID until you double click on it.
$("#crmRenderStatus").closest("td").append("<span id='GetObjectId'>" + crmForm.ObjectId + "</span><span id='ObjectIdCopied' style='display:none;'> Copied...</span>");
I’m also going to change the handler’s anonymous function to show the ” Copied…” text by fading it in and to allow them to get a second copy indication by fading it out.
$("#GetObjectId").dblclick(function() {
window.clipboardData.setData('Text', this.innerHTML);
$("#ObjectIdCopied").fadeIn("fast").fadeOut("slow");
});
This results in a brief moment that looks like this (insert imagined fade-in and fade-out here)
![]()
There we have it. A few lines of code that save me some hassle many times throughout the day. You can see the detail in this post by Jim Wang, but the other thing that I do is wrap this in an if statement that checks to make sure that all this only happens if you’re a System Administrator or System Customizer. Alright, I warned you this was a long post for such a small amount of code. Here’s the code all wrapped up. Hope this helps!
if (UserHasRole("System Administrator") || UserHasRole("System Customizer"))
{
$("#crmRenderStatus").closest("td").append("<span id='GetObjectId'>" + crmForm.ObjectId + "</span><span id='ObjectIdCopied' style='display:none;'> Copied...</span>");
$("#GetObjectId").dblclick(function(){
window.clipboardData.setData('Text', this.innerHTML);
$("#ObjectIdCopied").fadeIn("fast").fadeOut("slow");
});
}
There is one other addition to all of this which you may have noticed. We use a UserHasRole method to make sure that I’m not showing everyone the GUID. That’s not because I want to keep it a secret, it’s because I don’t need users telling me that CRM is emiting corrupted text.
Cheers!





