- •2. Launch Visual Studio 2013 as administrator.
- •Implementing a product recommendation solution using Microsoft Azure Overview Problem statement
- •Solution
- •Solution architecture
- •Setting up the Azure services Prerequisites
- •Deploying the Azure landscape using PowerShell
- •Publishing the retailer demo website using Visual Studio
- •Testing the product recommendation site
- •Verifying execution of the Azure Data Factory workflow
- •Viewing product-to-product recommendations
- •Deep dive into the solution
- •Prepare
- •Analyze
- •Publish
- •Consume
- •Next steps
- •Useful resources
- •Roll back Azure changes
- •Terms of use
Deep Dive into Apps for Office with Word
In this lab you will get hands-on experience developing an App for Office which targets Microsoft Word.
Prerequisites: Before you can start this lab, you must have installed Office 2013 with Service Pack 1 and Visual Studio 2013 with Update 2 on your development workstation.
Exercise 1: Creating the ContentWriter App for Office Project
In this exercise you will create a new App for Office project in Visual Studio so that you can begin to write, test and debug an App for Office which targets Microsoft Word. The user interface of the App for Office you will create in this lab will not be very complicated as it will just contain HTML buttons and JavaScript command handlers.
1. Launch Visual Studio 2013 as administrator.
2. From the File menu select the New Project command. When the New Project dialog appears, select the App for Office project template from the Office/SharePoint template folder as shown below. Name the new project ContentWriter and click OK to create the new project.
3. When you create a new App for Office project, Visual Studio prompts you with the Choose the app type page of the Create app for Office dialog. This is the point where you select the type of App for Office you want to create. Leave the default setting with the radio button titled Task pane and select Next to continue.
4. On the Choose the host applications page of the Create app for Office dialog, clear all of the Office applications except for Word, and then click Finish to create the new Visual Studio solution.
5. Display the Solution Explorer pane to view the structure of the new Visual Studio solution once it has been created. If the Solution Explorer pane is not visible, choose Solution Explorer from the View menu.
At a high-level, the new solution has been created using two Visual Studio projects named ContentWriter and ContentWriterWeb. You should also observe that the ContentWriter project contains a top-level manifest for the app named ContentWriterManifest which contains a single file named ContentWriter.xml.
6. In the Solution Explorer, double-click on the node named ContentWriterManifest to open the app manifest file in the Visual Studio designer. Update the Display Name settings in the app manifest from ContentWriter to Content Writer App.
7. Save and close ContentWriterManifest.
8. Over the next few steps you will walk through the default app implementation that Visual Studio generated for you when the app project was created. Begin by looking at the structure of the app folder which has two important files named app.css and app.js which contain CSS styles and JavaScript code which is to be used on an app-wide basis.
9. You can see that inside the app folder there is a child folder named Home which contains three files named Home.html, Home.css and Home.js. Note that the app project is currently configured to use Home.html as the app's start page and that Home.html is linked to both Home.css and Home.js.
10. Double-click on app.js to open it in a code editor window. you should be able to see that the code creates a global variable named app based on the JavaScript Closure pattern. The global app object defines a method named initialize but it does not execute this method.
var app = (function () { "use strict"; var app = {}; // Common initialization function (to be called from each page) app.initialize = function () { $('body').append( '<div id="notification-message">' + '<div class="padding">' + '<div id="notification-message-close"></div>' + '<div id="notification-message-header"></div>' + '<div id="notification-message-body"></div>' + '</div>' + '</div>'); $('#notification-message-close').click(function () { $('#notification-message').hide(); }); // After initialization, expose a common notification function app.showNotification = function (header, text) { $('#notification-message-header').text(header); $('#notification-message-body').text(text); $('#notification-message').slideDown('fast'); }; }; return app; })();
11. Close app.js and be sure not to save any changes.
12. Next you will examine the JavaScript code in home.js. Double-click on home.js to open it in a code editor window. Note that Home.html links to app.js before it links to home.js which means that JavaScript code written in Home.js can access the global app object created in app.js.
13. Walk through the code in Home.js and see how it uses a self-executing function to register an event handler on the Office.initialize method which in turn registers a document-ready event handler using jQuery. This allows the app to call app.initialize and to register an event handler using the getDataFromSelection function.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); $('#get-data-from-selection').click(getDataFromSelection); }); }; // Reads data from current document selection and displays a notification function getDataFromSelection() { Office.context.document.getSelectedDataAsync(Office.CoercionType.Text, function (result) { if (result.status === Office.AsyncResultStatus.Succeeded) { app.showNotification('The selected text is:', '"' + result.value + '"'); } else { app.showNotification('Error:', result.error.message); } }); } })();
14. Delete the entire getDataFromSelection function from Home.js. In addition, remove the following line of code: $('#get-data-from-selection').click(getDataFromSelection); Your code should match the following code listing.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // your app initialization code goes here }); }; })();
15. Save your changes to Home.js. You will return to this source file after you have added your HTML layout to Home.html.
16. Now it time to examine the HTML that has been added to the project to create the app's user interface. Double-click Home.html to open this file in a Visual Studio editor window. Examine the layout of HTML elements inside the body element.
<body> <div id="content-header"> <div class="padding"> <h1>Welcome</h1> </div> </div> <div id="content-main"> <div class="padding"> <p><strong>Add home screen content here.</strong></p> <p>For example:</p> <button id="get-data-from-selection">Get data from selection</button> <p style="margin-top: 50px;"> <a target="_blank" href="https://go.microsoft.com/fwlink/?LinkId=276812">Find more samples online...</a> </p> </div> </div> </body>
17. Locate the text h1 tag that has the text Welcome. Replace the word Welcome with the phrase Add Content to Document. Your markup should match the following markup: <div id="content-header"> <div class="padding"> <h1>Add Content to Document</h1> </div> </div>
18. Locate the div tag that has the id value content-main. Delete all of the content within the tags with the exception of the following markup:
<div id="content-main"> <div class="padding"> </div> </div>
19. Update the content-main div to match the following HTML layout. This adds a set of buttons to the app's layout.
<div id="content-main"> <div class="padding"> <div> <button id="addContentHellowWorld">Hello World</button> </div> <div> <button id="addContentHtml">HTML</button> </div> <div> <button id="addContentMatrix">Matrix</button> </div> <div> <button id="addContentOfficeTable">Office Table</button> </div> <div> <button id="addContentOfficeOpenXml">Office Open XML</button> </div> </div> </div>
20. Save and close Home.html.
21. Open the CSS file named Home.css and add the following CSS rule to ensure that all the app's command buttons and select element have a uniform width and spacing.
#content-main button, #content-main select{ width: 210px; margin: 8px; }
22. Save and close Home.js.
23.
Now
it's time to test the app using the Visual Studio debugger. Press the
{F5}
key to run the project in the Visual Studio debugger. The debugger
should launch Microsoft Word 2013 and you should see your App for
Office in the task pane on the right side of a new Word document as
shown in the following screenshot.
Note: You may see a
Security Alert dialog that asks if you want to trust the self-signed
Localhost certificate. If you see this Alert, click the Yes
button to continue. Then, click the Yes
button on the dialog that displays to install the certificate. If the
Office Activation Wizard displays, click the Cancel
button to continue.
24. Close Microsoft Word to terminate your debugging session and return to Visual Studio.
25. Return to the source file named Home.js or open it if it is not already open.
26. Add a new function named testForSuccess with the following implementation.
function testForSuccess(asyncResult) { if (asyncResult.status === Office.AsyncResultStatus.Failed) { app.showNotification('Error', asyncResult.error.message); } }
27. Create a function named onAddContentHellowWorld and add the following call to setSelectedDataAsync.
function onAddContentHellowWorld() { Office.context.document.setSelectedDataAsync("Hello World!", testForSuccess); }
28. Finally, add a line of jQuery code into the app initialization logic to bind the click event of the addContentHellowWorld button to the onAddContentHellowWorld function.
Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // add this code to wire up event handler $("#addContentHellowWorld").click(onAddContentHellowWorld) }); };
29. When you are done, the Home.js file should match the following listing.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // wire up event handler $("#addContentHellowWorld").click(onAddContentHellowWorld) }); }; // write text data to current at document selection function onAddContentHellowWorld() { Office.context.document.setSelectedDataAsync("Hello World!", testForSuccess); } function testForSuccess(asyncResult) { if (asyncResult.status === Office.AsyncResultStatus.Failed) { app.showNotification('Error', asyncResult.error.message); } } })();
30. Save your changes to Home.js.
31. Now test the functionality of the app. Press the {F5} key to begin a debugging session and click the Hello World button. You should see that "Hello World" has been added into the cursor position of the Word document.
32. You have now successfully run and tested the app and its JavaScript logic using the Visual Studio debugger. Close Microsoft Word to stop the debugging session and return to Visual Studio.
Exercise 2: Writing Content to a Word Document Using Coercion Types
In this exercise you will continue working on the Visual Studio solution for the ContentWriter app you created in the previous exercise. You will add additional JavaScript code to insert content into the current Word document in a variety of formats.
1. In Visual Studio, make sure you have the ContentWriter project open.
2. In the Solution Explorer, double click on Home.js to open this JavaScript file in an editor window.
3. Just below the onAddContentHelloWorld function, add four new functions named onAddContentHtml, onAddContentMatrix, onAddContentOfficeTable and onAddContentOfficeOpenXml.
function onAddContentHellowWorld() { Office.context.document.setSelectedDataAsync("Hello World!", testForSuccess); } function onAddContentHtml() { } function onAddContentMatrix() { } function onAddContentOfficeTable() { } function onAddContentOfficeOpenXml() { }
4. Just below the call to app.initialize, add the jQuery code required to bind each of the four new functions to the click event of the associated buttons.
Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // wire up event handler $("#addContentHellowWorld").click(onAddContentHellowWorld); $('#addContentHtml').click(onAddContentHtml); $('#addContentMatrix').click(onAddContentMatrix); $('#addContentOfficeTable').click(onAddContentOfficeTable); $('#addContentOfficeOpenXml').click(onAddContentOfficeOpenXml); }); };
5. Implement the onAddContentHtml function to create an HTML div element with several child elements using jQuery and then to write that HTML to the Word document using the HTML coercion type using the code in the following listing.
function onAddContentHtml() { // create HTML element var div = $("<div>") .append($("<h2>").text("My Heading")) .append($("<p>").text("This is paragraph 1")) .append($("<p>").text("This is paragraph 2")) // insert HTML into Word document Office.context.document.setSelectedDataAsync(div.html(), { coercionType: "html" }, testForSuccess); }
6. Test your work by starting a debug session and clicking the HTML button. When you click the button, you should see that the HTML content has been added to the Word document.
7. Implement onAddContentMatrix by creating an array of arrays and then by writing the matrix to the Word document using the matrix coercion type as shown in the following code listing.
function onAddContentMatrix() { // create matrix as an array of arrays var matrix = [["First Name", "Last Name"], ["Bob", "White"], ["Anna", "Conda"], ["Max", "Headroom"]]; // insert matrix into Word document Office.context.document.setSelectedDataAsync(matrix, { coercionType: "matrix" }, testForSuccess); }
8. Test your work by starting a debug session and clicking the Matrix button. When you click the button, you should see that the content from the matrix has been added to the Word document as a table.
9. Implement onAddContentOfficeTable by creating a new Office.TableData object and then by writing it to the Word document using the table coercion type as shown in the following code listing.
function onAddContentOfficeTable() { // create and populate an Office table var myTable = new Office.TableData(); myTable.headers = [['First Name', 'Last Name']]; myTable.rows = [['Bob', 'White'], ['Anna', 'Conda'], ['Max', 'Headroom']]; // add table to Word document Office.context.document.setSelectedDataAsync(myTable, { coercionType: "table" }, testForSuccess) }
10. Test your work by starting a debug session and clicking the Office Table button. When you click the button, you should see that the content from the Office Table object has been added to the Word document as a table.
11. You have now finished exercise 2 and it is time to move on to exercise 3.
Exercise 3: Writing Content to a Word Document using Office Open XML
In this exercise you will continue working on the Visual Studio solution for the ContentWriter app you worked on in the previous exercise. You will extend the app's capabilities by adding JavaScript code to insert content into the active Word document using Open Office XML.
1. In Solution Explorer, right-click the App\Home folder for the ContentWriterWeb project folder. Select Add, then Existing Item.
2. Navigate to the following folder: C:\Labfiles\O3652-2 Deep Dive Apps for Office in Word\Starter files
This folder contains four XML files as shown in the following screenshot.
3. Add the four XML files into the Visual Studio project in the App\Home folder.
4. Quickly open and review the XML content inside each of these four XML files. This will give you better idea of what Open Office XML looks like when targeting Microsoft Word.
5. Open Home.html and locate the button element with the id of addContentOfficeOpenXml. Directly under this button, add a new HTML select element as shown in the following code listing.
<div> <button id="addContentOfficeOpenXml">Office Open XML</button> <select id="listOpenXmlContent"> <option value="OpenXmlParagraph.xml">Paragraph</option> <option value="OpenXmlPicture.xml">Picture</option> <option value="OpenXmlChart.xml">Chart</option> <option value="OpenXmlTable.xml">Table</option> </select> </div>
6. Save and close Home.html.
7. Return to the code editor window with Home.js.
8. Implement the onAddContentOfficeOpenXml function to obtain the currently selected file name from the select element and then to execute an HTTP GET request using the jQuery $.ajax function to retrieve the associated XML file. In the success callback function, call setSelectedDataAsync to write the XML content to the current Word document using the ooxml coercion type as shown in the following code listing.
function onAddContentOfficeOpenXml() { var fileName = $("#listOpenXmlContent").val(); $.ajax({ url: fileName, type: "GET", dataType: "text", success: function (xml) { Office.context.document.setSelectedDataAsync(xml, { coercionType: "ooxml" }, testForSuccess) } }); }
9. Test your work by starting a debug session and clicking the Office Open XML button when the select element has the default selected value of Paragraph. You should see that the Open Office XML content has been used to created a formatted paragraph.
10. Change the value of the select element to Picture and click the Office Open XML button. You should see that the Open Office XML content has been used to insert a image into the document.
11. Change the value of the select element to Chart and click the Office Open XML button. You should see that the Open Office XML content has been used to created a simple bar chart.
12. Change the value of the select element to Table and click the Office Open XML button. You should see that the Open Office XML content has been used to created a formatted table.
13. You have now completed this lab
Deep Dive into Apps for Office with Excel
In this lab you will get hands-on experience developing an App for Office which targets Microsoft Excel and creates bindings between the app and a spreadsheet.
Prerequisites: Before you can start this lab, you must have installed Office 2013 with Service Pack 1 and Visual Studio 2013 with Update 2 on your development workstation.
Exercise 1: Creating the LoanAppraisal App for Office Project
In this exercise you will create a new App for Office project in Visual Studio so that you can begin to write, test and debug an App for Office which targets Microsoft Excel.
1. Launch Visual Studio 2013.
2. From the File menu select the New Project command. When the New Project dialog appears, expand the Visual C# node. Select the App for Office project template from the Office/SharePoint template folder as shown below. Name the new project LoanAppraisal and click OK to create the new project.
3. When you create a new App for Office project, Visual Studio prompts you with the Choose the app type page of the Create app for Office dialog. This is the point where you select the type of App for Office you want to create. Leave the default setting with the radio button titled Task pane and select Next to continue.
4. On the Choose the host applications page of the Create app for Office dialog, clear all of the Office application check boxes except for Excel, and then click Finish to create the new Visual Studio solution.
5. If the Solution Explorer is not visible, click on the View menu at the top of the page and select Solution Explorer. Take a look at the structure of the new Visual Studio solution. At a high-level, the new solution has been created using two Visual Studio projects named LoanAppraisal and LoanAppraisalWeb. You should also observe that the LoanAppraisal project contains a top-level manifest for the app named LoanAppraisalManifest which contains a single file named LoanAppraisal.xml.
6. In the Solution Explorer, double-click on the node named LoanAppraisalManifest to open the app manifest file in the Visual Studio designer. Update the Display Name settings in the app manifest from LoanAppraisal to Loan Appraisal App.
7. Save and close LoanAppraisalManifest.
8. Over the next few steps you will walk through the default app implementation that Visual Studio generated for you when the app project was created. Begin by looking at the structure of the app folder which has two important files named app.css and app.js which contain CSS styles and JavaScript code which is to be used on an app-wide basis.
9. You can see that inside the app folder there is a child folder named Home which contains three files named Home.html, Home.css and Home.js. Note that the app project is currently configured to use Home.html as the app's start page and that Home.html is linked to both Home.css and Home.js.
10. Double-click on app.js to open it in a code editor window. You should be able to see that the code creates a global variable named app based on the JavaScript Closure pattern. The global app object defines a method named initialize but it does not execute this method.
var app = (function () { "use strict"; var app = {}; // Common initialization function (to be called from each page) app.initialize = function () { $('body').append( '<div id="notification-message">' + '<div class="padding">' + '<div id="notification-message-close"></div>' + '<div id="notification-message-header"></div>' + '<div id="notification-message-body"></div>' + '</div>' + '</div>'); $('#notification-message-close').click(function () { $('#notification-message').hide(); }); // After initialization, expose a common notification function app.showNotification = function (header, text) { $('#notification-message-header').text(header); $('#notification-message-body').text(text); $('#notification-message').slideDown('fast'); }; }; return app; })();
11. Close app.js and be sure not to save any changes.
12. Next you will examine the JavaScript code in home.js. Double-click on home.js to open it in a code editor window. Note that Home.html links to app.js before it links to home.js which means that JavaScript code written in Home.js can access the global app object created in app.js.
13. Walk through the code in Home.js and see how it uses a self-executing function to register an event handler on the Office.initialize method which in turn registers a document-ready event handler using jQuery. This allows the app to call app.initialize and to register an event handler using the getDataFromSelection function.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); $('#get-data-from-selection').click(getDataFromSelection); }); }; // Reads data from current document selection and displays a notification function getDataFromSelection() { Office.context.document.getSelectedDataAsync(Office.CoercionType.Text, function (result) { if (result.status === Office.AsyncResultStatus.Succeeded) { app.showNotification('The selected text is:', '"' + result.value + '"'); } else { app.showNotification('Error:', result.error.message); } }); } })();
14. Remove the following line of code from the Office.initialize method: $('#get-data-from-selection').click(getDataFromSelection);
15. Delete the getDataFromSelection function from Home.js. Your code should match the following code listing.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // your app initialization code goes here }); }; })();
16. Save the changes to Home.js. You will return to this source file after you have added your HTML layout to Home.html.
17. Examine the HTML markup that has been added to the project to create the app's user interface. Double-click Home.html to open this file in a Visual Studio editor window. Examine the layout of HTML elements inside the body element.
<body> <div id="content-header"> <div class="padding"> <h1>Welcome</h1> </div> </div> <div id="content-main"> <div class="padding"> <p><strong>Add home screen content here.</strong></p> <p>For example:</p> <button id="get-data-from-selection">Get data from selection</button> <p style="margin-top: 50px;"> <a target="_blank" href=https://go.microsoft.com/fwlink/?LinkId=276812> Find more samples online... </a> </p> </div> </div> </body>
18. Replace the text message of Welcome inside the h1 element with a different message such as Loan Information. Replace the contents of the content-main DIV element with the following HTML markup: <div id="content-main"> <div class="padding"> <div id="results"></div> </div> </div> Your markup should match the HTML markup shown below. <body> <div id="content-header"> <div class="padding"> <h1>Loan Information</h1> </div> </div> <div id="content-main"> <div class="padding"> <div id="results"></div> </div> </div> </body>
19. Save and close Home.html.
20. Return to Home.js. Add the following code to write a simple message to the results DIV element: $('#results').text("Hello world"); Your code should resemble the following:
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); $('#results').text("Hello world"); }); } })();
21. Save Home.js.
22.
Now
it's time to test the app using the Visual Studio debugger. Press the
Start
button
on the toolbar to run the project in the Visual Studio debugger.
If
you see a Security Alert window appear that asks you if you want to
trust the self-signed Localhost certificate, click the Yes
button,
then on the following Security Warning dialog box, click the Yes
button
again. The debugger should launch Microsoft Excel 2013.
If
you are asked to activate Office Professional Plus 2013, leave the
radio button selected as is, and click the Next
button.
Then click the Close
button. You should see your LoanAppraisal
app in the task pane on the right side of a new Excel workbook as
shown in the following screenshot.
23. Close Microsoft Excel to terminate your debugging session and return to Visual Studio. If you are asked to save your changes, click the Don’t Save button.
Exercise 2: Adding a Test Document to an Apps for Office project
In this exercise you continue to work on the LoanAppraisal project you created in the previous lab by integrating a preexisting Excel workbook into the development process. This will make it possible for you to develop an app that binds to named ranges within the workbook.
1. Ensure that you still have the LoanAppraisal app project opened in Visual Studio.
2. Using Windows Explorer, look in the Starter Files folder inside C:\Labfiles\O3652-4 Deep Dive Apps for Office in Excel\ and find the workbook file named TestDoc.xlsx.
3. Double-click on TestDoc.xlsx to open the workbook in Microsoft Excel. The workbook provides mortgage loan information and a chart as shown in the following screenshot:
4. Close TestDoc.xlsx and also close Microsoft Excel.
5. Using Windows Explorer, copy the file TestDoc.xlsx into the folder Documents\Visual Studio 2013\Projects\LoanAppraisal\LoanAppraisal. Then, in Visual Studio, right click on the LoanAppraisal project and select Add from the menu, then Existing Item, and select TestDoc.xlsx from the window. When you are done, you should be able to see TestDoc.xlsx at the root of the the LoanAppraisal project right below LoanAppraisalManifest as shown in the following screenshot.
6. With the LoanAppraisal project selected in the Solution Explorer, locate the properties window and set the value of the Start Document property to TestDoc.xslx.
7. Press the Start button to begin a debugging session. Visual Studio initializes the debugging session with TestDoc.xlsx instead of using a new Excel workbook. If the workbook opens in PROTECTED VIEW, click on the Enable Editing button. You might notice that the LoanAppraisal app has not be activated. In the Excel ribbon, navigate to the Insert tab and select Loan Appraisal App from the Apps My Apps drop down menu.
Note: If the app does not display, press the Start button in Visual Studio a second time.
8. You should now see that the app has activated over in the task pane.
9. Inside Excel, save your changes to TestDoc.xlsx to update the test file to include the app in future debugging sessions.
10. Close TestDoc.xlsx and then close Microsoft Excel.
11. Return to Visual Studio and press the Start button to start another debugging session. The app should be initialized automatically when Visual Studio initialize a debugging session.
12. Now that you have integrated the test document into your project, it is time to move ahead to the next exercise where you will write code to bind to named ranges in the workbook.
Exercise 3: Adding Bindings between an App and an Excel Workbook
In this exercise you will write code to create bindings on named ranges within the the Excel workbook named TestDoc.xlsx. You will also create event handlers so that the app responds to the user when updating the app user interface.
1. The workbook TestDoc.xlsx contains several cells that have already been defined as named ranges. Review the following list which shows the names of the Excel named ranges that you will be programming against in this exercise.
– Applicant_Name
– Loan_Amount
– Interest_Rate
– Loan_Duration
– Monthly_Payment
– Total_Income
– Yearly_Mortgage
– Fixed_Expenses
– Available_Income
2. In Visual Studio, open Home.html in an editor window.
3. Modify the contents of the content-main DIV element with the code shown in the following listing. If you would rather not type it all in by hand, you copy and paste this HTML code from content-main.css.txt. This file is located in the following folder: C:\Labfiles\O3652-4 Deep Dive Apps for Office in Excel\Starter Files
<div id="content-main"> <div id="currentApplicantInfo"> <table> <tr> <td colspan="2" class="header_cell">Loan Application Detail</td> </tr> <tr> <td>Name:</td> <td id="applicant_name"> </td> </tr> <tr> <td>Loan Amount:</td> <td id="loan_amount"> </td> </tr> <tr> <td>Interest Rate:</td> <td id="interest_rate"> </td> </tr> <tr> <td>Load Duration:</td> <td id="loan_duration"> </td> </tr> <tr> <td>Monthy Payment:</td> <td id="monthly_payment"> </td> </tr> <tr> <td colspan="2" class="header_cell">High-level Finanical Summary</td> </tr> <tr> <td>Total Income:</td> <td id="total_income"> </td> </tr> <tr> <td>Yearly Morgage:</td> <td id="yearly_mortgage"> </td> </tr> <tr> <td>Fixed Expenses:</td> <td id="fixed_expenses"> </td> </tr> <tr> <td>Available Income:</td> <td id="available_income"> </td> </tr> </table> </div> <div class="padding"> <h3>Interest Rate</h3> <div id="selectInterestRate" class="section"></div> <h3>Select a loan applicant</h3> <div id="selectApplicant" class="section"></div> </div> </div>
4. Save and close Home.html.
5. Open Home.css in an editor window.
6. Add the following CSS rules to Home.css. If you would rather not type it all in by hand, you copy and paste this HTML code from Home.css.txt which is located in the Starter Files folder for this lab.
body { padding: 0px; background-color: #eee; } h3 { margin: 2px; } #currentApplicantInfo { margin: 0px; padding: 0px; } .section { margin: 0px; padding: 0px; padding-top: 2px; padding-bottom: 4px; } .section input[type="radio"] { margin: 0px; margin-left: 4px; padding: 0px; } .section label { margin: 0px; padding: 0px; font-size: 0.8em; } #currentApplicantInfo table { margin: 0px; width: 100%; box-sizing: border-box; border: 1px solid black; border-collapse: collapse; } #currentApplicantInfo table td { min-width: 100px; border: 1px solid #ddd; border-collapse: collapse; padding: 2px; padding-left: 4px; background: white; font-size: 1.0em; } #currentApplicantInfo table td.header_cell { color: #eee; background-color: navy; font-weight: bold; border: 1px solid black; } #monthly_payment { color: red; }
7. Save and close Home.css.
8. Open Home.js in a code editor widow. Remove the following line of code.
$('#results').text("Hello world");
9. At this point, the code in Home.js should look like the following code listing.
/// <reference path="../App.js" /> (function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); }); } })();
10. Start a debugging session by pressing the Start button on the toolbar to inspect the app's new HTML layout. You should see the user interface appears like the one in the following screenshot.
11. Close Excel and return to Visual Studio.
12. Inside Home.js, place the cursor under the "use strict;" statement at the top of the closure and add the following code. If you would rather not type this code by hand, you can copy and paste it from Home.js_Part1.txt inside the Starter Files folder for this lab.
var officeDoc; var bindings; var interestRates = [0.0425, 0.0500, 0.0750]; var currentRate = interestRates[0]; var applicants = [ { name: "Brian Cox", loan_amount: 100000, loan_duration: 30, total_income: 82000, fixed_expenses: 22000 }, { name: "Wendy Wheeler", loan_amount: 325000, loan_duration: 30, total_income: 145000, fixed_expenses: 40000 }, { name: "Ken Sanchez", loan_amount: 225000, loan_duration: 30, total_income: 162000, fixed_expenses: 40000 }, { name: "Joe Healy", loan_amount: 625000, loan_duration: 30, total_income: 182000, fixed_expenses: 72000 }, { name: "Mke Fitzmaurice", loan_amount: 725000, loan_duration: 8, total_income: 320000, fixed_expenses: 120000 }, { name: "Chris Sells", loan_amount: 1225000, loan_duration: 15, total_income: 325000, fixed_expenses: 167000 } ]; var currentApplicant = applicants[0];
13. After this step is complete, your Home.js file should match the following code listing.
/// <reference path="../App.js" /> (function () { "use strict"; var officeDoc; var bindings; var interestRates = [0.0425, 0.0500, 0.0750]; var currentRate = interestRates[0]; var applicants = [ { name: "Brian Cox", loan_amount: 100000, loan_duration: 30, total_income: 82000, fixed_expenses: 22000 }, { name: "Wendy Wheeler", loan_amount: 325000, loan_duration: 30, total_income: 145000, fixed_expenses: 40000 }, { name: "Ken Sanchez", loan_amount: 225000, loan_duration: 30, total_income: 162000, fixed_expenses: 40000 }, { name: "Joe Healy", loan_amount: 625000, loan_duration: 30, total_income: 182000, fixed_expenses: 72000 }, { name: "Mke Fitzmaurice", loan_amount: 725000, loan_duration: 8, total_income: 320000, fixed_expenses: 120000 }, { name: "Chris Sells", loan_amount: 1225000, loan_duration: 15, total_income: 325000, fixed_expenses: 167000 } ]; var currentApplicant = applicants[0];
// The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); }); } })();
14. Place your cursor near the bottom of the Home.js file, just above the last line. Add five new functions named updateAppUI, onInitializeUI, formatToCurrencyUSD, onRateChanged and onApplicantChanged.
// The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); }); }; function updateAppUI() { } function onInitializeUI() { } function formatToCurrencyUSD(amount) { } function onRateChanged() { } function onApplicantChanged() { }
15. Implement the updateAppUI function by adding the following code. If you prefer, you can copy and paste this code from the file named updateAppUI.js.txt in the Starter Files folder for this lab.
function updateAppUI() { $("#applicant_name").text(currentApplicant.name); $("#loan_amount").text(formatToCurrencyUSD(currentApplicant.loan_amount)); $("#interest_rate").text((currentRate * 100) + "%"); $("#loan_duration").text(currentApplicant.loan_duration + " years"); $("#total_income").text(formatToCurrencyUSD(currentApplicant.total_income)); $("#fixed_expenses").text(formatToCurrencyUSD(currentApplicant.fixed_expenses)); }
16. Implement the onInitializeUI function by adding the following code. If you prefer, you can copy and paste this code from the file named onInitializeUI.js.txt in the Starter Files folder for this lab.
function onInitializeUI() { var divRates = $("#selectInterestRate"); divRates.empty(); for (var i = 0; i < interestRates.length; ++i) { var rate = interestRates[i]; divRates.append($('<input>', { type: 'radio', name: 'rate', value: rate })); var formatedRate = (rate * 100).toFixed(2) + "%"; divRates.append($('<label>').text(formatedRate)); divRates.append($("<br>")); } var divApplicants = $("#selectApplicant"); divApplicants.empty(); for (i = 0; i < applicants.length; ++i) { var name = applicants[i].name; divApplicants.append($('<input>', { type: 'radio', name: 'Applicant', value: i })); divApplicants.append($('<label>').text(applicants[i].name)); divApplicants.append($("<br>")); } $("#selectInterestRate :first-child").attr("checked", "checked"); $("#selectApplicant :first-child").attr("checked", "checked"); $("input[name='rate']").click(onRateChanged); $("input[name='Applicant']").click(onApplicantChanged); updateAppUI(); }
17. Implement the formatToCurrencyUSD function by adding the following code. If you prefer, you can copy and paste this code from the file named formatToCurrencyUSD.js.txt in the Starter Files folder for this lab.
function formatToCurrencyUSD(amount) { var sign; var cents; var i; amount = amount.toString().replace(/\$|\,/g, ''); if (isNaN(amount)) { amount = "0"; } sign = (amount == (amount = Math.abs(amount))); amount = Math.floor(amount * 100 + 0.50000000001); cents = amount % 100; amount = Math.floor(amount / 100).toString(); if (cents < 10) { cents = '0' + cents; } for (i = 0; i < Math.floor((amount.length - (1 + i)) / 3) ; i++) { amount = amount.substring(0, amount.length - (4 * i + 3)) + ',' + amount.substring(amount.length - (4 * i + 3)); } return (((sign) ? '' : '-') + '$' + amount + '.' + cents); }
18. Implement onRateChanged and onApplicantChanged by adding the following code.
function onRateChanged() { var rate = parseFloat($(this).attr("value")); currentRate = rate; updateAppUI(); } function onApplicantChanged() { var applicant = applicants[parseInt(this.value)]; currentApplicant = applicant; updateAppUI(); }
19. Modify the app's initialization code to call the onInitializeUI function.
// The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); onInitializeUI(); }); }
20.
Now
it's again time to test the app in the Visual Studio. Press the Start
button and wait for the debugging session and the app to initialize.
Once the app has activated, you should be able to see it is
displaying information about a load for the current applicant as
shown in the following screenshot. Also note that the UI for the app
will automatically update when you change the interest rate or the
loan applicant.
21. Close Excel and return to Visual Studio.
22. Inside Home.js directly below the onApplicantChanged function, add six new functions named createBindings, onAllBindingCreated, updateBindingsToDocument, onBindingUpdated, updateBindingsFromDocument and onBindingReadFromDocument.
function createBindings() { } function onAllBindingCreated(asyncResult) { } function updateBindingsToDocument() { } function onBindingUpdated(asncResult) { } function updateBindingsFromDocument() { } function onBindingReadFromDocument(asyncResult) { }
23. Implement the createBindings function by adding the following code. If you prefer, you can copy and paste this code from the file named createBindings.js.txt in the Starter Files folder for this lab.
function createBindings() { bindings.addFromNamedItemAsync("Sheet1!Applicant_Name", "text", { id: "applicant_name" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Loan_Amount", "text", { id: "loan_amount" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Interest_Rate", "text", { id: "interest_rate" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Loan_Duration", "text", { id: "loan_duration" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Monthly_Payment", "text", { id: "monthly_payment" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Total_Income", "text", { id: "total_income" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Yearly_Mortgage", "text", { id: "yearly_mortgage" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Fixed_Expenses", "text", { id: "fixed_expenses" }, function () { }); bindings.addFromNamedItemAsync("Sheet1!Available_Income", "text", { id: "available_income" }, onAllBindingCreated); }
24. Implement the onAllBindingCreated function by adding the following code.
function onAllBindingCreated(asyncResult) { updateBindingsToDocument(); }
25. Implement the updateBindingsToDocument function by adding the following code. If you prefer, you can copy and paste this code from the file named updateBindingsToDocument.js.txt in the Starter Files folder for this lab.
function updateBindingsToDocument() { Office.select("bindings#applicant_name") .setDataAsync(currentApplicant.name, function () { }); Office.select("bindings#loan_amount") .setDataAsync(currentApplicant.loan_amount, function () { }); Office.select("bindings#interest_rate") .setDataAsync(currentRate, function () { }); Office.select("bindings#loan_duration")
.setDataAsync(currentApplicant.loan_duration, function () { }); Office.select("bindings#total_income") .setDataAsync(currentApplicant.total_income, function () { }); Office.select("bindings#fixed_expenses") .setDataAsync(currentApplicant.fixed_expenses, onBindingUpdated); } }
26. Implement the onBindingUpdated function by adding the following code.
function onBindingUpdated(asyncResult) { updateBindingsFromDocument(); }
27. Implement the updateBindingsFromDocument function by adding the following code. If you prefer, you can copy and paste this code from the file named updateBindingsFromDocument.js.txt in the Starter Files folder for this lab.
function updateBindingsFromDocument() { Office.select("bindings#monthly_payment") .getDataAsync({ asyncContext: "monthly_payment", valueFormat: Office.ValueFormat.Formatted }, onBindingReadFromDocument); Office.select("bindings#yearly_mortgage") .getDataAsync({ asyncContext: "yearly_mortgage", valueFormat: Office.ValueFormat.Formatted }, onBindingReadFromDocument); Office.select("bindings#available_income") .getDataAsync({ asyncContext: "available_income", valueFormat: Office.ValueFormat.Formatted }, onBindingReadFromDocument); }
28. Implement the onBindingReadFromDocument function by adding the following code.
function onBindingReadFromDocument(asyncResult) { var value = asyncResult.value; var targetDiv = "#" + asyncResult.asyncContext; $(targetDiv).text(value); }
29. Update both onRateChanged and onApplicantChanged so that each of these functions calls updateBindingsToDocument.
function onRateChanged() { var rate = parseFloat($(this).attr("value")); currentRate = rate; updateAppUI(); updateBindingsToDocument(); } function onApplicantChanged() { var applicant = applicants[parseInt(this.value)]; currentApplicant = applicant; updateAppUI(); updateBindingsToDocument(); }
30. Modify the app's initialization code to call the createBindings function just after calling onInitializeUI.
// The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize();
officeDoc = Office.context.document;
bindings = officeDoc.bindings; onInitializeUI(); createBindings(); }); }
31. Now it's again time to test the app in the Visual Studio. Press the Start button on the toolbar and wait for the debugging session and the app to initialize. Once the app has activated, test how the app behaves when you change the Interest Rate or the Loan Applicant using the radio button at the bottom of the task pane. You should see that the app updates information in the workbook and then retrieves values from the workbook for Monthly Payment and Yearly Morgage and updates the UI in the task pane.
32. You have now completed this exercise.
Deep Dive into Apps for Office with PowerPoint
In this lab you will get hands-on experience developing an App for Office which targets Microsoft PowerPoint.
Prerequisites: Before you can start this lab, you must have installed Office 2013 with Service Pack 1 and Visual Studio 2013 with Update 2 on your development workstation.
Exercise 1: Creating the PowerPointTV App for Office Project
In this exercise you will create a new App for Office project in Visual Studio so that you can begin to write, test and debug an App for Office which targets Microsoft PowerPoint.
1. Launch Visual Studio 2013 as administrator with password Passw0rd!.
2. Click to open Microsoft Visual Studio on the task bar. From the File menu select the New Project command. When the New Project dialog appears, select the App for Office project template from the Office/SharePoint template folder as shown below. Name the new project PowerPointTV and click OK to create the new project.
3. When you create a new App for Office project, Visual Studio prompts you with the Choose the app type page of the Create app for Office dialog. This is the point where you select the type of App for Office you want to create. Select the setting with the radio button titled Content and select Next to continue.
4. On the Choose the host applications page of the Create app for Office dialog, uncheck all the Office application except for PowerPoint and then click Finish to create the new Visual Studio solution.
5. Take a look at the structure of the new Visual Studio solution once it has been created. At a high-level, the new solution has been created using two Visual Studio projects named PowerPointTV and PowerPointTVWeb. You should also observe that the top project contains a top-level manifest for the app named PowerPointTVManifest which contains a single file named PowerPointTV.xml.
6. In the Solution Explorer, double-click on the node named PowerPointTVManifest to open the app manifest file in the Visual Studio designer. Update the Display Name settings in the app manifest from PowerPointTV to PowerPoint TV App. (Note: If you do not see the Solution Explorer window choose Window Reset Window.)
7. Move down in PowerPointTVManifest and locate the Requested width setting. Modify Requested width to 1000 pixels.
8. Save and close PowerPointTVManifest.
9. Over the next few steps you will walk through the default app implementation that Visual Studio generated for you when the app project was created. Begin by looking at the structure of the app folder which has two important files named app.css and app.js which contain CSS styles and JavaScript code which is to be used on an app-wide basis.
10. You can see that inside the app folder there is a child folder named Home which contains three files named Home.html, Home.css and Home.js. Note that the app project is currently configured to use Home.html as the app's start page and that Home.html is linked to both Home.css and Home.js.
11. Double-click on app.js to open it in a code editor window. you should be able to see that the code creates a global variable named app based on the JavaScript Closure pattern. The global app object defines a method named initialize but it does not execute this method.
var app = (function () { "use strict"; var app = {}; // Common initialization function (to be called from each page) app.initialize = function () { $('body').append( '<div id="notification-message">' + '<div class="padding">' + '<div id="notification-message-close"></div>' + '<div id="notification-message-header"></div>' + '<div id="notification-message-body"></div>' + '</div>' + '</div>'); $('#notification-message-close').click(function () { $('#notification-message').hide(); }); // After initialization, expose a common notification function app.showNotification = function (header, text) { $('#notification-message-header').text(header); $('#notification-message-body').text(text); $('#notification-message').slideDown('fast'); }; }; return app; })();
12. Close app.js and be sure not to save any changes.
13. Next you will examine the JavaScript code in home.js. Double-click on home.js to open it in a code editor window. Note that Home.html links to app.js before it links to home.js which means that JavaScript code written in Home.js can access the global app object created in app.js.
14. Walk through the code in Home.js and see how it uses a self-executing function to register an event handler on the Office.initialize method which in turn registers a document-ready event handler using jQuery. This allows the app to call app.initialize and to register an event handler using the getDataFromSelection function.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); $('#get-data-from-selection').click(getDataFromSelection); }); }; // Reads data from current document selection and displays a notification function getDataFromSelection() { Office.context.document.getSelectedDataAsync(Office.CoercionType.Text, function (result) { if (result.status === Office.AsyncResultStatus.Succeeded) { app.showNotification('The selected text is:', '"' + result.value + '"'); } else { app.showNotification('Error:', result.error.message); } }); } })();
15. Delete the getDataFromSelection function from Home.js and also remove the line of code that binds the event handler to the button with the id of get-data-from-selection so your code matches the following code listing.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // your app initialization code goes here }); }; })();
16. Save your changes to Home.js. You will return to this source file after you have added your HTML layout to Home.html.
17. Now it time to examine the HTML that has been added to the project to create the app's user interface. Double-click Home.html to open this file in a Visual Studio editor window. Examine the layout of HTML elements inside the body element.
<body> <div id="content-header"> <div class="padding"> <h1>Welcome</h1> </div> </div> <div id="content-main"> <div class="padding"> <p><strong>Add home screen content here.</strong></p> <p>For example:</p> <button id="get-data-from-selection">Get data from selection</button> <p style="margin-top: 50px;"> <a target="_blank" href="https://go.microsoft.com/fwlink/?LinkId=276812">Find more samples online...</a> </p> </div> </div> </body>
18. Replace the text message of Welcome inside the h1 element with a different message such as Loan Information. Also trim down the contents of the content-main div element to match the HTML code shown below. You will start off your HTML layout using a single div element with an id of player.
<body> <div id="content-main"> <div id="player"></div> </div> </body>
19. Save and close Home.html.
20. Return to Home.js and modify to code to write a simple message to the results div using the following code.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); $('#player').text("Hello world"); }); } })();
21. Now it's time to test the app using the Visual Studio debugger. Press the {F5} key to run the project in the Visual Studio debugger. The debugger should launch Microsoft PowerPoint 2013 and you should see your PowerPointTV app in the task pane on the right side of a new PowerPoint presentation as shown in the following screenshot. (Note: If you get a 'Security Alert' message box, choose 'Yes' to trust the certificate.)
22. Inside the PowerPoint slide, select the content app and center it in the middle of the slide. If you'd like, change the PowerPoint presentation theme to give the slide background some color.
23. Save the PowerPoint presentation as a file named TestDec.pptx and make sure to save this file in the root folder of the PowerPointTV project.
24. Exit the Debugger by choosing Debug -> Stop Debugging.
25. In Visual Studio, add the file TestDec.pptx into PowerPointTV by copying and pasting from file explorer.
26. Select the PowerPointTV project and then navigate to the property sheet and change the Start Document setting to TestDeck.pptx.
27. Test your work by pressing {F5} and starting a debugging session. The debugging session should load and initialize the app using TestDeck.pptx instead of a new PowerPoint presentation.
28. Close PowerPoint to terminate your debugging session and return to Visual Studio.
Exercise 2: Programming the PowerPointTV App to Load YouTube Videos
In this exercise, you will continue working on the PowerPointTV app project you created in the previous exercise by extending with a custom YouTube video player.
1. Make sure you have the PowerPointTV app open in Visual Studio. If the project is not open, open it now.
2. Open Home.css and add the following CSS rule.
#content-main{ background-color: black; padding: 4px; }
3. Save and close Home.css.
4. Open Home.js and replace the code inside using the code shown in the following code listing.
(function () { "use strict"; // The initialize function must be run each time a new page is loaded Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // your app initialization code goes here var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); }); }; })(); // add support for YouTube player var player; function onYouTubeIframeAPIReady() { var videoId = 'Y0hsjr7S-kM'; player = new YT.Player('player', { height: '390', width: '640', videoId: videoId, events: { 'onReady': onPlayerReady } }); } function onPlayerReady(event) { event.target.playVideo(); }
5. Save your changes to Home.js.
6. Test your work by pressing {F5} to start a debugging session. You should see that the app displays and plays a video from youtube.
7. Open Home.html and update the body element with the following HTML.
<body> <div id="content-main"> <div id="player"></div> <div id="control_panel"> <div> <button id="cmdStart">Start</button> <button id="cmdPause">Pause</button> <button id="cmdStop">Stop</button> </div> <div> <select size="10" id="videoList"></select> </div> </div> </div> </body>
8. Save and close Home.html.
9. Open Home.css and update it to match the following code listing.
#content-main{ background-color: black; padding: 4px; } #player { float: left; } #control_panel { background-color: #DDD; padding: 8px; margin-left: 644px; } #control_panel select { width: 100%; }
10. Save and close Home.css.
11. Open Home.js.
12. At the bottom of Home.js, add the following three function named onStart, onPause and onStop.
function onStart() { player.playVideo(); } function onPause() { player.pauseVideo(); } function onStop() { player.stopVideo(); }
13. At the bottom of the document ready handler, add code to register the click event handler for the three button.
Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); // your app initialization code goes here var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); // register event handlers for control panem buttons $("#cmdStart").click(onStart); $("#cmdPause").click(onPause); $("#cmdStop").click(onStop); }); };
14. Test your work by pressing {F5} to start a debugging session. You should see that the app displays and plays a video from youtube just as before. However, now the three buttons should work and allow you to play, pause and stop the video.
15. Close PowerPoint to terminate your debugging session and return to Visual Studio.
Exercise 3: Programming the PowerPointTV App to Load YouTube Videos
In this exercise, you will continue working on the PowerPointTV app project you created in the previous exercise by extending with a custom web service to supply the app with a list of videos.
1. Make sure you have the PowerPointTV app open in Visual Studio. If the project is not open, open it now.
2. Add a new folder the PowerPointTVWeb project named Controllers.
3. Right-click on the Controllers folder and select Add > Controller.
4. In the Add Scaffold dialog, select Web API 2 Controller - Empty and click the Add button.
5. On the AddController dialog, enter a name of VideosController and click the Add button.
6. You should now see that the Web API controller has been added to a file named VideosController.cs. You can also see that Visual Studio has added a few extra files such as Global.asax and WebApiConfig.cs to provide support for the Web API.
7. Examine what's inside VideosController.cs. You can see that there is an ApiController-derived class named VideosController which is initially empty.
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace PowerPointTVWeb.Controllers { public class VideosController : ApiController { } }
8. Just above the VideosController class, add a new class named VideoInfo using the following code.
namespace PowerPointTVWeb.Controllers { public class VideoInfo { public string videoId { get; set; } public string title { get; set; } } public class VideosController : ApiController { } }
9. Implement a Get method in the VideosController class using the following code.
public class VideosController : ApiController { public IEnumerable<VideoInfo> Get() { return new List<VideoInfo>() { new VideoInfo{videoId="Y0hsjr7S-kM", title="Adding Provider Hosted App To Windows Azure for Office365"}, new VideoInfo{videoId="GbYzzubLGEI", title="Async Site Collection Provisioning With App Model for Office365"}, new VideoInfo{videoId="_Duwtgn9rhc", title="Building Connected SharePoint App Parts With SignalR"}, new VideoInfo{videoId="m2R8Bfb9Qss", title="Scot Hillier on what makes IT Unity Special"} }; } }
10. Save and close VideosController.cs.
11. Open Home.js and add two functions named onLoadVideo and loadVideos at the bottom of the file.
function loadVideos() { // call Videos web service using URL of /api/Videos/ $.ajax({ url: "/api/Videos/", }).done(function (videos) { // handle async response from web service call // make sure select list is empty $("#videoList").empty(); // add option element for each video for (var i = 0; i < videos.length; i++) { $("#videoList").append($("<option>", { value: videos[i].videoId }).text(videos[i].title)); } // attach click event handler to select list $("#videoList").click(onLoadVideo); }); } function onLoadVideo() { var videoId = $("#videoList").val(); if (videoId) { player.loadVideoById(videoId); } }
12. At the end of the app initialization code, add a call to the loadVideos function.
Office.initialize = function (reason) { $(document).ready(function () { app.initialize(); var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); $("#cmdStart").click(onStart); $("#cmdPause").click(onPause); $("#cmdStop").click(onStop); // call to Videos web service loadVideos(); }); };
13. Test your work by pressing {F5} to start a debugging session. The app should fill the select element with a list of videos using data retrieved from the web service call. You should also be able to change the currently playing video by clicking one of the videos titles in the list of videos.
14. You have now completed this lab.
Getting Started with SharePoint App Development
In this lab you will get hands-on experience working with the new SharePoint App model. Through the exercises in this lab you will learn how to create and test a SharePoint-hosted app as well as a provider-hosted app.
Prerequisites: Before you can start this lab, you must have Visual Studio 2013 installed with Update 2. You must have a Developer site in Office 365. If you do not yet have an Office 365 developer site. perform the following steps to create a Developer site:
1. Click the desktop shortcut named Office 365 Portal and sign in to your Office 365 tenant with your credentials (user@mycompany.onmicrosoft.com). Note: If you are using your own Office 365 tenant, you must sign in using credentials that have Global or SharePoint Online admin permissions to create the developer site.
2. Locate the Admin links (the link may appear at the top or left side of the page) and click the link to manage SharePoint.
3. In the SharePoint admin center, on the Site Collections tab, click the New button, and then click Private Site Collection.
4. Enter information about the new Developer site and then click the OK button.
5. Once the new site is created, copy the URL into a new text document by using a utility such as Notepad. You will use the URL in later steps.
Exercise 1: Creating and Debugging a SharePoint-hosted App
1. Using the browser, navigate to your Office 365 developer site and sign on using your credentials.
2. On your developer workstation, launch Visual Studio as administrator.
3. Create a new project in Visual Studio 2013 by selecting the menu command File > New > Project.
4. In the New Project dialog, find the App for SharePoint project template under the Templates > Visual C# > Office / SharePoint > Apps section. Enter a name of My First SharePoint Hosted App, then click the OK button.
5. In the New app for SharePoint wizard, enter the URL for your Office 365 Developer site and select SharePoint-hosted for the app hosting model. When done, complete the wizard by clicking the Finish button. If the Connect to SharePoint dialog displays, enter your Office 365 credentials and click the Sign in button.
6. In Solution Explorer, examine the files that Visual Studio creates for a SharePoint-Hosted app. If you do not see Solution Explorer, click the View menu and then click Solution Explorer. The solution is a traditional SharePoint solution-based project because you have a Features node and a Packages node.
7. Note that there are project folders named Content, Images & Pages are actually SharePoint Project Items (SPI) that are Modules and will provision their contents to the respective folders in the app web that will be generated upon installing the app.
• Content/App.css: main Cascading Style Sheet used for the app.
• Images/AppIcon.png: default image used for the app.
• Pages/Default.aspx: default start page for the app.
• Scripts/App.js: main JavaScript file which is referenced by Default.aspx.
• AppManifest.xml app manifest containing app metadata such as its Name, Product ID, App Version Number and minimum version for the SharePoint host environment.
8. Examine the app's start page by right-clicking Pages/Default.aspx file and selecting Open.
• Look at the links to other JavaScript libraries inside the PlaceHolderAdditionalPageHead placeholder.
• There are references to the jQuery library and the App.js file.
• There is a reference to the App.css file as well.
9. Using the Solution Explorer tool window, right-click the Scripts/App.js file and select Open.
• This file has four functions and a few variables.
• The function $(document).ready(function()){ … } gets a reference to the client object model (CSOM) ClientContext object and then gets a reference to the current site.
• The getUserName() function is one that will usually be deleted from the project when you get more experience with SharePoint-Hosted apps. It uses the CSOM to get the name of the current user logged in.
• The last two functions are used as the success and failed callback when the CSOM request completes.
10. Now it is time to update the app homepage. Using the Solution Explorer tool window, right-click the Pages/Default.aspx file and select Open. After the existing DIV element, add the following HTML markup:
<input type="button" value="Push Me" onclick="hello();" /> <div id="displayDiv"></div>;
11. Locate the PlaceHolderPageTitleInTitleArea placeholder control and replace the content inside with the title My Hello World App.
12. In this step you will update the app script file. Using the Solution Explorer tool window, right-click the Scripts/App.js file and select Open. Add the following function to the bottom of the file that will be called when you click the button.
function hello() { $get("displayDiv").innerHTML = "<p>Hello, Apps!</p>"; }
13. Save all changes: File > Save All.
14. Build and Test the Project by pressing the F5 key or clicking Debug, then Start Debugging.
15. The installation process for an app will take a moment to complete. If you watch the lower-left corner of Visual Studio, it will tell you what it is currently doing. If you want more information, click the Output tab at the bottom of Visual Studio to see a log of what is going on. If the Output tab is not present, click the View menu and then click Output.
16. Once the app is installed, Internet Explorer will launch and navigate to the app’s start page, default.aspx.
17. When the page loads, click the Push me button to see your text get written to the page:
18. Once you have tested the app, close the browser to stop the debugging session and return to Visual Studio.
19. In Visual Studio, save all changes using File > Save All.
Exercise 2: Using jQuery in a SharePoint-hosted App
In this lab, you will continue working with the SharePoint-hosted app project you created in the previous lab exercise. However, you will rewrite the JavaScript code to use the jQuery library to initialize the app and create an event handler using best practice techniques.
1. Open default.aspx and ensure that the HTML code inside the PlaceHolderMain content control looks exactly like the following code listing.
<asp:content contentplaceholderid="PlaceHolderMain" runat="server"> <div> <p id="message"> <!-- The following content will be replaced with the user name when you run the app - see App.js --> initializing... </p> </div> <input type="button" value="Push Me" onclick="hello();" /> <div id="displayDiv"></div> </asp:content>
2. Remove the onclick attribute from the INPUT element and add an id of cmdPushMe. Your markup should look like the following markup:
<input id="cmdPushMe" type="button" value="Push Me" />
3. Save your changes and close default.aspx.
4. Right-click on app.js and select Open to open this JavaScript file in an editor window.
5. Delete all the code inside app.js except for the 'use strict'; statement at the top.
6. Inside app.js, add two new functions into onPageLoad and onButtonClicked.
'use strict'; function onPageLoad() { } function onButtonClicked() { }
7. At the top of the App.js file, just after the use strict statement, add a jQuery document ready event handler to execute the onPageLoad function once the page loads and the JavaScript DOM is available for access within the browser.
'use strict'; $(document).ready(onPageLoad); function onPageLoad() { } function onButtonClicked() { }
8. Add the following code. This code displays a text message on the page when the document ready event handle executes, and registers the onButtonClick function as an event handler for the input control with the id of cmdPushMe.
function onPageLoad() { $("#message").text("Hello from the document ready event handler"); $("#cmdPushMe").click(onButtonClicked); }
9. Add the following code. This method writes the text message "Hello Apps" into the DIV element with the id of displayDiv, and uses the jQuery css method to style the DIV element with a margin of 16px, a font color of green, and a font-size of 32px.
function onButtonClicked() { $("#displayDiv") .text("Hello Apps") .css({ "margin": "16px", "color": "green", "font-size": "32px" }); }
10. Once the code inside your app.js file looks like the following code listing, you are ready to test your work.
'use strict'; $(document).ready(onPageLoad); function onPageLoad() { $("#message").text("Hello from the document ready event handler"); $("#cmdPushMe").click(onButtonClicked); } function onButtonClicked() { $("#displayDiv") .text("Hello Apps") .css({ "margin": "16px", "color": "green", "font-size": "32px" }); }
11. Save all changes by executing the File > Save All menu command.
12. Build and test the project by pressing the F5 key or clicking Debug, then Start Debugging.
13. Once the app is installed, Internet Explorer will launch and navigate to the app’s start page, default.aspx.
14. When the page loads, you should see the message "Hello from the document ready event handler" on the page.
15. Click the Push me button. Your text is written to the page using the custom font styles that you defined.
16. Once you have tested the app, close the browser to stop the debugging session and return to Visual Studio.
17. In Visual Studio, save all changes using File > Save All.
18. Close the MyHelloWorldApp project.
Exercise 3: Creating and Debugging a Provider-Hosted App
In this exercise you will create and test a simple Provider-Hosted App. This will give you opportunity to observe the basic differences between developing SharePoint-hosted apps and cloud-hosted apps using Visual Studio 2013. Note that this lab will not involve security topics such as app authentication. Instead, you will configure the app to use Internal security so that you can get the app up and running without worrying about how to configure app authenhtication.
1. Launch Visual Studio 2013 as administrator if it is not already running.
2. In Visual Studio select File > New > Project.
3. In the New Project dialog select the App for SharePoint template under the Templates > Visual C# > Office / SharePoint > Apps section.
4. Enter a Name of MyFirstCloudHostedApp and then click OK.
5. Next, you will see the New app for SharePoint wizard which begins by prompting you with the Specify the App for SharePoint Settings page. Enter the URL to your Office 365 developer site, configure the app's hosting model to be Provider-hosted and click Next.
6. On the Specify the web project type page, select the ASP.NET Web Forms Application setting and click Next.
7. On the Configure authentication settings page, accept the default settings and click Finish.
8. Examine the structure of the Visual Studio solution that has been created. The solution for a Provider-Hosted app has two projects. This is very different from the solution for the SharePoint-Hosted app that you created in Exercise 2. It only has one project.
9. Observe that top project named MyFirstCloudHostedApp contains only two files: AppManifest.xml and AppIcon.png. This effectively means the app will not install any resources into the SharePoint host such as pages. This project only contains app metadata and an image file that get added to the SharePoint host when the app gets installed.
10. Take a look at the project named MyFirstCloudHostedAppWeb. It provides the implementation for the app's remote web. This project is a standard ASP.NET Web application. It includes the following files:
• TokenHelper.cs: This is a code file provided by Microsoft to make it easier to obtain the user identity, the OAuth token or the token provided by highly trusted apps. You will ignore this for now.
• Default.aspx.cs: (Inside the Pages folder expand out the Default.aspx file to see this) the code behind file for the page contains logic to call back into SharePoint to obtain the title of the host web. The code is written to assume this app will use OAuth authentication.
• SharePointContext.cs: This is a code file provided by Microsoft to encapsulate all the information from SharePoint. You will ignore this for now.
• Scripts: A common folder to place JavaScript files.
11. By default a Provider-Hosted app expects external authentication with either OAuth or S2S. These technologies are not covered in this lab. To eliminate security requirements which would complicate building and testing this Provider-hosted app, in the following steps you will disable external authentication.
12. In the Solution Explorer within the MyFirstCloudHostedApp project, right-click AppManifest.xml and select View Code.
13. Locate the <AppPrincipal> node.
14. Replace the contents of the node so that it looks like the following markup:
<AppPrincipal> <Internal/> </AppPrincipal>
15. You will now write server-side C# code that will run in the remote web of the app. Note: this is not possible to do in a SharePoint-Hosted app. In the Solution Explorer tool window within the MyFirstCloudHostedAppWeb project, right-click Default.aspx and select Open.
16. Replace the existing <body> element on the page with an ASP.NET literal control with an id of Message and a hyperlink control with an id of HostWebLink. The body of the page should resemble the following markup:
<body> <form id="form1" runat="server"> <asp:Literal ID="Message" runat="server" /> <p><asp:HyperLink ID="HostWebLink" runat="server" /></p> </form> </body>
17. In the Solution Explorer within the MyFirstCloudHostedAppWeb project, right-click the file named Default.aspx.cs file and select Open to open the file in a code editor window.
18. Delete the existing Page_PreInit method and all of the code within the method.
19. Replace the contents of the Page_Load method with the following code:
this.Message.Text = "My first SharePoint Provider-Hosted App!"; var hostWeb = Page.Request["SPHostUrl"]; this.HostWebLink.NavigateUrl = hostWeb; this.HostWebLink.Text = "Back to host web";
20. Save all changes by using the File > Save All menu command.
21. Build and test the project by pressing the F5 key or clicking Debug, then Start Debugging.
22. Visual Studio 2013 may display a Security Alert that asks you to trust a self-signed certificate. You are not using a certificate in this solution, so just click Yes. Then, click Yes on the confirmation page to continue.
23. Once the solution is deployed, Internet Explorer will launch and navigate to the start page of the app in the remote web.
24. When the page first loads, it displays as a plain white page with the text you added and a link back to the hosting site.
25. Test the Back to host web link to ensure that it correctly redirects you back to the host web which should be your Office 365 developers site.
26. Close the browser to stop the debugger and go back to Visual Studio.
In this exercise you created a simple SharePoint Provider-Hosted app. As in the last exercise, you didn’t do much in this exercise beyond creating and testing the simplest cloud-hosted app possible. In later labs you will build on this foundation to add more capabilities to SharePoint apps.
Deep Dive into Provider Hosted Apps
In this lab, you will create a Provider-Hosted app and make use of some of the advanced capabilities.
Prerequisites
1. You must have an Office 365 tenant and Windows Azure subscription to complete this lab. If you need further assistance with this step, you may ask a Lab Proctor for further instructions.
2. You must have a SharePoint site based on the Developer Site template. If you need to create this, follow the instructions at https://msdn.microsoft.com/en-US/library/office/jj692554 and ensure that you select the Developer Site template. Record the URL to your developer site for the lab.
NOTE: To find the SharePoint Admin page in your portal, click the menu in the top left corner and select Admin. Then in the left menu, select Admin, SharePoint.
Exercise 1: Create a Provider-Hosted App
In this exercise you create a new provider-hosted app.
1. Create the new project in Visual Studio 2013:
2. Launch Visual Studio 2013 as administrator.
3. In Visual Studio select File/New/Project.
4. In the New Project dialog:
1. Select Templates/Visual C#/Office/SharePoint/Apps.
2. Click App for SharePoint.
3.
Name
the new project DeepDiveCloudApp
and click OK.
5. In the New App for SharePoint wizard:
1. Enter the address of a SharePoint site to use for testing the app (NOTE: The targeted site must be based on a Developer Site template)
2. Select Provider-Hosted as the hosting model.
3.
Click
Next.
4. Select ASP.NET Web Forms Application.
5.
Click
Next.
6. Select the option labeled Use Windows Azure Access Control Service (for SharePoint cloud apps).
7.
Click
Finish.
8. When prompted, log in using your O365 administrator credentials.
9. Open Default.aspx.cs from the DeepDiveCloudAppWeb project.
10.
Delete
the code that is used to obtain the host web title so your code
appears as follows:
Exercise 2: Chrome Control
In this exercise you will add the Chrome Control to the project you created in Exercise 1.
1. Open DeepDiveCloudApp.sln in Visual Studio 2013 if not already open.
2. Rich click the DeepDiveCloudAppWeb project and select Add/New Folder.
3. Name the new folder Images.
4. Copy the file AppIcon.png from the DeepDiveCloudApp project into the Images folder.
5. Right click the Pages folder in the DeepDiveCloudAppWeb project and select Add/New/Web Form.
6. Name the new Web Form CrossDomain.
7. Click OK.
8. Add a div element to the body of the page to hold the Chrome Control. The following code shows the div in context with the body.
<body> <form id="form1" runat="server"> <div id="chrome_ctrl_placeholder"></div> <div> </div> </form> </body>
9. Open Default.aspx from the DeepDiveCloudAppWeb project.
10. Add a div element to the body of the page to hold the Chrome Control. The following code shows the div in context with the body.
<body> <form id="form1" runat="server"> <div id="chrome_ctrl_placeholder"></div> <div> </div> </form> </body>
11. Right click the Scripts folder and select Add/New/JavaScript File.
12. Name the new file app.
13. Click OK.
14. Add the following code to app.js to initialize the Chrome Control.
"use strict"; var ChromeControl = function () { var init = function () { var hostWebUrl = queryString("SPHostUrl"); $.getScript(hostWebUrl + "/_layouts/15/SP.UI.Controls.js", render); }, render = function () { var options = { "appIconUrl": "../Images/AppIcon.png", "appTitle": "Deep Dive Cloud App", "settingsLinks": [ { "linkUrl": "../Pages/CrossDomain.aspx?" + document.URL.split("?")[1], "displayName": "Cross Domain Library" } ] }; var nav = new SP.UI.Controls.Navigation( "chrome_ctrl_placeholder", options ); nav.setVisible(true); }, queryString = function (p) { var params = document.URL.split("?")[1].split("&"); var strParams = ""; for (var i = 0; i < params.length; i = i + 1) { var singleParam = params[i].split("="); if (singleParam[0] == p) return decodeURIComponent(singleParam[1]); } } return { init: init, } }(); (function () { "use strict"; jQuery(function () { ChromeControl.init(); }); }());
15. Open Default.aspx from the DeepDiveCloudAppWeb project.
16. Add the following script references in the head section.
<script src="../Scripts/jquery-1.9.1.js"></script> <script src="../Scripts/app.js"></script>
17. Open CrossDomain.aspx from the DeepDiveCloudAppWeb project.
18. Add the following script references in the head section.
<script src="../Scripts/jquery-1.9.1.js"></script> <script src="../Scripts/app.js"></script>
19. Press F5 to debug your app. Confirm any security prompts.
20.
Verify
that the Chrome Control appears (top right corner) and that you can
navigate between the Default.aspx and crossDomain.aspx pages.
21. Stop debugging.
Exercise 3: Cross-Domain Library
In this exercise you use the cross-domain library to access a list in the app web.
1. Open DeepDiveCloudApp.sln in Visual Studio 2013 if not already open.
2. Right click the DeepDiveCloudApp project and select Add/New Item.
3. In the Add New Item dialog, select List.
4. name the new list Terms.
5.
Click
Add.
6. In the SharePoint Customization Wizard, select Create list instance based on existing list template.
7. Select Custom List.
8.
Click
Finish.
9. Open the Elements.xml file associated with the new list instance DeepDiveCloudApp/Terma/Elements.xml.
10. Add the following XML inside the ListInstance element to pre-populate the list with data.
<Data> <Rows> <Row> <Field Name="Title">SharePoint-Hosted App</Field> </Row> <Row> <Field Name="Title">Provider-Hosted App</Field> </Row> <Row> <Field Name="Title">Microsoft Azure</Field> </Row> <Row> <Field Name="Title">Office 365</Field> </Row> <Row> <Field Name="Title">SharePoint Online</Field> </Row> </Rows> </Data>
11. Right click the Scripts folder in the DeepDiveCloudAppWeb project and select Add/New/JavaScript File.
12. Name the new file crossdomain.
13. Click OK.
14. Add the following code to crossdomain.js to read the Terms list items.
(function () { "use strict"; jQuery(function () { //Get Host and App web URLS var appWebUrl = ""; var spHostUrl = ""; var args = window.location.search.substring(1).split("&"); for (var i = 0; i < args.length; i++) { var n = args[i].split("="); if (n[0] == "SPHostUrl") spHostUrl = decodeURIComponent(n[1]); } for (var i = 0; i < args.length; i++) { var n = args[i].split("="); if (n[0] == "SPAppWebUrl") appWebUrl = decodeURIComponent(n[1]); } //Load Libraries var scriptbase = spHostUrl + "/_layouts/15/"; jQuery.getScript(scriptbase + "SP.RequestExecutor.js", function (data) { //Call Host Web with REST var executor = new SP.RequestExecutor(appWebUrl); executor.executeAsync({ url: appWebUrl + "/_api/web/lists/getbytitle('Terms')/items", method: "GET", headers: { "accept": "application/json;odata=verbose" }, success: function (data) { var results = JSON.parse(data.body).d.results; for (var i = 0; i < results.length; i++) { $("#termList").append("<li>" + results[i].Title + "</li>"); } }, error: function () { alert("Error!"); } }); }); }); }());
15. Open CrossDomain.aspx from the DeepDiveCloudAppWeb project.
16. Add the following script reference in the head section.
<script src="../Scripts/crossdomain.js"></script>
17. Add an unordered list element to display the terms. The list element is shown in context below.
<body> <form id="form1" runat="server"> <div id="chrome_ctrl_placeholder"></div> <div> <ul id="termList"></ul> </div> </form> </body>
18. Press F5 to debug the app.
19. Use the Chrome Control navigation element to open the Cross-Domain Library page.
Congratulations! You have completed creating a Provider-Hosted app that uses the Chrome Control and Cross-Domain Library.
Getting started with Office 365 APIs
In this lab, you will investigate the O365 APIs.
Prerequisites
1. You must have an Office 365 tenant to complete this lab. If you do not have one, raise your hand and ask one of the TLGs for a temporary Office 365 tenant login.
Note: This lab is based on the Office 365 API Tools version 1.3.41104.1. Later versions may have subtle differences in the screen shots & steps outlined in this module.
Exercise 1: Create & Configure an MVC Web Application
In this exercise you will create a new MVC web application to utilize the O365 APIs.
1. Start Visual Studio from the Task bar. In Visual Studio, click File/New/Project.
2. In the New Project dialog, Select Templates/Visual C#/Web.
3. Select ASP.NET Web Application.
4. Enter Exercise2 for the name and click OK.
5. In the New ASP.NET Project dialog
6. Click MVC.
7. Clear the Host in the cloud checkbox.
8. Click Change Authentication.
9. Select No Authentication.
10. Click OK.
11. Click OK.
12. Wait fore the project to be created and configured. Then update the web project to use SSL by default:
13. In the Solution Explorer tool window, select the project and look at the Properties tool window.
14. Change the property SSL Enabled to TRUE.
15. Copy the SSL URL property to the clipboard for use in the next step.
16. Save your changes.
>
It is important to do this now because in the next step when you
create the application in Azure AD, you want the reply URL to use
HTTPS. If you did not do this now, you would have to manually make
the changes the Visual Studio wizard is going to do for you in
creating the app.
17. Configure the project to always go to the homepage of the web application when debugging:
18. In the Solution Explorer tool window, right click the project & select Properties.
19. Select the Web tab in the left margin.
20. Find the section Start Action.
21. Click the radio button Start URL and enter the SSL URL of the web project that you copied from the previous step.
22. Now add the Office 365 APIs as a connected service:
23. In the Solution Explorer, right click the project and select Add/Connected Service.
24. In the Services Manager dialog...
1. Click Register Your App.
2. When prompted sign in with your Organizational Account.
3. Click App Properties.
4. Make a note of the name of the application as you will need this in the next exercise.
5. Verify the option Single Organization is selected.
6. Make sure there is only a single URL listed in the Redirect URIs and it is the HTTPS URL of the web project.
7. Click Apply.
8. Click Calendar.
9. Click Permissions.
10. Check Read user calendars.
11. Click Apply.
12. Click Users and Groups.
13. Click Permissions.
14. Click Enable sign-on and read users’ profile.
15. Click Apply.
16. Click OK.
Exercise 3: Configure Web Application to use Azure AD and OWIN
In this exercise you will take the ASP.NET MVC web application you created in the previous exercise and configure it to use Azure AD & OpenID Connect for user & app authentication. You will do this by utilizing the OWIN framework. Once authenticated, you can use the access token returned by Azure AD to access the Office 365 APIs.
1. Obtain and store the Azure AD tenant ID in the web.config.
2. Browse to the Azure Management Portal (https://manage.windowsazure.com) and sign in with your Organizational Account.
3. In the left-hand navigation, click Active Directory.
4. Select the directory you share with your Office 365 subscription.
5. Click Applications at the top of the directory menu.
6. Select the application you created for this lab. This is the name of the application in the App Properties dialog when you were adding the Connected Service in the last exercise. You may need to change the view to show Applications my company owns and click the check mark to the right.
7. Select the Quick Start page for the in the top navigation... that's the left-most menu item that looks like a lightning bolt in a cloud:
8. On the Quick Start page, expand the Get Started / Enable Users to Sign On. Locate the field Federation Metadata Document URL. Within that field value you will see a GUID immediately after the login.windows.net part of the URL. Copy just the GUID value to the clipboard.
9. Open the web.config file in the project.
10. Add the following node to the <appSettings> section, setting the value equal to the directory tenant ID you acquired in the previous step:
<add key="ida:TenantId" value="######-####-####-####-############"/>
11. Now you need a few NuGet packages to enable OWIN OpenID Connect authentication & to create a secure token cache (using Entity Framework) in the application:
12. Open the Package Manager Console: View/Other Windows/Package Manager Console.
13. Enter each line below in the console, one at a time, pressing ENTER after each one. NuGet will install the package and all dependent packages:
PM> Install-Package -Id EntityFramework PM> Install-Package -Id Microsoft.IdentityModel.Clients.ActiveDirectory PM> Install-Package -Id Microsoft.Owin.Host.SystemWeb PM> Install-Package -Id Microsoft.Owin.Security.Cookies PM> Install-Package -Id Microsoft.Owin.Security.OpenIdConnect
14. Add a new model that will be used by our persistent token cache:
15. Right-click Models folder in the project and select Add/Class.
16. Name the class PerWebUserCache.cs.
17. When the file has been created, add the following code to the class:
[Key] public int EntryId { get; set; } public string webUserUniqueId { get; set; } public byte[] cacheBits { get; set; } public DateTime LastWrite { get; set; }
18. At the top of the file, add the following using statement:
using System.ComponentModel.DataAnnotations;
19. Add a new persistent data store that will be used for the token cache:
20. Right-click the project and select Add/New Folder.
21. Name the folder Data.
22. Locate the Lab Files folder (C:\Labfiles\O3651-5 Getting started with Office 365 APIs\Lab Files) provided with this lab & find two files: Exercise2Context.cs & Exercise2Initializer.cs. Copy these two files to the Data folder you just created.
23. Add a token cache that leverages Entity Framework to store the user specific tokens in persistent storage:
24. Right-click the project and select Add/New Folder.
25. Name the folder Utils.
26. Locate the Lab Files folder provided with this lab & find the file EDADALTokenCache.cs. Copy that file to the Utils folder.
Take a moment to examine this file. It uses the DbContext you added in the previous step to implement a TokenCache which you will use in a moment. This will store the token received from a successful authentication in a persistent store.
27. Add a helper class that will be used to harvest settings out of the web.config and create the necessary strings that will be used for authentication:
28. Locate the Lab Files folder provided with this lab & find the file SettingsHelper.cs. Copy that file to the Utils folder.
29. Configure the app to run startup code when the OWIN libraries startup:
30. Right-click the project and select Add/Class.
31. Name the class Startup.cs.
32. Add the following using statements after the existing using statements:
using Owin; using Microsoft.Owin;
33. Add the following assembly directive to call the Startup.Configuration() method when OWIN starts up. Note that you will only point to the class:
[assembly:OwinStartup(typeof(Exercise2.Startup))]
34. Update the signature of the Startup class to be a partial class as you will create another in the next step. Do this by adding the partial keyword after the public statement so it looks like the following:
public partial class Startup {}
35. Add the following Confguration() to the Startup class. This calls a method you will create in a moment:
public void Configuration(IAppBuilder app) { ConfigureAuth(app); }
36. Save your changes.
37. Create an authentication process when a user hits the website:
38. Right-click the App_Start folder and select Add/Class.
39. Name the class Startup.Auth.cs.
40. When the file opens make the following two changes:
1. Modify the namespace to just be Exercise2.
2. Modify the class declaration to be a partial class named Startup so it looks like the following:
c# public partial class Startup {}
41. Add the following using statements after the existing using statements:
using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.OpenIdConnect; using Owin; using System.Configuration; using System.Threading.Tasks; using Exercise2.Utils;
42. Add the following method to the Startup class:
public void ConfigureAuth(IAppBuilder app) {}
43. Within the ConfigureAuth method, add the following code to configure the authentication type and settings for the app:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions());
44. Below the code you just added, add the following code to configure the OWIN authentication process, force the user to go through the login process and collect the result returned from Azure AD:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { ClientId = SettingsHelper.ClientId, Authority = SettingsHelper.AzureADAuthority, Notifications = new OpenIdConnectAuthenticationNotifications() { // when an auth code is received... AuthorizationCodeReceived = (context) => { // get the OpenID Connect code passed from Azure AD on successful auth string code = context.Code; // create the app credentials & get reference to the user ClientCredential creds = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret); string userObjectId = context.AuthenticationTicket.Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value; // use the OpenID Connect code to obtain access token & refresh token... // save those in a persistent store... EFADALTokenCache sampleCache = new EFADALTokenCache(userObjectId); AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, sampleCache); // obtain access token for the AzureAD graph Uri redirectUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)); AuthenticationResult authResult = authContext.AcquireTokenByAuthorizationCode(code, redirectUri, creds, SettingsHelper.AzureAdGraphResourceId); // successful auth return Task.FromResult(0); }, AuthenticationFailed = (context) => { context.HandleResponse(); return Task.FromResult(0); } }, TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters { ValidateIssuer = false } });
45. Save your changes.
46. With the authentication process wired up into the OWIN startup process, now implement a login controller to provide sign in & sign out functionality:
47. Right-click the Controllers folder and select Add/Controller.
1. In the Add Scaffold dialog, select MVC 5 Controller - Empty.
2. Click Add.
3. When prompted for a name, enter AccountController.
4. Click Add.
48. Within the AccountController file, add the following using statements to the top of the file:
using Exercise2.Utils; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Microsoft.Owin.Security.OpenIdConnect; using System.Security.Claims;
49. Delete the default Index() method from the AcountController class.
50. Add a new function to provide a sign in route. This will simply initiate a login challenge using the OWIN framework that will take the user to the Azure AD login page. When this runs, if the user has not already given the app consent to access their Office 365 data, they will be prompted to grant the app consent at this time.
public void SignIn() { if (!Request.IsAuthenticated) { HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType); } }
51. Add a new function to provide a sign out route. This will log the user out of the site & clear the local cache of tokes:
public void SignOut() { // Remove all cache entries for this user and send an OpenID Connect sign-out request. string usrObjectId = ClaimsPrincipal.Current.FindFirst(SettingsHelper.ClaimTypeObjectIdentifier).Value; AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, new EFADALTokenCache(usrObjectId)); authContext.TokenCache.Clear(); HttpContext.GetOwinContext().Authentication.SignOut( OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType); }
52. Add a pair of functions to handle requesting consent for the application.
public ActionResult ConsentApp() { string strResource = Request.QueryString["resource"]; string strRedirectController = Request.QueryString["redirect"]; string authorizationRequest = String.Format( "{0}oauth2/authorize?response_type=code&client_id={1}&resource={2}&redirect_uri={3}", Uri.EscapeDataString(SettingsHelper.AzureADAuthority), Uri.EscapeDataString(SettingsHelper.ClientId), Uri.EscapeDataString(strResource), Uri.EscapeDataString(String.Format("{0}/{1}", this.Request.Url.GetLeftPart(UriPartial.Authority), strRedirectController)) ); return new RedirectResult(authorizationRequest); } public ActionResult AdminConsentApp() { string strResource = Request.QueryString["resource"]; string strRedirectController = Request.QueryString["redirect"]; string authorizationRequest = String.Format( "{0}oauth2/authorize?response_type=code&client_id={1}&resource={2}&redirect_uri={3}&prompt={4}", Uri.EscapeDataString(SettingsHelper.AzureADAuthority), Uri.EscapeDataString(SettingsHelper.ClientId), Uri.EscapeDataString(strResource), Uri.EscapeDataString(String.Format("{0}/{1}", this.Request.Url.GetLeftPart(UriPartial.Authority), strRedirectController)), Uri.EscapeDataString("admin_consent") ); return new RedirectResult(authorizationRequest); }
53. Add one more function to the AccountController class to refresh the session and reissue the OWIN authentication challenge:
public void RefreshSession() { string strRedirectController = Request.QueryString["redirect"]; HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = String.Format("/{0}", strRedirectController) }, OpenIdConnectAuthenticationDefaults.AuthenticationType); }
54. Now that the AccountController is setup, the last step is to implement the user interface components to provide sign in and sign out capabilities.
1. Locate the Views/Shared folder in the project.
2. Right-click the folder and select Add/View.
3. Complete the Add View dialog as shown in the following picture, then click Add:
1. Add the following code to the **_LoginPartial.cshtml** file:
asp @if (Request.IsAuthenticated) { <text> <ul class="nav navbar-nav navbar-right"> <li class="navbar-text"> Hello, @User.Identity.Name! </li> <li> @Html.ActionLink("Sign out", "SignOut", "Account") </li> </ul> </text> } else { <ul class="nav navbar-nav navbar-right"> <li>@Html.ActionLink("Sign in", "SignIn", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li> </ul> }
1. Open the **_Layout.cshtml** file found in the Views/Shared folder.
2. Locate the part of the file that includes a few links at the top of the page... it should look similar to the following code:
<div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul> </div>
3. Update that navigation to have a new link (the Calendar link added below) as well as a reference to the login control you just created:
<div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> <li>@Html.ActionLink("Calendar", "Index", "Calendar")</li> </ul> @Html.Partial("_LoginPartial") </div>
The Calendar link will not work yet... you will add that in the next exercise.
55. At this point you can test the authentication flow for your application.
56. In Visual Studio, press F5. The browser will automatically launch taking you to the HTTPS start page for the web application.
57. To sign in, click the Sign In link the upper-right corner.
58. Login using your Organizational Account.
59. Upon a successful login, since this will be the first time you have logged into this app, Azure AD will present you with the common consent dialog that looks similar to the following image:
60. Click OK to approve the app's permission request on your data in Office 365.
61. You will then be redirected back to your web application. However notice in the upper right corner, it now shows your email address & the Sign Out link.
Congratulations... at this point your app is configured with Azure AD and leverages OpenID Connect and OWIN to facilitate the authentication process!
Exercise 4: Leverage the Office 365 APIs and SDK
In this exercise you will add a controller and views that utilize the Office 365 APIs and SDK.
1. With the authentication process complete, add a new controller that will retrieve events from your calendar:
2. Right-click the Controllers folder and select Add/Controller.
1. In the Add Scaffold dialog, select MVC 5 Controller - Empty and click Add.
2. In the Add Controller dialog, give the controller the name CalendarController and click Add.
3. Add the following using statements after the existing using statements in the CalendarController.cs file:
using Microsoft.Ajax.Utilities; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Office365.Discovery; using Microsoft.Office365.OutlookServices; using System.Security.Claims; using System.Threading.Tasks; using Exercise2.Utils;
4. Decorate the controller to only allow authenticated users to execute it by adding the [Authorize] attribute on the line immediately before the controller declaration:
namespace Exercise2.Controllers { [Authorize] public class CalendarController : Controller {} }
5. Modify the Index() method to be asynchronous by adding the async keyword and modifying the return type:
public async Task<ActionResult> Index() {}
6. In the Index method, create a few local variables to store the user's claim ID and their object ID returned by Azure AD.
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value; var userObjectId = ClaimsPrincipal.Current.FindFirst(SettingsHelper.ClaimTypeObjectIdentifier).Value; // discover contact endpoint var clientCredential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret); var userIdentifier = new UserIdentifier(userObjectId, UserIdentifierType.UniqueId);
7. Create an AuthenticationContext, passing in the token cache created previously to avoid having to go through the login process each time the user hits the page:
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, new EFADALTokenCache(signInUserId));
8. Create an instance of the DiscoveryClient and authenticate using the user's credentials. This will be used to determine the user's Office 365 API endpoints they have access to and the specific URLs:
DiscoveryClient discovery = new DiscoveryClient(new Uri(SettingsHelper.O365DiscoveryServiceEndpoint), async () => { var authResult = await authContext.AcquireTokenSilentAsync(SettingsHelper.O365DiscoveryResourceId, clientCredential, userIdentifier); return authResult.AccessToken; });
9. Use the DiscoveryClient to query for the user's endpoint for the Calendar API:
var dcr = await discovery.DiscoverCapabilityAsync("Calendar");
10. Use the response from the DiscoveryClient to create an instance of the OutlookServicesClient used to communicate with the Calendar API:
OutlookServicesClient client = new OutlookServicesClient(dcr.ServiceEndpointUri, async () => { var authResult = await authContext.AcquireTokenSilentAsync(dcr.ServiceResourceId, clientCredential, userIdentifier); return authResult.AccessToken; });
11. Finally, execute a query to retrieve the first 20 events in the user's calendar:
var results = await client.Me.Events.Take(20).ExecuteAsync(); ViewBag.Events = results.CurrentPage.OrderBy(c => c.Start);
The last line in the Index() method will return the default view for the controller so leave that as is.
12. Save your changes.
13. Finally, update the view to display the results.
14. Within the CalendarController class, right click the View() at the end of the Index() method and select Add View.
15. Within the Add View dialog, set the following values:
1. View Name: Index.
2. Template: Empty (without model).
Leave all other fields blank & unchecked.
1. Click Add.
16. Within the Views/Calendar/Index.cshtml file, delete all the code in the file and replace it with the following code:
@{ ViewBag.Title = "Home Page"; } <div> <table> <thead> <tr> <th>Subject</th> <th>Start</th> <th>End</th> </tr> </thead> <tbody> @foreach (var o365Contact in ViewBag.Events) { <tr> <td>@o365Contact.Subject</td> <td>@o365Contact.Start</td> <td>@o365Contact.End</td> </tr> } </tbody> </table> </div>
17. Save your changes.
18. Run the application by pushing F5.
19. Click Sign in. When prompted, login using your Organizational Account.
20. When prompted, trust the permissions requested by the application if prompted.
21. When you go back to the homepage of the application, click the Calendar link in the top navigation.
Note: If you don’t see any events, browse to https://outlook.office365.com/owa/#path=/calendar and log into your Organizational Account and add some events.
22. Verify that events appear in the web application.
Congratulations! You have completed your first Office 365 API application.
