/canvas-starter-kit

A template for developing on the Salesforce Canvas platform in Javascript, Node.js and Express.

Primary LanguageCSSMIT LicenseMIT

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. This is a Node.js version of the Canvas Starter Kit published by Seedcode for AngularJS developers.

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 routes/canvas.js page for authenticating your Canvas app with Salesforce
  • A basic view/sfOauth.ejs page for when users must self-authorize
  • A basic views/sfOauthCallback.ejs 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 Node.js 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 /canvas 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 Node.js with Express. 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

  2. Go To SetUp / Apps / App Manager / New Connected App

  3. Click the 'New Connected App' link on the right side of the page

  4. Fill in the 'Connected App Name', 'API Name' and other required fields in the 'Basic Information' section. For e.g., 1. Connected App Name: Canvas Starter Kit 2. API Name: Canvas_Starter_Kit

  5. Enable OAuth Settings 1. For the Callback URL specify https://oauth/sfOauthCallback 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

  6. Skip down to the 'Canvas App Settings' and check 'Force.com Canvas' 1. For the Canvas App URL, enter https://canvas 2. For Access Method, choose Signed Request (POST) 3. For Locations, choose (at least) Visualforce Page

  7. Click Save

  8. You will now be brought to the Detail Page. From here please copy the following three values. We need to add these to our routes/canvas.js and models/consumerData.js files 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

  9. Add App Consumer Data to the JS Files

  10. In the Hosted app find the canvas.js and the consumerData.js files.

  11. In the canvas.js file enter the consumer secret on line 34 in the spot reserved by <consumer secret for your connected/canvas app>.

  12. In the consumerData.js file enter your consumer key on line 2 in the spot reserved by <consumer key for your connected/canvas app>.

  13. In the consumerData.js file enter your callback URL on line 3 in the spot resrerved by <callback url for your connected/canvas app>.

  14. Create the Custom Setting

    1. Go To SetUp/Custom Code/Custom Settings
    2. Click New
    3. Label and Object Name should be 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. In the 'Field Name' and 'Field Label' enter Resource Name
    10. Set the Type to Text and give it a length of 100
    11. Click Save
  15. Upload the Static Resources

  16. Go To SetUp/Custom Code/Static Resources

  17. Click New

  18. Name the Static Resource canvasStatic and upload the canvas-static.js file from the packageComponents/staticrecources/ folder

  19. Find the styles folder in the same packageComponents/staticrecources/ folder and zip it

  20. Once the styles folder is zipped create a new static resource named style_reources and upload the zipped folder to this resource.

  21. Create Package

  22. Go To SetUp/Apps/Package Manager

  23. In the Developer Settings panel, click Edit

  24. Review the selections that are required for configuring developer settings, and then click Continue

  25. Enter the namespace prefix you want to register (e.g., yourcompanynamespace)

  26. Click Check Availability to determine if the namespace prefix is already in use

  27. If the namespace prefix that you entered isn’t available, repeat the previous two steps

  28. Click Review My Selections.

  29. Click Save.

  30. Create the Apex Classes

  31. Go To SetUp/Custom Code/Apex Classes

  32. Click New

  33. Paste in the contents of the AlternateResource.apex file

  34. Click Save

  35. Managed Packages require 75% Testing Coverage, so repeat the above steps for the AlternateResourceTest.apex file for this coverage.

  36. You can then click Run Test from the Test Class. (Hopefully it passes!)

  37. If you've set up your development org prefix (Section 6 above), then add it to the appropriate spot in both classes. 1. In AlternateResource.apex, change the string 'yournamespace' in lines 3, 7 and 12 what you registered in Section 6. 2. In AlternateResourceTest.apex do the same for lines 6, 13 and 23

  38. Run the test again (just to be sure!)

  39. Create the Visualforce page

  40. Go To SetUp/Custom Code/Visualforce Pages

  41. Click New

  42. Label the page Canvas Starter Kit

  43. Name the page Canvas_Starter_Kit

  44. Click the checkbox for Available for Salesforce mobile apps and Lightning Pages

  45. In the code section paste in the contents of our Canvas_Starter.vf page

  46. Change the string 'yournamespace' in line 16 to the namespace registered in Section 6 above

  47. Click Save

  48. In the Visualforce Page List View, click on Security and enable the Profiles that have access to this page.

  49. Create the Visualforce Tab

  50. Go To SetUp/User Interface/Tabs

  51. Click New in the Visualforce Tab section

  52. Name the Tab Canvas Starter Kit

  53. Select a Tab style

  54. Click Save

You should now be able to access the app as a Visualforce tab in Visualforce or Lightning!

Hello 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 routes/canvas.js and models/consumerData.js files per step 2 above.

example:

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

Deletes the Access token from the canvas object and the one stored in the cnv object . This function requires the consumer key, the consumer secret and the callback html to be set in the routes/canvas.js and models/consumerData.js files per step 2 above.

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

example:

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

Used as the callback function in the OAuth callback url page(/oauth/sfOauthCallback). 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);