Monday 27 June 2016

TLS 1.0 has been disabled in this organization. Please use TLS 1.1 or higher

Eclipse Error:  TLS 1.0 has been disabled in this organization. Please use TLS 1.1 or higher when connecting to Salesforce using https.



Solution:

Please update your eclipse.ini (configuration settings) file and add below line, 

-Dhttps.protocols=TLSv1.1,TLSv1.2

Then, restart Eclipse.

Wednesday 9 March 2016

Two duplicate error messages on VF page

IssueWhen custom/standard input fields are used with custom label and are required on visualforce page, then pageMessages shows two error messages. Lets check below example with code:
<apex:page StandardController="Contact" sidebar="false" showHeader="false" >  
   <apex:form >  
     <apex:pageBlock title="Complete below form">  
       <apex:pageMessages />  
       <apex:pageBlockButtons location="bottom">  
         <apex:commandButton action="{!save}" value="Save" />  
       </apex:pageBlockButtons>  
       <apex:pageBlockSection title="Confirm Below Fields">  
                     <apex:inputField label="Your Birth Date" value="{!contact.Birthdate}" required="true"/>  
       </apex:pageBlockSection>  
     </apex:pageBlock>  
   </apex:form>  
 </apex:page>  


When you need to change standard label with custom one on VF page then page message will show two error instead of one as stated above.


SolutionExplicitly change label on load client side using jquery. It requires:
  • Custom Label -> to provide ease to user to set desired custom label.
  • div element with static id -> to wrap <apex:pageMessages /> within so that it can be identified at client side. 
  • jQuery -> to browse through the html elements on client side(browser).
The VF page code:

 <apex:page StandardController="Contact" sidebar="false" showHeader="false" >   
      <script src="https://code.jquery.com/jquery-2.2.0.min.js" />   
      <apex:form >   
           <apex:pageBlock title="Complete below form">   
                <div id="errorPanel">   
                     <apex:pageMessages />   
                </div>   
                <apex:pageBlockButtons location="bottom">   
                     <apex:commandButton action="{!save}" value="Save" />   
                </apex:pageBlockButtons>   
                <apex:pageBlockSection columns="1" title="Confirm Below Field">   
                     <apex:pageblockSectionItem>   
                     <apex:outputLabel value="{!$label.Contact_BirthDate}"></apex:outputLabel>   
                     <apex:inputField value="{!contact.Birthdate}" required="true"/>   
                     </apex:pageblockSectionItem>       
                </apex:pageBlockSection>   
           </apex:pageBlock>   
      </apex:form>   
      <script>   
           window.onload = changeErrorLabels();   
           function changeErrorLabels(){   
           var msgEle = $("#errorPanel").find(".messageText");   
           var replacedHtml = msgEle.html().replace('Birthdate','{!$label.Contact_BirthDate}');   
           msgEle.html(replacedHtml);   
           }   
      </script>   
 </apex:page>  

The above solution once implemented provides flexibility to user to change/modify custom label with desired label text that have same error message labels on VF page.

Click here to see working demo, Happy Coding :) !!

Thursday 25 February 2016

Apex Readable Code - Best Practices

Code readability is a universal subject for computer programming that should be learned as developers. This article is for Salesforce Developers and focuses on 8 most important best practices considered when writing readable code using Apex.
The eight most important practices are:
1.       Class Documentation - Every class/trigger should have proper documentation stating the purpose of class with its author.

2.       Commenting - Programmers use comments to annotate a program and help the reader (or grader) understand how and why your program works. Comments should describe what or why you are doing something, rather than how. Eg,
 i++;      //  increment i by one
·         Above comment how to increment rather why. So, do not write comments that restate the code.
·         Update the comments when you update the code.

3.       Consistent Indentation & Whitespace – When you are part of a team or if you are contributing code to a project, you should follow the existing style that is being used in that project.
·         Don't put more than one statement on a line.
·         Use blank lines to separate your code into logical sections.
·         Put a space between all binary operators (e.g., <=, =, +) and their operands. One possible exception is to emphasize precedence, like z = a*b + c*d
·         Put a space after each comma in an argument list.
·         Do not put spaces before a semicolon.
·         Do not put spaces between a method name and its left parenthesis.
·         Include blank lines to improve readability by grouping blocks of related code.
·         Avoid code horizontal line longer than 100 characters (approx., it depends).

4.       DRY Principle - DRY stands for “Don't Repeat Yourself”. The purpose for most applications (or computers in general) is to automate repetitive tasks. This principle should be maintained and same piece of code should not be repeated over and over again. Let say you need to check duplicate in a list on 2 VF pages or in 2 triggers then it’s good to have a static method in utility class which can be reused from anywhere within application.

5.       Overcome Apex Line Characters Limit
·         When populating map in apex in traditional style:

·         Populating map in apex, optimized style from 6 lines to 4 lines:

·         Use of ternary operator instead of if-else where possible,


6.       Consistent Naming - There are some general principles when choosing names for your variables, methods, and classes.
·         Use meaningful names that convey the purpose of the variable. Choose names that are easy to pronounce, and avoid cryptic abbreviations. For example, use wagePerHour or hourlyWage instead of wph. Use polygon instead of p or poly or pgon.
·         Be consistent.
·         Name boolean variables and methods so that their meaning is unambiguous, e.g., isPrime or hasValue.
·         Use shorter names (e.g., i) for short-lived variables and loop-index variables. Use more descriptive names for variables that serve an important purpose.
·         Avoid generic names like foo or tmp and meaningless names like fred. Use terminology from the application domain when possible.
·         Name a constant by its meaning, not its value, e.g., name your variable DAYS_PER_WEEK instead of SEVEN.
7.       Avoid Deep Nesting – Too many levels of nesting can make code harder to read and follow. 

For the sake of readability, it is usually possible to make changes to your code to reduce the level of nesting:

But my personal choice is method result variable and avoid multiple return in single method.


8.       Capitalize SOQL Special Words – It is a good idea to capitalize SOQL special words. Even though SOQL special words and function names are case insensitive, it is common practice to capitalize them to distinguish them from other variables and keywords.



Thursday 26 June 2014

Parse zipped XML files on visualforce page

Salesforce doesn't have direct method to read files. We can do it either by external service written in Java/.Net or using javascript.

We will be discussing to parse data coming in zipped XML over http from 3-party API and populate data in salesforce. Basically, there were three main tasks:

  1. Collect http reponse from API in form of zipped xml.
  2. Unzip that response and get xml string out of it.
  3. Parse xml string to insert data.
Main focus of this blog is to accomplish Task 2 but first let see the key points for tasks 1 & 3:

Task 1: 
  • You just need to call response.getBodyAsBlob() when ever response is in form of file.
  • Then, you need to convert bolb into string using code syntax: 
String xmlzippedFile = EncodingUtil.base64Encode(res.getBodyAsBlob());

Task 3:
  • If your XML string contains <![CDATA[value]]> tag then make use of XmlStreamReader class.
  • If not, then you can either use Document class or XmlStreamReader class.

Task 2:


Main task to unzip xml file, the steps to achieve are mentioned below:
  • Add jZip.js file to static resource (download)
  • Add inflate.js file to static resource. (download)
  • Open notepad, paste below code and save as file.js file and upload to static resource.
var filearr = [];

/* Main unzip function */
function unzip(zip){
 model.getEntries(zip, function(entries) {
  entries.forEach(function(entry) {
   model.getEntryFile(entry, "Blob");
  });
 });
}

//model for zip.js
var model = (function() {
 var URL = window.webkitURL || window.mozURL || window.URL;
 var acount = 0;
 var bcount = 0;

 //compile a list of file extensions and content types
 var mapping = {
   "pdf":"application/pdf",
   "zip":"application/zip",
   "rar":"application/rar",
   "json":"application/json",
   "mid":"audio/mid",
   "mp3":"audio/mpeg",
   "bmp":"image/bmp",
   "gif":"image/gif",
   "png":"image/png",
   "jpg":"image/jpeg",
   "jpeg":"image/jpeg",
   "svg":"image/svg+xml",
   "xml":"text/xml"
  }
 return {
  getEntries : function(file, onend) {
   zip.createReader(new zip.BlobReader(file), function(zipReader) {
    zipReader.getEntries(onend);
   }, onerror);
  },
  getEntryFile : function(entry, creationMethod, onend, onprogress) {
   acount++;
   var writer, zipFileEntry;
   function getData() {
    entry.getData(writer, function(blob) {
     bcount++;
     filearr.push(blob);
     if(acount == bcount){
      //your vf page method
      waitForProcess();
     }
    }, onprogress);
   }
   var extension = entry.filename.substring(entry.filename.indexOf(".")+1);
   var mime = mapping[extension] || 'text/plain';
   writer = new zip.BlobWriter(mime);
   getData();
  }
 };
 })();
  • Create a VF page and include reference to jZip.js and file.js
  • Create a button on VF page to invoke a javascript remoting method which returns a string from Task 1. (Or you can also place a input file element with button if you just want to upload zipped from client machine). 
Below is VF page code:
<!-- place after page start-->

    <meta charset="utf8" />       
     <apex:includeScript value="{!URLFOR($Resource.jQuery, '')}"/>
    <apex:includeScript value="{!URLFOR($Resource.zip, '')}"/>
    
    <script>
    
    $( document ).ready(function() {
        //Call loading
        requestFile();            
    });
    
    function base64toBlob(base64Data, contentType) {
        contentType = contentType || '';
        var sliceSize = 1024;
        var byteCharacters = atob(base64Data);
        var bytesLength = byteCharacters.length;
        var slicesCount = Math.ceil(bytesLength / sliceSize);
        var byteArrays = new Array(slicesCount);
    
        for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
            var begin = sliceIndex * sliceSize;
            var end = Math.min(begin + sliceSize, bytesLength);
    
            var bytes = new Array(end - begin);
            for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
                bytes[i] = byteCharacters[offset].charCodeAt(0);
            }
            byteArrays[sliceIndex] = new Uint8Array(bytes);
        }
        return new Blob(byteArrays, { type: contentType });
    }
    
    function requestFile() {
            var param = 5; //set your param to pass in method
            Visualforce.remoting.Manager.invokeAction(
            '{!$RemoteAction.APEX_PAGE_CONTROLLER_NAME(GLOBAL).methodName}',
            param,
            function(result, event){
                if (event.status) {
                    //call unzip method
                    unzip(base64toBlob(result, ''));
                } else if (event.type === 'exception') {
                    console.log(event.message);
                }
            },  
            {escape: true}
        );
    }
    
    var xmlStrResult = [];
    var isExecuted = false;
    var timerVal;
    
       //your vf page method

function waitForProcess(){
        console.log('call timer');
        timerVal = setInterval('prepareXMLString()', 10);        
    }
    
    function prepareXMLString() {
        if(!isExecuted){            
            $.each(filearr, function(index, file){
                var reader = new FileReader();  // Create a FileReader object
                
                reader.onload = function() {    // Define an event handler
                    try {
                        var xmlDoc = $.parseXML(reader.result);
                        var xmlString = (new XMLSerializer()).serializeToString(xmlDoc);
                        xmlStrResult.push(xmlString); // Display file contents
                    }catch(err) {
                        //TODO
                    }       
                }
                reader.readAsText(file);
                isExecuted = true;
            });
        }
        
        if(xmlStrResult.length != 0){
            clearInterval(timerVal);
            $('#showData').val(xmlStrResult[0]);
            console.log('Done');          
        }
    }
    </script>
    
    <apex:pageBlock >
        <apex:pageBlockSection columns="1" title="Your XML string">
            <apex:pageBlockSectionItem >
               <textarea id="showData" name="showData" rows="30" cols="150">
               Please wait....
               </textarea>
            </apex:pageBlockSectionItem>
        </apex:pageBlockSection>
    </apex:pageBlock>
    
    <apex:includeScript value="{!$Resource.file}" loadOnReady="true"/>


<!-- place before page ends-->

Key points to remember: 
  • Remoting is required, when you get file from apex controller as http response or from attachment/static resource.
  • In case of input file element, pass file directly to unzip() method.
Happy coding :) !!

Tuesday 16 April 2013

Salesforce based on MVC


Blog discuss very basic understanding of MVC with respect to Salesforce.

MVC

  • Front End(User Interface) - View
  • Entity/ Object to hold data - Model
  • Business logic - Controller
In simple terms, Controller fetches the data from data source and populates Model(object) to display it on View.


Salesforce

  • Front End(User Interface) - Visual Force page, Native page layout.
  • Entity/ Object to hold data - Salesforce Object (standard, custom)
  • Business logic - Apex Class behaves as controller.