├── .gitignore ├── O365-Win-Snippets.sln ├── README.md ├── Readme-images ├── ConnectedServices.PNG ├── LaunchScreen.png └── MainPage.png └── src ├── App.xaml ├── App.xaml.cs ├── Assets ├── Logo.scale-100.png ├── Run.png ├── SmallLogo.scale-100.png ├── SplashScreen.scale-100.png └── StoreLogo.scale-100.png ├── AuthenticationHelper.cs ├── Calendar ├── CalendarSnippets.cs └── CalendarStories.cs ├── Contacts ├── ContactsSnippets.cs └── ContactsStories.cs ├── DurationToDurationStringConverter.cs ├── Email ├── EmailSnippets.cs └── EmailStories.cs ├── Files ├── FilesSnippets.cs └── FilesStories.cs ├── MainPage.xaml ├── MainPage.xaml.cs ├── O365-Win-Snippets.csproj ├── OdataProxy exceptions.txt ├── Package.appxmanifest ├── Properties └── AssemblyInfo.cs ├── ResultToBrushConverter.cs ├── StoryDefinition.cs ├── UsersAndGroups ├── UsersAndGroupsSnippets.cs └── UsersAndGroupsStories.cs ├── ViewModelBase.cs └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /O365-Win-Snippets.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "O365-Win-Snippets", "src\O365-Win-Snippets.csproj", "{186CF7AD-D3B3-478E-B08B-650E71DCA726}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|ARM = Debug|ARM 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|Any CPU = Release|Any CPU 15 | Release|ARM = Release|ARM 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 23 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|ARM.ActiveCfg = Debug|ARM 24 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|ARM.Build.0 = Debug|ARM 25 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|ARM.Deploy.0 = Debug|ARM 26 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|x64.ActiveCfg = Debug|x64 27 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|x64.Build.0 = Debug|x64 28 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|x64.Deploy.0 = Debug|x64 29 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|x86.ActiveCfg = Debug|x86 30 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|x86.Build.0 = Debug|x86 31 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Debug|x86.Deploy.0 = Debug|x86 32 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|Any CPU.Deploy.0 = Release|Any CPU 35 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|ARM.ActiveCfg = Release|ARM 36 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|ARM.Build.0 = Release|ARM 37 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|ARM.Deploy.0 = Release|ARM 38 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|x64.ActiveCfg = Release|x64 39 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|x64.Build.0 = Release|x64 40 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|x64.Deploy.0 = Release|x64 41 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|x86.ActiveCfg = Release|x86 42 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|x86.Build.0 = Release|x86 43 | {186CF7AD-D3B3-478E-B08B-650E71DCA726}.Release|x86.Deploy.0 = Release|x86 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ARCHIVED] Office 365 Code Snippets for Windows 2 | 3 | **Note:** This repo is archived and no longer actively maintained. Security vulnerabilities may exist in the project, or its dependencies. If you plan to reuse or run any code from this repo, be sure to perform appropriate security checks on the code or dependencies first. Do not use this project as the starting point of a production Office Add-in. Always start your production code by using the Office/SharePoint development workload in Visual Studio, or the [Yeoman generator for Office Add-ins](https://github.com/OfficeDev/generator-office), and follow security best practices as you develop the add-in. 4 | 5 | **Table of contents** 6 | 7 | * [Introduction](#introduction) 8 | * [Prerequisites](#prerequisites) 9 | * [Register and configure the app](#register) 10 | * [Build and debug](#build) 11 | * [How the sample affects your tenant data](#how-the-sample-affects-your-tenant-data) 12 | * [Add a snippet](#add-a-snippet) 13 | * [Troubleshooting](#troubleshooting) 14 | * [Questions and comments](#questions) 15 | * [Next steps](#next-steps) 16 | * [Additional resources](#additional-resources) 17 | 18 | 19 | ##Introduction 20 | 21 | The Office 365 Windows Snippets project contains a repository of code snippets that show you how to use the client libraries from the Office 365 API tools to interact with Office 365 objects, including users, groups, calendars, contacts, mail, files, and folders. 22 | 23 | These snippets are simple and self-contained, and you can copy and paste them into your own code, whenever appropriate, or use them as a resource for learning how to use the client libraries. 24 | 25 | The image below shows what you'll see when you launch the app. 26 | 27 | ![Application main page](/Readme-images/MainPage.png "Launch page of O365 Window snippet app") 28 | 29 | You can choose to run all of the snippets, or just the ones you select. After you choose to run, you’ll be prompted to authenticate with your Office 365 account credentials, and the snippets will run. 30 | 31 | **Note:** This project contains code that authenticates and connects a user to Office 365, but if you want to learn about authentication specifically, look at the [Connecting to Office 365 in Windows Store, Phone, and universal apps](https://github.com/OfficeDev/O365-Win-Connect). 32 | 33 | 34 | ## Prerequisites ## 35 | 36 | This sample requires the following: 37 | - Visual Studio 2013 with Update 4. 38 | - [Office 365 API Tools version 1.4.50428.2](http://aka.ms/k0534n). 39 | - An Office 365 account. You can sign up for [an Office 365 Developer subscription](http://aka.ms/ro9c62) that includes the resources that you need to start building Office 365 apps. 40 | 41 | 42 | 43 | ###Register and configure the app 44 | 45 | You can register the app with the Office 365 API Tools for Visual Studio. Be sure to download and install the [Office 365 API tools](http://aka.ms/k0534n) from the Visual Studio Gallery. 46 | 47 | **Note:** If you see any errors while installing packages (for example, *Unable to find "Microsoft.IdentityModel.Clients.ActiveDirectory"*) make sure the local path where you placed the solution is not too long/deep. Moving the solution closer to the root of your drive resolves this issue. 48 | 49 | 1. Open the O365-Win-Snippets.sln file using Visual Studio 2013. 50 | 2. Build the solution. The NuGet Package Restore feature will load the assemblies listed in the packages.config file. You should do this before adding connected services in the following steps so that you don't get older versions of some assemblies. 51 | 3. In the Solution Explorer window, right-click each project name and select Add -> Connected Service. 52 | 4. A Services Manager dialog box will appear. Choose **Office 365** and then **Register your app**. 53 | 5. On the sign-in dialog box, enter the user name and password for your Office 365 tenant. This user name will often follow the pattern @.onmicrosoft.com. If you don't already have an Office 365 tenant, you can get a free Developer Site as part of your MSDN Benefits or sign up for a free trial. After you're signed in, you will see a list of all the services. No permissions will be selected, since the app is not registered to use any services yet. 54 | 55 | 6. To register for the services used in this sample, choose the following services and permissions: 56 | 57 | - **Calendar** - Read and write to your calendars. 58 | - **Contacts** - Read and write to your contacts. 59 | - **Mail** - Read and write to your mail. Send mail as you. 60 | - **My Files** - Read and write your files. 61 | - **Users and Groups** - Sign you in and read your profile. Access your organization's directory. 62 | 63 | 64 | The dialog will look like this: 65 | ![Office 365 app registration page](/Readme-images/ConnectedServices.PNG "Office 365 app registration page") 66 | 67 | After you click **OK** in the Services Manager dialog box, you can select **Build Solution** from the **Build menu** to load the Microsoft.IdentityModel.Clients.ActiveDirectory assembly, or you can wait until you debug. 68 | 69 | 70 | 71 | ## Build and debug ## 72 | 73 | After you've loaded the solution in Visual Studio, press F5 to build and debug. 74 | Run the solution and sign in with your organizational account to Office 365. 75 | 76 | 77 | ##How the sample affects your tenant data 78 | This sample runs REST commands that create, read, update, or delete data. When running commands that delete or edit data, the sample creates fake entities. The fake entities are deleted or edited so that your actual tenant data is unaffected. The sample will leave behind fake entities on your tenant. 79 | 80 | 81 | ##Add a snippet 82 | 83 | This project includes five snippets files: Calendar\CalendarSnippets.cs, Contacts\ContactsSnippets.cs, Email\EmailSnippets.cs, Files\FilesSnippets.cs, and UsersAndGroups\UsersAndGroupsSnippets.cs. 84 | 85 | If you have a snippet of your own and you would like to run it in this project, just follow these three steps: 86 | 87 | 1. **Add your snippet to the snippets file.** Be sure to include a try/catch block. The snippet below is an example of a simple snippet that gets one page of calendar events: 88 | 89 | public static async Task> GetCalendarEventsAsync() 90 | { 91 | try 92 | { 93 | // Make sure we have a reference to the Exchange client 94 | OutlookServicesClient client = await GetOutlookClientAsync(); 95 | 96 | IPagedCollection eventsResults = await client.Me.Calendar.Events.ExecuteAsync(); 97 | 98 | // You can access each event as follows. 99 | if (eventsResults.CurrentPage.Count > 0) 100 | { 101 | string eventId = eventsResults.CurrentPage[0].Id; 102 | Debug.WriteLine("First event:" + eventId); 103 | } 104 | 105 | return eventsResults.CurrentPage.ToList(); 106 | } 107 | catch { return null; } 108 | } 109 | 2. **Create a story that uses your snippet and add it to the associated stories file.** For example, the `TryCreateCalendarEventAsync()` story uses the `AddCalendarEventAsync ()` snippet inside the Calendar\CalendarStories.cs file: 110 | 111 | public static async Task TryCreateCalendarEventAsync() 112 | { 113 | var newEventId = await CalendarSnippets.AddCalendarEventAsync(); 114 | 115 | if (newEventId == null) 116 | return false; 117 | 118 | //Cleanup 119 | await CalendarSnippets.DeleteCalendarEventAsync(newEventId); 120 | 121 | return true; 122 | } 123 | Sometimes your story will need to run snippets in addition to the one that you're implementing. For example, if you want to update an event, you first need to use the `AddCalendarEventAsync()` method to create an event. Then you can update it. Always be sure to use snippets that already exist in the snippets file. If the operation you need doesn't exist, you'll have to create it and then include it in your story. It's a best practice to delete any entities that you create in a story, especially if you're working on anything other than a test or developer tenant. 124 | 125 | 3. **Add your story to the story collection in MainPageXaml.cs** (inside the `CreateTestList()` method): 126 | 127 | `StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Create", RunStoryAsync = CalendarStories.TryCreateCalendarEventAsync });` 128 | 129 | Now you can test your snippet. When you run the app, your snippet will appear as a new box in the grid. Select the box for your snippet, and then run it. Use this as an opportunity to debug your snippet. 130 | 131 | 132 | ## Troubleshooting ## 133 | 134 | - You run the Windows App Certification Kit against the installed app, and the app fails the supported APIs test. This likely happened because the Visual Studio tools installed older versions of some assemblies. Check the entries for Microsoft.Azure.ActiveDirectory.GraphClient and the Microsoft.OData assemblies in your project's packages.config file. Make sure that the version numbers for those assemblies match the version numbers in [this repo's version of packages.config](https://github.com/OfficeDev/O365-Win-Snippets/blob/master/src/packages.config). When you rebuild and reinstall the solution with the updated assemblies, the app should pass the supported APIs test. 135 | 136 | 137 | ## Questions and comments 138 | 139 | We'd love to get your feedback on the O365 Windows Snippets project. You can send your questions and suggestions to us in the [Issues](https://github.com/OfficeDev/O365-Win-Snippets/issues) section of this repository. 140 | 141 | Questions about Office 365 development in general should be posted to [Stack Overflow](http://stackoverflow.com/questions/tagged/Office365+API). Make sure that your questions or comments are tagged with [Office365] and [API]. 142 | 143 | 144 | ## Next steps ## 145 | 146 | - If you're interested in a sample that has a richer interface for interacting with the Office 365 services in a Windows app, look at the [Office 365 Starter Project for Windows Store App](https://github.com/OfficeDev/O365-Windows-Start). 147 | - For more details on what else you can do with the Office 365 services in your Windows app, start with the [Getting started](http://dev.office.com/getting-started) page on dev.office.com. 148 | 149 | 150 | ## Additional resources ## 151 | 152 | - [Office 365 APIs platform overview](https://msdn.microsoft.com/office/office365/howto/platform-development-overview) 153 | - [Office 365 API code samples and videos](https://msdn.microsoft.com/office/office365/howto/starter-projects-and-code-samples) 154 | - [Office developer code samples](http://dev.office.com/code-samples) 155 | - [Office dev center](http://dev.office.com/) 156 | - [Connecting to Office 365 in Windows Store, Phone, and universal apps](https://github.com/OfficeDev/O365-Win-Connect) 157 | - [Office 365 Starter Project for Windows Store App](https://github.com/OfficeDev/O365-Windows-Start) 158 | - [Office 365 Profile sample for Windows](https://github.com/OfficeDev/O365-Win-Profile) 159 | - [Office 365 REST API Explorer for Sites](https://github.com/OfficeDev/Office-365-REST-API-Explorer) 160 | 161 | 162 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 163 | -------------------------------------------------------------------------------- /Readme-images/ConnectedServices.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/Readme-images/ConnectedServices.PNG -------------------------------------------------------------------------------- /Readme-images/LaunchScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/Readme-images/LaunchScreen.png -------------------------------------------------------------------------------- /Readme-images/MainPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/Readme-images/MainPage.png -------------------------------------------------------------------------------- /src/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.ApplicationModel; 7 | using Windows.ApplicationModel.Activation; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI.Xaml; 11 | using Windows.UI.Xaml.Controls; 12 | using Windows.UI.Xaml.Controls.Primitives; 13 | using Windows.UI.Xaml.Data; 14 | using Windows.UI.Xaml.Input; 15 | using Windows.UI.Xaml.Media; 16 | using Windows.UI.Xaml.Navigation; 17 | 18 | // The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227 19 | 20 | namespace O365_Win_Snippets 21 | { 22 | /// 23 | /// Provides application-specific behavior to supplement the default Application class. 24 | /// 25 | sealed partial class App : Application 26 | { 27 | /// 28 | /// Initializes the singleton application object. This is the first line of authored code 29 | /// executed, and as such is the logical equivalent of main() or WinMain(). 30 | /// 31 | public App() 32 | { 33 | this.InitializeComponent(); 34 | this.Suspending += OnSuspending; 35 | } 36 | 37 | /// 38 | /// Invoked when the application is launched normally by the end user. Other entry points 39 | /// will be used such as when the application is launched to open a specific file. 40 | /// 41 | /// Details about the launch request and process. 42 | protected override void OnLaunched(LaunchActivatedEventArgs e) 43 | { 44 | 45 | #if DEBUG 46 | if (System.Diagnostics.Debugger.IsAttached) 47 | { 48 | this.DebugSettings.EnableFrameRateCounter = true; 49 | } 50 | #endif 51 | 52 | Frame rootFrame = Window.Current.Content as Frame; 53 | 54 | // Do not repeat app initialization when the Window already has content, 55 | // just ensure that the window is active 56 | if (rootFrame == null) 57 | { 58 | // Create a Frame to act as the navigation context and navigate to the first page 59 | rootFrame = new Frame(); 60 | // Set the default language 61 | rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; 62 | 63 | rootFrame.NavigationFailed += OnNavigationFailed; 64 | 65 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 66 | { 67 | //TODO: Load state from previously suspended application 68 | } 69 | 70 | // Place the frame in the current Window 71 | Window.Current.Content = rootFrame; 72 | } 73 | 74 | if (rootFrame.Content == null) 75 | { 76 | // When the navigation stack isn't restored navigate to the first page, 77 | // configuring the new page by passing required information as a navigation 78 | // parameter 79 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 80 | } 81 | // Ensure the current window is active 82 | Window.Current.Activate(); 83 | } 84 | 85 | /// 86 | /// Invoked when Navigation to a certain page fails 87 | /// 88 | /// The Frame which failed navigation 89 | /// Details about the navigation failure 90 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 91 | { 92 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 93 | } 94 | 95 | /// 96 | /// Invoked when application execution is being suspended. Application state is saved 97 | /// without knowing whether the application will be terminated or resumed with the contents 98 | /// of memory still intact. 99 | /// 100 | /// The source of the suspend request. 101 | /// Details about the suspend request. 102 | private void OnSuspending(object sender, SuspendingEventArgs e) 103 | { 104 | var deferral = e.SuspendingOperation.GetDeferral(); 105 | //TODO: Save application state and stop any background activity 106 | deferral.Complete(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Assets/Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/src/Assets/Logo.scale-100.png -------------------------------------------------------------------------------- /src/Assets/Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/src/Assets/Run.png -------------------------------------------------------------------------------- /src/Assets/SmallLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/src/Assets/SmallLogo.scale-100.png -------------------------------------------------------------------------------- /src/Assets/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/src/Assets/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /src/Assets/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/O365-Win-Snippets/9665c4fb551d015b3edcbf3d28c8750f1eb33ac4/src/Assets/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /src/AuthenticationHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 4 | using Microsoft.Office365.Discovery; 5 | using Microsoft.Office365.OutlookServices; 6 | using System; 7 | using System.Diagnostics; 8 | using System.Net.Http; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | using Windows.Security.Authentication.Web; 12 | using Windows.Storage; 13 | 14 | namespace O365_Win_Snippets 15 | { 16 | public static class AuthenticationHelper 17 | { 18 | // The ClientID is added as a resource in App.xaml when you register the app with Office 365. 19 | // As a convenience, we load that value into a variable called ClientID. This way the variable 20 | // will always be in sync with whatever client id is added to App.xaml. 21 | private static readonly string ClientID = App.Current.Resources["ida:ClientID"].ToString(); 22 | private static Uri _returnUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri(); 23 | 24 | 25 | // Properties used for communicating with your Windows Azure AD tenant. 26 | // The AuthorizationUri is added as a resource in App.xaml when you regiter the app with 27 | // Office 365. As a convenience, we load that value into a variable called _commonAuthority, adding _common to this Url to signify 28 | // multi-tenancy. This way it will always be in sync with whatever value is added to App.xaml. 29 | public static readonly string CommonAuthority = App.Current.Resources["ida:AADInstance"].ToString() + @"Common"; 30 | public static readonly Uri DiscoveryServiceEndpointUri = new Uri("https://api.office.com/discovery/v1.0/me/"); 31 | public const string DiscoveryResourceId = "https://api.office.com/discovery/"; 32 | 33 | 34 | //Store login settings 35 | public static ApplicationDataContainer _settings = ApplicationData.Current.LocalSettings; 36 | 37 | //Property for storing and returning the authority used by the last authentication. 38 | //This value is populated when the user connects to the service and made null when the user signs out. 39 | public static string LastAuthority 40 | { 41 | get 42 | { 43 | if (_settings.Values.ContainsKey("LastAuthority") && _settings.Values["LastAuthority"] != null) 44 | { 45 | return _settings.Values["LastAuthority"].ToString(); 46 | } 47 | else 48 | { 49 | return string.Empty; 50 | } 51 | 52 | } 53 | 54 | set 55 | { 56 | _settings.Values["LastAuthority"] = value; 57 | } 58 | } 59 | 60 | //Property for storing the tenant id so that we can pass it to the ActiveDirectoryClient constructor. 61 | //This value is populated when the user connects to the service and made null when the user signs out. 62 | static internal string TenantId 63 | { 64 | get 65 | { 66 | if (_settings.Values.ContainsKey("TenantId") && _settings.Values["TenantId"] != null) 67 | { 68 | return _settings.Values["TenantId"].ToString(); 69 | } 70 | else 71 | { 72 | return string.Empty; 73 | } 74 | 75 | } 76 | 77 | set 78 | { 79 | _settings.Values["TenantId"] = value; 80 | } 81 | } 82 | 83 | // Property for storing the logged-in user so that we can display user properties later. 84 | //This value is populated when the user connects to the service. 85 | static internal string LoggedInUser 86 | { 87 | get 88 | { 89 | if (_settings.Values.ContainsKey("LoggedInUser") && _settings.Values["LoggedInUser"] != null) 90 | { 91 | return _settings.Values["LoggedInUser"].ToString(); 92 | } 93 | else 94 | { 95 | return string.Empty; 96 | } 97 | 98 | } 99 | 100 | set 101 | { 102 | _settings.Values["LoggedInUser"] = value; 103 | } 104 | } 105 | 106 | 107 | // Property for storing the logged-in user email address so that we can display user properties later. 108 | //This value is populated when the user connects to the service. 109 | public static string LoggedInUserEmail 110 | { 111 | get 112 | { 113 | if (_settings.Values.ContainsKey("LoggedInUserEmail") && _settings.Values["LoggedInUserEmail"] != null) 114 | { 115 | return _settings.Values["LoggedInUserEmail"].ToString(); 116 | } 117 | else 118 | { 119 | return string.Empty; 120 | } 121 | 122 | } 123 | 124 | set 125 | { 126 | _settings.Values["LoggedInUserEmail"] = value; 127 | } 128 | } 129 | 130 | //Property for storing the authentication context. 131 | public static AuthenticationContext _authenticationContext { get; set; } 132 | 133 | 134 | /// 135 | /// Signs the user out of the service. 136 | /// 137 | public static void SignOut() 138 | { 139 | //Handle case where user signs out without first running any snippets. 140 | if (_authenticationContext == null) 141 | { 142 | _authenticationContext = new AuthenticationContext(LastAuthority); 143 | } 144 | 145 | _authenticationContext.TokenCache.Clear(); 146 | 147 | //Clean up all existing clients 148 | //Clear stored values from last authentication. 149 | _settings.Values["TenantId"] = null; 150 | _settings.Values["LastAuthority"] = null; 151 | _settings.Values["LoggedInUser"] = null; 152 | _settings.Values["LoggedInUserEmail"] = null; 153 | 154 | } 155 | 156 | // Get an access token for the given context and resourceId. An attempt is first made to 157 | // acquire the token silently. If that fails, then we try to acquire the token by prompting the user. 158 | public static async Task GetTokenHelperAsync(AuthenticationContext context, string resourceId) 159 | { 160 | string accessToken = null; 161 | AuthenticationResult result = null; 162 | 163 | result = await context.AcquireTokenAsync(resourceId, ClientID, _returnUri); 164 | 165 | if (result.Status == AuthenticationStatus.Success) 166 | { 167 | accessToken = result.AccessToken; 168 | //Store values for logged-in user, tenant id, and authority, so that 169 | //they can be re-used if the user re-opens the app without disconnecting. 170 | _settings.Values["LoggedInUser"] = result.UserInfo.GivenName; 171 | _settings.Values["LoggedInUserEmail"] = result.UserInfo.DisplayableId; 172 | _settings.Values["TenantId"] = result.TenantId; 173 | _settings.Values["LastAuthority"] = context.Authority; 174 | 175 | return accessToken; 176 | } 177 | else 178 | { 179 | return null; 180 | } 181 | } 182 | 183 | } 184 | } 185 | 186 | 187 | 188 | //********************************************************* 189 | // 190 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 191 | // 192 | //Copyright (c) Microsoft Corporation 193 | //All rights reserved. 194 | // 195 | // MIT License: 196 | // Permission is hereby granted, free of charge, to any person obtaining 197 | // a copy of this software and associated documentation files (the 198 | // ""Software""), to deal in the Software without restriction, including 199 | // without limitation the rights to use, copy, modify, merge, publish, 200 | // distribute, sublicense, and/or sell copies of the Software, and to 201 | // permit persons to whom the Software is furnished to do so, subject to 202 | // the following conditions: 203 | 204 | // The above copyright notice and this permission notice shall be 205 | // included in all copies or substantial portions of the Software. 206 | 207 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 208 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 209 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 210 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 211 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 212 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 213 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 214 | // 215 | //********************************************************* -------------------------------------------------------------------------------- /src/Calendar/CalendarSnippets.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 4 | using Microsoft.OData.Core; 5 | using Microsoft.Office365.Discovery; 6 | using Microsoft.Office365.OutlookServices; 7 | using Microsoft.OData.ProxyExtensions; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Diagnostics; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace O365_Win_Snippets 16 | { 17 | public static class CalendarSnippets 18 | { 19 | private static OutlookServicesClient _outlookClient = null; 20 | 21 | /// 22 | /// Checks that an OutlookServicesClient object is available. 23 | /// 24 | /// The OutlookServicesClient object. 25 | public static async Task GetOutlookClientAsync() 26 | { 27 | if (_outlookClient != null && !String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 28 | { 29 | Debug.WriteLine("Got an Outlook client for Calendar."); 30 | return _outlookClient; 31 | } 32 | else 33 | { 34 | try 35 | { 36 | //First, look for the authority used during the last authentication. 37 | //If that value is not populated, use CommonAuthority. 38 | string authority = null; 39 | 40 | if (String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 41 | { 42 | authority = AuthenticationHelper.CommonAuthority; 43 | } 44 | else 45 | { 46 | authority = AuthenticationHelper.LastAuthority; 47 | } 48 | 49 | // Create an AuthenticationContext using this authority. 50 | AuthenticationHelper._authenticationContext = new AuthenticationContext(authority); 51 | 52 | // Set the value of _authenticationContext.UseCorporateNetwork to true so that you 53 | // can use this app inside a corporate intranet. If the value of UseCorporateNetwork 54 | // is true, you also need to add the Enterprise Authentication, Private Networks, and 55 | // Shared User Certificates capabilities in the Package.appxmanifest file. 56 | AuthenticationHelper._authenticationContext.UseCorporateNetwork = true; 57 | 58 | //See the Discovery Service Sample (https://github.com/OfficeDev/Office365-Discovery-Service-Sample) 59 | //for an approach that improves performance by storing the discovery service information in a cache. 60 | DiscoveryClient discoveryClient = new DiscoveryClient( 61 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, AuthenticationHelper.DiscoveryResourceId)); 62 | 63 | // Get the specified capability ("Calendar"). 64 | CapabilityDiscoveryResult result = 65 | await discoveryClient.DiscoverCapabilityAsync("Calendar"); 66 | 67 | var token = await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId); 68 | // Check the token 69 | if (String.IsNullOrEmpty(token)) 70 | { 71 | // User cancelled sign-in 72 | return null; 73 | } 74 | else 75 | { 76 | 77 | _outlookClient = new OutlookServicesClient( 78 | result.ServiceEndpointUri, 79 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId)); 80 | Debug.WriteLine("Got an Outlook client for Calendar."); 81 | return _outlookClient; 82 | } 83 | } 84 | // The following is a list of exceptions you should consider handling in your app. 85 | // In the case of this sample, the exceptions are handled by returning null upstream. 86 | catch (DiscoveryFailedException dfe) 87 | { 88 | Debug.WriteLine(dfe.Message); 89 | } 90 | catch (ArgumentException ae) 91 | { 92 | Debug.WriteLine(ae.Message); 93 | } 94 | 95 | AuthenticationHelper._authenticationContext.TokenCache.Clear(); 96 | 97 | return null; 98 | } 99 | } 100 | 101 | /// 102 | /// Gets a page of celendar events. 103 | /// 104 | /// A list of calendar events. 105 | public static async Task> GetCalendarEventsAsync() 106 | { 107 | 108 | // Make sure we have a reference to the Exchange client 109 | OutlookServicesClient client = await GetOutlookClientAsync(); 110 | 111 | IPagedCollection eventsResults = await client.Me.Calendar.Events.ExecuteAsync(); 112 | 113 | // You can access each event as follows. 114 | if (eventsResults.CurrentPage.Count > 0) 115 | { 116 | string eventId = eventsResults.CurrentPage[0].Id; 117 | Debug.WriteLine("First event:" + eventId); 118 | } 119 | 120 | return eventsResults.CurrentPage.ToList(); 121 | 122 | } 123 | 124 | /// 125 | /// Adds a new event to user's default calendar 126 | /// 127 | public static async Task AddCalendarEventAsync() 128 | { 129 | 130 | // Make sure we have a reference to the Exchange client 131 | var client = await GetOutlookClientAsync(); 132 | 133 | Location location = new Location 134 | { 135 | DisplayName = "Water cooler" 136 | }; 137 | ItemBody body = new ItemBody 138 | { 139 | Content = "Status updates, blocking issues, and next steps", 140 | ContentType = BodyType.Text 141 | }; 142 | 143 | Attendee[] attendees = 144 | { 145 | new Attendee 146 | { 147 | Type = AttendeeType.Required, 148 | EmailAddress = new EmailAddress 149 | { 150 | Address = "mara@fabrikam.com" 151 | }, 152 | }, 153 | }; 154 | 155 | Event newEvent = new Event 156 | { 157 | Subject = "Weekly Sync", 158 | Location = location, 159 | Attendees = attendees, 160 | Start = new DateTimeOffset(new DateTime(2014, 12, 1, 9, 30, 0)), 161 | End = new DateTimeOffset(new DateTime(2014, 12, 1, 10, 0, 0)), 162 | Body = body 163 | }; 164 | 165 | await client.Me.Calendar.Events.AddEventAsync(newEvent); 166 | 167 | // Get the ID of the event. 168 | string eventId = newEvent.Id; 169 | 170 | Debug.WriteLine("Added event: " + eventId); 171 | return eventId; 172 | 173 | 174 | } 175 | 176 | /// 177 | /// Adds a new event to user's default calendar 178 | /// 179 | /// string. The name of the event location 180 | /// string. The body of the event. 181 | /// string. semi-colon delimited list of invitee email addresses 182 | /// string. The subject of the event 183 | /// DateTimeOffset. The start date of the event 184 | /// DateTimeOffset. The end date of the event 185 | /// TimeSpan. The start hour:Min:Sec of the event 186 | /// TimeSpan. The end hour:Min:Sec of the event 187 | /// The Id of the event that was created; Otherwise, null. 188 | public static async Task AddCalendarEventWithArgsAsync( 189 | string LocationName, 190 | string BodyContent, 191 | string Attendees, 192 | string EventName, 193 | DateTimeOffset start, 194 | DateTimeOffset end, 195 | TimeSpan startTime, 196 | TimeSpan endTime) 197 | { 198 | string newEventId = string.Empty; 199 | Location location = new Location(); 200 | location.DisplayName = LocationName; 201 | ItemBody body = new ItemBody(); 202 | body.Content = BodyContent; 203 | body.ContentType = BodyType.Text; 204 | string[] splitter = { ";" }; 205 | var splitAttendeeString = Attendees.Split(splitter, StringSplitOptions.RemoveEmptyEntries); 206 | Attendee[] attendees = new Attendee[splitAttendeeString.Length]; 207 | for (int i = 0; i < splitAttendeeString.Length; i++) 208 | { 209 | attendees[i] = new Attendee(); 210 | attendees[i].Type = AttendeeType.Required; 211 | attendees[i].EmailAddress = new EmailAddress() { Address = splitAttendeeString[i] }; 212 | } 213 | 214 | Event newEvent = new Event 215 | { 216 | Subject = EventName, 217 | Location = location, 218 | Attendees = attendees, 219 | Start = start, 220 | End = end, 221 | Body = body, 222 | }; 223 | //Add new times to start and end dates. 224 | newEvent.Start = (DateTimeOffset?)CalcNewTime(newEvent.Start, start, startTime); 225 | newEvent.End = (DateTimeOffset?)CalcNewTime(newEvent.End, end, endTime); 226 | 227 | // Make sure we have a reference to the calendar client 228 | var exchangeClient = await GetOutlookClientAsync(); 229 | 230 | // This results in a call to the service. 231 | await exchangeClient.Me.Events.AddEventAsync(newEvent); 232 | Debug.WriteLine("Added event: " + newEvent.Id); 233 | return newEvent.Id; 234 | 235 | } 236 | 237 | 238 | /// 239 | /// Updates an existing event in the user's default calendar 240 | /// 241 | /// string. The unique Id of the event to update 242 | /// string. The name of the event location 243 | /// string. The body of the event. 244 | /// string. semi-colon delimited list of invitee email addresses 245 | /// string. The subject of the event 246 | /// DateTimeOffset. The start date of the event 247 | /// DateTimeOffset. The end date of the event 248 | /// TimeSpan. The start hour:Min:Sec of the event 249 | /// TimeSpan. The end hour:Min:Sec of the event 250 | /// IEvent. The updated event 251 | public static async Task UpdateCalendarEventAsync(string eventId, 252 | string LocationName, 253 | string BodyContent, 254 | string Attendees, 255 | string EventName, 256 | DateTimeOffset start, 257 | DateTimeOffset end, 258 | TimeSpan startTime, 259 | TimeSpan endTime) 260 | { 261 | // Make sure we have a reference to the Exchange client 262 | var client = await GetOutlookClientAsync(); 263 | 264 | var eventToUpdate = await client.Me.Calendar.Events.GetById(eventId).ExecuteAsync(); 265 | eventToUpdate.Attendees.Clear(); 266 | string[] splitter = { ";" }; 267 | var splitAttendeeString = Attendees.Split(splitter, StringSplitOptions.RemoveEmptyEntries); 268 | Attendee[] attendees = new Attendee[splitAttendeeString.Length]; 269 | for (int i = 0; i < splitAttendeeString.Length; i++) 270 | { 271 | Attendee newAttendee = new Attendee(); 272 | newAttendee.EmailAddress = new EmailAddress() { Address = splitAttendeeString[i] }; 273 | newAttendee.Type = AttendeeType.Required; 274 | eventToUpdate.Attendees.Add(newAttendee); 275 | } 276 | 277 | eventToUpdate.Subject = EventName; 278 | Location location = new Location(); 279 | location.DisplayName = LocationName; 280 | eventToUpdate.Location = location; 281 | eventToUpdate.Start = (DateTimeOffset?)CalcNewTime(eventToUpdate.Start, start, startTime); 282 | eventToUpdate.End = (DateTimeOffset?)CalcNewTime(eventToUpdate.End, end, endTime); 283 | ItemBody body = new ItemBody(); 284 | body.ContentType = BodyType.Text; 285 | body.Content = BodyContent; 286 | eventToUpdate.Body = body; 287 | 288 | // Update the calendar event in Exchange 289 | await eventToUpdate.UpdateAsync(); 290 | 291 | Debug.WriteLine("Updated event: " + eventToUpdate.Id); 292 | return eventToUpdate; 293 | 294 | // A note about Batch Updating 295 | // You can save multiple updates on the client and save them all at once (batch) by 296 | // implementing the following pattern: 297 | // 1. Call UpdateAsync(true) for each event you want to update. Setting the parameter dontSave to true 298 | // means that the updates are registered locally on the client, but won't be posted to the server. 299 | // 2. Call exchangeClient.Context.SaveChangesAsync() to post all event updates you have saved locally 300 | // using the preceding UpdateAsync(true) call to the server, i.e., the user's Office 365 calendar. 301 | 302 | } 303 | 304 | /// 305 | /// Removes an event from the user's default calendar. 306 | /// 307 | /// string. The unique Id of the event to delete. 308 | /// 309 | public static async Task DeleteCalendarEventAsync(string eventId) 310 | { 311 | try 312 | { 313 | // Make sure we have a reference to the Exchange client 314 | var exchangeClient = await GetOutlookClientAsync(); 315 | 316 | // Get the event to be removed from the Exchange service. This results in a call to the service. 317 | var eventToDelete = await exchangeClient.Me.Calendar.Events.GetById(eventId).ExecuteAsync(); 318 | 319 | // Delete the event. This results in a call to the service. 320 | await eventToDelete.DeleteAsync(false); 321 | Debug.WriteLine("Deleted event: " + eventToDelete.Id); 322 | return eventToDelete; 323 | } 324 | catch (ODataErrorException ex) 325 | { 326 | // GetById will throw an ODataErrorException when the 327 | // item with the specified Id can't be found in the contact store on the server. 328 | Debug.WriteLine(ex.Message); 329 | return null; 330 | } 331 | 332 | } 333 | 334 | //Helper method that creates a new DateTime for an updated meeting. 335 | 336 | /// 337 | /// Sets new time component of the datetimeoffset from the TimeSpan property of the UI 338 | /// 339 | /// DateTimeOffset. The original date 340 | /// DateTimeOffset. The new date 341 | /// TimeSpan. The new time 342 | /// DateTimeOffset. The new start date/time 343 | private static DateTimeOffset CalcNewTime(DateTimeOffset? OldDate, DateTimeOffset NewDate, TimeSpan newTime) 344 | { 345 | //Default return value to New start date 346 | DateTimeOffset returnValue = NewDate; 347 | 348 | //Get original time components 349 | int hour = OldDate.Value.ToLocalTime().TimeOfDay.Hours; 350 | int min = OldDate.Value.ToLocalTime().TimeOfDay.Minutes; 351 | int second = OldDate.Value.ToLocalTime().TimeOfDay.Seconds; 352 | 353 | //Get new time components from TimeSpan updated by UI 354 | int newHour = newTime.Hours; 355 | int newMin = newTime.Minutes; 356 | int newSec = newTime.Seconds; 357 | 358 | //Update the new datetime by the difference between old and new time components 359 | returnValue = returnValue.AddHours(newHour - hour); 360 | returnValue = returnValue.AddMinutes(newMin - min); 361 | returnValue = returnValue.AddSeconds(newSec - second); 362 | 363 | return returnValue; 364 | } 365 | 366 | } 367 | } 368 | 369 | //********************************************************* 370 | // 371 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 372 | // 373 | //Copyright (c) Microsoft Corporation 374 | //All rights reserved. 375 | // 376 | // MIT License: 377 | // Permission is hereby granted, free of charge, to any person obtaining 378 | // a copy of this software and associated documentation files (the 379 | // ""Software""), to deal in the Software without restriction, including 380 | // without limitation the rights to use, copy, modify, merge, publish, 381 | // distribute, sublicense, and/or sell copies of the Software, and to 382 | // permit persons to whom the Software is furnished to do so, subject to 383 | // the following conditions: 384 | 385 | // The above copyright notice and this permission notice shall be 386 | // included in all copies or substantial portions of the Software. 387 | 388 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 389 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 390 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 391 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 392 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 393 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 394 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 395 | // 396 | //********************************************************* -------------------------------------------------------------------------------- /src/Calendar/CalendarStories.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace O365_Win_Snippets 10 | { 11 | public class CalendarStories 12 | { 13 | private static readonly string STORY_DATA_IDENTIFIER = Guid.NewGuid().ToString(); 14 | 15 | public static async Task TryGetOutlookClientAsync() 16 | { 17 | var outlookClient = await CalendarSnippets.GetOutlookClientAsync(); 18 | return outlookClient != null; 19 | } 20 | 21 | public static async Task TryGetCalendarEventsAsync() 22 | { 23 | var events = await CalendarSnippets.GetCalendarEventsAsync(); 24 | 25 | return events != null; 26 | } 27 | 28 | public static async Task TryCreateCalendarEventAsync() 29 | { 30 | var newEventId = await CalendarSnippets.AddCalendarEventAsync(); 31 | 32 | if (newEventId == null) 33 | return false; 34 | 35 | //Cleanup 36 | await CalendarSnippets.DeleteCalendarEventAsync(newEventId); 37 | 38 | return true; 39 | } 40 | 41 | public static async Task TryCreateCalendarEventWithArgsAsync() 42 | { 43 | var newEventId = await CalendarSnippets.AddCalendarEventWithArgsAsync( 44 | Guid.NewGuid().ToString(), 45 | STORY_DATA_IDENTIFIER, 46 | string.Empty, 47 | Guid.NewGuid().ToString(), 48 | new DateTimeOffset(DateTime.Now), 49 | new DateTimeOffset(DateTime.Now), 50 | new TimeSpan(DateTime.Now.Ticks), 51 | new TimeSpan(DateTime.Now.Ticks) 52 | ); 53 | 54 | if (newEventId == null) 55 | return false; 56 | 57 | //Cleanup 58 | await CalendarSnippets.DeleteCalendarEventAsync(newEventId); 59 | 60 | return true; 61 | } 62 | 63 | public static async Task TryUpdateCalendarEventAsync() 64 | { 65 | 66 | var newEventId = await CalendarSnippets.AddCalendarEventWithArgsAsync( 67 | "OrigLocationValue", 68 | STORY_DATA_IDENTIFIER, 69 | string.Empty, 70 | Guid.NewGuid().ToString(), 71 | new DateTimeOffset(DateTime.Now), 72 | new DateTimeOffset(DateTime.Now), 73 | new TimeSpan(DateTime.Now.Ticks), 74 | new TimeSpan(DateTime.Now.Ticks) 75 | ); 76 | 77 | if (newEventId == null) 78 | return false; 79 | 80 | // Update our event 81 | var updatedEvent = await CalendarSnippets.UpdateCalendarEventAsync(newEventId, 82 | "NewLocationValue", 83 | STORY_DATA_IDENTIFIER, 84 | string.Empty, 85 | Guid.NewGuid().ToString(), 86 | new DateTimeOffset(DateTime.Now), 87 | new DateTimeOffset(DateTime.Now), 88 | new TimeSpan(DateTime.Now.Ticks), 89 | new TimeSpan(DateTime.Now.Ticks) 90 | ); 91 | 92 | if (updatedEvent == null || updatedEvent.Id != newEventId) 93 | return false; 94 | 95 | //Cleanup 96 | await CalendarSnippets.DeleteCalendarEventAsync(newEventId); 97 | 98 | 99 | return true; 100 | } 101 | 102 | public static async Task TryDeleteCalendarEventAsync() 103 | { 104 | var newEventId = await CalendarSnippets.AddCalendarEventAsync(); 105 | 106 | if (newEventId == null) 107 | return false; 108 | 109 | // Delete the event 110 | var deletedEvent = await CalendarSnippets.DeleteCalendarEventAsync(newEventId); 111 | if (deletedEvent == null) 112 | return false; 113 | 114 | return true; 115 | } 116 | 117 | } 118 | } 119 | 120 | //********************************************************* 121 | // 122 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 123 | // 124 | //Copyright (c) Microsoft Corporation 125 | //All rights reserved. 126 | // 127 | // MIT License: 128 | // Permission is hereby granted, free of charge, to any person obtaining 129 | // a copy of this software and associated documentation files (the 130 | // ""Software""), to deal in the Software without restriction, including 131 | // without limitation the rights to use, copy, modify, merge, publish, 132 | // distribute, sublicense, and/or sell copies of the Software, and to 133 | // permit persons to whom the Software is furnished to do so, subject to 134 | // the following conditions: 135 | 136 | // The above copyright notice and this permission notice shall be 137 | // included in all copies or substantial portions of the Software. 138 | 139 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 140 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 141 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 142 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 143 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 144 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 145 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 146 | // 147 | //********************************************************* -------------------------------------------------------------------------------- /src/Contacts/ContactsSnippets.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 4 | using Microsoft.OData.Core; 5 | using Microsoft.Office365.Discovery; 6 | using Microsoft.Office365.OutlookServices; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Diagnostics; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | 15 | namespace O365_Win_Snippets 16 | { 17 | public static class ContactsSnippets 18 | { 19 | 20 | private static OutlookServicesClient _outlookClient = null; 21 | 22 | /// 23 | /// Checks that an OutlookServicesClient object is available. 24 | /// 25 | /// The OutlookServicesClient object. 26 | public static async Task GetOutlookClientAsync() 27 | { 28 | 29 | if (_outlookClient != null && !String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 30 | { 31 | Debug.WriteLine("Got an Outlook client for Contacts."); 32 | return _outlookClient; 33 | } 34 | else 35 | { 36 | try 37 | { 38 | //First, look for the authority used during the last authentication. 39 | //If that value is not populated, use CommonAuthority. 40 | string authority = null; 41 | 42 | if (String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 43 | { 44 | authority = AuthenticationHelper.CommonAuthority; 45 | } 46 | else 47 | { 48 | authority = AuthenticationHelper.LastAuthority; 49 | } 50 | 51 | // Create an AuthenticationContext using this authority. 52 | AuthenticationHelper._authenticationContext = new AuthenticationContext(authority); 53 | 54 | // Set the value of _authenticationContext.UseCorporateNetwork to true so that you 55 | // can use this app inside a corporate intranet. If the value of UseCorporateNetwork 56 | // is true, you also need to add the Enterprise Authentication, Private Networks, and 57 | // Shared User Certificates capabilities in the Package.appxmanifest file. 58 | AuthenticationHelper._authenticationContext.UseCorporateNetwork = true; 59 | 60 | //See the Discovery Service Sample (https://github.com/OfficeDev/Office365-Discovery-Service-Sample) 61 | //for an approach that improves performance by storing the discovery service information in a cache. 62 | DiscoveryClient discoveryClient = new DiscoveryClient( 63 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, AuthenticationHelper.DiscoveryResourceId)); 64 | 65 | // Get the specified capability ("Calendar"). 66 | CapabilityDiscoveryResult result = 67 | await discoveryClient.DiscoverCapabilityAsync("Contacts"); 68 | 69 | var token = await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId); 70 | // Check the token 71 | if (String.IsNullOrEmpty(token)) 72 | { 73 | // User cancelled sign-in 74 | return null; 75 | } 76 | else 77 | { 78 | 79 | _outlookClient = new OutlookServicesClient( 80 | result.ServiceEndpointUri, 81 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId)); 82 | Debug.WriteLine("Got an Outlook client for Contacts"); 83 | return _outlookClient; 84 | } 85 | } 86 | // The following is a list of exceptions you should consider handling in your app. 87 | // In the case of this sample, the exceptions are handled by returning null upstream. 88 | catch (DiscoveryFailedException dfe) 89 | { 90 | Debug.WriteLine(dfe.Message); 91 | } 92 | catch (ArgumentException ae) 93 | { 94 | Debug.WriteLine(ae.Message); 95 | } 96 | 97 | AuthenticationHelper._authenticationContext.TokenCache.Clear(); 98 | 99 | return null; 100 | } 101 | } 102 | 103 | /// 104 | /// Retrieve a page of contacts from the server. 105 | /// 106 | /// A list of contacts. 107 | public static async Task> GetContactsPageAsync() 108 | { 109 | // Get exchangeclient 110 | var outlookClient = await GetOutlookClientAsync(); 111 | 112 | // Get contacts 113 | var contactsResults = await outlookClient.Me.Contacts.ExecuteAsync(); 114 | 115 | // You can access each contact as follows. 116 | if (contactsResults.CurrentPage.Count > 0) 117 | { 118 | string contactId = contactsResults.CurrentPage[0].Id; 119 | 120 | if (contactsResults.CurrentPage.Count > 0) 121 | { 122 | Debug.WriteLine("First contact:" + contactId); 123 | } 124 | } 125 | 126 | return contactsResults.CurrentPage.ToList(); 127 | } 128 | 129 | public static async Task GetContactAsync(string Id) 130 | { 131 | try 132 | { 133 | var exchangeClient = await GetOutlookClientAsync(); 134 | var contact = await exchangeClient.Me.Contacts.GetById(Id).ExecuteAsync(); 135 | 136 | Debug.WriteLine("Got contact:" + contact.Id); 137 | 138 | return contact; 139 | } 140 | catch (ODataErrorException ex) 141 | { 142 | // GetById will throw an ODataErrorException when the 143 | // item with the specified Id can't be found in the contact store on the server. 144 | Debug.WriteLine(ex.Message); 145 | return null; 146 | } 147 | } 148 | 149 | /// 150 | /// Adds a new contact. 151 | /// 152 | public static async Task AddContactItemAsync( 153 | string fileAs, 154 | string givenName, 155 | string surname, 156 | string jobTitle, 157 | string email, 158 | string workPhone, 159 | string mobilePhone 160 | ) 161 | { 162 | Contact newContact = new Contact 163 | { 164 | FileAs = fileAs, 165 | GivenName = givenName, 166 | Surname = surname, 167 | JobTitle = jobTitle, 168 | MobilePhone1 = mobilePhone 169 | }; 170 | 171 | newContact.BusinessPhones.Add(workPhone); 172 | 173 | 174 | // Note: Setting EmailAddress1 to a null or empty string will throw an exception that 175 | // states the email address is invalid and the contact cannot be added. 176 | // Setting EmailAddress1 to a string that does not resemble an email address will not 177 | // cause an exception to be thrown, but the value is not stored in EmailAddress1. 178 | if (!string.IsNullOrEmpty(email)) 179 | newContact.EmailAddresses.Add(new EmailAddress() { Address = email }); 180 | 181 | // Make sure we have a reference to the Exchange client 182 | var outlookClient = await GetOutlookClientAsync(); 183 | 184 | // This results in a call to the service. 185 | await outlookClient.Me.Contacts.AddContactAsync(newContact); 186 | 187 | Debug.WriteLine("Added contact: " + newContact.Id); 188 | 189 | return newContact; 190 | 191 | } 192 | 193 | /// 194 | /// Updates an existing contact. 195 | /// 196 | public static async Task UpdateContactItemAsync(string selectedContactId, 197 | string fileAs, 198 | string givenName, 199 | string surname, 200 | string jobTitle, 201 | string email, 202 | string workPhone, 203 | string mobilePhone, 204 | byte[] contactImage 205 | ) 206 | { 207 | 208 | // Make sure we have a reference to the Exchange client 209 | var exchangeClient = await GetOutlookClientAsync(); 210 | 211 | var contactToUpdate = await exchangeClient.Me.Contacts[selectedContactId].ExecuteAsync(); 212 | 213 | contactToUpdate.FileAs = fileAs; 214 | contactToUpdate.GivenName = givenName; 215 | contactToUpdate.Surname = surname; 216 | contactToUpdate.JobTitle = jobTitle; 217 | 218 | contactToUpdate.MobilePhone1 = mobilePhone; 219 | 220 | // Note: Setting EmailAddress1 to a null or empty string will throw an exception that 221 | // states the email address is invalid and the contact cannot be added. 222 | // Setting EmailAddress1 to a string that does not resemble an email address will not 223 | // cause an exception to be thrown, but the value is not stored in EmailAddress1. 224 | 225 | if (!string.IsNullOrEmpty(email)) 226 | { 227 | contactToUpdate.EmailAddresses.Clear(); 228 | contactToUpdate.EmailAddresses.Add(new EmailAddress() { Address = email, Name = email }); 229 | } 230 | 231 | // Update the contact in Exchange 232 | await contactToUpdate.UpdateAsync(); 233 | 234 | Debug.WriteLine("Updated contact: " + contactToUpdate.Id); 235 | 236 | return contactToUpdate; 237 | 238 | // A note about Batch Updating 239 | // You can save multiple updates on the client and save them all at once (batch) by 240 | // implementing the following pattern: 241 | // 1. Call UpdateAsync(true) for each contact you want to update. Setting the parameter dontSave to true 242 | // means that the updates are registered locally on the client, but won't be posted to the server. 243 | // 2. Call exchangeClient.Context.SaveChangesAsync() to post all contact updates you have saved locally 244 | // using the preceding UpdateAsync(true) call to the server, i.e., the user's Office 365 contacts list. 245 | 246 | } 247 | 248 | /// 249 | /// Deletes a contact. 250 | /// 251 | /// The contact to delete. 252 | /// True if deleted;Otherwise, false. 253 | public static async Task DeleteContactAsync(string contactId) 254 | { 255 | try 256 | { 257 | // Make sure we have a reference to the Exchange client 258 | var outlookClient = await GetOutlookClientAsync(); 259 | 260 | var contactToDelete = await outlookClient.Me.Contacts.GetById(contactId).ExecuteAsync(); 261 | 262 | await contactToDelete.DeleteAsync(); 263 | 264 | Debug.WriteLine("Deleted contact: " + contactToDelete.Id); 265 | 266 | return true; 267 | } 268 | catch (ODataErrorException ex) 269 | { 270 | // GetById will throw an ODataErrorException when the 271 | // item with the specified Id can't be found in the contact store on the server. 272 | Debug.WriteLine(ex.Message); 273 | return false; 274 | } 275 | } 276 | 277 | } 278 | } 279 | 280 | //********************************************************* 281 | // 282 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 283 | // 284 | //Copyright (c) Microsoft Corporation 285 | //All rights reserved. 286 | // 287 | // MIT License: 288 | // Permission is hereby granted, free of charge, to any person obtaining 289 | // a copy of this software and associated documentation files (the 290 | // ""Software""), to deal in the Software without restriction, including 291 | // without limitation the rights to use, copy, modify, merge, publish, 292 | // distribute, sublicense, and/or sell copies of the Software, and to 293 | // permit persons to whom the Software is furnished to do so, subject to 294 | // the following conditions: 295 | 296 | // The above copyright notice and this permission notice shall be 297 | // included in all copies or substantial portions of the Software. 298 | 299 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 300 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 301 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 302 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 303 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 304 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 305 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 306 | // 307 | //********************************************************* -------------------------------------------------------------------------------- /src/Contacts/ContactsStories.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace O365_Win_Snippets 10 | { 11 | public class ContactsStories 12 | { 13 | private static readonly string STORY_DATA_IDENTIFIER = Guid.NewGuid().ToString(); 14 | 15 | public static async Task TryGetOutlookClientAsync() 16 | { 17 | var outlookClient = await ContactsSnippets.GetOutlookClientAsync(); 18 | return outlookClient != null; 19 | } 20 | 21 | public static async Task TryGetContactsAsync() 22 | { 23 | var contacts = await ContactsSnippets.GetContactsPageAsync(); 24 | 25 | return contacts != null; 26 | } 27 | 28 | public static async Task TryGetContactAsync() 29 | { 30 | var newContact = await ContactsSnippets.AddContactItemAsync( 31 | Guid.NewGuid().ToString(), 32 | Guid.NewGuid().ToString(), 33 | STORY_DATA_IDENTIFIER, 34 | Guid.NewGuid().ToString(), 35 | "a@b.com", 36 | Guid.NewGuid().ToString(), 37 | Guid.NewGuid().ToString()); 38 | 39 | var contact = await ContactsSnippets.GetContactAsync(newContact.Id); 40 | 41 | //Cleanup 42 | 43 | await ContactsSnippets.DeleteContactAsync(newContact.Id); 44 | 45 | return contact != null; 46 | } 47 | 48 | public static async Task TryAddNewContactAsync() 49 | { 50 | var newContact = await ContactsSnippets.AddContactItemAsync( 51 | Guid.NewGuid().ToString(), 52 | Guid.NewGuid().ToString(), 53 | STORY_DATA_IDENTIFIER, 54 | Guid.NewGuid().ToString(), 55 | "a@b.com", 56 | Guid.NewGuid().ToString(), 57 | Guid.NewGuid().ToString()); 58 | 59 | //Cleanup 60 | 61 | await ContactsSnippets.DeleteContactAsync(newContact.Id); 62 | 63 | return newContact != null; 64 | } 65 | 66 | public static async Task TryUpdateContactAsync() 67 | { 68 | var testContact = await ContactsSnippets.AddContactItemAsync( 69 | "FileAsValue", 70 | "FirstNameValue", 71 | STORY_DATA_IDENTIFIER, 72 | "JobTitleValue", 73 | "a@b.com", 74 | "WorkPhoneValue", 75 | "MobilePhoneValue"); 76 | 77 | // Verify a valid contact id was returned 78 | if (testContact == null) 79 | return false; 80 | 81 | 82 | // Update our contact 83 | await ContactsSnippets.UpdateContactItemAsync( 84 | testContact.Id, 85 | "NewFileAsValue", 86 | "FirstNameValue", 87 | STORY_DATA_IDENTIFIER, 88 | "NewJobTitleValue", 89 | "a@b.com", 90 | "WorkPhoneValue", 91 | "MobilePhoneValue", 92 | null); 93 | 94 | var contactCheck = await ContactsSnippets.GetContactAsync(testContact.Id); 95 | if (contactCheck == null) 96 | return false; 97 | 98 | //Cleanup 99 | 100 | await ContactsSnippets.DeleteContactAsync(testContact.Id); 101 | 102 | return (contactCheck.FileAs == "NewFileAsValue" && contactCheck.JobTitle == "NewJobTitleValue"); 103 | 104 | 105 | } 106 | 107 | public static async Task TryDeleteContactAsync() 108 | { 109 | var newContact = await ContactsSnippets.AddContactItemAsync( 110 | Guid.NewGuid().ToString(), 111 | Guid.NewGuid().ToString(), 112 | STORY_DATA_IDENTIFIER, 113 | Guid.NewGuid().ToString(), 114 | "a@b.com", 115 | Guid.NewGuid().ToString(), 116 | Guid.NewGuid().ToString()); 117 | 118 | // Verify a valid contact id was returned 119 | if (newContact == null) 120 | return false; 121 | 122 | // Verify that count has increased by 1 123 | var contactCheck = await ContactsSnippets.GetContactAsync(newContact.Id); 124 | if (contactCheck == null) 125 | return false; 126 | 127 | // Delete contact we added 128 | var contactDeleted = await ContactsSnippets.DeleteContactAsync(newContact.Id); 129 | if (!contactDeleted) 130 | return false; 131 | 132 | 133 | contactCheck = await ContactsSnippets.GetContactAsync(newContact.Id); 134 | return (contactCheck == null); 135 | 136 | } 137 | 138 | } 139 | } 140 | 141 | //********************************************************* 142 | // 143 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 144 | // 145 | //Copyright (c) Microsoft Corporation 146 | //All rights reserved. 147 | // 148 | // MIT License: 149 | // Permission is hereby granted, free of charge, to any person obtaining 150 | // a copy of this software and associated documentation files (the 151 | // ""Software""), to deal in the Software without restriction, including 152 | // without limitation the rights to use, copy, modify, merge, publish, 153 | // distribute, sublicense, and/or sell copies of the Software, and to 154 | // permit persons to whom the Software is furnished to do so, subject to 155 | // the following conditions: 156 | 157 | // The above copyright notice and this permission notice shall be 158 | // included in all copies or substantial portions of the Software. 159 | 160 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 161 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 162 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 163 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 164 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 165 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 166 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 167 | // 168 | //********************************************************* -------------------------------------------------------------------------------- /src/DurationToDurationStringConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Windows.UI.Xaml.Data; 9 | 10 | namespace O365_Win_Snippets 11 | { 12 | public class DurationToDurationStringConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, string language) 15 | { 16 | return String.Format("{0} ms", value); 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, string language) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | 26 | //********************************************************* 27 | // 28 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 29 | // 30 | //Copyright (c) Microsoft Corporation 31 | //All rights reserved. 32 | // 33 | // MIT License: 34 | // Permission is hereby granted, free of charge, to any person obtaining 35 | // a copy of this software and associated documentation files (the 36 | // ""Software""), to deal in the Software without restriction, including 37 | // without limitation the rights to use, copy, modify, merge, publish, 38 | // distribute, sublicense, and/or sell copies of the Software, and to 39 | // permit persons to whom the Software is furnished to do so, subject to 40 | // the following conditions: 41 | 42 | // The above copyright notice and this permission notice shall be 43 | // included in all copies or substantial portions of the Software. 44 | 45 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 46 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 47 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 48 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 49 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 50 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 51 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | // 53 | //********************************************************* -------------------------------------------------------------------------------- /src/Email/EmailSnippets.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 10 | using Microsoft.OData.Core; 11 | using Microsoft.Office365.Discovery; 12 | using Microsoft.Office365.OutlookServices; 13 | using Microsoft.OData.ProxyExtensions; 14 | 15 | 16 | namespace O365_Win_Snippets 17 | { 18 | class EmailSnippets 19 | { 20 | private static OutlookServicesClient _outlookClient = null; 21 | 22 | /// 23 | /// Checks that an OutlookServicesClient object is available. 24 | /// 25 | /// The OutlookServicesClient object. 26 | public static async Task GetOutlookClientAsync() 27 | { 28 | if (_outlookClient != null && !String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 29 | { 30 | Debug.WriteLine("Got an Outlook client for Mail."); 31 | return _outlookClient; 32 | } 33 | else 34 | { 35 | try 36 | { 37 | //First, look for the authority used during the last authentication. 38 | //If that value is not populated, use CommonAuthority. 39 | string authority = null; 40 | 41 | if (String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 42 | { 43 | authority = AuthenticationHelper.CommonAuthority; 44 | } 45 | else 46 | { 47 | authority = AuthenticationHelper.LastAuthority; 48 | } 49 | 50 | // Create an AuthenticationContext using this authority. 51 | AuthenticationHelper._authenticationContext = new AuthenticationContext(authority); 52 | 53 | // Set the value of _authenticationContext.UseCorporateNetwork to true so that you 54 | // can use this app inside a corporate intranet. If the value of UseCorporateNetwork 55 | // is true, you also need to add the Enterprise Authentication, Private Networks, and 56 | // Shared User Certificates capabilities in the Package.appxmanifest file. 57 | AuthenticationHelper._authenticationContext.UseCorporateNetwork = true; 58 | 59 | //See the Discovery Service Sample (https://github.com/OfficeDev/Office365-Discovery-Service-Sample) 60 | //for an approach that improves performance by storing the discovery service information in a cache. 61 | DiscoveryClient discoveryClient = new DiscoveryClient( 62 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, AuthenticationHelper.DiscoveryResourceId)); 63 | 64 | // Get the specified capability ("Calendar"). 65 | CapabilityDiscoveryResult result = 66 | await discoveryClient.DiscoverCapabilityAsync("Mail"); 67 | 68 | var token = await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId); 69 | // Check the token 70 | if (String.IsNullOrEmpty(token)) 71 | { 72 | // User cancelled sign-in 73 | return null; 74 | } 75 | else 76 | { 77 | 78 | _outlookClient = new OutlookServicesClient( 79 | result.ServiceEndpointUri, 80 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId)); 81 | Debug.WriteLine("Got an Outlook client for Mail."); 82 | return _outlookClient; 83 | } 84 | } 85 | // The following is a list of exceptions you should consider handling in your app. 86 | // In the case of this sample, the exceptions are handled by returning null upstream. 87 | catch (DiscoveryFailedException dfe) 88 | { 89 | Debug.WriteLine(dfe.Message); 90 | } 91 | catch (ArgumentException ae) 92 | { 93 | Debug.WriteLine(ae.Message); 94 | } 95 | 96 | AuthenticationHelper._authenticationContext.TokenCache.Clear(); 97 | 98 | return null; 99 | } 100 | } 101 | 102 | public static async Task> GetInboxMessagesAsync() 103 | { 104 | 105 | // Make sure we have a reference to the Exchange client 106 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 107 | 108 | //Get messages from a specific folder. Using Inbox this time because it is most likely to be populated. 109 | var results = await outlookClient.Me.Folders.GetById("Inbox").Messages.ExecuteAsync(); 110 | 111 | if (results.CurrentPage.Count > 0) 112 | { 113 | Debug.WriteLine("First message from Inbox folder:" + results.CurrentPage[0].Id); 114 | } 115 | 116 | return results.CurrentPage.ToList(); 117 | 118 | } 119 | 120 | 121 | public static async Task> GetMessagesAsync() 122 | { 123 | 124 | // Make sure we have a reference to the Exchange client 125 | var outlookClient = await GetOutlookClientAsync(); 126 | 127 | //Get messages (from Inbox by default) 128 | var results = await outlookClient.Me.Messages.ExecuteAsync(); 129 | 130 | if (results.CurrentPage.Count > 0) 131 | { 132 | Debug.WriteLine("First message from Inbox folder:" + results.CurrentPage[0].Id); 133 | } 134 | 135 | return results.CurrentPage.ToList(); 136 | 137 | } 138 | 139 | public static async Task GetMessagesAsync(string subject, DateTimeOffset after) 140 | { 141 | 142 | // Make sure we have a reference to the Exchange client 143 | var outlookClient = await GetOutlookClientAsync(); 144 | 145 | //Get messages (from Inbox by default). 146 | // Note: This query is not guaranteed to return 0 or 1 message, so 147 | // I need to use ExecuteAsync. Otherwise, ExecuteSingleAsync will throw 148 | // InvalidOperationException. 149 | var result = await outlookClient.Me.Messages 150 | .Where(m => m.Subject == subject && m.DateTimeReceived > after) 151 | .ExecuteAsync(); 152 | 153 | 154 | return (result.CurrentPage.Count > 0) ? result.CurrentPage[0] : null; 155 | 156 | } 157 | 158 | public static async Task SendMessageAsync( 159 | string Subject, 160 | string Body, 161 | string RecipientAddress 162 | ) 163 | { 164 | 165 | // Make sure we have a reference to the Outlook Services client 166 | var outlookClient = await GetOutlookClientAsync(); 167 | 168 | //Create Body 169 | ItemBody body = new ItemBody 170 | { 171 | Content = Body, 172 | ContentType = BodyType.HTML 173 | }; 174 | List toRecipients = new List(); 175 | toRecipients.Add(new Recipient 176 | { 177 | EmailAddress = new EmailAddress 178 | { 179 | Address = RecipientAddress 180 | } 181 | }); 182 | 183 | Message newMessage = new Message 184 | { 185 | Subject = Subject, 186 | Body = body, 187 | ToRecipients = toRecipients 188 | }; 189 | 190 | // To send a message without saving to Sent Items, specify false for 191 | // the SavetoSentItems parameter. 192 | await outlookClient.Me.SendMailAsync(newMessage, true); 193 | 194 | Debug.WriteLine("Sent mail: " + newMessage.Id); 195 | 196 | return true; 197 | 198 | } 199 | 200 | public static async Task CreateDraftAsync( 201 | string Subject, 202 | string Body, 203 | string RecipientAddress) 204 | { 205 | 206 | // Make sure we have a reference to the Outlook Services client 207 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 208 | 209 | ItemBody body = new ItemBody 210 | { 211 | Content = Body, 212 | ContentType = BodyType.HTML 213 | }; 214 | List toRecipients = new List(); 215 | toRecipients.Add(new Recipient 216 | { 217 | EmailAddress = new EmailAddress 218 | { 219 | Address = RecipientAddress 220 | } 221 | }); 222 | Message draftMessage = new Message 223 | { 224 | Subject = Subject, 225 | Body = body, 226 | ToRecipients = toRecipients, 227 | Importance = Importance.High 228 | }; 229 | 230 | // Save the draft message. Saving to Me.Messages saves the message in the Drafts folder. 231 | await outlookClient.Me.Messages.AddMessageAsync(draftMessage); 232 | 233 | Debug.WriteLine("Created draft: " + draftMessage.Id); 234 | 235 | return draftMessage.Id; 236 | 237 | } 238 | 239 | public static async Task CreateDraftAndSendAsync( 240 | string Subject, 241 | string Body, 242 | string RecipientAddress) 243 | { 244 | 245 | // Make sure we have a reference to the Outlook Services client 246 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 247 | 248 | ItemBody body = new ItemBody 249 | { 250 | Content = Body, 251 | ContentType = BodyType.HTML 252 | }; 253 | List toRecipients = new List(); 254 | toRecipients.Add(new Recipient 255 | { 256 | EmailAddress = new EmailAddress 257 | { 258 | Address = RecipientAddress 259 | } 260 | }); 261 | Message draftMessage = new Message 262 | { 263 | Subject = Subject, 264 | Body = body, 265 | ToRecipients = toRecipients, 266 | Importance = Importance.High 267 | }; 268 | 269 | // Save the draft message. This ensures that we'll get a message Id to return. 270 | await outlookClient.Me.Messages.AddMessageAsync(draftMessage); 271 | 272 | //Send the message. 273 | 274 | await outlookClient.Me.Messages[draftMessage.Id].SendAsync(); 275 | 276 | Debug.WriteLine("Created and sent draft: " + draftMessage.Id); 277 | 278 | return draftMessage.Id; 279 | 280 | } 281 | 282 | public static async Task UpdateMessageAsync(string MessageId, string UpdatedContent) 283 | { 284 | try 285 | { 286 | // Make sure we have a reference to the Outlook Services client 287 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 288 | 289 | // Get the message to update and set changed properties 290 | IMessage message = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 291 | message.Body = new ItemBody 292 | { 293 | Content = UpdatedContent, 294 | ContentType = BodyType.HTML 295 | }; 296 | 297 | await message.UpdateAsync(); 298 | 299 | Debug.WriteLine("Updated message: " + message.Id); 300 | 301 | return true; 302 | } 303 | catch (ODataErrorException ex) 304 | { 305 | // GetById will throw an ODataErrorException when the 306 | // item with the specified Id can't be found in the contact store on the server. 307 | Debug.WriteLine(ex.Message); 308 | return false; 309 | } 310 | } 311 | 312 | public static async Task ReplyMessageAsync(string MessageId, string ReplyContent) 313 | { 314 | try 315 | { 316 | // Make sure we have a reference to the Outlook Services client 317 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 318 | 319 | IMessage message = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 320 | await message.ReplyAsync(ReplyContent); 321 | 322 | Debug.WriteLine("Replied to message: " + message.Id); 323 | 324 | return true; 325 | } 326 | catch (ODataErrorException ex) 327 | { 328 | // GetById will throw an ODataErrorException when the 329 | // item with the specified Id can't be found in the contact store on the server. 330 | Debug.WriteLine(ex.Message); 331 | return false; 332 | } 333 | } 334 | 335 | public static async Task ReplyAllAsync(string MessageId, string ReplyContent) 336 | { 337 | try 338 | { 339 | // Make sure we have a reference to the Outlook Services client 340 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 341 | 342 | IMessage message = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 343 | await message.ReplyAllAsync(ReplyContent); 344 | 345 | Debug.WriteLine("Replied all to message: " + message.Id); 346 | 347 | 348 | return true; 349 | } 350 | catch (ODataErrorException ex) 351 | { 352 | // GetById will throw an ODataErrorException when the 353 | // item with the specified Id can't be found in the contact store on the server. 354 | Debug.WriteLine(ex.Message); 355 | return false; 356 | } 357 | } 358 | 359 | public static async Task ForwardMessageAsync( 360 | string MessageId, 361 | string ForwardMessage, 362 | string RecipientAddress) 363 | { 364 | 365 | try 366 | { 367 | // Make sure we have a reference to the Outlook Services client 368 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 369 | 370 | List toRecipients = new List(); 371 | toRecipients.Add(new Recipient 372 | { 373 | EmailAddress = new EmailAddress 374 | { 375 | Address = RecipientAddress 376 | } 377 | }); 378 | 379 | await outlookClient.Me.Messages.GetById(MessageId).ForwardAsync(ForwardMessage, toRecipients); 380 | 381 | Debug.WriteLine("Forwarded message: " + MessageId); 382 | 383 | return true; 384 | } 385 | catch (ODataErrorException ex) 386 | { 387 | // GetById will throw an ODataErrorException when the 388 | // item with the specified Id can't be found in the contact store on the server. 389 | Debug.WriteLine(ex.Message); 390 | return false; 391 | } 392 | } 393 | 394 | 395 | public static async Task MoveMessageAsync(string MessageId, string OriginalFolder, string ToFolder) 396 | { 397 | try 398 | { 399 | // Make sure we have a reference to the Outlook Services client 400 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 401 | 402 | IMessage messageToMove = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 403 | IMessage movedMessage = await messageToMove.MoveAsync(ToFolder); 404 | 405 | Debug.WriteLine("Moved message: " + MessageId + " from " + OriginalFolder + " to " + ToFolder); 406 | 407 | return true; 408 | } 409 | catch (ODataErrorException ex) 410 | { 411 | // GetById will throw an ODataErrorException when the 412 | // item with the specified Id can't be found in the contact store on the server. 413 | Debug.WriteLine(ex.Message); 414 | return false; 415 | } 416 | } 417 | 418 | public static async Task CopyMessageAsync(string MessageId, string OriginalFolder, string ToFolder) 419 | { 420 | try 421 | { 422 | // Make sure we have a reference to the Outlook Services client 423 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 424 | 425 | IMessage messageToMove = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 426 | IMessage movedMessage = await messageToMove.CopyAsync(ToFolder); 427 | 428 | Debug.WriteLine("Copied message: " + MessageId + " from " + OriginalFolder + " to " + ToFolder); 429 | 430 | return true; 431 | } 432 | catch (ODataErrorException ex) 433 | { 434 | // GetById will throw an ODataErrorException when the 435 | // item with the specified Id can't be found in the contact store on the server. 436 | Debug.WriteLine(ex.Message); 437 | return false; 438 | } 439 | } 440 | 441 | public static async Task AddFileAttachmentAsync(string MessageId, MemoryStream fileContent) 442 | { 443 | // Make sure we have a reference to the Outlook Services client 444 | 445 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 446 | 447 | var attachment = new FileAttachment(); 448 | 449 | attachment.ContentBytes = fileContent.ToArray(); 450 | attachment.Name = "fileAttachment"; 451 | attachment.Size = fileContent.ToArray().Length; 452 | 453 | try 454 | { 455 | var storedMessage = outlookClient.Me.Messages.GetById(MessageId); 456 | await storedMessage.Attachments.AddAttachmentAsync(attachment); 457 | await storedMessage.SendAsync(); 458 | Debug.WriteLine("Added attachment to message: " + MessageId); 459 | 460 | return true; 461 | } 462 | catch (ODataErrorException ex) 463 | { 464 | // GetById will throw an ODataErrorException when the 465 | // item with the specified Id can't be found in the contact store on the server. 466 | Debug.WriteLine(ex.Message); 467 | return false; 468 | } 469 | 470 | } 471 | 472 | public static async Task GetFileAttachmentsAsync(string MessageId) 473 | { 474 | 475 | try 476 | { 477 | // Make sure we have a reference to the Outlook Services client 478 | 479 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 480 | 481 | var message = outlookClient.Me.Messages.GetById(MessageId); 482 | var attachmentsResult = await message.Attachments.ExecuteAsync(); 483 | var attachments = attachmentsResult.CurrentPage.ToList(); 484 | 485 | foreach (IFileAttachment attachment in attachments) 486 | { 487 | Debug.WriteLine("Attachment: " + attachment.Name); 488 | } 489 | 490 | return true; 491 | } 492 | catch (ODataErrorException ex) 493 | { 494 | // GetById will throw an ODataErrorException when the 495 | // item with the specified Id can't be found in the contact store on the server. 496 | Debug.WriteLine(ex.Message); 497 | return false; 498 | } 499 | 500 | } 501 | 502 | public static async Task GetMessageWebLinkAsync(string MessageId) 503 | { 504 | try 505 | { 506 | // Make sure we have a reference to the Outlook Services client 507 | 508 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 509 | var message = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 510 | Debug.WriteLine("Web link for message " + message.Id + ": " + message.WebLink); 511 | 512 | return message.WebLink; 513 | } 514 | catch (ODataErrorException ex) 515 | { 516 | // GetById will throw an ODataErrorException when the 517 | // item with the specified Id can't be found in the contact store on the server. 518 | Debug.WriteLine(ex.Message); 519 | return null; 520 | } 521 | } 522 | 523 | public static async Task DeleteMessageAsync(string MessageId) 524 | { 525 | try 526 | { 527 | // Make sure we have a reference to the Outlook Services client 528 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 529 | 530 | // Get the message to delete. 531 | IMessage message = await outlookClient.Me.Messages.GetById(MessageId).ExecuteAsync(); 532 | await message.DeleteAsync(); 533 | 534 | Debug.WriteLine("Deleted message: " + MessageId) 535 | ; 536 | return true; 537 | } 538 | catch (ODataErrorException ex) 539 | { 540 | // GetById will throw an ODataErrorException when the 541 | // item with the specified Id can't be found in the contact store on the server. 542 | Debug.WriteLine(ex.Message); 543 | return false; 544 | } 545 | } 546 | 547 | //Mail Folders operations 548 | 549 | public static async Task> GetMailFoldersAsync() 550 | { 551 | 552 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 553 | 554 | IPagedCollection foldersResults = await outlookClient.Me.Folders.ExecuteAsync(); 555 | 556 | string folderId = foldersResults.CurrentPage[0].Id; 557 | 558 | if (string.IsNullOrEmpty(folderId)) return null; 559 | 560 | Debug.WriteLine("First mail folder in the collection: " + folderId); 561 | 562 | return foldersResults; 563 | 564 | } 565 | 566 | public static async Task CreateMailFolderAsync(string ParentFolder, string NewFolderName) 567 | { 568 | try 569 | { 570 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 571 | 572 | Folder newFolder = new Folder 573 | { 574 | DisplayName = NewFolderName 575 | }; 576 | await outlookClient.Me.Folders.GetById(ParentFolder).ChildFolders.AddFolderAsync(newFolder); 577 | 578 | Debug.WriteLine("Created folder: " + newFolder.Id); 579 | 580 | // Get the ID of the new folder. 581 | return newFolder.Id; 582 | } 583 | catch (ODataErrorException ex) 584 | { 585 | // GetById will throw an ODataErrorException when the 586 | // item with the specified Id can't be found in the contact store on the server. 587 | Debug.WriteLine(ex.Message); 588 | return null; 589 | } 590 | } 591 | 592 | public static async Task UpdateMailFolderAsync(string FolderId, string NewFolderName) 593 | { 594 | try 595 | { 596 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 597 | 598 | IFolder folder = await outlookClient.Me.Folders.GetById(FolderId).ExecuteAsync(); 599 | folder.DisplayName = NewFolderName; 600 | await folder.UpdateAsync(); 601 | string updatedName = folder.DisplayName; 602 | 603 | Debug.WriteLine("Updated folder name: " + FolderId + " " + NewFolderName); 604 | 605 | return true; 606 | } 607 | catch (ODataErrorException ex) 608 | { 609 | // GetById will throw an ODataErrorException when the 610 | // item with the specified Id can't be found in the contact store on the server. 611 | Debug.WriteLine(ex.Message); 612 | return false; 613 | } 614 | } 615 | 616 | public static async Task MoveMailFolderAsync(string folderId, string ToFolderName) 617 | { 618 | try 619 | { 620 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 621 | 622 | IFolder folderToMove = await outlookClient.Me.Folders.GetById(folderId).ExecuteAsync(); 623 | await folderToMove.MoveAsync(ToFolderName); 624 | Debug.WriteLine("Moved folder: " + folderId + " to " + ToFolderName); 625 | 626 | return true; 627 | } 628 | catch (ODataErrorException ex) 629 | { 630 | // GetById will throw an ODataErrorException when the 631 | // item with the specified Id can't be found in the contact store on the server. 632 | Debug.WriteLine(ex.Message); 633 | return false; 634 | } 635 | } 636 | 637 | public static async Task CopyMailFolderAsync(string folderId, string ToFolderName) 638 | { 639 | try 640 | { 641 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 642 | 643 | IFolder folderToCopy = await outlookClient.Me.Folders.GetById(folderId).ExecuteAsync(); 644 | IFolder copiedFolder = await folderToCopy.CopyAsync(ToFolderName); 645 | 646 | Debug.WriteLine("Copied folder: " + folderId + " to " + ToFolderName); 647 | 648 | return copiedFolder.Id; 649 | } 650 | catch (ODataErrorException ex) 651 | { 652 | // GetById will throw an ODataErrorException when the 653 | // item with the specified Id can't be found in the contact store on the server. 654 | Debug.WriteLine(ex.Message); 655 | return null; 656 | } 657 | } 658 | 659 | 660 | public static async Task DeleteMailFolderAsync(string folderId) 661 | { 662 | try 663 | { 664 | OutlookServicesClient outlookClient = await GetOutlookClientAsync(); 665 | 666 | IFolder folder = await outlookClient.Me.Folders.GetById(folderId).ExecuteAsync(); 667 | await folder.DeleteAsync(); 668 | 669 | Debug.WriteLine("Deleted folder: " + folderId); 670 | 671 | return true; 672 | } 673 | catch (ODataErrorException ex) 674 | { 675 | // GetById will throw an ODataErrorException when the 676 | // item with the specified Id can't be found in the contact store on the server. 677 | Debug.WriteLine(ex.Message); 678 | return false; 679 | } 680 | } 681 | 682 | } 683 | } 684 | 685 | //********************************************************* 686 | // 687 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 688 | // 689 | //Copyright (c) Microsoft Corporation 690 | //All rights reserved. 691 | // 692 | // MIT License: 693 | // Permission is hereby granted, free of charge, to any person obtaining 694 | // a copy of this software and associated documentation files (the 695 | // ""Software""), to deal in the Software without restriction, including 696 | // without limitation the rights to use, copy, modify, merge, publish, 697 | // distribute, sublicense, and/or sell copies of the Software, and to 698 | // permit persons to whom the Software is furnished to do so, subject to 699 | // the following conditions: 700 | 701 | // The above copyright notice and this permission notice shall be 702 | // included in all copies or substantial portions of the Software. 703 | 704 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 705 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 706 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 707 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 708 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 709 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 710 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 711 | // 712 | //********************************************************* -------------------------------------------------------------------------------- /src/Email/EmailStories.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Linq; 10 | using Microsoft.Office365.OutlookServices; 11 | using Microsoft.OData.ProxyExtensions; 12 | using Microsoft.OData.Client; 13 | using Microsoft.OData.Core; 14 | 15 | 16 | namespace O365_Win_Snippets 17 | { 18 | class EmailStories 19 | { 20 | private static readonly string STORY_DATA_IDENTIFIER = Guid.NewGuid().ToString(); 21 | private static readonly string DEFAULT_MESSAGE_BODY = "This message was sent from the Office 365 Windows Snippets project"; 22 | 23 | public static async Task TryGetOutlookClientAsync() 24 | { 25 | var exchangeClient = await EmailSnippets.GetOutlookClientAsync(); 26 | return exchangeClient != null; 27 | } 28 | 29 | public static async Task TryGetInboxMessagesAsync() 30 | { 31 | var messages = await EmailSnippets.GetInboxMessagesAsync(); 32 | 33 | return messages != null; 34 | } 35 | 36 | public static async Task TryGetMessagesAsync() 37 | { 38 | var messages = await EmailSnippets.GetMessagesAsync(); 39 | return messages != null; 40 | } 41 | 42 | public static async Task TrySendMessageAsync() 43 | { 44 | 45 | bool isSent = await EmailSnippets.SendMessageAsync( 46 | STORY_DATA_IDENTIFIER, 47 | DEFAULT_MESSAGE_BODY, 48 | AuthenticationHelper.LoggedInUserEmail 49 | ); 50 | 51 | return isSent; 52 | 53 | } 54 | 55 | public static async Task TryCreateDraftAsync() 56 | { 57 | 58 | // Create the draft message. 59 | var newMessageId = await EmailSnippets.CreateDraftAsync( 60 | STORY_DATA_IDENTIFIER, 61 | DEFAULT_MESSAGE_BODY, 62 | AuthenticationHelper.LoggedInUserEmail 63 | ); 64 | 65 | if (newMessageId == null) 66 | return false; 67 | 68 | //Cleanup 69 | await EmailSnippets.DeleteMessageAsync(newMessageId); 70 | 71 | return true; 72 | 73 | } 74 | 75 | public static async Task TryReplyMessageAsync() 76 | { 77 | 78 | // Create a draft message and then send it. If you send the message without first creating a draft, you can't easily retrieve 79 | // the message Id. 80 | 81 | var newMessageId = await EmailSnippets.CreateDraftAndSendAsync( 82 | STORY_DATA_IDENTIFIER, 83 | DEFAULT_MESSAGE_BODY, 84 | AuthenticationHelper.LoggedInUserEmail 85 | ); 86 | 87 | if (newMessageId == null) 88 | return false; 89 | 90 | // Find the sent message. 91 | var sentMessageId = await GetSentMessageIdAsync(); 92 | if (String.IsNullOrEmpty(sentMessageId)) 93 | return false; 94 | 95 | // Reply to the message. 96 | bool isReplied = await EmailSnippets.ReplyMessageAsync( 97 | sentMessageId, 98 | DEFAULT_MESSAGE_BODY); 99 | 100 | return isReplied; 101 | } 102 | 103 | public static async Task TryReplyAllAsync() 104 | { 105 | 106 | // Create a draft message and then send it. If you send the message without first creating a draft, you can't easily retrieve 107 | // the message Id. 108 | 109 | var newMessageId = await EmailSnippets.CreateDraftAndSendAsync( 110 | STORY_DATA_IDENTIFIER, 111 | DEFAULT_MESSAGE_BODY, 112 | AuthenticationHelper.LoggedInUserEmail 113 | ); 114 | 115 | if (newMessageId == null) 116 | return false; 117 | 118 | 119 | // Find the sent message. 120 | var sentMessageId = await GetSentMessageIdAsync(); 121 | if (String.IsNullOrEmpty(sentMessageId)) 122 | return false; 123 | 124 | // Reply to the message. 125 | bool isReplied = await EmailSnippets.ReplyAllAsync( 126 | sentMessageId, 127 | DEFAULT_MESSAGE_BODY); 128 | 129 | return isReplied; 130 | 131 | } 132 | 133 | public static async Task TryForwardMessageAsync() 134 | { 135 | 136 | // Create a draft message and then send it. If you send the message without first creating a draft, you can't easily retrieve 137 | // the message Id. 138 | 139 | var newMessageId = await EmailSnippets.CreateDraftAndSendAsync( 140 | STORY_DATA_IDENTIFIER, 141 | DEFAULT_MESSAGE_BODY, 142 | AuthenticationHelper.LoggedInUserEmail 143 | ); 144 | 145 | if (newMessageId == null) 146 | return false; 147 | 148 | // Find the sent message. 149 | var sentMessageId = await GetSentMessageIdAsync(); 150 | if (String.IsNullOrEmpty(sentMessageId)) 151 | return false; 152 | 153 | // Reply to the message. 154 | bool isReplied = await EmailSnippets.ForwardMessageAsync( 155 | sentMessageId, 156 | DEFAULT_MESSAGE_BODY, 157 | AuthenticationHelper.LoggedInUserEmail); 158 | 159 | return isReplied; 160 | 161 | } 162 | 163 | public static async Task TryUpdateMessageAsync() 164 | { 165 | 166 | // Create a draft message. If you send the message without first creating a draft, you can't easily retrieve the message Id. 167 | var newMessageId = await EmailSnippets.CreateDraftAsync( 168 | STORY_DATA_IDENTIFIER, 169 | DEFAULT_MESSAGE_BODY, 170 | AuthenticationHelper.LoggedInUserEmail 171 | ); 172 | 173 | if (newMessageId == null) 174 | return false; 175 | 176 | // Update the message. 177 | bool isUpdated = await EmailSnippets.UpdateMessageAsync( 178 | newMessageId, 179 | DEFAULT_MESSAGE_BODY); 180 | 181 | //Cleanup. Comment if you want to verify the update in your Drafts folder. 182 | await EmailSnippets.DeleteMessageAsync(newMessageId); 183 | 184 | return isUpdated; 185 | 186 | } 187 | 188 | public static async Task TryMoveMessageAsync() 189 | { 190 | 191 | // Create a draft message and then send it. If you send the message without first creating a draft, you can't easily retrieve 192 | // the message Id. 193 | 194 | var newMessageId = await EmailSnippets.CreateDraftAndSendAsync( 195 | STORY_DATA_IDENTIFIER, 196 | DEFAULT_MESSAGE_BODY, 197 | AuthenticationHelper.LoggedInUserEmail 198 | ); 199 | 200 | if (newMessageId == null) 201 | return false; 202 | 203 | // Find the sent message. 204 | var sentMessageId = await GetSentMessageIdAsync(); 205 | if (String.IsNullOrEmpty(sentMessageId)) 206 | return false; 207 | 208 | // Reply to the message. 209 | bool isReplied = await EmailSnippets.MoveMessageAsync( 210 | sentMessageId, 211 | "Inbox", 212 | "Drafts"); 213 | 214 | return isReplied; 215 | 216 | } 217 | 218 | public static async Task TryCopyMessageAsync() 219 | { 220 | 221 | // Create a draft message and then send it. If you send the message without first creating a draft, you can't easily retrieve 222 | // the message Id. 223 | 224 | var newMessageId = await EmailSnippets.CreateDraftAndSendAsync( 225 | STORY_DATA_IDENTIFIER, 226 | DEFAULT_MESSAGE_BODY, 227 | AuthenticationHelper.LoggedInUserEmail 228 | ); 229 | 230 | if (newMessageId == null) 231 | return false; 232 | // Find the sent message. 233 | var sentMessageId = await GetSentMessageIdAsync(); 234 | if (String.IsNullOrEmpty(sentMessageId)) 235 | return false; 236 | 237 | // Reply to the message. 238 | bool isReplied = await EmailSnippets.CopyMessageAsync( 239 | sentMessageId, 240 | "Inbox", 241 | "Drafts"); 242 | 243 | return isReplied; 244 | 245 | } 246 | 247 | public static async Task TryGetFileAttachmentsAsync() 248 | { 249 | 250 | var newMessageId = await EmailSnippets.CreateDraftAsync( 251 | STORY_DATA_IDENTIFIER, 252 | DEFAULT_MESSAGE_BODY, 253 | AuthenticationHelper.LoggedInUserEmail 254 | ); 255 | 256 | if (newMessageId == null) 257 | return false; 258 | 259 | await EmailSnippets.AddFileAttachmentAsync(newMessageId, new MemoryStream(Encoding.UTF8.GetBytes("TryAddMailAttachmentAsync"))); 260 | 261 | // Find the sent message. 262 | var sentMessageId = await GetSentMessageIdAsync(); 263 | if (String.IsNullOrEmpty(sentMessageId)) 264 | return false; 265 | 266 | await EmailSnippets.GetFileAttachmentsAsync(sentMessageId); 267 | 268 | return true; 269 | } 270 | 271 | public static async Task TryGetMessageWebLinkAsync() 272 | { 273 | // Create a draft message and send it. 274 | 275 | var newMessageId = await EmailSnippets.CreateDraftAndSendAsync( 276 | STORY_DATA_IDENTIFIER, 277 | DEFAULT_MESSAGE_BODY, 278 | AuthenticationHelper.LoggedInUserEmail 279 | ); 280 | 281 | if (newMessageId == null) 282 | return false; 283 | // Find the sent message. 284 | var sentMessageId = await GetSentMessageIdAsync(); 285 | if (String.IsNullOrEmpty(sentMessageId)) 286 | return false; 287 | 288 | var webLink = await EmailSnippets.GetMessageWebLinkAsync(sentMessageId); 289 | 290 | if (String.IsNullOrEmpty(webLink)) 291 | return false; 292 | 293 | return true; 294 | 295 | } 296 | 297 | public static async Task TryDeleteMessageAsync() 298 | { 299 | 300 | // Create a draft message. If you send the message without first creating a draft, you can't easily retrieve the message Id. 301 | var newMessageId = await EmailSnippets.CreateDraftAsync( 302 | STORY_DATA_IDENTIFIER, 303 | DEFAULT_MESSAGE_BODY, 304 | AuthenticationHelper.LoggedInUserEmail 305 | ); 306 | 307 | if (newMessageId == null) 308 | return false; 309 | 310 | // Delete the message. 311 | var isDeleted = await EmailSnippets.DeleteMessageAsync(newMessageId); 312 | 313 | return isDeleted; 314 | 315 | } 316 | 317 | public static async Task TryGetMailFoldersAsync() 318 | { 319 | 320 | // The example gets the Inbox and its siblings. 321 | var foldersResults = await EmailSnippets.GetMailFoldersAsync(); 322 | 323 | foreach (var folder in foldersResults.CurrentPage) 324 | { 325 | if ((folder.DisplayName == "Inbox") 326 | || (folder.DisplayName == "Drafts") 327 | || (folder.DisplayName == "DeletedItems") 328 | || (folder.DisplayName == "SentItems")) 329 | return true; 330 | } 331 | 332 | return false; 333 | 334 | } 335 | 336 | public static async Task TryCreateMailFolderAsync() 337 | { 338 | 339 | var folderId = await EmailSnippets.CreateMailFolderAsync("Inbox", "FolderToDelete"); 340 | 341 | 342 | if (!string.IsNullOrEmpty(folderId)) 343 | { 344 | //Cleanup 345 | await EmailSnippets.DeleteMailFolderAsync(folderId); 346 | 347 | return true; 348 | } 349 | 350 | return false; 351 | } 352 | 353 | public static async Task TryUpdateMailFolderAsync() 354 | { 355 | 356 | var folderId = await EmailSnippets.CreateMailFolderAsync("Inbox", "FolderToUpdateAndDelete"); 357 | 358 | 359 | if (!string.IsNullOrEmpty(folderId)) 360 | { 361 | 362 | bool isFolderUpdated = await EmailSnippets.UpdateMailFolderAsync(folderId, "FolderToDelete"); 363 | 364 | //Cleanup 365 | await EmailSnippets.DeleteMailFolderAsync(folderId); 366 | 367 | return isFolderUpdated; 368 | } 369 | 370 | return false; 371 | } 372 | 373 | public static async Task TryMoveMailFolderAsync() 374 | { 375 | 376 | var folderId = await EmailSnippets.CreateMailFolderAsync("Inbox", "FolderToDelete"); 377 | 378 | 379 | if (!string.IsNullOrEmpty(folderId)) 380 | { 381 | 382 | bool isFolderMoved = await EmailSnippets.MoveMailFolderAsync(folderId, "Drafts"); 383 | 384 | //Cleanup 385 | await EmailSnippets.DeleteMailFolderAsync(folderId); 386 | 387 | return isFolderMoved; 388 | } 389 | 390 | return false; 391 | 392 | } 393 | 394 | public static async Task TryCopyMailFolderAsync() 395 | { 396 | 397 | var folderId = await EmailSnippets.CreateMailFolderAsync("Inbox", "FolderToCopyAndDelete"); 398 | 399 | 400 | if (!string.IsNullOrEmpty(folderId)) 401 | { 402 | 403 | string copiedFolderId = await EmailSnippets.CopyMailFolderAsync(folderId, "Drafts"); 404 | 405 | if (!string.IsNullOrEmpty(copiedFolderId)) 406 | { 407 | 408 | //Cleanup 409 | await EmailSnippets.DeleteMailFolderAsync(folderId); 410 | await EmailSnippets.DeleteMailFolderAsync(copiedFolderId); 411 | 412 | return true; 413 | } 414 | } 415 | 416 | return false; 417 | } 418 | 419 | public static async Task TryAddFileAttachmentAsync() 420 | { 421 | 422 | var newMessageId = await EmailSnippets.CreateDraftAsync( 423 | STORY_DATA_IDENTIFIER, 424 | DEFAULT_MESSAGE_BODY, 425 | AuthenticationHelper.LoggedInUserEmail 426 | ); 427 | 428 | if (newMessageId == null) 429 | return false; 430 | 431 | // Pass a MemoryStream object for the sake of simplicity. 432 | 433 | using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes("TryAddMailAttachmentAsync"))) 434 | { 435 | await EmailSnippets.AddFileAttachmentAsync(newMessageId, ms); 436 | } 437 | 438 | return true; 439 | 440 | } 441 | 442 | public static async Task TryDeleteMailFolderAsync() 443 | { 444 | 445 | var folderId = await EmailSnippets.CreateMailFolderAsync("Inbox", "FolderToDelete"); 446 | 447 | var isFolderDeleted = await EmailSnippets.DeleteMailFolderAsync(folderId); 448 | return isFolderDeleted; 449 | } 450 | 451 | private static async Task GetSentMessageIdAsync() 452 | { 453 | // Search for a maximum of 10 times 454 | for (int i = 0; i < 10; i++) 455 | { 456 | var message = await EmailSnippets.GetMessagesAsync(STORY_DATA_IDENTIFIER 457 | , DateTimeOffset.Now.Subtract(new TimeSpan(0, 1, 0))); 458 | if (message != null) 459 | return message.Id; 460 | 461 | // Delay before trying again. Increase this value if you connection to the server 462 | // is slow and causes false results. 463 | await Task.Delay(200); 464 | 465 | } 466 | 467 | // Couldn't find the sent message 468 | return string.Empty; 469 | } 470 | 471 | } 472 | } 473 | 474 | //********************************************************* 475 | // 476 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 477 | // 478 | //Copyright (c) Microsoft Corporation 479 | //All rights reserved. 480 | // 481 | // MIT License: 482 | // Permission is hereby granted, free of charge, to any person obtaining 483 | // a copy of this software and associated documentation files (the 484 | // ""Software""), to deal in the Software without restriction, including 485 | // without limitation the rights to use, copy, modify, merge, publish, 486 | // distribute, sublicense, and/or sell copies of the Software, and to 487 | // permit persons to whom the Software is furnished to do so, subject to 488 | // the following conditions: 489 | 490 | // The above copyright notice and this permission notice shall be 491 | // included in all copies or substantial portions of the Software. 492 | 493 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 494 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 495 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 496 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 497 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 498 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 499 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 500 | // 501 | //********************************************************* -------------------------------------------------------------------------------- /src/Files/FilesSnippets.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 4 | using Microsoft.OData.Core; 5 | using Microsoft.Office365.Discovery; 6 | using Microsoft.Office365.SharePoint.CoreServices; 7 | using Microsoft.Office365.SharePoint.FileServices; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Diagnostics; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | 17 | namespace O365_Win_Snippets 18 | { 19 | class FilesSnippets 20 | { 21 | private static SharePointClient _sharePointClient = null; 22 | 23 | /// 24 | /// Checks that an OutlookServicesClient object is available. 25 | /// 26 | /// The OutlookServicesClient object. 27 | public static async Task GetSharePointClientAsync() 28 | { 29 | if (_sharePointClient != null && !String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 30 | { 31 | Debug.WriteLine("Got a SharePoint client for Files."); 32 | return _sharePointClient; 33 | } 34 | else 35 | { 36 | try 37 | { 38 | //First, look for the authority used during the last authentication. 39 | //If that value is not populated, use CommonAuthority. 40 | string authority = null; 41 | 42 | if (String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 43 | { 44 | authority = AuthenticationHelper.CommonAuthority; 45 | } 46 | else 47 | { 48 | authority = AuthenticationHelper.LastAuthority; 49 | } 50 | 51 | // Create an AuthenticationContext using this authority. 52 | AuthenticationHelper._authenticationContext = new AuthenticationContext(authority); 53 | 54 | // Set the value of _authenticationContext.UseCorporateNetwork to true so that you 55 | // can use this app inside a corporate intranet. If the value of UseCorporateNetwork 56 | // is true, you also need to add the Enterprise Authentication, Private Networks, and 57 | // Shared User Certificates capabilities in the Package.appxmanifest file. 58 | AuthenticationHelper._authenticationContext.UseCorporateNetwork = true; 59 | 60 | //See the Discovery Service Sample (https://github.com/OfficeDev/Office365-Discovery-Service-Sample) 61 | //for an approach that improves performance by storing the discovery service information in a cache. 62 | DiscoveryClient discoveryClient = new DiscoveryClient( 63 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, AuthenticationHelper.DiscoveryResourceId)); 64 | 65 | // Get the specified capability ("Calendar"). 66 | CapabilityDiscoveryResult result = 67 | await discoveryClient.DiscoverCapabilityAsync("MyFiles"); 68 | 69 | var token = await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId); 70 | // Check the token 71 | if (String.IsNullOrEmpty(token)) 72 | { 73 | // User cancelled sign-in 74 | return null; 75 | } 76 | else 77 | { 78 | 79 | _sharePointClient = new SharePointClient( 80 | result.ServiceEndpointUri, 81 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, result.ServiceResourceId)); 82 | Debug.WriteLine("Got a SharePoint client for Files."); 83 | return _sharePointClient; 84 | } 85 | } 86 | // The following is a list of exceptions you should consider handling in your app. 87 | // In the case of this sample, the exceptions are handled by returning null upstream. 88 | catch (DiscoveryFailedException dfe) 89 | { 90 | Debug.WriteLine(dfe.Message); 91 | } 92 | catch (ArgumentException ae) 93 | { 94 | Debug.WriteLine(ae.Message); 95 | } 96 | 97 | AuthenticationHelper._authenticationContext.TokenCache.Clear(); 98 | 99 | return null; 100 | } 101 | } 102 | 103 | //Files operations 104 | 105 | public static async Task CreateFileAsync(string fileName, Stream fileContent) 106 | { 107 | 108 | var sharePointClient = await GetSharePointClientAsync(); 109 | 110 | File newFile = new File 111 | { 112 | Name = fileName 113 | }; 114 | 115 | await sharePointClient.Files.AddItemAsync(newFile); 116 | await sharePointClient.Files.GetById(newFile.Id).ToFile().UploadAsync(fileContent); 117 | 118 | Debug.WriteLine("Created a file: " + newFile.Id); 119 | 120 | return newFile.Id; 121 | 122 | } 123 | 124 | public static async Task UpdateFileContentAsync(string Id, Stream fileContent) 125 | { 126 | try 127 | { 128 | var sharePointClient = await GetSharePointClientAsync(); 129 | 130 | //update the file with the content 131 | await sharePointClient.Files.GetById(Id).ToFile().UploadAsync(fileContent); 132 | 133 | Debug.WriteLine("Updated file content: " + Id); 134 | 135 | return true; 136 | 137 | } 138 | catch (ODataErrorException ex) 139 | { 140 | // GetById will throw an ODataErrorException when the 141 | // item with the specified Id can't be found in the contact store on the server. 142 | Debug.WriteLine(ex.Message); 143 | return false; 144 | } 145 | } 146 | 147 | public static async Task DownloadFileAsync(string Id) 148 | { 149 | 150 | try 151 | { 152 | var sharePointClient = await GetSharePointClientAsync(); 153 | 154 | var stream = await sharePointClient.Files.GetById(Id).ToFile().DownloadAsync(); 155 | 156 | Debug.WriteLine("Downloaded a file: " + Id); 157 | 158 | return stream; 159 | } 160 | catch (ODataErrorException ex) 161 | { 162 | // GetById will throw an ODataErrorException when the 163 | // item with the specified Id can't be found in the contact store on the server. 164 | Debug.WriteLine(ex.Message); 165 | return null; 166 | } 167 | } 168 | 169 | public static async Task DeleteFileAsync(string Id) 170 | { 171 | try 172 | { 173 | var sharePointClient = await GetSharePointClientAsync(); 174 | var file = await sharePointClient.Files.GetById(Id).ToFile().ExecuteAsync(); 175 | await file.DeleteAsync(); 176 | 177 | Debug.WriteLine("Deleted a file: " + Id); 178 | 179 | return true; 180 | } 181 | catch (ODataErrorException ex) 182 | { 183 | // GetById will throw an ODataErrorException when the 184 | // item with the specified Id can't be found in the contact store on the server. 185 | Debug.WriteLine(ex.Message); 186 | return false; 187 | } 188 | } 189 | 190 | public static async Task CopyFileAsync(string fileId, string destinationFolderId) 191 | { 192 | try 193 | { 194 | var sharePointClient = await GetSharePointClientAsync(); 195 | 196 | var copiedFile = await sharePointClient.Files.GetById(fileId).ToFile().CopyAsync(destinationFolderId, null, null); 197 | 198 | Debug.WriteLine("Copied file to folder."); 199 | 200 | return copiedFile.Id; 201 | } 202 | catch (ODataErrorException ex) 203 | { 204 | // GetById will throw an ODataErrorException when the 205 | // item with the specified Id can't be found in the contact store on the server. 206 | Debug.WriteLine(ex.Message); 207 | return null; 208 | } 209 | } 210 | 211 | public static async Task RenameFileAsync(string fileId, string newName) 212 | { 213 | 214 | try 215 | { 216 | var sharePointClient = await GetSharePointClientAsync(); 217 | 218 | var file = await sharePointClient.Files.GetById(fileId).ToFile().ExecuteAsync(); 219 | 220 | file.Name = newName; 221 | await file.UpdateAsync(); 222 | 223 | Debug.WriteLine("Renamed a file: " + fileId); 224 | 225 | return file.Name; 226 | } 227 | catch (ODataErrorException ex) 228 | { 229 | // GetById will throw an ODataErrorException when the 230 | // item with the specified Id can't be found in the contact store on the server. 231 | Debug.WriteLine(ex.Message); 232 | return null; 233 | } 234 | } 235 | 236 | //Folders operations 237 | public static async Task> GetFolderChildrenAsync(string folderId) 238 | { 239 | var sharePointClient = await GetSharePointClientAsync(); 240 | try 241 | { 242 | var items = await sharePointClient.Files.GetById(folderId).ToFolder().Children.ExecuteAsync(); 243 | 244 | Debug.WriteLine("First child of " + folderId + ": " + items.CurrentPage[0].Id); 245 | 246 | return items.CurrentPage.ToList(); 247 | } 248 | catch (ODataErrorException ex) 249 | { 250 | // GetById will throw an ODataErrorException when the 251 | // item with the specified Id can't be found in the contact store on the server. 252 | Debug.WriteLine(ex.Message); 253 | return null; 254 | } 255 | } 256 | 257 | public static async Task CreateFolderAsync(string folderName, string parentFolderId) 258 | { 259 | try 260 | { 261 | var sharePointClient = await GetSharePointClientAsync(); 262 | Folder newFolder = new Folder 263 | { 264 | Name = folderName 265 | }; 266 | 267 | await sharePointClient.Files.GetById(parentFolderId).ToFolder().Children.AddItemAsync(newFolder); 268 | var newItem = await sharePointClient.Files.GetById(newFolder.Id).ToFolder().ExecuteAsync(); 269 | 270 | Debug.WriteLine("Created a folder: " + newItem.Id); 271 | 272 | return (Folder)newItem; 273 | } 274 | catch (ODataErrorException ex) 275 | { 276 | // GetById will throw an ODataErrorException when the 277 | // item with the specified Id can't be found in the contact store on the server. 278 | Debug.WriteLine(ex.Message); 279 | return null; 280 | } 281 | } 282 | 283 | 284 | public static async Task DeleteFolderAsync(string folderId) 285 | { 286 | try 287 | { 288 | var sharePointClient = await GetSharePointClientAsync(); 289 | var item = await sharePointClient.Files.GetById(folderId).ToFolder().ExecuteAsync(); 290 | 291 | await item.DeleteAsync(); 292 | 293 | return true; 294 | 295 | } 296 | catch (ODataErrorException ex) 297 | { 298 | // GetById will throw an ODataErrorException when the 299 | // item with the specified Id can't be found in the contact store on the server. 300 | Debug.WriteLine(ex.Message); 301 | return false; 302 | } 303 | } 304 | 305 | } 306 | } 307 | 308 | //********************************************************* 309 | // 310 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 311 | // 312 | //Copyright (c) Microsoft Corporation 313 | //All rights reserved. 314 | // 315 | // MIT License: 316 | // Permission is hereby granted, free of charge, to any person obtaining 317 | // a copy of this software and associated documentation files (the 318 | // ""Software""), to deal in the Software without restriction, including 319 | // without limitation the rights to use, copy, modify, merge, publish, 320 | // distribute, sublicense, and/or sell copies of the Software, and to 321 | // permit persons to whom the Software is furnished to do so, subject to 322 | // the following conditions: 323 | 324 | // The above copyright notice and this permission notice shall be 325 | // included in all copies or substantial portions of the Software. 326 | 327 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 328 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 329 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 330 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 331 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 332 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 333 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 334 | // 335 | //********************************************************* -------------------------------------------------------------------------------- /src/Files/FilesStories.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using Microsoft.Office365.SharePoint.FileServices; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace O365_Win_Snippets 12 | { 13 | class FilesStories 14 | { 15 | private static readonly string STORY_DATA_IDENTIFIER = Guid.NewGuid().ToString(); 16 | 17 | public static async Task TryGetSharePointClientAsync() 18 | { 19 | var sharepointClient = await FilesSnippets.GetSharePointClientAsync(); 20 | return sharepointClient != null; 21 | } 22 | 23 | //Files stories 24 | 25 | public static async Task TryCreateFileAsync() 26 | { 27 | // Grab a list of folder items 28 | var items = await FilesSnippets.GetFolderChildrenAsync("root"); 29 | if (items == null) 30 | return false; 31 | 32 | var origCount = items.Count; 33 | 34 | var createdFileId = await FilesSnippets.CreateFileAsync(STORY_DATA_IDENTIFIER + "_" + Guid.NewGuid().ToString(), new MemoryStream(Encoding.UTF8.GetBytes("TryAddFileAsync"))); 35 | if (createdFileId == null) 36 | return false; 37 | 38 | 39 | // Grab the files again 40 | items = await FilesSnippets.GetFolderChildrenAsync("root"); 41 | if (items == null) 42 | return false; 43 | 44 | // Number of files should have increased by 1 45 | if (items.Count != origCount + 1) 46 | return false; 47 | 48 | //Cleanup 49 | await FilesSnippets.DeleteFileAsync(createdFileId); 50 | 51 | 52 | return true; 53 | 54 | } 55 | 56 | public static async Task TryUpdateFileContentAsync() 57 | { 58 | // Add a file & verify 59 | // Grab a list of files 60 | var items = await FilesSnippets.GetFolderChildrenAsync("root"); 61 | if (items == null) 62 | return false; 63 | 64 | var origCount = items.Count; 65 | 66 | // Create a file 67 | var createdFileId = await FilesSnippets.CreateFileAsync(STORY_DATA_IDENTIFIER + "_" + Guid.NewGuid().ToString(), new MemoryStream(Encoding.UTF8.GetBytes("TryUpdateFileAsync"))); 68 | if (createdFileId == null) 69 | return false; 70 | 71 | // Grab the files again 72 | items = await FilesSnippets.GetFolderChildrenAsync("root"); 73 | if (items == null) 74 | return false; 75 | 76 | // Number of files should have increased by 1 77 | if (items.Count != origCount + 1) 78 | return false; 79 | 80 | // Update the content 81 | 82 | string updatedContent = "Updated content"; 83 | var updated = await FilesSnippets.UpdateFileContentAsync(createdFileId, new MemoryStream(Encoding.UTF8.GetBytes(updatedContent))); 84 | 85 | // Download the file and compare with the updated content. 86 | 87 | using (var stream = await FilesSnippets.DownloadFileAsync(createdFileId)) 88 | { 89 | if (stream == null) 90 | return false; 91 | 92 | StreamReader reader = new StreamReader(stream); 93 | var downloadedString = await reader.ReadToEndAsync(); 94 | if (downloadedString != updatedContent) 95 | return false; 96 | } 97 | 98 | //Cleanup 99 | await FilesSnippets.DeleteFileAsync(createdFileId); 100 | 101 | return updated; 102 | 103 | 104 | } 105 | 106 | public static async Task TryDownloadFileAsync() 107 | { 108 | 109 | string fileContents = "TryDownloadFileAsync"; 110 | 111 | // Create a file 112 | var createdFile = await FilesSnippets.CreateFileAsync(STORY_DATA_IDENTIFIER + "_" + Guid.NewGuid().ToString(), new MemoryStream(Encoding.UTF8.GetBytes(fileContents))); 113 | if (createdFile == null) 114 | return false; 115 | 116 | // Download the file 117 | using (var stream = await FilesSnippets.DownloadFileAsync(createdFile)) 118 | { 119 | if (stream == null) 120 | return false; 121 | 122 | StreamReader reader = new StreamReader(stream); 123 | var downloadedString = await reader.ReadToEndAsync(); 124 | if (downloadedString != fileContents) 125 | return false; 126 | } 127 | 128 | return true; 129 | } 130 | 131 | public static async Task TryDeleteFileAsync() 132 | { 133 | // Grab a list of files 134 | var items = await FilesSnippets.GetFolderChildrenAsync("root"); 135 | if (items == null) 136 | return false; 137 | 138 | var origCount = items.Count; 139 | 140 | // Create a file 141 | var createdFile = await FilesSnippets.CreateFileAsync(STORY_DATA_IDENTIFIER + "_" + Guid.NewGuid().ToString(), new MemoryStream(Encoding.UTF8.GetBytes("CanAddFileAsync"))); 142 | if (createdFile == null) 143 | return false; 144 | 145 | // Grab the files again 146 | items = await FilesSnippets.GetFolderChildrenAsync("root"); 147 | if (items == null) 148 | return false; 149 | 150 | // Number of files should have increased by 1 151 | if (items.Count != origCount + 1) 152 | return false; 153 | 154 | // Delete our test file 155 | await FilesSnippets.DeleteFileAsync(createdFile); 156 | 157 | //Grab the files again 158 | items = await FilesSnippets.GetFolderChildrenAsync("root"); 159 | if (items == null) 160 | return false; 161 | 162 | // Number of files should be back at the original count 163 | if (items.Count != origCount) 164 | return false; 165 | 166 | return true; 167 | } 168 | 169 | public static async Task TryCopyFileAsync() 170 | { 171 | 172 | // Grab the root folder. 173 | var items = await FilesSnippets.GetFolderChildrenAsync("root"); 174 | if (items == null) 175 | return false; 176 | 177 | // Create a new file. 178 | var createdFileId = await FilesSnippets.CreateFileAsync(STORY_DATA_IDENTIFIER + "_" + Guid.NewGuid().ToString(), new MemoryStream(Encoding.UTF8.GetBytes("TryAddFileAsync"))); 179 | if (createdFileId == null) 180 | return false; 181 | 182 | // Create a new folder in the root folder. 183 | var folder = await FilesSnippets.CreateFolderAsync(STORY_DATA_IDENTIFIER, "root"); 184 | 185 | // Copy the new file into the new folder. 186 | var copiedFileId = await FilesSnippets.CopyFileAsync(createdFileId, folder.Id); 187 | 188 | // Clean up. 189 | // Comment out if you want to see the file, the folder, and the copied file. 190 | await FilesSnippets.DeleteFileAsync(createdFileId); 191 | 192 | // Deleting the folder also deletes the file copied into it. 193 | await FilesSnippets.DeleteFolderAsync(folder.Id); 194 | 195 | 196 | return true; 197 | 198 | } 199 | 200 | public static async Task TryRenameFileAsync() 201 | { 202 | 203 | string newFileName = "updated name"; 204 | 205 | // Create a file 206 | var createdFileId = await FilesSnippets.CreateFileAsync(STORY_DATA_IDENTIFIER + "_" + Guid.NewGuid().ToString(), new MemoryStream(Encoding.UTF8.GetBytes("TryUpdateFileAsync"))); 207 | if (createdFileId == null) 208 | return false; 209 | 210 | var fileName = await FilesSnippets.RenameFileAsync(createdFileId, "updated name"); 211 | 212 | if (fileName != newFileName) 213 | return false; 214 | 215 | //Cleanup 216 | 217 | await FilesSnippets.DeleteFileAsync(createdFileId); 218 | 219 | return true; 220 | 221 | 222 | } 223 | 224 | //Folders stories 225 | 226 | public static async Task TryGetFolderChildrenAsync() 227 | { 228 | var items = await FilesSnippets.GetFolderChildrenAsync("root"); 229 | return items != null; 230 | } 231 | 232 | public static async Task TryCreateFolderAsync() 233 | { 234 | 235 | var folder = await FilesSnippets.CreateFolderAsync(STORY_DATA_IDENTIFIER, "root"); 236 | 237 | //Cleanup. Comment if you want to see the new folder under your root folder. 238 | await FilesSnippets.DeleteFolderAsync(folder.Id); 239 | 240 | return folder != null; 241 | } 242 | 243 | public static async Task TryDeleteFolderAsync() 244 | { 245 | 246 | var folder = await FilesSnippets.CreateFolderAsync(STORY_DATA_IDENTIFIER, "root"); 247 | 248 | 249 | var result = await FilesSnippets.DeleteFolderAsync(folder.Id); 250 | 251 | return result; 252 | 253 | } 254 | 255 | } 256 | } 257 | 258 | //********************************************************* 259 | // 260 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 261 | // 262 | //Copyright (c) Microsoft Corporation 263 | //All rights reserved. 264 | // 265 | // MIT License: 266 | // Permission is hereby granted, free of charge, to any person obtaining 267 | // a copy of this software and associated documentation files (the 268 | // ""Software""), to deal in the Software without restriction, including 269 | // without limitation the rights to use, copy, modify, merge, publish, 270 | // distribute, sublicense, and/or sell copies of the Software, and to 271 | // permit persons to whom the Software is furnished to do so, subject to 272 | // the following conditions: 273 | 274 | // The above copyright notice and this permission notice shall be 275 | // included in all copies or substantial portions of the Software. 276 | 277 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 278 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 279 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 280 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 281 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 282 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 283 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 284 | // 285 | //********************************************************* -------------------------------------------------------------------------------- /src/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Runtime.InteropServices.WindowsRuntime; 8 | using System.Threading.Tasks; 9 | using Windows.Foundation; 10 | using Windows.Foundation.Collections; 11 | using Windows.UI.Xaml; 12 | using Windows.UI.Xaml.Controls; 13 | using Windows.UI.Xaml.Controls.Primitives; 14 | using Windows.UI.Xaml.Data; 15 | using Windows.UI.Xaml.Input; 16 | using Windows.UI.Xaml.Media; 17 | using Windows.UI.Xaml.Navigation; 18 | 19 | // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238 20 | 21 | namespace O365_Win_Snippets 22 | { 23 | /// 24 | /// An empty page that can be used on its own or navigated to within a Frame. 25 | /// 26 | public sealed partial class MainPage : Page 27 | { 28 | public List StoryCollection { get; private set; } 29 | public MainPage() 30 | { 31 | this.InitializeComponent(); 32 | CreateTestList(); 33 | } 34 | 35 | protected override void OnNavigatedTo(NavigationEventArgs e) 36 | { 37 | // Developer code - if you haven't registered the app yet, we warn you. 38 | if (!App.Current.Resources.ContainsKey("ida:ClientID")) 39 | { 40 | Debug.WriteLine("Oops - App not registered with Office 365. To run this sample, you must register it with Office 365. You can do that through the 'Add | Connected services' dialog in Visual Studio. See Readme for more info"); 41 | 42 | } 43 | } 44 | private void CreateTestList() 45 | { 46 | StoryCollection = new List(); 47 | 48 | // These stories require your app to have permission to access your organization's directory. 49 | // Comment them if you're not going to run the app with that permission level. 50 | 51 | StoryCollection.Add(new StoryDefinition() { GroupName = "Users & Groups", Title = "Client", RunStoryAsync = UsersAndGroupsStories.TryGetAadGraphClientAsync }); 52 | StoryCollection.Add(new StoryDefinition() { GroupName = "Users & Groups", Title = "Read Users", RunStoryAsync = UsersAndGroupsStories.TryGetUsersAsync }); 53 | StoryCollection.Add(new StoryDefinition() { GroupName = "Users & Groups", Title = "Tenant Details", RunStoryAsync = UsersAndGroupsStories.TryGetTenantAsync }); 54 | StoryCollection.Add(new StoryDefinition() { GroupName = "Users & Groups", Title = "Read Groups", RunStoryAsync = UsersAndGroupsStories.TryGetGroupsAsync }); 55 | 56 | StoryCollection.Add(new StoryDefinition() { GroupName = "Contacts", Title = "Client", RunStoryAsync = ContactsStories.TryGetOutlookClientAsync }); 57 | StoryCollection.Add(new StoryDefinition() { GroupName = "Contacts", Title = "Read", RunStoryAsync = ContactsStories.TryGetContactsAsync }); 58 | StoryCollection.Add(new StoryDefinition() { GroupName = "Contacts", Title = "Get contact", RunStoryAsync = ContactsStories.TryGetContactAsync }); 59 | StoryCollection.Add(new StoryDefinition() { GroupName = "Contacts", Title = "Create", RunStoryAsync = ContactsStories.TryAddNewContactAsync }); 60 | StoryCollection.Add(new StoryDefinition() { GroupName = "Contacts", Title = "Delete", RunStoryAsync = ContactsStories.TryDeleteContactAsync }); 61 | StoryCollection.Add(new StoryDefinition() { GroupName = "Contacts", Title = "Update", RunStoryAsync = ContactsStories.TryUpdateContactAsync }); 62 | 63 | 64 | StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Client", RunStoryAsync = CalendarStories.TryGetOutlookClientAsync }); 65 | StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Read", RunStoryAsync = CalendarStories.TryGetCalendarEventsAsync }); 66 | StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Create", RunStoryAsync = CalendarStories.TryCreateCalendarEventAsync }); 67 | StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Create with args", RunStoryAsync = CalendarStories.TryCreateCalendarEventWithArgsAsync }); 68 | StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Update", RunStoryAsync = CalendarStories.TryUpdateCalendarEventAsync }); 69 | StoryCollection.Add(new StoryDefinition() { GroupName = "Calendar", Title = "Delete", RunStoryAsync = CalendarStories.TryDeleteCalendarEventAsync }); 70 | 71 | 72 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Client", RunStoryAsync = EmailStories.TryGetOutlookClientAsync }); 73 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Read Inbox", RunStoryAsync = EmailStories.TryGetInboxMessagesAsync }); 74 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Read messages", RunStoryAsync = EmailStories.TryGetMessagesAsync }); 75 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "SendMail", RunStoryAsync = EmailStories.TrySendMessageAsync }); 76 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Reply", RunStoryAsync = EmailStories.TryReplyMessageAsync }); 77 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Reply All", RunStoryAsync = EmailStories.TryReplyAllAsync }); 78 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Forward", RunStoryAsync = EmailStories.TryForwardMessageAsync }); 79 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Create draft", RunStoryAsync = EmailStories.TryCreateDraftAsync }); 80 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Update", RunStoryAsync = EmailStories.TryUpdateMessageAsync }); 81 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Delete", RunStoryAsync = EmailStories.TryDeleteMessageAsync }); 82 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Move", RunStoryAsync = EmailStories.TryMoveMessageAsync }); 83 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Copy", RunStoryAsync = EmailStories.TryCopyMessageAsync }); 84 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Add Attachment", RunStoryAsync = EmailStories.TryAddFileAttachmentAsync }); 85 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Get Attachments", RunStoryAsync = EmailStories.TryGetFileAttachmentsAsync }); 86 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email", Title = "Get Web Link", RunStoryAsync = EmailStories.TryGetMessageWebLinkAsync }); 87 | 88 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email folder", Title = "Read Folders", RunStoryAsync = EmailStories.TryGetMailFoldersAsync }); 89 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email folder", Title = "Create", RunStoryAsync = EmailStories.TryCreateMailFolderAsync }); 90 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email folder", Title = "Rename", RunStoryAsync = EmailStories.TryUpdateMailFolderAsync }); 91 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email folder", Title = "Move", RunStoryAsync = EmailStories.TryMoveMailFolderAsync }); 92 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email folder", Title = "Copy", RunStoryAsync = EmailStories.TryCopyMailFolderAsync }); 93 | StoryCollection.Add(new StoryDefinition() { GroupName = "Email folder", Title = "Delete", RunStoryAsync = EmailStories.TryDeleteMailFolderAsync }); 94 | 95 | 96 | 97 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Client", RunStoryAsync = FilesStories.TryGetSharePointClientAsync }); 98 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Read folders", RunStoryAsync = FilesStories.TryGetFolderChildrenAsync }); 99 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Create folder", RunStoryAsync = FilesStories.TryCreateFolderAsync }); 100 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Delete folder", RunStoryAsync = FilesStories.TryDeleteFolderAsync }); 101 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Create file", RunStoryAsync = FilesStories.TryCreateFileAsync }); 102 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Update content", RunStoryAsync = FilesStories.TryUpdateFileContentAsync }); 103 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Delete file", RunStoryAsync = FilesStories.TryDeleteFileAsync }); 104 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Download", RunStoryAsync = FilesStories.TryDownloadFileAsync }); 105 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Copy file", RunStoryAsync = FilesStories.TryCopyFileAsync }); 106 | StoryCollection.Add(new StoryDefinition() { GroupName = "Files", Title = "Rename file", RunStoryAsync = FilesStories.TryRenameFileAsync }); 107 | 108 | var result = from story in StoryCollection group story by story.GroupName into api orderby api.Key select api; 109 | StoriesByApi.Source = result; 110 | } 111 | 112 | 113 | private async void RunSelectedStories_Click(object sender, RoutedEventArgs e) 114 | { 115 | 116 | await runSelectedAsync(); 117 | } 118 | 119 | private async Task runSelectedAsync() 120 | { 121 | ResetStories(); 122 | Stopwatch sw = new Stopwatch(); 123 | 124 | foreach (var story in StoryGrid.SelectedItems) 125 | { 126 | StoryDefinition currentStory = story as StoryDefinition; 127 | currentStory.IsRunning = true; 128 | sw.Restart(); 129 | bool result = false; 130 | try 131 | { 132 | result = await currentStory.RunStoryAsync(); 133 | Debug.WriteLine(String.Format("{0}.{1} {2}", currentStory.GroupName, currentStory.Title, (result) ? "passed" : "failed")); 134 | } 135 | catch (Exception ex) 136 | { 137 | Debug.WriteLine("{0}.{1} failed. Exception: {2}", currentStory.GroupName, currentStory.Title, ex.Message); 138 | result = false; 139 | 140 | } 141 | currentStory.Result = result; 142 | sw.Stop(); 143 | currentStory.DurationMS = sw.ElapsedMilliseconds; 144 | currentStory.IsRunning = false; 145 | 146 | 147 | } 148 | 149 | // To shut down this app when the Stories complete, uncomment the following line. 150 | //Application.Current.Exit(); 151 | } 152 | 153 | private async void RunAll_Click(object sender, RoutedEventArgs e) 154 | { 155 | StoryGrid.SelectedItems.Clear(); 156 | foreach (var item in StoryGrid.Items) 157 | { 158 | StoryGrid.SelectedItems.Add(item); 159 | } 160 | await runSelectedAsync(); 161 | } 162 | 163 | private void ResetStories() 164 | { 165 | foreach (var story in StoryCollection) 166 | { 167 | story.Result = null; 168 | story.DurationMS = null; 169 | } 170 | } 171 | 172 | private void ClearSelection_Click(object sender, RoutedEventArgs e) 173 | { 174 | StoryGrid.SelectedItems.Clear(); 175 | } 176 | 177 | private void Disconnect_Click(object sender, RoutedEventArgs e) 178 | { 179 | AuthenticationHelper.SignOut(); 180 | StoryGrid.SelectedItems.Clear(); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/O365-Win-Snippets.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {186CF7AD-D3B3-478E-B08B-650E71DCA726} 8 | AppContainerExe 9 | Properties 10 | O365_Win_Snippets 11 | O365-Win-Snippets 12 | en-US 13 | 8.1 14 | 12 15 | 512 16 | {BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | O365-Win-Snippets_TemporaryKey.pfx 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE;NETFX_CORE;WINDOWS_APP 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE;NETFX_CORE;WINDOWS_APP 35 | prompt 36 | 4 37 | 38 | 39 | true 40 | bin\ARM\Debug\ 41 | DEBUG;TRACE;NETFX_CORE;WINDOWS_APP 42 | ;2008 43 | full 44 | ARM 45 | false 46 | prompt 47 | true 48 | 49 | 50 | bin\ARM\Release\ 51 | TRACE;NETFX_CORE;WINDOWS_APP 52 | true 53 | ;2008 54 | pdbonly 55 | ARM 56 | false 57 | prompt 58 | true 59 | 60 | 61 | true 62 | bin\x64\Debug\ 63 | DEBUG;TRACE;NETFX_CORE;WINDOWS_APP 64 | ;2008 65 | full 66 | x64 67 | false 68 | prompt 69 | true 70 | 71 | 72 | bin\x64\Release\ 73 | TRACE;NETFX_CORE;WINDOWS_APP 74 | true 75 | ;2008 76 | pdbonly 77 | x64 78 | false 79 | prompt 80 | true 81 | 82 | 83 | true 84 | bin\x86\Debug\ 85 | DEBUG;TRACE;NETFX_CORE;WINDOWS_APP 86 | ;2008 87 | full 88 | x86 89 | false 90 | prompt 91 | true 92 | 93 | 94 | bin\x86\Release\ 95 | TRACE;NETFX_CORE;WINDOWS_APP 96 | true 97 | ;2008 98 | pdbonly 99 | x86 100 | false 101 | prompt 102 | true 103 | 104 | 105 | 106 | App.xaml 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | MainPage.xaml 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | Designer 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | MSBuild:Compile 144 | Designer 145 | 146 | 147 | MSBuild:Compile 148 | Designer 149 | 150 | 151 | 152 | 153 | ..\packages\Microsoft.Azure.ActiveDirectory.GraphClient.2.0.8\lib\portable-net40+wp8+win8+MonoAndroid10+MonoTouch10+WindowsPhoneApp81\Microsoft.Azure.ActiveDirectory.GraphClient.dll 154 | True 155 | 156 | 157 | False 158 | ..\packages\Microsoft.Data.Edm.5.6.4\lib\portable-net45+wp8+win8+wpa\Microsoft.Data.Edm.dll 159 | 160 | 161 | False 162 | ..\packages\Microsoft.Data.OData.5.6.4\lib\portable-net45+wp8+win8+wpa\Microsoft.Data.OData.dll 163 | 164 | 165 | False 166 | ..\packages\Microsoft.Data.Services.Client.5.6.4\lib\portable-net45+wp8+win8+wpa\Microsoft.Data.Services.Client.dll 167 | 168 | 169 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.14.201151115\lib\netcore45\Microsoft.IdentityModel.Clients.ActiveDirectory.winmd 170 | 171 | 172 | False 173 | ..\packages\Microsoft.OData.Client.6.11.0\lib\portable-net45+wp8+win8+wpa\Microsoft.OData.Client.dll 174 | 175 | 176 | False 177 | ..\packages\Microsoft.OData.Core.6.11.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.OData.Core.dll 178 | 179 | 180 | False 181 | ..\packages\Microsoft.OData.Edm.6.11.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.OData.Edm.dll 182 | 183 | 184 | ..\packages\Microsoft.OData.ProxyExtensions.1.0.30\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Microsoft.OData.ProxyExtensions.dll 185 | 186 | 187 | ..\packages\Microsoft.Office365.Discovery.1.0.22\lib\portable-net40+sl5+wp8+win8+MonoAndroid10+MonoTouch10+WindowsPhoneApp81\Microsoft.Office365.Discovery.dll 188 | 189 | 190 | False 191 | ..\packages\Microsoft.Office365.OutlookServices.1.0.34\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Microsoft.Office365.OutlookServices.Portable.dll 192 | 193 | 194 | ..\packages\Microsoft.Office365.SharePoint.1.0.22\lib\portable-net40+sl5+wp8+win8+MonoAndroid10+MonoTouch10+WindowsPhoneApp81\Microsoft.Office365.SharePoint.Portable.dll 195 | 196 | 197 | False 198 | ..\packages\Microsoft.Spatial.6.11.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.Spatial.dll 199 | 200 | 201 | ..\packages\Newtonsoft.Json.6.0.5\lib\netcore45\Newtonsoft.Json.dll 202 | 203 | 204 | False 205 | ..\packages\System.Spatial.5.6.4\lib\portable-net45+wp8+win8+wpa\System.Spatial.dll 206 | 207 | 208 | 209 | 12.0 210 | 211 | 212 | 219 | -------------------------------------------------------------------------------- /src/OdataProxy exceptions.txt: -------------------------------------------------------------------------------- 1 | User: 2 | 3 | Error 1 The type 'Microsoft.OData.ProxyExtensions.IEntityBase' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.OData.ProxyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. C:\Users\jamescro\Documents\Visual Studio 2013\Projects\GraphAPIDemo\GraphAPIDemo\MainPage.xaml.cs 117 21 GraphAPIDemo 4 | Error 2 The type 'Microsoft.OData.ProxyExtensions.EntityBase' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.OData.ProxyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. C:\Users\jamescro\Documents\Visual Studio 2013\Projects\GraphAPIDemo\GraphAPIDemo\MainPage.xaml.cs 119 21 GraphAPIDemo 5 | 6 | 7 | 8 | Files 9 | 10 | 11 | Error 3 The type 'Microsoft.OData.ProxyExtensions.IReadOnlyQueryableSetBase`1' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.OData.ProxyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. C:\Users\jamescro\Documents\Visual Studio 2013\Projects\GraphAPIDemo\GraphAPIDemo\MainPage.xaml.cs 142 21 GraphAPIDemo 12 | Error 2 The type 'Microsoft.OData.ProxyExtensions.IReadOnlyQueryableSetBase' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.OData.ProxyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. C:\Users\jamescro\Documents\Visual Studio 2013\Projects\GraphAPIDemo\GraphAPIDemo\MainPage.xaml.cs 142 21 GraphAPIDemo 13 | Error 1 The type 'Microsoft.OData.ProxyExtensions.IEntityBase' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.OData.ProxyExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. C:\Users\jamescro\Documents\Visual Studio 2013\Projects\GraphAPIDemo\GraphAPIDemo\MainPage.xaml.cs 141 21 GraphAPIDemo 14 | Error 4 'Microsoft.Graph.IItemCollection' does not contain a definition for 'Take' and no extension method 'Take' accepting a first argument of type 'Microsoft.Graph.IItemCollection' could be found (are you missing a using directive or an assembly reference?) C:\Users\jamescro\Documents\Visual Studio 2013\Projects\GraphAPIDemo\GraphAPIDemo\MainPage.xaml.cs 142 45 GraphAPIDemo 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | O365-Win-Snippets 6 | Microsoft 7 | Assets\StoreLogo.png 8 | 9 | 10 | 6.3.0 11 | 6.3.0 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("O365-Win-Snippets")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("O365-Win-Snippets")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /src/ResultToBrushConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Windows.UI; 9 | using Windows.UI.Xaml.Data; 10 | using Windows.UI.Xaml.Media; 11 | 12 | namespace O365_Win_Snippets 13 | { 14 | /// 15 | /// Value converter that translates true to and false to 16 | /// . 17 | /// 18 | public sealed class ResultToBrushConverter : IValueConverter 19 | { 20 | private static readonly SolidColorBrush NOT_STARTED_BRUSH = new SolidColorBrush(Colors.LightSlateGray); 21 | private static readonly SolidColorBrush SUCCESS_BRUSH = new SolidColorBrush(Colors.Green); 22 | private static readonly SolidColorBrush FAILED_BRUSH = new SolidColorBrush(Colors.Red); 23 | public object Convert(object value, Type targetType, object parameter, string language) 24 | { 25 | bool? result = (bool?)value; 26 | if (result.HasValue) 27 | { 28 | return (result.Value) ? SUCCESS_BRUSH : FAILED_BRUSH; 29 | } 30 | else 31 | { 32 | return NOT_STARTED_BRUSH; 33 | } 34 | } 35 | 36 | public object ConvertBack(object value, Type targetType, object parameter, string language) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | } 41 | } 42 | 43 | //********************************************************* 44 | // 45 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 46 | // 47 | //Copyright (c) Microsoft Corporation 48 | //All rights reserved. 49 | // 50 | // MIT License: 51 | // Permission is hereby granted, free of charge, to any person obtaining 52 | // a copy of this software and associated documentation files (the 53 | // ""Software""), to deal in the Software without restriction, including 54 | // without limitation the rights to use, copy, modify, merge, publish, 55 | // distribute, sublicense, and/or sell copies of the Software, and to 56 | // permit persons to whom the Software is furnished to do so, subject to 57 | // the following conditions: 58 | 59 | // The above copyright notice and this permission notice shall be 60 | // included in all copies or substantial portions of the Software. 61 | 62 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 63 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 64 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 65 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 66 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 67 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 68 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 69 | // 70 | //********************************************************* -------------------------------------------------------------------------------- /src/StoryDefinition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace O365_Win_Snippets 10 | { 11 | 12 | public class StoryDefinition : ViewModelBase 13 | { 14 | public string GroupName { get; set; } 15 | public string Title { get; set; } 16 | 17 | // Delegate method to call 18 | public Func> RunStoryAsync { get; set; } 19 | 20 | bool _isRunning = false; 21 | public bool IsRunning 22 | { 23 | get 24 | { 25 | return _isRunning; 26 | } 27 | set 28 | { 29 | SetProperty(ref _isRunning, value); 30 | } 31 | } 32 | 33 | bool? _result = null; 34 | public bool? Result 35 | { 36 | get 37 | { 38 | return _result; 39 | } 40 | set 41 | { 42 | SetProperty(ref _result, value); 43 | } 44 | } 45 | 46 | long? _durationMS = 0; 47 | public long? DurationMS 48 | { 49 | get 50 | { 51 | return _durationMS; 52 | } 53 | set 54 | { 55 | SetProperty(ref _durationMS, value); 56 | } 57 | } 58 | 59 | } 60 | 61 | public class TestGroup 62 | { 63 | public string GroupTitle { get; set; } 64 | public List Tests { get; set; } 65 | } 66 | 67 | 68 | } 69 | 70 | //********************************************************* 71 | // 72 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 73 | // 74 | //Copyright (c) Microsoft Corporation 75 | //All rights reserved. 76 | // 77 | // MIT License: 78 | // Permission is hereby granted, free of charge, to any person obtaining 79 | // a copy of this software and associated documentation files (the 80 | // ""Software""), to deal in the Software without restriction, including 81 | // without limitation the rights to use, copy, modify, merge, publish, 82 | // distribute, sublicense, and/or sell copies of the Software, and to 83 | // permit persons to whom the Software is furnished to do so, subject to 84 | // the following conditions: 85 | 86 | // The above copyright notice and this permission notice shall be 87 | // included in all copies or substantial portions of the Software. 88 | 89 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 90 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 91 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 92 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 93 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 94 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 95 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 96 | // 97 | //********************************************************* -------------------------------------------------------------------------------- /src/UsersAndGroups/UsersAndGroupsSnippets.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using Microsoft.Azure.ActiveDirectory.GraphClient; 4 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 5 | using Microsoft.Office365.Discovery; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | 14 | namespace O365_Win_Snippets 15 | { 16 | class UsersAndGroupsSnippets 17 | { 18 | /// 19 | /// Checks that a Graph client is available to the client. 20 | /// 21 | /// The Graph client. 22 | private static ActiveDirectoryClient _graphClient = null; 23 | 24 | public static async Task GetGraphClientAsync() 25 | { 26 | //Check to see if this client has already been created. If so, return it. Otherwise, create a new one. 27 | if (_graphClient != null) 28 | { 29 | Debug.WriteLine("Got a Graph client for Users and Groups."); 30 | return _graphClient; 31 | } 32 | else 33 | { 34 | // Active Directory service endpoints 35 | const string AadServiceResourceId = "https://graph.windows.net/"; 36 | Uri AadServiceEndpointUri = new Uri("https://graph.windows.net/"); 37 | 38 | try 39 | { 40 | //First, look for the authority used during the last authentication. 41 | //If that value is not populated, use _commonAuthority. 42 | string authority = null; 43 | if (String.IsNullOrEmpty(AuthenticationHelper.LastAuthority)) 44 | { 45 | authority = AuthenticationHelper.CommonAuthority; 46 | } 47 | else 48 | { 49 | authority = AuthenticationHelper.LastAuthority; 50 | } 51 | 52 | // Create an AuthenticationContext using this authority. 53 | AuthenticationHelper._authenticationContext = new AuthenticationContext(authority); 54 | 55 | var token = await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, AadServiceResourceId); 56 | 57 | // Check the token 58 | if (String.IsNullOrEmpty(token)) 59 | { 60 | // User cancelled sign-in 61 | return null; 62 | } 63 | else 64 | { 65 | // Create our ActiveDirectory client. 66 | _graphClient = new ActiveDirectoryClient( 67 | new Uri(AadServiceEndpointUri, AuthenticationHelper.TenantId), 68 | async () => await AuthenticationHelper.GetTokenHelperAsync(AuthenticationHelper._authenticationContext, AadServiceResourceId)); 69 | 70 | Debug.WriteLine("Got a Graph client for Users and Groups."); 71 | 72 | return _graphClient; 73 | } 74 | 75 | 76 | } 77 | 78 | catch (Exception) 79 | { 80 | // Argument exception 81 | } 82 | AuthenticationHelper._authenticationContext.TokenCache.Clear(); 83 | return null; 84 | } 85 | } 86 | 87 | public static async Task> GetUsersAsync() 88 | { 89 | 90 | var client = await GetGraphClientAsync(); 91 | 92 | var users = await client.Users.ExecuteAsync(); 93 | 94 | Debug.WriteLine("First user in collection: " + users.CurrentPage[0].DisplayName); 95 | 96 | return users.CurrentPage.ToList(); 97 | 98 | 99 | } 100 | 101 | public static async Task GetTenantDetailsAsync() 102 | { 103 | 104 | var client = await GetGraphClientAsync(); 105 | 106 | var tenantDetails = await client.TenantDetails.ExecuteAsync(); 107 | 108 | Debug.WriteLine("Got tenant details."); 109 | 110 | return tenantDetails.CurrentPage.First(); 111 | 112 | 113 | } 114 | 115 | public static async Task> GetGroupsAsync() 116 | { 117 | 118 | var client = await GetGraphClientAsync(); 119 | 120 | var groups = await client.Groups.ExecuteAsync(); 121 | 122 | if (groups.CurrentPage.Count == 0) 123 | { 124 | Debug.WriteLine("No groups."); 125 | return new List(); 126 | } 127 | 128 | Debug.WriteLine("First group in collection: " + groups.CurrentPage[0].DisplayName); 129 | 130 | return groups.CurrentPage.ToList(); 131 | 132 | } 133 | 134 | } 135 | } 136 | 137 | //********************************************************* 138 | // 139 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 140 | // 141 | //Copyright (c) Microsoft Corporation 142 | //All rights reserved. 143 | // 144 | // MIT License: 145 | // Permission is hereby granted, free of charge, to any person obtaining 146 | // a copy of this software and associated documentation files (the 147 | // ""Software""), to deal in the Software without restriction, including 148 | // without limitation the rights to use, copy, modify, merge, publish, 149 | // distribute, sublicense, and/or sell copies of the Software, and to 150 | // permit persons to whom the Software is furnished to do so, subject to 151 | // the following conditions: 152 | 153 | // The above copyright notice and this permission notice shall be 154 | // included in all copies or substantial portions of the Software. 155 | 156 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 157 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 158 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 159 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 160 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 161 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 162 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 163 | // 164 | //********************************************************* -------------------------------------------------------------------------------- /src/UsersAndGroups/UsersAndGroupsStories.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace O365_Win_Snippets 10 | { 11 | class UsersAndGroupsStories 12 | { 13 | public static async Task TryGetAadGraphClientAsync() 14 | { 15 | var client = await UsersAndGroupsSnippets.GetGraphClientAsync(); 16 | return client != null; 17 | } 18 | 19 | public static async Task TryGetUsersAsync() 20 | { 21 | var users = await UsersAndGroupsSnippets.GetUsersAsync(); 22 | 23 | return users != null; 24 | } 25 | 26 | public static async Task TryGetTenantAsync() 27 | { 28 | var details = await UsersAndGroupsSnippets.GetTenantDetailsAsync(); 29 | 30 | return details != null; 31 | } 32 | 33 | public static async Task TryGetGroupsAsync() 34 | { 35 | var groups = await UsersAndGroupsSnippets.GetGroupsAsync(); 36 | 37 | return groups != null; 38 | } 39 | 40 | } 41 | } 42 | 43 | //********************************************************* 44 | // 45 | //O365-Win-Snippets, https://github.com/OfficeDev/O365-Win-Snippets 46 | // 47 | //Copyright (c) Microsoft Corporation 48 | //All rights reserved. 49 | // 50 | // MIT License: 51 | // Permission is hereby granted, free of charge, to any person obtaining 52 | // a copy of this software and associated documentation files (the 53 | // ""Software""), to deal in the Software without restriction, including 54 | // without limitation the rights to use, copy, modify, merge, publish, 55 | // distribute, sublicense, and/or sell copies of the Software, and to 56 | // permit persons to whom the Software is furnished to do so, subject to 57 | // the following conditions: 58 | 59 | // The above copyright notice and this permission notice shall be 60 | // included in all copies or substantial portions of the Software. 61 | 62 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 63 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 64 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 65 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 66 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 67 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 68 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 69 | // 70 | //********************************************************* -------------------------------------------------------------------------------- /src/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. 2 | 3 | using System; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace O365_Win_Snippets 8 | { 9 | /// 10 | /// Base view model for working with Office 365 services. 11 | /// 12 | public class ViewModelBase : INotifyPropertyChanged 13 | { 14 | 15 | protected bool SetProperty(ref T field, T value, [CallerMemberName] string propertyName = "") 16 | { 17 | // If the value is the same as the current value, return false to indicate this was a no-op. 18 | if (Object.Equals(field, value)) 19 | return false; 20 | 21 | // Raise any registered property changed events and indicate to the user that the value was indeed changed. 22 | field = value; 23 | NotifyPropertyChanged(propertyName); 24 | return true; 25 | } 26 | 27 | public event PropertyChangedEventHandler PropertyChanged; 28 | 29 | 30 | protected void NotifyPropertyChanged([CallerMemberName]string propertyName = "") 31 | { 32 | if (PropertyChanged != null) 33 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 34 | } 35 | } 36 | } 37 | 38 | //********************************************************* 39 | // 40 | // MIT License: 41 | // Permission is hereby granted, free of charge, to any person obtaining 42 | // a copy of this software and associated documentation files (the 43 | // ""Software""), to deal in the Software without restriction, including 44 | // without limitation the rights to use, copy, modify, merge, publish, 45 | // distribute, sublicense, and/or sell copies of the Software, and to 46 | // permit persons to whom the Software is furnished to do so, subject to 47 | // the following conditions: 48 | 49 | // The above copyright notice and this permission notice shall be 50 | // included in all copies or substantial portions of the Software. 51 | 52 | // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, 53 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 54 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 55 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 56 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 57 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 58 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 59 | // 60 | //********************************************************* 61 | -------------------------------------------------------------------------------- /src/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | --------------------------------------------------------------------------------