Showing posts with label apex. Show all posts
Showing posts with label apex. Show all posts

Thursday, 18 February 2016

Bristol DUG Report - Spring '16 Preparation Quiz (With TEST SLAYER!)

On Wednesday 10th February, the Bristol Developer User Group met at the Desynit offices. In the city you could go outside at 5pm without it being pitch black, and no more ice patches were waiting to greet you outside your door, which meant only one thing, spring was just around the corner. How appropriate then, that this edition of the DUG covered the Spring '16 release, focusing on some of the new cool features coming to all Salesforce orgs.

Local Salesforce MVP Simon Lawrence kicked things off with a presentation on some of the new cool Apex code features arriving in Spring '16. The presentation included the awesome ability to set the created dates on test methods (my favourite portion of the release), and also create test suites of re-runnable tests. The presentation also mentioned how developers also now have the ability to stop test runs that already have failing tests registered, making the rerun cycle cover much less time. This ability to halt tests doesn't have an official name, so Simon created the name TEST SLAYER, which I really hope Salesforce adopt!




Simon also covered some additional enhancements, including global picklists and API enhancements. An important concept with the Spring '16 is that from this release forward, all results returned from SOQL queries will be formatted in the locale of the executing user. This could be very significant for multi-national orgs who have to present information to users in different formats, not only from a functionality standpoint, but also from a testing perspective. So be aware that you may have to make some changes!


Here are Simon's presentation slides:


After the presentation, it was time to check if the attendees were ready for the release exam, with a quick fire 10 question quiz. The quiz focused on the features contained in the slides.

While Simon busily marked all the attendees scores, I provided a presentation with live demos of some of the great new features coming to Lightning Experience as part of Spring '16. A new feature that will interest developers is the introduction of new user level global variables for the intended and actual theme displayed to users. Prior to this release, the process of distinguishing which UI theme (LEX, Classic or other) users were viewing content with through code was a tricky business involving interrogating the existence of the sforce.one javascript global. Now with the new UITheme and UIThemeDisplayed variables, available through both the $User global variable and the Userinfo Apex class, this process has been made much more simple and reliable.



Here are my presentation slides:


As soon as my presentation and accompanying demos were completed, Simon read out the answers to the quiz questions and announced the winner. Congratulations to Käbi from Desynit, who scooped the top prize of a Starbucks voucher. The award was presented by Salesforce MVP Simon Goodyear, fresh and buzzing from co-running the massive Londons Calling SF community event!


Thanks to all those who attended, great to see you all, we hope to see you again next month. If you are an existing or aspiring Salesforce developer, why not come along to our next meetup, details here: http://www.meetup.com/Bristol-Salesforce-Platform-Developer-User-Group/events/227339685/

For those who fancy testing their knowledge for the Spring release before the release are available exams, here are the questions and answers from the quiz (no cheating using the slides haha):

Questions:
1. The last/end date of the Spring '16 production roll out?

2. When using a global picklist on a custom field, can you use Profile settings to exclude some values from displaying to certain users?

3. How many test classes can you include in a test suite?

4. For two points: What is the exact, and correct syntax to set the created date of a contact, called “c” to the second of July 1984, in a United Kingdom locale org?

5. What would you enter to make a test exit as soon as the first test fails?

6. Can you delete a Global Picklist definition once it’s created?

7. What API number is Spring’16?

8. Can you set the CreatedDate on a record to be “in the future”?

9. What setup menu item is “Picklists” under?

10. Bonus Question (used as a tie breaker) : How many points does the original “provide universal picklists” ideas exchange post have?

Answers:
1) Sat. Feb 13th 2016
2) No
3) 200
4) Test.setCreatedDate(c.Id, DateTime.newInstance(1984,07,02));
5) 0
6) Yes
7) 36
8) Yes
9) Create
10) 44340

Thursday, 31 December 2015

The Force.com is Strong with NEW TRAILHEAD BADGES!!!



As 2015 draws to a close, it seems to have flown by in a lightning flash. This has been another fantastic year for Salesforce, with the release of an entire new UI, bigger APIs, better analytics and even greater platform functionality.

For me, Trailhead has been massive this year. The Salesforce development learning platform gets stronger every month, has a massive following, and has really captured the spirit of the developer community, It truly is an amazing educational resource for Salesforce developers, administrators and users of all ability levels.

As a special holiday bonus, Salesforce has rounded off the year in style with the introduction of 6 new badges to get stuck into. Here's a summary of the badges and what they have to offer:

Name Overview


Advanced Formulas
Are you looking to take your formula ninja skills to the next level? This module looks at some more complex practical formulas, including a range of the pre-built functions you have always seen in passing, but weren't sure how to use! 


Apex Integration Services
Need Salesforce to connect to your external systems and communicate with external REST and SOAP data sources? No problem, Apex integration classes to the rescue! Essential for developers, includes great example test methods and mock classes.


Lightning Chatter Basics
A whirlwind tour of how Chatter works in the new Lightning Experience UI. A good introductory badge for those new to chatter, or trying out LEX for the first time.


Lightning Data Management
Load data into Salesforce, Load data out of Salesforce. Simple but effective module.


Application Lifecycle Management
Questions about version control? Can't decide between change sets and the Force.com Migration tool? Confused about the concept of a sandbox that doesn't involve a bucket and spade? This is the module for you!


Build a Battle Station Project
Awaken your Salesforce abilities with this cool app project that enables you to build a whole planning project with NO CODING REQUIRED. Now anyone can be a Salesforce Jedi master! 


For the developers out there, although there is definite benefit to doing all of the badges, the most relevant of the new content is the Apex Integration Services badge. The badge is made up of 4 different units, 1 set of questions and 3 practical exercises (I like that ratio!) :

Apex Integration Overview: This short introductory unit provides some information on connecting to external services. Also includes information on adding remote sites before making calls to external REST and SOAP services, a classic gotcha when starting with integrations.


Apex REST Callouts: The first practical exercise focuses on connecting to external data using REST callouts, explaining the difference between the varieties of call (GET, POST, DELETE etc.). The challenge involves creating an Apex Class that retrieves data on an animal through a callout to a REST service, and then creating your own test methods and accompanying mock class. You cannot pass the challenge until you have run all tests and have 100% coverage on the apex callout class.

Tip: The practical exercise for this unit includes creating a custom mock class to provide a sample response for the callout when made from a test.  However, this is not the only way to accomplish this, make sure you try out using the StaticResourceCalloutMock class as mentioned in the unit body. This is a really cool feature, allowing you to use a static resource as the example response to a callout!


Apex SOAP Callouts: This unit switches focus to retrieving data using an Apex to connect to an external SOAP based web service with accompanying WSDL service definition file. This is an important module, as although in modern development there is much more of a lean towards REST based HTTP callouts to transfer data between systems, there are still a lot of enterprise level systems that only allow access through WSDL based SOAP web service calls.

Tip: Make sure you follow the initial exercise instructions carefully. I got myself in quite a horrible renaming mess when I just skipped through the generate Apex Classes fromWSDL menu, as I didn't ensure that the name of the class was "ParkService" leaving it as the default "parksServices".


Apex Web Services: The previous two units dealt with interacting with services published from other systems. In this unit the roles are reversed, as Salesforce becomes the source of the data. The unit content and accompanying practical challenge is all about defining a class and annotated methods that allows data interaction to be made possible through REST calls to Salesforce. Completing this method once again reminded me how ridiculously easy it is to create REST interfaces for accessing your data.

Tip 1: The practical challenge can be quite a lot to get your head around at first, but its actually quite simple when it comes to the implementation. Think subquery to get all the Accounts with their related Contacts in one SOQL request, and getting the id of the Account from the url is just a case of using some funky substring methods (I used the substringBeforeLast and SubstringAfterLast, never used them before, great!).

Tip 2: In the practical challenge you simply have to create a get request. Make sure to try creating other methods with other supported annotations, such as a @HttpPut or @HttpDelete.


The Apex Integration Services badge is definitely one of my favourite modules so far. When I started Salesforce Development (around 6 years ago) one of the first development tasks I had to do was integrate Salesforce with an external system using WSDL based SOAP callouts. I found it really difficult and the amount of practical content out there for help was minimal. I wish I had great resources like this back then, that allow you to get hands on with great examples.

One of the most impressive elements of the module is the presence of test classes and methods in each of the exercises. In some of the previous Apex modules, while the core functionality was demonstrated in great fashion, there was little mention of testing. While this is ok in training exercises, as soon as you start coding in real world practical applications, you soon realise the importance of writing test methods, and especially writing good test methods. Any good force.com developer knows that you should be spending a considerable portion of your coding time writing test methods, so why should be training be any different?

I thought it was a great idea to prevent developers from passing challenges until they add test methods that cover all of the class code 100%. By writing the modules in this way, Salesforce is encouraging more test centric development, which in my opinion is fantastic!!

Another great positive is the presence of dedicated web services specifically initialised for the module. It may sound like a minor point, but it makes a big difference having a reliable service to test your code against. I've seen plenty of web service training examples in the past where the source service no longer works or has been replaced, which is incredibly frustrating.

So if you want to learn more about coding integrations on the Salesforce platform, or earn any of the other cool badges, then get yourself down to the trailhead site now!

Here's to a great 2016, a year hopefully filled with just as much great Trailhead content, and badges galore! If your located in the South West of the UK, why not come to our January Trailhead Smash and get on the badge trail early!

HAPPY NEW YEAR EVERYONE!!!

Tuesday, 18 December 2012

Clone Plus: A quick update

After posting the original clone plus implementation, it has been pointed out that it doesn't work for objects that do not have "Name" fields (I didn't even realise such objects existed!). However, as far as I am aware, every object has an Id field, so here is an altered implementation that will work for the objects missing the name field.

Controller:
public class ClonePlusController {

  public List<relatedObjects> objectChildren  { get; set; }
  public String               objectTypeName  { get; set; }
  public String               objectName      { get; set; }
   
  private SObject headSObject, headClone;
  
  // Initialisation method called when the clone plus page is  loaded.
  // Use the id page parameter to find out what object type to clone.
  // Then load the object from the database.
  // Finally call the populateObjectChildren method to      
  public void initialiseObjectsForCloning()
  {

    // Here we generate a keyprefixmap using the global describe 
    // Then compare that to our object to determine type.  
    Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe(); 
       
    Map<String,String> keyPrefixMap = new Map<String,String>{};
          
    for(String sObj : gd.keySet()){
      Schema.DescribeSObjectResult r =  gd.get(sObj).getDescribe();
      keyPrefixMap.put(r.getKeyPrefix(), r.getName());
    }
      
    String objectID = ApexPages.currentPage().getParameters().get('id');
    String objectTypeKey = objectId.subString(0,3);
      
    objectTypeName = keyPrefixMap.get(objectTypeKey);
      
    String primaryObjectQueryString = 'SELECT Id, Name FROM '
                                    + objectTypeName
                                    + ' WHERE Id = \''
                                    + objectId
                                    + '\'';
    
    headSObject = Database.query(primaryObjectQueryString);
    objectName          = '' + headSObject.get('Name');
    populateObjectChildren();    
  }

  // Get all of the children of the current object that have a object
  // type contained in the child object types page parameter.
  // Not restricting the child objects to particular types results in
  // many uncloneable system objects being added to the possibiilites.
  // Making these object type choices also allows us 
  // pick and chose the specific kinds of objects we want to clone.  
  public void populateObjectChildren()
  {
       
    objectChildren = new List<relatedObjects>{};
        
    Set<String> childObjectTypes = new Set<String>{};
    
    // read the object types from the page parameter.    
    childObjectTypes.addAll(
         ApexPages.currentPage().getParameters()
                  .get('childobjecttypes').split(',')
    );
    
    // Use the sobjecttype describe method to retrieve all 
    // child relationships for the object to be cloned.    
    Schema.DescribeSObjectResult headDescribe = 
           headsObject.getSObjectType().getDescribe();
    
    List<Schema.ChildRelationship> childRelationships = 
           headDescribe.getChildRelationships(); 
    
    // Iterate through each relationship, retrieve related objects.       
    for (Schema.ChildRelationship childRelationship : 
                                   childRelationships)
    {
      Schema.SObjectType childObjectType = 
             childRelationship.getChildSObject();
      
      // Only retrieve the objects if their type is 
      // one of those we are interested in.          
      if (childObjectTypes.contains(
          childObjectType.getDescribe().getName()))
      {
        List<relatedObjectRow> relatedObjects = 
                    new List<relatedObjectRow>{};
                
        Schema.SObjectField childObjectField = 
                            childRelationship.getField();
                
        String relatedChildSObjectsquery = 'SELECT ID FROM ' 
                        + childObjectType.getDescribe().getName()
                        + ' WHERE '
                        + childObjectField.getDescribe().getName()
                        + ' = \'' 
                        + headsObject.Id
                        + '\''; 
                                                        
        for (SObject childObject : 
                     Database.query(relatedChildSObjectsquery))
        {
          relatedObjects.add(new relatedObjectRow(childObject));
        }
            
        if (!relatedObjects.isEmpty())
        {
          objectChildren.add(new relatedObjects(relatedObjects, 
                 childObjectType.getDescribe().getLabelPlural(), 
                 childObjectField.getDescribe().getName()));
        }  
      }
    }
  }
  
  // Perform the cloning process.
  // First clone the parent, then all of the child objects. 
  // Then redirect the user to the new object page.
  public PageReference doClone()
  {
    headClone = cloneObjects(new List<sObject>{headSObject}).get(0);
    
    insert headClone;
    
    cloneSelectedObjects();
    
    return new PageReference('/' + headClone.Id);
  }
  
  // Clone the selected child objects.
  // Associate the cloned objects with the new cloned parent object.
  public void cloneSelectedObjects()
  {
        
    List<sObject> clonedObjects = new List<sObject>{};
    List<sObject> selectedRelatedObjects;
     
    for (relatedObjects relatedObject : objectChildren)
    {
      selectedRelatedObjects = new List<sObject>{};  
      clonedObjects = new List<sObject>{};  
      
      for (relatedObjectRow row : relatedObject.objectRows) 
      {
        if (row.selected)
        {
          selectedRelatedObjects.add(row.obj);
        }
      }
      
      if (!selectedRelatedObjects.isEmpty())
      {
        clonedObjects = cloneObjects(selectedRelatedObjects);
        
        for (sObject clone : clonedObjects)
        {
          clone.put(relatedObject.relatedFieldName, headClone.Id);  
        }
        
        insert clonedObjects;
      }
    }
  }

  // Clone a list of objects to a particular object type
  // Parameters 
  // - List<sObject> sObjects - the list of objects to be cloned 
  // The sObjects you pass in must include the ID field, 
  // and the object must exist already in the database, 
  // otherwise the method will not work.
  public static List<sObject> cloneObjects(List<sObject> sObjects){
                                                
    Schema.SObjectType objectType = sObjects.get(0).getSObjectType();
    
    // A list of IDs representing the objects to clone
    List<Id> sObjectIds = new List<Id>{};
    // A list of fields for the sObject being cloned
    List<String> sObjectFields = new List<String>{};
    // A list of new cloned sObjects
    List<sObject> clonedSObjects = new List<sObject>{};
    
    // Get all the fields from the selected object type using 
    // the get describe method on the object type.    
    if(objectType != null)
    {
      for (Schema.SObjectField objField : 
           objectType.getDescribe().fields.getMap().values())
      { 
        Schema.DescribeFieldResult fieldDesc = objField.getDescribe();
        // If the field type is location, then do not include it,
        // otherwise it will cause a soql exception.
        // Note that excluding the field does not stop the location from
        // being copied to the new cloned object.
        if(fieldDesc.getType() != DisplayType.LOCATION)
        {
          sObjectFields.add(fieldDesc.Name);
        }
      }
    }
    
    // If there are no objects sent into the method, 
    // then return an empty list
    if (sObjects != null || 
        sObjects.isEmpty() || 
        sObjectFields.isEmpty()){
    
      // Strip down the objects to just a list of Ids.
      for (sObject objectInstance: sObjects){
        sObjectIds.add(objectInstance.Id);
      }

      /* Using the list of sObject IDs and the object type, 
         we can construct a string based SOQL query 
         to retrieve the field values of all the objects.*/
    
      String allSObjectFieldsQuery = 'SELECT ' + 
                                     String.join(sObjectFields,','); 
    
      allSObjectFieldsQuery += ' FROM ' + 
                               objectType.getDescribe().getName() + 
                               ' WHERE ID IN (\'' + sObjectIds.get(0) + 
                               '\'';

      for (Integer i=1 ; i < sObjectIds.size() ; i++){
        allSObjectFieldsQuery += ', \'' + sObjectIds.get(i) + '\'';
      }
    
      allSObjectFieldsQuery += ')';
    
      system.debug('allSObjectFieldsQuery: ' + allSObjectFieldsQuery);
    
      try{
      
        // Execute the query. For every result returned, 
        // use the clone method on the generic sObject 
        // and add to the collection of cloned objects
        for (SObject sObjectFromDatabase:
             Database.query(allSObjectFieldsQuery)){
          clonedSObjects.add(sObjectFromDatabase.clone(false,true));  
        }
    
      } catch (exception e){
      }
      
    }
   
    return clonedSObjects;
    
  }
  
  // Related objects data construct - 
  // used to store a collection of child objects
  // connected to the head object through 
  // the same relationship field.
  public class relatedObjects
  {
    public List<relatedObjectRow> objectRows       { get; set; }
    public String                 pluralLabel      { get; set; }
    public String                 relatedFieldName { get; set; }
    
    public relatedObjects(List<relatedObjectRow> objectRows, 
                          String pluralLabel, 
                          String relatedFieldName) 
    {
      this.objectRows       = objectRows;
      this.pluralLabel      = pluralLabel;
      this.relatedFieldName = relatedFieldName;
    }   
  }     

  // An indidual child object row. 
  // It simple contains the object definition, 
  // and a checkbox to select the row for cloning 
  // on the clone plus page.
  public class relatedObjectRow
  {
    public sObject obj      { get; set; }
    public Boolean selected { get; set; }
    
    public relatedObjectRow(Sobject obj)
    {
      this.obj      = obj;
      // All object rows are selected by default.
      this.selected     = true;
    }
    
    public String getId(){
      try{
        return '' + obj.get('Id');
      } catch (Exception e){
        return '';
      }    
    }
  }
}

Page:
<apex:page controller="ClonePlusController" 
                   action="{!initialiseObjectsForCloning}">
  
  <apex:sectionHeader title="Clone Plus: {!objectName}"/>

  <apex:form id="theform" >
  
    Please select the child objects you would like to clone.<br/><br/>
  
    <apex:repeat value="{!objectChildren}" var="child">
      <apex:PageBlock title="{!child.pluralLabel}"> 
        <apex:pageBlockTable value="{!child.objectRows}" 
                                var="objectRow">
          <apex:column headerValue="Clone" width="10%">
            <apex:inputCheckbox value="{!objectRow.selected}"/>
          </apex:column>
       <apex:column headerValue="Id"   
                              value="{!objectRow.Id}" 
                              width="45%"/>
        </apex:pageBlockTable>
      </apex:PageBlock>
    </apex:repeat>
    
    <apex:PageBlock >
      <apex:commandButton action="{!doClone}" value="Clone"/>
    </apex:PageBlock>
  </apex:form>  

</apex:page>


At this current point in time, my plan is to develop the next release of clone plus on the AppExchange as a free app, with a much better management facility, so you can control what you are cloning much more effectively. I'll keep you posted!

Monday, 19 November 2012

Using Ids and strings in SOQL queries, a word of warning.

When querying your sObject data to retrieve particular objects, a common approach is to use Id or String fields as the criteria in a SOQL query.

Take a look at the following sample code that retrieves account objects by Id, and updates related contacts so that they are opted in to future fax and email communications. This type of function may be commonly used in batch operations or triggers.

// Opt all contacts related to any of a list of accounts
// in to email and fax communications. 
public static void optInContacts (List<Account> updatedAccounts)
{
    // Put the accounts Ids in a set
    Set<Id> accountIds = new Set<Id>{};
        
    for (Account acc: updatedAccounts)
    {
        accountIds.add(acc.Id);
    }
        
    // Use SOQL to retrieve the related contacts
    List<Contact> relatedContacts = 
        [SELECT HasOptedOutOfFax, HasOptedOutOfEmail
         FROM Contact
         WHERE AccountId IN :accountIds];
        
    // For each contact set opt out preferences
    for (Contact con : relatedContacts)
    {
        con.HasOptedOutOfFax   = false;
        con.HasOptedOutOfEmail = false;
    }
        
    // Update the contact records
    update relatedContacts;
}

Doesn't look too unusual right? However, upon running this method, you may affect more records than you intend to. The problem is that internal, non-API Salesforce code uses 15 character unique object Ids. These object Ids are case sensitive (externally 18 character insensitive Ids including check digits are available). However, database query tool SOQL is not case sensitive.

So in the above example imagine there were two account objects in your org with Ids 01rG0000000FBr6 and 01rG0000000FbR6 respectively. If you ran the code operation on a collection that contains the account with Id 01rG0000000FBr6, then both records would be picked up by the case-insensitive query and processed, potentially signing up contacts for communications when they have already refused.

There are a few ways to deal with this. As a general rule I would recommend using names as oppose to Ids. This however comes with the caveat that all your names are alphabetically unique. This will definitely work if the name is an auto-number field.

Alternatively you could use the set.contains method to remove the incorrect values before you process the results. The updated method looks something like this:

public class IdInsensitivityTest{

    // Opt all contacts related to any of a list of accounts
    // in to email and fax communications. 
    public static void optInContacts (List<Account> updatedAccounts)
    {
        // Put the accounts Ids in a set
        Set<Id> accountIds = new Set<Id>{};
        
        for (Account acc: updatedAccounts)
        {
            accountIds.add(acc.Id);
        }
        
        // Use SOQL to retrieve the related contacts
        List<Contact> relatedContacts = new List<Contact>{};
        
        for (Contact c : [SELECT AccountId, HasOptedOutOfFax, HasOptedOutOfEmail
                          FROM Contact
                          WHERE AccountId IN :accountIds])
        {
            // Use the set "contains" method to determine if 
            // the contacts related account is correct
            // Even though SOQL is not case sensitive, Apex is
            if (accountIDs.contains(c.AccountId))
            {
                relatedContacts.add(c);
            }
        }
        
        // For each contact set opt out preferences
        for (Contact con : relatedContacts)
        {
            con.HasOptedOutOfFax   = false;
            con.HasOptedOutOfEmail = false;
        }
        
        // Update the contact records
        update relatedContacts;
    }
}

You may think that this is overkill, and that the chances 15 length Ids will match alphabetically are pretty low, but if your functionality does something drastic like delete data, should you really be taking that chance? I urge you to consider this, as I will, in any further development involving id or string criteria in SOQL queries.

PS -> Thanks to Desynit's very own Gary McGeorge, who pointed out that old Sherlock Holmes saying :"when you have eliminated the impossible, whatever remains, however improbable, must be the truth?", Which certainly helped me work out why this was happening. Always something to bear in mind when tackling problems of this nature.

Tuesday, 7 August 2012

Visualforce Component that creates a picklist without a -none- value

About a year and a half ago, when I was relatively new to the Force.com platform, I devised a way to create a picklist value selector with a '-none-' value for a field that could be placed on to a Visualforce page, to prevent users from not setting a value for a picklist field.

A question was posted in that blog entry that asked if it was possible to have two picklists for different fields on the same Visualforce page. I replied with an answer that was basically "write the code out twice". While this works, its not exactly re-usable or easy to maintain.

Being a big fan of Visualforce components, I set about converting the no '-none-' picklist code into a re-usable component that can be placed onto any number of pages, and used in different applications easily. I found that this was possible by using the describe object and describe field calls.

The component I have created is called InputPicklistNoNone. It has two attribute arguments that have to be defined when using it in a Visualforce Page:
  • Value: This is the reference for the variable you want to assign the picklist selection to. It is much like the value attribute for most Visaulforce input components, such as apex:inputField and apex:inputText.
  • Field: This is the API reference of the picklist field you want to extract the possible values from. If this value points to an invalid field, an error message will be added to the page.
Below is the component code, and sample page and controller code to show how the component can be used. This is just a simple example using the component in a Visualforce Page and a standard controller with an extension. The component is flexible, it can be used with custom controllers and the value it populates can be an object field or a string variable.

Component Code:
<apex:component controller="InputPicklistNoNoneController">
  <apex:attribute name="value" required="true" type="String" 
   description="The variable the selected option will be assigned to"/>
  
  <apex:attribute name="field" assignTo="{!fieldName}"    
   required="true" type="String" 
   description="The picklist field that forms the basis of the input"/>

  <!--If the value is empty, then set it to be equal to the default 
      value from the schema description-->

  <apex:variable var="value" value="{!defaultOption}" 
   rendered="{!ISNULL(value)}"/>

  <apex:selectList size="1" value="{!value}">
    <apex:selectOptions value="{!options}"/>
  </apex:selectList>
</apex:component>

Component Controller:
public class InputPicklistNoNoneController 
{
    public String             defaultOption {get;set;}
    public List<SelectOption> options       {get;set;}
    public String             fieldName
    { 
        get;
        set 
        {
            fieldName = value;
            options = new List<SelectOption>();
            List<String> fieldNameSplit = fieldName.split('\\.');
        
            Schema.DescribeFieldResult picklistFieldDescription =
              Schema.getGlobalDescribe().get(fieldNameSplit[0])
              .getDescribe().fields.getMap()
              .get(fieldNameSplit[1]).getDescribe();

            for (Schema.Picklistentry picklistEntry:
                 picklistFieldDescription.getPicklistValues())
            {
                options.add(new SelectOption(pickListEntry.getValue(),
                                             pickListEntry.getLabel()));

                if (picklistEntry.defaultValue)
                {
                    defaultOption = pickListEntry.getValue();
                }
            }    
        }
    }    
}

Example Page:
<apex:page standardController="Invoice_Statement__c"
           extensions="InputPicklistNoNoneExampleExtension">
           
  <apex:sectionHeader title="No none picklist component example"/>
  
  <apex:form >
    
    <apex:panelGrid columns="2">
    
      
      <!-- The component can be used to populate an object field -->  
      <apex:outputText value="Status:"/>
      <c:InputPicklistNoNone value="{!Invoice_Statement__c.Status__c}" 
                             field="Invoice_Statement__c.Status__c"/>
    
      
      <!-- The component can also be used with a controller variable -->
      <apex:outputText value="Industry:"/>
      <c:InputPicklistNoNone value="{!myControllerVariable}"
                             field="Account.Industry"/>    
    
      <apex:commandButton value="Save" action="{!save}"/>
    
    </apex:panelGrid>
    
  </apex:form>  
</apex:page>

Example Controller Extension:
public class InputPicklistNoNoneExampleExtension 
{
    public String myControllerVariable {get; set;}

    public InputPicklistNoNoneExampleExtension(
           ApexPages.StandardController controller) {}

}

Screenshot of Example Page:

I am a big fan of how this has turned out, it keeps the functionality intact while making it easily adaptable for use on numerous variables on any number of pages, result! If you have any questions, or need some pointers on how to use it, then please add a comment below.

Friday, 25 May 2012

Summer '12 release component layout attribute

*Special thanks to Abhinav Gupta on this one, without his blog post on his Summer'12 release favourites, this problem would have taken a lot longer to diagnose*

In the upcoming Summer '12 Salesforce release, one of the new features is a layout attribute for Apex component tags. This new layout feature allows you to wrap your components in a div or span tag. The full definition can be found in the release notes under "Visualforce Enhancements". It states that the default value for the new attribute is "inline" (span tag), so any existing component where this attribute does not exist should be unaffected, as is the Salesforce release way.

However, I have found in some cases that this new attribute has had an adverse affect on the appearance of Visualforce pages and email templates that leverage components.

For example, if you have an email template that that has a text attachment defined in Visualforce, and that definition has components included, span tags will now appear as part of the attachment. Take this simple email template and component definition.

Visualforce Email Template:
<messaging:emailTemplate subject="Sample Email With Text Attachment" 
                         recipientType="Contact" >

<messaging:plainTextEmailBody >
Wow look at this attachment, It's nearly as amazing as hoverboards!
</messaging:plainTextEmailBody>

<messaging:attachment renderAs="text/plain" filename="test.txt">
Hi {!recipient.Name}, Here is your awesome text!
<c:TextComponent />
</messaging:attachment>

</messaging:emailTemplate>


Visualforce Compnent Defintion:
<apex:component access="global">
Text from a component, YES! YES! YES!
</apex:component>

If an email message using this template is sent out this is how it appears in the recipient inbox if the Salesforce Org uses the Summer '12 release:



Here is the text attachment. As you can see it contains span tags, which is not what was intended. The same template would not have produced these tags in previous releases.



The way to resolve this is simple, just add the new layout attribute to the apex component definition, with a value of "none".
<apex:component access="global" layout="none">
Text from a component, YES! YES! YES!
</apex:component>

Now when an email using this template is sent, the email attachment looks like we want:


Summer '12 is currently in sandbox orgs, but will be promoted to all orgs over the next few weeks. My advice would be to identify the pages and templates where components are used before the release, making sure they still behave as intended. If they don't, add the layout attribute with a value of "none" as above.

Wednesday, 25 April 2012

Clone Plus: Clone Salesforce objects with children

In a previous post, I outlined an Apex cloning method that copied an sObject in its entirety, all data values included. Recently there have been some comments and questions on the post asking if it would be possible to use the method to create a way to clone an object and its children via a custom button on an object page without extensive development expertise required.

With that in mind, I have created a little helper tool I have called "Clone Plus" that does just that. It consists of an Apex controller, a single Visualforce page and custom button.

The clone plus button can be created on custom and standard object page layouts. When clicked, the button redirects the user to a Visualforce page that displays the children that can be cloned and associated with the new object. The user selects the objects they want to clone using checkboxes, and then clicks on a clone button to finish the operation. After the saving process is completed, the user is redirected to the new object.

The clone button on the page



The child clone selection screen



The new cloned object



Create the clone button by following the following steps: 

1) Create a new Apex class (Navigate to Setup -> Develop -> Apex Classes and click new). Copy in the following code into the class body:

public class ClonePlusController {

  public List<relatedObjects> objectChildren  { get; set; }
  public String               objectTypeName  { get; set; }
  public String               objectName      { get; set; }
   
  private SObject headSObject, headClone;
  
  // Initialisation method called when the clone plus page is loaded.
  // Use the id page parameter to find out what kind of 
  // object we are trying to clone.
  // Then load the object from the database.
  // Finally call the populateObjectChildren method to      
  public void initialiseObjectsForCloning()
  {

    // Here we generate a keyprefixmap using the global describe 
    // Then compare that to our object to determine type.  
    Map<String, Schema.SObjectType> gd = 
                   Schema.getGlobalDescribe(); 
       
    Map<String,String> keyPrefixMap = 
                  new Map<String,String>{};
          
    for(String sObj : gd.keySet()){
      Schema.DescribeSObjectResult r =  gd.get(sObj).getDescribe();
      keyPrefixMap.put(r.getKeyPrefix(), r.getName());
    }
      
    String objectID = 
           ApexPages.currentPage().getParameters().get('id');
    
    String objectTypeKey = 
           objectId.subString(0,3);
      
    objectTypeName = 
           keyPrefixMap.get(objectTypeKey);
      
    String primaryObjectQueryString = 'SELECT Id, Name FROM '
                                    + objectTypeName
                                    + ' WHERE Id = \''
                                    + objectId
                                    + '\'';
    
    headSObject = Database.query(primaryObjectQueryString);
    objectName          = '' + headSObject.get('Name');
    populateObjectChildren();    
  }

  // Get all of the children of the current object that have a 
  // object type contained in the child object types page parameter.
  // Not restricting the child objects to particular types 
  // results in unclonable system objects being added to the options, 
  // which we need to avoid (You will not want to clone these!)
  // Making these object type choices also allows us 
  // focus our efforts on the specific kinds of objects 
  // we want to allow users to clone.  
  public void populateObjectChildren()
  {
       
    objectChildren = new List<relatedObjects>{};
        
    Set<String> childObjectTypes = new Set<String>{};
    
    // read the object types from the page parameter.    
    childObjectTypes.addAll(
         ApexPages.currentPage().getParameters()
                                .get('childobjecttypes')
                                .split(',')
    );
    
    // Use the sobjecttype describe method to retrieve all 
    // child relationships for the object to be cloned.    
    Schema.DescribeSObjectResult headDescribe = 
           headsObject.getSObjectType().getDescribe();
    
    List<Schema.ChildRelationship> childRelationships = 
           headDescribe.getChildRelationships(); 
    
    // Iterate through each relationship, 
    // and retrieve the related objects.       
    for (Schema.ChildRelationship childRelationship : 
                                  childRelationships)
    {
      Schema.SObjectType childObjectType = 
                         childRelationship.getChildSObject();
      
      // Only retrieve the objects if their type is 
      // included in the page argument.          
      if (childObjectTypes.contains(
                           childObjectType.getDescribe().getName()))
      {
        List<relatedObjectRow> relatedObjects = 
                         new List<relatedObjectRow>{};
                
        Schema.SObjectField childObjectField = 
                         childRelationship.getField();
                
        String relatedChildSObjectsquery = 
               'SELECT ID, Name FROM ' 
             + childObjectType.getDescribe().getName()
             + ' WHERE '
             + childObjectField.getDescribe().getName()
             + ' = \'' 
             + headsObject.Id
             + '\''; 
                                                        
        for (SObject childObject : 
             Database.query(relatedChildSObjectsquery))
        {
          relatedObjects.add(new relatedObjectRow(childObject));
        }
            
        if (!relatedObjects.isEmpty())
        {
          objectChildren.add(new relatedObjects(relatedObjects, 
                childObjectType.getDescribe().getLabelPlural(), 
                childObjectField.getDescribe().getName()));
        }  
      }
    }
  }
  
  // Perform the cloning process.
  // First clone the parent, then all of the child objects. 
  // Then redirect the user to the new object page.
  public PageReference doClone()
  {
    headClone = cloneObjects(new List<sObject>{headSObject}).get(0);
    
    insert headClone;
    
    cloneSelectedObjects();
    
    return new PageReference('/' + headClone.Id);
  }
  
  // Clone the selected child objects.
  // Associate the cloned objects with the new cloned parent object.
  public void cloneSelectedObjects()
  {
        
    List<sObject> clonedObjects = new List<sObject>{};
    List<sObject> selectedRelatedObjects;
     
    for (relatedObjects relatedObject : objectChildren)
    {
      selectedRelatedObjects = new List<sObject>{};  
      clonedObjects = new List<sObject>{};  
      
      for (relatedObjectRow row : relatedObject.objectRows) 
      {
        if (row.selected)
        {
          selectedRelatedObjects.add(row.obj);
        }
      }
      
      if (!selectedRelatedObjects.isEmpty())
      {
        clonedObjects = cloneObjects(selectedRelatedObjects);
        
        for (sObject clone : clonedObjects)
        {
          clone.put(relatedObject.relatedFieldName, headClone.Id);  
        }
        
        insert clonedObjects;
      }
    }
  }

  // Clone a list of objects to a particular object type
  // Parameters 
  // - List<sObject> sObjects - the list of objects to be cloned 
  // The sObjects you pass in must include the ID field, 
  // and the object must exist already in the database, 
  // otherwise the method will not work.
  public static List<sObject> cloneObjects(List<sObject> sObjects){
                                                
    Schema.SObjectType objectType = sObjects.get(0).getSObjectType();
    
    // A list of IDs representing the objects to clone
    List<Id> sObjectIds = new List<Id>{};
    // A list of fields for the sObject being cloned
    List<String> sObjectFields = new List<String>{};
    // A list of new cloned sObjects
    List<sObject> clonedSObjects = new List<sObject>{};
    
    // Get all the fields from the selected object type using 
    // the get describe method on the object type.    
    if(objectType != null)
    {
      for (Schema.SObjectField objField : 
           objectType.getDescribe().fields.getMap().values())
      { 
        Schema.DescribeFieldResult fieldDesc = objField.getDescribe();
        // If the field type is location, then do not include it,
        // otherwise it will cause a soql exception.
        // Note that excluding the field does not stop the location from
        // being copied to the new cloned object.
        if(fieldDesc.getType() != DisplayType.LOCATION)
        {
          sObjectFields.add(fieldDesc.Name);
        }
      }
    }
    
    // If there are no objects sent into the method, 
    // then return an empty list
    if (sObjects != null || 
        sObjects.isEmpty() || 
        sObjectFields.isEmpty()){
    
      // Strip down the objects to just a list of Ids.
      for (sObject objectInstance: sObjects){
        sObjectIds.add(objectInstance.Id);
      }

      /* Using the list of sObject IDs and the object type, 
         we can construct a string based SOQL query 
         to retrieve the field values of all the objects.*/
    
      String allSObjectFieldsQuery = 'SELECT ' + sObjectFields.get(0); 
    
      for (Integer i=1 ; i < sObjectFields.size() ; i++){
        allSObjectFieldsQuery += ', ' + sObjectFields.get(i);
      }
    
      allSObjectFieldsQuery += ' FROM ' + 
                               objectType.getDescribe().getName() + 
                               ' WHERE ID IN (\'' + sObjectIds.get(0) + 
                               '\'';

      for (Integer i=1 ; i < sObjectIds.size() ; i++){
        allSObjectFieldsQuery += ', \'' + sObjectIds.get(i) + '\'';
      }
    
      allSObjectFieldsQuery += ')';
    
      system.debug('allSObjectFieldsQuery: ' + allSObjectFieldsQuery);
    
      try{
      
        // Execute the query. For every result returned, 
        // use the clone method on the generic sObject 
        // and add to the collection of cloned objects
        for (SObject sObjectFromDatabase:
             Database.query(allSObjectFieldsQuery)){
          clonedSObjects.add(sObjectFromDatabase.clone(false,true));  
        }
    
      } catch (exception e){
      }
      
    }
   
    return clonedSObjects;
    
  }
  
  // Related objects data construct - 
  // used to store a collection of child objects connected to 
  // the head object through the same relationship field.
  public class relatedObjects
  {
    public List<relatedObjectRow> objectRows { get; set; }
    public String                 pluralLabel      { get; set; }
    public String                 relatedFieldName { get; set; }
    
    public relatedObjects(List<relatedObjectRow> objectRows, 
                          String pluralLabel, 
                          String relatedFieldName) 
    {
      this.objectRows       = objectRows;
      this.pluralLabel      = pluralLabel;
      this.relatedFieldName = relatedFieldName;
    }   
  }     

  // An indidual child object row. 
  // Each instance simply contains the object definition, 
  // and a checkbox to select the row for cloning 
  // on the clone plus page.
  public class relatedObjectRow
  {
    public sObject obj      { get; set; }
    public Boolean selected { get; set; }
    
    public relatedObjectRow(Sobject obj)
    {
      this.obj      = obj;
      // All object rows are selected by default.
      this.selected     = true;
    }
    
    public String getName(){
      try{
        return '' + obj.get('Name');
      } catch (Exception e){
        return '';
      }    
    }   
  }
}

2) Create a new Visualforce Page  (Navigate to Setup -> Develop -> Pages and click new).
Enter ClonePlus in both the label and name fields.




3) Copy the following into the Visualforce Markup section:

<apex:page controller="ClonePlusController" action="{!initialiseObjectsForCloning}">
  
  <apex:sectionHeader title="Clone Plus: {!objectName}"/>

  <apex:form id="theform" >
  
    Please select the child objects you would like to clone.<br/><br/>
  
    <apex:repeat value="{!objectChildren}" var="child">
      <apex:PageBlock title="{!child.pluralLabel}"> 
        <apex:pageBlockTable value="{!child.objectRows}" 
                             var="objectRow">
          <apex:column headerValue="Clone" width="10%">
            <apex:inputCheckbox value="{!objectRow.selected}"/>
          </apex:column>
          <apex:column headerValue="Name" value="{!objectRow.name}" 
                                          width="90%"/>
        </apex:pageBlockTable>
      </apex:PageBlock>
    </apex:repeat>
    
    <apex:PageBlock >
      <apex:commandButton action="{!doClone}" value="Clone"/>
    </apex:PageBlock>
  </apex:form>  

</apex:page>

4) Navigate to the custom button and links menu for the object you want to add the clone with children functionality for.

If this is standard object, select  Setup -> Customize -> *Object Name* -> Buttons and Links. See the following example for contact.



If instead you want to clone a custom object, go to the custom objects definition page (Setup -> Create Object) and select the object from the list. Once the custom object definition page is open, scroll down to see the button and links menu. The following screenshots show how to do this for an example object called Invoice Statement




5) Click on the new button on the menu. Populate the form values as follows:




6) In the button definition body, write a URL in the following form:

/apex/ClonePlus?id={!*Parent Object Type*.Id}&childobjecttypes=*Child Object Types*

Where  *Parent Object Type*  is the API name of the object you are creating the button for, and  *Child Object Types* is a comma separated list of the types of child object you want to be able to clone alongside your main parent object.

Here are some examples to help:

If you want to clone the standard Account object from the accont page, but also want to clone related standard Contact objects at the same time:

/apex/ClonePlus?id={!Account.Id}&childobjecttypes=Contact

If you want to clone the standard Campaign object, and also want the option to clone related standard Opportunity objects, but also related child instances of a custom object called Campaign Advert (Api name: Campaign_Advert__c):

/apex/ClonePlus?id={!Campaign.Id}&childobjecttypes=Opportunity,Campaign_Advert__c

If you want to clone a custom object of type Custom Parent (Api name: Custom_Parent__c) and two related child custom object types Custom Child (Api name:  Custom_Child__c) and Another Child (Api name:  Another_Child__c):

/apex/ClonePlus?id={!Custom_Parent__c.Id}&childobjecttypes=Custom_Child__c,Another_Child__c

Your form should look something like this:



Click the save to confirm the changes.

NOTE: If you have any problems defining your URL, write a comment below with the object names and relationships and I would be happy to reply with what the value should be.

7) Finally, add the new "Clone Plus" custom button to the page layouts you want it to appear on, using the handy WYSIWYG editior.

Page Layouts for standard objects can be found through the menu ( Setup -> Customize -> *Object Name* -> Page Layouts)



For custom objects, the page layouts appear on the same object summary page as the custom button and links menu.

So there you have it, Happy Cloning!! This is a first iteration of Clone Plus. I hope to develop this concept as time moves forward, all feedback and suggestions is greatly appreciated!

A few notes:
  • Due to the generic nature of the method, you can add this functionality to as many objects as you like. You do not need to create any extra controllers or pages, they can all use the two already defined.
  • At the moment clone plus only clones direct children, not parents, grandchildren or further relationships. Of course this is possible, all it takes is some more complex method calls. For this example I wanted to keep things simple.
  • In this example I make the user define the types of object they want to clone. You may be asking, why not just clone everything? This was actually the approach I had at the beginning, but after trying it out, there are a lot of setup objects that are related to each object type behind the scenes. Most of these related objects would never be cloned, so including them does nothing but confuse the user.


Sunday, 21 August 2011

Writing unit tests for the PageReference Class

Before you can deploy any Salesforce project to production, unit tests have to be written that ensure that future releases or platform upgrades do not cause any element of your system to malfunction. When writing tests for Apex controller classes, it is likely that you will have to write test methods that relate to action methods. Action methods in controllers have to return a Page Reference, which can be used to direct the user to another page as soon as the action completes. Parameters and other variables can also be added to the page references.

To achieve a suitable level of coverage and fault tolerance, your test methods should ensure that calling an action returns the correct page reference. It sounds simple enough, but testing these page references is not as straightforward as you might think. Take the following example of a simple controller method and an accompanying test method.

public class PageReferenceTestingController{

  PageReference changeThePageAction(){
    
   // Action code HERE

    return Page.SomeOtherPage;
  }
  
  static testmethod void testChangeThePageAction(){
  
    PageReferenceTestingController testPRTC = 
      new PageReferenceTestingController();
  
    system.assertEquals(Page.SomeOtherPage, 
                        testPRTC.changeThePageAction());
  }
}

Looks simple enough, but when we execute the test methods through the Salesforce interface, we retrieve the following error message:


Not really the most helpful message in the world, as the two are seemingly identical :) .

 The simplest way to avoid this problem is to simply compare the URL attributes of the page references, like so:

public class PageReferenceTestingController{

  PageReference changeThePageAction(){
  
    // Action code HERE
  
    return Page.SomeOtherPage;
  }
  
  static testmethod void testChangeThePageAction(){
  
    PageReferenceTestingController testPRTC = 
      new PageReferenceTestingController();
  
    system.assertEquals(Page.SomeOtherPage.getURL(), 
                        testPRTC.changeThePageAction().getURL());
  }
}

If we run the test methods for this class again, it successfully passes.

BUT......... we are only testing the URL of the redirected page, what if we have added parameters we want to check? Wouldn't it be better to test the whole object?

 The way to best achieve this is to write a static page reference comparison method. This will iterate through the different page reference attributes and compare them one by one. This will allow you to easily compare two page references quickly and effectively. See the following example.

ublic class PageReferenceTestingController{

  PageReference changeThePageAction(){
  
    // Action code HERE
  
    PageReference goToSomeOtherPage = Page.SomeOtherPage;
    
    goToSomeOtherPage.setRedirect(false);
    goToSomeOtherPage.getParameters().put('a','b');
    goToSomeOtherPage.getParameters().put('c','d');
    goToSomeOtherPage.getParameters().put('e','f');
  
    return goToSomeOtherPage;
  }
  
  static testmethod void testChangeThePageAction(){
  
    PageReferenceTestingController testPRTC = 
      new PageReferenceTestingController();
  
    PageReference testPageReference = Page.SomeOtherPage;
    
    testPageReference.setRedirect(false);
    testPageReference.getParameters().put('e','f');
    testPageReference.getParameters().put('a','b');
    testPageReference.getParameters().put('c','d');
  
    system.assert(arePageReferencesEqual(testPageReference, 
                                         testPRTC.changeThePageAction()));
  }
  
  static Boolean arePageReferencesEqual(PageReference page1, 
                                        PageReference page2){
  
    // First do a null test.
    if (page1 == null && page2 == null) return true;
    if (page1 == null && page2 != null) return false;
    if (page1 != null && page2 == null) return false;   
  
    // If none of the page references are null, compare their attributes.
    if (page1.getAnchor()     == page2.getAnchor()
     && page1.getURL()        == page2.getURL()
     && page1.getRedirect()   == page2.getRedirect()
     && page1.getCookies()    == page2.getCookies()
     && page1.getHeaders()    == page2.getHeaders()
     && page1.getParameters() == page2.getParameters()){
     
       return true;
    }   
      
    return false; 
  }
}

Friday, 15 July 2011

How to handle exceptions when using the Messaging.sendEmail Apex method

Apex, the native Salesforce OO language, provides methods to create and send emails. You can define your own body, or create standard templates using Visualforce, the Salesforce page markup language. Messages can be sent using the Messaging.sendEmail method. If any problems occur while attempting to send these emails, exceptions are thrown by the method.

Let's have a look at the method definition, taken directly from the Salesforce documentation.

Messaging.sendEmail(new Messaging.Email[] { mail } , opt_allOrNone);

The documentation explains that the all_or_none argument allows you to choose to send successful emails even if some fail. This sounds pretty straightforward, but what the documentation doesn't explain is that the value of this argument actually affects how errors are reported back to the calling program.

If the argument value is "true" or simply not populated, then all exceptions are thrown, and can be caught, reported and dealt with using a traditional try/catch structure.

    try{
      Messaging.sendEmailResult[] sendEmailResults = 
        Messaging.sendEmail(new Messaging.Email[] { mail });
    } catch(System.EmailException ex){
      // Exceptions are passed to here.
    }

However, if the value is set to "false" none of the exceptions are thrown. These are instead stored inside the Messaging.SendEmailResult collection that is passed back from the method call.

    
   Messaging.sendEmailResult[] sendEmailResults = 
      Messaging.sendEmail(new Messaging.Email[] { mail }, false);

   for(Messaging.SendEmailResult sendEmailResult: 
        sendEmailResults){
            
     if(!sendEmailResult.isSuccess()){
        // deal with failure here.
     }
   }
 

The following page and controller combo includes actions that allow you to directly see how calling the Messaging.sendEmail method with different argument values affects error handling. A sample combination of successful and failing emails are generated and passed to the method for testing.

Page:
<apex:page controller="EmailErrorHandlingExampleController">

    <apex:sectionHeader title="Email Error Handling Example"/>
    
    <apex:form >
        <apex:commandButton 
          action="{!sendSampleEmailsAllOrNothing}" 
          value="Send emails all or nothing"/>
        <apex:commandButton 
          action="{!handleIndividualSendEmailErrors}" 
          value="Send emails individually"/>
    </apex:form>    
    
    <apex:pageMessages />
    
</apex:page>

Controller:
public class EmailErrorHandlingExampleController {

  // Send some emails all or nothing, if one fails, all fail.
  public PageReference sendSampleEmailsAllOrNothing(){
    
    List<Messaging.SendEmailResult> sendEmailResults = 
      new List<Messaging.SendEmailResult>{};
    
    Integer numberOfSuccessfulMessages = 0;
    
    try{
      // note the all_or_none option has not been set 
      //(default value is true)
      // an email exception should be thrown, 
      // and the results list should not be populated. 
      sendEmailResults = 
        
        Messaging.sendEmail(getSampleEmailMessages());
     
    } catch(System.EmailException ex){
      // This message should be added to the page, 
      // as an exception is caught
      Apexpages.addMessage(new ApexPages.Message(
        ApexPages.Severity.ERROR, 
        'Caught email exception: ' + ex));
    }
        
    // This section of code should be skipped, 
    // as the result set is empty.
    for(Messaging.SendEmailResult sendEmailResult: 
        sendEmailResults){
            
      if(sendEmailResult.isSuccess()){
        numberOfSuccessfulMessages++;
      }
      else {
        for (Messaging.Sendemailerror sendEmailError : 
             sendEmailResult.getErrors()){
          
          Apexpages.addMessage(new ApexPages.Message (
            ApexPages.Severity.ERROR, 
            'Send Email Result Error: ' + 
            sendEmailError.Message));
        }
      }
    }
        
    // This page messsage should confirm 
    // no messages have been sent.
    Apexpages.addMessage(new ApexPages.Message (
      ApexPages.Severity.INFO, 
      'You should have just received ' 
      + numberOfSuccessfulMessages + ' emails'));
    
    return null;
  }
    
  // Send some emails on an individual basis. 
  // If one fails, only that one fails.
  public PageReference handleIndividualSendEmailErrors(){
        
    List<Messaging.SendEmailResult> sendEmailResults = 
      new List<Messaging.SendEmailResult>{};
    
    Integer numberOfSuccessfulMessages = 0;
    
    try{
      // note the all_or_none option has been set to false
      // an email exception should not be thrown, 
      // but rather stored in the result.
      sendEmailResults = 
        Messaging.sendEmail(getSampleEmailMessages(),false);
    
    } catch(System.EmailException ex){
      // This section should never be accessed, 
      // as an exception is never thrown.
      Apexpages.addMessage(new ApexPages.Message (
        ApexPages.Severity.ERROR, 
        'Caught email exception: ' + ex));
    }
        
    // This section of code should be run through four times, 
    // one for each populated result.
    for(Messaging.SendEmailResult sendEmailResult: 
        sendEmailResults){
            
      if(sendEmailResult.isSuccess()){
        numberOfSuccessfulMessages++;
      }
      else {
        for (Messaging.Sendemailerror sendEmailError : 
             sendEmailResult.getErrors()){
              
          Apexpages.addMessage(new ApexPages.Message (
            ApexPages.Severity.ERROR, 
            'Send Email Result Error: ' 
            + sendEmailError.Message));
        }
      }
    }
        
    // This message should indicate that 
    // two messages were successfully sent.
    Apexpages.addMessage(new ApexPages.Message (
      ApexPages.Severity.INFO, 
      'You should have just received ' + 
      numberOfSuccessfulMessages + ' emails'));
      
    return null;
  }

  // Get the current users emailAddress
  String currentUserEmailAddress {
        
    get{
      if (currentUserEmailAddress == null){
        
        // If the value has not been populated, retrieve it
        // using a SOQL query and the userinfo object.
        User currentUser = [SELECT email
                            FROM user
                            WHERE Id = :userInfo.getUserId()
                            LIMIT 1];
                                    
          currentUserEmailAddress = currentUser.email;
      }
        
      return currentUserEmailAddress;
    }
      
    set;
  }   

  // Generate a sample set of emails, 
  // some with errors, some without
  List<Messaging.SingleEmailMessage> getSampleEmailMessages(){
        
    // Simple success email message sent to the current user.    
    Messaging.SingleEmailMessage sampleSuccessEmail1 =
      new Messaging.SingleEmailMessage();
        
    sampleSuccessEmail1.setToAddresses(
      new List<String>{currentUserEmailAddress});
    
    sampleSuccessEmail1.setSubject('Just to let you know');
    
    sampleSuccessEmail1.setPlainTextBody(
      'This is a successful test email');
        
    // Simple fail message sent to the current user.    
    Messaging.SingleEmailMessage sampleFailEmail1 = 
      new Messaging.SingleEmailMessage();
        
    sampleFailEmail1.setToAddresses(
      new List<String>{currentUserEmailAddress});
    
    sampleFailEmail1.setSubject(
      'This is a failed email, it has no body!');
        
    // Simple fail message sent to no one.    
    Messaging.SingleEmailMessage sampleFailEmail2 =
      new Messaging.SingleEmailMessage();
        
    sampleFailEmail2.setToAddresses(new List<String>{});
    
    sampleFailEmail2.setSubject('This is a failed email');
   
    sampleFailEmail2.setPlainTextBody(
      'It is adressed to no one!');
        
    // Another Simple success email message 
    // sent to the current user.    
    Messaging.SingleEmailMessage sampleSuccessEmail2 = 
      new Messaging.SingleEmailMessage();
        
    sampleSuccessEmail2.setToAddresses(
    
      new List<String>{currentUserEmailAddress});
      
    sampleSuccessEmail2.setSubject(
      'Just to let you know (again)');
      
    sampleSuccessEmail2.setPlainTextBody(
      'This is another successful email');
        
    return new List<Messaging.SingleEmailMessage>{
      sampleSuccessEmail1,
      sampleFailEmail1,
      sampleFailEmail2,
      sampleSuccessEmail2};
  }
}

The following page messages should appear on the screen when you click the "Send emails all or nothing" command button:



Whereas these page messages should appear when the "Send emails individually" command button is clicked:



Being aware of the impact of the all_or_none argument on exception handling will help you to manage errors more effectively, and avoid writing redundant error handling code that will never be called.

Wednesday, 18 May 2011

How to dynamically clone Salesforce objects with all fields populated


UPDATE 25/04/2012: If cloning child objects using a custom button is of interest to you, check out my post regarding Clone Plus!!!  

There are several scenarios during development where it becomes necessary to create complete copies of instances of Salesforce objects. Salesforce already provides a generic "clone" method to copy objects by casting them first to a more primitive base sObject. The clone method for sObjects even has a parameter which re-creates all relationships opt_IsDeepClone. The documentation describes that turning this option to true will result in a full copy of the sObject being returned by the method.

However, a common problem developers have when using this method is that it does not automatically copy all of the objects field values from the database. Instead, the opt_IsDeepClone option only copies the field values currently held in memory that have been retrieved through use of a SOQL query or have been populated directly in the Apex code. 

It is well known that Salesforce does not provide a "SELECT * FROM" method, so creating a cloning method that is guaranteed to copy all fields, and maintaining that functionality if any modifications are later made to the object, is challenging.

The way to accomplish a complete clone including all field values is to make use of the schema object definitions, which are accessible through apex code. You can use these definitions to retrieve all the fields for a particular object. These field name strings can be concatenated into a SOQL query to ensure all values are retrieved. My example code can be seen below:

public class SObjectAllFieldCloner {

  // Clone a list of objects to a particular object type
  // Parameters 
  // - List<sObject> sObjects - the list of objects to be cloned 
  // - Schema.SobjectType objectType - the type of object to be cloned.
  // The sObjects you pass in must include the ID field, 
  // and the object must exist already in the database, 
  // otherwise the method will not work.
  public static List<sObject> cloneObjects(List<sObject> sObjects,
                                        Schema.SObjectType objectType){
    
    // A list of IDs representing the objects to clone
    List<Id> sObjectIds = new List<Id>{};
    // A list of fields for the sObject being cloned
    List<String> sObjectFields = new List<String>{};
    // A list of new cloned sObjects
    List<sObject> clonedSObjects = new List<sObject>{};
    
    // Get all the fields from the selected object type using 
    // the get describe method on the object type.
    if(objectType != null){
      sObjectFields.addAll(
        objectType.getDescribe().fields.getMap().keySet());
    }
    
    // If there are no objects sent into the method, 
    // then return an empty list
    if (sObjects != null && 
        !sObjects.isEmpty() && 
        !sObjectFields.isEmpty()){
    
      // Strip down the objects to just a list of Ids.
      for (sObject objectInstance: sObjects){
        sObjectIds.add(objectInstance.Id);
      }

      /* Using the list of sObject IDs and the object type, 
         we can construct a string based SOQL query 
         to retrieve the field values of all the objects.*/
    
      String allSObjectFieldsQuery = 'SELECT ' + sObjectFields.get(0); 
    
      for (Integer i=1 ; i < sObjectFields.size() ; i++){
        allSObjectFieldsQuery += ', ' + sObjectFields.get(i);
      }
    
      allSObjectFieldsQuery += ' FROM ' + 
                               objectType.getDescribe().getName() + 
                               ' WHERE ID IN (\'' + sObjectIds.get(0) + 
                               '\'';

      for (Integer i=1 ; i < sObjectIds.size() ; i++){
        allSObjectFieldsQuery += ', \'' + sObjectIds.get(i) + '\'';
      }
    
      allSObjectFieldsQuery += ')';
    
      try{
      
        // Execute the query. For every result returned, 
        // use the clone method on the generic sObject 
        // and add to the collection of cloned objects
        for (SObject sObjectFromDatabase:
             Database.query(allSObjectFieldsQuery)){
          clonedSObjects.add(sObjectFromDatabase.clone(false,true));  
        }
    
      } catch (exception e){
        // Write exception capture method 
        // relevant to your organisation. 
        // Debug message, Apex page message or 
        // generated email are all recommended options.
      }
    }    
    
    // return the cloned sObject collection.
    return clonedSObjects;
  }
}

An example of how you would call this method  to clone an Account object from another Apex controller is:

Account originalAccount = [select Id from Account where 
                                name = 'My Favourite Account'];

sObject originalSObject = (sObject) originalAccount;

List<sObject> originalSObjects = new List<sObject>{originalSObject};
      
List<sObject> clonedSObjects = SObjectAllFieldCloner.cloneObjects(
                                      originalSobjects,
                                      originalSobject.getsObjectType());
                                     
Account clonedAccount = (Account)clonedSObjects.get(0);

Notes: This query only copies a single type of object at a time, related child objects are not cloned, however all lookup relationships/master record references are copied. Also in this example, I have shown how to create a virtual "SELECT * FROM" to make this resistant to change. This type of query should only be used when absolutely necessary, never out of developer laziness.

There are a lot of combined concepts in this example; mapping objects, using object definitions and generic sObject methods amongst other. If you require any further explanation to any of the elements of this solution, please list a question below.

Wednesday, 9 June 2010

2 uses of the apex:outputPanel tag

Throughout my experience with the force, I have come across a few problems where the solution involved simply the inclusion an apex:outputPanel tag. Below are two uses that are not commonly known.

  1. Multi-component facets

    When using facets, salesforce only allows for a single sub-component as the contents. If you require multiple components, such as an image and text for an action status, then simply enter an apex:outputPanel tag inside the facet, then place all of your components inside.


  2. Partial form refreshing

    When developing apex pages, a common request is to make components appear and hide using the 'rendered' attribute. If a component is hidden, and an action occurs that then requires that component to be shown, you cannot simply re-render the component, as it was never rendered and so does not exist in the current context of the page. Normally this means you have to refresh an entire form, but this can be avoiding using an ouput panel around the components to be shown and hidden, and by using the id of the output panel in the 'reRender' attribute of the action