├── README.md ├── app.js ├── bin └── www ├── config.js ├── doc ├── login-sample.html ├── post-html-sample.html ├── post-multi-part-with-image.html ├── post-pdf-attachment.html ├── post-with-url-as-image.html └── readme.html ├── file.pdf ├── image.jpg ├── images ├── HostsFile.png └── OneNoteMSAScreen.png ├── lib ├── create-examples.js └── liveconnect-client.js ├── package.json ├── public ├── images │ └── spinner.gif ├── javascripts │ └── index.js └── stylesheets │ └── style.css ├── routes ├── callback.js └── index.js └── views ├── callback.jade ├── error.jade ├── index.jade ├── layout.jade └── result.jade /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## OneNote API Node.js Sample README 3 | 4 | Created by Microsoft Corporation, 2014. Provided As-is without warranty. Trademarks mentioned here are the property of their owners. 5 | 6 | ### API functionality demonstrated in this sample 7 | 8 | The following aspects of the API are covered in this sample. You can 9 | find additional documentation at the links below. 10 | 11 | * [Log-in the user](http://msdn.microsoft.com/EN-US/library/office/dn575435.aspx) 12 | * [POST simple HTML to a new OneNote QuickNotes page](http://msdn.microsoft.com/EN-US/library/office/dn575428.aspx) 13 | * [POST multi-part message with image data included in the request](http://msdn.microsoft.com/EN-US/library/office/dn575432.aspx) 14 | * [POST page with a URL rendered as an image](http://msdn.microsoft.com/EN-US/library/office/dn575431.aspx) 15 | * [POST page with HTML rendered as an image](http://msdn.microsoft.com/en-us/library/office/dn575432.aspx) 16 | * [POST page with a PDF file rendered and attached](http://msdn.microsoft.com/EN-US/library/office/dn655137.aspx) 17 | * [Extract the returned oneNoteClientURL and oneNoteWebURL links](http://msdn.microsoft.com/EN-US/library/office/dn575433.aspx) 18 | 19 | ### Prerequisites 20 | 21 | **Tools and Libraries** you will need to download, install, and configure for your development environment. 22 | 23 | * [Node.js](http://nodejs.org/download) 24 | * [Express framework for Node.js](http://expressjs.com) 25 | * You have a normal URL with hostname (not just an IP address) to use for the Redirect URL. If you run this from your own desktop, you'll need to modify your Hosts file (in C:\Windows\System32\drivers\etc for Windows machines and /private/etc for Macs) and map your local server IP address to a new domain name, as in the following example. 26 | ![Modify your HOSTS file to map your local server IP address.](images/HostsFile.png) 27 | 28 | **Accounts** 29 | 30 | * As the developer, you'll need to [have a Microsoft account and get a client ID string](http://msdn.microsoft.com/EN-US/library/office/dn575426.aspx) 31 | so your app can authenticate with the Microsoft Live connect SDK. 32 | * As the user of the sample, you'll need a Microsoft account so the OneNote API can 33 | send the pages to your OneDrive. 34 | 35 | ### Using the sample 36 | 37 | After you've setup your web server described above,.... 38 | 39 | 1. Download the repo as a ZIP file to your local computer, and extract the files. Or, clone the repository into a local copy of Git. 40 | 2. Go to the [Microsoft app registration page](https://account.live.com/developers/applications/index). 41 | 3. On the API Settings page, set Mobile or desktop setting to No. 42 | 4. Set the Redirect URI to the domain name of your web site, as in the following example. The root domain name must be unique, so if you use one domain for testing and another for production, you'll need to register separate client ids and secrets for each domain. 43 | ![Setting API properties in the Microsoft application portal.](images/OneNoteMSAScreen.png) 44 | 5. On the App Setting page, copy the client ID and secret into the config.js file. 45 | 6. Open a command prompt and go to the root directory of the project. 46 | 7. Setup project dependencies with the `npm install` command. 47 | 8. Run the app with the `npm start` command. 48 | 9. Open a browser and navigate to the app running by default on port 3000. 49 | 10. Login using your Microsoft account, and allow the app to create pages in your OneNote notebooks. 50 | 51 | ### Version info 52 | 53 | This is the initial public release for this code sample. 54 | 55 | 56 | ### Learning more 57 | 58 | * Visit the [dev.onenote.com](http://dev.onenote.com) Dev Center 59 | * Contact us on [StackOverflow (tagged OneNote)](http://go.microsoft.com/fwlink/?LinkID=390182) 60 | * Follow us on [Twitter @onenotedev](http://www.twitter.com/onenotedev) 61 | * Read our [OneNote Developer blog](http://go.microsoft.com/fwlink/?LinkID=390183) 62 | * Explore the API using the [apigee.com interactive console](http://go.microsoft.com/fwlink/?LinkID=392871). 63 | Also, see the [short overview/tutorial](http://go.microsoft.com/fwlink/?LinkID=390179). 64 | * [API Reference](http://msdn.microsoft.com/en-us/library/office/dn575437.aspx) documentation 65 | * [Debugging / Troubleshooting](http://msdn.microsoft.com/EN-US/library/office/dn575430.aspx) 66 | * [Getting Started](http://go.microsoft.com/fwlink/?LinkID=331026) with the OneNote API 67 | 68 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 69 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var cookieParser = require('cookie-parser'); 4 | var bodyParser = require('body-parser'); 5 | 6 | var routes = require('./routes/index'); 7 | var callback = require('./routes/callback'); 8 | 9 | var app = express(); 10 | 11 | // view engine setup 12 | app.set('views', path.join(__dirname, 'views')); 13 | app.set('view engine', 'jade'); 14 | 15 | app.use(bodyParser()); 16 | app.use(cookieParser()); 17 | app.use(express.static(path.join(__dirname, 'public'))); 18 | 19 | app.use('/', routes); 20 | app.use('/callback', callback); 21 | 22 | // catch 404 and forwarding to error handler 23 | app.use(function (req, res, next) { 24 | var err = new Error('Not Found'); 25 | err.status = 404; 26 | next(err); 27 | }); 28 | 29 | /// error handler 30 | app.use(function (err, req, res, next) { 31 | res.status(err.status || 500); 32 | res.render('error', { 33 | message: err.message, 34 | error: { 35 | status: err.status, 36 | details: err.stack 37 | } 38 | }); 39 | }); 40 | 41 | module.exports = app; 42 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var debug = require('debug')('onenote-nodejs-sample'); 3 | var app = require('../app'); 4 | 5 | app.set('port', process.env.PORT || 3000); 6 | 7 | var server = app.listen(app.get('port'), function () { 8 | debug('Express server listening on port ' + server.address().port); 9 | }); 10 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //Live Connect API information 3 | clientId: '000000004011C945', 4 | clientSecret: 'U5ofKaQiol75VbohV4hh-2x8XZpcJoPf ', 5 | redirectUrl: 'http://onenoteapisamples.com:3000/callback' 6 | }; -------------------------------------------------------------------------------- /doc/login-sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Authenticate the user with Node.js

9 | 10 |

One of the ways to sign a user in with Node.js is to have a route handle the Live Connect Authentication callback. 11 | The route controller would take the authorization code supplied by the callback and request an access token with a REST client. 12 | The access token is sent back to the client in a cookie. 13 |

14 | 15 |

The REST client:

16 |
17 |
18 |
19 | Javascript 20 |
21 |
22 | 23 |
24 |
25 |
26 |
 27 | var oauthAuthorizeUrl = 'https://login.live.com/oauth20_authorize.srf',
 28 |     oauthTokenUrl = 'https://login.live.com/oauth20_token.srf',
 29 |     clientId = config.clientId,
 30 |     clientSecret = config.clientSecret,
 31 |     redirectUrl = config.redirectUrl;
 32 | 
 33 | // Helper function to create an encoded url query string from an object
 34 | function toQueryString(obj) {
 35 |     var str = [];
 36 |     for (var p in obj)
 37 |         if (obj.hasOwnProperty(p)) {
 38 |             str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
 39 |         }
 40 |     return str.join("&");
 41 | }
 42 | 
 43 | /**
 44 |  * Obtain a Live Connect authorization endpoint URL based on configuration.
 45 |  * @returns {string} The authorization endpoint URL
 46 |  */
 47 | this.getAuthUrl = function () {
 48 |     var scopes = ['wl.signin', 'wl.basic', 'wl.offline_access', 'office.onenote_create'];
 49 |     var query = toQueryString({
 50 |         'client_id': clientId,
 51 |         'scope': scopes.join(' '),
 52 |         'redirect_uri': redirectUrl,
 53 |         'display': 'page',
 54 |         'locale': 'en',
 55 |         'response_type': 'code'
 56 |     });
 57 |     return oauthAuthorizeUrl + "?" + query;
 58 | };
 59 | 
 60 | /* Live Connect API request sender */
 61 | function requestAccessToken(data, callback) {
 62 |     request.post({url: oauthTokenUrl,
 63 |             form: _.extend({
 64 |                 'client_id': clientId,
 65 |                 'client_secret': clientSecret,
 66 |                 'redirect_uri': redirectUrl
 67 |             }, data)},
 68 |         function (error, response, body) {
 69 |             if (error) {
 70 |                 callback({});
 71 |             } else {
 72 |                 callback(JSON.parse(body));
 73 |             }
 74 |         });
 75 | }
 76 | 
 77 | /**
 78 |  * @callback accessTokenCallback
 79 |  * @param {object} Response data parsed from JSON API result
 80 |  */
 81 | 
 82 | /**
 83 |  * Request an access token by supplying an authorization code.
 84 |  * @param {string} authCode The authorization code
 85 |  * @param {accessTokenCallback} callback The callback with response data
 86 |  */
 87 | this.requestAccessTokenByAuthCode = function (authCode, callback) {
 88 |     requestAccessToken({'code': authCode, 'grant_type': 'authorization_code'}, callback);
 89 | };
 90 | 
 91 | /**
 92 |  * Request an access token by supplying a refresh token.
 93 |  * @param {string} refreshToken The refresh token
 94 |  * @param {accessTokenCallback} callback The callback with response data
 95 |  */
 96 | this.requestAccessTokenByRefreshToken = function(refreshToken, callback) {
 97 |     requestAccessToken({'refresh_token': refreshToken, 'grant_type': 'refresh_token'}, callback);
 98 | };
 99 |                     
100 |
101 |
102 |
103 |
104 | 105 |

The callback route controller:
106 | (The liveConnect variable is an instance of the REST client snippet above)

107 |
108 |
109 |
110 | Javascript 111 |
112 |
113 | 114 |
115 |
116 |
117 |
118 | /* GET Live Connect Auth callback. */
119 | router.get('/', function (req, res) {
120 |     // Get the auth code from the callback url query parameters
121 |     var authCode = req.query['code'];
122 | 
123 |     if (authCode) {
124 |         // Request an access token from the auth code
125 |         liveConnect.requestAccessTokenByAuthCode(authCode,
126 |             function (responseData) {
127 |                 var accessToken = responseData['access_token'],
128 |                     refreshToken = responseData['refresh_token'],
129 |                     expiresIn = responseData['expires_in'];
130 |                 if (accessToken && refreshToken && expiresIn) {
131 |                     // Save the access token on a session. Using cookies in this case:
132 |                     res.cookie('access_token', accessToken, { maxAge: expiresIn * 1000});
133 |                     res.cookie('refresh_token', refreshToken);
134 | 
135 |                     res.render('callback');
136 |                 } else {
137 |                     // Handle an authentication error response
138 |                     res.render('error', {
139 |                         message: 'Invalid Live Connect Response',
140 |                         error: {details: JSON.stringify(responseData, null, 2)}
141 |                     });
142 |                 }
143 |             });
144 |     } else {
145 |         // Handle an error passed from the callback query params
146 |         var authError = req.query['error'],
147 |             authErrorDescription = req.query['error_description'];
148 |         res.render('error', {
149 |             message: 'Live Connect Auth Error',
150 |             error: {status: authError, details: authErrorDescription}
151 |         });
152 |     }
153 | 
154 | });
155 |                     
156 |
157 |
158 |
159 |
160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /doc/post-html-sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Creating a simple page in a Node.js app 11 | 12 |

This snippet creates a basic OneNote page from HTML by placing the HTML directly in the HTTPS request body. 13 | It doesn't use the multi-part format. The snippet also adds the Content-Type and Authentication headers to the POST request.

14 | 15 |
16 |
17 |
18 | Javascript 19 |
20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | /**
28 |  * Create OneNote Page with Text
29 |  *
30 |  * @param {string} accessToken The access token
31 |  * @param {createPageCallback} callback The callback with response data
32 |  */
33 | this.createPageWithSimpleText = function (accessToken, callback) {
34 |     var htmlPayload =
35 |         "<!DOCTYPE html>" +
36 |         "<html>" +
37 |         "<head>" +
38 |         "    <title>A page created from basic HTML-formatted text (Node.js Sample)</title>" +
39 |         "    <meta name=\"created\" value=\"" + dateTimeNowISO() + "\">" +
40 |         "</head>" +
41 |         "<body>" +
42 |         "    <p>This is a page that just contains some simple <i>formatted</i>" +
43 |         "    <b>text</b></p>" +
44 |         "</body>" +
45 |         "</html>";
46 | 
47 |     createPage(accessToken, htmlPayload, callback, false);
48 | };
49 | /**
50 |  * @callback createPageCallback
51 |  * @param {object} Error
52 |  * @param {object} HTTP Response
53 |  * @param {string} Response body
54 |  */
55 | 
56 | var oneNotePagesApiUrl = 'https://www.onenote.com/api/v1.0/pages';
57 | 
58 | /* Pages API request builder & sender */
59 | function createPage(accessToken, payload, callback, multipart) {
60 |     var options = {
61 |         url: oneNotePagesApiUrl,
62 |         headers: {'Authorization': 'Bearer ' + accessToken}
63 |     };
64 |     // Build simple request
65 |     if (!multipart) {
66 |         options.headers['Content-Type'] = 'text/html';
67 |         options.body = payload;
68 |     }
69 |     var r = request.post(options, callback);
70 |     // Build multi-part request
71 |     if (multipart) {
72 |         var CRLF = '\r\n';
73 |         var form = r.form(); // FormData instance
74 |         _.each(payload, function (partData, partId) {
75 |             form.append(partId, partData.body, {
76 |                 // Use custom multi-part header
77 |                 header: CRLF +
78 |                     '--' + form.getBoundary() + CRLF +
79 |                     'Content-Disposition: form-data; name=\"' + partId + '\"' + CRLF +
80 |                     'Content-Type: ' + partData.contentType + CRLF + CRLF
81 |             });
82 |         });
83 |     }
84 | }
85 | 
86 | function dateTimeNowISO() {
87 |     return new Date().toISOString();
88 | }
89 |                     
90 |
91 |
92 |
93 |
94 | 95 | 96 | -------------------------------------------------------------------------------- /doc/post-multi-part-with-image.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Capture images in a Node.js app 11 | 12 |

The following snippet builds a multi-part request that contains a "Presentation" part with HTML, 13 | and a second part with binary image data. 14 | In this example, a logo image is compiled into the app, but the technique is the same for other images.

15 | 16 |
17 |
18 |
19 | Javascript 20 |
21 |
22 | 23 |
24 |
25 |
26 | 27 |
 28 | /**
 29 |  * Create OneNote Page with Text and Images
 30 |  *
 31 |  * @param {string} accessToken The access token
 32 |  * @param {createPageCallback} callback The callback with response data
 33 |  */
 34 | this.createPageWithTextAndImage = function (accessToken, callback) {
 35 |     var htmlPayload =
 36 |         "<!DOCTYPE html>" +
 37 |         "<html>" +
 38 |         "<head>" +
 39 |         "    <title>A page created containing an image (Node.js Sample)</title>" +
 40 |         "    <meta name=\"created\" value=\"" + dateTimeNowISO() + "\">" +
 41 |         "</head>" +
 42 |         "<body>" +
 43 |         "    <p>This is a page that just contains some simple <i>formatted</i>" +
 44 |         "    <b>text</b> and an image</p>" +
 45 |         "    <img src=\"name:ImageData\" width=\"426\" height=\"68\">" +
 46 |         "</body>" +
 47 |         "</html>";
 48 | 
 49 |     createPage(accessToken, {
 50 |         'Presentation': {
 51 |             body: htmlPayload,
 52 |             contentType: 'text/html'
 53 |         },
 54 |         'ImageData': {
 55 |             body: fs.readFileSync(path.normalize(__dirname + '/../image.jpg')),
 56 |             contentType: 'image/jpeg'
 57 |         }
 58 |     }, callback, true);
 59 | };
 60 | /**
 61 |  * @callback createPageCallback
 62 |  * @param {object} Error
 63 |  * @param {object} HTTP Response
 64 |  * @param {string} Response body
 65 |  */
 66 | 
 67 | var oneNotePagesApiUrl = 'https://www.onenote.com/api/v1.0/pages';
 68 | 
 69 | /* Pages API request builder & sender */
 70 | function createPage(accessToken, payload, callback, multipart) {
 71 |     var options = {
 72 |         url: oneNotePagesApiUrl,
 73 |         headers: {'Authorization': 'Bearer ' + accessToken}
 74 |     };
 75 |     // Build simple request
 76 |     if (!multipart) {
 77 |         options.headers['Content-Type'] = 'text/html';
 78 |         options.body = payload;
 79 |     }
 80 |     var r = request.post(options, callback);
 81 |     // Build multi-part request
 82 |     if (multipart) {
 83 |         var CRLF = '\r\n';
 84 |         var form = r.form(); // FormData instance
 85 |         _.each(payload, function (partData, partId) {
 86 |             form.append(partId, partData.body, {
 87 |                 // Use custom multi-part header
 88 |                 header: CRLF +
 89 |                     '--' + form.getBoundary() + CRLF +
 90 |                     'Content-Disposition: form-data; name=\"' + partId + '\"' + CRLF +
 91 |                     'Content-Type: ' + partData.contentType + CRLF + CRLF
 92 |             });
 93 |         });
 94 |     }
 95 | }
 96 | 
 97 | function dateTimeNowISO() {
 98 |     return new Date().toISOString();
 99 | }
100 |                     
101 |
102 |
103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /doc/post-pdf-attachment.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Embed files on a page in a Node.js app 11 | 12 |

The following snippet builds a multi-part request that contains a "Presentation" part with HTML, 13 | and a second part with binary data, that will be inserted into the page as an embedded file. 14 | In this example, a PDF document is compiled into the app, but the technique is the same for other files.

15 | 16 |
17 |
18 |
19 | Javascript 20 |
21 |
22 | 23 |
24 |
25 |
26 | 27 |
 28 | /**
 29 |  * Create OneNote Page with an Embedded File
 30 |  *
 31 |  * @param {string} accessToken The access token
 32 |  * @param {createPageCallback} callback The callback with response data
 33 |  */
 34 | this.createPageWithFile = function (accessToken, callback) {
 35 |     var htmlPayload =
 36 |         "<!DOCTYPE html>" +
 37 |         "<html>" +
 38 |         "<head>" +
 39 |         "    <title>A page with a file on it (Node.js Sample)</title>" +
 40 |         "    <meta name=\"created\" value=\"" + dateTimeNowISO() + "\"/>" +
 41 |         "</head>" +
 42 |         "<body>" +
 43 |         "    <object data-attachment=\"PDF File.pdf\" data=\"name:EmbeddedFile\" type=\"application/pdf\"></object>" +
 44 |         "    <img data-render-src=\"name:EmbeddedFile\" />" +
 45 |         "</body>" +
 46 |         "</html>";
 47 | 
 48 |     createPage(accessToken, {
 49 |         'Presentation': {
 50 |             body: htmlPayload,
 51 |             contentType: 'text/html'
 52 |         },
 53 |         'EmbeddedFile': {
 54 |             body: fs.readFileSync(path.normalize(__dirname + '/../file.pdf')),
 55 |             contentType: 'application/pdf'
 56 |         }
 57 |     }, callback, true);
 58 | }
 59 | /**
 60 |  * @callback createPageCallback
 61 |  * @param {object} Error
 62 |  * @param {object} HTTP Response
 63 |  * @param {string} Response body
 64 |  */
 65 | 
 66 | var oneNotePagesApiUrl = 'https://www.onenote.com/api/v1.0/pages';
 67 | 
 68 | /* Pages API request builder & sender */
 69 | function createPage(accessToken, payload, callback, multipart) {
 70 |     var options = {
 71 |         url: oneNotePagesApiUrl,
 72 |         headers: {'Authorization': 'Bearer ' + accessToken}
 73 |     };
 74 |     // Build simple request
 75 |     if (!multipart) {
 76 |         options.headers['Content-Type'] = 'text/html';
 77 |         options.body = payload;
 78 |     }
 79 |     var r = request.post(options, callback);
 80 |     // Build multi-part request
 81 |     if (multipart) {
 82 |         var CRLF = '\r\n';
 83 |         var form = r.form(); // FormData instance
 84 |         _.each(payload, function (partData, partId) {
 85 |             form.append(partId, partData.body, {
 86 |                 // Use custom multi-part header
 87 |                 header: CRLF +
 88 |                     '--' + form.getBoundary() + CRLF +
 89 |                     'Content-Disposition: form-data; name=\"' + partId + '\"' + CRLF +
 90 |                     'Content-Type: ' + partData.contentType + CRLF + CRLF
 91 |             });
 92 |         });
 93 |     }
 94 | }
 95 | 
 96 | function dateTimeNowISO() {
 97 |     return new Date().toISOString();
 98 | }
 99 |                     
100 |
101 |
102 |
103 |
104 | 105 | 106 | -------------------------------------------------------------------------------- /doc/post-with-url-as-image.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Capture web page snapshots in a Node.js app 11 | 12 |

The following code snippet builds a simple multi-part POST request with some HTML in a "Presentation" block. 13 | In that HTML is an <img> tag that shows how to specify an internet URL to the page rendering engine.

14 | 15 |
16 |
17 |
18 | Javascript 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | /**
27 |  * Create OneNote Page with a Screenshot of a URL
28 |  *
29 |  * @param {string} accessToken The access token
30 |  * @param {createPageCallback} callback The callback with response data
31 |  */
32 | this.createPageWithScreenshotFromUrl = function (accessToken, callback) {
33 |     var htmlPayload =
34 |         "<!DOCTYPE html>" +
35 |         "<html>" +
36 |         "<head>" +
37 |         "    <title>A page created with a URL snapshot on it (Node.js Sample)</title>" +
38 |         "    <meta name=\"created\" value=\"" + dateTimeNowISO() + "\"/>" +
39 |         "</head>" +
40 |         "<body>" +
41 |         "    <img data-render-src=\"http://www.onenote.com\" alt=\"An important web page\" />" +
42 |         "    Source URL: <a href=\"http://www.onenote.com\">http://www.onenote.com</a>" +
43 |         "</body>" +
44 |         "</html>";
45 | 
46 |     createPage(accessToken, htmlPayload, callback, false);
47 | };
48 | /**
49 |  * @callback createPageCallback
50 |  * @param {object} Error
51 |  * @param {object} HTTP Response
52 |  * @param {string} Response body
53 |  */
54 | 
55 | var oneNotePagesApiUrl = 'https://www.onenote.com/api/v1.0/pages';
56 | 
57 | /* Pages API request builder & sender */
58 | function createPage(accessToken, payload, callback, multipart) {
59 |     var options = {
60 |         url: oneNotePagesApiUrl,
61 |         headers: {'Authorization': 'Bearer ' + accessToken}
62 |     };
63 |     // Build simple request
64 |     if (!multipart) {
65 |         options.headers['Content-Type'] = 'text/html';
66 |         options.body = payload;
67 |     }
68 |     var r = request.post(options, callback);
69 |     // Build multi-part request
70 |     if (multipart) {
71 |         var CRLF = '\r\n';
72 |         var form = r.form(); // FormData instance
73 |         _.each(payload, function (partData, partId) {
74 |             form.append(partId, partData.body, {
75 |                 // Use custom multi-part header
76 |                 header: CRLF +
77 |                     '--' + form.getBoundary() + CRLF +
78 |                     'Content-Disposition: form-data; name=\"' + partId + '\"' + CRLF +
79 |                     'Content-Type: ' + partData.contentType + CRLF + CRLF
80 |             });
81 |         });
82 |     }
83 | }
84 | 
85 | function dateTimeNowISO() {
86 |     return new Date().toISOString();
87 | }
88 |                     
89 |
90 |
91 |
92 |
93 | 94 | 95 | -------------------------------------------------------------------------------- /doc/readme.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

OneNote API Node.js Sample README

10 |

Created by Microsoft Corporation, 2014. Provided As-is without warranty. Trademarks mentioned here are the property of their owners.

11 |

API functionality demonstrated in this sample

12 |

The following aspects of the API are covered in this sample. You can find additional documentation at the links below.

13 | 20 | 21 |

Prerequisites

22 | 23 |

Tools and Libraries you will need to download, install, and configure for your development environment.

24 | 32 | 33 |

Accounts

34 | 35 | 38 | 39 |

Using the sample

40 | 41 |

After you've setup your development tools, and checked the prerequisites listed above,....

42 | 43 |
    44 |
  1. Open a terminal and change directory to the project's directory.
  2. 45 |
  3. Setup project dependencies with the command: npm install
  4. 46 |
  5. Edit the config.js file to add in your Live Connect SDK information: Client ID, secret, redirect callback
  6. 47 |
  7. Run the app with the command: npm start
  8. 48 |
  9. Open a browser and navigate to the app running by default on port 3000.
  10. 49 |
  11. Authenticate in the running app, using your Microsoft account.
  12. 50 |
  13. Allow the app to create new pages in OneNote.
  14. 51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /file.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneNoteDev/OneNoteAPISampleNodejs/7766f2bf0e59834a4b4a4f5966344c9a1d3343ed/file.pdf -------------------------------------------------------------------------------- /image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneNoteDev/OneNoteAPISampleNodejs/7766f2bf0e59834a4b4a4f5966344c9a1d3343ed/image.jpg -------------------------------------------------------------------------------- /images/HostsFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneNoteDev/OneNoteAPISampleNodejs/7766f2bf0e59834a4b4a4f5966344c9a1d3343ed/images/HostsFile.png -------------------------------------------------------------------------------- /images/OneNoteMSAScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneNoteDev/OneNoteAPISampleNodejs/7766f2bf0e59834a4b4a4f5966344c9a1d3343ed/images/OneNoteMSAScreen.png -------------------------------------------------------------------------------- /lib/create-examples.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var _ = require('underscore'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var CreateExamples = function () { 7 | var oneNotePagesApiUrl = 'https://www.onenote.com/api/v1.0/pages'; 8 | 9 | /* Pages API request builder & sender */ 10 | function createPage(accessToken, payload, callback, multipart) { 11 | var options = { 12 | url: oneNotePagesApiUrl, 13 | headers: {'Authorization': 'Bearer ' + accessToken} 14 | }; 15 | // Build simple request 16 | if (!multipart) { 17 | options.headers['Content-Type'] = 'text/html'; 18 | options.body = payload; 19 | } 20 | var r = request.post(options, callback); 21 | // Build multi-part request 22 | if (multipart) { 23 | var CRLF = '\r\n'; 24 | var form = r.form(); // FormData instance 25 | _.each(payload, function (partData, partId) { 26 | form.append(partId, partData.body, { 27 | // Use custom multi-part header 28 | header: CRLF + 29 | '--' + form.getBoundary() + CRLF + 30 | 'Content-Disposition: form-data; name=\"' + partId + '\"' + CRLF + 31 | 'Content-Type: ' + partData.contentType + CRLF + CRLF 32 | }); 33 | }); 34 | } 35 | } 36 | 37 | function dateTimeNowISO() { 38 | return new Date().toISOString(); 39 | } 40 | 41 | /** 42 | * @callback createPageCallback 43 | * @param {object} Error 44 | * @param {object} HTTP Response 45 | * @param {string} Response body 46 | */ 47 | 48 | /** 49 | * Create OneNote Page with Text 50 | * 51 | * @param {string} accessToken The access token 52 | * @param {createPageCallback} callback The callback with response data 53 | */ 54 | this.createPageWithSimpleText = function (accessToken, callback) { 55 | var htmlPayload = 56 | "" + 57 | "" + 58 | "" + 59 | " A page created from basic HTML-formatted text (Node.js Sample)" + 60 | " " + 61 | "" + 62 | "" + 63 | "

This is a page that just contains some simple formatted" + 64 | " text

" + 65 | "" + 66 | ""; 67 | 68 | createPage(accessToken, htmlPayload, callback, false); 69 | }; 70 | 71 | /** 72 | * Create OneNote Page with Text and Images 73 | * 74 | * @param {string} accessToken The access token 75 | * @param {createPageCallback} callback The callback with response data 76 | */ 77 | this.createPageWithTextAndImage = function (accessToken, callback) { 78 | var htmlPayload = 79 | "" + 80 | "" + 81 | "" + 82 | " A page created containing an image (Node.js Sample)" + 83 | " " + 84 | "" + 85 | "" + 86 | "

This is a page that just contains some simple formatted" + 87 | " text and an image

" + 88 | " " + 89 | "" + 90 | ""; 91 | 92 | createPage(accessToken, { 93 | 'Presentation': { 94 | body: htmlPayload, 95 | contentType: 'text/html' 96 | }, 97 | 'ImageData': { 98 | body: fs.readFileSync(path.normalize(__dirname + '/../image.jpg')), 99 | contentType: 'image/jpeg' 100 | } 101 | }, callback, true); 102 | }; 103 | 104 | /** 105 | * Create OneNote Page with a Screenshot of HTML 106 | * 107 | * @param {string} accessToken The access token 108 | * @param {createPageCallback} callback The callback with response data 109 | */ 110 | this.createPageWithScreenshotFromHtml = function (accessToken, callback) { 111 | var htmlPayload = 112 | "" + 113 | "" + 114 | "" + 115 | " A page created with a screenshot of HTML on it (Node.js Sample)" + 116 | " " + 117 | "" + 118 | "" + 119 | " " + 120 | "" + 121 | "", 122 | 123 | htmlForScreenshot = 124 | "" + 125 | "" + 126 | " Embedded HTML" + 127 | "" + 128 | "" + 129 | "

This is a screen grab of a web page

" + 130 | "

" + 131 | " Lorem ipsum dolor sit amet, consectetur adipiscing elit." + 132 | " Nullam vehicula magna quis mauris accumsan, nec imperdiet nisi tempus. " + 133 | " Suspendisse potenti. Duis vel nulla sit amet turpis venenatis elementum. " + 134 | " Cras laoreet quis nisi et sagittis. Donec euismod at tortor ut porta. " + 135 | " Duis libero urna, viverra idaliquam in, ornare sed orci. " + 136 | " Pellentesque condimentum gravida felis, sed pulvinar erat suscipit sit amet. Nulla id felis quis " + 137 | " sem blandit dapibus. " + 138 | " Utviverra auctor nisi ac egestas. " + 139 | " Quisque ac neque nec velit fringilla sagittis porttitor sit amet quam." + 140 | "

" + 141 | "" + 142 | ""; 143 | 144 | createPage(accessToken, { 145 | 'Presentation': { 146 | body: htmlPayload, 147 | contentType: 'text/html' 148 | }, 149 | 'HtmlForScreenshot': { 150 | body: htmlForScreenshot, 151 | contentType: 'text/html' 152 | } 153 | }, callback, true); 154 | }; 155 | 156 | /** 157 | * Create OneNote Page with a Screenshot of a URL 158 | * 159 | * @param {string} accessToken The access token 160 | * @param {createPageCallback} callback The callback with response data 161 | */ 162 | this.createPageWithScreenshotFromUrl = function (accessToken, callback) { 163 | var htmlPayload = 164 | "" + 165 | "" + 166 | "" + 167 | " A page created with a URL snapshot on it (Node.js Sample)" + 168 | " " + 169 | "" + 170 | "" + 171 | " \"An" + 172 | " Source URL: http://www.onenote.com" + 173 | "" + 174 | ""; 175 | 176 | createPage(accessToken, htmlPayload, callback, false); 177 | }; 178 | 179 | /** 180 | * Create OneNote Page with an Embedded File 181 | * 182 | * @param {string} accessToken The access token 183 | * @param {createPageCallback} callback The callback with response data 184 | */ 185 | this.createPageWithFile = function (accessToken, callback) { 186 | var htmlPayload = 187 | "" + 188 | "" + 189 | "" + 190 | " A page with a file on it (Node.js Sample)" + 191 | " " + 192 | "" + 193 | "" + 194 | " " + 195 | " " + 196 | "" + 197 | ""; 198 | 199 | createPage(accessToken, { 200 | 'Presentation': { 201 | body: htmlPayload, 202 | contentType: 'text/html' 203 | }, 204 | 'EmbeddedFile': { 205 | body: fs.readFileSync(path.normalize(__dirname + '/../file.pdf')), 206 | contentType: 'application/pdf' 207 | } 208 | }, callback, true); 209 | } 210 | 211 | }; 212 | module.exports = new CreateExamples(); -------------------------------------------------------------------------------- /lib/liveconnect-client.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var _ = require('underscore'); 3 | 4 | var config = require('../config'); 5 | 6 | var LiveConnectClient = function () { 7 | var oauthAuthorizeUrl = 'https://login.live.com/oauth20_authorize.srf', 8 | oauthTokenUrl = 'https://login.live.com/oauth20_token.srf', 9 | clientId = config.clientId, 10 | clientSecret = config.clientSecret, 11 | redirectUrl = config.redirectUrl; 12 | 13 | // Helper function to create an encoded url query string from an object 14 | function toQueryString(obj) { 15 | var str = []; 16 | for (var p in obj) 17 | if (obj.hasOwnProperty(p)) { 18 | str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); 19 | } 20 | return str.join("&"); 21 | } 22 | 23 | /** 24 | * Obtain a Live Connect authorization endpoint URL based on configuration. 25 | * @returns {string} The authorization endpoint URL 26 | */ 27 | this.getAuthUrl = function () { 28 | var scopes = ['wl.signin', 'wl.basic', 'wl.offline_access', 'office.onenote_create']; 29 | var query = toQueryString({ 30 | 'client_id': clientId, 31 | 'scope': scopes.join(' '), 32 | 'redirect_uri': redirectUrl, 33 | 'display': 'page', 34 | 'locale': 'en', 35 | 'response_type': 'code' 36 | }); 37 | return oauthAuthorizeUrl + "?" + query; 38 | }; 39 | 40 | /* Live Connect API request sender */ 41 | function requestAccessToken(data, callback) { 42 | request.post({url: oauthTokenUrl, 43 | form: _.extend({ 44 | 'client_id': clientId, 45 | 'client_secret': clientSecret, 46 | 'redirect_uri': redirectUrl 47 | }, data)}, 48 | function (error, response, body) { 49 | if (error) { 50 | callback({}); 51 | } else { 52 | callback(JSON.parse(body)); 53 | } 54 | }); 55 | } 56 | 57 | /** 58 | * @callback accessTokenCallback 59 | * @param {object} Response data parsed from JSON API result 60 | */ 61 | 62 | /** 63 | * Request an access token by supplying an authorization code. 64 | * @param {string} authCode The authorization code 65 | * @param {accessTokenCallback} callback The callback with response data 66 | */ 67 | this.requestAccessTokenByAuthCode = function (authCode, callback) { 68 | requestAccessToken({'code': authCode, 'grant_type': 'authorization_code'}, callback); 69 | }; 70 | 71 | /** 72 | * Request an access token by supplying a refresh token. 73 | * @param {string} refreshToken The refresh token 74 | * @param {accessTokenCallback} callback The callback with response data 75 | */ 76 | this.requestAccessTokenByRefreshToken = function(refreshToken, callback) { 77 | requestAccessToken({'refresh_token': refreshToken, 'grant_type': 'refresh_token'}, callback); 78 | }; 79 | 80 | 81 | 82 | }; 83 | module.exports = new LiveConnectClient(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onenote-nodejs-sample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "express": "~4.0.0", 10 | "body-parser": "~1.0.2", 11 | "cookie-parser": "~1.0.1", 12 | "debug": "~0.7.4", 13 | "jade": "~1.3.0", 14 | "request": "^2.34.0", 15 | "underscore": "^1.6.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /public/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneNoteDev/OneNoteAPISampleNodejs/7766f2bf0e59834a4b4a4f5966344c9a1d3343ed/public/images/spinner.gif -------------------------------------------------------------------------------- /public/javascripts/index.js: -------------------------------------------------------------------------------- 1 | var $loginBtn, 2 | isLoggedIn; 3 | 4 | $(function () { 5 | $loginBtn = $('#loginBtn'); 6 | updateLoginButton(false); 7 | checkLogin(); 8 | $('#createExamples').find('button').each(function(){ 9 | $(this).on('mouseup',function(e){ 10 | $(e.target).after(''); 11 | window.setTimeout(function(){disableCreateButtons(true);},0); 12 | }); 13 | }); 14 | }); 15 | 16 | function openPopUp(url) { 17 | var width = 525, 18 | height = 630, 19 | screenTop = !!window.screenTop ? window.screenTop : window.screenY, 20 | screenLeft = !!window.screenLeft ? window.screenLeft : window.screenX, 21 | top = Math.floor(screenTop + ($(window).height() - height) / 2), 22 | left = Math.floor(screenLeft + ($(window).width() - width) / 2); 23 | 24 | var features = [ 25 | "width=" + width, 26 | "height=" + height, 27 | "top=" + top, 28 | "left=" + left, 29 | "status=no", 30 | "resizable=yes", 31 | "toolbar=no", 32 | "menubar=no", 33 | "scrollbars=yes"]; 34 | 35 | var popup = window.open(url, "oauth", features.join(",")); 36 | popup.focus(); 37 | 38 | return popup; 39 | } 40 | 41 | function showLogin() { 42 | openPopUp(window.authUrl); 43 | } 44 | 45 | function getCookie(name) { 46 | var cookies = document.cookie; 47 | name += "="; 48 | var start = cookies.indexOf(name); 49 | if (start >= 0) { 50 | start += name.length; 51 | 52 | var end = cookies.indexOf(';', start); 53 | if (end < 0) { 54 | end = cookies.length; 55 | } 56 | return cookies.substring(start, end); 57 | } 58 | return null; 59 | } 60 | 61 | function deleteAllCookies() { 62 | var cookies = document.cookie.split(";"); 63 | for (var i = 0; i < cookies.length; i++) { 64 | var cookie = cookies[i]; 65 | var eqPos = cookie.indexOf("="); 66 | var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie; 67 | document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"; 68 | } 69 | } 70 | 71 | function disableCreateButtons(disabled) { 72 | $('#createExamples').find('button').each(function () { 73 | $(this).attr('disabled', disabled); 74 | }); 75 | } 76 | 77 | function disableLoginButton(disabled) { 78 | $loginBtn.attr('disabled', disabled); 79 | } 80 | 81 | function updateLoginButton(isLoggedIn) { 82 | $loginBtn.text(isLoggedIn ? 'Sign Out' : 'Sign In'); 83 | $loginBtn.off('click'); 84 | $loginBtn.on('click', function () { 85 | disableLoginButton(true); 86 | if (isLoggedIn) { 87 | deleteAllCookies(); 88 | } else { 89 | showLogin(); 90 | } 91 | }); 92 | disableCreateButtons(!isLoggedIn); 93 | } 94 | 95 | function checkLogin() { 96 | if (getCookie("access_token")) { 97 | if (!isLoggedIn) { 98 | disableLoginButton(false); 99 | isLoggedIn = true; 100 | updateLoginButton(isLoggedIn); 101 | } 102 | } else { 103 | if (isLoggedIn) { 104 | disableLoginButton(false); 105 | isLoggedIn = false; 106 | updateLoginButton(isLoggedIn); 107 | } 108 | } 109 | } 110 | 111 | window.setInterval(checkLogin, 1000); 112 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | button { 11 | padding: 10px 20px; 12 | margin-bottom: 10px; 13 | } -------------------------------------------------------------------------------- /routes/callback.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var liveConnect = require('../lib/liveconnect-client'); 5 | 6 | /* GET Live Connect Auth callback. */ 7 | router.get('/', function (req, res) { 8 | // Get the auth code from the callback url query parameters 9 | var authCode = req.query['code']; 10 | 11 | if (authCode) { 12 | // Request an access token from the auth code 13 | liveConnect.requestAccessTokenByAuthCode(authCode, 14 | function (responseData) { 15 | var accessToken = responseData['access_token'], 16 | refreshToken = responseData['refresh_token'], 17 | expiresIn = responseData['expires_in']; 18 | if (accessToken && refreshToken && expiresIn) { 19 | // Save the access token on a session. Using cookies in this case: 20 | res.cookie('access_token', accessToken, { maxAge: expiresIn * 1000}); 21 | res.cookie('refresh_token', refreshToken); 22 | 23 | res.render('callback'); 24 | } else { 25 | // Handle an authentication error response 26 | res.render('error', { 27 | message: 'Invalid Live Connect Response', 28 | error: {details: JSON.stringify(responseData, null, 2)} 29 | }); 30 | } 31 | }); 32 | } else { 33 | // Handle an error passed from the callback query params 34 | var authError = req.query['error'], 35 | authErrorDescription = req.query['error_description']; 36 | res.render('error', { 37 | message: 'Live Connect Auth Error', 38 | error: {status: authError, details: authErrorDescription} 39 | }); 40 | } 41 | 42 | }); 43 | 44 | module.exports = router; 45 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var liveConnect = require('../lib/liveconnect-client'); 5 | var createExamples = require('../lib/create-examples'); 6 | 7 | /* GET Index page */ 8 | router.get('/', function (req, res) { 9 | var authUrl = liveConnect.getAuthUrl(); 10 | res.render('index', { title: 'OneNote API Node.js Sample', authUrl: authUrl}); 11 | }); 12 | 13 | /* POST Create example request */ 14 | router.post('/', function (req, res) { 15 | var accessToken = req.cookies['access_token']; 16 | var exampleType = req.body['submit']; 17 | 18 | // Render the API response with the created links or with error output 19 | var createResultCallback = function (error, httpResponse, body) { 20 | if (error) { 21 | return res.render('error', { 22 | message: 'HTTP Error', 23 | error: {details: JSON.stringify(error, null, 2)} 24 | }); 25 | } 26 | 27 | // Parse the body since it is a JSON response 28 | var parsedBody; 29 | try { 30 | parsedBody = JSON.parse(body); 31 | } catch (e) { 32 | parsedBody = {}; 33 | } 34 | // Get the submitted resource url from the JSON response 35 | var resourceUrl = parsedBody['links'] ? parsedBody['links']['oneNoteWebUrl']['href'] : null; 36 | 37 | if (resourceUrl) { 38 | res.render('result', { 39 | title: 'OneNote API Result', 40 | body: body, 41 | resourceUrl: resourceUrl 42 | }); 43 | } else { 44 | res.render('error', { 45 | message: 'OneNote API Error', 46 | error: {status: httpResponse.statusCode, details: body} 47 | }); 48 | } 49 | }; 50 | 51 | // Request the specified create example 52 | switch (exampleType) { 53 | case 'text': 54 | createExamples.createPageWithSimpleText(accessToken, createResultCallback); 55 | break; 56 | case 'textimage': 57 | createExamples.createPageWithTextAndImage(accessToken, createResultCallback); 58 | break; 59 | case 'html': 60 | createExamples.createPageWithScreenshotFromHtml(accessToken, createResultCallback); 61 | break; 62 | case 'url': 63 | createExamples.createPageWithScreenshotFromUrl(accessToken, createResultCallback); 64 | break; 65 | case 'file': 66 | createExamples.createPageWithFile(accessToken, createResultCallback); 67 | break; 68 | } 69 | }); 70 | 71 | module.exports = router; 72 | -------------------------------------------------------------------------------- /views/callback.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | body 5 | script. 6 | window.close(); -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.details} 7 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | button(type="button", id="loginBtn") Sign In 6 | p OneNote Create Examples: 7 | form(id="createExamples" name="createExamples", method="post") 8 | button(type="submit", name="submit", value="text" disabled) Create OneNote Page with Text 9 | br 10 | button(type="submit", name="submit", value="textimage" disabled) Create OneNote Page with Text and Images 11 | br 12 | button(type="submit", name="submit", value="html" disabled) Create OneNote Page with a Screenshot of HTML 13 | br 14 | button(type="submit", name="submit", value="url" disabled) Create OneNote Page with a Screenshot of a URL 15 | br 16 | button(type="submit", name="submit", value="file" disabled) Create OneNote Page with an Embedded File 17 | br 18 | script. 19 | window.authUrl = '!{authUrl}'; 20 | script(src="/javascripts/index.js") -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | script(src='//code.jquery.com/jquery-1.11.0.min.js') 7 | body 8 | block content -------------------------------------------------------------------------------- /views/result.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | a(href='/') Go back 6 | br 7 | p 8 | a(href=resourceUrl, target='_onenote') Access submitted resource 9 | pre #{body} --------------------------------------------------------------------------------