Treat the Salesforce VisualForce page as an external widget

advertisements

I want to turn a Salesforce VisualForce Page into a widget for my corporate website. I want to access the widget using server side code and a service account. I'll cache, style, and output the widget html in my web page. I know I could just reproduce the widget with server side code and several API calls but I like the idea of being able to manage the widget in Salesforce and having the widget available for reuse elsewhere within Salesforce.

In this case, the widget is going to report on the current leaders in a contest we're tracking in Salesforce. I plan on reusing this approach for other "mini-report widgets." This is a very useful pattern. I'm sure somebody out there has elegantly solved it already. My searches have come up empty so far.

I was trying to avoid Sites because the user hitting our corporate website does not have a user account in Salesforce (now I'm thinking about a Site that allows anonymous access).

My first attempt looked like this (inspired by SSO to Self Service Portal):

var ws = new sfapi.SforceService();
var lr = ws.login(Secrets.salesforce_user_name, Secrets.salesforce_password);
string url = "https://na6.salesforce.com/apex/mywidget?sessionId=" + lr.sessionId;

I tried ?sid and ?csssid too, but urls like these just bounce me to the Salesforce login page:

https://na6.salesforce.com/apex/mywidget?sessionId=00D3000000myorg!my-session-id-from-logging-in-with-the-api-8_i2fX_9wnYdwnwatGVuX6jGjmVmnv50j78yfGF1aKHdoDFtIx_J9

I could probably use standard screen scraping techniques to post to the standard Salesforce login form with a username and password and then pull the /apex/mywidget page. But that feels clumsy and unsupported.

Now I'm thinking about making a new Site that is publicly available just to expose my widget. At least I could screen scrape that page with confidence.

Any help is appreciated. I'm going to play with the anonymous sites approach for now.


Rendering the data into a Visualforce page, then screenscraping it seems a bit brittle, not to mention inefficient - there is a better way...

Define an Apex REST web service, then you can easily invoke that web service from your server side code, and still render it in Visualforce if you need to - you can call an Apex REST method like any other Apex method.

Here's a sample REST web service. I'm just returning a Map<String,String> here, but you can return any primitive type, any sObject or a List or Map of primitives or sObjects (as long as the Map has String keys) - see the Apex REST Web Services docs

@RestResource(urlMapping='/MyResource/*')
global with sharing class MyRestResource {
    @HttpGet
    global static Map<String,String> getResource() {
        Map<String,String> result = new Map<String,String>();

        result.put('key1', 'value1');
        result.put('key2', 'value2');

        return result;
    }
}

Here's some PHP that calls it (I'll assume you have an access token (aka session ID) and instance URL):

$url = "$instance_url/services/apexrest/MyResource";
$curl = curl_init($url);

curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER,
  array("Authorization: OAuth $access_token",
    "Content-type: application/json"));

$json_response = curl_exec($curl);

$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

if ( $status != 200 ) {
    die("Error: call to URL $url failed with status $status, ".
      "response $json_response\n");
}

$response = json_decode($json_response, true);

echo "The service says ".$response['key1'].' '.$response['key2']."\n";

curl_close($curl);

And here's a Visualforce page, if you want to show the same data

<apex:page controller="TestRestController">
  <p>Controller says {!result}</p>
</apex:page>

The page controller:

public class TestRestController {
    public String getResult() {
        Map<String, String> result = MyRestResource.getResource();

        return result.get('key1') + ' ' + result.get('key2');
    }
}