Wednesday, February 12, 2014

Hiding Tab Tools using JQuery

If you've logged into Salesforce a million times or more like me, you may have trained your eyes to ignore some of the subsections on the standard tabs.  For example, when you click on the Accounts tab, there is a Tools section with all kinds of goodies that you may not want your users to know they have the power to do.  I mean, does "Mass Delete Accounts" sound like a link you want someone to click on... ever?!



In most cases you can control access to these links by some profile permission or user permission.  For example, if you remove the "Modify All Data" setting on the profile, most of the links above go away. However, you may encounter a need to give someone a very broad permission like "Modify All Data" or "Transfer Records", but want to restrict their ability to see their access these tools.  One option to evaluate is using jquery to scrub out the links from the ui.  Borrowing some ideas from stackexchange, a little script like this to the home page (as an html component) has the ability to remove any link (or the entire section).  In my use case, I want to remove the ability to "Transfer Accounts" from this tab except by my power user:

******
<script type="text/javascript" src="/soap/ajax/27.0/connection.js"></script> 
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$j = jQuery.noConflict();
$j(document).ready(function()
{  
var sid = getCookie('sid');  
var server = "https://" + window.location.host + "/services/Soap/u/27.0";  
sforce.connection.init(sid, server);
var currentUser = sforce.connection.getUserInfo();   
if(currentUser.profileId != 'SOMEPOWERUSER')  
{  
var url = location.href;
var tabUrl = "/001/o";   
if(url.indexOf(tabUrl) !== -1)  
{  
$j('.toolsContentRight a').each(function() 
{  
if ($j(this).text() == 'Transfer Accounts')
{  
$j(this).text('');  
}  
 
});   
}     
}});
</script>

*******

Mind you, that this does not remove their permission to do these things.  It just makes it less obvious by removing it from the tab.

Preventing Your Form from Submitting Twice (or more!)

If you have a visualforce page that does any kind of update or insert and you're using a custom method on your controller, be mindful of your aggressive clickers!  They'll get you in trouble by submitting your form more than once, wreaking havoc on all of your hard work.

I just dealt with this and was surprised that I was not able to use the apex:actionstatus and facet combination that I typically use to disable buttons that initiate queries.  Turns out that the important detail is that the actionstatus tag requires us to rerender a component.  When this rerendering occurs, you potentially get another form submission.  It may have to do with the redirect my page was doing after the save.  The good folks over at the stack exchange saved my bacon once again.  Their approach to disable your button is the following:

Button click calls an actionfunction js.  The actionfunction does some javascript to disable the buttons using jquery, then calls the actionfunction component, which describes the controller method to run and what to do when the method completes.

I changed up the accepted stack exchange solution in two ways:

1. The button, while gray and disabled, still behaved like a button (it was still clickable).  I ended up using jquery to remove the btn class (which makes it clickable) and also restore the btn class, after any page messages/errors are displayed:

function buttonsEnabled(enabled) {
        // to disable the button
        if (enabled === false) {
            var $b = jQuery('.btn');
            $b.removeClass('btn');
            $b.addClass('btnDisabled');
            $b.toggleClass('btnDisabled', true).attr('disabled', 'disabled');
        } else {
            var $b = jQuery('.btnDisabled');
            $b.toggleClass('btnDisabled', false).attr('disabled', null);
            $b.removeClass('btnDisabled');
            $b.addClass('btn');
            
        } 
    }

2. If you have some validations on your page, you'll want to be sure you're re-rendering the messages tag in your action function.  In this example, my pagemessages has the id "msgs".

<apex:form id="form">
<apex:actionFunction name="doSomeWorkActionFunction" 
        action="{!mySave}" 
        oncomplete="buttonsEnabled(true);"
        rerender="msgs">
</apex:actionFunction>
<apex:pagemessages escape="false" id="msgs"/>
<apex:pageblock mode="Detail" id="pageblock" >
    <apex:pageblockButtons >
        <apex:commandButton id="mysavebutton" 
                  onclick="return doSomeWork();" value="Save"  />
        <apex:commandButton action="{!myCancel}" value="Cancel" immediate="true" />
    </apex:pageblockButtons>