Archive for Visualforce Category Feed

Static Maps in Visualforce

Geopointe includes a Visualforce Component that allows you to create Static Maps on your Visualforce Pages.  A Static Map results in an image file (png, jpg or gif) that you can configure and place on your Visualforce Pages or embedded in Page Layouts.  The nice thing about Static Maps is that they do not slow down your pages from loading.  Rather, you are using the component to intelligently build an image URL.

Geopointe comes with some examples with the “embeddedMap…” pages.  These are pre-configured Visualforce Pages using the static map component to show you the location of a record.

We added a Static Map page on the website to act as the official documentation home for this functionality.  With Geopointe installed, you also get a documentation in your Component Reference regarding the geopointe:staticMap component.

Below are some examples showing off the functionality.

<geopointe:StaticMap mapProvider=”goog” width=”500″ height=”300″ useIconLabels=”true” iconColor=”green” locationIDs=”a07A000000ATQjhIAH,a07A0000006O0qc,a07A0000006NKq2,Evanston|IL,OakPark|IL”/>

<geopointe:StaticMap mapProvider=”mq” width=”800″ height=”200″ useIconLabels=”true” iconColor=”purple” locationIDs=”a07A000000ATQjhIAH,a07A0000006O0qc,a07A0000006NKq2,Evanston|IL,OakPark|IL” mapType=”hyb”/>

Comments off comments feed

Endpoint for Debugging HTTP Callouts

When I use Apex to make HTTP callouts to other web services (e.g. Google Checkout, PayPal, Shopify, MapQuest, etc), it is often a game of trial and error to get things right. Oftentimes, I get errors from the recipient that my request is invalid and, for the life of me, I cannot see the problem.

To help me debug, I keep a test endpoint in my DE orgs that I make callouts to in order to get a good look at what I am sending. It’s a simple Visualforce page that I expose over Sites to act as a fake endpoint while I do development. When developing a callout, I will often use this endpoint in the beginning to organize the parameters and then periodically throughout development to debug.

The page is simple.

<apex:page controller="Endpoint_Controller" cache="false" showHeader="false" sidebar="false" action="{!init}">
<pre>
<apex:outputText escape="false" value="{!debugInfo}"></apex:outputText>
</pre>
</apex:page>

Depending upon my need, I will add/remove the action=”{!init}” parameter from the page because I have it in there to write the {!debugInfo} variable to a Task for later review (kind of like using a task as a mini-debug log). I don’t always need it turned on, so that parameter comes and goes.

My controller, with test method, is:

public without sharing class Endpoint_Controller {

public String debugInfo {get; set;}{

debugInfo = '';

if (ApexPages.currentPage() != null){
// Incoming Headers
debugInfo += '\n***ALL INCOMING HEADERS ***\n';
for (string key: ApexPages.currentPage().getHeaders().keySet()){
if (ApexPages.currentPage().getHeaders().get(key) != null){
debugInfo += key + ' = ' + ApexPages.currentPage().getHeaders().get(key) + '\n';
}
}

// Incoming Parameters
debugInfo += '\n***ALL INCOMING PARAMETERS ***\n';
for (string key: ApexPages.currentPage().getParameters().keySet()){
if (ApexPages.currentPage().getParameters().get(key) != null){
debugInfo += key + ' = ' + ApexPages.currentPage().getParameters().get(key) + '\n';
}
}

// Other Page Reference Stuff
debugInfo += '\n***OTHER PAGE REFERENCE INFO ***\n';
debugInfo += 'Anchor: ' + ApexPages.currentPage().getAnchor() + '\n';
debugInfo += 'URL: ' + ApexPages.currentPage().getUrl() + '\n';
}

}

public Endpoint_Controller(){}

public PageReference init() {

Task t = new Task();
t.Subject = 'Endpoint Invoked';
t.Description = debugInfo;
t.ActivityDate = Date.Today();
insert t;

return null;

}

static testMethod void Endpoint_Controller_test() {

// Set the page reference and pass through some parameters
PageReference thePage = Page.Endpoint;
thePage.getParameters().put('param1','1');
thePage.getParameters().put('param2','2');
Test.setCurrentPage(thePage);

// Run the init function to have it handle the web to lead submission
Endpoint_Controller ep = new Endpoint_Controller();
ep.init();
}
}

What does it do? Well, it simply gathers up all the headers, page parameters and other useful info and outputs it in a readable way. I sent a test request to one of my pages and this is what the page produced (and logged to a task in that org):

***ALL INCOMING HEADERS ***
Accept = application/xml;charset=UTF-8
Cache-Control = no-cache, max-age=0
CipherSuite = RC4-MD5 TLSv1 128-bits
Content-Type = application/xml;charset=UTF-8
Host = testorg.secure.force.com
Pragma = no-cache
SFDC_STACK_DEPTH = 1
User-Agent = SFDC-Callout/18.0
X-Forwarded-For = 10.226.8.134
X-Salesforce-Forwarded-To = na7.salesforce.com
X-Salesforce-SIP = 204.14.234.8

***ALL INCOMING PARAMETERS ***
param1 = value1
param2 = value2
param3 = value3

***OTHER PAGE REFERENCE INFO ***
Anchor: null
URL: /apex/Endpoint?param1=value1&param2=value2&param3=value3

I also construct debugInfo variables in my Production system to capture the receipt of incoming calls to Sites pages I have exposed (e.g. Google Checkout talks to my Salesforce org via a Sites page). I will usually have a custom setting that I can turn on/off so I have an easy way to turn off the creation of those activities.

Comments (6) comments feed

Invoking Apex from a Button (JS –> Apex Web Service)

In January I posted about how to invoke Apex from a Custom Button using a Visualforce Page.  It has been a popular post and is a topic which is of interest to many developers.  I wanted to draw your attention to another post.  Sam Arjmandi, from Salesforce Source, posted about how to invoke Apex from a button by calling the Apex directly from JavaScript whereby the Apex must be available as a web service.

Both methods work just fine and there is a choice of approach.  Recently, my personal choice for implementing this kind of functionality is to use Sam’s approach and it’s mostly due to the user experience.  Using this approach, the code is called directly and there is no time spent by the browser needing to load a new blank page only to return to the same page.  It works much more seamlessly.  You also get the benefit of having less objects to develop (no VF page) and tie together.  The only real downside (outside of requirement-specific ones) is that Salesforce won’t bark at you if you delete the web service class because it doesn’t know that the web service is tied to JavaScript.  If your Apex Code is a controller for your VF page, Salesforce will protect you from deleting it accidentally.

Comments (2) comments feed

Invoke Apex from a Custom Button using a Visualforce Page

I have had a few occasions where I wanted to invoke Apex Code by clicking a button on a Page Layout.  Everywhere I looked, it always said I needed to have the button call an s-Control, which would then invoke Apex Code that’s written as a Web Service.  I am doing my best to avoid using s-Controls and figured there had to be a way using Visualforce instead of an s-Control.  The key was the action attribute on the <apex:page tag.

Suppose you wanted to add a button on your Opportunity page that automatically did something when you pushed the button.  You can’t execute Apex Code directly from a button.  You need something in the middle.  Let’s try to do it with a Visualforce page instead of an s-Control.

Controller

The controller has the main method in it to be executed. This method returns a PageReference (very important). Note that the method does not need to be a webservice.


public class VFController {

	// Constructor - this only really matters if the autoRun function doesn't work right
	private final Opportunity o;
	public VFController(ApexPages.StandardController stdController) {
		this.o = (Opportunity)stdController.getRecord();
	}
	
	// Code we will invoke on page load.
	public PageReference autoRun() {

		String theId = ApexPages.currentPage().getParameters().get('id');

		if (theId == null) {
			// Display the Visualforce page's content if no Id is passed over
			return null;
		}

		for (Opportunity o:[select id, name, etc from Opportunity where id =:theId]) {
			// Do all the dirty work we need the code to do
		}

		// Redirect the user back to the original page
		PageReference pageRef = new PageReference('/' + theId);
		pageRef.setRedirect(true);
		return pageRef;

	}

}

Visualforce

The Visualforce page is very simple. You really don’t need anything in there except the action parameter invokes code (the autoRun method) that runs before the page is initialized. That method returns a Page Reference that redirects the user back to the original page.


<apex:page standardController="Opportunity" 
 extensions="VFController" 
 action="{!autoRun}"
>
  <apex:sectionHeader title="Auto-Running Apex Code"/>
  <apex:outputPanel >
      You tried calling Apex Code from a button.  If you see this page, something went wrong.  You should have 
      been redirected back to the record you clicked the button from.
  </apex:outputPanel>
</apex:page>

Custom Button

All we need now is a custom Opportunity button. Because we used the Opportunity standard controller in the Visualforce page, we can simply have the button point to a the page we created.

Add that button to your Page Layout and all should work swimmingly.

Comments (33) comments feed

Campaign Member Summary using Google Charts

I was inspired by Sam Arjmandi’s post about embedding Google Charts into VisualForce pages.  I have a use case that requires a little different approach.  I needed to get a quick view of the Campaign Member Statuses for a Campaign.  I went ahead and started with Sam’s example and tweaked it for my purpose.  Here’s the result!

It’s an embedded VisualForce component in the Results section of my Campaign page.  It shows a quick count for each Member Status that is being used.  To get it going in your org, here’s what you need.

VisualForce Page

It’s a simple page containing 1 DIV so that I can set the background color to match that of a Page Layout.  Other than that it’s just an image returned from Google Charts.  There is a lot of flexibility with Google Charts.  Therefore, I made it so most of the URL can be tweaked in VisualForce. Only the data values and its labels come from the controller.  This is nice because you can edit VF in a production org, but you can’t edit Apex. This will let you change things like width, height, chart colors, chart type, etc.


<apex:page standardController="Campaign" extensions="VFController_CampaignMemberStatusChart">
    <style>
        #DIV_Container{padding: 0; margin: 0; background-color: #F3F3EC;}
    </style>
    <div id="DIV_Container">
        <!-- See http://code.google.com/apis/chart/ for more info on customizing the chart -->
        <apex:image url="http://chart.apis.google.com/chart?cht=p3&chs=1000x125&chf=bg,s,F3F3EC&chco=CC9933{!chartData}"></apex:image>
    </div>
</apex:page>

Apex Controller
The controller is an extension of the Campaign standard controller. This is so the VisualForce page becomes an option to include on a Campaign Page Layout. The method that does all the work is getChartData. It gets the available Campaign Member Statuses and then queries the Campaign Member object for that Campaign and adds a data/label value for each one. It then returns a query string that you include into the Image src on the Visual Force page.

I am bad at Test classes, but there is 1 in there that passes and should be good enough. This functionality is pretty harmless.

public class VFController_CampaignMemberStatusChart {

	private final Campaign camp;

	public VFController_CampaignMemberStatusChart(ApexPages.StandardController stdController) {
		this.camp = (Campaign)stdController.getRecord();
	}
	
	public String getChartData() {
		
		// The list of chart items	
		List<ChartDataItem> items = new List<ChartDataItem>();
		
		// List of valid Campaign Member Statuses for the Campaign
		List<CampaignMemberStatus> list_cms = [select Id, Label from CampaignMemberStatus where CampaignId = :camp.id];
		
		// Loop through each Campaign Member Status, get a count of Campaign Members and add it to the items list 
		for (CampaignMemberStatus cms:list_cms) {
			integer Count = [select count() from CampaignMember where CampaignId = :camp.id AND Status = :cms.Label];
			if (Count > 0) {
				items.add(new ChartDataItem(cms.Label, Count.format()));
			}
		}
		
		// Initialize Strings
		String chd = ''; // Data
		String chl = ''; // Labels
	
		for(ChartDataItem citem : items) {
			chd += citem.ItemValue + ',';
			chl += citem.Label + ' (' + citem.ItemValue + ')|';
		}
		
		//remove the last comma or pipe
		if (items.size() > 0) {
			chd = chd.substring(0, chd.length() -1);
			chl = chl.substring(0, chl.length() -1);
		}
		
		// We are only returning the values and labels. The rest of the URL string is in the VF page
		String result = '&chd=t:' + chd + '&chl=' + chl; // &chl returns with labels pointing to pie pieces
		//String result = '&chd=t:' + chd + '&chdl=' + chl; // &chdl returns with labels in a legend
		
		return result;
	}

	// Class holding each chart data item
	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;
		}
	}
	
	static testMethod void testVFController_Sidebar_Summary() {
		
		// Create Campaign
        Campaign c = new Campaign();
        c.Name = 'Test Campaign';
        insert c;
        
        // Create Lead
        Lead l = new Lead();
        l.LastName = 'Last Name';
        l.Company = 'Company';
        insert l;
        
        // Create Campaign Member
        CampaignMember cms = new CampaignMember();
        cms.CampaignId = c.id;
        cms.LeadId = l.id;
        insert cms;
		
		test.startTest();
		
		ApexPages.StandardController sc = new ApexPages.StandardController(c);
		VFController_CampaignMemberStatusChart controller = new VFController_CampaignMemberStatusChart(sc);
		String s1 = controller.getChartData();
		
		test.stopTest();
	}

}

Page Layout
When you add it to the Page Layout, make the height of the component the same as the height you specified in the VF page’s image src for Google Charts. In this example, it’s 125. Doing this will ensure the background colors match your Page Layout.

Comments (7) comments feed

« Previous entries