Aug 11

Step by Step Guide for using Salesforce Command Line Interface Data Loader

In this article, I will cover a step by step guide for using Salesforce Command Line Interface Data Loader:

So first of all what is Command Line Interface Data Loader, and why should we use it from Command Line when there is already a visual user interface provided? Well, the answer is:

  • You can automate the data loading process using the CLI data loader. Suppose you want some data from your org to be extracted every night.
  • You may want a CSV file which is kept at a particular location on a machine, to be uploaded into an org at a fixed time.

 

NOTE: The CLI data loader functionality is only available for Windows Operating System. 

 

Prerequisites for using CLI Data Loader:

  1. First of all, make sure you have the Admin rights on the machine.
  2. Then, download the data loader setup file on the machine by going into Salesforce environment Setup –> Data Management –> Data Loader. Choose the option “Download Data Loader for Windows“. It will download the latest version of data loader setup file. Install it by following the setup wizard.
  3. Also, you need to have the required version of JRE (Java Run Time Environment). Go to step a and b only if you don’t have the JRE already on the system, otherwise only check step c.
    1.  Download the latest JRE version from Oracle site downloads page.
    2. Install the downloaded JRE file.
    3. Setup the environment variables and then check the installation by following the steps given by Mr. Arun on this link: Setup Environment Variables Java

 

Before going to further steps:

  • Validate your data loader directory looks like below screenshot.
  • Make sure that you validate the JRE installation and version by hitting Java -version command on your Command Prompt Window as shown in the screenshot below.
data loader directory structure

data loader directory structure

 

Check JRE Installation and Path

 

All Looks good? Yes? Okay, let’s start step by step on how to use Salesforce CLI Data Loader now:

Step 1: Prepare your key for encrypting the password of your org:

  • Open the command prompt window, and navigate to the bin folder of data loader directory (steps shown in screenshot below). We need to navigate to bin location, because encrypt.bat and process.bat both files exists here at this location.
  • Use the command encrypt.bat -g anyKeyTextOfYourChoiceYou can use any text you want to use as an Encryption key. In my screenshot, I have used myKey, but you can use any of your choice.
  • Once you will hit enter, it will show you the encrypted version of your text. Copy that value (highlighted in screenshot), and save it in a .txt file and save the file at some proper location on your machine. It will be used in further steps. I have saved mine at “D:\data loader files\encryptionKey.txt” location.
Prepare the encryption key

Prepare the encryption key

 

Step 2: Encrypt your Salesforce environment password by using the encrypted key prepared above.

  • On the same Command Prompt Window, use the command encrypt.bat -e <yourOrgPassword> <location of the txt file with encrypted key>
  • Our command was like: encrypt.bat -e nitish$0413 “D:\data loader files\encryptionKey.txt”. 
  • Copy the encrypted value of your password, we will use in further steps when creating process-conf.XML file.

 

Step 3: Create the mapping file and save it in .sdl format

  • You need to create the mapping file by putting the CSV file header name on left side and Salesforce API name on right side.
  • Save the file in .sdl format on your machine.
  • If there are multiple fields and complex mapping, you can also take help of Dataloader field mapping UI, and save the mapping file from there instead of creating everything manually.

Note: Make sure that API Name of fields should be exactly as in Salesforce. The API names are on the right-hand side of the = character in the mapping file. Capital and Small letters also make a difference and you may face issues. 


#Mapping values created by Nitish on 11th Aug
NAME=Name
INDUSTRY=Industry
NUMBEROFEMPLOYEES=NumberOfEmployees

Here is the screenshot of our CSV file which we are gonna upload:

CSV File Screenshot

CSV File Screenshot

 

Step 4: Prepare the process-conf.XML file according to our requirements

  • One sample process-conf.XML file has already been given in the data-loader directory Samples folder.
  • Copy that sample file and paste in the location at D drive, where we stored the encryptionKey.txt file as well.
  • Keep all other files like mapping file, data files at the same location, which is “D:\data loader files”.
  • Edit the process-conf.XML file now and change the contents of the file as given below. This file contains the information and specifications about all the operations you need to perform in your data load. For ex:
    • What is the action you want to perform (insert, update, export etc.)
    • Your username
    • Password (encrypted)
    • Instance Name
    • Success error file location
    • Few others as shown in the text.

 


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
 <bean id="accountInsert" class="com.salesforce.dataloader.process.ProcessRunner" singleton="false">
 <description>accountInsert job gets the account record from the CSV file and inserts it into Salesforce.</description>
 <property name="name" value="accountInsert"/>
 <property name="configOverrideMap">

<map>
  <entry key="sfdc.debugMessages" value="true"/>
  <entry key="sfdc.debugMessagesFile" value="D:\accountInsertSoapTrace.log"/>
  <entry key="sfdc.endpoint" value="https://login.salesforce.com"/>
  <entry key="sfdc.username" value="yourusername@gmail.com"/>
  <entry key="sfdc.password" value="148d6c06b76cdc88415aa34c673e00ed3423a42"/>
  <entry key="process.encryptionKeyFile" value="D:\data loader files\encryptionKey.txt"/>
  <entry key="sfdc.timeoutSecs" value="600"/>
  <entry key="sfdc.loadBatchSize" value="200"/>
  <entry key="sfdc.entity" value="Account"/>
  <entry key="process.operation" value="insert"/>
  <entry key="process.mappingFile" value="D:\data loader files\accountInsertMap.sdl"/>
  <entry key="dataAccess.name" value="D:\data loader files\accountDataToInsert.csv"/>
  <entry key="process.outputSuccess" value="D:\data loader files\accountDataToInsertSuccess.csv"/>
  <entry key="process.outputError" value="D:\data loader files\accountDataToInsertLogError.csv"/>
  <entry key="dataAccess.type" value="csvRead"/>
  <entry key="process.initialLastRunDate" value="2017-08-10T00:00:00.000-0800"/>
</map>

</property>
</bean>
</beans>

Step 4: Hit the final command, and run the process for data load.

  • You need to be at the same location as shown in above CMD screenshots, because process.bat file resides there only.
  • Hit the command process.bat “<file path to process-conf.xml>” <process name>
  • Replace <file path to process-conf.xml> with the path to the directory containing process-conf.xml & replace <process name> with the process specified in process-conf.xml.
  • In our case, it was process.bat “D:\data loader files” accountInsert. accountInsert is the process name mentioned in the process-conf.XML file.
  • process-conf.XML file can contain many processes and you need to run one at a time by using the above command.

Once the process is run, the success and error CSV files will be available at the location you mentioned in process-conf.XML file.

Now you have learned how to use data loader CLI version to load the data, you can also schedule mutiple operations by creating a .bat file and write multiple commands to execute multiple processes one by one. You can get more information about creating .bat file in the PDF at the link below. Check out the last paragraph on P.6 of the cheatsheet for an example:

Note: You should also take a look at the process-conf.XML file provided in samples folder of data loader default directory, to understand some other tags needed to upsert, export operations of data loading.

Related Posts:

Data Loader Command Line Introduction

Command line dataloader in Salesforce – Solve Common errors

Good luck for data loading! :)

Permanent link to this article: http://www.sfdcpoint.com/salesforce/step-by-step-guide-for-using-salesforce-command-line-interface-data-loader/

Jul 23

Salesforce Web to lead form with reCAPTCHA

Salesforce has already provided a cool out of the box feature where you can generate HTML for the web to lead form, which you can simply put on your website. It is very simple to generate the customized form with fields of your choice to generate leads for your company through various company portals or social platforms.

But till some time back, there was no spam check and anyone with wrong intentions could create junk lead data repeatedly in your company’s org, just by writing some small script. Because of this, it becomes a concern for the organization to keep a track of the Lead data quality on a large scale which results in more resources wasted in correcting data quality.

Spring ’17 release enhancement introduced a feature, which allows you to enable spam filtering with reCAPTCHA from Google. This option is available in out of the box feature itself on the same screen as previous.

But before generating the form (web-to-lead HTML) now, you need to register for an API Key pair with Google for your domain.

Steps to register for an API key with Google Account:

  1. Login to your company’s google account or for your practice you can log in to your own personal Google account.
  2. Once logged in, Register here for the reCaptcha with google.
  3. Don’t forget to give your domain name, otherwise, your captcha box won’t work and will give you an error. The domain name has to be of that site, which will host your web-to-lead form. In my case, I have shown SFDCPOINT.COM, just for example. My demo domain is different, so I registered that as well later.
  4. Click on Register after checking the checkbox “Accept the reCAPTCHA Terms of Service“.
  5. You will get the Site Key and Secret Key on the next page, just copy and save it somewhere.

Register for ReCaptcha API Key with Google.

NOTE: After clicking on register, and before testing your web to lead form on your website, there should be a gap of at least 30 minutes, as Google can take some time to activate the key for your domain. 

Now you are done with the registration of API Key Pair with Google. Now Let’s use the same site key and secret key in Salesforce in order to get Salesforce Web to lead form with reCAPTCHA feature.

Steps to configure the Site Key and Site Secret in Salesforce:

  1. Go to Setup –> Leads –> Web-to-lead.
  2. Click on “Create Web-to-Lead Form” button.
  3. Select the fields you want on the form, give the redirect URL, and come down to enter the API Key Pair information.
  4. Select “Enable Spam Filtering” checkbox to see the API key information section.
  5. Clik on the Lookup icon to enter the info about the site key and secret.
  6. In the popup window, Click New Button.
  7. Enter the name of your choice, and Site Key and Secret Key, Click Save. If you encounter “Insufficient Privileges” error while saving the key information, then follow the instructions in Step 8 else go to Step 9.
  8. Go to Setup -> user interface. Make sure to select the checkbox “Show Quick Create”. Click on Save button at the bottom. Repeat the Steps 5 – 7 again.
  9. Click on Generate button, and you have the HTML code ready to put on your company’s website and start capturing leads from there.

 

Create API Key Pair In Salesforce

Create API Key Pair In Salesforce

 

 

 

lookup screen

lookup screen

 

Enter key and secret

Enter key and secret

 

If you see the error “Insufficient Privileges”, then follow the screenshot below:

 

Show Quick Create

Show Quick Create

 

HTML Code generated with a few modifications, which are explained below after the code:

 


<!-- ---------------------------------------------------------------------- -->
<!-- NOTE: Please add the following <META> element to your page <HEAD>. -->
<!-- If necessary, please modify the charset parameter to specify the -->
<!-- character set of your HTML page. -->
<!-- ---------------------------------------------------------------------- -->

<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<script src="https://www.google.com/recaptcha/api.js"></script>
<script>
function timestamp() {
var response = document.getElementById("g-recaptcha-response");
if (response == null || response.value.trim() == "")
{
var elems = JSON.parse(document.getElementsByName("captcha_settings")[0].value);
elems["ts"] = JSON.stringify(new Date().getTime());
document.getElementsByName("captcha_settings")[0].value = JSON.stringify(elems);
}
}
setInterval(timestamp, 500);
</script>
<script type="text/javascript">
function recaptcha_callback(){
$('.button').prop("disabled", false);
}
</script>
<!-- ---------------------------------------------------------------------- -->
<!-- NOTE: Please add the following <FORM> element to your page. -->
<!-- ---------------------------------------------------------------------- -->

<form action="https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">

<input type="hidden" name='captcha_settings' value='{"keyname":"SecondCaptcha","fallback":"true","orgId":"00DQ000000Ee123","ts":""}'/>
<input type="hidden" name="oid" value="00DQ000000Ee123"/>
<input type="hidden" name="retURL" value="http://www.sfdcpoint.com"/>

<!-- ---------------------------------------------------------------------- -->
<!-- NOTE: These fields are optional debugging elements. Please uncomment -->
<!-- these lines if you wish to test in debug mode. -->
<!-- <input type="hidden" name="debug" value=1> -->
<!-- <input type="hidden" name="debugEmail" value="nitish@test.com"> -->
<!-- ---------------------------------------------------------------------- -->

<label for="first_name">First Name</label><input id="first_name" maxlength="40" name="first_name" size="20" type="text" required="true"/><br/>

<label for="last_name">Last Name</label><input id="last_name" maxlength="80" name="last_name" size="20" type="text" required="true"/><br/>

<label for="email">Email</label><input id="email" maxlength="80" name="email" size="20" type="text" required="true"/><br/>

<label for="phone">Phone</label><input id="phone" maxlength="40" name="phone" size="20" type="text" /><br/>

<label for="company">Company</label><input id="company" maxlength="40" name="company" size="20" type="text" required="true"/><br/>

<div class="g-recaptcha" data-sitekey="6LfU8ikUAAAAALP-P1aIMcWaEGqXOpsd6mhL1234" data-callback="recaptcha_callback"></div><br/>

<input type="submit" name="submit" id="submitBtn" disabled="true" class="button"/>

</form>

 

In the above code, I have made small modifications, just to make sure that form doesn’t submit until somebody has verified captcha by clicking “I am not a robot”.

I have set the disabled=”true” on submit button. I have used a data-callback attribute of div in which reCaptcha is there, and called a javascript method recaptcha_callback. This javascript method will be called if a user has verified himself, and in this method, I have enabled the submit button by removing the disabled attribute.

Click Here For Demo

Code for the demo link:


<apex:page>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8"/>

<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<script src="https://www.google.com/recaptcha/api.js"></script>
<script>
function timestamp() {
var response = document.getElementById("g-recaptcha-response");
if (response == null || response.value.trim() == "")
{
var elems = JSON.parse(document.getElementsByName("captcha_settings")[0].value);
elems["ts"] = JSON.stringify(new Date().getTime());
document.getElementsByName("captcha_settings")[0].value = JSON.stringify(elems);
}
}
setInterval(timestamp, 500);
</script>
<script type="text/javascript">
function recaptcha_callback(){
alert("callback working");
$('.button').prop("disabled", false);
}
</script>
<!-- ---------------------------------------------------------------------- -->
<!-- NOTE: Please add the following <FORM> element to your page. -->
<!-- ---------------------------------------------------------------------- -->

<form action="https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">
<input type="hidden" name='captcha_settings' value='{"keyname":"SecondCaptcha","fallback":"true","orgId":"00D90000000r123","ts":""}'/>
<input type="hidden" name="oid" value="00D90000000r123"/>
<input type="hidden" name="retURL" value="http://www.sfdcpoint.com"/>
<label for="first_name">First Name</label><input id="first_name" maxlength="40" name="first_name" size="20" type="text" required="true"/><br/>

<label for="last_name">Last Name</label><input id="last_name" maxlength="80" name="last_name" size="20" type="text" required="true"/><br/>

<label for="email">Email</label><input id="email" maxlength="80" name="email" size="20" type="text" required="true"/><br/>
<div class="g-recaptcha" data-sitekey="6LfU8ikUAAAAALP-P1aIMcWaEGqXOpsd6mhLe123" data-callback="recaptcha_callback"></div><br/>

<input type="submit" name="submit" id="submitBtn" disabled="true" class="button"/>
</form>
</apex:page>

Try generating the form, and comment if you face any issue in using the web-to-lead form on your website.

NOTE: For security purpose, I have changed the org Id and Secret Key in both the code samples above.

 

Happy coding!! :)

 

Permanent link to this article: http://www.sfdcpoint.com/salesforce/salesforce-web-to-lead-form-with-recaptcha/

May 24

OpportunityAccessLevel field not writable in Apex

Recently, I was working on an assignment, where I had to create Opportunity Team Member records in a trigger on some user action. But when I started to write the code and tried saving it, I got the error “Field is not writeable: OpportunityTeamMember.OpportunityAccessLevel“. Uuuhhh.. I got stuck! According to the documentation this field is writable, then why not for me. Below is the code that I tried, and came to know, that OpportunityAccessLevel field not writable in Apex.

 


OpportunityTeamMember OppTeamMember = new OpportunityTeamMember();
OppTeamMember.UserId = UserInfo.getUserId();
OppTeamMember.OpportunityId = opp.Id;
OppTeamMember.OpportunityAccessLevel='Edit';
OppTeamMember.TeamMemberRole = 'Opportunity Team Member';

The above code gave me the error:

Field is not writeable: OpportunityTeamMember.OpportunityAccessLevel

 

This error doesn’t occur when you create opportunity team member by going to the related list on the opportunity. You can give any access level from there.

Opportunity Team Member Manually

Adding Opportunity Team Member manually

 

After spending some time, I figured out and came to know, that: If Opportunity sharing settings are set to Private, then you can not write this field OpportunityAccessLevel. This field will be set to Read Only by default.

 

But my requirement was to give “Edit” access to opportunity team member while creating. So the workaround I opted was:

  1. Create opportunity team member record first, Salesforce will give READ permission by default.
  2. Then, give edit access using the OpportunityShare object because as soon as you create an OpportunityTeamMember record, Salesforce creates an entry in sharing records for that opportunity and user.
  3. Query the sharing record that Salesforce created just now, and update the access level to EDIT.

 

Here is the code:


OpportunityTeamMember OppTeamMember = new OpportunityTeamMember();
OppTeamMember.UserId = UserInfo.getUserId();
OppTeamMember.OpportunityId = opp.Id;
//OppTeamMember.OpportunityAccessLevel='Edit';
OppTeamMember.TeamMemberRole = 'Opportunity Team Member';
insert OppTeamMember;

//get all of the sharing records which Salesforce created right now. rowCause is Sales Team.
List<OpportunityShare> oppShareRecords = [select Id, OpportunityAccessLevel, RowCause from OpportunityShare where OpportunityId =: opp.Id and RowCause = 'Sales Team'];

// set all team members access to read/write
for (OpportunityShare OppShare : oppShareRecords){
OppShare.OpportunityAccessLevel = 'Edit';

}

update oppShareRecords;

 

Good Luck and Happy Coding !!

 

 

 

Permanent link to this article: http://www.sfdcpoint.com/salesforce/opportunityaccesslevel-field-not-writable-in-apex/

May 02

Pagination using StandardSetController with wrapper class

Pagination using StandardSetController with wrapper class without losing data during pagination

Click here for Demo – Pagination with wrapper

Very often, we need pagination in our visualforce page, and we use wrapper class to show the records with checkboxes in pageBlockTable. We use the standardSetController for pagination, display checkboxes for each record BUT while paginating, the value of the selected checkboxes are gone, it doesn’t retain during paginating.

This is because the list that we use to display records on page, is getting refreshed everytime, and not retaining the values. So, to hold or retain the values during pagination, we will see how we can use Pagination using StandardSetController with wrapper class without losing data during pagination/ paginating (moving next or previous).

 

PaginationImage,Pagination using StandardSetController with wrapper class

Contact pagination – Wrapper Class – With checkboxes

 

So, to overcome this, we will not use the direct list from standardSetController, instead of that we’ll use a map to show the values on page. This map will always contain the correct values of checkboxes (for retaining purpose), we will use map on visualforce page pageBlockTable. Whenever user clicks on next or previous, first or last, we store the copy of records with checkbox value in map, so that even if the wrapper list is refreshed with next set of records, we can store the checkbox values for previous set of records.

Below is the code snippet you can look at, and we have a demo link as well, just to give you an idea how it works.

Visualforce Page Code:


<apex:page controller="contactPaginationController" docType="html-5.0" tabStyle="Contact">
   <apex:sectionHeader title="Contact" subtitle="Contact Pagination" />
    <apex:form id="theForm">
      <apex:pageBlock title="All Contacts" rendered="{!wrapperRecordList.size!=0}" id="pbId" >
        <apex:pageBlockTable value="{!wrapperRecordList}" var="cont">
           <apex:column headerValue="Select">
             <apex:inputCheckbox value="{!cont.isSelected}"/>
           </apex:column>
           <apex:column headerValue="Name">
             <apex:outputField value="{!cont.contactRecord.name}"/>
           </apex:column>
           <apex:column headerValue="Email">
             <apex:outputField value="{!cont.contactRecord.Email}"/>
           </apex:column>
           <apex:column headerValue="Phone">
            <apex:outputField value="{!cont.contactRecord.Phone}"/>
           </apex:column>
       </apex:pageBlockTable>

 <!-- Action Buttons visible on bottom of page for pagination -->
       <apex:outputPanel style="text-align:center;" layout="block">
          <apex:commandButton value="First" reRender="pbId" action="{!first}" disabled="{!NOT(hasPrevious)}" status="paginationStatus"/>
          <apex:commandButton value="Previous" rerender="pbId" action="{!previous}" disabled="{!NOT(hasPrevious)}" status="paginationStatus"/>&nbsp;Page {!pageNumber} of {!totalPages}&nbsp;
          <apex:commandButton value="Next" rerender="pbId" action="{!next}" disabled="{!NOT(hasNext)}" status="paginationStatus"/>
          <apex:commandButton value="Last" rerender="pbId" action="{!last}" disabled="{!NOT(hasNext)}" status="paginationStatus"/>
          <apex:actionStatus id="paginationStatus">
             <apex:facet name="start">
                 Please wait...<img src="/img/loading32.gif" style="width: 18px;"/>
             </apex:facet>
          </apex:actionStatus>
       </apex:outputPanel>
 </apex:pageBlock>
 </apex:form>
</apex:page>

Apex Controller:


public class contactPaginationController{

 //variable used in page.
 Public Integer size{get;set;}
 Public Integer noOfRecords{get; set;}
 public List<SelectOption> paginationSizeOptions{get;set;}
 public static final Integer QUERY_LIMIT = 10000;
 public static final Integer PAGE_SIZE = 5;

 public List <WrapperClass> wrapperRecordList{get;set;}
 Map<Id, WrapperClass> mapHoldingSelectedRecords{get;set;}

 //constructor calling init method.
 public contactPaginationController(){
   mapHoldingSelectedRecords = new Map<Id, WrapperClass>();
   init();

 }

//Init method which queries the records from standard set controller.
 public void init() {
 wrapperRecordList = new List<WrapperClass>();
 for (Contact cont : (List<Contact>)setCon.getRecords()) {
 if(mapHoldingSelectedRecords != null && mapHoldingSelectedRecords.containsKey(cont.id)){
 wrapperRecordList.add(mapHoldingSelectedRecords.get(cont.id));

 }
 else{
   wrapperRecordList.add(new WrapperClass(cont, false));
 }
 }
 }

 /** Instantiate the StandardSetController from a query locater*/
 public ApexPages.StandardSetController setCon {
 get {
 if(setCon == null) {
   setCon = new ApexPages.StandardSetController(Database.getQueryLocator([SELECT Id,Name, Email, Phone FROM Contact LIMIT : QUERY_LIMIT ]));

   // sets the number of records to show in each page view
   setCon.setPageSize(PAGE_SIZE);
 }
   return setCon;
 }
 set;
 }

 /** indicates whether there are more records after the current page set.*/
 public Boolean hasNext {
 get {
   return setCon.getHasNext();
 }
 set;
 }

 /** indicates whether there are more records before the current page set.*/
 public Boolean hasPrevious {
 get {
   return setCon.getHasPrevious();
 }
 set;
 }

 /** returns the page number of the current page set*/
 public Integer pageNumber {
 get {
   return setCon.getPageNumber();
 }
 set;
 }

 /** return total number of pages for page set*/
   Public Integer getTotalPages(){
     Decimal totalSize = setCon.getResultSize();
     Decimal pageSize = setCon.getPageSize();
     Decimal pages = totalSize/pageSize;
     return (Integer)pages.round(System.RoundingMode.CEILING);
 }

 /** returns the first page of the page set*/
 public void first() {
   updateSearchItemsMap();
   setCon.first();
   init();
 }

 /** returns the last page of the page set*/
 public void last() {
   updateSearchItemsMap();
   setCon.last();
   init();
 }

 /** returns the previous page of the page set*/
 public void previous() {
   updateSearchItemsMap();
   setCon.previous();
   init();
 }

 /** returns the next page of the page set*/
 public void next() {
   updateSearchItemsMap();
   setCon.next();
   init();
 }

 //This is the method which manages to remove the deselected records, and keep the records which are selected in map.
 private void updateSearchItemsMap() {
 for(WrapperClass wrp : wrapperRecordList){
  if(wrp.isSelected){
     mapHoldingSelectedRecords.put(wrp.contactRecord.id, wrp);
  }
  if(wrp.isSelected == false && mapHoldingSelectedRecords.containsKey(wrp.contactRecord.id)){
     mapHoldingSelectedRecords.remove(wrp.contactRecord.id);
  }
 }
 }

 //wrapper class being used for checkbox showing.
 public class WrapperClass {
 public Boolean isSelected {get;set;}
 public Contact contactRecord {get;set;}
 public WrapperClass(Contact contactRecord, Boolean isSelected) {
    this.contactRecord = contactRecord;
    this.isSelected = isSelected;
 }
 }

}

Click here for Demo – Pagination with wrapper

This is just an example we are taking here (i.e. display contacts with checkboxes), there can be several other scenarios as well, where you might have some inputField in one column of the pageBlockTable, and you need to retain the values of inputfield during pagination.

There also you can use this concept of Pagination using StandardSetController with wrapper class without losing data during pagination.

This is also very important because this kind of scenario is included for sure in Advanced Developer certification exam exercise, where you have to do pagination with either checkboxes or inputField in pageBlockTable.

Hope this post will give you a better idea about pagination and that too with wrapper classes.

Happy coding !! :)

Permanent link to this article: http://www.sfdcpoint.com/salesforce/pagination-using-standardsetcontroller-with-wrapper-class/

Apr 27

Only Aggregate Expressions Use Field Aliasing – SOQL Error

Only Aggregate Expressions Use Field Aliasing – SOQL Error

Out of many other errors we encounter, this is one with the SOQL. Many times, when writing SOQL, we face the error – “Only Aggregate Expressions Use Field Aliasing” and we are stuck that what’s happening, everything seems all right to me.

You must be missing a comma in the SOQL between two fields before FROM keyword. 

Aliasing is when you want to give some different name to the column in any SOQL result, but you can only do so in aggregate queries of Salesforce, where you use some aggregate functions like sum(Custom_Field__c), count(Id) or any other similar aggregate function.

Below is a valid SOQL using aggregate function with aliasing. We are trying to give “amountTotal” name to Sum(Amount) column.


[Select Sum(Amount) amountTotal, AccountId from Opportunity group by AccountId];

BUT, because aliasing is not allowed in normal SOQL, that is why below SOQL is considered faulty, and will encounter the error “Only Aggregate Expressions Use Field Aliasing”.


[select id, Name Amount from Opportunity];

You might notice we are missing a comma between Name & Amount field, which is why Salesforce thinks that we are trying to give a different label to Name column in SOQL result but that is not allowed.

If you ever encounter this error in future, look for missing comma between two fields in your SOQL.

Happy coding !! :)

Permanent link to this article: http://www.sfdcpoint.com/salesforce/only-aggregate-expressions-use-field-aliasing-soql-error/

Apr 24

TestSetUp method in apex test classes

Test classes – an important part of overall SDLC in Salesforce. We as a developer have to write the test classes very often, and we need to create the test data as well, to have our test class executed successfully. We’ll cover a new concept introduced in salesforce test classes i.e TestSetUp method in apex test classes.

Initially, we had to create test data in every test method, because every test method is considered a different transaction with its own governor limits. But Salesforce has introduced @TestSetUp annotation for test class method. You can write a method in test class, with @TestSetUp annotation applied, and create all your common test data in this method.

Few key points about TestSetUp methods:

  1. Method marked with @TestSetUp annotation executes before any testMethod.
  2. Data created in this method doesn’t need to be created again and again, and it is by default available for all test methods.
  3. There can be only one setup method per test class.
  4. Test setup methods are supported only with the default data isolation mode for a test class. If the test class or a test method has access to organization data by using the @isTest(SeeAllData=true) annotation, test setup methods aren’t supported in this class. 
  5. Test setup methods are available for 24.0 or later versions only.
  6. Every test method will get unchanged version of the test data created in setup method, doesn’t matter if any other test method has modified the data. We will show this in testMethod2 of below example.

 

Below is a sample code, which will show how the test data is available in every test method.


@isTest
private class TestSetupMethodExample {
    //Below is a method with @testsetup annotation, the name can be anything like setup(), oneTimeData(), etc.
    @testSetup static void setup() {
        // Create common test accounts
        List<Account> testAccts = new List<Account>();
        for(Integer i=0;i<2;i++) {
            testAccts.add(new Account(Name = 'TestAcct'+i));
        }
        insert testAccts;
    }

    @isTest static void testMethod1() {
        // Here, we will see if test data created in setup method is available or not, Get the first test account by using a SOQL query
        Account acct = [SELECT Id FROM Account WHERE Name='TestAcct0' LIMIT 1];
        // Modify first account
        acct.Phone = '555-1212';
        // This update is local to this test method only.
        update acct;

        // Delete second account
        Account acct2 = [SELECT Id FROM Account WHERE Name='TestAcct1' LIMIT 1];
        // This deletion is local to this test method only.
        delete acct2;

        // Perform some testing
    }

    @isTest static void testMethod2() {
        // The changes made by testMethod1() are rolled back and
        // are not visible to this test method.
        // Get the first account by using a SOQL query
        Account acct = [SELECT Phone FROM Account WHERE Name='TestAcct0' LIMIT 1];
        // Verify that test account created by test setup method is unaltered.
        System.assertEquals(null, acct.Phone);

        // Get the second account by using a SOQL query
        Account acct2 = [SELECT Id FROM Account WHERE Name='TestAcct1' LIMIT 1];
        // Verify test account created by test setup method is unaltered.
        System.assertNotEquals(null, acct2);

        // Perform some testing
    }

}

Because, test data created is less in number, that’s why rolling back of records at the end of test class takes less time. This actually ends up in improving the performance and reducing the time take to run the test class.

Best of luck with your test class and test data !! Happy coding :)

Permanent link to this article: http://www.sfdcpoint.com/salesforce/testsetup-method-in-apex-test-classes/

Apr 20

Compare old and new field values in trigger

Compare old and new field values in trigger Salesforce

Sometimes we have requirement that our trigger should run only when some field value is changed on a record. So we need to compare value of that field between old version of record and new version of record to make sure that trigger will not run every time when record is changed/edited. For example, we may have requirement that send an email to VP of company or do some task when an opportunity status is changed to Closed Won. So, we have to compare old and new field values in trigger to make it sure.

Salesforce provides Trigger.OldMap where records with older version (last version of record committed in database) are stored in map with key as their Salesforce record Id’s.

Trigger.OldMap = Map<Id, OldVersionOfRecord>();

I have put an example below to show that how can we use Trigger.OldMap and Trigger.New context variables to compare the field values, because the Id of the record is common in both Trigger.OldMap and Trigger.New, that’s why we can use the record Id to get older and newer version of any record from these maps.

Here in this example, trigger compares the account number field’s old value with the new value. That is, trigger checks if the account number was changed.

If the account number is changed the trigger assigns the Type field value as “prospect” else it assigns it a value as “Other“.

trigger Compare_OldandNewvalues on Account (before update) {

//Here we will iterate on trigger.new list, which already holds the new values of all records.
for (Account acc: Trigger.new) {
//Here we use the account id, to get the older version of record.
Account oldAccount = Trigger.oldMap.get(acc.ID);

//once we get the older version, we can get any field's value from older version to compare.
if(acc.AccountNumber != oldAccount.AccountNumber) {

//Here is some logic being performed on a condition basis.
System.debug('--*Account Number is changed*--');
System.debug('**Old Account Number :'+oldAccount.AccountNumber);
System.debug('**New Account Number :'+acc.AccountNumber);
acc.Type = 'Prospect';
}
else{
System.debug('--**Account Number has not been Updated**--');
acc.Type = 'Other';
}
}
}

Also, please note that the Trigger.oldMap is only available in update and delete events and cannot be accessed in insert event triggers. Please take care of this point when writing your trigger code.

Happy coding !! :)

 

Permanent link to this article: http://www.sfdcpoint.com/salesforce/compare-old-and-new-field-values-in-trigger/

Apr 11

Use static resource in Visualforce

Use static resource in Visualforce – Salesforce

Force.com platform provides us a facility to upload, manage and use the content that is static (not changing) for your organization, and it can be stored under “Static Resources“. It can be a Javascript file, CSS file, an image or even a zip file containing all the files required in one zip file. Today we’ll see how to use static resource in Visualforce – Salesforce.

Below are the ways to use the static resources in our visualforce pages:

Suppose there is a single file like any single image or standalone css file, that you need to refer in your VF page, then you can directly use the “$Resource.resourceName” to refer the static resource where ‘$Resource‘ is a global variable to use any static resource within visualforce page. You need not to hard code the path of the static resource in VF page code. Below are some examples of the same.

Suppose you have an image file uploaded in static resource with name “Z_test” as shown below in the screenshot.

Static resource

You can refer this image in your code using expression {$Resource.Z_Test}. Below are some other examples as well:


<!-- An image can be referenced like this -->
<img src="<strong>{!$Resource.Z_Test}</strong>"/>

<!-- Similarly any CSS file can be referenced like below -->
<apex:stylesheet value="<strong>{!$Resource.sampleCss}</strong>"/>

You need to remember only the keyword “$Resource” and the name of the static resource and not the name of actual file.

Similarly when we have the bundle of files, uploaded as a zip file in static resources section, then along with the name of static resource, you have to give the path to the file within the context of the archived zip.

Suppose below is the directory structure of the zip file with the name “bootstrap” that you have uploaded, in which you have three different folders for storing CSS, images, and JS files respectively.

Static resource archive structure
So, to reference a particular file at a given location, you need to give the path of the file in context of the zip, along with the name of static resource while referencing in Visualforce page code. For this we have to use a function called “URLFOR(nameOfStaticResource, RelativePathOfFile)”. Below code snippets shows you how to refer the particular files.


<meta charset="utf-8" />Force.com Developer<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<!-- Static Resources for CSS -->
<apex:styleSheet value="{!URLFOR($Resource.bootstrap, 'bootstrap/css/bootstrap.css')}"/>
<apex:styleSheet value="{!URLFOR($Resource.bootstrap, 'bootstrap/css/bootstrap-responsive.css')}"/>

<!-- Static Resource for individual imag -->
<image src="{!URLFOR($Resource.bootstrap, 'img/glyphicons-halflings.png')}"/>

Above we explained the two ways of using the static resources in visualforce page, one for single file, another for the files within a zipped static resources file.

Hope this will help you in your coding.

Happy coding !! Good Luck :)

Permanent link to this article: http://www.sfdcpoint.com/salesforce/use-static-resource-in-visualforce/

Apr 07

apex:pageMessage and apex:pageMessages – Salesforce

apex:pageMessage and apex:pageMessages – Salesforce

Most of the times we are confused between these two visualforce components i.e. apex:pageMessage and apex:pageMessages. A small different of ‘s’. Let’s see the difference between apex:pageMessage and apex:pageMessages – Salesforce.

We are confused that which one should we use, and in which case. There is a small difference between these two. Let me explain the same:

We should use apex:pageMessage when we want to have individual message on the visualforce page and you provide the message to be displayed in the attributes of this tag. This is a kind of static message which you will write in visualforce itself, and can put some conditions in rendered attribute to display the message. The strength attribute of the tag controls the size of the message box displayed on the page. No need to have any controller code for adding the message on the page, because message has been written in visualforce page itself.


<apex:page controller="theController">
<apex:pageMessage severity="Error" summary="First Error message on page" strength="1"/>
<apex:pageMessage severity="Error" detail="Second Error message on page" strength="2"/>
</apex:page>

apex:pageMessage and apex:pageMessages - Salesforce

apex:pageMessage

This can be used to display any custom message that you always want to appear on the screen. There may be a case that whenever a user is on a form you may want a warning to appear to display important information to them.

You can also use this to conditionally display a message, based on some condition that you put in the rendered attribute of this component.

Now Let’s talk about apex:pageMessages:

apex:pageMessages is used to display more than one message on a visualforce page. It will display Salesforce generated messages(probably some exception or error) as well as custom messages that you have added to the ApexPages class in your controller code.

Below is the sample code. All the messages added in apex code using ApexPages class are shown in one box only unlike the previous tag apex:pageMessage where all the messages are shown in different boxes as they are put on page. Below is one example:

<apex:page controller="theController">
<apex:pageMessage severity="Error" summary="First Error message on page" strength="1"></apex:pageMessage>
<apex:pageMessage severity="Error" detail="Second Error message on page" strength="2"></apex:pageMessage>
<apex:pageMessages ></apex:pageMessages>

</apex:page>

The controller code for the same below:


public class theController {

public theController(){

ApexPages.addmessage(new ApexPages.message(ApexPages.severity.Error, 'First Error Message added from apex'));
ApexPages.addmessage(new ApexPages.message(ApexPages.severity.Error, 'Second Error Message added from apex'));

}
}

apex:pageMessage and apex:pageMessages - Salesforce

apex:pageMessages

Noticed that, how a single instance of the apex:pageMessages handles all of the errors on the page. The use case for this object is to capture all errors on the page including the error or exception messages generated by salesforce, and the messages added in ApexClass as well. It should be used on most custom Visualforce page as a catch all to let users know what errors occured (if no other messaging is used).

Best practice is to always keep apex:pageMessages on your page, because sometimes Salesforce generates some exception or error (may be because of some trigger or workflow), but because of non-availablitiy of this tag on your page, those messages are not displayed and you are confused why my page or functionality is not processing and not going further.

So I would suggest always keep apex:pageMessages tag on your visualforce page.

Good Luck !!

Permanent link to this article: http://www.sfdcpoint.com/salesforce/apexpagemessage-and-apexpagemessages-salesforce/

Apr 07

Relationship Query in Apex – Salesforce

Relationship Query in Apex – Salesforce

Most of the times, we have relationships between the objects(master-detail OR lookup) and often we need to fetch the list of all child records related to parent record in SOQL. This can be achieved using relationship query.

We can also say it as a Inner Query (query inside query).

For using relationship query, most important is, to know the Name of relationship between the two objects. You can get this relationship name by going to the Look-up OR Master-detail field on child object. Here is the screen that you will see after clicking on the look-up/Master-detail field name that is present on child object.

Here are two examples.

1.First one is for Account(Parent) – Contact (Child) relationship. Below screen, we have opened the Lookup field present on contact(child object).

Child Relationship name2. The second one is for some custom object. When we create a custom relationship field on any object, we get an option to choose the name of relationship, while creating the look-up or Master-Detail relationship field. Below you can see the same, and then the same name is used in relationship queries.

How to create relationship name

Then this relationship name reflects on the custom field as shown below:

Child Relationship name - 2

 

So, in case of relationship between standard objects, relationship name is fixed already, and you can not change, but in case you are creating the relationship on your own, then you get the option of choosing the relationship name.

Now you have got the relationship name. We’ll see, how to construct the query required for our problem i.e. to get all the child records related to one parent record in a single query.

Here is a significant difference while making relationship queries for standard relationships(already defined by salesforce) and custom relationships(defined by user – b/w two custom objects OR b/w custom & standard object).

For example, for account and contacts relationship(already defined by salesforce), the query will be like:

List<Account> accList = [select id,name,(select name, id, email from contacts) from account];

Here you can see in the phrase (select name, id, email from contacts ), we have used the relationship name as it is mentioned on the field detail page as shown below:

Child Relationship name

But in case of custom relationship the query will be like:

List<Account> accList = [select id, (select id, name from tests__r) from account];

Here we have used __r with the relationship name. We have to use __r suffix after the relationship name shown on the field detail page as shown below:

Child Relationship name - 2

This relationship query returns all the accounts with all the child test records associated to each account record. You can use the query result as shown below:


//Perform the query on Account.

List<Account> accList = [select id, (select id, name from tests__r) from account];

//Iterate over the account with.
for(Account a : accList){

//For each account object, get the child records using tests__r and iterate over each child records.
for(test__c test : a.tests__r){

System.debug('The test name is:::::' + test.name);

}

}

Limitation of Relationship Query:

  • In each specified relationship, only one level of parent-to-child relationship can be specified in a query. For example, if the FROM clause specified Account, the SELECT clause could only specify the Contact or other objects at that level. It could not specify a child object of Contact.
  • No more than 20 relationships can be specified in a single query.
  • In each specified relationship, no more than five levels can be specified in a child-to-parent relationship. For example, Contact.Account.Owner.FirstName (three levels).

Good Luck !! :)



Permanent link to this article: http://www.sfdcpoint.com/salesforce/relationship-query-apex-salesforce/

Older posts «