Simple dashboard using Bootstrap + Chart.js inside Salesforce.com

This post is about  developing a very simple dashboard inside Salesforce.com using Chart.js and Bootstrap. Click here to see this in action

Recently I came across Chart.js which seems to be the simplest way to achieve this. Being a fan of developing things inside Salesforce.com using Bootstrap so decided it to mix with Bootstrap (a bit).

Benefits —

Looks beautiful

Animates

Responsive

Light weight

To get started with it you need to download package available at Chart.js website and upload it to your Salesforce.com org as Static Resource as name chart

http://www.chartjs.org/

graphweb

In our case the  path become like this

{!URLFOR($Resource.chart,'Chart.js-master/Chart.js')}

To use bootstrap with this I have used bootstrap’s CDNs.

bootstrap

Bootstrap3 – http://getbootstrap.com/

The implementation of chart.js is very straight forward and we only need to define the dataset/datastructure to draw the charts . Label denotes the labels you want to put across and supply the values with repect to them. Below is the complete code, I have tried to keep it as simple as I can.

Visualforce Page 1 – for Bar and curve chart

1

    <apex:page sidebar="false" controller="ChartCreatorCls"  id="pgId">
    
    
        <head>
            <title>Charts</title>
            <script src="{!URLFOR($Resource.chart,'Chart.js-master/Chart.js')}"></script>
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css"/>
            <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
        </head>
      <body>
          <nav class="navbar navbar-default">
              <div class="container-fluid">
                    <div class="navbar-header">
                     <a class="navbar-brand" href="#">Dashboard</a>
                    </div>
                    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                      <ul class="nav navbar-nav">
                        
                        <li class="active"><a href="{!URLFOR($Site.Prefix+'/apex/chart')}">Opportunities <span class="sr-only">(current)</span></a></li>
                        <li><a href="{!URLFOR($Site.Prefix+'/apex/chart2')}">Leads</a></li>
                       
                      </ul>
                    </div>
              </div>
         </nav>
         <div style="width: 50%;float:left;">
                <canvas id="barchart" height="400" width="400"></canvas>
          </div>
          <div style="width: 50%;float:right;">
                <canvas id="linechart" height="400" width="400"></canvas>
          </div>
      </body>
      <script>
                  var barChartData = {
                        labels : ["Prospecting","Qualification","Needs Analysis","Value Proposition","Id. Decision Makers","Perception Analysis","Proposal/Price Quote","Negotiation/Review","Closed Won","Closed Lost"],
                        datasets : [
                           
                            {
                                fillColor : "rgba(151,187,205,0.5)",
                                strokeColor : "rgba(151,187,205,0.8)",
                                highlightFill : "rgba(151,187,205,0.75)",
                                highlightStroke : "rgba(151,187,205,1)",
                                data:[{!prospecting},{!qualification},{!needsAnalysis},{!valueProposition},{!IdDecisionMakers},{!perceptionAnalysis},{!proposalOrPriceQuote},{!NegotiationOrReview},{!closedWon},{!closedLost}]
                            }
                        ]
                
                    }
                    var randomScalingFactor = function(){ return Math.round(Math.random()*100)};
                    var lineChartData = {
                            labels : ["Prospecting","Qualification","Needs Analysis","Value Proposition","Id. Decision Makers","Perception Analysis","Proposal/Price Quote","Negotiation/Review","Closed Won","Closed Lost"],
                        
                            datasets : [
                                {
                                    label: "Expected",
                                    fillColor : "rgba(220,220,220,0.2)",
                                    strokeColor : "rgba(220,220,220,1)",
                                    pointColor : "rgba(220,220,220,1)",
                                    pointStrokeColor : "#fff",
                                    pointHighlightFill : "#fff",
                                    pointHighlightStroke : "rgba(220,220,220,1)",
                                    // Using Random Scaling Factor to generate expected curve randomly.
                                    data : [randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor(),randomScalingFactor()]
                                },
                                {
                                    label: "Actuals",
                                    fillColor : "rgba(151,187,205,0.2)",
                                    strokeColor : "rgba(151,187,205,1)",
                                    pointColor : "rgba(151,187,205,1)",
                                    pointStrokeColor : "#fff",
                                    pointHighlightFill : "#fff",
                                    pointHighlightStroke : "rgba(151,187,205,1)",
                                    // Using actual values salesforce.com org.
                                    data:[{!prospecting},{!qualification},{!needsAnalysis},{!valueProposition},{!IdDecisionMakers},{!perceptionAnalysis},{!proposalOrPriceQuote},{!NegotiationOrReview},{!closedWon},{!closedLost}]
                                }
                            ]
                
                        } 
                    
                    window.onload = function(){
                        var ctx = document.getElementById("barchart").getContext("2d");
                        var ctz = document.getElementById("linechart").getContext("2d");
                        
                        window.myBar = new Chart(ctx).Bar(barChartData, {
                            responsive : true
                        });
                        window.myLine = new Chart(ctz).Line(lineChartData, {
                           responsive: true
                        }
                        
                        
                        );
                       
                    }
                
        </script>
        
    
</apex:page>

Controller for above page –

    public class ChartCreatorCls {
    public Integer prospecting {get;set;}
    public Integer qualification {get;set;}
    public Integer needsAnalysis {get;set;}
    public Integer valueProposition {get;set;}
    public Integer iDDecisionMakers {get;set;}
    public Integer perceptionAnalysis {get;set;}
    public Integer proposalOrPriceQuote {get;set;}
    public Integer negotiationOrReview {get;set;}
    public Integer closedWon {get;set;}
    public Integer closedLost {get;set;}
   
    
    public chartCreatorCls(){
        
        prospecting = 0;
        qualification = 0;
        needsAnalysis = 0;
        valueProposition = 0;
        iDDecisionMakers = 0;
        perceptionAnalysis = 0;
        proposalOrPriceQuote = 0;
        negotiationOrReview = 0;
        closedWon =  0;
        closedLost = 0;
              
        generateData();
        
    
    }
    
    
   
   public void generateData(){
   
   
    AggregateResult[] groupedResults  = [SELECT StageName, Count(Id) cnt FROM Opportunity group by StageName];

        for (AggregateResult ar : groupedResults)  {
            if(ar.get('StageName').equals('Prospecting'))
                prospecting = (Integer) ar.get('cnt');
            if(ar.get('StageName').equals('Qualification'))
                qualification = (Integer) ar.get('cnt');    
            if(ar.get('StageName').equals('Needs Analysis'))
                needsAnalysis = (Integer) ar.get('cnt'); 
            if(ar.get('StageName').equals('Value Proposition'))
                valueProposition = (Integer) ar.get('cnt'); 
            if(ar.get('StageName').equals('Id. Decision Makers'))
                iDDecisionMakers  = (Integer)  ar.get('cnt'); 
            if(ar.get('StageName').equals('Perception Analysis'))
                perceptionAnalysis = (Integer) ar.get('cnt'); 
            if(ar.get('StageName').equals('Proposal/Price Quote'))
                proposalOrPriceQuote =(Integer) ar.get('cnt'); 
            if(ar.get('StageName').equals('Negotiation/Review'))
               negotiationOrReview  =(Integer) ar.get('cnt'); 
            if(ar.get('StageName').equals('Closed Won'))
               closedWon = (Integer) ar.get('cnt'); 
            if(ar.get('StageName').equals('Closed Lost'))
               closedLost  = (Integer) ar.get('cnt');       
        }
    }

}

Visualforce Page 2 – Donut and Radar Chart
1

   <apex:page sidebar="false" controller="ChartCreatorCls2" id="pgId" >

    <head>
        <title>Charts</title>
        <script src="{!URLFOR($Resource.chart,'Chart.js-master/Chart.js')}"></script>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css"/>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
    </head>
    <body>
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                     <a class="navbar-brand" href="#">Dashboard</a>
                </div>
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li><a href="{!URLFOR($Site.Prefix+'/apex/chart')}">Opportunities <span class="sr-only"></span></a></li>
                        <li class="active"><a href="{!URLFOR($Site.Prefix+'/apex/chart2')}" >Leads <span class="sr-only"></span></a></li>
                    </ul>
                </div>
             </div>
        </nav>
        <div style="width: 50%;float:left;">
            <canvas id="chart-area" width="100" height="100"/>
        </div>
        <div style="width:30%;float:right;">
            <canvas id="radar" height="200" width="200"></canvas>
        </div>
    </body> 
    <script>
                   var doughnutData = [
                            {
                                value: {!openNotContacted},
                                color:"#F7464A",
                                highlight: "#FF5A5E",
                                label: "Open Not Contacted"
                            },
                            {
                                value: {!workingContacted},
                                color: "#46BFBD",
                                highlight: "#5AD3D1",
                                label: "Working Contacted"
                            },
                            {
                                value: {!closedConverted},
                                color: "#FDB45C",
                                highlight: "#FFC870",
                                label: "Closed Converted"
                            },
                            {
                                value: {!closedNotConverted},
                                color: "#949FB1",
                                highlight: "#A8B3C5",
                                label: "Closed Not Converted"
                            }
            
                        ];
                    
                    var radarChartData = {
                    labels: ["Closed Not Converted", "Closed Converted", "Working Contacted", "Open Not Contacted"],
                    datasets: [
                        {
                            label: "Expected",
                            fillColor: "rgba(220,220,220,0.2)",
                            strokeColor: "rgba(220,220,220,1)",
                            pointColor: "rgba(220,220,220,1)",
                            pointStrokeColor: "#fff",
                            pointHighlightFill: "#fff",
                            pointHighlightStroke: "rgba(220,220,220,1)",
                            data: [10,20,30,50]
                        },
                        {
                            label: "Actual",
                            fillColor: "rgba(151,187,205,0.2)",
                            strokeColor: "rgba(151,187,205,1)",
                            pointColor: "rgba(151,187,205,1)",
                            pointStrokeColor: "#fff",
                            pointHighlightFill: "#fff",
                            pointHighlightStroke: "rgba(151,187,205,1)",
                            data: [{!openNotContacted},{!workingContacted},{!closedConverted},{!closedNotConverted}]
                        }
                    ]
                };
            
                        window.onload = function(){
                            var ctx = document.getElementById("chart-area").getContext("2d");
                            window.myDoughnut = new Chart(ctx).Doughnut(doughnutData, {responsive : true});
                            window.myRadar = new Chart(document.getElementById("radar").getContext("2d")).Radar(radarChartData, {
                             responsive: true
                            });
                            
                            
                            
                        };
        
   </script>
        
    
</apex:page>

Controller for above page –

  public class ChartCreatorCls2 {

    
    public Integer openNotContacted {get;set;}
    public Integer workingContacted {get;set;}
    public Integer closedConverted {get;set;}
    public Integer closedNotConverted {get;set;}
    
    
    public chartCreatorCls2(){

        
        openNotContacted = 0;
        workingContacted =0; 
        closedConverted = 0;        
        closedNotConverted = 0;       
        generateData();
        
    
    }
    
    

   
   public void generateData(){
        AggregateResult[] groupedResults  = [SELECT Status, Count(Id) cnt FROM Lead group by Status];

        for (AggregateResult ar : groupedResults)  {
            if(ar.get('Status').equals('Open - Not Contacted'))
                openNotContacted = (Integer) ar.get('cnt');
            if(ar.get('Status').equals('Working - Contacted'))
               workingContacted = (Integer) ar.get('cnt');    
            if(ar.get('Status').equals('Closed - Converted'))
                closedConverted = (Integer) ar.get('cnt'); 
            if(ar.get('Status').equals('Closed - Not Converted'))
                closedNotConverted  = (Integer) ar.get('cnt'); 
             
        }
   } 
}

You can see this in action here.

Learn Salesforce1 Mobile Basics Using Trailhead

If you are new to Salesforce1 platform or want to know how you can learn Salesforce1 Mobile Basics this post is for you.

Salesforce.com has come up with a great learning module called Trailhead, announced in Dreamforce’14 which enables the developers of every level to brush up their skills.

My favorite module on Trailhead is Salesforce1 Mobile Basics.

This trailhead module have 5 steps which gives you basic know how of Salesforce1 Mobile App.

1.Getting Started with the Salesforce1 Mobile App.

This step covers the basic introduction of Salesforce1 Mobile and how to access it. Its all about getting familiar with the navigation of app.

Best thing is the video introduction given in this step.

2.Customizing Navigation

This step walks you through the navigation menu overview and then makes you understand how you can customize the navigation menu.

s1_navigation_ss

3.Customizing Compact Layout

This step is important one, this lets you understand compact layouts and how you can customize them or override the standard ones.

Basically compact layouts are the area which shows the key information related to a record.

compact_layout_before_after_2fer

4.Creating Global Publisher Actions

Global publisher actions lets you to create records quickly.

For example you want the users of your app to quickly create a Case without going into the navigation menu Global Publisher Actions will let users to do that directly from the action bar.

s1_actionbar

5.Creating Object-Specific Publisher Actions

The step lets you understand how you can create object specific quick actions (Publisher Actions).

For example, you want to create an Invoice Line Item when you are viewing an Invoice Record in this scenario you can use Object-Specific Publisher Actions.

Key Take Aways:

1.Customization of Salesforce1 Mobile Navigation

2.Customization of Compact Layouts

3.Publisher Actions

So try this out here – https://developer.salesforce.com/trailhead/module/salesforce1_mobile_app  

Force.com Winter 15 Visualforce Enhancements

So Force.com’s winter release has arrived with lots of new features. There are various new enhancements to Visualforce in this release that are very important for developers, so here are these

2014-09-09 17_23_00-Salesforce.com Winter ’15 Release Notes - salesforce_winter15_release_notes.pdf

1. Remote Objects for Visualforce are now generally available with this release, if you are not familiar about these you can follow up my previous post https://forceexperiment.wordpress.com/2014/03/12/visualforcce-remote-objects/

2. Standard Style Sheets are combined in Visualforce now, this will reduce the number of network connections made during the load of a single page and will greatly improve the performance of a page.The content of this combination is unchanged so you don’t need to worry about references to make and you don’t need to do anything to make use of this enhancement.

3. Preserve sObject Field Encryption in Visualforce Expression, In Visualforce pages set to API  32.0 or above expressions that points to encrypted sObject Fields will return encrypted values instead of plain values. Visualforce pages set to API version 31.0 or earlier continue to display decrypted values for encrypted sObject fields, except when displayed by using <apex:inputField> and <apex:outputField>

 

 

Just in 5 points. Why you should attend Dreamforce?

So here I am. Two year ago I started learning Salesforce in my college days and I was introduced to a grand event called Dreamforce at that time i used to watch live stream of keynotes. Since then It was one of my dreams to attend Dreamforce.

After a deep patience of 2 years , this year I am going to Dreamforce ! Yes  just after completing a one in Industry[Thanks to my employer for considering me :) ] .

Here through this blog post I am going to share my views just in 5 points . Why you should attend Dreamforce?

So let me start :

1, You will get to learn new things, choose your path if you are a developer then go for developer sessions and if you are more on admin side do attend the administrative sessions.

This time i have decided to give large part of my time to developer sessions, if you are looking to learn more and enhance your skills just look at these sessions

http://www.salesforce.com/dreamforce/DF14/sessions.jsp#?search=blank&role=Developer&product=blank&industry=blank   

2. The Keynotes , attend these to get idea about the vision and new products of Salesforce.

3. Meet your clients and far  friends that are generally at other side of your machine and have fun .:)

4.Its a great time to get trained and certified. Salesforce provides huge discounts on training and certification.

5.Attend cloud expo,get your hands on all the Salesforce products and solutions. You can interact with Salesforce products firsthand and hear success stories shared by salesforce.com customers. Stop by and check out the latest features, connected devices, and interactive demos.

 See you soon at Dreamforce !

Securing your force.com app

Securing your force.com app

Building an app on force.com is great. But when it comes to developing a secure force.com app its a bit different thing.

Following are the guide lines, tips and tricks that you can follow to ease your app to pass Force.com security review .

 

  1. Parameter Tampering Issue.

This happens when you are trying to send some value from visualforce page and the value is being used throughout the controller. When this being used with any query it may result into database tempering.

 

Example :

String vid = ApexPages.currentPage().getParameters().get(‘id’);

account acc =[select id,name from account where id=:vid limit 1];

Solution:

account acc =[select id,name from account where id=:ApexPages.currentPage().getParameters().get(‘id’) limit 1];

 

 

  1. Second Order SOQL and SOSL Injection.

SOQL injection involves taking user-supplied input and using thosevalues in a dynamic SOQL query. If the input is not validated, it may include SOQL commands that effectively modify the SOQL statement and trickthe application into performing unintended commands.

 

Solution 1.: Avoid using dynamic SOQL/SOSL queries

 

Solution 2:

 

Example :

 

Folio__cfo=[select id,name from Folio__c where

Transaction__r.Id=:sr.Transaction__r.Id AND id!=null LIMIT 1];

 

Can be changed into

 

Folio__cfo=[select id,name from Folio__c where Transaction__r.Id=:String.escapeSingleQuotes(sr.Transaction__r.Id) AND id!=null LIMIT 1];

 

 

 

 

  1. Queries with No Where or No Limit Clause

 

Apex has governor limits in place that limits the number of records that can be retrieved through a SOQL query. This issue says SOQLqueries in the apex code that does not have a WHERE clause nor uses the LIMIT clause to restrict the number of records retrieved.

 

Solution: Apply Limit , if not possible to use limits use Where clause.

The Where clause can be a null check on id field

 

accq=[select id,name from account where id!=null];

 

  1. Sharing with controller

 

By default Apex have capability to read and update all data and doesn’t cares about FLS,OWD or profile permissions. We must take care of all these from a developer’s perspective.If no sharing setting is defined on the controller this may give a security issue.

 

Solution:

 

Use “public with sharing class className”

 

  1. FLS Create/FLS Partial Create /FLS Update/ FLS Partial Update:

 

While creating/inserting/updating a record the Apex code must check if the user have sufficient privileges to insert/update the record.

 

 

Example:

 

OrderPayment__c op = new OrderPayment__c();

Payment_Amount__c=grandtotal;

Collection_Date__c=Date.valueOf(System.now());

Collection__c=true;

insert op;

 

Solution :

 

OrderPayment__c op = new OrderPayment__c();

Payment_Amount__c=grandtotal;

Collection_Date__c=Date.valueOf(System.now());

Collection__c=true;

 

if(Schema.sObjectType.OrderPayment__c.fields.Payment_Amount__c.isCreateable() && Schema.sObjectType.OrderPayment__c.fields.Collection_Date__c.isCreateable() && Schema.sObjectType.OrderPayment__c.fields.Collection__c.isCreateable() ){

insert op;

}

 

In Case of update

 

if(Schema.sObjectType.OrderPayment__c.fields.Payment_Amount__c.isUpdateable() && Schema.sObjectType.OrderPayment__c.fields.Collection_Date__c.isUpdateable() && Schema.sObjectType.OrderPayment__c.fields.Collection__c.isUpdateable() ){

Update op;

}

 

 

 

  1. Test_Methods_With_No_Assert :

Proper assert statements are advised, at least 10 to 20 in a single test class.

 

 

 

  1. For Store XSS (Cross Site Scripting )Issue

About: Cross-site scripting is a vulnerability that occurs when an attacker can insert unauthorized JavaScript, VBScript, HTML, or other active content into a web page viewed by other users.

This issue raised when controllervariable are being used in JavaScript / JQuery .

Example:

Public class mycontroller{

String myval{get;set;}

Public mycontroller(){

Myval=’Hello Page Loaded’;

}

}

//On page

<Script>

Varscriptvar = ‘{!myval}’;

Alert(scriptvar);

</script>

 

POSSIBLE SOLUTION:

Varscriptvar = ‘{!JSENCODE(myvar)}’;

Happy Secure Coding :)

10 Days of 10 Dollar Ebooks

10yr-webbanner2As you all know I have authored two ( one to get released in September 2014)  books on Salesforce.com, by Packt Publishing . You can get my book ( or any other of your choice) in only $10 . Yes! its true.

 

To know more about this offer go on this link http://bit.ly/1sWO4Qv

 

 

Why Salesforce1 week matters for Students in India?

s1devweekbanner

Salesforce1 week is series of events that is going to take place around the world from 27 April to 3 May.In this week Students and Developers in India will get a chance to be Introduced to the new platform “Salesforce1″ by Salesforce.com which was announced last year in Dreamforce’13 .In India, students hardly get chance to learn mobile platform development as it needs high investment of time and money along with  regular studies.

If you are a student or a fresh developer who is hungry to  learn mobile development, Salesforce1 week is for you. Salesforce1 enables you to build mobile apps faster by using  JavaScript, HTML5 and CSS knowledge without worrying about the front end at all ! . Yes! mobile development without knowledge of Objective-C or Android SDK. Just log-in and start developing in the all new cloud platform.

If you have some experience on Apex or Visualforce or tried it as a experiment in your college to build an app that will add into this as Salesforce1 platform is backed with Force.com Platform.

If you want to take a hand-on look over Salesforce1, it  is near you! just register  for the nearest local developer user group here :

https://developer.salesforce.com/developer-week

s1joinus

If you are Student in India and willing to participate just join us  in Bikaner(Rajasthan).

Salesforce1 Developer Week Comes for Students in India

Saturday, May 3, 2014, 2:00 PM

Acme Embedded Technologies
3/503 Mukta Prasad Nagar

10 Students Attending

Join us for Salesforce1 Developer Week! This is a global event where Salesforce Developer Groups across the world will be meeting to talk Salesforce1. We are proud to be part of the 1.5 Million developers in the Salesforce Developer Community and are celebrating by taking part in Salesforce1 Developer Week on 3 May 2014. Join us for a hands-on look…

Check out this Meetup →

Don’t forget to grab a T-Shirt(while supplies last).We will have new books and swag for you and plenty of resources for you to work on your very first Salesforce1 app.

See you soon !

 

 

 

Follow

Get every new post delivered to your Inbox.