A template for developing on the Salesforce Canvas platform in Javascript.
CSS JavaScript HTML PHP

README.md

Canvas Starter Kit

This kit is designed for JavaScript developers who want to build a Salesforce app using the Force.com Canvas Framework. It can serve as starting point or template for building your own Canvas app and publishing it as a Managed Package on the AppExchange.

We've been there!

This kit is the result of our journey bringing our DayBack Web App to the Salesforce AppExchange. We learned a lot during this experience, some easy things, some harder things, and wanted to share this with people who are where we were. We also wanted to provide a simpler "Hello World" Experience for prospective Canvas users. The Java example provided by Salesforce is very good, but a little complicated for JavaScript folks (like us) who are maybe a little more comfortable with a simple PHP page than a full Java web app.

Help us out!

We are very pleased with how we incorporated our app using the techniques in this kit, but we're sure there are better methods we missed (or mis-understood), so please contribute to improving this kit.

What's in the kit?

Package Components:
  • Visualforce Code for creating a Canvas app in a VF page
  • Static Resource Javascript that provides:
    • Resizing your Canvas app within the tab
    • Navigation in Salesforce from the Canvas app
    • A template for publishing and subscribing to events between the VF page and the Canvas app
    • Functionality across VF, Lightning and SF1
  • Static Resource CSS for optimizing the Visualforce page in the tab for VF, Lightning and SF1.
  • Apex Classes that allow an org to use its own Static Resource in an installed Managed Package.
App Components:
  • The Salesforce Canvas Javascript SDK
  • A basic PHP page for authenticating your Canvas app with Salesforce
  • A basic OAuth.html page for when users must self-authorize
  • A basic callback.html page for OAuth handling
  • Simplified Javascript functions for handling
    • OAuth
    • CRUD with Salesforce data
    • Publishing and subscribing to events between the VF page and the Canvas app
  • A sample Angular app with simple examples of the functionality for re-engineering
Managed Package:

We put together a Managed Package of the Sample Angular app with all of the above components so you can see how it works once completed. We wanted to provide this as an Unmanaged Package, but Connected/Canvas apps are not permitted in Unmanaged Packages. The Managed Package Components are all in this repository and you'll find step-by-step instructions, below for re-building the App that you can turn into your own Managed Package. Here are the install links for the Managed Package:

Building the Kit in Your Org

A Hosted App

The idea behind Canvas is that you have an existing Web App you'd like to bring into Salesforce, so presumably your app is currently already hosted. Salesforce does require https for your app to work in Canvas. You can get around this a little bit in development by making a browser exception to the PHP endpoint, but not with the OAuth callback endpoint. If you are starting from the very beginning, then Heroku and Appfog are excellent services for hosting your app, and Salesforce provides a Heroku Quickstart path for developers.

The only server technology this kit requires is PHP. Once your hosting is set up, deploy the whole kit, except for the packageComponents folder. You can now set up the components in your Salesforce Developer Org.

Adding the Package components

You will need to do this in a Salesforce Development Org. Only Development orgs can create Canvas apps and Managed Packages. We recommend going ahead and setting up a namespace prefix for your org now, if you don't already have one. You can't do Unmanaged Packages with Canvas, and you need the prefix for the managed one.

Please follow the following steps in order to re-create the kit app in your org.

  1. Set Up the Canvas App

    1. Go To SetUp / Create / apps
    2. Scroll Down to Connected Apps and click New
    3. Name your App and fill in the other required fields in the top section
    4. Enable OAuth Settings
      1. For the Callback URL specify https://oauth/callback.html
      2. Enable OAuth Scopes. If you're not sure what to do here, we recommend starting with Access and manage your data and Access your basic information
    5. Skip down to the Canvas App Settings and check Force.com Canvas
      1. For the Canvas App URL, enter https://php/canvas.php
      2. For Access Method, choose Signed Request (POST)
      3. For Locations, choose (at least) Visualforce Page
    6. Click Save
    7. You will now be brought to the Detail Page. From here please copy the following three values. We need to add these to our PHP pages to allow the handshaking between our app and Canvas:
      1. Consumer Key
      2. Consumer Secret (You need to click to reveal it)
      3. Callback URL
  2. Add App Consumer Data to PHP Pages

    1. In the Hosted app find the canvas.php and the consumerData.php pages in the app's php folder.
    2. In the canvas.php page enter the consumer secret on line 10 in the spot reserved by <consumer secret for your connected/canvas app>.
    3. In the consumerData.php file enter your consumer key on line 2 in the spot reserved by <consumer key for your connected/canvas app>.
    4. In the consumerData.php file enter your callback URL on line 3 in the spot resrerved by <callback url for your connected/canvas app>.
  3. Create the Custom Setting

    1. Go To SetUp/Develop/Custom Settings
    2. Click New
    3. Name the Custom Setting Definition alternateJavascript
    4. Setting Type should be Hierarchy
    5. Visibility should be public
    6. Description should be "The name of an alternate javascript file for use by the canvas visualforce page."
    7. Click Save
    8. Now click New in the Custom Fields section
    9. Name the Field Resource Name
    10. Set the Type to Text and give it a length of 100
    11. Click Save
  4. Upload the Static Resources

    1. Go To SetUp/Develop/Static Resources
    2. Click New
    3. Name the Static Resource canvasStatic and upload the canvas-static.js file from the packageComponents/staticrecources/ folder
    4. Find the styles folder in the same packageComponents/staticrecources/ folder and zip it
    5. Once the styles folder is zipped create a new static resource named style_reources and upload the zipped folder to this resource.
  5. Create the Apex Classes

    1. Go To SetUp/Develop/Apex Classes
    2. Click New
    3. Paste in the contents of the AlternateResource.apex file
    4. Click Save
    5. Managed Packages require 75% Testing Coverage, so repeat the above steps for the AlternateResourceTest.apex file for this coverage.
    6. You can then click Run Test from the Test Class. (Hopefully it passes!)
    7. If you've set up your development org prefix, then add it to the appropriate spot in both classes.
      1. In AlternateResource.apex, change lines 7 and 12 to pre=''
      2. In AlternateResourceTest.apex do the same for lines 13 and 23
    8. Run the test again (just to be sure!)
  6. Create the Visualforce page

    1. Go To SetUp/Develop/Visualforce Pages
    2. Click New
    3. Name the page Canvas Starter
    4. Click the checkbox for Available for Salesforce mobile apps and Lightning Pages
    5. In the code section paste in the contents of our Canvas_Starter.vf page.
    6. Click Save
    7. In the Visualforce Page List View, click on Security and enable the Profiles that have access to this page.
  7. Create the Visualforce Tab

    1. Go To SetUp/Create/Tabs
    2. Click New in the Visualforce Tab section
    3. Name the Tab Canvas Starter
    4. Select a Tab style
    5. Click Save
You should now be able to access the app as a Visualforce tab in Visualforce or Lightning!

Hello

JavaScript Function Reference

This kit includes the Salesforce Canvas Javascript SDK, and you can reference the directly. The canvas sdk function reference is here. Additionally the canvas-starter.js file loads the cnv object, based on the sdk, with the following public methods:

initialze ( [ callback ] )

This function retrieves the signed request after the user has authenticated into salesforce and stores it for subsequent calls in the cnv object. It also publishes a resize event to the visualforce page. In the canvas-starter-kit example, this function is called by an angular directive when the app loads. The callback function is optional and returns the signed request object.

  • callback (optional) function: function for handling the signed request.

example:

function sayHi(signedrequest) {
  document.getElementById('firstName') = signedRequest.client.user.firstName;
}
cnv.initialize (sayHi);
login ()

Initiates the login pop-up from Salesforce to authenticate the canvas app (when required) . This function requires the consumer key, the consumer secret and the callback html to be set in the php files per step 2 above.

example:

  <button ng-click="cnv.login()">Authorize App</button>
logout ( [ loginPage ] )

Deletes the Access token from the canvas object and the one sored in the cnv object . This function requires the consumer key, the consumer secret and the callback html to be set in the php files per step 2 above.

  • loginPage (optional) boolean: redirects to the OAuth page (/oauth.sfOauth.html) after the access token is cleared.

example:

  <button ng-click="cnv.logout(true)">log out of this app</button>
refresh ()

Used as the callback function in the OAuth callback url page(/oauth/callback.html). The function simply navigates back to the index page of the app re-initializing it after log-in.

example:

//run from the OAuth popover callback.html page
//notify parent window we're authorized
try {
  if(window.opener.cnv) {
    window.opener.cnv.refresh();
  }
  else {
    //cnv should be there, but use the standard canvas function as a fallback
    window.opener.Sfdc.canvas.oauth.childWindowUnloadNotification(self.location.hash);
  }
} catch (ignore) {}
self.close();
querySalesforce( query, callback )

General AJAX query for getting salesforce data.

  • query string: A SOQL query
  • callback function: the handler for the query result.

example:

//get leads sorted by most recently viewed by me
var query = 'SELECT Id,Name,LastViewedDate FROM Account ORDER BY LastViewedDate DESC NULLS LAST'
function showResults(result) {
  document.getElementById('result').innerHTML = JSON.stringify(result,null,2);
}
cnv.querySalesforce(query, showResults);
editSalesforce( object, request, callback )

General AJAX query for editing/creating salesforce data.

  • object string: The target Salesforce Object
  • request object: A JavaScript object for the edit request. If the Id property is not included in the request, then a new record will be created in the target vie POST. If the Id is specified in the request, then a PATCH will be sent to the target record with the specified changes.
  • callback function: the handler for the query result.

example:

//create a test task for tomorrow
var due = new Date();
due.setDate(due.getDate()+1);
due = due.toISOString();
var request = {
  'ActivityDate':due.substring(0,10),
  'Subject':'Test Task From the canvas-starter-kit',
};
cnv.editSalesforce('Task',request,process);
function process(result) {
  document.getElementById.innerHTML = 'new task result: ' + JSON.stingify(result,null,2);
}
deleteSalesforce( object, id, callback )

General AJAX query for deleting salesforce data.

  • object string: The target Salesforce Object
  • id string: The target record's Id.
  • callback function: the handler for the query result.

example:

cnv.deleteSalesforce('Task',this.id,process);
function process(result) {
  if(result && result[0].errorCode) {
    //error deleting task
    alert(result[0].errorCode)
  }
  else{
    //task deleted, remove this element.
    var element = document.getElementById(this.id);
    element.parentNode.removeChild(element);
  }
}
publish( event [, payload ] )

General function for publishing an event to a visualforce page. The visualforce page must be subscribing to this event to take action,

  • event string: The name of the subscription event we want to trigger. Typically has a prefix matching the namespace, although this is not a requirement.
  • payload (optional) object: The data object to be processed by the subscribing event,.

example:

//publish an event to the cnvstart.navigate subscription to go to
//this record (a new tab/window if not in lightning or sf1)
publish ( "cnvstart.navigate" , {
  'id':this.id,
  'new' : true } );
}
navigate( id, url [, newWindow] )

Formatted function for publishing to the cnvstart.navigate subscription and navigating in the parent window.

  • id string: The id of the target Salesforce Object. If the id is specified, then the url property is ignored. If in Visualforce and the newWindow property is set to true, then the target object will be opened in a new tab/window. If in lightning or SF1 the newWindow property is ignored.
  • url string: The url to navigate to in the outer window. When specifying the url property, then pass null for the id property.
  • ** newWindow** (optional, default:false) boolean: If url is specified then the function will attempt to open the target in a new window/tab when newWindow is set to true. If in Visualforce and id is specified then the function will attempt to open the target object in a new tab/window. If in Lightning or SF1 then this propert is ignored.

example:

cnv.navigate(this.id, null, true);