Friday, January 25, 2008

Hiding Buttons in MSCRM 4.0

This weeks post comes from one of my co-workers. We've set up a number of solutions for clients where we hide a custom button or even the "Convert Lead" button.

If you have hidden buttons in CRM 3.0 you know that the getElementById method is used to find the Id of the button you want to hide and then you use DHTML to hide the button.

In CRM 4.0 there is some auto-incremented number or randomly generated number that gets placed at the end of all of the ElementId's on a page. Which means that the code used for CRM 3.0 will break.

The solution we found for this is to use the "title" tag, which is linked to the tooltip of your buttons. Here's the code we used to hide a custom button we wrote to map out driving directions to a client:

//Get all of the List Elements
var lis = document.getElementsByTagName('LI');

var i = 0;
//Loop through the list items
while (i < lis.length) {
//Don't worry about any list item that doesn't have the title you are looking for.
if (lis[i].getAttribute('title') == 'View directions to this account.')
{
//Replace the DHTML with blank tags to hide the button
lis[i].outerHTML='<SPAN></SPAN>'
}
i = i + 1;
}

And you're all set. Happy scripting!


If you want to hide the "Add Existing" button you can use the following script (posted by Dave Hawes):

HideAssociatedViewButtons('Account_New_Project', ['Add existing Project to this record']);

function HideAssociatedViewButtons(loadAreaId, buttonTitles){
var navElement = document.getElementById('nav_' + loadAreaId);
if (navElement != null) {
navElement.onclick = function LoadAreaOverride() {
// Call the original CRM method to launch the navigation link and create area iFrame
loadArea(loadAreaId);
HideViewButtons(document.getElementById(loadAreaId + 'Frame'), buttonTitles);
}
}
}

function HideViewButtons(Iframe, buttonTitles) {
if (Iframe != null ) {
Iframe.onreadystatechange = function HideTitledButtons() {
if (Iframe.readyState == 'complete') {
var iFrame = frames[window.event.srcElement.id];
var liElements = iFrame.document.getElementsByTagName('li');

for (var j = 0; j < buttonTitles.length; j++) {
for (var i = 0; i < liElements.length; i++) {
if (liElements[i].getAttribute('title') == buttonTitles[j]) {
liElements[i].style.display = 'none';
break;
}
}
}
}
}
}
}


David Fronk
Dynamic Methods Inc.

46 comments:

Michael Dodd said...

Are these rendomly assigned numbers static, or are they dynamic on a record to record basis?

If the later, and getElementById is a wash, I assume that any custom buttons will require a tooltip for this approach to function.

Dynamic Methods said...

The numbers are assigned each time you open a record. So, if you open a record today, it could have a number of 1234 in the elementid. Tomorrow however, if you open the same record the elementid will not be 1234, it could be 1423 or some other number. That's why it's hard to tell if the numbers are sequential or random, because each time you open a record its different. I haven't done a ton of research into how that number is generated because I figured it would be faster to find another way around the problem than to figure out and mimic their number generation for my code to hide a button.

Hope this helps a bit.

David Fronk
Dynamic Methods Inc.

Michael Dodd said...

Does this have anything to do with Multi-tenancy? I just can't understand the need to change something like this so drasticly (and randomly to boot).

I guess this is why we get warned about using unsupported customizations. Seems that many of our clients will require a lot of re-working once they upgrade...

Dynamic Methods said...

Unfortunately I don't know the in's and out's as to why MS has decided to do things this way, or even whether it is related to Multi-tenancy. Though I personally don't think that it is related to multi-tenancy due to the fact that multi-tenancy is handled in the first part of the URL via the org name.

But yes, this is the risk run by going down the unsupported route.

David Fronk
Dynamic Methods Inc.

Nate said...

Thats a great find. I've used this code several times now and it works perfectly. You saved me hours of dev time. Thanks David, youre truely a CRM MVP!

darksander said...

super useful hack. thanks a lot for the idea, as I did find a place in my customizations which was crying for that :)

Unknown said...

Great workaround you found there! I have tried and it simply worked!
I have a question for you though: do you know if it's possible to hide a button dynamically in runtime? I would like to hide a button as soon as a user types something in a specific field. If he doesn't type anything he has access to the button. I don't know if it's possible to do it. Thank you!

Regards,

Sergio

Unknown said...

I left a comment a couple of minutes ago and I would like to tell you that I managed to hide a button in runtime using the onChange field event and the onLoad event for the entity (to keep it hidden after being saved).
Thank you again!

Regards,

Sergio Coelho

Dynamic Methods said...

Thanks for your comments Sergio, that's great to hear that it all worked for you. Were you able to use the same code in an onChange to get it to hide the button at runtime? I'll have to play with it myself now.

Thanks again,

David Fronk
Dynamic Methods Inc.

Unknown said...

Hi again David!

I was able to hide the button at runtime by inserting your code in an onChange event field. To keep it hidden afterwars I also had to apply the code to the onLoad entity event.

I will post this at my blog (http://crm4dummies.blogspot.com) and of course I will give you full credit for the code.

Thank you, regards,

Sergio

Dynamic Methods said...

Sergio,

Thanks for the heads up. That's a great post. For anyone looking for another good blog, check out Sergio's blog at
http://crm4dummies.blogspot.com
He's got some great posts on HTML emails out of CRM (which can be a pain in CRM if you don't know what you're doing), data imports, report wizard, etc.

Thanks again Sergio.

David Fronk
Dynamic Methods Inc.

Anonymous said...

Good post!

I was wondering if there is a way to add an option to the Actions menu in MSCRM 4.0? Thanks.

Dynamic Methods said...

Dave,

Yes, you can add to the Actions menu. If you look in the SDK there are numerous examples. What you want to do is look at the ISV.config and add MenuItem tags to the Menu tags within the XML. You have the availability to add to the main screen or any entity in the system. The SDK articles that I think will help you the most are:

ISVConfig Element
ISV.Config XML Reference
Sample ISV.Config

I think that should give you what you are looking for.

And in case I misunderstood the question, since this post was on hiding things, yes you can even hide menu items. I have hidden the Close Opportunity option to keep users from closing an opportunity too early in the process.

Hope this helps,

David Fronk
Dynamic Methods Inc.

Anonymous said...

Using the code above, if you hide a button and then manually resize the window you will most likely get a popup like "Microsoft Dynamics CRM has encountered an error...". This is due to the dynamic handling of buttons and text labels in CRM. CRM expects to find a number of elements below the LI-element and will fail when they have disappeared which is the case when changing LI to a single SPAN. The solution is to add 4 empty SPANS like this:

lis[i].outerHTML='<SPAN> <SPAN> <SPAN> <SPAN> </SPAN> </SPAN> </SPAN> </SPAN>’

Dynamic Methods said...

Trond,

Thanks for the insight, it's always nice to see the code working in any given situation.

David Fronk
Dynamic Methods Inc.

Mehul Mehta said...

Thanks David and Sergio,

i did tried your code which works perfect for hiding the button onload but can you help me how to show it again on some condition i.e. onchange of some picklist.

regards,
MEHUL

Dynamic Methods said...

MEHUL,

Use the exact same code that has been posted and just put it inside your logic on an onChange of whatever field you want. That's all you should need to do and you'll be all set.

David Fronk
Dynamic Methods Inc.

Anonymous said...

Is there a way to HIDE a button in the main CRM 4.0 web-client work area, such as the delete button? For example if you click the Account menu in the nav-bar a default view of Account is listed along with a set of button, delete being one of them. What if you don't want a record deleted if it meets a certain condition? Would a plugin be better here?

Mehul Mehta said...

Thanks David, but i don't think that it will again show/display/unhide button on some condition after it's been hidden at onload event of th form.

Dynamic Methods said...

Mehul,

I have had a coworker use this code on an onChange event successfully as well as someone else posting comments. Both have had success with it. I didn't that it would work either since it was modifying the DHTML but it worked. If you haven't tried it then I would say try it first. If it doesn't work then we can see if there is another way to do this. But to my knowledge this should work for both onLoad and onChange on a form.

David Fronk
Dynamic Methods Inc.

Dynamic Methods said...

Hiding a button in the list view is difficult at best. I personally have not come across any way to do this. A plugin would be a great way to prevent people from deleting records that meet a certain criteria. That's what I would suggest. Either that or remove their rights to delete and tell them to deactivate records.

David Fronk
Dynamic Methods Inc.

Stephen Maij said...

Hi,

I use this code (with GetElementById) and it works fine for me.

I didn't know the window resize bug.. Will try to find a solution for it.

var Hide = function(menuItem){
if (document.getElementById(menuItem) != null)
{
document.getElementById(menuItem).style.display = "none";
}
}

var RemoveSpacerAfter = function(menuItem){
if (document.getElementById(menuItem) != null)
{
var item = document.getElementById(menuItem);
if (item.nextSibling != null)
item.nextSibling.style.display="none";
}
}

// QUOTE SCRIPT
RemoveSpacerAfter('_MBprintQuote');
Hide('_MBprintQuote');

Hide('_MBcrmFormSubmitCrmForm1truetruefalse');
RemoveSpacerAfter('_MBLookupAddress');
Hide('_MBLookupAddress');

Hide('_MIcrmFormSubmitCrmForm1truetruefalse');
Hide('_MILookupAddress');
Hide('_MIgetProducts28');
RemoveSpacerAfter('_MIactivateQuote');
Hide('_MIactivateQuote');

RemoveSpacerAfter('_MBAcceptQuote');
Hide('_MBAcceptQuote');

Hide('_MIAcceptQuote');
Hide('_MIreviseActiveQuote');


//ORDER SCRIPT
Hide('_MBcrmFormSubmitCrmForm1truetruefalse');
Hide('_MBLookupAddress');
Hide('_MIonActionMenuClickdelete1088');
RemoveSpacerAfter('_MIonActionMenuClickdelete1088');
Hide('_MIcrmFormSubmitCrmForm1truetruefalse');
Hide('_MILookupAddress');
Hide('_MIgetProducts43');
Hide('_MIcloseOrder3');
Hide('_MIcloseOrder2');
Hide('_MIlock');

Kind regards,

Stephen
Visit my website for more details

Dynamic Methods said...

Stephen,

Thanks for the code sample, that's great.

David Fronk
Dynamic Methods Inc.

Anonymous said...

good post

Ganesan Nagarajan said...

Guys I am trying to hide a Button "Add Existing Address" In the Contact Entity, But i am not able to Successfully hide using the below method you guys were using. Pls Help me to resolve this, If can pls Give me one real entity Example.

function HideAssociatedViewButton(loadAreaId, buttonTitle){ var navElement = document.getElementById('nav_' + loadAreaId); if (navElement != null) { navElement.onclick = function LoadAreaOverride() { // Call the original method to launch the navigation link loadArea(loadAreaId); var associatedViewIFrame = document.getElementById(loadAreaId + 'Frame'); if (associatedViewIFrame != null) { associatedViewIFrame.onreadystatechange = function HideTitledButton() { if (associatedViewIFrame.readyState == 'complete') { var iFrame = frames[window.event.srcElement.id]; var liElements = iFrame.document.getElementsByTagName('li'); for (var i = 0; i < liElements.length; i++) { if (liElements[i].getAttribute('title') == buttonTitle) { liElements[i].style.display = 'none'; break; } } } } } } }}

Thanks In Advance,
Ganesh

Dynamic Methods said...

Ganesh,

The key is figuring out the loadAreaId and the buttonTitle. The loadAreaId is a combination of the names of the entities. For instance, if I am on an Account and I have a custom relationship with Projects (new_project) then the name would be nav_account_new_project. The code you posted (which came from Dave Hawes blog) adds the "nav_" to the beginning of your area. So, your loadarea in this case would be "account_new_project". If you do a view source, or use the IE Developer Tool, you can easily find the name of NavArea you are trying to gain access to.

Once you have that, the buttonTitle is the Tooltip of the button you want to hide. Typically it is "Add existing [EntityName] to this record". So, for Project it would be:
"Add existing Project to this record"

You can use the formula I mention here, but the for sure way is to get it from the source of the page.

For anyone not aware of this here is the code straight from Dave Hawes' blog (http://blog.davehawes.com/post/2008/04/23/MSCRM-4-Remove-Add-Existing-xxxxx-button.aspx)

HideAssociatedViewButtons('Account_New_expenses', ['Add existing Expense to this record']);

function HideAssociatedViewButtons(loadAreaId, buttonTitles){
var navElement = document.getElementById('nav_' + loadAreaId);
if (navElement != null) {
navElement.onclick = function LoadAreaOverride() {
// Call the original CRM method to launch the navigation link and create area iFrame
loadArea(loadAreaId);
HideViewButtons(document.getElementById(loadAreaId + 'Frame'), buttonTitles);
}
}
}

function HideViewButtons(Iframe, buttonTitles) {
if (Iframe != null ) {
Iframe.onreadystatechange = function HideTitledButtons() {
if (Iframe.readyState == 'complete') {
var iFrame = frames[window.event.srcElement.id];
var liElements = iFrame.document.getElementsByTagName('li');

for (var j = 0; j < buttonTitles.length; j++) {
for (var i = 0; i < liElements.length; i++) {
if (liElements[i].getAttribute('title') == buttonTitles[j]) {
liElements[i].style.display = 'none';
break;
}
}
}
}
}
}
}

Hope this helps,

David Fronk
Dynamic Methods Inc.

Anonymous said...

Great information, thank you for this nice post.

Anonymous said...

Can you help me figure out how to selectively (based on user) hide the "Convert Lead", "Delete Lead", and "Assign" buttons and menu items? I want users to enter anything and everything as a lead, but only a few users to be able to convert, delete, or assign those leads, because each of those actions kicks off workflow that most users shouldn't be able to start. Please advise. Thanks.

Dynamic Methods said...

The best way to hide the "Assign" and "Delete" buttons is to restrict their access on those privileges on the Lead in their given role. No custom code, 30 seconds of clicking and you're done. As for converting, you either have to restrict Create privileges on Account, Contact and Opportunity, or you would want to hide the button.

There are a couple of ways that I've seen this done. One way is to have a status field to encourage users to qualify their leads before they convert them. Get the lead to a certain stage (picklist value) and then use script to hide the button if the status isn't a given value. The other option is to do a query via FetchXML in your script to get the role of the given user who is accessing the page. If the role is a specified role that is allowed to convert leads, then show the button. Otherwise, hide it. You could set up a role called "Lead Converter" and anyone with that role would then get to see the button. Everyone else doesn't.

I won't put all of the FetchXML query in this comment but you definitely can find it by doing some basic searches.

Hopefully this gets you on your way.

David Fronk
Dynamic Methods Inc.

CRMShare said...

I am trying to hide the button "Add a Campaign" from Marketing List form.

The these are the values i am sending and i am still getting error any idea?

HideAssociatedViewButtons('navCampaignsInList', ['Add to Campaign']);

Dynamic Methods said...

CRMShare,

The ID that you should probabaly use would be:

_MIdoActionnull4300addtocampaigncrmFormSubmitcrmFormSubmitIdvalue

However, that ID doesn't follow the standard rules that the other ID's have whenever I've hidden buttons before. So, the script in this post may not work, you may have to tweak it further in order to make it work.

Good luck,

David Fronk
Dynamic Methods Inc.

Unknown said...

David,
I'm trying to figure out how to hide the "new opportunity" button on the account screen using this function call:
HideAssociatedViewButtons('Account_New_Opportunity', ['Add a new Opportunity to this record']);

I haven't had any success, which makes me wonder if I'm even putting this on the correct form. Would this to on the account form onload? Thanks for your help!

Dynamic Methods said...

Juggle12,

It looks like a fair number of the the out of the box relationships don't follow the same naming convention as the custom relationships. "navOpps" looks like the "loadAreaId" you will want. However, whenever I use that I end up in hiding the entire area that it is supposed to show. I found the same thing occuring when I tried this for attempting to hide the Campaign buttons. I'm going to have to research out of the box relationship calls to see if there is an easy way to do this for all entities.

Sorry I didn't have the answer readily available, but hopefully I'll find one soon.

David Fronk
Dynamic Methods Inc.

Anonymous said...

Hi David,

How can i hide the new button using the same code for hiding 'add existing button'. the loadareaid for the new button is a guid for the related entity.

Many thanks

Bidhan Chakraborty said...

Hi There,
i have tried the above two functions in the Roll up 4 and 5 of CRM but did not work at all.
the following line
var navElement = document.getElementById('nav_' + loadAreaId);
always get null values and which does not allow to proceed to the next step.
even though i tried with
var navElement = document.getElementById('nav' + loadAreaId); //rather 'nav_'

but still did not work at all.
someone please help me out.
Thanks in advance

Dynamic Methods said...

To hide the "New" button that is right next to the "Add Existing" button you use the exact same code. The only difference will be your call to the HideAssociatedViewButtons function. You will want to take whatever is in the ID for the NavBarItem in the source of the page, remove the "nav_" and add the rest. Then add the tooltip text as the second parameter and you're all set. An example of the call would be:

HideAssociatedViewButtons('dm_dm_asset_dm_assetlog', ['Add a new Asset Log to this record']);

And the ID of that item was nav_dm_dm_asset_dm_assetlog. That should get you taken care of.

David Fronk
Dynamic Methods Inc.

Dynamic Methods said...

Bidhan,

Have you verified that the ID of the item you are trying to hide begins with "nav_"? I have seen a couple of items that do not and for those I've had to modify the script. There were some that I found that do not start with "nav_" (system relationships) and was unable to hide those. That's not to say that they couldn't be hidden though. This script will handle most cases but the few exceptions will need to have this script tweaked.

I have had this work in multiple rollup 4 and 5 environments.

David Fronk
Dynamic Methods Inc.

Anonymous said...

Does anyone know a way to hide the 'Send Direct EMail' button in the 'Grid Toolbar'?

Which is the correct route to take here; I would expect to see a security setting for this given it's importance.

Where would the code go in this instance?

Thanks,
Simon

Dynamic Methods said...

Simon,

You would have to hack the main CRM page in order to hide that button. The better way to do this would be to remove Read rights from the "Email Template" privilege on any role and that user will no longer be able to see the "Send Direct E-mail" button.

David Fronk
Dynamic Methods Inc.

Anonymous said...

David,

I tried this but the user can still see the 'Send Direct E-mail' button, and as long as there is a valid email in the related field the template email is sent out. Perhaps there is another security setting that works in combination with this that I may have missed off?

Thanks,
Simon

Anonymous said...

David,
My apologies - ignore the last response. There was a c360 default role that had this setting - once I removed it from there the 'Direct Email' button is disabled.

Many thanks for the fix.

Regards,
Simon

Dynamic Methods said...

Simon,

Glad to hear that worked out.

David Fronk
Dynamic Methods Inc.

Anonymous said...

Hi David,

Can you share the code that hides the "Convert Lead" button in the Lead entity?

Dynamic Methods said...

This should hide the "Convert Lead" button for you:

CRM 4.0
//Get all of the List Elements
var lis = document.getElementsByTagName('LI');

var i = 0;
//Loop through the list items
while (i < lis.length) {
//Don't worry about any list item that doesn't have the title you are looking for.
if (lis[i].getAttribute('title') == 'Qualify or disqualify the lead')
{
//Replace the DHTML with blank tags to hide the button
lis[i].outerHTML='<SPAN></SPAN>'
}
i = i + 1;
}

David Fronk
Dynamic Methods Inc.

Unknown said...

Hi David, thank you for such helpful blog post :) I tried it and it works!

I have a question, is there a way to hide the 'Export to an Excel Worksheet' button on the Account view? Here's the screenshot:

http://i170.photobucket.com/albums/u277/kuroneko1313/exporttoexcel.jpg

We don't want any users to do this... so we take away the 'Import to Excel' privilege on all Security Roles (except for the System Administration and System Customizer).

So this removes the Excel button on the Account page. However, we noticed that removing this privilege also removes the 'Export to' button in the CRM Reports.. and users are complaining that they cannot export their reports to Excel... is there a way to bring this ability to Export Reports but not the one on the Account level? I'm also looking at removing the Excel button on the Account view page... but no luck yet.

Thanks in advanced for your help!

-Elisabeth K.

Dynamic Methods said...

Elisabeth,

Security is the only way to do that...that is without hacking the base Microsoft CRM web page...which I do not recommend, and it's not supported. Unfortunately at this time exporting to Excel in all cases is an all or nothing kind of privilege.

David Fronk
Dynamic Methods Inc.

Post a Comment