Sunday 11 October 2015

Write your first BOOTSTRAP

BOOTSTRAP is famous for its fast and easier web development, so we should start designing our page using this.

BOOTSTRAP has multiple advantages:

  • Easier to implement
  • More responsive UI
  • Mobile compatible UI
  • Open source
  • HTML syntax
  • And the list goes on
Now let's learn how to include BOOTSTRAP framework in VF pages, which will help us to design a   responsive UI in Salesforce.

Few things to know before we proceed to VF coding:
  • BOOTSTRAP and jQuery Library must be included in page
  • All the BOOTSTRAP codes should be written inside Container-Fluid or Container class
  • It is always advisible to include META tags within the Bootstrap page for encoding, width scale and IE compatibility.
  • All the Bootstrap flavour on the page comes with the Class property of HTML elements, we just need to write the class value of the component and it will work as expected.
  • Remembering Bootstrap is also very easy, because the component names are exactly same what  they do. For ex: row creates a row, col creates a column, panel creates a panel etc..
Few properties which is very common and we will be using it in the example:
  • DIV: This will be used mostly to design the page, with different types of classes attached to it.
  • ROW: This creates a row in the page for parent element or divides the page in rows
  • COL-XX-YY: This creates the column for the row or divides the parent element area in columns. XX is the screen size of the machine and the possible values would be LG(Large), MD(Medium), SM(Small), XS(Extra Small). YY is the column size according to the page or parent element area, max size would be 12 and minimum could be 1. So the complete page can be divided into 12 parts.
  • PANEL: This creates a Panel in the page, which can further contain Header, Body and Footer.
  • BTN: This crates the button in the page.
Few styling of Bootstrap element, we can use few predefined attribute along with the class name:
  • DEFAULT(Grey color)
  • PRIMARY(Blue color)
  • SUCCESS(Green color)
  • INFO(Light Blue color)
  • WARNING(Orange color)
  • DANGER(Red color)
Above styling properties is always used with some class name, like panel-default, btw-primary etc..

Below is a very basic VF page of a login screen, but it will give you an idea how to go ahead with BOOTSTRAP.

VF Page:

<!--In below apex:page tag, I have disabled all the CSS property so that we get on boostrap flavour in our page. And also I have used a custom controller for the page-->

<apex:page id="loginFlow" showHeader="false" sidebar="false" standardStylesheets="false" applyBodyTag="false" applyHtmlTag="false" controller="CustomLogin">

<html>
    <head>
        <title>AK Computing</title>
        <!-- below is for character encoding -->
        <meta charset="utf-8" />
        <!-- below is to make the page IE compatible -->
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <!-- below is make the page responsive according to the screen size -->
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <!-- These three libraries are the pre-requisite for Bootstrap -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
        <style>
            .blackBackground{
                background-color: #000000 !important; 
            } 
            .commandButton{
                background-color: #337ab7 !important;
                border-color: #2e6da4 !important;
                color:#EEEEEE;
                width:100% !important:
            }
            .inputTxt{
                width:35%;
                margin-bottom:2%;
            }
            .mainPanel{
                min-height:200px;
                margin-left:30%;
                margin-right:30%;
                margin-top:20%;
                margin-bottom:20%; 
            } 
        </style>
        <script></script>
    </head>
    <body class="blackBackground">
        <apex:Form >
            <!--below tag contains all the bootstrap -->
            <div class="container-fluid">
                <!--below creates a panel of type default, but here I have customised it with css -->
                <div class="panel panel-default mainPanel">
                    <div class="panel-body">
                        <!-- row divides the pages or parent element area into rows, here i have divided Panel into three parts -->
                        <div class="row"> 
                            &nbsp; 
                        </div>
                        <div class="row">
                            <!-- col creates the column, here it will take the complete width as I have mentioned 12 in the size -->           
                            <div class="col-xs-12">
                                <center>
                                    <div class="row">
                                        <apex:inputText value="{!userName}" id="userNameInput" 
                                            html-placeholder="Username" styleClass="inputTxt"/>
                                    </div>
                                    <div class="row">
                                        <apex:inputSecret value="{!password}" id="passwordInput" 
                                            html-placeholder="Password" styleClass="inputTxt" />
                                    </div>
                                    <div class="row">
                                        <apex:outputPanel rendered="{!error}">
                                            <apex:outputText ><Font color="red">{!errorMessage}</font></apex:outputText>
                                        </apex:outputPanel>
                                    </div>
                                    <div class="row">
                                        <apex:commandButton action="{!login}" id="commandBtn" styleClass="commandButton"
                                            style="width:35%;" value="{!$Label.site.login_button}" />
                                    </div>
                                    <div class="row">
                                        &nbsp; 
                                    </div>
                                    <div class="row">
                                        <apex:outputLink value="{!$Page.CustomForgotPassword}"> {!$Label.site.forgot_your_password_q}
                                        </apex:outputLink>
                                    </div>
                                </center>
                            </div>
                        </div>
                        <div class="row"> 
                            &nbsp; 
                        </div>
                    </div>
                </div>
            </div>
        </apex:Form>
    </body>
</html>
</apex:page>


There is quite a long list of Bootstrap predefined classes which makes life easier for the designer, I hope this will give you a hint how to start with Bootstrap.

Decrypt complete document in Salesforce

In my previous post I have written how to Encrypt a document, so here it is how to decrypt a document.

There are two ways to do it:

  1. Without storing the decrypted document on cloud
  2. Store it in decrypted form as an attachment.
Below are the codes for both of the things:

APEX:

//Using future method here to avoid CPU time limit exception

// Below method do not store anything on cloud and sends email with decrypted document to an 
// email Id

@future
public static void sendDecryptedAtt(String accId,Blob key, String emailId)
{

    List<Attachment> attList=[Select id,name,body from Attachment where parentId=:accId limit 1];


    if(attList.size() > 0){
        Blob decrypted = Crypto.decryptWithManagedIV('AES256', key, attList[0].body);
 
        String blobString=decrypted.toString();

        Blob doc=EncodingUtil.base64decode(blobString);
        String fileName=accAtt.name;

        sendEmail(doc,emailId,fileName);

        createAtt(doc, fileName, accId);
    }    
}

// below method sends email to the given email address
Public Static Void sendEmail(Blob attachmentFile, String emailId,String fileName)
{
    Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
    efa.setFileName(fileName);
    efa.setBody(attachmentFile);

    Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
    email.setSubject('Decrypted File: '+ fileName);
    String[] emails=new List<String> {emailId};
    email.setToAddresses( emails);
    email.setPlainTextBody('Hi,\n\nPlease find attached decrypted document.\n\nThanks');

    email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa});

    // Sends the email
    Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[ {email});
}


//Below method creates an attachment in the Salesforce with decrypted content
Public Static Void createAtt(Blob attachmentFile, String fileName, String accId)){
    Attachment att=new Attachment();
    att.name = 'Decrypted_'+fileName;
    att.body = attachmentFile;
    att.parentId = accId;

    insert att; 
}

Above code works for all kind of documents. Encryption technique is present in my previous posts.

Encrypt Complete Document in Salesforce

In multiple business scenario it is required to encrypt the document which are restricted in nature, the reason is since Salesforce is a on cloud application so there could be a policy of a company to not to store documents on cloud without encrypting it.

Salesforce do provide encrypted fields to store the data securely but do not provide any standard functionality to encrypt a complete document, but I was able to encrypt the complete document successfully using custom logic.  Although I had done it two years back but now making available for everyone.

Salesforce supports multiple encryption algorithm, here I am going to use AES256 for encryption.

For encryption key, I have used Protected Custom Setting as this is a most secured place to store the data.

Please note this code can be used for any document type.

Below is the example with end to end code to encrypt a document and attach to the account. Below code can be customised using custom logic for different scenarios like in the Trigger to automatic encrypt based on the condition etc.

APEX:

// below method takes account id as parameter for which we will be inserting the attachment
Public static void encrypt(String accId, String fileName, Blob fileContent){ 
 
    //EncryptionKey__c is a custom setting where we would be storing private key, insertion is only
    //one time activity. Please be very careful in managing the KEY it should not get deleted else 
    //the document would not be decrypted.

    EncryptionKey__c ek = EncryptionKey__c.getValues('Private Key');
    if(ek.Key__c == null){
        Blob cryptoKey = Crypto.generateAesKey(256);
        ek.Key__c = EncodingUtil.base64Encode(cryptoKey);
        update(ek);
    }
    system.debug(ek.Key__c);

    Blob cryptoKey = EncodingUtil.base64Decode(ek.Key__c); 

    //calling other method which actually encrypt and upload the file as an attachment
    encryptlargeBlob(fileContent,cryptoKey, accId, fileName); 
}


// I am using Future method here because sometimes file size is big and system throws time out 
// exception, to avoid this we can use future for increased limits

@future
Public Static Void encryptlargeBlob(Blob largeBlob, Blob key, String accId, String fileName)
{
    String blobString=EncodingUtil.base64encode(largeBlob);
    
    // Below will encrypt the string and will return encrypted blob
    Blob encrypted = Crypto.encryptWithManagedIV('AES256', key, tempBlob);

    Attachment att=new Attachment();
    att.name = fileName;
    att.body = encrypted;
    att.parentId = accId;

    insert att;
}

Using above code you can easily Encrypt the document, the decryption part is different, Please find my other post for decryption.

Please be careful while playing with Encryption and do proper testing based on your scenario.
 

Friday 25 September 2015

Google Map Integration in Salesforce using Javascript

Google Map can easily be integration with VF page using few standard libraries provided by Google and Salesforce.

Below is an example, where I am integration with Google Map to display few nearby Accounts from my current position or a default position.

 
VF Page:

<apex:page sidebar="false" showheader="false" recordSetVar="clubs" controller="ShowNearByPlaces">
    
    <script type="text/javascript" 
        src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAVrfZm7_NhbLjHrFPdl242BYV1PBmDPqs&sensor=false"> 
    </script>
        
    <!--Setup the map to take up the whole window-->
    <style>
        html, body { height: 100%; }
        .page-map, .ui-content, #map-canvas { width: 100%; height:100%; padding: 0; }
        #map-canvas { height: min-height: 100%; }
    </style>
    
    <script>
        function initialize() {
            var lat, lon;
              
             // If we can, get the position of the user via device geolocation
             if (navigator.geolocation) {
                 navigator.geolocation.getCurrentPosition(function(position){
                     lat = position.coords.latitude;
                     lon = position.coords.longitude;                    
                     
                     // Use Visualforce JavaScript Remoting to query for nearby clubs      
                     Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.FindNearby.getNearby}', lat, lon,
                         function(result, event){
                             if (event.status) {
                                 console.log(result);
                                 createMap(lat, lon, result);           
                             } else if (event.type === 'exception') {
                                 //exception code          
                             } else {
                                            
                             }
                          }, 
                          {escape: true}
                      );
                  });
              } else {
                  // Set default values for map if the device doesn't have geolocation capabilities
                    /** Pune, India **/
                    lat = 18.516726;
                    lon = 73.856255;
                    
                    var result = [];
                    createMap(lat, lon, result);
              }
          
         }
    
         function createMap(lat, lon, clubs){
            // Get the map div, and center the map at the proper geolocation
            var currentPosition = new google.maps.LatLng(lat,lon);
            var mapDiv = document.getElementById('map-canvas');
            var map = new google.maps.Map(mapDiv, {
                center: currentPosition, 
                zoom: 13,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            });
            
            // Set a marker for the current location
            var positionMarker = new google.maps.Marker({
                map: map,
                position: currentPosition,
                icon: 'http://maps.google.com/mapfiles/ms/micons/green.png'
            });
            
                        
            // Keep track of the map boundary that holds all markers
            var mapBoundary = new google.maps.LatLngBounds();
            mapBoundary.extend(currentPosition);
            
            // Set markers on the map from the @RemoteAction results
            var club;
            for(var i=0; i<clubs.length;i++){
                club = clubs[i];
                console.log(clubs[i]);
                setupMarker();
            }
            
            // Resize map to neatly fit all of the markers
            map.fitBounds(mapBoundary);

           function setupMarker(){ 
                var clubNavUrl;
                
                // Determine if we are in Salesforce1 and set navigation link appropriately
                try{
                    if(sforce.one){
                        clubNavUrl = 
                            'javascript:sforce.one.navigateToSObject(\'' + club.Id + '\')';
                    }
                } catch(err) {
                    console.log(err);
                    clubNavUrl = '\\' + club.Id;
                }
                
                var clubDetails = 
                    '<a href="' + clubNavUrl + '">' + 
                    club.Name + '</a><br/>' + 
                    club.Street_Address__c + '<br/>' + 
                    club.City_Address__c + '<br/>' + 
                    club.Phone__c;
               
               // Create the callout that will pop up on the marker     
               var infowindow = new google.maps.InfoWindow({ 
                   content: clubDetails
               });
               
               // Place the marker on the map   
               var marker = new google.maps.Marker({
                   map: map,
                   position: new google.maps.LatLng( 
                                   club.Address_Geo__Latitude__s, 
                                   club.Address_Geo__Longitude__s)
               });
               mapBoundary.extend(marker.getPosition());
               
               // Add the action to open up the panel when it's marker is clicked      
               google.maps.event.addListener(marker, 'click', function(){
                   infowindow.open(map, marker);
               });
           }
        }
        
        // Fire the initialize function when the window loads
        google.maps.event.addDomListener(window, 'load', initialize);
        
    </script>
    
    <!--  All content is rendered by the Google Maps code -->
    <!--  This minimal HTML justs provide a target for GMaps to write to -->
    <body style="font-family: Arial; border: 0 none;">
        <div id="map-canvas"></div>
    </body>
</apex:page>





Controller:

Global Class ShowNearByPlaces{ 
    @RemoteAction
    Global static List<Account> findNearby(String lat, String lon) {
        Decimal Latitude = Decimal.valueOf(lat);
        Decimal Longitude = Decimal.valueOf(lon);

        List<Account> accList = [Select Id,Name, BillingStreet, BillingCity, BillingState, BillingPostalCode,BillingCountry, BillingLatitude, BillingLongitude,Geo_Location__Longitude__s, Geo_Location__Latitude__s FROM Account WHERE DISTANCE(Geo_Location__c, GEOLOCATION(:Latitude, :Longitude), 'mi') < 10];

        return accList;
    }
}

GEOLOCATION field in Salesforce

Geolocation is a compound field in Salesforce which stores the Longitude and Latitude. Below are few key points about Geolocation field:

  • It is a compound field which stores Longitude and Latitude, Once stored it will be treated as Location in Salesforce.
  • It can be used to find the distance between two Locations
  • It can be used in SOQL query condition.
  • It can be used to display the location of Google Map using Salesforce Standard feature.
Example:

Let's assume that we have created a Geolocation field in Salesforce with the name Geo_Location__c,  then Salesforce automatically creates three fields in the background with this:
  • Longitude: Geo_Location__Longitude__s
  • Latitude: Geo_Location__Latitude__s
  • System field: For internal use

Location Based SOQL Query:

Using DISTANCE keyword we can find the distance between two Geolocation values, below is a quick example to understand this:

Public Class ShowNearByPlaces{

    Public static List<Account> findNearby(String lat, String lon) {

        Decimal Latitude = Decimal.valueOf(lat);
        Decimal Longitude = Decimal.valueOf(lon);

        //for example, below query is on Account
        List<Account> accList = [Select Id,Name, BillingStreet, BillingCity, BillingState, BillingPostalCode,
BillingCountry, BillingLatitude, BillingLongitude,Geo_Location__Longitude__s, Geo_Location__Latitude__s FROM Account WHERE DISTANCE(Geo_Location__c, GEOLOCATION(:Latitude, :Longitude), 'mi') < 10];

        return accList;
    }

}

JavaScript Remoting

Javascript Remoting in Salesforce is a functionality which enables a developer to call Controller methods from VF page. This is really helpful in the situation which can't be handled using AJAX or few complex scenarios.

There are basically three things required:
  1. Remote method definition in the controller
  2. Remote method invocation in the JS.
  3. Remote method response handler
Below is a very basic example for JS Remoting:

      VF Page:

            <apex:page controller="testClass">

                 <script>
                   window.onload = function deleteRecord(){
                        var stringName =  'test';
                                                         
                        Visualforce.remoting.Manager.invokeAction('{!$RemoteAction. testClass. check}',                
                         stringName, function(result, event){

                             if (event.status) {

                                 if(result.length > 0){
                                     //do some logic
                                 }
                             }
                              else if(event.type == 'exception'){
                                   alert(event.message + ' '+ event.where);
                              }
                         },
                         {escape: true}                    }

                 </script>

           </apex:page>

        Controller:

       Global class testClass{

             @RemoteAction
             Global void check(String name){

                  String s= name;

                  apexPages.addMessages(s);

             }

        }

Note
  •  If there are multiple parameter required to call a method then pass the parameter in JS method exactly similar which is defined in the controller method. For ex:
          Visualforce.remoting.Manager.invokeAction('{!$RemoteAction. testClass. check}',                
                       stringName,stringName1,stringName2, function(result, event){

Thursday 24 September 2015

Replace content of the Email Template using Apex

There could be multiple scenarios where we need to replace few parts of the content while sending the email from Apex. Below is the example for the same

Apex Code:

    //Below fetches the email template which we want to use for email
    EmailTemplate et=[Select id, htmlValue, Body, subject from EmailTemplate where DeveloperName = 'xyz'];

    //for an example we will be sending the email to the below User
    User u = [select Id, UserName, FirstName from User where Id  =: UserInfo.getUserId()];

    String htmlBody = et.HtmlValue;

    //below replaces the UserName with current User Name
    htmlBody = htmlBody.replace('{!Receiving_User.Username}', u.Username);
    //below replaces the FirstName with current User First Name
    htmlBody = htmlBody.replace('{!Receiving_User.FirstName}', u.FirstName);

    String plainBody = et.Body;

    plainBody = plainBody.replace('{!Receiving_User.Username}', u.Username);
    plainBody = plainBody.replace('{!Receiving_User.FirstName}', u.FirstName);
    //Below list is not required, it is just to show that we can send multiple email as well
    List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMessage>();

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    mail.setTargetObjectId(u.Id);
    mail.setSenderDisplayName('Support Group');
    mail.setSubject('Welcome! Your Account Details');
    mail.setSaveAsActivity(false);
    mail.setHtmlBody(htmlBody);
    mail.setPlainTextBody(plainBody);
    mails.add(mail);


    if(mails.size() > 0 ) {
        Messaging.sendEmail(mails);
    }

Note: 
  • plainTextBody is optional, Either TemplateId or serHtmlBody or plainTextBody should be present to send an email.