Dynamically Sorting with Comparable Interface

I have seen several time questions regarding sorting in visual page, so I wrote simple example that show how to implement such requirement with the comparable interface.

I have visual page that show list of Case records.
The requirement is to allow sorting the list by clicking on the header of each column.

There are some techniques for achieving this functionality:
1.With JavaScript code inside the page.
2.Comparable interface (this example).
3.Order By in the SQL and re-running the query after each time the user click column header.

The javascript might be little complicate for people who not familiar with this language, the last one might be limited for some cases and require re-quering database each time, so I found the Comparable method as the most flexible and comfortable for most cases.


1.The controller for the page - vf_PageWithSorting


public class vf_PageWithSorting   {  

 public list<CaseWrp> caseWrpLst {get; set;}  
 public static String compareField {get; set;}  
 public static String sortOrder{get; set;}  

 public vf_PageWithSorting()  {  
  caseWrpLst = new list<CaseWrp>();  

  for(Case c : [ select Id, CaseNumber, Status, Priority, Subject from Case])  {  
   caseWrpLst.add(new CaseWrp(c));  
  }  
 }  

 public void sortWrpLst() {  
  caseWrpLst.sort();  
 }  

 public class caseWrp implements Comparable {  
  public Case caseRec {get; set;}  
  public Boolean selected {get; set;}  

  public caseWrp(Case c) {  
   caseRec = c;  
   selected = false;  
  }  

  public Integer compareTo(Object vCase) {  
   caseWrp caseToComp = (caseWrp )vCase;  

   if((String)caseRec.get(compareField) > (String)caseToComp.caseRec.get(compareField)) {
    return sortOrder.equals('asc') ? 1 : 0;  
   }
   else {
    return sortOrder.equals('asc') ? 0 : 1;  
   }
  }  
 }  
}  

The class have list of wrapper class - CaseWrpLst- that will be displayed in the page.

Also we define 2 static variables:
-compareField - should contain the name (API) of the field that should use for the sorting.
-sortOrder - the sort order (asc/desc).
Those 2 fields should be defined as static since they referenced from other class -CaseWrp- which use those fields for the sorting logic.

The sorting logic is in lines : 35-43. It compare 2 object caseWrp according to the field compareField. Returning 1 means the current object greater than the object he compare to.

In line 19 function sortWrpLst call function sort on the list of wrapper object.
We can automatically use the sort function since we define our class Comparable and implement the compareTo function. The sort function automatically use the compareTo function in the object.


2.The page - PageWithSorting


<apex:page controller="vf_PageWithSorting ">  
 <apex:form id="frm">  
  <apex:sectionHeader title="Case List with Sorting" >  
   <apex:pageBlock>   
    <apex:pageBlockTable value="{!caseWrpLst}" var="caseWrp">  

     <apex:column headerValue="Selected" width="50">  
      <apex:inputCheckbox value="{!caseWrp.selected}" />  
     </apex:column>  

     <apex:column>  
      <apex:facet name="header">  
       <apex:commandLink value="Case Number {!IF(compareField=='CaseNumber',IF(sortOrder='asc','▼','▲'),'')}" action="{!sortWrpLst}">  
        <apex:param name="compareField" value="CaseNumber" assignTo="{!compareField}" />  
        <apex:param name="orderType" value="{!IF(sortOrder='asc', 'desc', 'asc')}" assignTo="{!sortOrder}" />  
       </apex:commandLink>  
      </apex:facet>  
      <apex:outputField value="{!caseWrp.caseRec.CaseNumber}"/>  
     </apex:column>  

     <apex:column>  
      <apex:facet name="header">  
       <apex:commandLink value="Subject {!IF(compareField=='Subject',IF(sortOrder='asc','▼','▲'),'')}" action="{!sortWrpLst}">  
        <apex:param name="compareField" value="Subject" assignTo="{!compareField}" />  
        <apex:param name="orderType" value="{!IF(sortOrder='asc', 'desc', 'asc')}" assignTo="{!sortOrder}" />  
       </apex:commandLink>  
      </apex:facet>  
      <apex:outputField value="{!caseWrp.caseRec.Subject}"/>  
     </apex:column>  

     <apex:column>  
      <apex:facet name="header">  
       <apex:commandLink value="Status {!IF(compareField=='Status',IF(sortOrder='asc','▼','▲'),'')}" action="{!sortWrpLst}">  
        <apex:param name="compareField" value="Status" assignTo="{!compareField}" />  
        <apex:param name="orderType" value="{!IF(sortOrder='asc', 'desc', 'asc')}" assignTo="{!sortOrder}" />  
       </apex:commandLink>  
      </apex:facet>  
      <apex:outputField value="{!caseWrp.caseRec.Status}"/>  
     </apex:column>  

     <apex:column>  
      <apex:facet name="header">  
       <apex:commandLink value="Priority {!IF(compareField=='Priority',IF(sortOrder='asc','▼','▲'),'')}" action="{!sortWrpLst}">  
        <apex:param name="compareField" value="Priority" assignTo="{!compareField}" />  
        <apex:param name="orderType" value="{!IF(sortOrder='asc', 'desc', 'asc')}" assignTo="{!sortOrder}" />  
       </apex:commandLink>  
      </apex:facet>  
      <apex:outputField value="{!caseWrp.caseRec.Priority}"/>  
     </apex:column>  

    </apex:pageBlockTable>  
   </apex:pageBlock>  
  </apex:sectionHeader>  
 </apex:form>  
</apex:page>  

The page show all the data inside PageBlockTable.
Inside each column we define commandLink so the user can click on the column.
The title on the column is the name of the field + sign ▼ or ▲ if it is the current compareField.
The action on each link is to call the function sortWrpLst on the controller (which sort the list).
The apex:param inside the commandLink used to assign the relevant variable in the class, according to the field.


Result (after clicking the "Subject" column):

Using Apex Class from Visual Flow

Visual flow it's a very nice and useful tool for creating process with minimum time effort.

However, there are cases where you can implement most of your requirement easily with the flow, except small part of the process which can be done only with code.
For that reason we can connect our flow to apex class, and from there we can do almost anything.

I will show it with simple example:
Visual flow to close case (update the status to closed) instead of the standard Close Case button, and sending email to inform specific person that the case was closed.
The last part can be done with apex class (actually it can also be done with workflow, but for the example I'll use apex code).


1.Creating the class.

This class must implement interface Process.Plugin, and must implement 2 methods:

Process.PluginResult invoke(Process.PluginRequest request)
            This is the function that the flow call from the class.

Process.PluginDescribeResult describe()
            this function describe to the flow which data the plugin need to receive.


global with sharing class cls_FlowSendEmailCase implements Process.Plugin {  

 //The main method to be implemented. The Flow calls this at runtime.   
 global Process.PluginResult invoke(Process.PluginRequest request) {   
  String caseId = (String) request.inputParameters.get('vCaseID');  
  String emailAddress = (String) request.inputParameters.get('vEmailAddress');  

  sendCaseEmail(caseId, emailAddress );  

  //return to Flow   
  Map<String,Object> result = new Map<String,Object>();  
  return new Process.PluginResult(result);   
 }   

 //Returns the describe information for the interface   
 global Process.PluginDescribeResult describe()  {   

  Process.PluginDescribeResult result = new Process.PluginDescribeResult();  
  result.Name = 'CloseCase';  
  result.Tag = 'Name';  

  result.inputParameters =new List<Process.PluginDescribeResult.InputParameter>();  

  result.inputParameters.add(  
   new Process.PluginDescribeResult.InputParameter('vCaseID',  
   Process.PluginDescribeResult.ParameterType.STRING, true));  

  result.inputParameters.add(  
   new Process.PluginDescribeResult.InputParameter('vEmailAddress',  
   Process.PluginDescribeResult.ParameterType.STRING, true));  

  return result;  
 }  

 public void sendCaseEmail(String caseId, String emailAddress) {  
  Case caseRec = [SELECT id, caseNumber FROM Case WHERE id=:caseId];  

  Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();  
  mail.setToAddresses(new String[] {emailAddress});  
  mail.setSubject('Case Closed. ' + caseRec.caseNumber);  
  mail.setPlainTextBody('This case has been closed ');  

  Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });  
 }  
}  



In this example the class received 2 parameters: the case ID and the email address (line 6-7).

In line 9, we call function sendCaseEmail, to send the email.

After creating such class (that implement Process.Plugin), we will see it in the flow designer, and will be able to use it by dragging it from the Palette tab to our flow.

2.Creating the flow.

This is our flow



First It will show kind of information message + option to send email and filling the email address.


Next we will first update the case to closed, and send email depend on the checkbox that user tag.

If the user select the check-box (equal true), then the flow will get to the CloseCase Send Case Email (with the plugin icon) and call the function invoke inside the class.

When we double clicking this square, we will show the variable we defined in the describe method, and we will be able to assign values to them.

The caseId in the class get the case id of the flow (should pass in the URL when calling the flow).
The email address get the email from the user input (stage 1 in the flow).



3.Finally add custom button in Case, that will call this flow.

The button will call URL:
/flow/CloseCase?vCaseID={!Case.Id}&retURL={!Case.Id}

The vCaseID is variable inside the flow (name must be exactly the same).

4.The final result:


Filling the email address and clicking 'Next'

Confirmation message

The email received and the case closed.

Using Dynamic Component in VisualForce Page

For some cases it might be useful to build component for visualforce page dynamically inside the code.

Take a look at the following code example of generating PageBlockTable inside the code.


public class vf_DynamicComponentExmp {  

 transient public Component.Apex.PageBlockTable table {get;set;}  

 //list of record to display  
 public list<Case> caseLst {get; set;}  

 //fields and labels to display in the page block table  
 public list<String> displayFieldLst= new list<String>{'CaseNumber', 'Subject', 'Priority', 'Status', 'ContactID'};  
 public list<String> labelFieldLst= new list<String>{'Case Number', 'Subject', 'Priority', 'Status', 'Contact'};  

 public vf_DynamicComponentExmp() {  
  String dynamicSQL='SELECT id ';  
  for(String field : displayFieldLst) {
   dynamicSQL+=', ' + field;  
  }

  dynamicSQL+=' FROM Case';  

  caseLst=Database.query(dynamicSQL);  

  table = new Component.Apex.PageBlockTable(var='rec');  
  table.expressions.value='{!caseLst}';  
    
  //add the field  
  Component.Apex.Column column;  
  Component.Apex.InputField inputField;  

  for(Integer index=0;index<displayFieldLst.size();index++) {  
   column = new Component.Apex.Column(headerValue= '' + labelFieldLst.get(index) + '');  
   inputField = new Component.Apex.InputField();  
   inputField.expressions.value = '{!rec.' + displayFieldLst.get(index) + '}';  
   column.childComponents.add(inputField);  
   table.childComponents.add(column);  
  }  
 }  

 public PageReference saveData() {  
  try {   
   update caseLst;   
  }   
  catch(Exception e) {   
   ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Error while saving ' + e));   
  }   
  return ApexPages.currentPage();   
 }  
}  

In the page we reference it by:
<apex:dynamicComponent componentValue="{!table}"/>


<apex:page controller="vf_DynamicComponentExmp">  
 <apex:pageMessages id="errors" escape="false" />   
 <apex:form >  
  <apex:pageBlock >  
   <apex:pageBlockButtons location="top" id="buttons">   
    <apex:commandButton id="Save" value="Save" action="{!saveData}" />   
   </apex:pageBlockButtons>  

   <apex:dynamicComponent componentValue="{!table}"/>   
  </apex:pageBlock>   
 </apex:form>  
</apex:page>  



The result:




Link to Approval Process Screen

When using approval process you may not specify email template.
In this case SF will use the default template for approval processes with the following structure:
http://login.salesforce.com/help/doc/en/wf_approval_settings.htm

In case you want a customise email template you need to create it.
Sometimes, even when you do create your own email template you want to have the link to the approval screen.

I'll demonstrate how it add it with approval process on Case object.

1.Create email template on Case object.
After desinging the template as needed, at the end add component:
<c:CmpApprovalURL objID="{!relatedTo.id}"/>



<messaging:emailTemplate subject="Case onHold Request" recipientType="User" relatedToType="Case">  
 <messaging:htmlEmailBody >  
  You have been requested to approve or reject this case:<br/>  
  {!relatedTo.Subject}<br/>  
  Contact: {!relatedTo.Contact.Name}<br/>  
  Priority: {!relatedTo.Priority}<br/><br/>  

  <c:CmpApprovalURL objID="{!relatedTo.id}"/>  
 </messaging:htmlEmailBody>  
</messaging:emailTemplate>  

2.Component:CmpApprovalURL.
Using String urlStr at class cls_CmpApprovalUrl


<apex:component controller="cls_CmpApprovalURL" access="global">  
 <apex:attribute name="objID" description="Obj ID" type="Id" assignTo="{!caseID}"/>  
 <a href="{!urlStr}">Log-in to approve or reject</a>  
</apex:component> 

3.Class:cls_CmpApprovalURL.
Need 2 variable :
1.Input for the CaseID
2.Output URL


public class cls_CmpApprovalURL  {  
 
 public String caseID {get; set;}  

 public String urlStr{  
  get  {  
   return cls_createApprovalURL.generateApprovalURL(caseID);  
  }}  
}  


4.Class cls_createApprovalURL
I create the function generateApprovalURL in this seperate class since it can be used for any object for any approval process.
It receive object ID, and return the correct URL for it's approval screen.


public class cls_createApprovalURL  {  
   
 public static String generateApprovalURL(String recordID)  {  
  String url='';  
  
  List<ProcessInstanceWorkitem> workItemLst =   
   [  SELECT id  FROM ProcessInstanceWorkitem  
    WHERE processInstance.TargetObjectId=:recordID];   

  if(workItemLst.size() > 0)  {  

   url='https://'+ System.URL.getSalesforceBaseUrl().getHost() +   
    '/p/process/ProcessInstanceWorkitemWizardStageManager?id=' + workItemLst[0].id;      
  }  
  return url;  
 }  
}


5.The result:

In the email received:

The link from the email:


Long Text Field Limitations

There are some actions in SF which cannot use long text fields.
For example:
* Filters in look up window.
* Filter in where clauses in SOQL.
* Track history for those fields value (SF can only track that the field has been changed).

Kind of workaround for this issues is to create another text field which contain the first 255 characters (or keyword) of the original long text, and use it in all relevant processes.

It's not perfect, but for many cases it will suffice.


I'll demonstrate it with 2 custom objects I created:
1.Author
2.Book

The Author have look up field to Book (Favourite Book), and in the look up I want
to allow filtering by long text field - description.

So I'll use the above workaround:


1.Create String field - short description






2.Create work flow + field update to populate this new field from the long text field.





3.Add the Short Description field to the filter fields.





In case you don't see this Lookup Filter Fields section in the object:
go to Setup -> App Setup --> Customise -->Search -->Search Settings

In the Lookup Settings select the check box in 'Enhance Lookup' for the relevant object.


4.Result: you can use the short description field to filter.




In addition, you can use the short description field for the other processes that mentions at the beginning (track history, SOQL query).





Generic Mass Edit Page

Hi,

This is really nice piece of code that can be reuse for any object.
The following visual page allow you to mass edit many records quickly with javascript.
Any comment will be appreciate.

1.Usage:
Build the URL as follow:
/apex/MassEditPage?ids= < list of records ds seperated by :> &obj= <object API name> &fSet= <fields set for the object>

Parameters:
ids - is the records ids that you want to edit seperated by ':'
obj - abject API name of the records you updating
fSet - field set of the fields you want to modify.

2.functionality:
The functionality is to display list of records in page block table, and above single "dummy" record, that allowing to use mass edit. each time a field in the "dummy" record is changed we run javascript code to change the corresponding field in the list.




3.Field set:
The use of field Set (the third parameter in the URL) allowing the page to work dynamically, and of course allowing also for update the list of editable fields without changing the code.

How to create Field Set?



public class vf_MassEditPage   {  
 
 public list<sObject> objLst {get; set;}               //list of object to update  
 public list<String> objFieldLst {get; set;}          //list of fields in the object  
 public map<String, String> fieldAPI_Label {get; set;}  
 public list<sObject> dummyObjLst {get; set;}     //list with 1 object- use for mass update in the page  

 public vf_MassEditPage()  {  
  
  objFieldLst=new list<String>();  
  fieldAPI_Label=new map<String, String>();  

  //collect the object id from URL  
  list<String> objidLst=new list<String>();  
  for (String id : ApexPages.currentPage().getParameters().get('ids').split(':'))  {
   objidLst.Add(id);  
  }

  String objName=ApexPages.currentPage().getParameters().get('obj');  
  String fieldSetName=ApexPages.currentPage().getParameters().get('fSet');  

  Schema.SObjectType sObjectTypeObj = Schema.getGlobalDescribe().get(objName);  
  Schema.DescribeSObjectResult describeSObjectResultObj = sObjectTypeObj.getDescribe();  
  Schema.FieldSet fieldSetObj = describeSObjectResultObj.FieldSets.getMap().get(fieldSetName);  

  //build sql to get all the objects  
  String sql='SELECT id';  
  for(Schema.FieldSetMember field : fieldSetObj.getFields())  {  
   sql+=',' + field.getFieldPath();  
   objFieldLst.add(field.getFieldPath());  
   fieldAPI_Label.put(field.getFieldPath(), field.getLabel());  
  }  
  sql+=' FROM ' + objName + ' WHERE id in : objidLst';  

  objLst=Database.query(sql);  

  dummyObjLst=new list<sObject>{sObjectTypeObj.newSobject()};  
 }  

 public PageReference saveData()  {  
  try  {  
   update objLst;  
  }  
  catch(Exception e)  {  
   ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Error while saving ' + e));  
  }  
  return ApexPages.currentPage();  
 }  
}  


page:

<apex:page controller="vf_MassEditPage" sidebar="false" id="pageid">  

 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>  
 <script type="text/javascript">  

  function updateAll(inputComp, fieldTarget)  {  
   var inputElem = document.getElementsByTagName("input");  
   for(var i=0; i < inputElem.length; i++)   {  
    if (inputElem[i].className == 'cls_' + fieldTarget)   {  
     inputElem[i].value = inputComp.value;  
    }       
   }  
  }  
 </script>  

 <apex:form id="frm">  
  <apex:pageMessages id="errors" escape="false" />  
  <apex:pageBlock mode="edit" id="results">  
   <apex:pageBlockButtons location="top" id="buttons">  
    <apex:commandButton id="Save" value="Save" action="{!saveData}" />  
   </apex:pageBlockButtons>  

   <apex:pageBlockTable value="{!dummyObjLst}" var="obj" id="editLst1" >  
    <apex:repeat value="{!objFieldLst}" var="fieldName" id="editLst2">  
     <apex:column headerValue="{!fieldAPI_Label[fieldName]}">  
      <apex:inputField value="{!obj[fieldName]}" onChange="updateAll(this, '{!fieldName}')"/>  
     </apex:column>  
    </apex:repeat>  
   </apex:pageBlockTable>  

   <apex:pageBlockTable value="{!objLst}" var="obj" id="objData1" >  
    <apex:repeat value="{!objFieldLst}" var="fieldName" id="objData2">  
     <apex:column >  
      <apex:inputField styleClass="cls_{!fieldName}" value="{!obj[fieldName]}"/>  
     </apex:column>  
    </apex:repeat>  
   </apex:pageBlockTable>  
  </apex:pageBlock>  
 </apex:form>  
</apex:page>  

Retire of Permission on Profiles

If you are working as a Salesforce admin/developer you've probably heard somewhere that Salesforce is planning to make a significant cha...