Monday, March 23, 2015

, , ,

Getting Dependent Picklist Values from your Apex Code


The requirement is to get all the State values based on the Country values from the standard Country - State picklist values.
In several Salesforce.com implementations, the standard State and Country Picklists are enabled and setup as per the org requirement.
In many cases, we may need to query all the States active for a specific country.
For example, 
1. In a custom page we want to show users valid States to choose from based on the Country chosen by the user.
2. We might have an external UI application using the Force.com platform and we need to expose State values based on Country values.
To do so, I need to fetch dependent picklist values based on the controlling field value.
For example, for country United States the values returned should be "Alabama", "Alaska", "Arizona",......etc.
Salesforce does not allow in your apex code to directly fetch such values.
However, in the API documentation (Salesforce documentation), the PicklistEntry object is shown to have a field as below:       
Name   Type   Description
validFor   byte[]  A set of bits where each bit indicates a controlling  value for which this PicklistEntry is valid.
So there we have the information of the dependency, which value in a dependent picklist set depends on which controlling value. 
Now to use this information, we have two hurdles to overcome:
First problem, as the description of validFor says, the value returned is a set of bits, which we need to decode to get the original field value.
Second problem, we need to use SOAP API from an external application to get the values based on the controlling value.

Now we solve.
For the first problem, Salesforce document gives a Java method, showing how to decode the bits and get the validFor value. We need to find a way to get the values in the validFor in a human readable format, namely, the string value.
We create an Apex method to find the values from the byte array we get.
Now, my requirement is to get the dependent values based on the controling value in my apex class and expose the same values to an external service to consume, without any additional logic on the external application.
The external application will just use a simple service query and get all the results, like in a simple REST query to get the value of a text field.
Once we think about it, the Salesforce documentation exposes the validFor field in the API calls. Hence, if we serialize this PicklistEntry object value to a JSON structure, the validFor value will get transfered, and if we now deserialize the same JSON into a wrapper class we can get the validFor value. Then, all we need to do is to decode this set of bits, and get the validFor value.
What we do first, is create an Apex class to decode the bit set for validFor following the Java example in the documentation.

public class BitSet{
    public Map<String,Integer> alphaNumCharCodes {get;set;}
    public Map<String, Integer> base64CharCodes {get;set;}
    
    public BitSet(){
        LoadCharCodes();
    }
    
    //Method loads the character codes for all letters
    private void LoadCharCodes(){
        alphaNumCharCodes = new Map<String,Integer>{
            'A'=>65,'B'=>66,'C'=>67,'D'=>68,'E'=>69,'F'=>70,'G'=>71,'H'=>72,'I'=>73,'J'=>74,
            'K'=>75,'L'=>76,'M'=>77,'N'=>78,'O'=>79,'P'=>80,'Q'=>81,'R'=>82,'S'=>83,'T'=>84,
            'U'=>85,'V'=> 86,'W'=>87,'X'=>88,'Y'=>89,'Z'=>90    
        };
        base64CharCodes = new Map<String, Integer>();
        //all lower cases
        Set<String> pUpperCase = alphaNumCharCodes.keySet();
        for(String pKey : pUpperCase){
            //the difference between upper case and lower case is 32
            alphaNumCharCodes.put(pKey.toLowerCase(),alphaNumCharCodes.get(pKey)+32);
            //Base 64 alpha starts from 0 (The ascii charcodes started from 65)
            base64CharCodes.put(pKey,alphaNumCharCodes.get(pKey) - 65);
            base64CharCodes.put(pKey.toLowerCase(),alphaNumCharCodes.get(pKey) - (65) + 26);
        }
        //numerics
        for (Integer i=0; i<=9; i++){
            alphaNumCharCodes.put(string.valueOf(i),i+48);
            //base 64 numeric starts from 52
            base64CharCodes.put(string.valueOf(i), i + 52);
        }
    }
        
      public List<Integer> testBits(String pValidFor,List<Integer> nList){
            List<Integer> results = new List<Integer>();
            List<Integer> pBytes = new List<Integer>();
            Integer bytesBeingUsed = (pValidFor.length() * 6)/8;
            Integer pFullValue = 0;
            if (bytesBeingUsed <= 1)
                return results;
            for(Integer i=0;i<pValidFor.length();i++){
                pBytes.Add((base64CharCodes.get((pValidFor.Substring(i, i+1)))));
            }   
            for (Integer i = 0; i < pBytes.size(); i++)
            {
                Integer pShiftAmount = (pBytes.size()-(i+1))*6;//used to shift by a factor 6 bits to get the value
                pFullValue = pFullValue + (pBytes[i] << (pShiftAmount));
            }
            
            Integer bit;
            Integer targetOctet;
            Integer shiftBits;
            Integer tBitVal;
            Integer n;
            Integer nListSize = nList.size();
            for(Integer i=0; i<nListSize; i++){
                n = nList[i];
                bit = 7 - (Math.mod(n,8)); 
                targetOctet = (bytesBeingUsed - 1) - (n >> bytesBeingUsed); 
                shiftBits = (targetOctet * 8) + bit;
                tBitVal = ((Integer)(2 << (shiftBits-1)) & pFullValue) >> shiftBits;
                if (tBitVal==1)
                    results.add(n);
            }
            return results;
        }
}

Next, important step, is to define a wrapper class which we will use to deserialize the JSON format and get the validFor value in a field of the class.


public class PicklistEntryWrapper{
    
    public PicklistEntryWrapper(){            
    }
    public String active {get;set;}
    public String defaultValue {get;set;}
    public String label {get;set;}
    public String value {get;set;}
    public String validFor {get;set;}
}

Next, we fetch all the controlling and dependent values for specific picklist fields in an object.
Then we search through each dependent value and see if it is valid for a specific controlling field.
We define a method to do so. To get any dependent picklist values we pass the method, the object api name, the controlling field api name and the dependent api field name.
We get back the list of dependent values, as a comma separated text, for each controlling field value.
For each controlling field we check the dependent values and check if the dependent is valid for the controlling field. We insert the controlling field into a Map<String,List<String>> as the key and the list of dependent values in a list as the value.


public with sharing class PicklistFieldController {    
    
    public Map<String,List<String>> getDependentOptionsImpl(String objName, String contrfieldName, String depfieldName){
        
        String objectName = objName.toLowerCase();
        String controllingField = contrfieldName.toLowerCase();
        String dependentField = depfieldName.toLowerCase();        
        
        Map<String,List<String>> objResults = new Map<String,List<String>>();
        //get the string to sobject global map
        Map<String,Schema.SObjectType> objGlobalMap = Schema.getGlobalDescribe();
        if (!Schema.getGlobalDescribe().containsKey(objectName)){
            System.debug('OBJNAME NOT FOUND --.> ' + objectName);
            return null;
        }
        
        Schema.SObjectType objType = Schema.getGlobalDescribe().get(objectName);
        if (objType==null){
            return objResults;
        }
        Bitset bitSetObj = new Bitset();
        Map<String, Schema.SObjectField> objFieldMap = objType.getDescribe().fields.getMap();
        //Check if picklist values exist
        if (!objFieldMap.containsKey(controllingField) || !objFieldMap.containsKey(dependentField)){
            System.debug('FIELD NOT FOUND --.> ' + controllingField + ' OR ' + dependentField);
            return objResults;     
        }
        List<Schema.PicklistEntry> contrEntries = objFieldMap.get(controllingField).getDescribe().getPicklistValues();
        List<Schema.PicklistEntry> depEntries = objFieldMap.get(dependentField).getDescribe().getPicklistValues();
        objFieldMap = null;
        List<Integer> controllingIndexes = new List<Integer>();
        for(Integer contrIndex=0; contrIndex<contrEntries.size(); contrIndex++){            
            Schema.PicklistEntry ctrlentry = contrEntries[contrIndex];
            String label = ctrlentry.getLabel();
            objResults.put(label,new List<String>());
            controllingIndexes.add(contrIndex);
        }
        List<Schema.PicklistEntry> objEntries = new List<Schema.PicklistEntry>();
        List<PicklistEntryWrapper> objJsonEntries = new List<PicklistEntryWrapper>();
        for(Integer dependentIndex=0; dependentIndex<depEntries.size(); dependentIndex++){            
               Schema.PicklistEntry depentry = depEntries[dependentIndex];
               objEntries.add(depentry);
        } 
        objJsonEntries = (List<PicklistEntryWrapper>)JSON.deserialize(JSON.serialize(objEntries), List<PicklistEntryWrapper>.class);
        List<Integer> indexes;
        for (PicklistEntryWrapper objJson : objJsonEntries){
            if (objJson.validFor==null || objJson.validFor==''){
                continue;
            }
            indexes = bitSetObj.testBits(objJson.validFor,controllingIndexes);
            for (Integer idx : indexes){                
                String contrLabel = contrEntries[idx].getLabel();
                objResults.get(contrLabel).add(objJson.label);
            }
        }
        objEntries = null;
        objJsonEntries = null;
        return objResults;
    }

}

Now, to get the standard Country - State picklist values, we need to make use of two fields in the Account object.
a) ShippingCountryCode, b) ShippingStateCode

Once we use these two fields as the controlling field and the dependent field, we get all the Country names and the dependent State names respectively.

We write a simple Test class and to get the dependent values and print them.


@isTest
public class PicklistFieldControllerTest {    
    static testMethod void getDependentOptionsImplTest(){
        PicklistFieldController controller = new PicklistFieldController();
        Map<String,List<String>> valueMap = controller.getDependentOptionsImpl('Account','ShippingCountryCode','ShippingStateCode');
        for(String contr : valueMap.keySet()){
            System.debug('CONTROLLING FIELD : ' + contr);
            System.debug('DEPENDENT VALUES ...  : ' + valueMap.get(contr));
        }
    }

}

I have only two Countries enabled 1. India, 2. Unites States
So what I get running the test class :

CONTROLLING FIELD : United States
DEPENDENT VALUES ...  : Armed Forces Americas, Armed Forces Europe

CONTROLLING FIELD : India
DEPENDENT VALUES ...  : Andaman and Nicobar Islands, Andhra Pradesh, Arunachal Pradesh, Assam, Bihar, Chandigarh, Chhattisgarh, Dadra and Nagar Haveli, Daman and Diu, Delhi, ...

So we are done. Now, access dependent values based on controlling value for a dependent picklist field, right from your apex code.

Sunday, March 15, 2015

, , ,

Using the New Process Builder - Part 3 - Adding Scheduled Actions to a Process


This is part of the series - Using the New Process Builder

This time let us create a scheduled action for a process
My requirement is when - an Account is created with Rating Hot, after an hour, I will generate an email alert to notify the account owner.

*Note: (I have a custom picklist 'Account Rating' on my Account object with values - Cold,Warm, Hot)

Scheduled actions can be used for Business use cases like:

  1. When a Case remains open for more than 2 days, send an email alert to the owner for attending the case.
  2. Auto update any field after some time of a record creation etc.

For us, let us create a new Process - name it Process to 'Account Create Email Alert'.

First let us create an Email Template we will use for the process.





Create the Email Alert, we need to create an Email Alert on the Account object, with Process Builder only Email Alerts on the object for which the process is being created is available for use. Also remember, if you configure an email alert to be sent from an organization-wide email address, that email alert isn't supported in flows and processes.






Now we come to the actual process.

Step 1: Create a New Process, we name it 'Account Create Email Alert' add a nice description as well :)



Step 2: Add the Account object to the process, we want to have the process working on Account create/edit.



Step 3: Add a criteria, we check the 'Account Rating' to be Hot, we check both on Account creation as well as on Account update.



Do select 'Do you want to execute the actions only when specified changes are made to the record?' as Yes. Only on selection of this the Scheduled action is enabled. This setting is similar to the setting 'created, and any time it’s edited to subsequently meet criteria' used for workflows.


Step 4: Now we add the schedule, we add a schedule of 1 hour after the Account is Last modified.




Step 5: Let us add the final step, that is, the scheduled action. The email alert we created.




Step 6: All done, we activate the process.

Step 7: To check the Scheduled action we can see the 'Paused and Waiting Interviews' under Workflow & Approvals -> Flows. Basically, Salesforce creates a flow in result of a process to handle the scheduled task. Notice, the 'Workflow' type in the Flow created, different from the usual 'Flow' or 'Autolaunched Flow'



To do a test, we create a new Account and mark it as Hot








Wait for an hour and voila! I receive an email alerting me that a new Hot Account is created.



See the time, exactly and hour after I created the Account. :)

We can do the same by updating an exisitng account and marking the same as Hot. This will work as we selected the 'Do you want to execute the actions only when specified changes are made to the record?'

Next Post: We do a field update in a process.

Thursday, March 12, 2015

, , ,

Using the New Process Builder - Part 2 - How to version control an existing Process in Process Builder.


This is part of the series - Using the New Process Builder

Today, we learn how to update existing Processes. 
Salesforce has brought in version controlling with Processes.
It is similar to what we do with say word documents, we create initial versions and then with each change/update we increase the version of the document.

Similarly, when you have already created a process and then want to make some changes/amendments to the same process, you end up creating a new version and activate this new version.

Let us see with the simple process we created earlier.
Let's say I want to update the Task subject we had set earlier from 'Please work on the JA' to 'Please work on the Job Application'.

First, let's see what we already have in the Process Builder, from the All processes view, click on 'Manage Versions' for the process 'Assign Task on Job Application creation' we created.



You see all the existing versions you have and the only one which is currently active. Obvious enough, only one version at a time can be active.


This is the Version History, Salesforce maintains a version history for all processes you create and if you want to delete any old versions the same can be done from here.

Now, we update our process. 

Step 1: Click on the process name and you see the process as we had completed it last time.



Step 2: Click on the Clone button on top right of the process.

Step 3: We should choose the option 'Version of current process' as we want to update the same process and generate a new version of it.
In other cases, you can choose to create a new process cloning the existing one and append additional steps to the new process.
Here, we update the existing one.



Step 4: Fill in the Name of the process same as before and a description and click Save. 

Step 5: Now you have the same process in editable mode and can edit what you want in the process. I choose the Immediate action of creating the task.

Step 6: I update the subject in the Task fields for record creation


Step 7: Important! This is important - You need to activate the new version for it to be working. 
A warning tells you that other active versions will now be deactivated and the new version works as the current active process. This is exactly what you want so click Ok and proceed.


The new version will become the latest active version and the others remain as Inactive versions.


Now, I create a new Job Application and see the Task created for it. 
Voila! The new Task reflects the new changed Subject.


So, now we know how to Version control a Process and work with its latest version.

Next Post: We learn how to use the Scheduled Task in a process and create some time-based processes.

Sunday, March 8, 2015

, ,

Using the New Process Builder - Part 1 - Creating New Record for business flow


This is part of the series - Using the New Process Builder
Today, let us talk about the all new Process Builder in place from Spring '15.
In all our applications, we implement series of business processes. We involve a combination of Triggers, Flows and Workflows to get to achieve our desired flow of processes.
In implementing these, Salesforce has given us a new improved utility to make use of - The Process Builder. What it is gives us:
  • Create your processes using a convenient visual layout with point-and-click efficiency.
  • Create your whole process in one place rather than using multiple workflow rules.
  • Create processes by collaborating with different teams in your business.
  • Stop using Apex code to automate simple tasks.
And what we lack in workflows comes with Process Builder - 
  • Creating a record
  • Submitting a record for approval
Today, I use a very simple example to demonstrate the first use of Process Builder, creating a record on a specific event.
I have a custom object representing Job Application. Job Applications get created by an Admin team and assigned accordingly to various recruiters. My requirement is:

  1. When a Job Application gets created,  I want to assign a Task to the owner to manage the Job application assigned to him.

Now, let us begin creating the process Step by Step
Step 1: Create a new Process under Create -> Workflow & Approvals -> Process Builder

Step 2: Add the Object on which you want to run your process - here Job Application object

Step 3: Now we add the immediate action, i.e creating a Task assigning to the JA owner. Choose Record create as the Action Type, name your Action and choose Task as the Object.


We assign values to the fields of the Task object. 
I assign the Assigned to as the Owner ID and the Related To to the Job Application ID. 
The required fields of an object automatically comes and are non editable. For me the Priority and Status field are required for Task object. Hence, these fields come automatically when Record Create is chosen.

Step 4: All done lets Activate the Process and see it work.

Now we create a Job Application object. And assign the same to me.

As I am the owner a task gets created and assigned to me.


Done. A simple process is used to create a new record on creation of another related record. This is one of the most important feature of the Process Builder, and one we cannot do through a workflow.
Next Post: We learn how to version control (in other words) update an existing Process.

Wednesday, February 25, 2015

, , ,

Code and Visualforce Optimization using Custom Component and Abstraction


This post aims at minimizing Apex code and reusing visual force construct using custom components.

Let us start with a straight forward implementation, forgetting about any optimization. My requirement is I want to display an Account's Annual revenue only on a button click via a custom page.
I want do the same on the Contact page as well, here showing the associated Account's annual revenue.
I create two separate visualforce pages and two separate apex controllers to support them.
I added the visual force pages as inline to the Account layout and the Contact layout respectively.
Page 1: GetAccountAnnualRevenue
<apex:page standardController="Account" extensions="AnnualRevenueController">
<apex:form >
    <apex:pageBlock >
        <apex:pageBlockButtons location="top">
            <apex:commandButton value="Click Here" action="{!fetchAnnualRevenue}"/><br/><br/>
            <apex:outputText value="Annual Revenue   " />
            <apex:outputText value="{!annualRevenue}" label="Annual Revenue"></apex:outputText><br/>
        </apex:pageBlockButtons>
    </apex:pageBlock>
</apex:form>
</apex:page>


Controller 1: AnnualRevenueController 
public with sharing class AnnualRevenueController {

  public String annualRevenue {get;set;}
  public Id objectId {get;set;}
  public Account account {get;set;}
  
  public AnnualRevenueController(ApexPages.StandardController stdController){
    account = [SELECT Id,Name,AnnualRevenue
                  FROM Account WHERE Id =: ApexPages.currentPage().getParameters().get('id')];
  }
  
  public void fetchAnnualRevenue(){
    annualRevenue = account.AnnualRevenue.toPlainString();
  }
}

Page 2: GetContactAnnualRevenue
<apex:page standardController="Contact" extensions="ContactAnnualRevenueController">
<apex:form >
    <apex:pageBlock >
        <apex:pageBlockButtons location="top">
            <apex:commandButton value="Click Here" action="{!fetchAnnualRevenue}"/><br/><br/>
            <apex:outputText value="Annual Revenue   " />
            <apex:outputText value="{!annualRevenue}" label="Annual Revenue"></apex:outputText><br/>
        </apex:pageBlockButtons>
    </apex:pageBlock>
</apex:form>
</apex:page>

Controller 1: ContactAnnualRevenueController
public with sharing class ContactAnnualRevenueController {
  public String annualRevenue {get;set;}
  public Id objectId {get;set;}
  public Contact contact {get;set;}
  
  public ContactAnnualRevenueController(ApexPages.StandardController stdController){
    
    contact = [SELECT Id,Name,Account.AnnualRevenue
                  FROM Contact WHERE Id =: ApexPages.currentPage().getParameters().get('id')];
  }
  
  public void fetchAnnualRevenue(){
    annualRevenue = contact.Account.AnnualRevenue.toPlainString();
  }
}

The Account Page shows the Annual revenue something like this:
And on click


Now, we are doing the same thing for two different pages, the only difference is the way we pull in the Annual Revenue data. Once from an Account record and the second time from a Contact record.
We display the exact same thing, but have ended up putting in the same lines for the two different pages. If we tomorrow decide to show the same for an Opportunity record, we will mimic all the lines of code in another page. Unnecessary.
Now, let us collate these together for a more precise solution.
First, what we show in both the pages, and perhaps multiple other pages in future, come into a component as below:
<apex:component >
<apex:attribute name="revenueController" description="This is the Controller" type="AnnualRevenueController" required="true"/>
<apex:form >
    <apex:pageBlock >
        <apex:pageBlockButtons location="top">
            <apex:commandButton value="Click Here" action="{!revenueController.fetchAnnualRevenue}"/><br/><br/>
            <apex:outputText value="Annual Revenue   " />
            <apex:outputText value="{!revenueController.annualRevenue}" label="Annual Revenue"></apex:outputText><br/>
        </apex:pageBlockButtons>
    </apex:pageBlock>
</apex:form>
</apex:component>

Now, the Visualforce page can be concised to as :
<apex:page standardController="Account" extensions="AnnualRevenueController">
<c:commonannualrevenuedisplay revenueController="{!this}"></c:commonannualrevenuedisplay>
</apex:page>

Ok, let's see what we did. The Visualforce component does all the work. From the page we just assign the instance of the AnnualRevenueController class to the revenueController attribute of the component. Using the revenueController the component can fetch and display the revenue.
The actual Annual Revenue is fetched from database by the AnnualRevenueController.
To make this work, add the below lines to your AnnualRevenueController 
//returns the instance of this class
    public AnnualRevenueController getThis(){
      return this;
    }

This above snippet of code, basically, returns the current instance of the AnnualRevenueController class to the visualforce page and in turn the component.

So, we slimmed down the visualforce page. However, we still have a problem here.

We arrived at a station but not our destination. What happens to our Contact page? I have passed the Account related controller to the component, and hence it would work only for the Account page.
Here, we do another trick - one of the strongest tool OOPs provides - "ABSTRACTION"
We create an abstract class to fetch and initialize the Annual revenue from the page. See below:
public abstract class AbstractAnnualRevenueController{
    abstract String getAnnualRevenue();
    public String annualRevenue{get;set;};
    
    public void fetchAnnualRevenue(){
        annualRevenue = getAnnualRevenue();
    }
}

Now, abstract class is a class which cannot be initialized. Simply put, this class is incomplete. We have declared a method getAnnualRevenue, but never defined what is does.
Here,is where, abstraction starts, as we mark this method as abtract, we need not define it, but we enforce other classes (child) who extend this class to define this method.
The individual classes can decide what to do inside and what final value to return. 
So, the trick is let the page get the details from the same attribute/methos. Internally, you channel your method to fetch the actual relevant data.

Now,let us see what changed in our controller classes


public with sharing class AnnualRevenueController extends AbstractAnnualRevenueController{

   public Account account {get;set;}
        
    public AnnualRevenueController(ApexPages.StandardController stdController){
        account = [SELECT Id,Name,AnnualRevenue
                                  FROM Account WHERE Id =: ApexPages.currentPage().getParameters().get('id')];
    }            
    //returns the instance of this class
    public AnnualRevenueController getThis(){
      return this;
    }
    
    public String getAnnualRevenue(){
        return account.AnnualRevenue.toPlainString();
    }
}



public with sharing class ContactAnnualRevenueController  extends AbstractAnnualRevenueController {
    
    public Contact contact {get;set;}    
    public ContactAnnualRevenueController(ApexPages.StandardController stdController){
        
        contact = [SELECT Id,Name,Account.AnnualRevenue
                                  FROM Contact WHERE Id =: ApexPages.currentPage().getParameters().get('id')];
    }    
    //returns the instance of this class
    public ContactAnnualRevenueController getThis(){
      return this;
    }
    
    public String getAnnualRevenue(){
        return contact.Account.AnnualRevenue.toPlainString();
    }
}

As simple as that, and now we make a very small change in our component


<apex:attribute name="revenueController" description="This is the Controller" type="AbstractAnnualRevenueController" required="true"/>

Our contact page can also be concise now:


<apex:page standardController="Contact" extensions="ContactAnnualRevenueController">
<c:commonannualrevenuedisplay revenueController="{!this}"></c:commonannualrevenuedisplay>
</apex:page>

And that completes our goal. Our Account and Contact page successfully renders the Annual Revenue : 


We reused a single component and an abstract class to create a custom page displaying the Annual revenue for an Account, a Contact and in future can plug in any object to show its Annual revenue. 
All we need to do is extend the class AbstractAnnualRevenueController and override the method getAnnualRevenue. Voila!!
Try doing this for an Opportunity record. It will be a cake walk.

Hope all of you enjoyed this, do post your comments and any queries.


Saturday, February 14, 2015

, , ,

Using Hierarchical Custom Setting to your Advantage



Salesforce gives you many a construct to make your life easier and present your users a very customized and relevant UI to work on.
To begin my Salesforce post, I will start with such a one - Hierarchical Custom Settings.
The Salesforce Hierarchical custom settings gives you a way to use the Salesforce profile and user variations to make your UI more relevant and useful to a particular user or type of user.
To demonstrate the power of Hierarchical custom settings, let us take up a very simple and basic example.
I have a custom object called Survey in my org. The object records are visible to all users in the org, however, I want only a specific type of user - Recruiters to be able to delete any irrelevant survey.
Even as a recruiter deletes a survey, I have a set of additional logic running behind the screen to do so.
I override my standard Survey detail page with a Visualforce page to begin with, as I want some additional UI customizarions.
To begin with, I remove the standard delete button from my Survey page layout.

I just add the below to my Visualforce page, to display the basic fields on the Survey Detail Page

<apex:page standardController="Survey__c">
  <apex:detail relatedList="false" />
</apex:page>

Now, as any type of user I will only see the 'Edit' and 'Submit for Approval' buttons on the Survey detail page

Now, we go ahead and create a new Hierarchical Custom Setting to be used to make my Visualforce page display a Delete button only for a Recruiter.

We create a new Checkbox field in the Custom settings

With 'Manage' we set the 'canDeleteSurveyRecords' value to true/checked only for a Recruiter

Now, a very important aspect of Hierarchical Settings.Your next thought may come to the point, that your org or any org may have several Profiles and hundreds of users to work with.
Do we set the fields of a custom settings for all the profiles and users ?!
The Answer is NO.
We use the Default Organization Level Value of a Hierarchical custom setting.


In our case we want it to be false for any one else so we leave the check box unchecked.

Now we change our visualforce page, I have added an additonal text just to print the value of the settings for convenience
<apex:page standardController="Survey__c">
  <apex:detail relatedList="false" />
  <apex:form >
  <apex:pageBlock>
  <apex:pageBlockSection >
      <apex:outputText label="Value on Hierarchy Setting" value="{!$Setup.UserAccessSettings__c.canDeleteSurveyRecords__c}"/>
      <apex:commandButton value="Delete" rendered="{!$Setup.UserAccessSettings__c.canDeleteSurveyRecords__c}"/>
  </apex:pageBlockSection>
  </apex:pageBlock>
  </apex:form>
</apex:page>
This does nothing, just makes the commnadbutton 'Delete' rendered based on the value of the Hierarchical custom setting - canDeleteSurveyRecords field.
Now, we login as System admin the page will look like
However, as a Recruiter, I see

So, with intelligent use of Hierarchical Custom settings, you can customize your visualforce pages, with no additional lines of logic in your page.
Similarly, you can branch out your Apex code and and its flow based on a User or a User profile using the very effective Hierarchical Custom settings.

Hope this helps you to design more intelligent and manageable pages and classes.

In my next post, I will be talking about how to make your email templates display your desired information.

Popular Posts

Powered by Blogger.