├── .gitignore ├── src ├── views │ ├── files-view.html │ ├── mails-view.html │ ├── reports-view.html │ ├── employees-view.html │ ├── all-view.html │ ├── partial │ │ ├── reports.html │ │ ├── employees.html │ │ ├── mails.html │ │ └── files.html │ └── home-view.html ├── content │ ├── OfficeDev.png │ ├── app.css │ ├── style.css │ └── shuffle-animation.css ├── app.module.js ├── services │ ├── officeService.js │ └── restService.js ├── app.routes.js ├── controllers │ ├── filesController.js │ ├── employeesController.js │ ├── mailsController.js │ ├── reportsController.js │ └── homeController.js └── index.html ├── package.json ├── server-production.js ├── bower.json ├── hostkey.pem ├── hostcert.pem ├── server.js ├── server-debug.js ├── OutlookAddInOffice365Api-Debug.xml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.log.* 2 | /node_modules 3 | /bower_components -------------------------------------------------------------------------------- /src/views/files-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/views/mails-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/views/reports-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/views/employees-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/views/all-view.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/content/OfficeDev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matejv1/mail-addin-for-outlook-consuming-office365-api/HEAD/src/content/OfficeDev.png -------------------------------------------------------------------------------- /src/content/app.css: -------------------------------------------------------------------------------- 1 | h5{ 2 | margin:5px 0; 3 | } 4 | md-toolbar { 5 | min-height: 25px; 6 | margin: auto; 7 | text-align: center; 8 | } -------------------------------------------------------------------------------- /src/content/style.css: -------------------------------------------------------------------------------- 1 | #container{ 2 | padding:5px; 3 | } 4 | .panel-body { 5 | padding: 10px; 6 | } 7 | .list-group { 8 | margin-bottom: 5px; 9 | } 10 | .navbar { 11 | margin-bottom: 10px; 12 | } -------------------------------------------------------------------------------- /src/views/partial/reports.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | Reports 5 | 6 | 7 | 8 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "office-app-for-outlook-angular", 3 | "description": "Office App for Outlook built using Angular 1.3.x", 4 | "version": "0.1.0", 5 | "main": "server.js", 6 | "scripts": { 7 | "init": "npm install", 8 | "install": "bower install" 9 | }, 10 | "author": { 11 | "name": "OfficeDev", 12 | "url": "http://dev.office.com" 13 | }, 14 | "license": "ISC", 15 | "dependencies": { 16 | "express": "^4.12.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // create the angular app 5 | var outlookApp = angular.module('appowa', [ 6 | 'ngRoute', 7 | 'ui.bootstrap', 8 | 'AdalAngular', 9 | 'highcharts-ng' 10 | ]); 11 | 12 | // configure the app 13 | outlookApp.config(['$logProvider', function ($logProvider) { 14 | // set debug logging to on 15 | if ($logProvider.debugEnabled) { 16 | $logProvider.debugEnabled(true); 17 | } 18 | 19 | }]); 20 | 21 | // when office has initalized, manually bootstrap the app 22 | 23 | 24 | })(); -------------------------------------------------------------------------------- /src/views/partial/employees.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | Employees ({{ ctrl.numEmployees }}) 5 | 6 |
7 | {{company.Name}} - {{company.Email}} 8 |
    9 |
  • 10 | {{ employee.FullName }} - {{ employee.JobTitle }} 11 |
  • 12 |
13 |
14 |
15 |
-------------------------------------------------------------------------------- /server-production.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | express = require('express'), 5 | http = require('http'); 6 | 7 | var app = express(); 8 | 9 | // set static routes 10 | app.use('/', express.static(__dirname + '/src')); 11 | app.use('/vendor', express.static(__dirname + '/bower_components')); 12 | 13 | var httpServer = http.createServer(app); 14 | 15 | var PORT = process.env.PORT || 80; 16 | httpServer.listen(PORT); 17 | 18 | console.log('+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+'); 19 | console.log('HTTPS Server listening @ https://%s:%s', PORT); 20 | console.log('+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+'); 21 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "office-app-for-outlook-angular", 3 | "description": "Office App for Outlook built using Angular 1.3.x", 4 | "version": "0.1.0", 5 | "authors": [ 6 | "OfficeDev" 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "test", 14 | "tests" 15 | ], 16 | "dependencies": { 17 | "microsoft.office.js": "*", 18 | "jquery": "~2.1.3", 19 | "angular": "~1.3.14", 20 | "angular-route": "~1.3.14", 21 | "angular-bootstrap": "latest", 22 | "adal-angular": "*", 23 | "highcharts-ng": "~0.0.8", 24 | "highcharts": "*", 25 | "bootstrap": "*", 26 | "ui.bootstrap": "*" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hostkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQCdnTa9AVP/1Jm7cCuanfnOaKEF9lpboYOYzA7w/IJwFW8zMM4C 3 | 652rEWXQsgmz8CADLyKK++0/BbCH5B+01D4fR1TZQK1sZie7mTM3JOPSEsOBbF0l 4 | pekZF0sWcZQ+gqdGKGnaRVN+5MXGAf5c0D0wSoBjUmZxk6MZ9Dc3IeGSNQIDAQAB 5 | AoGAdSy1zxm3qxARIkooAJDFcHBUlawqTMjsnqHF0d7H0i/9NZ9o6L7lvpH2kYpq 6 | sD0qb3ORSKY2II5ISR6Ne8/2RfvlZRQLbo6wZb/EWGHgSKjKjr4PTZsyXftX6jl2 7 | uVbZBwob14FyEbMGioCjMqThF/XofK3gJnTQadlVnNSo+5kCQQDOpbS9R7o6hMXM 8 | s0wW3Aomw6Jc2otgtP6yIN4NDwUCIrDBaI4zXtrj7vlVP243jbcdhHM7R9Cl7rZ9 9 | qYnQdrefAkEAw0Gl/h/fqVs/Rirx4l0Y8rKVQWTtlyOj9ErngC8/4gvZ1BG2rwJG 10 | WEpVYq7Lacl06hrkok/sN7OeL70iC881qwJBAKfetye4eBGHwIOc9+fuEpURAfm6 11 | U9VY0zrzNk+XbvMgKUTE4hfbK6hUftoAsFGS3zmsWkvC+D2D4V3N9zWQ63MCQAsP 12 | 8Bias7BDpC0Uh8m8Xop5ha1Dy5Yxhbt0dvMzOhpTc47pRgFaTqP36iiCILIooMAW 13 | r/dmMcriGUc5HvGuVqsCQQCAM9oMX2GH0bwrhDi82mdwbZkeRX59M8/hw+aUIhlS 14 | FwWMBvy4ZiCUhcJclwZMpOtYa3ma8s0UMQA4ri7D/ezA 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /src/views/partial/mails.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | Related Emails ({{ ctrl.emails.length }}) 5 | 6 |
7 | 16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /src/services/officeService.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .service('officeService', ['$q', officeService]); 6 | 7 | /** 8 | * Custom Angular service that works with the host Office client. 9 | * 10 | * @returns {{getWordCandidatesFromEmail: getWordCandidatesFromEmail}} 11 | */ 12 | function officeService($q) { 13 | 14 | // public signature of the service. 15 | return { 16 | getCurrentMailboxItem: getCurrentMailboxItem 17 | }; 18 | 19 | /** *********************************************************** */ 20 | 21 | function getCurrentMailboxItem(){ 22 | var deferred = $q.defer(); 23 | 24 | try { 25 | var currentEmail = Office.cast.item.toItemRead(Office.context.mailbox.item); 26 | deferred.resolve(currentEmail); 27 | } catch (error) { 28 | deferred.reject(error); 29 | } 30 | 31 | return deferred.promise; 32 | 33 | } 34 | } 35 | 36 | })(); -------------------------------------------------------------------------------- /src/content/shuffle-animation.css: -------------------------------------------------------------------------------- 1 | /* animation container - ensure animate only the body of the page*/ 2 | .view-container { 3 | position: relative; 4 | overflow: hidden; 5 | } 6 | 7 | /*#region angular shuffle animation */ 8 | .shuffle-animation.ng-enter, 9 | .shuffle-animation.ng-leave { 10 | position: relative; 11 | } 12 | 13 | .shuffle-animation.ng-enter { 14 | -moz-transition: ease-out all 0.3s 0.4s; 15 | -o-transition: ease-out all 0.3s 0.4s; 16 | -webkit-transition: ease-out all 0.3s 0.4s; 17 | transition: ease-out all 0.3s 0.4s; 18 | left: 2em; 19 | opacity: 0; 20 | } 21 | 22 | .shuffle-animation.ng-enter.ng-enter-active { 23 | left: 0; 24 | opacity: 1; 25 | } 26 | 27 | .shuffle-animation.ng-leave { 28 | -moz-transition: 0.3s ease-out all; 29 | -o-transition: 0.3s ease-out all; 30 | -webkit-transition: 0.3s ease-out all; 31 | transition: 0.3s ease-out all; 32 | left: 0; 33 | opacity: 1; 34 | } 35 | 36 | .shuffle-animation.ng-leave.ng-leave-active { 37 | left: 2em; 38 | opacity: 0; 39 | } 40 | /*#endregion */ -------------------------------------------------------------------------------- /hostcert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC+DCCAmGgAwIBAgIJAI/GEfBfWwuRMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV 3 | BAYTAlNJMQswCQYDVQQIEwJMSjESMBAGA1UEBxMJTGp1YmxqYW5hMQ8wDQYDVQQK 4 | EwZBZ2lsZTkxCjAIBgNVBAsTAUExDzANBgNVBAMTBkpvaG5ueTAeFw0xNTA3MjEw 5 | ODM2MjJaFw0xNjA3MjAwODM2MjJaMFwxCzAJBgNVBAYTAlNJMQswCQYDVQQIEwJM 6 | SjESMBAGA1UEBxMJTGp1YmxqYW5hMQ8wDQYDVQQKEwZBZ2lsZTkxCjAIBgNVBAsT 7 | AUExDzANBgNVBAMTBkpvaG5ueTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA 8 | nZ02vQFT/9SZu3Armp35zmihBfZaW6GDmMwO8PyCcBVvMzDOAuudqxFl0LIJs/Ag 9 | Ay8iivvtPwWwh+QftNQ+H0dU2UCtbGYnu5kzNyTj0hLDgWxdJaXpGRdLFnGUPoKn 10 | Rihp2kVTfuTFxgH+XNA9MEqAY1JmcZOjGfQ3NyHhkjUCAwEAAaOBwTCBvjAdBgNV 11 | HQ4EFgQU2CX9+M1o2ZKK+hLwz/o14e1wQ+wwgY4GA1UdIwSBhjCBg4AU2CX9+M1o 12 | 2ZKK+hLwz/o14e1wQ+yhYKReMFwxCzAJBgNVBAYTAlNJMQswCQYDVQQIEwJMSjES 13 | MBAGA1UEBxMJTGp1YmxqYW5hMQ8wDQYDVQQKEwZBZ2lsZTkxCjAIBgNVBAsTAUEx 14 | DzANBgNVBAMTBkpvaG5ueYIJAI/GEfBfWwuRMAwGA1UdEwQFMAMBAf8wDQYJKoZI 15 | hvcNAQEFBQADgYEAbjumluTuJyYA3P58Vi3+lZU6Uhs1FK8llMm3H0xQ6BjJANfd 16 | vpV7ZIUsxPK2IU6UYAfh6dRtBgIRtrQGpHjaSAKUU/Iqof1cJGezslH/5arnYDsT 17 | nrnLdGt1RWxeBgM4gGaBpG6hDWfSLlP2orqv/HMaEuw+BzcoQIHe/o3eNmE= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | express = require('express'), 5 | http = require('http'), 6 | https = require('https'); 7 | 8 | var https_options = { 9 | key: fs.readFileSync('./hostkey.pem'), 10 | cert: fs.readFileSync('./hostcert.pem') 11 | }; 12 | 13 | var PORT = 8443, 14 | HOST = 'localhost'; 15 | 16 | var app = express(); 17 | 18 | // set static routes 19 | app.use('/', express.static(__dirname + '/src')); 20 | app.use('/vendor', express.static(__dirname + '/bower_components')); 21 | app.use('/template', express.static(__dirname + '/bower_components/ui.bootstrap/template')); 22 | 23 | //https://localhost:8443/ui.bootstrap/template/accordion/accordion-group.html 24 | 25 | var server = https.createServer(https_options, app) 26 | .listen(PORT, HOST); 27 | 28 | 29 | var httpServer = http.createServer(app); 30 | var httpOptions = { 31 | port: 80, 32 | host: 'localhost' 33 | }; 34 | 35 | httpServer.listen(httpOptions); 36 | 37 | console.log('+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+'); 38 | console.log('HTTPS Server listening @ https://%s:%s', HOST, PORT); 39 | console.log('+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+'); 40 | -------------------------------------------------------------------------------- /server-debug.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | express = require('express'), 5 | http = require('http'), 6 | https = require('https'); 7 | 8 | var https_options = { 9 | key: fs.readFileSync('./hostkey.pem'), 10 | cert: fs.readFileSync('./hostcert.pem') 11 | }; 12 | 13 | var PORT = 8443, 14 | HOST = 'localhost'; 15 | 16 | var app = express(); 17 | 18 | // set static routes 19 | app.use('/', express.static(__dirname + '/src')); 20 | app.use('/vendor', express.static(__dirname + '/bower_components')); 21 | app.use('/template', express.static(__dirname + '/bower_components/ui.bootstrap/template')); 22 | 23 | //https://localhost:8443/ui.bootstrap/template/accordion/accordion-group.html 24 | 25 | var server = https.createServer(https_options, app) 26 | .listen(PORT, HOST); 27 | 28 | 29 | var httpServer = http.createServer(app); 30 | var httpOptions = { 31 | port: 80, 32 | host: 'localhost' 33 | }; 34 | 35 | httpServer.listen(httpOptions); 36 | 37 | console.log('+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+'); 38 | console.log('HTTPS Server listening @ https://%s:%s', HOST, PORT); 39 | console.log('+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+'); 40 | -------------------------------------------------------------------------------- /OutlookAddInOffice365Api-Debug.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | edf8aebc-6288-4c5a-b38e-146cb4fb7102 6 | 1.0.0.0 7 | Agile9.net 8 | en-US 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 300 25 | 26 |
27 |
28 | ReadItem 29 | 30 | 31 | 32 |
-------------------------------------------------------------------------------- /src/views/partial/files.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | Related Documents ({{ ctrl.files.length }}) 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
#TitleFileExtensionAuthorCreatedModified
{{file.Title}}{{file.FileExtension}}{{file.Author}}{{file.Write | date:'dd/MM/yyyy h:mma'}}{{file.LastModifiedTime | date:'dd/MM/yyyy h:mma'}}
29 |
30 |
31 |
-------------------------------------------------------------------------------- /src/app.routes.js: -------------------------------------------------------------------------------- 1 | 2 | var appConf = { 3 | tenantName: "agile9" 4 | }; 5 | 6 | var routeConf = {}; 7 | routeConf['tenant'] = appConf.tenantName + '.onmicrosoft.com'; 8 | routeConf['clientId'] = '9e03550c-1678-4093-9b12-05946b4df46b'; 9 | routeConf['cacheLocation'] = 'localStorage'; 10 | routeConf["endpoints"] = {}; 11 | routeConf.endpoints['https://' + appConf.tenantName + '.sharepoint.com/_api/'] = 'https://' + appConf.tenantName + '.sharepoint.com'; 12 | routeConf.endpoints['https://' + appConf.tenantName + '-my.sharepoint.com/_api/v1.0/me'] = 'https://' + appConf.tenantName + '-my.sharepoint.com'; 13 | routeConf.endpoints['https://outlook.office365.com/api/v1.0/me'] = 'https://outlook.office365.com'; 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | var outlookApp = angular.module('appowa'); 19 | 20 | // load routes 21 | outlookApp.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', routeConfigurator]); 22 | 23 | function routeConfigurator($routeProvider, $httpProvider, adalProvider) { 24 | 25 | //Initialize ADAL 26 | adalProvider.init(routeConf, $httpProvider); 27 | 28 | $routeProvider 29 | .when('/', { 30 | templateUrl: '/views/home-view.html', 31 | controller: 'homeController', 32 | requireADLogin: true 33 | }) 34 | .when('/files', { 35 | templateUrl: '/views/files-view.html', 36 | controller: 'homeController', 37 | controllerAs: 'vm', 38 | requireADLogin: true 39 | }) 40 | .when('/mails', { 41 | templateUrl: '/views/mails-view.html', 42 | requireADLogin: true 43 | }) 44 | .when('/employees', { 45 | templateUrl: '/views/employees-view.html', 46 | requireADLogin: true 47 | }) 48 | .when('/reports', { 49 | templateUrl: '/views/reports-view.html', 50 | requireADLogin: true 51 | }); 52 | $routeProvider.otherwise({redirectTo: '/'}); 53 | } 54 | })(); -------------------------------------------------------------------------------- /src/controllers/filesController.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .controller('filesController', ['$q', '$location', 'officeService', 'restService', filesController]) 6 | .directive('files', filesDirective); 7 | 8 | function filesDirective(){ 9 | return { 10 | restriction: 'E', 11 | templateUrl: '/views/partial/files.html' 12 | } 13 | } 14 | /** 15 | * Controller constructor 16 | * @param $q Angular's $q promise service. 17 | * @param $location Angular's $location service. 18 | * @param officeService Custom Angular service for talking to the Office client. 19 | * @param restService Custom Angular service for rest data. 20 | */ 21 | function filesController($q, $location, officeService, restService) { 22 | var vm = this; 23 | 24 | vm.status = { 25 | isFirstOpen: true, 26 | isFirstDisabled: false 27 | }; 28 | 29 | init(); 30 | 31 | /** 32 | * Initialize the controller 33 | */ 34 | function init() { 35 | getCurrentMailboxItem() 36 | .then(function(){ 37 | return getFiles(); 38 | }); 39 | } 40 | 41 | function getCurrentMailboxItem(){ 42 | var deferred = $q.defer(); 43 | 44 | officeService.getCurrentMailboxItem() 45 | .then(function(mailbox){ 46 | vm.currentMailboxItem = mailbox; 47 | deferred.resolve(); 48 | }) 49 | .catch(function (error) { 50 | deferred.reject(error); 51 | }); 52 | 53 | return deferred.promise; 54 | } 55 | 56 | function getFiles(){ 57 | var deferred = $q.defer(); 58 | 59 | restService.getFiles(vm.currentMailboxItem) 60 | .then(function(files){ 61 | vm.count = files.length || 0; 62 | vm.files = files; 63 | deferred.resolve(); 64 | }) 65 | .catch(function (error) { 66 | deferred.reject(error); 67 | }); 68 | 69 | return deferred.promise; 70 | } 71 | } 72 | })(); -------------------------------------------------------------------------------- /src/controllers/employeesController.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .controller('employeesController', ['$q', '$location', 'officeService', 'restService', employeesController]) 6 | .directive('employees', employeesDirective); 7 | 8 | function employeesDirective(){ 9 | return { 10 | restriction: 'E', 11 | templateUrl: '/views/partial/employees.html' 12 | } 13 | } 14 | /** 15 | * Controller constructor 16 | * @param $q Angular's $q promise service. 17 | * @param $location Angular's $location service. 18 | * @param officeService Custom Angular service for talking to the Office client. 19 | * @param restService Custom Angular service for rest data. 20 | */ 21 | function employeesController($q, $location, officeService, restService) { 22 | var vm = this; 23 | 24 | Office.initialize = function () { 25 | console.log(">>> Office.initialize()"); 26 | init(); 27 | }; 28 | init(); 29 | 30 | vm.status = { 31 | isFirstOpen: true, 32 | isFirstDisabled: false 33 | }; 34 | 35 | /** 36 | * Initialize the controller 37 | */ 38 | function init() { 39 | getCurrentMailboxItem() 40 | .then(function(){ 41 | return getCompany(); 42 | }); 43 | } 44 | 45 | function getCurrentMailboxItem(){ 46 | var deferred = $q.defer(); 47 | 48 | officeService.getCurrentMailboxItem() 49 | .then(function(mailbox){ 50 | 51 | vm.currentMailboxItem = mailbox; 52 | deferred.resolve(); 53 | }) 54 | .catch(function (error) { 55 | deferred.reject(error); 56 | }); 57 | 58 | return deferred.promise; 59 | } 60 | 61 | function getCompany(){ 62 | var deferred = $q.defer(); 63 | 64 | restService.getCompany(vm.currentMailboxItem) 65 | .then(function(companies){ 66 | 67 | console.log("employeesController"); 68 | console.log(companies); 69 | 70 | vm.companies = companies; 71 | vm.numEmployees = companies.length > 0 ? companies[0].Employees.length : 0; 72 | deferred.resolve(); 73 | 74 | }) 75 | .catch(function (error) { 76 | deferred.reject(error); 77 | }); 78 | 79 | return deferred.promise; 80 | } 81 | } 82 | })(); -------------------------------------------------------------------------------- /src/controllers/mailsController.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .controller('mailsController', ['$q', '$location', 'officeService', 'restService',mailsController]) 6 | .directive('mails', mailsDirective); 7 | 8 | 9 | // helper function | filter trustAsHtml 10 | angular.module('appowa').filter('to_trusted', ['$sce', function ($sce) { 11 | return function (text) { 12 | return $sce.trustAsHtml(text); 13 | }; 14 | }]); 15 | 16 | 17 | function mailsDirective(){ 18 | return { 19 | restrict: 'E', 20 | templateUrl:'/views/partial/mails.html' 21 | } 22 | } 23 | 24 | 25 | /** 26 | * Controller constructor 27 | * @param $q Angular's $q promise service. 28 | * @param $location Angular's $location service. 29 | * @param officeService Custom Angular service for talking to the Office client. 30 | * @param restService Custom Angular service for rest data. 31 | */ 32 | function mailsController($q, $location, officeService, restService) { 33 | var vm = this; 34 | 35 | /** *********************************************************** */ 36 | 37 | Office.initialize = function () { 38 | console.log(">>> Office.initialize()"); 39 | init(); 40 | }; 41 | init(); 42 | 43 | /** 44 | * Initialize the controller 45 | */ 46 | function init() { 47 | getCurrentMailboxItem() 48 | .then(function(){ 49 | return getEmails(); 50 | }); 51 | } 52 | 53 | vm.status = { 54 | isFirstOpen: true, 55 | isFirstDisabled: false 56 | }; 57 | 58 | function getCurrentMailboxItem(){ 59 | var deferred = $q.defer(); 60 | 61 | officeService.getCurrentMailboxItem() 62 | .then(function(mailbox){ 63 | 64 | vm.currentMailboxItem = mailbox; 65 | deferred.resolve(); 66 | }) 67 | .catch(function (error) { 68 | deferred.reject(error); 69 | }); 70 | 71 | return deferred.promise; 72 | } 73 | 74 | function getEmails(){ 75 | var deferred = $q.defer(); 76 | 77 | restService.getEmails(vm.currentMailboxItem) 78 | .then(function(emails){ 79 | 80 | vm.emails = emails.data.value; 81 | deferred.resolve(); 82 | 83 | }) 84 | .catch(function (error) { 85 | deferred.reject(error); 86 | }); 87 | 88 | return deferred.promise; 89 | } 90 | } 91 | })(); -------------------------------------------------------------------------------- /src/controllers/reportsController.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .controller('reportsController',['$q', '$location', 'officeService', 'restService',reportsController]) 6 | .directive('reports', reportsDirective); 7 | 8 | function reportsDirective(){ 9 | return { 10 | restrict: 'E', 11 | templateUrl:'/views/partial/reports.html' 12 | } 13 | } 14 | 15 | /** 16 | * Controller constructor 17 | * @param $q Angular's $q promise service. 18 | * @param $location Angular's $location service. 19 | * @param officeService Custom Angular service for talking to the Office client. 20 | * @param restService Custom Angular service for rest data. 21 | */ 22 | function reportsController($q, $location, officeService, restService) { 23 | var vm = this; 24 | 25 | /** *********************************************************** */ 26 | 27 | Office.initialize = function () { 28 | console.log(">>> Office.initialize()"); 29 | init(); 30 | }; 31 | init(); 32 | 33 | /** 34 | * Initialize the controller 35 | */ 36 | function init() { 37 | getCurrentMailboxItem() 38 | .then(function(){ 39 | getReports(); 40 | }); 41 | } 42 | 43 | vm.chartConfig = { 44 | options: { 45 | chart: { 46 | type: 'area' 47 | }, 48 | xAxis: { 49 | tickmarkPlacement: 'on', 50 | title: { 51 | enabled: false 52 | } 53 | }, 54 | }, 55 | title: { 56 | text: 'Annual Reports' 57 | }, 58 | credits: { 59 | enabled: true 60 | }, 61 | loading: false, 62 | size: {} 63 | } 64 | 65 | function getCurrentMailboxItem(){ 66 | var deferred = $q.defer(); 67 | 68 | officeService.getCurrentMailboxItem() 69 | .then(function(mailbox){ 70 | vm.currentMailboxItem = mailbox; 71 | deferred.resolve(); 72 | }) 73 | .catch(function (error) { 74 | deferred.reject(error); 75 | }); 76 | 77 | return deferred.promise; 78 | } 79 | 80 | function getReports(){ 81 | var deferred = $q.defer(); 82 | 83 | restService.getReports(vm.currentMailboxItem) 84 | .then(function(object){ 85 | vm.chartConfig.series = object.data; 86 | vm.chartConfig.options.xAxis.categories = object.data[0].years; 87 | deferred.resolve(); 88 | }) 89 | .catch(function (error) { 90 | deferred.reject(error); 91 | }); 92 | 93 | return deferred.promise; 94 | } 95 | } 96 | })(); -------------------------------------------------------------------------------- /src/views/home-view.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | Related Emails ({{ ctrl.emails.length }}) 6 | 7 |
8 | 17 |
18 |
19 | 20 | 21 | 22 | Related Documents ({{ ctrl.files.length }}) 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
#TitleFileExtensionAuthorCreatedModified
{{document.Title}}{{document.FileExtension}}{{document.Author}}{{document.Write | date:'dd/MM/yyyy h:mma'}}{{document.LastModifiedTime | date:'dd/MM/yyyy h:mma'}}
47 |
48 |
49 | 50 | 51 | 52 | Employees ({{ ctrl.numEmployees }}) 53 | 54 |
55 | {{company.Name}} - {{company.Email}} 56 |
    57 |
  • 58 | {{ employee.FullName }} - {{ employee.JobTitle }} 59 |
  • 60 |
61 |
62 |
63 | 64 | 65 | 66 | Reports 67 | 68 | 69 | 70 |
71 |
-------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Customer Lookup 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/services/restService.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .service('restService', ['$q', '$http', restService]); 6 | 7 | /** 8 | * Custom Angular service that talks to a static JSON file simulating a REST API. 9 | */ 10 | function restService($q, $http) { 11 | // public signature of the service 12 | return { 13 | getFiles: getFiles, 14 | getEmails: getEmails, 15 | getCompany: getCompany, 16 | getReports: getReports 17 | }; 18 | 19 | function getCompany(mailbox){ 20 | var deferred = $q.defer(); 21 | var restQueryUrl = "https://localhost:44301/api/companies?$filter=substringof(Email,'" + mailbox.from.emailAddress + "')"; 22 | 23 | $http({ 24 | method: 'GET', 25 | url: restQueryUrl, 26 | headers: { 27 | "accept": "application/json; odata=verbose", 28 | } 29 | }).success(function (data) { 30 | deferred.resolve(data); 31 | }).error(function (error) { 32 | deferred.reject(error); 33 | }); 34 | 35 | return deferred.promise; 36 | } 37 | 38 | function getFiles(mailbox) { 39 | var deferred = $q.defer(); 40 | var restQueryUrl = "https://" + appConf.tenantName + ".sharepoint.com/_api/search/query?querytext='" + mailbox.from.emailAddress + "'"; 41 | 42 | $http({ 43 | method: 'GET', 44 | url: restQueryUrl, 45 | headers: { 46 | "accept": "application/json; odata=verbose", 47 | } 48 | }).success(function (data) { 49 | var result = {}; 50 | 51 | // find the matching customer 52 | result = $.map(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (item) { 53 | return getFields(item.Cells.results); 54 | }); 55 | 56 | deferred.resolve(result); 57 | }).error(function (error) { 58 | deferred.reject(error); 59 | }); 60 | 61 | return deferred.promise; 62 | } 63 | 64 | function getEmails(mailbox) { 65 | var deferred = $q.defer(); 66 | var restQueryUrl = "https://outlook.office365.com/api/v1.0/me/messages?$filter=From/EmailAddress/Address eq '" + mailbox.from.emailAddress + "'&$top=5"; 67 | 68 | return $http({ 69 | url: restQueryUrl, 70 | method: "GET", 71 | headers: { 72 | "accept": "application/json", 73 | } 74 | }).success(function (data) { 75 | deferred.resolve(data); 76 | }).error(function (error) { 77 | deferred.reject(error); 78 | }); 79 | 80 | return deferred.promise; 81 | } 82 | 83 | function getReports(mailbox) { 84 | var deferred = $q.defer(); 85 | var restQueryUrl = "https://localhost:44301/api/reports?mail=" + mailbox.from.emailAddress; 86 | 87 | return $http({ 88 | url: restQueryUrl, 89 | method: "GET", 90 | headers: { 91 | "accept": "application/json", 92 | } 93 | }).success(function (data) { 94 | deferred.resolve(data); 95 | }).error(function (error) { 96 | deferred.reject(error); 97 | }); 98 | 99 | return deferred.promise; 100 | } 101 | } 102 | })(); 103 | 104 | //helper function for rest-search result formating 105 | function getFields(results) { 106 | r = {}; 107 | for (var i = 0; i < results.length; i++) { 108 | if (results[i] != undefined && results[i].Key != undefined) { 109 | r[results[i].Key] = results[i].Value; 110 | } 111 | } 112 | return r; 113 | } 114 | -------------------------------------------------------------------------------- /src/controllers/homeController.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('appowa') 5 | .controller('homeController', 6 | ['$q', '$location', 'officeService', 'restService', 7 | homeController]); 8 | 9 | /** 10 | * Controller constructor 11 | * @param $q Angular's $q promise service. 12 | * @param $location Angular's $location service. 13 | * @param officeService Custom Angular service for talking to the Office client. 14 | * @param restService Custom Angular service for rest data. 15 | */ 16 | function homeController($q, $location, officeService, restService) { 17 | var vm = this; 18 | 19 | /** *********************************************************** */ 20 | 21 | Office.initialize = function () { 22 | console.log(">>> Office.initialize()"); 23 | init(); 24 | }; 25 | init(); 26 | 27 | /** 28 | * Initialize the controller 29 | */ 30 | function init() { 31 | getCurrentMailboxItem() 32 | .then(function(){ 33 | getFiles() 34 | getEmails() 35 | getCompany(); 36 | getReports(); 37 | }); 38 | } 39 | 40 | vm.chartConfig = { 41 | options: { 42 | chart: { 43 | type: 'area' 44 | }, 45 | xAxis: { 46 | tickmarkPlacement: 'on', 47 | title: { 48 | enabled: false 49 | } 50 | }, 51 | }, 52 | title: { 53 | text: 'Annual Reports' 54 | }, 55 | credits: { 56 | enabled: true 57 | }, 58 | loading: false, 59 | size: {} 60 | } 61 | 62 | function getCurrentMailboxItem(){ 63 | var deferred = $q.defer(); 64 | 65 | officeService.getCurrentMailboxItem() 66 | .then(function(mailbox){ 67 | vm.currentMailboxItem = mailbox; 68 | deferred.resolve(); 69 | }) 70 | .catch(function (error) { 71 | deferred.reject(error); 72 | }); 73 | 74 | return deferred.promise; 75 | } 76 | 77 | function getFiles(){ 78 | var deferred = $q.defer(); 79 | 80 | restService.getFiles(vm.currentMailboxItem) 81 | .then(function(files){ 82 | vm.files = files; 83 | deferred.resolve(); 84 | }) 85 | .catch(function (error) { 86 | deferred.reject(error); 87 | }); 88 | 89 | return deferred.promise; 90 | } 91 | 92 | function getEmails(){ 93 | var deferred = $q.defer(); 94 | 95 | restService.getEmails(vm.currentMailboxItem) 96 | .then(function(emails){ 97 | vm.emails = emails.data.value; 98 | deferred.resolve(); 99 | }) 100 | .catch(function (error) { 101 | deferred.reject(error); 102 | }); 103 | 104 | return deferred.promise; 105 | } 106 | 107 | function getCompany(){ 108 | var deferred = $q.defer(); 109 | 110 | restService.getCompany(vm.currentMailboxItem) 111 | .then(function(companies){ 112 | vm.companies = companies; 113 | vm.numEmployees = companies.length > 0 ? companies[0].Employees.length : 0; 114 | deferred.resolve(); 115 | }) 116 | .catch(function (error) { 117 | deferred.reject(error); 118 | }); 119 | 120 | return deferred.promise; 121 | } 122 | 123 | function getReports(){ 124 | var deferred = $q.defer(); 125 | 126 | restService.getReports(vm.currentMailboxItem) 127 | .then(function(object){ 128 | vm.chartConfig.series = object.data; 129 | vm.chartConfig.options.xAxis.categories = object.data[0].years; 130 | deferred.resolve(); 131 | }) 132 | .catch(function (error) { 133 | deferred.reject(error); 134 | }); 135 | 136 | return deferred.promise; 137 | } 138 | } 139 | })(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | More: http://blog.agile9.net/2015/08/18/mail-addin-for-outlook-using-office-365-apis-adal-js-angularjs-webapi-azure-ad/ 2 | 3 | # Mail Addin For Outlook Using Office 365 APIs (ADAL.JS, ANGULARJS, WEBAPI, AZURE AD) # 4 | 5 | ### Summary ### 6 | This sample demonstrates mail add-in for Outlook which extracts data from a mail message and retrieves data from Office 365 APIs. Node.js is used for server side code, AngularJS for front-end and for authentication purposes Adal.js + Azure AD. Additionaly(not required to run) there is .NET WebApi project to show how we can retrive data from custom REST APIs. (projectUrl) 7 | 8 | ### Applies to ### 9 | - Outlook on Office 365 (outlook.office365.com) - Google Chrome 10 | 11 | ### Prerequisites ### 12 | - Office 365 Developer Subscription. See [Sign up for an Office 365 Developer Subscription and set up your tools and environment](https://msdn.microsoft.com/EN-US/library/office/fp179924.aspx) 13 | - Must have an Office 365 developer site. See [How to: Create a Developer Site within your existing Office 365 subscription](https://msdn.microsoft.com/en-us/library/office/jj692554.aspx) 14 | - [Node.js](https://nodejs.org) environment (locally for development, Azure can be used for production) 15 | - [Azure](http://azure.microsoft.com) accout is required - you will need to configure Azure Active Directory for this sample. See [Step 2: Register the sample with your Azure Active Directory tenant](https://github.com/AzureADSamples/SinglePageApp-AngularJS-DotNet#step-2--register-the-sample-with-your-azure-active-directory-tenant) 16 | - Optionaly: you will run .NET WebApi project to show additional sample data (seen on picture as Reports, Employees) as an example how we can consume external REST APIs. Use this [agile9.outlook.context.db - Code First Entity Framework 6.0 Sample Project With Data](https://github.com/matejv1/agile9.outlook.context.db) 17 | 18 | ### Solution ### 19 | Solution | Author(s) | Twitter 20 | ---------|-----------|-------- 21 | Context | Matej Vodopivc (**Agile9.net**) | [@matejvodopivc](https://twitter.com/matejvodopivc) 22 | 23 | 24 | 25 | ### Version history ### 26 | Version | Date | Comments 27 | ---------| -----| -------- 28 | 1.0 | Aug 29rd 2015 | Initial release 29 | 30 | ### Disclaimer ### 31 | **THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** 32 | 33 | 34 | ---------- 35 | 36 | # Mail Addin For Outlook Using Office 365 APIs # 37 | This code sample demonstrates the use of an add-in for Outlook for showing additional data to user from Office 365 APIs and custom REST APIs. 38 | 39 | Video of the working sample: [YouTube - Mail Addin for Outlook - Office 365, Adal.js, AngularJS, Node.js](https://www.youtube.com/watch?v=EhppDWba6XY) 40 | 41 | ![](https://agile9blog.files.wordpress.com/2015/08/snagit0.png?w=672&h=372&crop=1) 42 | 43 | More images of this sample: [blog.agile9.net](http://blog.agile9.net/2015/08/18/mail-addin-for-outlook-using-office-365-apis-adal-js-angularjs-webapi-azure-ad/) 44 | 45 | ## 1. Building this sample ## 46 | This sample consists of 3 primary components: 47 | 48 | 1. Node.js server side code - running locally (can be hosted anywhere, Azure for example fully supports Node.js arhitecture) 49 | 2. Addin for Office Manifest - defines how our add-in is activated within Outlook 50 | 3. Front-end code - HTML markup and AngularJS Javascript for interacting with the server-side API 51 | 52 | 53 | ### 1.1 Configure Azure Active Directory ### 54 | 55 | This is already well documented. See [Step 2: Register the sample with your Azure Active Directory tenant](https://github.com/AzureADSamples/SinglePageApp-AngularJS-DotNet#step-2--register-the-sample-with-your-azure-active-directory-tenant) 56 | 57 | Note: 58 | 1. Make sure you create Key 59 | 2. make changes to Manifest file (Download, oauth2AllowImplicitFlow: true, Upload back) 60 | 3. give an app required permissions (Exchange, Sharepoint Online) 61 | 62 | ### 1.2 Configure App Settings ### 63 | 64 | 1. Open project location in Explorer. Open src/app.routes.js 65 | 2. Change tenant name (replace "agile9" with your Office 365 tenant name) 66 | 67 | ### 1.3 Install Node.js Dependecies ### 68 | 69 | 1. Open source location on local computer using Explorer. 70 | 2. Open Command Prompt in this folder (Hold SHIFT + Right click -> Open Command Windows here) 71 | 3. Run: npm install bower 72 | 4. Run: npm install 73 | 74 | ### 1.4 Upload Office Manifest to Exchange ### 75 | 76 | 1. Navigate to portal.office.com 77 | 2. Select Admin 78 | 3. In the left side menu select ADMIN -> Exchange 79 | 4. On Exchange admin center page select "add-ins" under "organization" group 80 | 5. Select Add from File and upload manifest 81 | 82 | ### 1.5 Running the sample 83 | 84 | 1. Follow steps in 1.2 to navigate to right location using CMD 85 | 2. Run: npm start 86 | 3. Open in Chrome browser: https://localhost:8443/#/ 87 | 4. Accept SSL warning (certificate is not verifed by know authority) 88 | 5. Open https://outlook.office365.com and see the result 89 | 90 | ### 1.6 Hosting/Running this sample in Azure 91 | 92 | 1. You will need to replace server.js with content from server-production.js. 93 | 2. For the rest you should follow this article [Build and deploy a Node.js web app in Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/web-sites-nodejs-develop-deploy-mac/?utm_content=buffer1e07e&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer) 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------