Category Archives: Salesforce

Mass Editing Profiles in List Views

Here’s an oldie, but goodie tip I picked up a while back. It came in use today, and I thought I’d share it. We had a new custom object deployed to production and needed to give permissions to Create, Edit, Update and Delete records. This wasn’t done when the custom object was deployed, so I needed to assign these permissions profile by profile. Using the edit in list view functionality built into Salesforce, I was able to update all my profiles in one fell swoop.

1. Create a list view showing the permissions you want to change:

Creating a custom list view

2. Select the profiles you want to make the changes to:

Select Multiple Profiles

3. Double click on the field you want to change and select what you want the new value to be as well as which records to apply the change to:

Mass Edit Permissions

And there you have it! I could have also created a permission set with the additional permissions, but in this case I felt it was better to be at the profile level.

Debugging with JSON.serializePretty

Here’s a quick tip that I thought I’d share. Today I was trying to troubleshoot a trigger and needed to know what the Trigger.newMap and Trigger.oldMap looked like. Rather than put a lot of System.Debug messages for each field I was interested in, I put in a single debug statement to show me the entire object. I used JSON.serializePretty to make it look nice.

Here’s a quick example:

1
2
Account a = new Account(name = 'Test Account');
System.debug(JSON.serializePretty(a));

This gives you a nice looking entry in the debug log:

 20:43:48.070 (70043000)|USER_DEBUG|[2]|DEBUG|{
 'attributes' : {
 'type' : 'Account'
 },
 'Name' : 'Test Account'
 }

Accessing Salesforce Object Tabs Without Actually Creating a Tab

Here’s a quick tip I use quite often. Sometimes I don’t actually want to create and use up a tab in my org for custom objects, especially when the object is usually accessed through related lists on another object. Sometimes I want to see all the records without going through a parent. In those cases, you can access the tab page using a URL trick:

1. Figure out what the key prefix is for the object. This is the first three characters of the object ID. You can use http://workbench.developerforce.com to find this prefix. Go to Info -> Standard and Custom Objects and select the object you want. Expand the Attributes and look for keyPrefix.

2. Once you’ve got the prefix, point your browser to https://yourinstance.salesforce.com/prefix/o. So to get to Accounts, you’d go to: https://na12.salesforce.com/001/o.

A Poor Man’s Product Configurator

I was recently contacted by one of my blog readers to help him with a project. It sounded like an interesting challenge, so I agreed to help him with a little guidance and also post an overview of what we did so others could learn as well.

The business problem: let’s say your company makes custom widgets. A part of the sales process is creating a quote request. You don’t want your sales people to have a generic quote request form to fill out. Instead, you want to only show them the options based on the type of  product for which they are creating the quote request.

There are a few ways to accomplish this. I originally proposed a fairly complicated, but extensible model using related lists so you could configure an unlimited number of options. It turned out that the product model was a bit simpler and we could do it with just the product object and some Visualforce and a controller extension. My example here also uses field sets to make it easily configurable once it is in production.

To set up for this, I created a custom product object and a quote request object. On the custom product, I created a picklist field with options such as Group1, Group2, etc. On the quote request object, I created all the options as fields and then created field sets to group them together for display on the quote request entry page. The benefit of using the field set is that if I decide I need to add a new option to a product group, I can do it without having to add code. I just add the field to the relevant field set.

Here’s the Visualforce page. You can see the repeaters with the field sets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<apex:page standardController="QuoteRequest__c" extensions="QuoteRequestExtension">
  <apex:form >
    <apex:pageBlock >
      <apex:pageMessages />
      <apex:pageBlockButtons >
        <apex:commandButton value="Save" action="{!save}"/>
        <apex:commandButton value="Cancel" action="{!Cancel}"/>
      </apex:pageBlockButtons>
      <apex:pageBlockSection title="Custom Product" collapsible="false">
        <apex:outputField value="{!QuoteRequest__c.CustomProduct__c}"/>
      </apex:pageBlockSection>
      <apex:pageBlockSection columns="1" title="Options" collapsible="false">
        <apex:repeat value="{!$ObjectType.QuoteRequest__c.FieldSets.Group1}" var="f" >
          <apex:inputfield value="{!QuoteRequest__c[f]}" rendered="{!CustomProduct.ConfigGroup__c == 'Group1'}"/>
        </apex:repeat>
        <apex:repeat value="{!$ObjectType.QuoteRequest__c.FieldSets.Group2}" var="f" >
          <apex:inputfield value="{!QuoteRequest__c[f]}" rendered="{!CustomProduct.ConfigGroup__c == 'Group2'}"/>
        </apex:repeat>
      </apex:pageBlockSection>
    </apex:pageBlock>
  </apex:form>
</apex:page>

And here’s the Apex controller extension. It takes the product ID passed as a parameter to the page and queries the configuration group on the custom product to display on the Visualforce page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public with sharing class QuoteRequestExtension {
 
  public CustomProduct__c CustomProduct {get; set;}
  private final QuoteRequest__c qr;
 
  public QuoteRequestExtension(ApexPages.StandardController controller) {
    this.qr = (QuoteRequest__c)controller.getRecord();
    try {
      CustomProduct = [select id, ConfigGroup__c from CustomProduct__c where id=
                       :ApexPages.currentPage().getParameters().get('product')];
    } catch(Exception e) {
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, 'Error Getting Product Info'));
    }
    if (CustomProduct != null)
      this.qr.CustomProduct__c = CustomProduct.Id;
  }
}

The last ingredient to this is to add a lookup field on the Opportunity object. We can then add a custom button on to the Opportunity that takes the user to the quote request page and passes in the custom product ID from the opportunity.

Apex and Object References

I learned something today that I’m not sure how I made it this far without knowing. It was confusing the heck out of me, so I thought I’d share. When you assign one object to another through the use of the “=” operator, it doesn’t actually make a copy of the object. Instead, it makes a reference to the object. What does this mean? Let me try to explain with some code:

Account a = new Account(Name = 'Test');
Account b = a; //here we set a reference to a
b.Name = 'Changed'; //changing b also changes a
System.assertEquals('Changed', a.Name);

If you want to make a copy of the object, then you need to use the clone method to make a copy of the object:

Account a = new Account(Name = 'Test');
Account b = a.clone();
b.Name = 'Changed';
System.assertEquals('Test', a.Name);

Salesforce Dashboard Filters in Action

A few releases ago, Salesforce introduced Dashboard Filters. This allowed users to use one dashboard and dynamically change it using a drop down filter. This is an awesome feature because it means you need to maintain less reports and dashboards to deliver desired functionality. I hadn’t moved my reports over to this functionality yet, but I’m now glad I waited until the Spring ’12 release. With this release, they’ve made dashboard filters even more powerful.

First let me try to explain my data model and why I had a zillion reports to accomplish my dashboard requirements. We have reps who might cover a set of territories. The rep owns all the accounts, and we want to be able to report by territory (which is a custom field on the account) on the dashboards. Before filtering was introduced, I couldn’t do dynamic dashboards in this case and just have the dashboard display as the running user. It wouldn’t show the breakout by territory. As a result, for each territory, I had to create a separate set of reports to be used on a dashboard for each territory. Each dashboard had 13 components on it, so let’s do some quick math: 20  territories with 13 components each = 260 reports to maintain!

Enter dashboard filtering. Now with dashboard filtering, I can have 13 reports that are used by the dashboards. I can also reduce the number of dashboards to the number of reps we have. If a rep covers 4 territories, now I just need a single dashboard for that rep. The dashboard then has a filter so when you first pull up the dashboard, you see a roll up of the all territories covered by the rep. You can then drill in using the filter to see individual territories. Now I could get greedy here and switch to one dashboard that is a dynamic one based on the logged-in user. I chose not to go this route because I find it easier for all users to understand and they can also take advantage of the Chatter Feed for the dashboard to discuss a specific rep’s performance without everyone seeing it.

Creating a Dashboard Filter
Dashboard Filters Example

The Spring ’12 release gave us many more features that I’m sure I’ll use soon:

  • You can now create three filters per dashboard.
  • You can now add filters to dynamic dashboards.
  • You can post snapshots of filtered dashboard components to Chatter.
  • You can now filter by the usual operators (starts with, contains, etc)

Some caveats regarding dashboard filters:

  • You can’t filter dashboards with s-controls or Visualforce components.
  • You can only have 10 options for each filter, but according to the release notes, you can increase it to 50 by contacting Salesforce.
  • You can’t filter on bucketed fields.
  • The iPad Dashboard App doesn’t support filters yet so users will only see the first view of data.

Take a good hard look at the new dashboard filters features. They could save you a lot of headaches!

Printing a Salesforce Record to a DYMO Printer

Sometimes you might need to print records in Salesforce onto label. You have a few options. You could print them in batches using a custom Visualforce page, or a merge tool such as CongaMerge or Drawloop. For my use case, though, I needed to be able to print one-off labels on demand. DYMO has a JavaScript library that can interact directly with DYMO printers installed on the computer. To use, it, you must first install the DYMO software onto your computer. After installing it, make sure to close your browser completely and then open it again – watch out for background processes – they caused me a lot of grief because I couldn’t get it to work until I found all the background Chrome processes running.

You can get the latest version of the DYMO JavaScript Library here: http://labelwriter.com/software/dls/sdk/js/DYMO.Label.Framework.latest.js. DYMO doesn’t recommend using that URL for production use. Instead, you should download it and upload it as a static resource in Salesforce.

I wanted to create a custom button on a Case that when clicked would print a label with the case number in a barcode format. Accessing static resources from custom JavaScript button is a little tricky, but I found this hack to get it to work: http://jevonearth.blogspot.com/2011/08/using-static-resources-with-salesforce.html.

The code for the JavaScript is fairly simple. It builds the label xml, then prints it to the first DYMO label printer that it finds. You can change what is printed by modifying the label xml. There are a lot of examples on the DYMO blog.

{!REQUIRESCRIPT('/resource/' & LEFT(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(TEXT(NOW()),':',''),'-',''),' ',''),10) & '000/DymoLib')}
 
try
{
// open label
 
var labelXml = '<?xml version="1.0" encoding="utf-8"?>' +
'<DieCutLabel Version="8.0" Units="twips">' +
'<PaperOrientation>Landscape</PaperOrientation>' +
'<Id>Address</Id>' +
'<PaperName>30252 Address</PaperName>' +
'<DrawCommands>' +
'<RoundRectangle X="0" Y="0" Width="1581" Height="5040" Rx="270" Ry="270" />' +
'</DrawCommands>' +
'<ObjectInfo>' +
'<BarcodeObject>' +
'<Name>Barcode</Name>' +
'<ForeColor Alpha="255" Red="0" Green="0" Blue="0" />' +
'<BackColor Alpha="0" Red="255" Green="255" Blue="255" />' +
'<LinkedObjectName></LinkedObjectName>' +
'<Rotation>Rotation0</Rotation>' +
'<IsMirrored>False</IsMirrored>' +
'<IsVariable>True</IsVariable>' +
'<Text>' + '{!Case.CaseNumber}' + '</Text>' +
'<Type>Code39</Type>' +
'<Size>Medium</Size>' +
'<TextPosition>Bottom</TextPosition>' +
'<TextFont Family="Arial" Size="7.3125" Bold="False" Italic="False" Underline="False" Strikeout="False" />' +
'<CheckSumFont Family="Arial" Size="7.3125" Bold="False" Italic="False" Underline="False" Strikeout="False" />' +
'<TextEmbedding>None</TextEmbedding>' +
'<ECLevel>0</ECLevel>' +
'<HorizontalAlignment>Center</HorizontalAlignment>' +
'<QuietZonesPadding Left="0" Top="0" Right="0" Bottom="0" />' +
'</BarcodeObject>' +
'<Bounds X="331" Y="345.600006103516" Width="4386.5" Height="720" />' +
'</ObjectInfo>' +
'</DieCutLabel>';
var label = dymo.label.framework.openLabelXml(labelXml);
 
// select printer to print on
// for simplicity sake just use the first LabelWriter printer
var printers = dymo.label.framework.getPrinters();
if (printers.length == 0)
throw "No DYMO printers are installed. Install DYMO printers.";
 
var printerName = "";
for (var i = 0; i < printers.length; ++i)
{
var printer = printers[i];
if (printer.printerType == "LabelWriterPrinter")
{
printerName = printer.name;
break;
}
}
 
if (printerName == "")
throw "No LabelWriter printers found. Install LabelWriter printer";
 
// finally print the label
label.print(printerName);
}
catch(e)
{
alert(e.message || e);
}

Basic JavaScript Debugging with Firebug

A lot of my posts have been JavaScript heavy and I get a lot of questions from readers trying to adapt the code. The most frequent problems are with errors in the JavaScript syntax. Unlike Apex and Visualforce markup, you can save pages with embedded JavaScript with syntax errors and have no save errors. It is then maddening when you try to view your fresh new page and nothing happens. The JavaScript fails silently in the background and you have no idea what happened. This is when I turn to one of the developer tools such as the one built into Chrome or my personal favorite, Firebug.

To use Firebug, you first need Firefox installed. If you don’t have it, go get it now: http://www.mozilla.org/en-US/firefox/new/. I’ll wait. OK, next you need to install Firebug: http://getfirebug.com/. There are a million sites you can use to learn about Firebug, so I’ll just go over the basics. In the upper right of Firefox, you should a little bug. If it is gray, then Firebug isn’t active for the page; just click on the bug to give it some color and make it active. A panel will probably open at the bottom of your browser. This is the console and where you can see all the messages.

Now let’s debug some code! Here is a little Visualforce page that will cause you some problems:

[code]
<apex:page >
<script src="/soap/ajax/19.0/connection.js" type="text/javascript" />

<script type = "text/javascript">
function tryConsole() {
console.log("Link clicked");
sforce.connection.sessionId = ‘{!$Api.Session_ID}’;
var result = sforce.connection.query("Select id, name" +
"from Account limit 1");
var it = new sforce.QueryResultIterator(result);
while(itt.hasNext()) {
var record = it.next();
console.log(record);
}
}
</script>
<a href="javascript:tryConsole()">click here</a>
</apex:page>

[/code]

Make a new Visualforce page and copy the code above into it. When you click the “click here” link, the tryConsole JavaScript function will run. Give it a try so you can look at the Firebug console. It should look something like this:

So what happened? First it logged a message to the console using the code console.log(“Link clicked”). This is more preferable to doing an alert with a message in it because it doesn’t interrupt the flow of the page. Then we see the big whammy. There was something wrong with my SOQL query. There was a malformed query because I’m concatenating two strings together and forgot to leave a space between the two strings. I can see the error message right in the console and make some changes to the code to fix it. To fix this problem, just add a space between name and the closing double quote. Make the change, save the page and try clicking on the link again.

Oops, we still have an error:

This time, it looks like a typo in the code. The variable itt isn’t defined. Notice that there is a link in the console that takes you directly to the offending line of JavaScript. We can see that the variable is actually defined as it, so let’s change the code to say “while(it.hasNext())” and save the page again. Third time’s the charm – let’s try clicking the link one more time and taking a look at the console:

Ah, much better. Now we can see that the console is showing us the record that was selected by the SOQL. This is because of the line of code “console.log(record)”. It allows us to inspect the values and see if they are what we expect and could help us with other trouble shooting.

This is by no means an exhaustive tutorial on debugging JavaScript, but I hope it gets you on your way to finding errors and squashing bugs!

Deleting Salesforce Contacts You Don’t Own

If you have a private sharing model for Accounts and Contacts in Salesforce, you might have run into this problem. You can only delete records you don’t own in Salesforce if you have been given Modify all Data permissions for the object or if you are in a Role above the owner. Our business requirements are a bit different from this. We wanted accounts to be private and shared by sharing rules to groups of other users. All of those users expect to be able to make changes to all contacts related to the account, including deleting contacts.

Let me digress here a minute: in my opinion, you shouldn’t delete contacts if they have any activities associated with them – they should be mark “Inactive” so we don’t lose all that history. We’ve added a before delete trigger to contacts to prevent them from being deleted in that case. In some cases, you might really need to delete a contact: it is a duplicate or there is no activity and it is just cluttering things up.

OK, back to the task at hand. Try as I might, I couldn’t figure out any way to accomplish deleting of contacts that were associated with accounts I could view and edit. I finally decided to roll my own solution with a custom button and some apex. It turned out to be pretty simple, but there is some room for improvement.

First the Apex code (complete with test!). In this code, I create a webservice that can be called by a custom button. It passes in the Contact Id and then deletes it. In the test, I create a contact and then try deleting the contact as a different user. Note that you might need to change the profile to get a valid user.

global without sharing class ContactUtil {
  webService static Boolean deleteContact(Id id) {
    Contact c = new Contact(id = id);
    try {
      delete c;
      return true;
    } catch (Exception e) {
      return false;
  }
}
 
  static testMethod void testDeleteContact() {
    Contact c = new Contact(LastName = 'Test');
    insert c;
    User u = [select id from user where Profile.Name = 'Sales User' and IsActive = true  limit 1];
    System.runAs(u) {
      deleteContact(c.Id);
    }
 
    Contact[] testContact = [select Id from Contact where Id = :c.Id];
    System.assert(testContact.isEmpty());
  }
}

Now that we have the webservice, we can create a button to add to the contact layout page. It will be a Detail Page Button that Executes JavaScript. Here’s what the javascript looks like. It prompts the user and if successful, redirects to the Contact tab.

{!REQUIRESCRIPT(&quot;/soap/ajax/22.0/connection.js&quot;)}
{!REQUIRESCRIPT(&quot;/soap/ajax/22.0/apex.js&quot;)}
 
if (confirm(&quot;Are you sure?&quot;)) {
  if (sforce.apex.execute(&quot;ContactUtil&quot;,&quot;deleteContact&quot;, {id:&quot;{!Contact.Id}&quot;})) {
    window.location.replace('/003/o');
  }
}

Add this button to your page layout and remove the standard delete button. Now your users will be able to delete any contacts that they can view on the detail page.

A couple of words of caution. You might have noticed that there is no validation that the user should be able to delete the contact. Ideally, I’d like to test to see if the user has write access to that contact record. If so, then allow the delete. I’ve been unable to find a way to test record level access, so I’ve left it wide open. [UPDATE: I found a way! http://verticalcode.wordpress.com/2012/04/03/deleting-salesforce-contacts-you-dont-own-part-2/] Because of this, make sure that you only give permissions to the the ContactUtil class who you want to be able to delete all contacts.

Using jQuery to Validate User Input

This week on Twitter, @lesteb posted a question about the best way to validate if a user had checked one of two checkboxes on a Visualforce page. We went back and forth for a while about his requirements. Since Salesforce doesn’t have radio buttons and it is impossible to make a checkbox field required, we needed some other way to make sure his requirements were met. I had a couple of ideas.

My first thought was to use Validation Rules – this is a great button click feature of Salesforce and, as Nick Hamm so eloquently pointed out recently on his blog, we should try to use configuration first and code second. In this case Bryan couldn’t use validation rules because he only wanted it to validate on one page and wanted to have more control of the error message. Our next option to look at was putting in some logic in the controller extension save method. Again, Bryan ruled this out because he wanted more control over the error message.

So, I then suggested using Javascript to do some validation with help from jQuery. In Visualforce, we can use onClick to run some JavaScript before the Apex method assigned to the button is executed. In the example below, the beforeSave() function is called first and only if that function returns true will it go to the Apex method mySave.

[code]<apex:commandbutton value="Save" id="saveBtn" action="{!mysave}" onClick="if(!beforeSave()) return false;"/>[/code]

The JavaScript function beforeSave is below. It uses jQuery to check the values of the the two checkboxes. At least one must be checked for the function to return true. If neither one is checked, it opens a jQuery UI dialog box telling the user to check a box. I’ll get into that in a minute.

[code]
function beforeSave() {
if (!j$(‘[id$=checkbox1]’).attr(‘checked’) && !j$(‘[id$=checkbox2]’).attr(‘checked’)) {
j$( "#dialog-message" ).dialog(‘open’);
return false;
} else
return true;
}
[/code]

Now for the dialog box. First we need a div on the page that will be used for the message:

[code]
<div id="dialog-message" title="Info Needed">
<p>
<span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 50px 0;"></span>
You should check a box!
</p>
</div>
[/code]

Once we have the div, we need to turn it into a jQuery UI Dialog. Here’s the way to do that:

[code]
j$(document).ready(function() {
j$("#dialog-message").dialog({
autoOpen: false,
modal: true,
buttons: {
Ok: function() {
j$( this ).dialog( "close" );
}
}
});
});
[/code]

Bryan had one more requirement. Only one of the two checkboxes could be checked. I turned to a little more JavaScript to make this work. Here’s the visualforce markup where I added an onClick call to a JavaScript function to the input field.

[code]
<apex:inputField value="{!account.Checkbox1__c}" id="checkbox1" onClick="deselectOther(this);"/>
[/code]

The JavaScript function to deselect one checkbox when the other gets checked is pretty simple. It gets the element clicked passed in, compares the id of that element and then use jQuery to find and uncheck the other one.

[code]
function deselectOther(e) {
if (e.id.match(/checkbox1$/))
j$(‘[id$=checkbox2]’).attr(‘checked’, false);
if (e.id.match(/checkbox2$/))
j$(‘[id$=checkbox1]’).attr(‘checked’, false);
}
[/code]

Bryan did a nice little demo video of his nearly finished page. Note that this video was done before the final trick of deselecting one checkbox when the other was selected. He was handling this case with an error message.

I’ve created an unmanaged package that you can install into your org to see the entire sample code. It doesn’t match Bryan’s video entirely as he did a bunch of other messages and validations, but I hope it points you in the right direction. As always, I’d love to hear thoughts on how I could improve this, or maybe you have a completely different way to do it!