Showing posts with label Google Apps. Show all posts
Showing posts with label Google Apps. Show all posts

How to Easily Bring Graphical Charts into your CRM?

There is no doubt that nothing communicates better and faster than graphics with users. It has been always the case that users fail to utilize software solutions merely because the information is not delivered to them intuitively, comprehensively and graphically.

Managers often require summarized business critical information quick and easy to grasp. Utilizing charts in Web Applications has been a challenge for many of us over the past two decade and now it is all much easier than ever!

Google Chart is an excellent tool available for free! It is actually very easy to work with and practical.

In this article I will demonstrate how you can utilize this outstanding component to bring new life to your Salesforce pages!

Example: I have a requirement to create a Visualforce page to view a pie chart that shows number of Accounts in our organization divided by their Types.


Here is how it works:
The Google Chart API requires me to pass the chart information via URL parameters and then in return Google will send me an image in PNG format. Then all I need to do is to view the image using the HTML img tag!

Ingredients:
Chart Server URL: http://chart.apis.google.com/chart
Parameters List:
  • chs: is in fact the chart size in pixels ex: 300x200
  • chd: you will need to pass the chart data using this parameter. ex: t:60,40
  • cht: chart type, ex: p3
  • chl: chart items' label, ex: Data 1|Data 2
The above are all basic required parameters in order to create charts. So there is more to it if you are interested.

So in order to utilize this component I create a Visualforce page as follows:



<apex:page controller="googleChartCon" tabStyle="Account">
<apex:sectionHeader title="Accounts by Type"></apex:sectionHeader>
<apex:image url="{!chartData}"></apex:image>
</apex:page>


This page only contains an apex:image component which talks to the controller to obtain it's image URL.

The URL is where the magic happens so we will need to focus on that.

In order to do this I first need to get a list of Account's "Type" picklist values and then
in my custom controller, I create an internal Apex class to save the chart's data while I am running through Account's records and find out how many of which type we have in our database.




public class googleChartCon {
private String chartData;

public String getChartData()inter
{
return chartData;
}

public googleChartCon()
{
//obtain a list of picklist values
Schema.DescribeFieldResult F = Account.Type.getDescribe();
List<Schema.PicklistEntry> P = F.getPicklistValues();
//where chart data should be stored.
List<ChartDataItem> items = new List<ChartDataItem>();

//iterate through each picklist value and get number of accounts
// I wish we could do GROUP BY in SOQL!
for(Schema.PicklistEntry pValue : P)
{
integer Count = [select count() from Account where Type = :pValue.getValue() limit 10000];
if (Count > 0)
items.add(new ChartDataItem(pValue.getValue()+ '-['+ Count.format() + ']' , Count.format()));
}

//Prepare the chart URL
String chartPath = 'http://chart.apis.google.com/chart?chs=600x200&cht=p3';
chartData = chartPath + getChartData(items);
}

private String getChartData(List<ChartDataItem> items)
{
String chd = ''; //23,34,56
String chl = ''; //Hello|World

for(ChartDataItem citem : items)
{
chd += citem.ItemValue + ',';
chl += citem.Label + '|';
}
//remove the last comma or pipe
chd = chd.substring(0, chd.length() -1);
chl = chl.substring(0, chl.length() -1);

String result = '&chd=t:' + chd + '&chl=' + chl;
return result;
}

public class ChartDataItem
{
public String ItemValue
{
get;
set;
}

public String Label
{
get;
set;
}

public ChartDataItem(String Label, String Value)
{
this.Label = Label;
this.ItemValue = Value;
}


}

}


For some security might be a concern sending the data via URL parameters.
Up to this point, I have not been able to confirm if Google is officially supporting SSL connections to rectify this problem. However the following seems to be fine:

https://www.google.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World


In this article I have barely scratched the surface of Google Charts API, if you are interested to learn more about it go to http://code.google.com/apis/chart/

Bringing Google Calendar into Salesforce CRM pages


Since the very first time I saw Google Calendar' API, I wondered if it can be used for any bizarre or genius applications (having a sense of a-tool-for-public) or not. For example I really liked to be able to load Google Calendar into my page and then using the API add as many events as I desire on it from my own database without any authentication requirement and just for the sake of showing the data to user, but I have not been able to get it work in that order so far.

Yet there are a lot of other benefits that Google Calendar can bring along, in terms of sharing events, publishing, flexibility and integration with other portals/applications in the cloud.

One of things you might find useful is the capability of sharing a Google public calendar with your Salesforce CRM users.

At first I tried to publish my own personal calendar into Salesforce, and I was successful to some extent. I could see the calendar is being shown properly and I can switch between the weekly, monthly, etc views. All nice, but I noticed that the Calendar is empty and my events are missing.

In Google Calendar's documentation is not mentioned that you should use a public calendar to be able to share it in other websites or maybe that goes without saying! So I initially thought that any Calendar could be shared and as long as my event is set as public people should be able to see them.

It was more like one of those scenarios where you show a button on your page to a user but say "Do not click on it, ok my boy?"

Anyway, in order to publish the calendar on your Salesforce CRM, you should first think about where you want to place it and you may require to create a new Visualforce page before copying the calendar HTML code into it.

In order to get the calendar's HTML code go to your Google Calendar page and either create a new Calendar to publish it as public calendar or if you already have one then click on settings (on the left side of the screen).

Once you have your public calendar ready go to its "details" page and look for a spot that says "Embed This Calendar", copy the code and paste it into your Visualforce page. That's all!



<apex:page tabStyle="Campaign">
<apex:form >
</apex:form>
<iframe src="http://www.google.com/calendar/embed?src=6lectl1l76jrmul8st527rbq6c%40group.calendar.google.com&ctz=America/New_York&color=%23336699;" style="border: 0" width="800" height="600" frameborder="0" scrolling="no"></iframe>
</apex:page>



And you will see something like below in your page:

How to create a Google Map S-Control?


In many cases you may need to create a control that utilizes the Google Map APIs to show location of a client, client's offices, driving directions, etc to the user.

In this example, I will demonstrate how you can develop a s-control to show driving directions from the User's location to any Account that the User is viewing.

In order to accomplish this I initially thought of a Visualforce Page which hosts the Google Map API and I could use the all-ready standard components with original Salesforce look and feel.

So I tried and I tired and did not seem to get anywhere since Google was not recognizing my key, so Google's authentication for my website let's say: http://na5.salesforce.com/ would fail each time I tried to view my VF page. Finally I decided to do this in a S-Control and then call the S-Control in VF page.

If you need more information about how you can call a S-Control in your VF pages click here.

So first let's see what we want to achieve:
The S-Control reads the URL parameters and looks for two parameters to be passed to it:
  • from: the location where the sales rep (user) is located.
  • to: The target location, could be the Account's address
Once the S-Control is called and the two parameters passed via URL, the S-Control then in turn calls Google's APIs to view the map and show the directions information.

Now let's first create a new S-Control:
  1. Click on "Setup" which is located on the top right corner of the force.com page.
  2. Expand "Develop" item and click on "S-Controls"
  3. Hit "New Custom S-Control"
  4. Provide a Label "Map Directions" and a description is you wished to
  5. The Type should be HTML since we want to create a HTML, Google API mash up
  6. Now it is time to enter the code for the S-Control


<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Google Maps JavaScript API Example: Advanced Directions</title>
<link href="/dCSS/Theme2/default/common.css" type="text/css" media="handheld,print,projection,screen,tty,tv"
rel="stylesheet">
<link href="/dCSS/Theme2/default/custom.css" type="text/css" media="handheld,print,projection,screen,tty,tv"
rel="stylesheet">

<script src=" http://maps.google.com/?file=api&amp;v=2.x&amp;key={YOUR KEY}"
type="text/javascript"></script>

<script src="/js/functions.js" type="text/javascript"></script>

<script type="text/javascript" src="/soap/ajax/13.0/connection.js"></script>

<style type="text/css">
body {
font-family: Verdana, Arial, sans serif;
font-size: 11px;
margin: 2px;
}
table.directions th {
background-color:#EEEEEE;
}

img {
color: #000000;
}
</style>

<script type="text/javascript">

var map;
var gdir;
var geocoder = null;
var addressMarker;
var dirFrom = '{!$Request.from}';
var dirTo = '{!$Request.to}';
var mapLocale = ""
var SControlID = '{!$Request.lid}';
var SFrameIC = '{!$Request.ic}';


function initValues()
{
mapLocale = "en_US";

setInputFields(dirFrom,dirTo);
}

function setInputFields(from, to)
{
window.document.getElementById("fromAddress").value = from;
window.document.getElementById("toAddress").value = to;
}


function initialize()
{
initValues();

if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map_canvas"));
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
gdir = new GDirections(map, document.getElementById("directions"));
GEvent.addListener(gdir, "load", onGDirectionsLoad);
GEvent.addListener(gdir, "error", handleErrors);

setDirections(dirFrom, dirTo, mapLocale);
}
}

function setDirections(fromAddress, toAddress, locale) {
if ((fromAddress) && (toAddress))
gdir.load("from: " + fromAddress + " to: " + toAddress,
{ "locale": locale });
}

function handleErrors()
{
if (gdir.getStatus().code == G_GEO_UNKNOWN_ADDRESS)
alert("No corresponding geographic location could be found for one of the specified addresses. This may be due to the fact that the address is relatively new, or it may be incorrect.\nError code: " + gdir.getStatus().code);
else if (gdir.getStatus().code == G_GEO_SERVER_ERROR)
alert("A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known.\n Error code: " + gdir.getStatus().code);

else if (gdir.getStatus().code == G_GEO_MISSING_QUERY)
alert("The HTTP q parameter was either missing or had no value. For geocoder requests, this means that an empty address was specified as input. For directions requests, this means that no query was specified in the input.\n Error code: " + gdir.getStatus().code);

// else if (gdir.getStatus().code == G_UNAVAILABLE_ADDRESS) <--- Doc bug... this is either not defined, or Doc is wrong
// alert("The geocode for the given address or the route for the given directions query cannot be returned due to legal or contractual reasons.\n Error code: " + gdir.getStatus().code);

else if (gdir.getStatus().code == G_GEO_BAD_KEY)
alert("The given key is either invalid or does not match the domain for which it was given. \n Error code: " + gdir.getStatus().code);

else if (gdir.getStatus().code == G_GEO_BAD_REQUEST)
alert("A directions request could not be successfully parsed.\n Error code: " + gdir.getStatus().code);

else alert("An unknown error occurred.");

}

function onGDirectionsLoad(){
// Use this function to access information about the latest load()
// results.

// e.g.
// document.getElementById("getStatus").innerHTML = gdir.getStatus().code;
// and yada yada yada...
}
</script>

</head>
<body onload="initialize()" onunload="GUnload()">
<div style="background-color: #CCCCCC">
<form action="#" onsubmit="setDirections(this.from.value, this.to.value, this.locale.value); return false">
<table>
<tr>
<th align="right">
From:&nbsp;</th>
<td>
<input type="text" size="25" id="fromAddress" name="from" value="" /></td>
<th align="right">
&nbsp;&nbsp;To:&nbsp;</th>
<td align="right">
<input type="text" size="25" id="toAddress" name="to" value="" /></td>
<th align="right">
Language:&nbsp;</th>
<th align="right">
<select id="locale" name="locale">
<option value="en" selected="selected">English</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="ja">Japanese</option>
<option value="es">Spanish</option>
</select>
</th>
<td>
<input name="submit" type="submit" value="Get Directions!" class="button" /></td>
</tr>
</table>
</form>
</div>
<div style="border-width: 1px; border-color: #000000; border-style: solid;">
<div style="overflow: auto; width: 99.5%; height: 380px">
<table class="directions" style="width: 100%; height: 100%">
<tr>
<td valign="top" style="width: 275px;">
<div id="directions" style="width: 275px; background-color: #ffffff;">
</div>
</td>
<td valign="top">
<div id="map_canvas" style="width: 100%; height: 375px; background-color: #ffffff;">
</div>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>


If you would like to know how you can add your VF page which contains the S-Control to your Account's page click here.