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.