├── .gitignore ├── .gitmodules ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── Procfile ├── README.md ├── SECURITY.md ├── pom.xml ├── src └── main │ ├── java │ ├── Main.java │ ├── canvas │ │ ├── CanvasClient.java │ │ ├── CanvasContext.java │ │ ├── CanvasEnvironmentContext.java │ │ ├── CanvasLinkContext.java │ │ ├── CanvasOrganizationContext.java │ │ ├── CanvasRequest.java │ │ ├── CanvasUserContext.java │ │ └── SignedRequest.java │ └── servlets │ │ ├── OAuthServlet.java │ │ └── ProxyServlet.java │ └── webapp │ ├── WEB-INF │ └── web.xml │ ├── examples │ ├── chatter-talk │ │ ├── chatter-talk.js │ │ ├── index.jsp │ │ ├── logo.png │ │ ├── share.png │ │ └── talk.css │ └── hello-world │ │ ├── index.jsp │ │ └── logo.png │ ├── images │ └── salesforce.png │ ├── index.html │ └── scripts │ └── json2.js └── system.properties /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.iws 3 | *.ipr 4 | *.class 5 | .project 6 | .classpath 7 | keystore 8 | target/ 9 | .settings/ 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/main/webapp/sdk"] 2 | path = src/main/webapp/sdk 3 | url = git://github.com/forcedotcom/SalesforceCanvasJavascriptSDK.git 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. 2 | #ECCN:Open Source 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Salesforce Open Source Community Code of Conduct 2 | 3 | ## About the Code of Conduct 4 | 5 | Equality is a core value at Salesforce. We believe a diverse and inclusive 6 | community fosters innovation and creativity, and are committed to building a 7 | culture where everyone feels included. 8 | 9 | Salesforce open-source projects are committed to providing a friendly, safe, and 10 | welcoming environment for all, regardless of gender identity and expression, 11 | sexual orientation, disability, physical appearance, body size, ethnicity, nationality, 12 | race, age, religion, level of experience, education, socioeconomic status, or 13 | other similar personal characteristics. 14 | 15 | The goal of this code of conduct is to specify a baseline standard of behavior so 16 | that people with different social values and communication styles can work 17 | together effectively, productively, and respectfully in our open source community. 18 | It also establishes a mechanism for reporting issues and resolving conflicts. 19 | 20 | All questions and reports of abusive, harassing, or otherwise unacceptable behavior 21 | in a Salesforce open-source project may be reported by contacting the Salesforce 22 | Open Source Conduct Committee at ossconduct@salesforce.com. 23 | 24 | ## Our Pledge 25 | 26 | In the interest of fostering an open and welcoming environment, we as 27 | contributors and maintainers pledge to making participation in our project and 28 | our community a harassment-free experience for everyone, regardless of gender 29 | identity and expression, sexual orientation, disability, physical appearance, 30 | body size, ethnicity, nationality, race, age, religion, level of experience, education, 31 | socioeconomic status, or other similar personal characteristics. 32 | 33 | ## Our Standards 34 | 35 | Examples of behavior that contributes to creating a positive environment 36 | include: 37 | 38 | * Using welcoming and inclusive language 39 | * Being respectful of differing viewpoints and experiences 40 | * Gracefully accepting constructive criticism 41 | * Focusing on what is best for the community 42 | * Showing empathy toward other community members 43 | 44 | Examples of unacceptable behavior by participants include: 45 | 46 | * The use of sexualized language or imagery and unwelcome sexual attention or 47 | advances 48 | * Personal attacks, insulting/derogatory comments, or trolling 49 | * Public or private harassment 50 | * Publishing, or threatening to publish, others' private information—such as 51 | a physical or electronic address—without explicit permission 52 | * Other conduct which could reasonably be considered inappropriate in a 53 | professional setting 54 | * Advocating for or encouraging any of the above behaviors 55 | 56 | ## Our Responsibilities 57 | 58 | Project maintainers are responsible for clarifying the standards of acceptable 59 | behavior and are expected to take appropriate and fair corrective action in 60 | response to any instances of unacceptable behavior. 61 | 62 | Project maintainers have the right and responsibility to remove, edit, or 63 | reject comments, commits, code, wiki edits, issues, and other contributions 64 | that are not aligned with this Code of Conduct, or to ban temporarily or 65 | permanently any contributor for other behaviors that they deem inappropriate, 66 | threatening, offensive, or harmful. 67 | 68 | ## Scope 69 | 70 | This Code of Conduct applies both within project spaces and in public spaces 71 | when an individual is representing the project or its community. Examples of 72 | representing a project or community include using an official project email 73 | address, posting via an official social media account, or acting as an appointed 74 | representative at an online or offline event. Representation of a project may be 75 | further defined and clarified by project maintainers. 76 | 77 | ## Enforcement 78 | 79 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 80 | reported by contacting the Salesforce Open Source Conduct Committee 81 | at ossconduct@salesforce.com. All complaints will be reviewed and investigated 82 | and will result in a response that is deemed necessary and appropriate to the 83 | circumstances. The committee is obligated to maintain confidentiality with 84 | regard to the reporter of an incident. Further details of specific enforcement 85 | policies may be posted separately. 86 | 87 | Project maintainers who do not follow or enforce the Code of Conduct in good 88 | faith may face temporary or permanent repercussions as determined by other 89 | members of the project's leadership and the Salesforce Open Source Conduct 90 | Committee. 91 | 92 | ## Attribution 93 | 94 | This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home], 95 | version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. 96 | It includes adaptions and additions from [Go Community Code of Conduct][golang-coc], 97 | [CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc]. 98 | 99 | This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us]. 100 | 101 | [contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/) 102 | [golang-coc]: https://golang.org/conduct 103 | [cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md 104 | [microsoft-coc]: https://opensource.microsoft.com/codeofconduct/ 105 | [cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/ 106 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Salesforce.com, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: sh target/bin/webapp 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Salesforce Force.com Canvas SDK 2 | ============================ 3 | 4 | ### Introduction 5 | 6 | Force.com Canvas is a mechanism for consuming third-party applications within Salesforce. Its goal is to connect applications at a UI level instead of just an API level. The purpose of this GitHub repository is to provide third-party applications with a Java/JavaScript SDK and examples so you can easily integrate canvas-style applications into Salesforce, while developing in the technology and platform of your choice. 7 | 8 | The best place to get started building canvas applications is the [online developer guide][1]. 9 | 10 | Currently, we provide Java examples in this repository, but you can develop in whatever language you prefer. Most of the integration with Salesforce is through JavaScript and REST. You can also run and test your application locally from your own host, or from [Heroku](http://www.heroku.com/). 11 | 12 | 13 | ### Examples 14 | 15 | This SDK contains some basic Java examples. We recommend you explore the [Heroku Quick Start][2], for additional examples in Java and Ruby. 16 | 17 | For other examples and resources, check out the [developer guide][1]. 18 | 19 | ### Prerequisites 20 | 21 | Below are some useful commands and links for your convenience. Before you use them, you'll need to make sure you have the necessary software installed on your computer [here][3]. 22 | 23 | ### How to clone the SDK repository 24 | 25 | git clone git@github.com:forcedotcom/SalesforceCanvasFrameworkSDK.git 26 | cd SalesforceCanvasFrameworkSDK 27 | git submodule init 28 | git submodule update 29 | 30 | ### How to build canvas locally 31 | 32 | If you prefer, you can build and test your application locally before you push to Heroku or any other server. If you decide to test locally, you'll also need to generate a local keystore so you can do SSL. 33 | 34 | mvn package 35 | 36 | ### First time keystore generation 37 | This is only needed to support SSL (https) when running locally. Heroku uses [piggyback SSL](https://devcenter.heroku.com/articles/ssl) so it's not needed there. 38 | 39 | > keytool -keystore keystore -alias jetty -genkey -keyalg RSA 40 | Enter keystore password: 123456 41 | Re-enter new password: 123456 42 | What is your first and last name? 43 | [Unknown]: John Doe 44 | What is the name of your organizational unit? 45 | [Unknown]: myorgunit 46 | What is the name of your organization? 47 | [Unknown]: myorg 48 | What is the name of your City or Locality? 49 | [Unknown]: San Fancisco 50 | What is the name of your State or Province? 51 | [Unknown]: CA 52 | What is the two-letter country code for this unit? 53 | [Unknown]: us 54 | Is CN=salesforce.com, OU=platform, O=chimera, L=San Fancisco, ST=CA, C=us correct? 55 | [no]: yes 56 | 57 | Enter key password for 58 | (RETURN if same as keystore password): 59 | Re-enter new password: 60 | 61 | 62 | ### How to run canvas locally 63 | 64 | If you're running and testing locally, this will start your Java Web server. 65 | 66 | sh target/bin/webapp 67 | 68 | ### Canvas URL 69 | 70 | If you're running locally 71 | https://localhost:8443/examples/hello-world/index.jsp 72 | 73 | Or if you're running on Heroku 74 | https://.herokuapp.com/examples/hello-world/index.jsp 75 | 76 | ### Canvas callback URLs 77 | 78 | If you're running locally 79 | https://localhost:8443/sdk/callback.html 80 | 81 | Or if you're running on Heroku 82 | https://.herokuapp.com/sdk/callback.html 83 | 84 | ### How to push new changes to Heroku 85 | 86 | To commit your changes into your local git repository and push those changes to Heroku, use these commands. Note that your repository name may be diffferent than 'heroku', use git remote -v to confirm. 87 | 88 | git add -A 89 | git commit -m "My change comments" 90 | git push heroku master 91 | 92 | ### How to get Heroku logs 93 | 94 | To access your logs on Heroku, use the following command. For more information on Heroku logs click [here](https://devcenter.heroku.com/articles/logging). 95 | 96 | heroku logs --tail 97 | 98 | [1]: https://developer.salesforce.com/docs/atlas.en-us.platform_connect.meta/platform_connect/canvas_framework_intro.htm 99 | [2]: https://developer.salesforce.com/docs/atlas.en-us.platform_connect.meta/platform_connect/quick_start_simple_intro.htm 100 | [3]: https://developer.salesforce.com/docs/atlas.en-us.platform_connect.meta/platform_connect/quick_start_prereqs.htm 101 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com) 4 | as soon as it is discovered. This library limits its runtime dependencies in 5 | order to reduce the total cost of ownership as much as can be, but all consumers 6 | should remain vigilant and have their security stakeholders review all third-party 7 | products (3PP) like this one and their dependencies. 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | sfdc 7 | 1.0-SNAPSHOT 8 | Salesforce Canvas Framework SDK 9 | sfdc-canvas-sdk 10 | 11 | 12 | 1.8 13 | UTF-8 14 | 9.0.90 15 | 16 | 17 | 18 | 19 | 20 | jstl 21 | jstl 22 | 1.2 23 | 24 | 25 | 26 | org.apache.tomcat.embed 27 | tomcat-embed-core 28 | ${tomcat.version} 29 | 30 | 31 | org.apache.tomcat.embed 32 | tomcat-embed-jasper 33 | ${tomcat.version} 34 | 35 | 36 | org.apache.tomcat 37 | tomcat-jasper 38 | ${tomcat.version} 39 | 40 | 41 | org.apache.tomcat 42 | tomcat-jasper-el 43 | ${tomcat.version} 44 | 45 | 46 | org.apache.tomcat 47 | tomcat-jsp-api 48 | ${tomcat.version} 49 | 50 | 51 | 52 | com.fasterxml.jackson.core 53 | jackson-core 54 | 2.8.5 55 | 56 | 57 | com.fasterxml.jackson.core 58 | jackson-databind 59 | 2.8.5 60 | 61 | 62 | 63 | 64 | asm 65 | asm 66 | 3.2 67 | 68 | 69 | 70 | 71 | org.json 72 | json 73 | 20080701 74 | 75 | 76 | 77 | 78 | commons-httpclient 79 | commons-httpclient 80 | 3.1 81 | 82 | 83 | commons-codec 84 | commons-codec 85 | 86 | 87 | 88 | 89 | 90 | 91 | commons-codec 92 | commons-codec 93 | 1.4 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-compiler-plugin 104 | 2.3.2 105 | 106 | ${java.version} 107 | ${java.version} 108 | 109 | 110 | 111 | 112 | org.codehaus.mojo 113 | appassembler-maven-plugin 114 | 1.1.1 115 | 116 | target 117 | 118 | 119 | Main 120 | webapp 121 | 122 | 123 | 124 | 125 | 126 | execution1 127 | package 128 | 129 | assemble 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/main/java/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, salesforce.com, inc. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | following disclaimer. 10 | 11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | 14 | Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | promote products derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | import org.apache.catalina.WebResourceRoot; 28 | import org.apache.catalina.core.StandardContext; 29 | import org.apache.catalina.startup.Tomcat; 30 | import org.apache.catalina.webresources.DirResourceSet; 31 | import org.apache.catalina.webresources.StandardRoot; 32 | import org.apache.catalina.startup.Tomcat; 33 | import org.apache.catalina.startup.Tomcat; 34 | import org.apache.catalina.connector.Connector; 35 | import org.apache.coyote.http11.AbstractHttp11Protocol; 36 | import org.apache.catalina.Context; 37 | import org.apache.catalina.startup.Tomcat; 38 | import org.apache.catalina.connector.Connector; 39 | import org.apache.catalina.Context; 40 | import org.apache.catalina.connector.Connector; 41 | import org.apache.catalina.startup.Tomcat; 42 | import org.apache.tomcat.util.scan.StandardJarScanner; 43 | import java.io.File; 44 | 45 | /** 46 | * 47 | * This class launches the web application in an embedded Jetty container. 48 | * This is the entry point to your application. The Java command that is used for 49 | * launching should fire this main method. 50 | * 51 | */ 52 | public class Main { 53 | 54 | /** 55 | * @param args 56 | */ 57 | public static void main(String[] args) throws Exception{ 58 | 59 | boolean heroku = false; 60 | // A hacky way to determine if we are running on heroku or not 61 | String basedir = (String)System.getProperty("basedir"); 62 | if (basedir != null && basedir.endsWith("/app/target")) { 63 | heroku = true; 64 | } 65 | 66 | if (!heroku) { 67 | System.out.println("Looks like we are NOT running on Heroku."); 68 | 69 | String webappDirLocation = "src/main/webapp/"; 70 | 71 | String webPort = System.getenv("PORT"); 72 | if (webPort == null || webPort.isEmpty()) { 73 | webPort = "8080"; 74 | } 75 | 76 | String sslPort = System.getenv("SSLPORT"); 77 | if (sslPort == null || sslPort.isEmpty()) { 78 | sslPort = System.getenv("SSL_PORT"); 79 | if (sslPort == null || sslPort.isEmpty()) { 80 | sslPort = "8443"; 81 | } 82 | } 83 | 84 | Tomcat tomcat = new Tomcat(); 85 | tomcat.setPort(Integer.parseInt(webPort)); 86 | 87 | // HTTP Connector 88 | Connector httpConnector = new Connector("HTTP/1.1"); 89 | httpConnector.setPort(Integer.parseInt(webPort)); 90 | tomcat.getService().addConnector(httpConnector); 91 | 92 | // HTTPS Connector 93 | Connector httpsConnector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); 94 | httpsConnector.setPort(Integer.parseInt(sslPort)); 95 | httpsConnector.setSecure(true); 96 | httpsConnector.setScheme("https"); 97 | httpsConnector.setAttribute("keyAlias", "jetty2"); 98 | httpsConnector.setAttribute("keystorePass", "123456"); 99 | httpsConnector.setAttribute("keystoreFile", "keystore"); 100 | httpsConnector.setAttribute("clientAuth", "false"); 101 | httpsConnector.setAttribute("sslProtocol", "TLS"); 102 | httpsConnector.setAttribute("SSLEnabled", true); 103 | tomcat.getService().addConnector(httpsConnector); 104 | 105 | // Set the default connector to HTTP 106 | tomcat.setConnector(httpConnector); 107 | 108 | // Add the web application context 109 | StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath()); 110 | System.out.println("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath()); 111 | 112 | File additionWebInfClasses = new File("target/classes"); 113 | WebResourceRoot resources = new StandardRoot(ctx); 114 | resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", 115 | additionWebInfClasses.getAbsolutePath(), "/")); 116 | ctx.setResources(resources); 117 | 118 | 119 | tomcat.start(); 120 | tomcat.getServer().await(); 121 | } 122 | else { 123 | 124 | // Heroku does it's own SSL piggyback thing 125 | System.out.println("Looks like we are running on heroku."); 126 | 127 | String webappDirLocation = "src/main/webapp/"; 128 | 129 | //The port that we should run on can be set into an environment variable 130 | //Look for that variable and default to 8080 if it isn't there. 131 | String webPort = System.getenv("PORT"); 132 | if(webPort == null || webPort.isEmpty()) { 133 | webPort = "8080"; 134 | } 135 | 136 | Tomcat tomcat = new Tomcat(); 137 | tomcat.setPort(Integer.valueOf(webPort)); 138 | 139 | Connector conn = new Connector(); 140 | conn.setPort(Integer.valueOf(webPort)); 141 | tomcat.setConnector(conn); 142 | 143 | StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath()); 144 | System.out.println("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath()); 145 | 146 | File additionWebInfClasses = new File("target/classes"); 147 | WebResourceRoot resources = new StandardRoot(ctx); 148 | resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", 149 | additionWebInfClasses.getAbsolutePath(), "/")); 150 | ctx.setResources(resources); 151 | 152 | tomcat.start(); 153 | tomcat.getServer().await(); 154 | } 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package canvas; 27 | 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | 31 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 32 | import com.fasterxml.jackson.annotation.JsonProperty; 33 | 34 | /** 35 | * Describes contextual information about the current canvas client (third party app). This Object, 36 | * in JS literal notation, needs to ba passed on all Sfdc.canvas.client requests. 37 | * 38 | */ 39 | @JsonIgnoreProperties(ignoreUnknown=true) 40 | public class CanvasClient 41 | { 42 | private String OAuthToken; 43 | private String clientId; 44 | private String instanceId; 45 | private String targetOrigin; 46 | private String instanceUrl; 47 | 48 | /** 49 | * The scoped OAuth token to be used to subsequent REST calls 50 | */ 51 | @JsonProperty("oauthToken") 52 | public String getOAuthToken() { 53 | return OAuthToken; 54 | } 55 | 56 | /** 57 | * @return The scoped OAuth token to be usd for subsequent REST calls. 58 | */ 59 | @JsonProperty("oauthToken") 60 | public void setOAuthToken(String OAuthToken) { 61 | this.OAuthToken = OAuthToken; 62 | } 63 | 64 | @JsonProperty("clientId") 65 | public String getClientId() { 66 | return clientId; 67 | } 68 | 69 | @JsonProperty("clientId") 70 | public void setClientId(String clientId) { 71 | this.clientId = clientId; 72 | } 73 | 74 | /** 75 | * Unique identifier for this canvas app. This value will be different for each instance of a canvas app, even if 76 | * the same canvas app is placed on a page more than once. 77 | * @return Unique identifier for this canvas app 78 | */ 79 | @JsonProperty("instanceId") 80 | public String getInstanceId() { 81 | return instanceId; 82 | } 83 | 84 | @JsonProperty("instanceId") 85 | public void setInstanceId(String instanceId) { 86 | this.instanceId = instanceId; 87 | } 88 | 89 | /** 90 | * @returns the origin (http://somesalesforcedomain:port) of the parent to the canvas app. This is used so 91 | * the in browser proxy knows where to post the request to. 92 | */ 93 | @JsonProperty("targetOrigin") 94 | public String getTargetOrigin() { 95 | return this.targetOrigin; 96 | } 97 | 98 | @JsonProperty("targetOrigin") 99 | public void setTargetOrigin(String targetOrigin) { 100 | this.targetOrigin = targetOrigin; 101 | } 102 | 103 | /** 104 | * The base url for all subsequent REST call, this has the correct 105 | * Salesforce instance this organization is pinned to. 106 | */ 107 | @JsonProperty("instanceUrl") 108 | public String getInstanceUrl() { 109 | return instanceUrl; 110 | } 111 | 112 | @JsonProperty("instanceUrl") 113 | public void setInstanceUrl(String instanceUrl) { 114 | this.instanceUrl = instanceUrl; 115 | } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 30 | import com.fasterxml.jackson.annotation.JsonProperty; 31 | 32 | /** 33 | * Describes all contextual information related to canvas applications. 34 | * 35 | *

36 | * Some information within the context depends on what oauth scopes are allowed 37 | * on the canvas application. Some/all items may be null if the oauth scope is 38 | * not set accordingly. 39 | * 40 | */ 41 | @JsonIgnoreProperties(ignoreUnknown=true) 42 | public class CanvasContext { 43 | 44 | private CanvasUserContext userContext = null; 45 | private CanvasOrganizationContext orgContext = null; 46 | private CanvasLinkContext linkContext = null; 47 | private CanvasEnvironmentContext envContext = null; 48 | 49 | 50 | /** 51 | * Provides the context about the current user. 52 | * 53 | * @return The current user context, or null if the oauth scope 54 | * will not allow. 55 | */ 56 | @JsonProperty("user") 57 | public CanvasUserContext getUserContext() { 58 | return this.userContext; 59 | } 60 | 61 | /** 62 | * Sets the context about the current user. 63 | */ 64 | @JsonProperty("user") 65 | public void setUserContext(CanvasUserContext userContext) 66 | { 67 | this.userContext = userContext; 68 | } 69 | 70 | /** 71 | * Provides the context about the current organization. 72 | * 73 | * @return The current organization context, or null if the oauth scope 74 | * will not allow. 75 | */ 76 | @JsonProperty("organization") 77 | public CanvasOrganizationContext getOrganizationContext() { 78 | return orgContext; 79 | } 80 | 81 | /** 82 | * Sets the context about the current organization. 83 | */ 84 | @JsonProperty("organization") 85 | public void setOrganizationContext(CanvasOrganizationContext orgContext) 86 | { 87 | this.orgContext = orgContext; 88 | } 89 | 90 | /** 91 | * Provides the context about the current environment (page, url, etc). 92 | */ 93 | @JsonProperty("environment") 94 | public CanvasEnvironmentContext getEnvironmentContext() { 95 | if (null == this.envContext){ 96 | return new CanvasEnvironmentContext(); 97 | } 98 | return envContext; 99 | } 100 | 101 | @JsonProperty("environment") 102 | public void setEnvironmentContext(CanvasEnvironmentContext envContext){ 103 | this.envContext = envContext; 104 | } 105 | 106 | /** 107 | * Provides links to external resources within sfdc. 108 | */ 109 | @JsonProperty("links") 110 | public CanvasLinkContext getLinkContext() { 111 | return linkContext; 112 | } 113 | 114 | /** 115 | * Sets the link context for this request. 116 | * @param linkContext 117 | */ 118 | @JsonProperty("links") 119 | public void setLinkContext(CanvasLinkContext linkContext) 120 | { 121 | this.linkContext = linkContext; 122 | } 123 | 124 | @Override 125 | public String toString() 126 | { 127 | return String.format("Canvas Context:\n\t" + 128 | "User Context:\n\t\t%s\n\t"+ 129 | "Org Context:\n\t\t%s\n\t"+ 130 | "Environment Context:\n\t\t%s\n\t"+ 131 | "Link Context:\n\t\t%s\n", 132 | null != userContext?userContext.toString():"null", 133 | null != orgContext?orgContext.toString():"null", 134 | null != envContext?envContext.toString():"null", 135 | null != linkContext?linkContext.toString():"null"); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasEnvironmentContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 33 | import com.fasterxml.jackson.annotation.JsonProperty; 34 | 35 | @JsonIgnoreProperties(ignoreUnknown=true) 36 | public class CanvasEnvironmentContext { 37 | 38 | private String locationUrl; 39 | private String uiTheme; 40 | private Dimensions dimensions; 41 | private SystemVersion version; 42 | private Map parameters; 43 | 44 | /** 45 | * Returns the url of the current location. 46 | */ 47 | @JsonProperty("locationUrl") 48 | public String getLocationUrl() { 49 | return this.locationUrl; 50 | } 51 | 52 | public void setLocationUrl(String referrerUrl) { 53 | this.locationUrl = referrerUrl; 54 | } 55 | 56 | /** 57 | * Returns the value Theme2 if the user is using the newer user interface theme of the online application, labeled 58 | * \u201cSalesforce.\u201d Returns Theme1 if the user is using the older user interface theme, labeled 59 | * \u201cSalesforce Classic.\u201d 60 | */ 61 | @JsonProperty("uiTheme") 62 | public String getUiTheme() { 63 | return this.uiTheme; 64 | } 65 | 66 | public void setUiTheme(String uiTheme) { 67 | this.uiTheme = uiTheme; 68 | } 69 | 70 | @JsonProperty("dimensions") 71 | public Dimensions getDimensions() { 72 | return this.dimensions; 73 | } 74 | 75 | @JsonProperty("dimensions") 76 | public void setDimensions(Dimensions dimensions) { 77 | this.dimensions = dimensions; 78 | } 79 | 80 | @JsonProperty("version") 81 | public SystemVersion getSystemVersion() { 82 | return this.version; 83 | } 84 | 85 | @JsonProperty("version") 86 | public void setSystemVersion(SystemVersion systemVersion) { 87 | this.version = systemVersion; 88 | } 89 | 90 | @JsonProperty("parameters") 91 | public Map getParameters() { 92 | if (null == this.parameters){ 93 | this.parameters = new HashMap(); 94 | } 95 | return this.parameters; 96 | } 97 | 98 | @JsonProperty("parameters") 99 | public void setParameters(Map parameters) { 100 | this.parameters = parameters; 101 | } 102 | 103 | @Override 104 | public String toString() 105 | { 106 | return locationUrl + ", " + 107 | uiTheme + "," + 108 | dimensions.toString() + "," + 109 | version.toString(); 110 | } 111 | 112 | @JsonIgnoreProperties(ignoreUnknown=true) 113 | public static class Dimensions{ 114 | /** 115 | * The width of the iframe 116 | */ 117 | private String width; 118 | /** 119 | * The height of the iframe. 120 | */ 121 | private String height; 122 | 123 | @JsonProperty("width") 124 | public String getWidth() { 125 | return this.width; 126 | } 127 | @JsonProperty("width") 128 | public void setWidth(String width) { 129 | this.width = width; 130 | } 131 | 132 | @JsonProperty("height") 133 | public String getHeight() { 134 | return this.height; 135 | } 136 | @JsonProperty("height") 137 | public void setHeight(String height) { 138 | this.height = height; 139 | } 140 | 141 | @Override 142 | public String toString(){ 143 | return String.format("(w:%s,h:%s)",width,height); 144 | } 145 | } 146 | 147 | @JsonIgnoreProperties(ignoreUnknown=true) 148 | public static class SystemVersion{ 149 | 150 | private String api; 151 | private String season; 152 | 153 | // Needs default ctor for Jackson to construct. 154 | public SystemVersion(){ 155 | } 156 | 157 | @JsonProperty("api") 158 | public String getApiVersion() { 159 | return this.api; 160 | } 161 | 162 | @JsonProperty("api") 163 | public void setApiVersion(String apiVersion) { 164 | this.api = apiVersion; 165 | } 166 | 167 | @JsonProperty("season") 168 | public String getSeason() { 169 | return this.season; 170 | } 171 | 172 | @JsonProperty("season") 173 | public void setSeason(String season) { 174 | this.season = season; 175 | } 176 | 177 | @Override 178 | public String toString(){ 179 | return String.format("%s - %s",api,season); 180 | } 181 | 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasLinkContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 30 | import com.fasterxml.jackson.annotation.JsonProperty; 31 | 32 | /** 33 | * Describes all contextual information around external references, or links to external resources. 34 | */ 35 | @JsonIgnoreProperties(ignoreUnknown=true) 36 | public class CanvasLinkContext { 37 | private String enterpriseUrl; 38 | private String metadataUrl; 39 | private String partnerUrl; 40 | private String restUrl; 41 | private String sobjectUrl; 42 | private String searchUrl; 43 | private String queryUrl; 44 | private String recentItemsUrl; 45 | private String userProfileUrl; 46 | private String chatterFeedsUrl; 47 | private String chatterGroupsUrl; 48 | private String chatterUsersUrl; 49 | private String chatterFeedItemsUrl; 50 | 51 | /** 52 | * Provide the url for enterprise-wide external clients. 53 | */ 54 | @JsonProperty("enterpriseUrl") 55 | public String getEnterpriseUrl() { 56 | return this.enterpriseUrl; 57 | } 58 | 59 | public void setEnterpriseUrl(String enterpriseUrl) { 60 | this.enterpriseUrl = enterpriseUrl; 61 | } 62 | 63 | /** 64 | * Provide the base url for the metadata api to access information about custom objects, apex classes, etc. 65 | */ 66 | @JsonProperty("metadataUrl") 67 | public String getMetadataUrl() { 68 | return this.metadataUrl; 69 | } 70 | 71 | public void setMetadataUrl(String metadataUrl) { 72 | this.metadataUrl = metadataUrl; 73 | } 74 | 75 | /** 76 | * Access to the partner api for developing client applications for multiple organizations. 77 | */ 78 | @JsonProperty("partnerUrl") 79 | public String getPartnerUrl() { 80 | return this.partnerUrl; 81 | } 82 | 83 | public void setPartnerUrl(String partnerUrl) { 84 | this.partnerUrl = partnerUrl; 85 | } 86 | 87 | /** 88 | * Access to the base url for RESTful services. 89 | */ 90 | @JsonProperty("restUrl") 91 | public String getRestUrl() { 92 | return this.restUrl; 93 | } 94 | 95 | public void setRestUrl(String restUrl) { 96 | this.restUrl = restUrl; 97 | } 98 | 99 | /** 100 | * Access to custom sobject definitions. 101 | */ 102 | @JsonProperty("sobjectUrl") 103 | public String getSobjectUrl() { 104 | return this.sobjectUrl; 105 | } 106 | 107 | public void setSobjectUrl(String sobjectUrl) { 108 | this.sobjectUrl = sobjectUrl; 109 | } 110 | 111 | /** 112 | * Access to search api. 113 | */ 114 | @JsonProperty("searchUrl") 115 | public String getSearchUrl() { 116 | return this.searchUrl; 117 | } 118 | 119 | public void setSearchUrl(String searchUrl) { 120 | this.searchUrl = searchUrl; 121 | } 122 | 123 | /** 124 | * Access to the SOQL query api. 125 | */ 126 | @JsonProperty("queryUrl") 127 | public String getQueryUrl() { 128 | return this.queryUrl; 129 | } 130 | 131 | public void setQueryUrl(String queryUrl) { 132 | this.queryUrl = queryUrl; 133 | } 134 | 135 | /** 136 | * Access to the recent items feed. 137 | */ 138 | @JsonProperty("recentItemsUrl") 139 | public String getRecentItemsUrl() { 140 | return this.recentItemsUrl; 141 | } 142 | 143 | public void setRecentItemsUrl(String recentItemsUrl) { 144 | this.recentItemsUrl = recentItemsUrl; 145 | } 146 | 147 | /** 148 | * Retrieve more information about the current user. 149 | */ 150 | @JsonProperty("userUrl") 151 | public String getUserUrl() { 152 | return this.userProfileUrl; 153 | } 154 | 155 | public void setUserUrl(String userProfileUrl) { 156 | this.userProfileUrl = userProfileUrl; 157 | } 158 | 159 | /** 160 | * Access to Chatter Feeds. Note: Requires user profile permissions, otherwise this will be null. 161 | */ 162 | @JsonProperty("chatterFeedsUrl") 163 | public String getChatterFeedsUrl() { 164 | return this.chatterFeedsUrl; 165 | } 166 | 167 | public void setChatterFeedsUrl(String chatterFeedsUrl) { 168 | this.chatterFeedsUrl = chatterFeedsUrl; 169 | } 170 | 171 | /** 172 | * Access to Chatter Groups. Note: Requires user profile permissions, otherwise this will be null. 173 | */ 174 | @JsonProperty("chatterGroupsUrl") 175 | public String getChatterGroupsUrl() { 176 | return this.chatterGroupsUrl; 177 | } 178 | 179 | public void setChatterGroupsUrl(String chatterGroupsUrl) { 180 | this.chatterGroupsUrl = chatterGroupsUrl; 181 | } 182 | 183 | /** 184 | * Access to Chatter Users. Note: Requires user profile permissions, otherwise this will be null. 185 | */ 186 | @JsonProperty("chatterUsersUrl") 187 | public String getChatterUsersUrl() { 188 | return this.chatterUsersUrl; 189 | } 190 | 191 | public void setChatterUsersUrl(String chatterUsersUrl) { 192 | this.chatterUsersUrl = chatterUsersUrl; 193 | } 194 | 195 | /** 196 | * Access to individual Chatter Feed items. Note: Requires user profile permissions, otherwise this will be null. 197 | */ 198 | @JsonProperty("chatterFeedItemsUrl") 199 | public String getChatterFeedItemsUrl() { 200 | return this.chatterFeedItemsUrl; 201 | } 202 | 203 | public void setChatterFeedItemsUrl(String chatterFeedItemsUrl) { 204 | this.chatterFeedItemsUrl = chatterFeedItemsUrl; 205 | } 206 | 207 | @Override 208 | public String toString() 209 | { 210 | return enterpriseUrl+ ", " + 211 | metadataUrl+ ", " + 212 | partnerUrl+ ", " + 213 | restUrl+ ", " + 214 | sobjectUrl+ ", " + 215 | searchUrl+ ", " + 216 | queryUrl+ ", " + 217 | recentItemsUrl+ ", " + 218 | userProfileUrl+ ", " + 219 | chatterFeedsUrl+ ", " + 220 | chatterGroupsUrl+ ", " + 221 | chatterUsersUrl+ ", " + 222 | chatterFeedItemsUrl; 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasOrganizationContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 30 | import com.fasterxml.jackson.annotation.JsonProperty; 31 | 32 | /** 33 | * Describes contextual information about the current organization/company. 34 | */ 35 | @JsonIgnoreProperties(ignoreUnknown=true) 36 | public class CanvasOrganizationContext { 37 | private String organizationId; 38 | private String name; 39 | private boolean multicurrencyEnabled; 40 | private String currencyISOCode; 41 | 42 | /** 43 | * The organization id of the organization. 44 | */ 45 | @JsonProperty("organizationId") 46 | public String getOrganizationId() { 47 | return this.organizationId; 48 | } 49 | 50 | public void setOrganizationId(String organizationId) { 51 | this.organizationId = organizationId; 52 | } 53 | 54 | /** 55 | * The name of the company or organization. 56 | */ 57 | @JsonProperty("name") 58 | public String getName() { 59 | return this.name; 60 | } 61 | 62 | public void setName(String name) { 63 | this.name = name; 64 | } 65 | 66 | /** 67 | * Indicates whether the user\u2019s organization uses multiple currencies (true) or not (false). 68 | */ 69 | @JsonProperty("multicurrencyEnabled") 70 | public boolean isMulticurrencyEnabled() { 71 | return this.multicurrencyEnabled; 72 | } 73 | 74 | public void setMulticurrencyEnabled(boolean multicurrencyEnabled) { 75 | this.multicurrencyEnabled = multicurrencyEnabled; 76 | } 77 | 78 | /** 79 | * Current company's default currency ISO code (applies only if multi-currency is disabled for the org). 80 | */ 81 | @JsonProperty("currencyIsoCode") 82 | public String getCurrencyISOCode() { 83 | return this.currencyISOCode; 84 | } 85 | 86 | public void setCurrencyISOCode(String currencyISOCode) { 87 | this.currencyISOCode = currencyISOCode; 88 | } 89 | 90 | @Override 91 | public String toString() 92 | { 93 | return organizationId + ","+ 94 | name + ","+ 95 | multicurrencyEnabled + ","+ 96 | currencyISOCode; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasRequest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 33 | import com.fasterxml.jackson.annotation.JsonProperty; 34 | 35 | /** 36 | * 37 | * The canvas request is what is sent to the client on the very first request. In this canvas 38 | * request is information for authenticating and context about the user, organization and environment. 39 | *

40 | * This class is serialized into JSON on then signed by the signature service to prevent tampering. 41 | * 42 | */ 43 | @JsonIgnoreProperties(ignoreUnknown=true) 44 | public class CanvasRequest { 45 | 46 | private String algorithm; 47 | private Integer issuedAt; 48 | private String userId; 49 | private CanvasContext canvasContext; 50 | private CanvasClient client; 51 | 52 | /** 53 | * The algorithm used to sign the request. typically HMAC-SHA256 54 | * @see platform.connect.service.SignRequestService.ALGORITHM 55 | */ 56 | @JsonProperty("algorithm") 57 | public String getAlgorithm() { 58 | return algorithm; 59 | } 60 | 61 | @JsonProperty("algorithm") 62 | public void setAlgorithm(String algorithm) { 63 | this.algorithm = algorithm; 64 | } 65 | 66 | /** 67 | * The unix time this request was issued at. 68 | */ 69 | @JsonProperty("issuedAt") 70 | public Integer getIssuedAt() { 71 | return issuedAt; 72 | } 73 | 74 | @JsonProperty("issuedAt") 75 | public void setIssuedAt(Integer issuedAt) { 76 | this.issuedAt = issuedAt; 77 | } 78 | 79 | /** 80 | * The Salesforce unique id for this user. 81 | * @return 82 | */ 83 | @JsonProperty("userId") 84 | public String getUserId() { 85 | return userId; 86 | } 87 | 88 | @JsonProperty("userId") 89 | public void setUserId(String userId) { 90 | this.userId = userId; 91 | } 92 | 93 | /** 94 | * Context information about the user, org and environment. 95 | */ 96 | @JsonProperty("context") 97 | public CanvasContext getContext() { 98 | return canvasContext; 99 | } 100 | 101 | /** 102 | * Client instance information required while using the Sfdc.canvas.client JavaScript API. 103 | */ 104 | @JsonProperty("context") 105 | public void setContext(CanvasContext canvasContext) { 106 | this.canvasContext = canvasContext; 107 | } 108 | 109 | /** 110 | * Unique information about this client (including oauth token). This information (in JSON) format needs to be 111 | * included on all client side SDK calls. 112 | */ 113 | @JsonProperty("client") 114 | public CanvasClient getClient() { 115 | return client; 116 | } 117 | 118 | @JsonProperty("client") 119 | public void setClient(CanvasClient client) { 120 | this.client = client; 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/main/java/canvas/CanvasUserContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import java.util.Locale; 30 | 31 | 32 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 33 | import com.fasterxml.jackson.annotation.JsonProperty; 34 | 35 | /** 36 | * Describes contextual information about the current user. 37 | */ 38 | @JsonIgnoreProperties(ignoreUnknown=true) 39 | public class CanvasUserContext{ 40 | 41 | private String userId; 42 | private String userName; 43 | private String firstName; 44 | private String lastName; 45 | private String email; 46 | private String fullName; 47 | private Locale locale; 48 | private Locale language; 49 | private String timeZone; 50 | private String profileId; 51 | private String roleId; 52 | private String userType; 53 | private String currencyISOCode; 54 | private boolean accessibilityMode; 55 | private String profilePhotoUrl; 56 | private String profileThumbnailUrl; 57 | 58 | /** 59 | * The Salesforce user identifier. 60 | */ 61 | @JsonProperty("userId") 62 | public String getUserId(){ 63 | return this.userId; 64 | } 65 | 66 | public void setUserId(String userId){ 67 | this.userId = userId; 68 | } 69 | 70 | /** 71 | * The Salesforce username. 72 | */ 73 | @JsonProperty("userName") 74 | public String getUserName(){ 75 | return this.userName; 76 | } 77 | 78 | public void setUserName(String username){ 79 | this.userName = username; 80 | } 81 | 82 | /** 83 | * User's first name. 84 | */ 85 | @JsonProperty("firstName") 86 | public String getFirstName(){ 87 | return this.firstName; 88 | } 89 | 90 | public void setFirstName(String firstName){ 91 | this.firstName = firstName; 92 | } 93 | 94 | /** 95 | * User's last name. 96 | */ 97 | @JsonProperty("lastName") 98 | public String getLastName(){ 99 | return this.lastName; 100 | } 101 | 102 | public void setLastName(String lastName){ 103 | this.lastName = lastName; 104 | } 105 | 106 | /** 107 | * Indicates whether user interface modifications for the visually impaired are on (true) or off (false). 108 | */ 109 | @JsonProperty("accessibilityModeEnabled") 110 | public boolean isAccessibilityMode(){ 111 | return this.accessibilityMode; 112 | } 113 | 114 | public void setAccessibilityMode(boolean accessibilityMode){ 115 | this.accessibilityMode = accessibilityMode; 116 | } 117 | 118 | /** 119 | * The user's email address. 120 | */ 121 | @JsonProperty("email") 122 | public String getEmail(){ 123 | return this.email; 124 | } 125 | 126 | public void setEmail(String email){ 127 | this.email = email; 128 | } 129 | 130 | /** 131 | * User's full name. 132 | */ 133 | @JsonProperty("fullName") 134 | public String getFullName(){ 135 | return this.fullName; 136 | } 137 | 138 | public void setFullName(String fullName){ 139 | this.fullName = fullName; 140 | } 141 | 142 | /** 143 | * User\u2019s locale, which controls the formatting of dates and choice of symbols for currency. 144 | */ 145 | @JsonProperty("locale") 146 | public Locale getLocale(){ 147 | return this.locale; 148 | } 149 | 150 | public void setLocale(Locale locale){ 151 | this.locale = locale; 152 | } 153 | 154 | /** 155 | * User's language, which controls the language for labels displayed in an application. 156 | */ 157 | @JsonProperty("language") 158 | public Locale getLanguage(){ 159 | return this.language; 160 | } 161 | 162 | public void setLanguage(Locale language){ 163 | this.language = language; 164 | } 165 | 166 | /** 167 | * The user's configured timezone. 168 | */ 169 | @JsonProperty("timeZone") 170 | public String getTimeZone(){ 171 | return this.timeZone; 172 | } 173 | 174 | public void setTimeZone(String timezone){ 175 | this.timeZone = timezone; 176 | } 177 | 178 | /** 179 | * Information about the user's profile identifier. 180 | */ 181 | @JsonProperty("profileId") 182 | public String getProfileId(){ 183 | return this.profileId; 184 | } 185 | 186 | public void setProfileId(String profileId){ 187 | this.profileId = profileId; 188 | } 189 | 190 | /** 191 | * Role ID of the role currently assigned to the user. 192 | */ 193 | @JsonProperty("roleId") 194 | public String getRoleId(){ 195 | return this.roleId; 196 | } 197 | 198 | public void setRoleId(String roleId){ 199 | this.roleId = roleId; 200 | } 201 | 202 | /** 203 | * Current user's license type in label form. 204 | */ 205 | public String getUserType(){ 206 | return this.userType; 207 | } 208 | 209 | public void setUserType(String userType){ 210 | this.userType = userType; 211 | } 212 | 213 | /** 214 | * Current user's default currency ISO code (applies only if multi-currency is enabled for the org). 215 | */ 216 | public String getCurrencyISOCode(){ 217 | return this.currencyISOCode; 218 | } 219 | 220 | public void setCurrencyISOCode(String currencyISOCode){ 221 | this.currencyISOCode = currencyISOCode; 222 | } 223 | 224 | /** 225 | * Returns the full profile photo of the current user. 226 | */ 227 | public String getProfilePhotoUrl(){ 228 | return this.profilePhotoUrl; 229 | } 230 | 231 | public void setProfilePhotoUrl(String profilePhotoUrl){ 232 | this.profilePhotoUrl = profilePhotoUrl; 233 | } 234 | 235 | /** 236 | * Returns the thumbnail photo of the current user. 237 | */ 238 | public String getProfileThumbnailUrl(){ 239 | return this.profileThumbnailUrl; 240 | } 241 | 242 | public void setProfileThumbnailUrl(String profileThumbnailUrl){ 243 | this.profileThumbnailUrl = profileThumbnailUrl; 244 | } 245 | 246 | @Override 247 | public String toString() 248 | { 249 | return userId+ ","+ 250 | userName+ ","+ 251 | firstName+","+ 252 | lastName+ ","+ 253 | email+ ","+ 254 | fullName+ ","+ 255 | locale+ ","+ 256 | language+ ","+ 257 | timeZone+ ","+ 258 | profileId+ ","+ 259 | roleId+ ","+ 260 | userType+ ","+ 261 | currencyISOCode+ ","+ 262 | accessibilityMode+ ","+ 263 | profilePhotoUrl+","+ 264 | profileThumbnailUrl; 265 | 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/main/java/canvas/SignedRequest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package canvas; 28 | 29 | import com.fasterxml.jackson.core.type.TypeReference; 30 | import com.fasterxml.jackson.databind.ObjectMapper; 31 | import com.fasterxml.jackson.databind.ObjectReader; 32 | import org.apache.commons.codec.binary.Base64; 33 | 34 | import javax.crypto.Mac; 35 | import javax.crypto.SecretKey; 36 | import javax.crypto.spec.SecretKeySpec; 37 | import java.io.IOException; 38 | import java.io.StringWriter; 39 | import java.security.InvalidKeyException; 40 | import java.security.NoSuchAlgorithmException; 41 | import java.util.Arrays; 42 | import java.util.HashMap; 43 | 44 | /** 45 | * 46 | * The utility method can be used to validate/verify the signed request. In this case, 47 | * the signed request is verified that it is from Salesforce and that it has not been tampered with. 48 | *

49 | * This utility class has two methods. One verifies and decodes the request as a Java object the 50 | * other as a JSON String. 51 | * 52 | */ 53 | public class SignedRequest { 54 | 55 | public static CanvasRequest verifyAndDecode(String input, String secret) throws SecurityException { 56 | 57 | String[] split = getParts(input); 58 | 59 | String encodedSig = split[0]; 60 | String encodedEnvelope = split[1]; 61 | 62 | // Deserialize the json body 63 | String json_envelope = new String(new Base64(true).decode(encodedEnvelope)); 64 | ObjectMapper mapper = new ObjectMapper(); 65 | ObjectReader reader = mapper.reader(CanvasRequest.class); 66 | CanvasRequest canvasRequest; 67 | String algorithm; 68 | try { 69 | canvasRequest = reader.readValue(json_envelope); 70 | algorithm = canvasRequest.getAlgorithm() == null ? "HMACSHA256" : canvasRequest.getAlgorithm(); 71 | } catch (IOException e) { 72 | throw new SecurityException(String.format("Error [%s] deserializing JSON to Object [%s]", e.getMessage(), CanvasRequest.class.getName()), e); 73 | } 74 | 75 | verify(secret, algorithm, encodedEnvelope, encodedSig); 76 | 77 | // If we got this far, then the request was not tampered with. 78 | // return the request as a Java object 79 | return canvasRequest; 80 | } 81 | 82 | 83 | public static String verifyAndDecodeAsJson(String input, String secret) throws SecurityException { 84 | 85 | String[] split = getParts(input); 86 | 87 | String encodedSig = split[0]; 88 | String encodedEnvelope = split[1]; 89 | 90 | String json_envelope = new String(new Base64(true).decode(encodedEnvelope)); 91 | ObjectMapper mapper = new ObjectMapper(); 92 | 93 | String algorithm; 94 | StringWriter writer; 95 | TypeReference> typeRef 96 | = new TypeReference>() { }; 97 | try { 98 | HashMap o = mapper.readValue(json_envelope, typeRef); 99 | writer = new StringWriter(); 100 | mapper.writeValue(writer, o); 101 | algorithm = (String)o.get("algorithm"); 102 | } catch (IOException e) { 103 | throw new SecurityException(String.format("Error [%s] deserializing JSON to Object [%s]", e.getMessage(), 104 | typeRef.getClass().getName()), e); 105 | } 106 | 107 | verify(secret, algorithm, encodedEnvelope, encodedSig); 108 | 109 | // If we got this far, then the request was not tampered with. 110 | // return the request as a JSON string. 111 | return writer.toString(); 112 | } 113 | 114 | private static String[] getParts(String input) { 115 | 116 | if (input == null || input.indexOf(".") <= 0) { 117 | throw new SecurityException(String.format("Input [%s] doesn't look like a signed request", input)); 118 | } 119 | 120 | String[] split = input.split("[.]", 2); 121 | return split; 122 | } 123 | 124 | private static void verify(String secret, String algorithm, String encodedEnvelope, String encodedSig ) 125 | throws SecurityException 126 | { 127 | if (secret == null || secret.trim().length() == 0) { 128 | throw new IllegalArgumentException("secret is null, did you set your environment variable CANVAS_CONSUMER_SECRET?"); 129 | } 130 | 131 | SecretKey hmacKey = null; 132 | try { 133 | byte[] key = secret.getBytes(); 134 | hmacKey = new SecretKeySpec(key, algorithm); 135 | Mac mac = Mac.getInstance(algorithm); 136 | mac.init(hmacKey); 137 | 138 | // Check to see if the body was tampered with 139 | byte[] digest = mac.doFinal(encodedEnvelope.getBytes()); 140 | byte[] decode_sig = new Base64(true).decode(encodedSig); 141 | if (! Arrays.equals(digest, decode_sig)) { 142 | String label = "Warning: Request was tampered with"; 143 | throw new SecurityException(label); 144 | } 145 | } catch (NoSuchAlgorithmException e) { 146 | throw new SecurityException(String.format("Problem with algorithm [%s] Error [%s]", algorithm, e.getMessage()), e); 147 | } catch (InvalidKeyException e) { 148 | throw new SecurityException(String.format("Problem with key [%s] Error [%s]", hmacKey, e.getMessage()), e); 149 | } 150 | 151 | // If we got here and didn't throw a SecurityException then all is good. 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/servlets/OAuthServlet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package servlets; 28 | 29 | import org.apache.commons.httpclient.HttpClient; 30 | import org.apache.commons.httpclient.HttpException; 31 | import org.apache.commons.httpclient.methods.PostMethod; 32 | import org.json.JSONException; 33 | import org.json.JSONObject; 34 | import org.json.JSONTokener; 35 | 36 | import javax.servlet.ServletException; 37 | import javax.servlet.http.HttpServlet; 38 | import javax.servlet.http.HttpServletRequest; 39 | import javax.servlet.http.HttpServletResponse; 40 | import java.io.IOException; 41 | import java.io.InputStreamReader; 42 | import java.io.UnsupportedEncodingException; 43 | import java.net.URLEncoder; 44 | 45 | /** 46 | * A Servlet for handeling OAuth flow. 47 | * This OAuth Servlet is only provided as an example and is provided as-is 48 | */ 49 | public class OAuthServlet extends HttpServlet { 50 | private static final long serialVersionUID = 1L; 51 | 52 | public static final String ACCESS_TOKEN = "ACCESS_TOKEN"; 53 | public static final String INSTANCE_URL = "INSTANCE_URL"; 54 | 55 | private String clientId = null; 56 | private String clientSecret = null; 57 | private String redirectUri = null; 58 | private String authUrl = null; 59 | private String tokenUrl = null; 60 | 61 | public void init() throws ServletException { 62 | 63 | String environment; 64 | 65 | clientId = this.getInitParameter("clientId"); 66 | clientSecret = this.getInitParameter("clientSecret"); 67 | redirectUri = this.getInitParameter("redirectUri"); // https://canvas.herokuapp.com/oauth/_callback 68 | environment = this.getInitParameter("environment"); // https://login.salesforce.com 69 | 70 | try { 71 | authUrl = environment 72 | + "/services/oauth2/authorize?response_type=code&client_id=" 73 | + clientId + "&redirect_uri=" 74 | + URLEncoder.encode(redirectUri, "UTF-8"); 75 | 76 | // Nots: &scope=email,read_chatter,... would be added here for oauth scope 77 | 78 | } catch (UnsupportedEncodingException e) { 79 | throw new ServletException(e); 80 | } 81 | 82 | tokenUrl = environment + "/services/oauth2/token"; 83 | } 84 | 85 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 86 | 87 | System.out.println("Begin OAuth"); 88 | 89 | String accessToken = (String) request.getSession().getAttribute(ACCESS_TOKEN); 90 | 91 | if (accessToken == null) { 92 | 93 | String instanceUrl = null; 94 | 95 | if (request.getRequestURI().endsWith("oauth")) { 96 | // we need to send the user to authorize 97 | response.sendRedirect(authUrl); 98 | return; 99 | } else { 100 | System.out.println("Auth successful - got callback"); 101 | 102 | String code = request.getParameter("code"); 103 | 104 | HttpClient httpclient = new HttpClient(); 105 | 106 | PostMethod post = new PostMethod(tokenUrl); 107 | post.addParameter("code", code); 108 | post.addParameter("grant_type", "authorization_code"); 109 | post.addParameter("client_id", clientId); 110 | post.addParameter("client_secret", clientSecret); 111 | post.addParameter("redirect_uri", redirectUri); 112 | 113 | try { 114 | httpclient.executeMethod(post); 115 | 116 | try { 117 | JSONObject authResponse = new JSONObject( 118 | new JSONTokener(new InputStreamReader( 119 | post.getResponseBodyAsStream()))); 120 | System.out.println("xAuth response: " 121 | + authResponse.toString(2)); 122 | 123 | accessToken = authResponse.getString("access_token"); 124 | 125 | // Instance URL is Salesforce specific. 126 | instanceUrl = authResponse.getString("instance_url"); 127 | 128 | System.out.println("Got access token: " + accessToken); 129 | } catch (JSONException e) { 130 | e.printStackTrace(); 131 | throw new ServletException(e); 132 | } 133 | } catch (HttpException e) { 134 | e.printStackTrace(); 135 | throw new ServletException(e); 136 | } 137 | finally { 138 | post.releaseConnection(); 139 | } 140 | } 141 | 142 | // Set a session attribute so that other servlets can get the access 143 | // token 144 | request.getSession().setAttribute(ACCESS_TOKEN, accessToken); 145 | 146 | // We also get the instance URL from the OAuth response, so set it 147 | // in the session too 148 | request.getSession().setAttribute(INSTANCE_URL, instanceUrl); 149 | } 150 | 151 | response.sendRedirect(request.getContextPath() + "/index.html"); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/servlets/ProxyServlet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | package servlets; 28 | 29 | import javax.servlet.ServletConfig; 30 | import javax.servlet.ServletException; 31 | import javax.servlet.http.HttpServlet; 32 | import javax.servlet.http.HttpServletRequest; 33 | import javax.servlet.http.HttpServletResponse; 34 | import java.io.IOException; 35 | import java.io.InputStream; 36 | import java.io.OutputStream; 37 | import java.net.HttpURLConnection; 38 | import java.net.URL; 39 | import java.util.Enumeration; 40 | import java.util.List; 41 | import java.util.Map; 42 | 43 | /** 44 | * ServerSide proxy for proxying request to remote server to get around cross domain issues. 45 | * This Proxy is only provided as an example and is provided as-is. 46 | */ 47 | public class ProxyServlet extends HttpServlet { 48 | 49 | // web.xml config parameter names 50 | private static final String INIT_PARAM_CONNECTION_TIMEOUT = "connection_timeout"; 51 | private static final String INIT_PARAM_FOLLOW_REDIRECTS = "follow_redirects"; 52 | private static final String INIT_PARAM_READ_TIMEOUT = "read_timeout"; 53 | private static final String INIT_PARAM_BUFFER_SIZE = "buffer_size"; 54 | private static final String INIT_PARAM_REMOTE_HOST = "remote_host"; 55 | 56 | // Configurable variables in web.xml 57 | private int connectionTimeout; 58 | private boolean followRedirects; 59 | private int readTimeout; 60 | private int bufferSize; 61 | private String remoteHost; 62 | 63 | @Override 64 | public void init(ServletConfig config) throws ServletException { 65 | super.init(config); 66 | connectionTimeout = getConfigParam(INIT_PARAM_CONNECTION_TIMEOUT, 5000); 67 | followRedirects = getConfigParam(INIT_PARAM_FOLLOW_REDIRECTS, true); 68 | readTimeout = getConfigParam(INIT_PARAM_READ_TIMEOUT, 0); 69 | bufferSize = getConfigParam(INIT_PARAM_BUFFER_SIZE, 8 * 1024); 70 | remoteHost = getConfigParam(INIT_PARAM_REMOTE_HOST, null); 71 | } 72 | 73 | @Override 74 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 75 | if ("PATCH".equals(request.getMethod())) { 76 | invoke("PATCH", request, response); 77 | } 78 | else { 79 | super.service(request, response); 80 | } 81 | } 82 | 83 | @Override 84 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 85 | invoke("GET", req, resp); 86 | } 87 | 88 | @Override 89 | protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 90 | invoke("PUT", req, resp); 91 | } 92 | 93 | @Override 94 | protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 95 | invoke("DELETE", req, resp); 96 | } 97 | 98 | @Override 99 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 100 | invoke("POST", req, resp); 101 | } 102 | 103 | @Override 104 | protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 105 | invoke("HEAD", req, resp); 106 | } 107 | 108 | @Override 109 | protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 110 | invoke("OPTIONS", req, resp); 111 | } 112 | 113 | protected void invoke(String verb, HttpServletRequest request, HttpServletResponse response) throws IOException 114 | { 115 | if (remoteHost == null) { 116 | response.sendError(HttpServletResponse.SC_BAD_REQUEST, "remote_host not configured"); 117 | return; 118 | } 119 | 120 | String uri = request.getRequestURI(); 121 | String remoteUrl = remoteHost + uri; 122 | 123 | if (request == null || remoteUrl == null || remoteUrl.equalsIgnoreCase(request.getRequestURL().toString())) { 124 | String.format("Bad preconditions remoteUrl [%s] request.getRequestURL() [%s]", remoteUrl, request.getRequestURL().toString()); 125 | return; 126 | } 127 | 128 | // Good to Go.... 129 | 130 | URL url; 131 | HttpURLConnection urlConnection = null; 132 | InputStream inputStream = null; 133 | try 134 | { 135 | //System.out.println("Producer URL: " + remoteUrl); 136 | 137 | url = new URL(remoteUrl); 138 | urlConnection = (HttpURLConnection)url.openConnection(); 139 | 140 | urlConnection.setRequestMethod(verb); 141 | urlConnection.setInstanceFollowRedirects(followRedirects); 142 | urlConnection.setConnectTimeout(connectionTimeout); 143 | urlConnection.setDoInput(true); 144 | urlConnection.setDoOutput(true); 145 | if (readTimeout != 0) urlConnection.setReadTimeout(readTimeout); 146 | 147 | copyRequestHeaders(request, urlConnection); 148 | 149 | if (verb.equals("PUT") || verb.equals("PATCH") || verb.equals("POST") ) { 150 | copyBody(urlConnection, request.getInputStream()); 151 | } 152 | 153 | response.setContentType(urlConnection.getContentType()); 154 | if (! "gzip".equalsIgnoreCase(urlConnection.getContentEncoding())) { 155 | response.setCharacterEncoding(urlConnection.getContentEncoding()); 156 | } 157 | 158 | int responseCode = urlConnection.getResponseCode(); 159 | copyResponseHeaders(response, urlConnection); 160 | 161 | response.setStatus(responseCode); 162 | 163 | int contentLength = urlConnection.getContentLength(); 164 | response.setContentLength(contentLength); 165 | 166 | if (responseCode != HttpURLConnection.HTTP_NOT_MODIFIED) { 167 | OutputStream outputStream = null; 168 | try 169 | { 170 | inputStream = urlConnection.getInputStream(); 171 | outputStream = response.getOutputStream(); 172 | byte buffer[] = new byte[bufferSize]; 173 | while (true) { 174 | int len = inputStream.read(buffer, 0, bufferSize); 175 | if (len < 0) { 176 | break; 177 | } 178 | outputStream.write(buffer, 0, len); 179 | } 180 | } finally { 181 | try { if (outputStream != null) outputStream.flush(); } catch (IOException e) {} 182 | try { if (outputStream != null) outputStream.close(); } catch (IOException e) {} 183 | } 184 | } 185 | 186 | } 187 | finally { 188 | try { if (inputStream != null) inputStream.close(); } catch (IOException e) {} 189 | try { if (urlConnection != null) urlConnection.disconnect(); } catch (Exception e) {} 190 | } 191 | } 192 | 193 | protected void copyBody(HttpURLConnection httpURLConnection, InputStream body) throws IOException 194 | { 195 | OutputStream outputStream = null; 196 | try { 197 | outputStream = httpURLConnection.getOutputStream(); 198 | int val; 199 | while ((val = body.read()) != -1) { 200 | outputStream.write(val); 201 | } 202 | } 203 | finally { 204 | try { if (body != null) body.close(); } catch (IOException e) {} 205 | try { if (outputStream != null) outputStream.flush(); } catch (IOException e) {} 206 | try { if (outputStream != null) outputStream.close(); } catch (IOException e) {} 207 | } 208 | } 209 | 210 | protected void copyRequestHeaders(HttpServletRequest request, HttpURLConnection connection) { 211 | 212 | Enumeration headerNames = request.getHeaderNames(); 213 | while (headerNames.hasMoreElements()) { 214 | 215 | String headerName = (String) headerNames.nextElement(); 216 | Enumeration headerValues = request.getHeaders(headerName); 217 | while (headerValues.hasMoreElements()) { 218 | 219 | String headerValue = (String) headerValues.nextElement(); 220 | if (headerValue != null) { 221 | connection.addRequestProperty(headerName, headerValue); 222 | } 223 | } 224 | } 225 | } 226 | 227 | protected void copyResponseHeaders(HttpServletResponse response, HttpURLConnection connection) { 228 | 229 | Map> headers = connection.getHeaderFields(); 230 | for (String name : headers.keySet()) { 231 | List values = headers.get(name); 232 | 233 | for (int i = 0; i < values.size(); i++) { 234 | String value = values.get(i); 235 | if (name == null || value == null) { 236 | continue; 237 | } 238 | response.addHeader(name, value); 239 | } 240 | } 241 | } 242 | 243 | // Helper methods 244 | private String getConfigParam(String name, String defaultValue) { 245 | String value = getServletConfig().getInitParameter(name); 246 | return (value == null || value.trim().length() == 0) ? defaultValue : value; 247 | } 248 | 249 | private int getConfigParam(String name, int defaultValue) { 250 | String value = getServletConfig().getInitParameter(name); 251 | return (value == null || value.trim().length() == 0) ? defaultValue : Integer.valueOf(value); 252 | } 253 | 254 | private boolean getConfigParam(String name, boolean defaultValue) { 255 | String value = getServletConfig().getInitParameter(name); 256 | return (value == null || value.trim().length() == 0) ? defaultValue : Boolean.valueOf(value); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Canvas SDK 7 | 8 | 9 | index.html 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/examples/chatter-talk/chatter-talk.js: -------------------------------------------------------------------------------- 1 | var chatterTalk; 2 | if (!chatterTalk) { 3 | chatterTalk = {}; 4 | } 5 | 6 | (function ($$) { 7 | 8 | "use strict"; 9 | 10 | function onClickHandler() { 11 | } 12 | 13 | chatterTalk.init = function(sr, button, input, callback) { 14 | $$.byId(button).onclick=function() { 15 | var value = $$.byId(input).value; 16 | chatterTalk.post(sr, value, callback); 17 | }; 18 | }; 19 | 20 | 21 | chatterTalk.post = function(sr, message, callback) { 22 | var url = sr.context.links.chatterFeedsUrl+"/news/"+sr.context.user.userId+"/feed-items"; 23 | var body = {body : {messageSegments : [{type: "Text", text: message}]}}; 24 | 25 | $$.client.ajax(url, 26 | {client : sr.client, 27 | method: 'POST', 28 | contentType: "application/json", 29 | data: JSON.stringify(body), 30 | success : function(data) { 31 | if ($$.isFunction(callback)) { 32 | callback(data); 33 | } 34 | } 35 | }); 36 | }; 37 | 38 | }(Sfdc.canvas)); 39 | -------------------------------------------------------------------------------- /src/main/webapp/examples/chatter-talk/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="canvas.SignedRequest" %> 2 | <%@ page import="java.util.Map" %> 3 | <% 4 | // Pull the signed request out of the request body and verify/decode it. 5 | Map parameters = request.getParameterMap(); 6 | String[] signedRequest = parameters.get("signed_request"); 7 | if (signedRequest == null) {%>This App must be invoked via a signed request!<% 8 | return; 9 | } 10 | String yourConsumerSecret=System.getenv("CANVAS_CONSUMER_SECRET"); 11 | String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret); 12 | %> 13 | 14 | 15 | 16 | 17 | 18 | Chatter Talk Example 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

32 |
33 |
34 | 35 |
36 |

37 | Chatter Talk
38 |

39 |
40 | 41 |
42 | 43 |

Speech input is not enabled in your browser.
44 | Try running Google Chrome with the --enable-speech-input flag.

45 |
46 |
47 | 48 |
49 |
50 | 51 |
52 |
53 | 62 |
63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/webapp/examples/chatter-talk/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/fa82823aaaee037db4bdc2677a7967401e7fc4f5/src/main/webapp/examples/chatter-talk/logo.png -------------------------------------------------------------------------------- /src/main/webapp/examples/chatter-talk/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/fa82823aaaee037db4bdc2677a7967401e7fc4f5/src/main/webapp/examples/chatter-talk/share.png -------------------------------------------------------------------------------- /src/main/webapp/examples/chatter-talk/talk.css: -------------------------------------------------------------------------------- 1 | #stylized { 2 | border: solid 2px #b7ddf2; 3 | /* Safari 4-5, Chrome 1-9 */ 4 | background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#E5F2F7), to(#FEFFFF)); 5 | 6 | /* Safari 5.1, Chrome 10+ */ 7 | background: -webkit-linear-gradient(top, #FEFFFF, #E5F2F7); 8 | 9 | /* Firefox 3.6+ */ 10 | background: -moz-linear-gradient(top, #FEFFFF, #E5F2F7); 11 | 12 | /* IE 10 */ 13 | background: -ms-linear-gradient(top, #FEFFFF, #E5F2F7); 14 | 15 | /* Opera 11.10+ */ 16 | background: -o-linear-gradient(top, #FEFFFF, #E5F2F7); 17 | 18 | /*background: #ebf4fb;*/ 19 | } 20 | 21 | #speech-input-field { 22 | width: 400px; 23 | height: 50px; 24 | padding: 10px 15px; 25 | margin-top: 20px; 26 | font-size: 100%; 27 | border-radius: 10px; 28 | border: 1px solid #ccc; 29 | outline: 0; 30 | } 31 | 32 | button { 33 | clear: both; 34 | width: 48px; 35 | height:24px; 36 | background: #666666 url(/examples/chatter-talk/share.png) no-repeat; 37 | } -------------------------------------------------------------------------------- /src/main/webapp/examples/hello-world/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="canvas.SignedRequest" %> 2 | <%@ page import="java.util.Map" %> 3 | <% 4 | // Pull the signed request out of the request body and verify/decode it. 5 | Map parameters = request.getParameterMap(); 6 | String[] signedRequest = parameters.get("signed_request"); 7 | if (signedRequest == null) {%> 8 | This App must be invoked via a signed request!<% 9 | return; 10 | } 11 | String yourConsumerSecret=System.getenv("CANVAS_CONSUMER_SECRET"); 12 | //String yourConsumerSecret="1818663124211010887"; 13 | String signedRequestJson = SignedRequest.verifyAndDecodeAsJson(signedRequest[0], yourConsumerSecret); 14 | %> 15 | 16 | 17 | 18 | 19 | 20 | Hello World Canvas Example 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 43 | 44 | 45 |
46 |

Hello

47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/webapp/examples/hello-world/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/fa82823aaaee037db4bdc2677a7967401e7fc4f5/src/main/webapp/examples/hello-world/logo.png -------------------------------------------------------------------------------- /src/main/webapp/images/salesforce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcedotcom/SalesforceCanvasFrameworkSDK/fa82823aaaee037db4bdc2677a7967401e7fc4f5/src/main/webapp/images/salesforce.png -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | Salesforce Canvas SDK 30 | 114 | 115 | 116 |
117 |
118 | 121 |
122 |

123 |
124 | Your sample Java application is up and running. 125 |

126 | For more information on Salesforce Canvas Framework, please visit 127 | our developer documentation. 128 |

129 |
130 |
131 | ` 132 | 133 |
134 | 135 | 136 | -------------------------------------------------------------------------------- /src/main/webapp/scripts/json2.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://www.JSON.org/json2.js 3 | 2011-01-18 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | 12 | This code should be minified before deployment. 13 | See http://javascript.crockford.com/jsmin.html 14 | 15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 16 | NOT CONTROL. 17 | 18 | 19 | This file creates a global JSON object containing two methods: stringify 20 | and parse. 21 | 22 | JSON.stringify(value, replacer, space) 23 | value any JavaScript value, usually an object or array. 24 | 25 | replacer an optional parameter that determines how object 26 | values are stringified for objects. It can be a 27 | function or an array of strings. 28 | 29 | space an optional parameter that specifies the indentation 30 | of nested structures. If it is omitted, the text will 31 | be packed without extra whitespace. If it is a number, 32 | it will specify the number of spaces to indent at each 33 | level. If it is a string (such as '\t' or ' '), 34 | it contains the characters used to indent at each level. 35 | 36 | This method produces a JSON text from a JavaScript value. 37 | 38 | When an object value is found, if the object contains a toJSON 39 | method, its toJSON method will be called and the result will be 40 | stringified. A toJSON method does not serialize: it returns the 41 | value represented by the name/value pair that should be serialized, 42 | or undefined if nothing should be serialized. The toJSON method 43 | will be passed the key associated with the value, and this will be 44 | bound to the value 45 | 46 | For example, this would serialize Dates as ISO strings. 47 | 48 | Date.prototype.toJSON = function (key) { 49 | function f(n) { 50 | // Format integers to have at least two digits. 51 | return n < 10 ? '0' + n : n; 52 | } 53 | 54 | return this.getUTCFullYear() + '-' + 55 | f(this.getUTCMonth() + 1) + '-' + 56 | f(this.getUTCDate()) + 'T' + 57 | f(this.getUTCHours()) + ':' + 58 | f(this.getUTCMinutes()) + ':' + 59 | f(this.getUTCSeconds()) + 'Z'; 60 | }; 61 | 62 | You can provide an optional replacer method. It will be passed the 63 | key and value of each member, with this bound to the containing 64 | object. The value that is returned from your method will be 65 | serialized. If your method returns undefined, then the member will 66 | be excluded from the serialization. 67 | 68 | If the replacer parameter is an array of strings, then it will be 69 | used to select the members to be serialized. It filters the results 70 | such that only members with keys listed in the replacer array are 71 | stringified. 72 | 73 | Values that do not have JSON representations, such as undefined or 74 | functions, will not be serialized. Such values in objects will be 75 | dropped; in arrays they will be replaced with null. You can use 76 | a replacer function to replace those with JSON values. 77 | JSON.stringify(undefined) returns undefined. 78 | 79 | The optional space parameter produces a stringification of the 80 | value that is filled with line breaks and indentation to make it 81 | easier to read. 82 | 83 | If the space parameter is a non-empty string, then that string will 84 | be used for indentation. If the space parameter is a number, then 85 | the indentation will be that many spaces. 86 | 87 | Example: 88 | 89 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 90 | // text is '["e",{"pluribus":"unum"}]' 91 | 92 | 93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 95 | 96 | text = JSON.stringify([new Date()], function (key, value) { 97 | return this[key] instanceof Date ? 98 | 'Date(' + this[key] + ')' : value; 99 | }); 100 | // text is '["Date(---current time---)"]' 101 | 102 | 103 | JSON.parse(text, reviver) 104 | This method parses a JSON text to produce an object or array. 105 | It can throw a SyntaxError exception. 106 | 107 | The optional reviver parameter is a function that can filter and 108 | transform the results. It receives each of the keys and values, 109 | and its return value is used instead of the original value. 110 | If it returns what it received, then the structure is not modified. 111 | If it returns undefined then the member is deleted. 112 | 113 | Example: 114 | 115 | // Parse the text. Values that look like ISO date strings will 116 | // be converted to Date objects. 117 | 118 | myData = JSON.parse(text, function (key, value) { 119 | var a; 120 | if (typeof value === 'string') { 121 | a = 122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 123 | if (a) { 124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 125 | +a[5], +a[6])); 126 | } 127 | } 128 | return value; 129 | }); 130 | 131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 132 | var d; 133 | if (typeof value === 'string' && 134 | value.slice(0, 5) === 'Date(' && 135 | value.slice(-1) === ')') { 136 | d = new Date(value.slice(5, -1)); 137 | if (d) { 138 | return d; 139 | } 140 | } 141 | return value; 142 | }); 143 | 144 | 145 | This is a reference implementation. You are free to copy, modify, or 146 | redistribute. 147 | */ 148 | 149 | /*jslint evil: true, strict: false, regexp: false */ 150 | 151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 154 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 155 | test, toJSON, toString, valueOf 156 | */ 157 | 158 | 159 | // Create a JSON object only if one does not already exist. We create the 160 | // methods in a closure to avoid creating global variables. 161 | 162 | var JSON; 163 | if (!JSON) { 164 | JSON = {}; 165 | } 166 | 167 | (function () { 168 | "use strict"; 169 | 170 | function f(n) { 171 | // Format integers to have at least two digits. 172 | return n < 10 ? '0' + n : n; 173 | } 174 | 175 | if (typeof Date.prototype.toJSON !== 'function') { 176 | 177 | Date.prototype.toJSON = function (key) { 178 | 179 | return isFinite(this.valueOf()) ? 180 | this.getUTCFullYear() + '-' + 181 | f(this.getUTCMonth() + 1) + '-' + 182 | f(this.getUTCDate()) + 'T' + 183 | f(this.getUTCHours()) + ':' + 184 | f(this.getUTCMinutes()) + ':' + 185 | f(this.getUTCSeconds()) + 'Z' : null; 186 | }; 187 | 188 | String.prototype.toJSON = 189 | Number.prototype.toJSON = 190 | Boolean.prototype.toJSON = function (key) { 191 | return this.valueOf(); 192 | }; 193 | } 194 | 195 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 196 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 197 | gap, 198 | indent, 199 | meta = { // table of character substitutions 200 | '\b': '\\b', 201 | '\t': '\\t', 202 | '\n': '\\n', 203 | '\f': '\\f', 204 | '\r': '\\r', 205 | '"' : '\\"', 206 | '\\': '\\\\' 207 | }, 208 | rep; 209 | 210 | 211 | function quote(string) { 212 | 213 | // If the string contains no control characters, no quote characters, and no 214 | // backslash characters, then we can safely slap some quotes around it. 215 | // Otherwise we must also replace the offending characters with safe escape 216 | // sequences. 217 | 218 | escapable.lastIndex = 0; 219 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) { 220 | var c = meta[a]; 221 | return typeof c === 'string' ? c : 222 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 223 | }) + '"' : '"' + string + '"'; 224 | } 225 | 226 | 227 | function str(key, holder) { 228 | 229 | // Produce a string from holder[key]. 230 | 231 | var i, // The loop counter. 232 | k, // The member key. 233 | v, // The member value. 234 | length, 235 | mind = gap, 236 | partial, 237 | value = holder[key]; 238 | 239 | // If the value has a toJSON method, call it to obtain a replacement value. 240 | 241 | if (value && typeof value === 'object' && 242 | typeof value.toJSON === 'function') { 243 | value = value.toJSON(key); 244 | } 245 | 246 | // If we were called with a replacer function, then call the replacer to 247 | // obtain a replacement value. 248 | 249 | if (typeof rep === 'function') { 250 | value = rep.call(holder, key, value); 251 | } 252 | 253 | // What happens next depends on the value's type. 254 | 255 | switch (typeof value) { 256 | case 'string': 257 | return quote(value); 258 | 259 | case 'number': 260 | 261 | // JSON numbers must be finite. Encode non-finite numbers as null. 262 | 263 | return isFinite(value) ? String(value) : 'null'; 264 | 265 | case 'boolean': 266 | case 'null': 267 | 268 | // If the value is a boolean or null, convert it to a string. Note: 269 | // typeof null does not produce 'null'. The case is included here in 270 | // the remote chance that this gets fixed someday. 271 | 272 | return String(value); 273 | 274 | // If the type is 'object', we might be dealing with an object or an array or 275 | // null. 276 | 277 | case 'object': 278 | 279 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 280 | // so watch out for that case. 281 | 282 | if (!value) { 283 | return 'null'; 284 | } 285 | 286 | // Make an array to hold the partial results of stringifying this object value. 287 | 288 | gap += indent; 289 | partial = []; 290 | 291 | // Is the value an array? 292 | 293 | if (Object.prototype.toString.apply(value) === '[object Array]') { 294 | 295 | // The value is an array. Stringify every element. Use null as a placeholder 296 | // for non-JSON values. 297 | 298 | length = value.length; 299 | for (i = 0; i < length; i += 1) { 300 | partial[i] = str(i, value) || 'null'; 301 | } 302 | 303 | // Join all of the elements together, separated with commas, and wrap them in 304 | // brackets. 305 | 306 | v = partial.length === 0 ? '[]' : gap ? 307 | '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : 308 | '[' + partial.join(',') + ']'; 309 | gap = mind; 310 | return v; 311 | } 312 | 313 | // If the replacer is an array, use it to select the members to be stringified. 314 | 315 | if (rep && typeof rep === 'object') { 316 | length = rep.length; 317 | for (i = 0; i < length; i += 1) { 318 | k = rep[i]; 319 | if (typeof k === 'string') { 320 | v = str(k, value); 321 | if (v) { 322 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 323 | } 324 | } 325 | } 326 | } else { 327 | 328 | // Otherwise, iterate through all of the keys in the object. 329 | 330 | for (k in value) { 331 | if (Object.hasOwnProperty.call(value, k)) { 332 | v = str(k, value); 333 | if (v) { 334 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 335 | } 336 | } 337 | } 338 | } 339 | 340 | // Join all of the member texts together, separated with commas, 341 | // and wrap them in braces. 342 | 343 | v = partial.length === 0 ? '{}' : gap ? 344 | '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : 345 | '{' + partial.join(',') + '}'; 346 | gap = mind; 347 | return v; 348 | } 349 | } 350 | 351 | // If the JSON object does not yet have a stringify method, give it one. 352 | 353 | if (typeof JSON.stringify !== 'function') { 354 | JSON.stringify = function (value, replacer, space) { 355 | 356 | // The stringify method takes a value and an optional replacer, and an optional 357 | // space parameter, and returns a JSON text. The replacer can be a function 358 | // that can replace values, or an array of strings that will select the keys. 359 | // A default replacer method can be provided. Use of the space parameter can 360 | // produce text that is more easily readable. 361 | 362 | var i; 363 | gap = ''; 364 | indent = ''; 365 | 366 | // If the space parameter is a number, make an indent string containing that 367 | // many spaces. 368 | 369 | if (typeof space === 'number') { 370 | for (i = 0; i < space; i += 1) { 371 | indent += ' '; 372 | } 373 | 374 | // If the space parameter is a string, it will be used as the indent string. 375 | 376 | } else if (typeof space === 'string') { 377 | indent = space; 378 | } 379 | 380 | // If there is a replacer, it must be a function or an array. 381 | // Otherwise, throw an error. 382 | 383 | rep = replacer; 384 | if (replacer && typeof replacer !== 'function' && 385 | (typeof replacer !== 'object' || 386 | typeof replacer.length !== 'number')) { 387 | throw new Error('JSON.stringify'); 388 | } 389 | 390 | // Make a fake root object containing our value under the key of ''. 391 | // Return the result of stringifying the value. 392 | 393 | return str('', {'': value}); 394 | }; 395 | } 396 | 397 | 398 | // If the JSON object does not yet have a parse method, give it one. 399 | 400 | if (typeof JSON.parse !== 'function') { 401 | JSON.parse = function (text, reviver) { 402 | 403 | // The parse method takes a text and an optional reviver function, and returns 404 | // a JavaScript value if the text is a valid JSON text. 405 | 406 | var j; 407 | 408 | function walk(holder, key) { 409 | 410 | // The walk method is used to recursively walk the resulting structure so 411 | // that modifications can be made. 412 | 413 | var k, v, value = holder[key]; 414 | if (value && typeof value === 'object') { 415 | for (k in value) { 416 | if (Object.hasOwnProperty.call(value, k)) { 417 | v = walk(value, k); 418 | if (v !== undefined) { 419 | value[k] = v; 420 | } else { 421 | delete value[k]; 422 | } 423 | } 424 | } 425 | } 426 | return reviver.call(holder, key, value); 427 | } 428 | 429 | 430 | // Parsing happens in four stages. In the first stage, we replace certain 431 | // Unicode characters with escape sequences. JavaScript handles many characters 432 | // incorrectly, either silently deleting them, or treating them as line endings. 433 | 434 | text = String(text); 435 | cx.lastIndex = 0; 436 | if (cx.test(text)) { 437 | text = text.replace(cx, function (a) { 438 | return '\\u' + 439 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 440 | }); 441 | } 442 | 443 | // In the second stage, we run the text against regular expressions that look 444 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 445 | // because they can cause invocation, and '=' because it can cause mutation. 446 | // But just to be safe, we want to reject all unexpected forms. 447 | 448 | // We split the second stage into 4 regexp operations in order to work around 449 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 450 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 451 | // replace all simple value tokens with ']' characters. Third, we delete all 452 | // open brackets that follow a colon or comma or that begin the text. Finally, 453 | // we look to see that the remaining characters are only whitespace or ']' or 454 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 455 | 456 | if (/^[\],:{}\s]*$/ 457 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 458 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 459 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 460 | 461 | // In the third stage we use the eval function to compile the text into a 462 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 463 | // in JavaScript: it can begin a block or an object literal. We wrap the text 464 | // in parens to eliminate the ambiguity. 465 | 466 | j = eval('(' + text + ')'); 467 | 468 | // In the optional fourth stage, we recursively walk the new structure, passing 469 | // each name/value pair to a reviver function for possible transformation. 470 | 471 | return typeof reviver === 'function' ? 472 | walk({'': j}, '') : j; 473 | } 474 | 475 | // If the text is not JSON parseable, then a SyntaxError is thrown. 476 | 477 | throw new SyntaxError('JSON.parse'); 478 | }; 479 | } 480 | }()); 481 | 482 | 483 | 484 | -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=1.8 --------------------------------------------------------------------------------