Sunday, 30 January 2011

Apex Test Coverage

I've been taking a look at some Spring 11 features this week, via a pre-release org, particularly the Apex Test Framework, which sadly is a pilot program. While most of the focus of the release notes/help etc is around the ability to select groups of classes to test and to execute those test in the background, there's an additional feature that grabbed my attention.

Once I'd run my unit tests, I browsed to the Apex Classes setup page and found a new column in the class list  which is going to save me time on every project I work on - code coverage:


During the development phase of a project, the first thing I do every morning is run all unit tests and check the output to see if I'm missing any coverage.  If there's been a lot of development taking place, I'll repeat this many times during the course of a day.  Sometimes I'll scribble the names of classes that need attention or print out the results page, but it all feels a bit clunky.  Now all I need to do is to execute the tests once (which can be automated - there's a post explaining this in the pipeline) and browse to the Apex Classes page each time I want to see the coverage percentages.

Even better - this looks to be available outside of the pilot program functionality - I've just checked my free force sandbox that has been upgraded to the Spring 11 release and the code coverage figures are there!

Saturday, 22 January 2011

Uploading Multiple Attachments via Visualforce

I've lost count of the number of times I've implemented an attachments page in Visualforce, sometimes for a single file, sometimes for a finite number and once or twice for an indeterminate number.  Following the principles of DRY, I decided to create a custom component that would allow me to attach files to any specified parent sobject with Add More functionality that allowed me to upload several files at once.  As its easy to forget what attachments you have already uploaded, I also wanted the existing attachments to be listed. Below is a screenshot of the completed component:



The component has a mandatory attribute named "sobjId" - this is the id of the sobject that the uploaded files will be attached to. Note that you can't use the name "id" for the attribute, as this is a standard attribute that allows the component to be identified by other components in the page.

The Attachment sobjects backing the input file elements are stored in a list in the controller that is initialised in the constructor to contain a single element.  Clicking the Add More button causes another five Attachment sobjects to be appended to the list.  As the user can choose to fill in the attachments in any order (e.g. just populating the last two elements), we can't simply insert the attachments when the save button is clicked, as that will attempt to insert the empty attachments that the user hasn't supplied details for.  Thus we iterate the list and only insert those Attachments that have the Body field populated.

Note that there is no field for the user to input the name of the Attachment - I'm using the filename for the this, as the filename extension gives a browser a good chance of automatically opening the attachment when it is downloaded.

The Done button simply takes the user back to the view page for the object matching the supplied id attribute.

There's a couple of wrinkles when dealing with file uploads:

  • When the page initially renders with one file chooser, if you choose a file and then click the Add More button, the chooser will not remember the file that you chose.  This is browser behaviour, as it is not possible to prefill a file chooser under any circumstances - otherwise malicious pages could upload files from your hard disk without your consent.
  • The body of the uploaded file is stored as a Blob.  To unit test code that handles file uploading, you can create a Blob from a String using the valueof method:

    Blob bodyBlob=Blob.valueOf('Unit Test Attachment Body');
    
The code (including unit tests!) and Visualforce can be downloaded here.  Simply unzip into the src directory of your org in the Force.com IDE.

Note that when using the example page, you'll need to supply the id of an existing sobject (Account, Opportunity etc).

Friday, 14 January 2011

Post Event Detail to Parent Chatter Feed

This week I needed to update an account's chatter feed when a new event was created related to it.  Turns out to be quite a short trigger:

trigger PostEventToParentChatterFeed on Event (after delete, after insert, after update) 
{
 List<Event> toProcess=trigger.new;
 
 if (trigger.isDelete)
 {
    toProcess=trigger.old;
 }

 // build a lookup of describe results by key prefix
 Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
 Map<String, Schema.DescribeSObjectResult> descMap=new Map<String, Schema.DescribeSObjectResult>();
    for (Schema.SObjectType sot : schemaMap.values())
    {
      Schema.DescribeSObjectResult descRes=sot.getDescribe();
      String kp=descRes.getKeyPrefix();
      descMap.put(kp, descRes);
    }
    
    List<FeedPost> feedPosts=new List<FeedPost>(); 
    for (Event ev : toProcess)
    {
        FeedPost fpost = new FeedPost();
        
        // if whatId (Opportunity etc) is defined, post to that, otherwise to the event owner
        Id parentId=(null!=ev.whatId?ev.whatId:ev.whoId);
        
        // ensure feed enabled
        String prefix=((String) parentId).substring(0, 3);
        Schema.DescribeSObjectResult descObj=descMap.get(prefix);
        if ( (null!=descObj) && (descObj.isFeedEnabled()) )
        {
         fpost.ParentId=parentId;
             String user=Userinfo.getUserName();
         String action='Created';
         
         // add a link to allow the user to click into the event from the feed
         String linkUrl= '/' + ev.id;
         String title='View Event';
         if (trigger.isDelete)
         { 
          action='Deleted';
          linkUrl='';
          title='';
         }
             else if (trigger.isUpdate)
         {
          action='Updated';
         }
         
          // set the body to a brief summary
             fpost.Body = action + ' event : ' + ev.Subject;
         fpost.LinkUrl=linkUrl;
         fpost.title=title;
             feedPosts.add(fpost);
  }
 }
    
    if (feedPosts.size()>0)
    {
       insert feedPosts;
    }
}

The basic premise is check if the parent object is feed enabled and then post a summary of the event to the parent's chatter feed.    In the case where this event isn't associated with an Account or Opportunity (the whatId is null) it will be posted to the feed of the Contact etc (the whoId) for the event.  This concept can also be applied to tasks and attachments.

Some obvious improvements:

(1) Allow users to mark events as Not for Chatter or similar, as otherwise everything gets posted!
(2) When an event is deleted, find the previous posts in the feed and remove them, otherwise the links in the feed take you to an error page.

Unit tests are left as an exercise for the avid student!

Wednesday, 27 October 2010

VisualForce Conditional List Rendering

This week I've been building VisualForce pages for an external site.  I had a number of different sobject lists scattered over the pages and I found myself adding property after property to my controller to avoid rendering empty lists.

I decided there had to be a better way to do this, so spent some time write a component that would take a list of sobjects, a title and some styling and only produce output of the list contained at least one element. So if there is data the list appears:


But if the list is empty, neither it nor its title appear:




The component appears in the page as follows:

<c:ListIterator list="{!accounts}" var="acc" title="Accounts" titleStyle="font-weight:bold">
   <p>
 > {!acc.Name} : {!acc.BillingStreet}
   </p> 
</c:ListIterator>

So the idea is that I have a list of accounts that I want to iterate and display an entry of the form 
"> My Account : My Street" for each one.

Below is my custom component.
<apex:component controller="ListIteratorController">
  <apex:attribute name="list" type="SObject[]" description="The list to iterate" assignTo="{!values}" />
  <apex:attribute name="title" type="String" description="The list title" />
  <apex:attribute name="titleStyleClass" type="String" description="The styleclass for the title" />
  <apex:attribute name="titleStyle" type="String" description="The inline style for the title" />
  <apex:attribute name="var" type="String" description="Variable representing an element in the list" />
  
  <apex:outputPanel rendered="{!render}">
      <apex:outputText value="{!title}" style="{!titleStyle}" styleClass="{!titleStyleClass}"/>
     <apex:repeat value="{!list}" var="ele">
    <apex:componentBody >
     <apex:variable var="{!var}" value="{!ele}"/>
      </apex:componentBody>
     </apex:repeat>
  </apex:outputPanel>
  
</apex:component>


The important section is:
<apex:repeat value="{!list}" var="ele">
 <apex:componentbody>
  <apex:variable value="{!ele}" var="{!var}">
 </apex:variable></apex:componentbody>
</apex:repeat>

This iterates the list of sobjects passed in as an attribute, and then pulls in the page markup that was enclosed by the <c:ListIterator> component.  The <apex:variable> tag allows the current element from the list to be used as a replacement in the page markup.

The custom controller associated with the component simply provides storage for the list that is being iterated and a method to determine if the list should be rendered (i.e. is not empty).

The component, page and controllers can be downloaded hereSimply unzip into the src directory of your Force.com project in the IDE.

Saturday, 25 September 2010

VisualForce Lookup

As VisualForce developers know, adding an inputfield backed by a lookup or master detail field renders a field to enter a text string into and a magnifying glass icon for launching the Salesforce lookup popup. There isn't much customization possible for the lookup popup, so often there is a need to replace it with one matching your own requirement.

This blog entry shows how to create your own visualforce lookup page backed by a custom controller that gives you full control.

My basic page for demonstrating custom lookup functionality is shown below:


The Lookup link pops up the lookup window.  Search text is entered into the input field and clicking the Go button returns the results into the results section:



Clicking the name populates the lookup text field on the main page, and to prove that it is working, clicking the Get Contacts button retrieves the contacts associated with the account:




The first point to mention regarding lookups is that there are actually two fields that are filled in on the target page when you select a lookup result.  The name of the selected item is displayed in the visible field, but names are not unique so this is not enough information to uniquely identify the chosen item.  Thus there is a hidden field that captures the the ID of the selected record.

Below is a snippet of a VisualForce page that contains my custom lookup combination field.  For the sake of simplicity I've chosen to use a clickable link to launch the lookup dialog.

<apex:pageBlock title="Lookup">
      <apex:pageBlockSection columns="1">
        <apex:pageBlockSectionitem >
          <apex:outputLabel value="Account"/>
          <apex:outputPanel >
         <apex:inputHidden value="{!accountId}" id="targetId" />
            <apex:inputText size="40" value="{!accountName}" id="targetName" onFocus="this.blur()" disabled="false"/> <a href="#" onclick="openLookupPopup('{!$Component.targetName}', '{!$Component.targetId}'); return false">Lookup</a>
          </apex:outputPanel>
        </apex:pageBlockSectionitem>
      </apex:pageBlockSection>
      <apex:pageBlockSection >
        <apex:pageBlockSectionitem >
           <apex:commandButton value="Get Contacts" action="{!findContacts}"/>
        </apex:pageBlockSectionitem>
      </apex:pageBlockSection>
    </apex:pageBlock>

Digging into the markup, we can see that as well as the text field and link, the hidden field to capture the link is also present.  When the user clicks the link, the following javascript is executed to open the popup.  Note that the HTML element ids are passed as parameters to the popup page - this allows the popup to locate the fields to populate when the user makes a selection.  Note also that the javascript function to close the popup window is located in the main page, not the popup page.  This is required as browsers often only allow a window to be closed by the same page that opened it.

<script>
 var newWin=null;
 function openLookupPopup(name, id)
 {
  var url="/apex/LookupExamplePopup?namefield=" + name + "&idfield=" + id;
  newWin=window.open(url, 'Popup','height=500,width=600,left=100,top=100,resizable=no,scrollbars=yes,toolbar=no,status=no');
  if (window.focus) 
  {
   newWin.focus();
  }
   
     return false;
    }
      
 function closeLookupPopup()
 {
    if (null!=newWin)
    {
       newWin.close();
    }  
 }
</script>


Now we move on to the popup VisualForce page. This consists of a criteria section and a results table.  Clicking the Go button invokes a controller action method that executes a SOSL query to retrieve all accounts matching the input string. The VisualForce markup for the results table is shown below.

<apex:pageBlock >
          <apex:pageBlockSection columns="1">
              <apex:pageBlockTable value="{!accounts}" var="account">
                <apex:column headerValue="Name">
                  <apex:outputLink value="#" onclick="fillIn('{!account.Name}', '{!account.id}')">{!account.Name}</apex:outputLink>       
                </apex:column>
                <apex:column headerValue="Street" value="{!account.BillingStreet}"/>
                <apex:column headerValue="City" value="{!account.BillingCity}"/>
                <apex:column headerValue="Postcode" value="{!account.BillingPostalCode}"/>
              </apex:pageBlockTable>    
          </apex:pageBlockSection>
        </apex:pageBlock>

As you can see, the account name column is actually a link that invokes a JavaScript function shown below:

function fillIn(name, id)
   {
      var winMain=window.opener;
      if (null==winMain)
      {
         winMain=window.parent.opener;
      }
      var ele=winMain.document.getElementById('{!$CurrentPage.parameters.namefield}');
      ele.value=name;
      ele=winMain.document.getElementById('{!$CurrentPage.parameters.idfield}');
      ele.value=id;
      CloseWindow();
   }

   function CloseWindow()
   {
      var winMain=window.opener;
      if (null==winMain)
      {
         winMain=window.parent.opener;
      }
      winMain.closeLookupPopup();
   }

This JavaScript is the glue between the two pages - it fills in the HTML elements in the main page and closes the popup window.

The pages and controllers can be downloaded hereSimply unzip into the src directory of your Force.com project in the IDE.

Friday, 3 September 2010

Rotating a VisualForce table

One issue that crops up repeatedly on the SalesForce Developer Discussion Boards is how to rotate a table. E.g. if I have a table that displays account information one account per row:

how can I rotate this to display one account per column:


First, to create a class to model the data that is being passed back to the page. In this case the class wraps a row of data, thus row 1 contains the name of each account, row2 contains the street address etc.

As each cell in the table is a text field, our class can simply wrap a list of String primitives. The  class implementation is shown below:

public class RowWrapper
    {
     // the values (cells) making up this row
     public List<String> values {get; set;}
     
     // constructor
     public RowWrapper()
     {
      values=new List<String>();
     }
     
     // append a value (cell) to the row
     public void addValue(String value)
     {
      values.add(value);
     }
    }

The next step is to write an extension to the Account standard controller that transforms a list of Accounts into a list of RowWrapper classes.

	// retrieves the list of accounts backing the page
    public List<Account> getAccounts()
    {
    	if (null==accs)
    	{
    		accs=[select id, Name, BillingStreet, BillingCity, BillingPostalCode from Account
    	          where BillingCity != null and BillingPostalCode!=null limit 3];
    	}
    	
    	return accs;
    }
    
    // retrieves the list of row wrappers
    public List<RowWrapper> getRowWrappers()
    {
    	if (null==rows)
    	{
    		rows=new List<RowWrapper>();
    		
    		// create a row for each field - there are 4 of these, Name, Street, City and PostCode
    		for (Integer idx=0; idx<4; idx++)
    		{
    			rows.add(new RowWrapper());
    		}
    		
    		// iterate the accounts and populate the rows
    		for (Integer idx=0; idx<getAccounts().size(); idx++)
    		{
    			rows[0].addValue(getAccounts()[idx].Name);
    			rows[1].addValue(getAccounts()[idx].BillingStreet);
    			rows[2].addValue(getAccounts()[idx].BillingCity);
    			rows[3].addValue(getAccounts()[idx].BillingPostalCode);
    		}
    	}
    	
    	return rows;
    }


The final step is to create a VisualForce page to iterate the RowWrapper list and output the table:

<table class="list" border="0" cellpadding="0" cellspacing="0">
    <tr class="headerRow  ">
      <apex:repeat value="{!headWrap.values}" var="heading">
        <th class="headerRow ">
           {!heading}
        </th>
      </apex:repeat>
    </tr>
    <apex:repeat value="{!rowWrappers}" var="row">
       <tr>
         <apex:repeat value="{!row.values}" var="value">
           <td>
             {!value}
           </td>
         </apex:repeat>
       </tr>
    </apex:repeat>
  </table>

The page and controller can be downloaded here. Simply unzip into the src directory of your Force.com project in the IDE.