├── .deployment ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Deployment └── azuredeploy.json ├── LICENSE ├── Manifest ├── color.png ├── manifest.json └── outline.png ├── README.md ├── SECURITY.md ├── Source ├── GlobalSuppressions.cs ├── Microsoft.Teams.Apps.Celebration.Notification.Func │ ├── EventNotificationFunction.cs │ ├── MessageDeliveryFunction.cs │ ├── Microsoft.Teams.Apps.Celebration.Notification.Func.csproj │ ├── PreviewFunction.cs │ ├── host.json │ ├── settings.json │ └── stylecop.json ├── Microsoft.Teams.Apps.Celebration.sln ├── Microsoft.Teams.Apps.Celebration │ ├── .gitignore │ ├── App_Start │ │ ├── BundleConfig.cs │ │ ├── FilterConfig.cs │ │ ├── RouteConfig.cs │ │ ├── Startup.Auth.cs │ │ └── WebApiConfig.cs │ ├── ApplicationConfig.cs │ ├── ApplicationInsights.config │ ├── CelebrationsAppModule.cs │ ├── Connected Services │ │ └── Application Insights │ │ │ └── ConnectedService.json │ ├── Content │ │ ├── css │ │ │ ├── button-themes.css │ │ │ ├── datepicker-custom-theme.css │ │ │ ├── datepicker.css │ │ │ ├── spinner.css │ │ │ ├── styles.css │ │ │ └── tabStyles.css │ │ └── images │ │ │ ├── Celebrations-bot-image-new-event.jpg │ │ │ ├── carousel │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ │ │ ├── celebration_bot_full-color.png │ │ │ ├── empty-state-celebrations.png │ │ │ ├── welcome-card-image.png │ │ │ └── welcome-dialog.png │ ├── Controllers │ │ ├── AuthController.cs │ │ ├── EventNotificationController.cs │ │ ├── MessageDeliveryController.cs │ │ ├── MessagesController.cs │ │ ├── PreviewController.cs │ │ ├── SharedSecretAuthenticationAttribute.cs │ │ ├── TabAuthController.cs │ │ └── TabsController.cs │ ├── Dialog │ │ ├── DialogFactory.cs │ │ ├── IgnoreEventShareDialog.cs │ │ ├── RootDialog.cs │ │ ├── ShareEventDialog.cs │ │ └── SkipEventDialog.cs │ ├── ErrorHandler │ │ └── AiHandleErrorAttribute.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Helpers │ │ ├── AdaptiveCardExtensions.cs │ │ ├── ApplicationSettings.cs │ │ ├── CelebrationCard.cs │ │ ├── Common.cs │ │ ├── ConnectorClientFactory.cs │ │ ├── ConversationExtensions.cs │ │ ├── DocumentQueryExtensions.cs │ │ ├── EventDataProvider.cs │ │ ├── IConnectorClientFactory.cs │ │ ├── IEventDataProvider.cs │ │ ├── IUserManagementHelper.cs │ │ └── UserManagementHelper.cs │ ├── Microsoft.Teams.Apps.Celebration.csproj │ ├── Models │ │ ├── BotScope.cs │ │ ├── CelebrationEvent.cs │ │ ├── EventMessage.cs │ │ ├── EventNotificationData.cs │ │ ├── EventOccurrence.cs │ │ ├── EventStatus.cs │ │ ├── EventTypes.cs │ │ ├── EventsTabViewModel.cs │ │ ├── ManageEventViewModel.cs │ │ ├── MessageSendResult.cs │ │ ├── MessageType.cs │ │ ├── PreviewCardPayload.cs │ │ ├── ShareEventPayload.cs │ │ ├── SubmitActionPayload.cs │ │ ├── Team.cs │ │ ├── TimeZoneDisplayInfo.cs │ │ ├── User.cs │ │ └── UserTeamMembership.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Resources │ │ ├── Strings.Designer.cs │ │ └── Strings.resx │ ├── Scripts │ │ ├── bootstrap-datepicker.js │ │ ├── custom-multiselect.js │ │ ├── events.js │ │ └── theme.js │ ├── Startup.cs │ ├── Views │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ └── _Spinner.cshtml │ │ ├── TabAuth │ │ │ ├── Callback.cshtml │ │ │ ├── Login.cshtml │ │ │ └── Start.cshtml │ │ ├── Tabs │ │ │ ├── Events.cshtml │ │ │ ├── EventsData.cshtml │ │ │ ├── ManageEvent.cshtml │ │ │ ├── ManageEventData.cshtml │ │ │ └── Tour.cshtml │ │ ├── _ViewStart.cshtml │ │ └── web.config │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── default.htm │ ├── libman.json │ ├── packages.config │ └── stylecop.json ├── Microsoft.Teams.Apps.Common │ ├── Authentication │ │ ├── ITokenValidator.cs │ │ └── TokenValidator.cs │ ├── CommonConfig.cs │ ├── CommonConstant.cs │ ├── Configuration │ │ ├── IConfigProvider.cs │ │ └── LocalConfigProvider.cs │ ├── Exceptions │ │ ├── SettingDeserializationException.cs │ │ └── SettingNotFoundException.cs │ ├── Extensions │ │ ├── ClaimsPrincipalExtensions.cs │ │ ├── ConfigurationExtensions.cs │ │ ├── EncryptedConfigAttribute.cs │ │ ├── LoggingExtensions.cs │ │ └── TimeTracker.cs │ ├── Http │ │ ├── HttpClientWrapper.cs │ │ └── IHttpClient.cs │ ├── Logging │ │ ├── AppInsightsLogProvider.cs │ │ ├── ILogProvider.cs │ │ └── LogLevel.cs │ ├── Microsoft.Teams.Apps.Common.csproj │ ├── Models │ │ └── Token.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Telemetry │ │ ├── TelemetryEvent.cs │ │ ├── TelemetryProperty.cs │ │ └── UserTelemetryInitializer.cs │ ├── app.config │ └── packages.config ├── Settings.StyleCop └── stylecop.json ├── deploy.app.cmd ├── deploy.cmd ├── deploy.function.cmd └── global.json /.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | command = deploy.cmd 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /Manifest/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Manifest/color.png -------------------------------------------------------------------------------- /Manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json", 3 | "manifestVersion": "1.5", 4 | "version": "1.0.0", 5 | "id": "5c0b8ee0-d445-409b-9c19-ab9edc07a314", 6 | "packageName": "com.microsoft.teams.celebrations", 7 | "developer": { 8 | "name": "", 9 | "websiteUrl": "", 10 | "privacyUrl": "", 11 | "termsOfUseUrl": "" 12 | }, 13 | "name": { 14 | "short": "Celebrations" 15 | }, 16 | "description": { 17 | "short": "Celebrate birthdays, anniversaries and other events with your team", 18 | "full": "Celebrations helps members celebrate each others' birthdays, anniversaries, and other recurring events" 19 | }, 20 | "icons": { 21 | "outline": "outline.png", 22 | "color": "color.png" 23 | }, 24 | "accentColor": "#64A2CC", 25 | "staticTabs": [ 26 | { 27 | "contentUrl": "https:///Tabs/Events?userObjectId={userObjectId}&theme={theme}&locale={locale}", 28 | "entityId": "EventsTab", 29 | "name": "Events", 30 | "scopes": [ 31 | "personal" 32 | ] 33 | } 34 | ], 35 | "bots": [ 36 | { 37 | "botId": "", 38 | "isNotificationOnly": false, 39 | "scopes": [ "team", "personal" ] 40 | } 41 | ], 42 | "permissions": [ 43 | "identity", 44 | "messageTeamMembers" 45 | ], 46 | "validDomains": [ 47 | "" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /Manifest/outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Manifest/outline.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - csharp 5 | products: 6 | - dotnet 7 | description: "Fun Teams app to celebrate special occasions in a team" 8 | --- 9 | 10 | # Celebrations App Template 11 | 12 | | [Documentation](https://github.com/OfficeDev/microsoft-teams-celebrations-app/wiki) | [Deployment guide](https://github.com/OfficeDev/microsoft-teams-celebrations-app/wiki/Deployment-guide) | [Architecture](https://github.com/OfficeDev/microsoft-teams-celebrations-app/wiki/Solution-overview) | 13 | | ---- | ---- | ---- | 14 | 15 | Celebrations is a Teams app that helps team members celebrate each others’ birthdays, anniversaries, and other recurring events. It remembers special occasions of all the team members and sends a friendly message in all the teams selected at the time of event creation, to make the team members feel special on their day. 16 | 17 | The app provides an easy interface for all the team members to personally add and view their events and also allows the user to select the teams in which the events gets shared. 18 | 19 | ## Get started 20 | 21 | Begin with the [Solution overview](https://github.com/OfficeDev/microsoft-teams-celebrations-app/wiki/Solution-overview) to read about what the app does and how it works. 22 | 23 | When you're ready to try out Celebrations, or to use it in your own organization, follow the steps in the [Deployment guide](https://github.com/OfficeDev/microsoft-teams-celebrations-app/wiki/Deployment-guide). 24 | 25 | ## Feedback 26 | 27 | Thoughts? Questions? Ideas? Share them with us on [Teams UserVoice](https://microsoftteams.uservoice.com/forums/555103-public)! 28 | 29 | Please report bugs and other code issues [here](https://github.com/OfficeDev/microsoft-teams-celebrations-app/issues/new). 30 | 31 | ## Legal notice 32 | 33 | This app template is provided under the [MIT License](https://github.com/OfficeDev/microsoft-teams-celebrations-app/blob/master/LICENSE) terms. In addition to these terms, by using this app template you agree to the following: 34 | 35 | - You, not Microsoft, will license the use of your app to users or organization. 36 | 37 | - This app template is not intended to substitute your own regulatory due diligence or make you or your app compliant with respect to any applicable regulations, including but not limited to privacy, healthcare, employment, or financial regulations. 38 | 39 | - You are responsible for complying with all applicable privacy and security regulations including those related to use, collection and handling of any personal data by your app. This includes complying with all internal privacy and security policies of your organization if your app is developed to be sideloaded internally within your organization. Where applicable, you may be responsible for data related incidents or data subject requests for data collected through your app. 40 | 41 | - Any trademarks or registered trademarks of Microsoft in the United States and/or other countries and logos included in this repository are the property of Microsoft, and the license for this project does not grant you rights to use any Microsoft names, logos or trademarks outside of this repository. Microsoft’s general trademark guidelines can be found [here](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general.aspx). 42 | 43 | - If the app template enables access to any Microsoft Internet-based services (e.g., Office365), use of those services will be subject to the separately-provided terms of use. In such cases, Microsoft may collect telemetry data related to app template usage and operation. Use and handling of telemetry data will be performed in accordance with such terms of use. 44 | 45 | - Use of this template does not guarantee acceptance of your app to the Teams app store. To make this app available in the Teams app store, you will have to comply with the [submission and validation process](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/appsource/publish), and all associated requirements such as including your own privacy statement and terms of use for your app. 46 | 47 | ## Contributing 48 | 49 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 50 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 51 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 52 | 53 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 54 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 55 | provided by the bot. You will only need to do this once across all repos using our CLA. 56 | 57 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 58 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 59 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 60 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /Source/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | // This file is used by Code Analysis to maintain SuppressMessage 6 | // attributes that are applied to this project. 7 | // Project-level suppressions either have no target or are given 8 | // a specific target and scoped to a namespace, type, member, etc. 9 | 10 | // Stylecop suppressions 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Style choice")] 12 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Style choice.")] 13 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/EventNotificationFunction.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace PreviewFunctionApp 6 | { 7 | using System; 8 | using System.Net.Http; 9 | using System.Threading.Tasks; 10 | using Microsoft.Azure.WebJobs; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.Logging; 13 | 14 | /// 15 | /// Send post request to celebration bot to send event card on the day of event 16 | /// 17 | public static class EventNotificationFunction 18 | { 19 | /// 20 | /// Timer triggered azure function that runs every hour and sends post request to bot to send the event card in teams on the day of event 21 | /// 22 | /// Timer instance 23 | /// ILogger instance 24 | /// ExecutionContext 25 | /// Tracking task 26 | [FunctionName("EventNotification")] 27 | public static async Task Run([TimerTrigger("0 0 * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context) 28 | { 29 | log.LogInformation($"Executing event notification function. Last run: {myTimer.ScheduleStatus.Last}"); 30 | 31 | try 32 | { 33 | var config = new ConfigurationBuilder() 34 | .SetBasePath(context.FunctionAppDirectory) 35 | .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) 36 | .AddEnvironmentVariables() 37 | .Build(); 38 | 39 | var baseUri = config["AppBaseUri"]; 40 | var endpointUri = $"{baseUri}/api/EventNotification?effectiveDateTime={DateTimeOffset.UtcNow.ToString("o")}"; 41 | 42 | var httpClient = new HttpClient(); 43 | httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("SharedSecret", config["AppApiKey"]); 44 | 45 | var response = await httpClient.PostAsync(endpointUri, new StringContent(string.Empty)); 46 | log.LogInformation($"Posted event notification trigger to app. Status code: {response.StatusCode}"); 47 | } 48 | catch (Exception ex) 49 | { 50 | log.LogError(ex, $"Error running event notification function: {ex.Message}"); 51 | } 52 | 53 | log.LogInformation($"Finished event notification function. Next run: {myTimer.Schedule.GetNextOccurrence(DateTime.UtcNow)}"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/MessageDeliveryFunction.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace ReliableDeliveryFunctionApp 6 | { 7 | using System; 8 | using System.Net.Http; 9 | using System.Threading.Tasks; 10 | using Microsoft.Azure.WebJobs; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.Logging; 13 | 14 | /// 15 | /// Send post request to celebration bot to send messages 16 | /// 17 | public static class MessageDeliveryFunction 18 | { 19 | /// 20 | /// Timer trigger azure function that runs in every 15 minutes and sends post request to bot to retry for failed messages 21 | /// 22 | /// Timer instance 23 | /// ILogger instance 24 | /// ExecutionContext 25 | /// Tracking task 26 | [FunctionName("MessageDelivery")] 27 | public static async Task Run([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context) 28 | { 29 | log.LogInformation($"Executing message delivery function. Last run: {myTimer.ScheduleStatus.Last}"); 30 | 31 | try 32 | { 33 | var config = new ConfigurationBuilder() 34 | .SetBasePath(context.FunctionAppDirectory) 35 | .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) 36 | .AddEnvironmentVariables() 37 | .Build(); 38 | 39 | var baseUri = config["AppBaseUri"]; 40 | var endpointUri = $"{baseUri}/api/MessageDelivery"; 41 | 42 | var httpClient = new HttpClient(); 43 | httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("SharedSecret", config["AppApiKey"]); 44 | 45 | var response = await httpClient.PostAsync(endpointUri, new StringContent(string.Empty)); 46 | log.LogInformation($"Posted message delivery trigger to app. Status code: {response.StatusCode}"); 47 | } 48 | catch (Exception ex) 49 | { 50 | log.LogError(ex, $"Error running message delivery function: {ex.Message}"); 51 | } 52 | 53 | log.LogInformation($"Finished message delivery function. Next run: {myTimer.Schedule.GetNextOccurrence(DateTime.UtcNow)}"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/Microsoft.Teams.Apps.Celebration.Notification.Func.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp2.1 4 | v2 5 | bin\Microsoft.Teams.Apps.Celebration.Notification.Func.xml 6 | 7 | 8 | bin\Release 9 | 10 | 11 | bin\Debug 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers 24 | 25 | 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | PreserveNewest 32 | 33 | 34 | PreserveNewest 35 | Never 36 | 37 | 38 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/PreviewFunction.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace PreviewFunctionApp 6 | { 7 | using System; 8 | using System.Net.Http; 9 | using System.Threading.Tasks; 10 | using Microsoft.Azure.WebJobs; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.Logging; 13 | 14 | /// 15 | /// Send post request to celebration bot to send the reminder of event 16 | /// 17 | public static class PreviewFunction 18 | { 19 | /// 20 | /// Timer trigger azure function that runs at 12 AM every day and sends post request to celebration app to process preview cards 21 | /// 22 | /// Timer instance 23 | /// ILogger instance 24 | /// ExecutionContext 25 | /// Tracking task 26 | [FunctionName("Preview")] 27 | public static async Task Run([TimerTrigger("0 0 0 * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context) 28 | { 29 | log.LogInformation($"Executing preview function. Last run: {myTimer.ScheduleStatus.Last}"); 30 | 31 | try 32 | { 33 | var config = new ConfigurationBuilder() 34 | .SetBasePath(context.FunctionAppDirectory) 35 | .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) 36 | .AddEnvironmentVariables() 37 | .Build(); 38 | 39 | var baseUri = config["AppBaseUri"]; 40 | var endpointUri = $"{baseUri}/api/Preview?effectiveDateTime={DateTimeOffset.UtcNow.ToString("o")}"; 41 | 42 | var httpClient = new HttpClient(); 43 | httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("SharedSecret", config["AppApiKey"]); 44 | 45 | var response = await httpClient.PostAsync(endpointUri, new StringContent(string.Empty)); 46 | log.LogInformation($"Posted preview trigger to app. Status code: {response.StatusCode}"); 47 | } 48 | catch (Exception ex) 49 | { 50 | log.LogError(ex, $"Error running preview function: {ex.Message}"); 51 | } 52 | 53 | log.LogInformation($"Finished preview function. Next run: {myTimer.Schedule.GetNextOccurrence(DateTime.UtcNow)}"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppBaseUri": "https://", 3 | "AppApiKey": "<>" 4 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.Notification.Func/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // 6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 7 | 8 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 9 | "settings": { 10 | "documentationRules": { 11 | "companyName": "Microsoft" 12 | } 13 | } 14 | } 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.Celebration", "Microsoft.Teams.Apps.Celebration\Microsoft.Teams.Apps.Celebration.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Teams.Apps.Common", "Microsoft.Teams.Apps.Common\Microsoft.Teams.Apps.Common.csproj", "{7FA2791F-E894-4876-8773-A5318631365C}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Teams.Apps.Celebration.Notification.Func", "Microsoft.Teams.Apps.Celebration.Notification.Func\Microsoft.Teams.Apps.Celebration.Notification.Func.csproj", "{2D93F294-E877-4988-B6FB-39F05ACFA38F}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {7FA2791F-E894-4876-8773-A5318631365C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {7FA2791F-E894-4876-8773-A5318631365C}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {7FA2791F-E894-4876-8773-A5318631365C}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {7FA2791F-E894-4876-8773-A5318631365C}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {2D93F294-E877-4988-B6FB-39F05ACFA38F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {2D93F294-E877-4988-B6FB-39F05ACFA38F}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {2D93F294-E877-4988-B6FB-39F05ACFA38F}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {2D93F294-E877-4988-B6FB-39F05ACFA38F}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {3C10A400-912E-4E21-BBB1-5DD411367AA6} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | Content/fontawesome/ -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System.Web.Optimization; 8 | 9 | /// 10 | /// Configures bundles 11 | /// 12 | public class BundleConfig 13 | { 14 | /// 15 | /// Configures bundles. 16 | /// 17 | /// Bundle collection to configure 18 | // For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862 19 | public static void RegisterBundles(BundleCollection bundles) 20 | { 21 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 22 | "~/lib/jquery/jquery.js")); 23 | bundles.Add(new ScriptBundle("~/bundles/jquery-validate").Include( 24 | "~/lib/jquery-validate/jquery.validate.js")); 25 | bundles.Add(new ScriptBundle("~/bundles/jquery-slimscroll").Include( 26 | "~/lib/jquery-slimscroll/jquery.slimscroll.js")); 27 | 28 | bundles.Add(new ScriptBundle("~/bundles/bootstrap3").Include( 29 | "~/lib/bootstrap3/dist/js/bootstrap.js")); 30 | bundles.Add(new ScriptBundle("~/bundles/bootstrap4").Include( 31 | "~/lib/bootstrap4/js/bootstrap.js")); 32 | bundles.Add(new ScriptBundle("~/bundles/bootstrap-multiselect").Include( 33 | "~/lib/bootstrap-multiselect/js/bootstrap-multiselect.js")); 34 | bundles.Add(new ScriptBundle("~/bundles/bootstrap-datepicker").Include( 35 | "~/Scripts/bootstrap-datepicker.js")); 36 | 37 | bundles.Add(new ScriptBundle("~/bundles/momentjs").Include( 38 | "~/lib/moment.js/moment.js")); 39 | bundles.Add(new ScriptBundle("~/bundles/adal-js").Include( 40 | "~/lib/adal-angular/dist/adal.min.js")); 41 | bundles.Add(new ScriptBundle("~/bundles/teams-js").Include( 42 | "~/lib/teams-js/dist/microsoftTeams.min.js")); 43 | 44 | bundles.Add(new StyleBundle("~/bundles/css").Include( 45 | "~/Content/css/styles.css", 46 | "~/Content/css/tabStyles.css", 47 | "~/Content/css/button-themes.css", 48 | "~/Content/css/spinner.css")); 49 | 50 | bundles.Add(new StyleBundle("~/bundles/bootstrap3-css").Include( 51 | "~/lib/bootstrap3/dist/css/bootstrap.css")); 52 | bundles.Add(new StyleBundle("~/bundles/bootstrap4-css").Include( 53 | "~/lib/bootstrap4/css/bootstrap.css")); 54 | bundles.Add(new StyleBundle("~/bundles/bootstrap-multiselect-css").Include( 55 | "~/lib/bootstrap-multiselect/css/bootstrap-multiselect.css")); 56 | bundles.Add(new StyleBundle("~/bundles/bootstrap-datepicker-css").Include( 57 | "~/Content/css/datepicker.css", 58 | "~/Content/css/datepicker-custom-theme.css")); 59 | bundles.Add(new StyleBundle("~/content/fontawesome/css/font-awesome").Include( 60 | "~/Content/fontawesome/css/font-awesome.css")); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System.Web.Mvc; 8 | 9 | /// 10 | /// Filter configurator 11 | /// 12 | public class FilterConfig 13 | { 14 | /// 15 | /// Configures global filters 16 | /// 17 | /// The filters to configure 18 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 19 | { 20 | filters.Add(new HandleErrorAttribute()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System.Web.Mvc; 8 | using System.Web.Routing; 9 | 10 | /// 11 | /// Route configurator 12 | /// 13 | public class RouteConfig 14 | { 15 | /// 16 | /// Registers routes with the 17 | /// 18 | /// The routes to configure 19 | public static void RegisterRoutes(RouteCollection routes) 20 | { 21 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 22 | 23 | routes.MapRoute( 24 | name: "Default", 25 | url: "{controller}/{action}/{id}", 26 | defaults: new { action = "Index", id = UrlParameter.Optional }); 27 | 28 | // routes.MapMvcAttributeRoutes(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/App_Start/Startup.Auth.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System; 8 | using global::Owin; 9 | using Microsoft.Owin.Security; 10 | using Microsoft.Owin.Security.Cookies; 11 | using Microsoft.Teams.Apps.Celebration.Controllers; 12 | 13 | /// 14 | /// OWIN startup class 15 | /// 16 | public partial class Startup 17 | { 18 | /// 19 | /// Configure authentication for the app 20 | /// 21 | /// App builder 22 | // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864 23 | public void ConfigureAuth(IAppBuilder app) 24 | { 25 | app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); 26 | 27 | app.UseCookieAuthentication(new CookieAuthenticationOptions 28 | { 29 | AuthenticationMode = AuthenticationMode.Active, 30 | AuthenticationType = CookieAuthenticationDefaults.AuthenticationType, 31 | LoginPath = new Owin.PathString(TabAuthController.LoginPath), 32 | ExpireTimeSpan = TimeSpan.FromDays(1), 33 | SlidingExpiration = true, 34 | Provider = new CookieAuthenticationProvider 35 | { 36 | OnApplyRedirect = (context) => 37 | { 38 | // Convert HTTP redirects to HTTPS 39 | var redirectUri = context.RedirectUri; 40 | if (!string.IsNullOrEmpty(redirectUri) && redirectUri.ToLowerInvariant().StartsWith("http://")) 41 | { 42 | redirectUri = "https://" + redirectUri.Substring(7); 43 | } 44 | 45 | context.Response.Redirect(redirectUri); 46 | }, 47 | }, 48 | }); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System.Web.Http; 8 | using Autofac.Integration.WebApi; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Newtonsoft.Json; 11 | using Newtonsoft.Json.Serialization; 12 | 13 | /// 14 | /// Web Api configurator 15 | /// 16 | public static class WebApiConfig 17 | { 18 | /// 19 | /// Register the routes and configuration settings. 20 | /// 21 | /// config. 22 | public static void Register(HttpConfiguration config) 23 | { 24 | // Json settings 25 | config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Include; 26 | config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; 27 | 28 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 29 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings() 30 | { 31 | Formatting = Formatting.Indented, 32 | NullValueHandling = NullValueHandling.Include, 33 | }; 34 | 35 | // Web API configuration and services 36 | config.DependencyResolver = new AutofacWebApiDependencyResolver(Conversation.Container); 37 | 38 | // Web API routes 39 | config.MapHttpAttributeRoutes(); 40 | 41 | config.Routes.MapHttpRoute( 42 | name: "DefaultApi", 43 | routeTemplate: "api/{controller}/{id}", 44 | defaults: new { id = RouteParameter.Optional }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/ApplicationConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | /// 8 | /// Application configuration keys 9 | /// 10 | public enum ApplicationConfig 11 | { 12 | /// 13 | /// Application base uri, without trailing slash 14 | /// 15 | BaseUrl, 16 | 17 | /// 18 | /// Cosmos DB endpoint url 19 | /// 20 | CosmosDBEndpointUrl, 21 | 22 | /// 23 | /// CosmosDB connection key 24 | /// 25 | CosmosDBKey, 26 | 27 | /// 28 | /// CosmosDB datbase name 29 | /// 30 | CosmosDBDatabaseName, 31 | 32 | /// 33 | /// Bot app id 34 | /// 35 | MicrosoftAppId, 36 | 37 | /// 38 | /// Bot app password 39 | /// 40 | MicrosoftAppPassword, 41 | 42 | /// 43 | /// Time to post the celebration in team 44 | /// 45 | TimeToPostCelebration, 46 | 47 | /// 48 | /// Maximum number of events per user 49 | /// 50 | MaxUserEventsCount, 51 | 52 | /// 53 | /// Number of days in advance to send the preview event notification 54 | /// 55 | DaysInAdvanceToSendEventPreview, 56 | 57 | /// 58 | /// Mininum time in hours to process event, otherwise the event will be skipped this year 59 | /// 60 | MinTimeToProcessEventInHours, 61 | 62 | /// 63 | /// Teams app ID 64 | /// 65 | ManifestAppId, 66 | } 67 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/CelebrationsAppModule.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System; 8 | using Autofac; 9 | using Microsoft.ApplicationInsights; 10 | using Microsoft.ApplicationInsights.Extensibility; 11 | using Microsoft.Bot.Builder.Azure; 12 | using Microsoft.Bot.Builder.Dialogs.Internals; 13 | using Microsoft.Bot.Builder.Internals.Fibers; 14 | using Microsoft.Bot.Connector; 15 | using Microsoft.Teams.Apps.Celebration.Dialog; 16 | using Microsoft.Teams.Apps.Celebration.Helpers; 17 | using Microsoft.Teams.Apps.Common; 18 | using Microsoft.Teams.Apps.Common.Authentication; 19 | using Microsoft.Teams.Apps.Common.Configuration; 20 | using Microsoft.Teams.Apps.Common.Logging; 21 | 22 | /// 23 | /// Autofac Module 24 | /// 25 | public class CelebrationsAppModule : Module 26 | { 27 | /// 28 | protected override void Load(ContainerBuilder builder) 29 | { 30 | base.Load(builder); 31 | 32 | var configProvider = new LocalConfigProvider(); 33 | builder.Register(c => configProvider) 34 | .Keyed(FiberModule.Key_DoNotSerialize) 35 | .AsImplementedInterfaces() 36 | .SingleInstance(); 37 | 38 | builder.Register(c => 39 | { 40 | return new TelemetryClient(new TelemetryConfiguration(configProvider.GetSetting(CommonConfig.ApplicationInsightsInstrumentationKey))); 41 | }).SingleInstance(); 42 | 43 | builder.RegisterType() 44 | .Keyed(FiberModule.Key_DoNotSerialize) 45 | .AsImplementedInterfaces() 46 | .SingleInstance(); 47 | builder.RegisterType() 48 | .Keyed(FiberModule.Key_DoNotSerialize) 49 | .AsImplementedInterfaces() 50 | .SingleInstance(); 51 | builder.RegisterType() 52 | .Keyed(FiberModule.Key_DoNotSerialize) 53 | .AsImplementedInterfaces() 54 | .SingleInstance(); 55 | 56 | // Override some Bot Framework registrations 57 | builder.Register(c => c.Resolve().MakeConnectorClient()) 58 | .Keyed(FiberModule.Key_DoNotSerialize) // Tag IConnectorClient as DoNotSerialize 59 | .As() 60 | .ExternallyOwned(); 61 | 62 | var appInsightsLogProvider = new AppInsightsLogProvider(configProvider); 63 | builder.Register(c => appInsightsLogProvider) 64 | .Keyed(FiberModule.Key_DoNotSerialize) 65 | .AsImplementedInterfaces() 66 | .SingleInstance(); 67 | 68 | builder.Register(c => new TokenValidator( 69 | configProvider.GetSetting(CommonConfig.ActiveDirectoryClientId))) 70 | .AsImplementedInterfaces() 71 | .SingleInstance(); 72 | 73 | // Use an in-memory store, as the bot does not currently use bot state storage. 74 | var store = new InMemoryDataStore(); 75 | builder.Register(c => store) 76 | .As>() 77 | .AsSelf() 78 | .SingleInstance(); 79 | 80 | // Register dialogs 81 | builder.RegisterType() 82 | .AsSelf() 83 | .InstancePerDependency(); 84 | 85 | builder.RegisterType() 86 | .Keyed(FiberModule.Key_DoNotSerialize) 87 | .AsSelf() 88 | .InstancePerMatchingLifetimeScope(DialogModule.LifetimeScopeTag); 89 | 90 | builder.RegisterType() 91 | .AsSelf() 92 | .InstancePerDependency(); 93 | 94 | builder.RegisterType() 95 | .AsSelf() 96 | .InstancePerDependency(); 97 | 98 | builder.RegisterType() 99 | .AsSelf() 100 | .InstancePerDependency(); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Connected Services/Application Insights/ConnectedService.json: -------------------------------------------------------------------------------- 1 | { 2 | "ProviderId": "Microsoft.ApplicationInsights.ConnectedService.ConnectedServiceProvider", 3 | "Version": "9.1.611.1" 4 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/css/datepicker-custom-theme.css: -------------------------------------------------------------------------------- 1 | /*default rules*/ 2 | .datepicker { 3 | height: 42%; 4 | width: 41%; 5 | font-size: small; 6 | box-shadow: 0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12) !important; 7 | } 8 | 9 | .datepicker .next { 10 | font-family: 'FontAwesome'; 11 | content: '\f104' !important; 12 | } 13 | 14 | .datepicker .prev { 15 | font-family: 'FontAwesome'; 16 | content: '\f105' !important; 17 | } 18 | 19 | .datepicker td.day { 20 | height:24px !important; 21 | } 22 | 23 | .datepicker .active { 24 | background: rgb(98, 100, 167) !important; 25 | } 26 | 27 | 28 | /*dark theme rules*/ 29 | .theme-dark .datepicker { 30 | background: #2d2c2c; 31 | } 32 | 33 | .theme-dark .datepicker::after { 34 | border-bottom: #2d2c2c !important; 35 | } 36 | 37 | .theme-dark .dow { 38 | color: #fff; 39 | } 40 | 41 | .theme-dark .year, 42 | .theme-dark .month, 43 | .theme-dark .day { 44 | color: #fff; 45 | } 46 | 47 | .theme-dark .old, 48 | .theme-dark .new { 49 | color: #000; 50 | } 51 | 52 | .theme-dark .datepicker-years span:hover, 53 | .theme-dark .datepicker-years span:focus, 54 | .theme-dark .datepicker-months span:hover, 55 | .theme-dark .datepicker-months span:focus, 56 | .theme-dark .datepicker-days td:hover, 57 | .theme-dark .datepicker-days td:focus { 58 | background: #484644 !important; 59 | cursor: pointer; 60 | } 61 | 62 | .theme-dark .active { 63 | background: rgb(98, 100, 167) !important; 64 | } 65 | 66 | .theme-dark .datepicker .switch, 67 | .theme-dark .datepicker .prev, 68 | .theme-dark .datepicker .next { 69 | color: #fff; 70 | background-color: #2d2c2c; 71 | border-radius: 1%; 72 | } 73 | 74 | .theme-dark .datepicker .switch:hover, 75 | .theme-dark .datepicker .prev:hover, 76 | .theme-dark .datepicker .next:hover { 77 | color: #fff !important; 78 | background-color: #484644 !important; 79 | } 80 | 81 | 82 | 83 | /*high contrast theme rules*/ 84 | .theme-highContrast .datepicker { 85 | background: #000; 86 | border: solid 1px #fff; 87 | } 88 | 89 | .theme-highContrast .datepicker::after { 90 | border-bottom: #000; 91 | 92 | } 93 | 94 | .theme-highContrast .dow { 95 | color: #fff; 96 | } 97 | 98 | .theme-highContrast .year, 99 | .theme-highContrast .month, 100 | .theme-highContrast .day { 101 | color: #fff; 102 | } 103 | 104 | .theme-highContrast .old, 105 | .theme-highContrast .new { 106 | color: #2d2c2c; 107 | } 108 | 109 | .theme-highContrast .datepicker-days td:hover, 110 | .theme-highContrast .datepicker-days td:focus, 111 | .theme-highContrast .datepicker-years span:hover, 112 | .theme-highContrast .datepicker-years span:focus, 113 | .theme-highContrast .datepicker-months span:hover, 114 | .theme-highContrast .datepicker-months span:focus { 115 | background-color: rgb(255, 255, 1) !important; 116 | color: #000; 117 | cursor: pointer; 118 | } 119 | 120 | .theme-highContrast .active { 121 | background: #1aebff !important; 122 | color: #000; 123 | } 124 | 125 | .theme-highContrast .datepicker .switch, 126 | .theme-highContrast .datepicker .prev, 127 | .theme-highContrast .datepicker .next { 128 | color: #fff; 129 | background-color: #000; 130 | border-radius: 5%; 131 | } 132 | 133 | .theme-highContrast .datepicker .switch:hover, 134 | .theme-highContrast .datepicker .prev:hover, 135 | .theme-highContrast .datepicker .next:hover { 136 | background-color: rgb(255, 255, 1) !important; 137 | color: #000; 138 | } 139 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/css/datepicker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Datepicker for Bootstrap 3 | * 4 | * Copyright 2012 Stefan Petre 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | */ 9 | .datepicker { 10 | top: 0; 11 | left: 0; 12 | padding: 4px; 13 | margin-top: 1px; 14 | -webkit-border-radius: 4px; 15 | -moz-border-radius: 4px; 16 | border-radius: 4px; 17 | /*.dow { 18 | border-top: 1px solid #ddd !important; 19 | }*/ 20 | 21 | } 22 | .datepicker:before { 23 | content: ''; 24 | display: inline-block; 25 | border-left: 7px solid transparent; 26 | border-right: 7px solid transparent; 27 | border-bottom: 7px solid #ccc; 28 | border-bottom-color: rgba(0, 0, 0, 0.2); 29 | position: absolute; 30 | top: -7px; 31 | left: 6px; 32 | } 33 | .datepicker:after { 34 | content: ''; 35 | display: inline-block; 36 | border-left: 6px solid transparent; 37 | border-right: 6px solid transparent; 38 | border-bottom: 6px solid #ffffff; 39 | position: absolute; 40 | top: -6px; 41 | left: 7px; 42 | } 43 | .datepicker > div { 44 | display: none; 45 | } 46 | .datepicker table { 47 | width: 100%; 48 | margin: 0; 49 | } 50 | .datepicker td, 51 | .datepicker th { 52 | text-align: center; 53 | width: 20px; 54 | height: 20px; 55 | -webkit-border-radius: 4px; 56 | -moz-border-radius: 4px; 57 | border-radius: 4px; 58 | } 59 | .datepicker td.day:hover { 60 | background: #eeeeee; 61 | cursor: pointer; 62 | } 63 | .datepicker td.day.disabled { 64 | color: #eeeeee; 65 | } 66 | .datepicker td.old, 67 | .datepicker td.new { 68 | color: #999999; 69 | } 70 | .datepicker td.active, 71 | .datepicker td.active:hover { 72 | color: #ffffff; 73 | background-color: #006dcc; 74 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 75 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 76 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 77 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 78 | background-image: linear-gradient(to bottom, #0088cc, #0044cc); 79 | background-repeat: repeat-x; 80 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); 81 | border-color: #0044cc #0044cc #002a80; 82 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 83 | *background-color: #0044cc; 84 | /* Darken IE7 buttons by default so they stand out more given they won't have borders */ 85 | 86 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 87 | color: #fff; 88 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 89 | } 90 | .datepicker td.active:hover, 91 | .datepicker td.active:hover:hover, 92 | .datepicker td.active:focus, 93 | .datepicker td.active:hover:focus, 94 | .datepicker td.active:active, 95 | .datepicker td.active:hover:active, 96 | .datepicker td.active.active, 97 | .datepicker td.active:hover.active, 98 | .datepicker td.active.disabled, 99 | .datepicker td.active:hover.disabled, 100 | .datepicker td.active[disabled], 101 | .datepicker td.active:hover[disabled] { 102 | color: #ffffff; 103 | background-color: #0044cc; 104 | *background-color: #003bb3; 105 | } 106 | .datepicker td.active:active, 107 | .datepicker td.active:hover:active, 108 | .datepicker td.active.active, 109 | .datepicker td.active:hover.active { 110 | background-color: #003399 \9; 111 | } 112 | .datepicker td span { 113 | display: block; 114 | width: 47px; 115 | height: 54px; 116 | line-height: 54px; 117 | float: left; 118 | margin: 2px; 119 | cursor: pointer; 120 | -webkit-border-radius: 4px; 121 | -moz-border-radius: 4px; 122 | border-radius: 4px; 123 | } 124 | .datepicker td span:hover { 125 | background: #eeeeee; 126 | } 127 | .datepicker td span.active { 128 | color: #ffffff; 129 | background-color: #006dcc; 130 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 131 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 132 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 133 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 134 | background-image: linear-gradient(to bottom, #0088cc, #0044cc); 135 | background-repeat: repeat-x; 136 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); 137 | border-color: #0044cc #0044cc #002a80; 138 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 139 | *background-color: #0044cc; 140 | /* Darken IE7 buttons by default so they stand out more given they won't have borders */ 141 | 142 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 143 | color: #fff; 144 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 145 | } 146 | .datepicker td span.active:hover, 147 | .datepicker td span.active:focus, 148 | .datepicker td span.active:active, 149 | .datepicker td span.active.active, 150 | .datepicker td span.active.disabled, 151 | .datepicker td span.active[disabled] { 152 | color: #ffffff; 153 | background-color: #0044cc; 154 | *background-color: #003bb3; 155 | } 156 | .datepicker td span.active:active, 157 | .datepicker td span.active.active { 158 | background-color: #003399 \9; 159 | } 160 | .datepicker td span.old { 161 | color: #999999; 162 | } 163 | .datepicker th.switch { 164 | width: 145px; 165 | } 166 | .datepicker th.next, 167 | .datepicker th.prev { 168 | font-size: 21px; 169 | } 170 | .datepicker thead tr:first-child th { 171 | cursor: pointer; 172 | } 173 | .datepicker thead tr:first-child th:hover { 174 | background: #eeeeee; 175 | } 176 | .input-append.date .add-on i, 177 | .input-prepend.date .add-on i { 178 | display: block; 179 | cursor: pointer; 180 | width: 16px; 181 | height: 16px; 182 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/css/spinner.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes translate-small { 2 | to { 3 | -webkit-transform: translate3d(0, -144rem, 0); 4 | transform: translate3d(0, -144rem, 0); 5 | } 6 | } 7 | 8 | @keyframes translate-small { 9 | to { 10 | -webkit-transform: translate3d(0, -144rem, 0); 11 | transform: translate3d(0, -144rem, 0); 12 | } 13 | } 14 | 15 | @-webkit-keyframes translate-medium { 16 | to { 17 | -webkit-transform: translate3d(0, -216rem, 0); 18 | transform: translate3d(0, -216rem, 0); 19 | } 20 | } 21 | 22 | @keyframes translate-medium { 23 | to { 24 | -webkit-transform: translate3d(0, -216rem, 0); 25 | transform: translate3d(0, -216rem, 0); 26 | } 27 | } 28 | 29 | @-webkit-keyframes translate-large { 30 | to { 31 | -webkit-transform: translate3d(0, -432rem, 0); 32 | transform: translate3d(0, -432rem, 0); 33 | } 34 | } 35 | 36 | @keyframes translate-large { 37 | to { 38 | -webkit-transform: translate3d(0, -432rem, 0); 39 | transform: translate3d(0, -432rem, 0); 40 | } 41 | } 42 | 43 | @-webkit-keyframes fade-spinner-in { 44 | to { 45 | opacity: 1; 46 | } 47 | } 48 | 49 | @keyframes fade-spinner-in { 50 | to { 51 | opacity: 1; 52 | } 53 | } 54 | 55 | busy-animation { 56 | display: inline-block; 57 | } 58 | 59 | .ts-spinner-internal { 60 | overflow: hidden; 61 | } 62 | 63 | .ts-spinner-internal.size-1x { 64 | height: 2.4rem; 65 | width: 2.4rem; 66 | } 67 | 68 | .ts-spinner-internal.size-1x .spinner-animate { 69 | width: 2.4rem; 70 | height: 146.4rem; 71 | -webkit-animation: 2s steps(60) both infinite translate-small; 72 | animation: 2s steps(60) both infinite translate-small; 73 | } 74 | 75 | .ts-spinner-internal.size-2x { 76 | height: 3.6rem; 77 | width: 3.6rem; 78 | margin: auto; 79 | } 80 | 81 | .ts-spinner-internal.size-2x .spinner-animate { 82 | width: 3.6rem; 83 | height: 219.6rem; 84 | -webkit-animation: 2s steps(60) both infinite translate-medium; 85 | animation: 2s steps(60) both infinite translate-medium; 86 | } 87 | 88 | .ts-spinner-internal.size-3x { 89 | height: 7.2rem; 90 | width: 7.2rem; 91 | } 92 | 93 | .ts-spinner-internal.size-3x .spinner-animate { 94 | width: 7.2rem; 95 | height: 439.2rem; 96 | -webkit-animation: 2s steps(60) both infinite translate-large; 97 | animation: 2s steps(60) both infinite translate-large; 98 | } 99 | 100 | .ts-spinner-container { 101 | overflow: hidden; 102 | position: relative; 103 | } 104 | 105 | .loading { 106 | position: fixed; 107 | z-index: 999; 108 | height: 2em; 109 | width: 2em; 110 | overflow: visible; 111 | margin: auto; 112 | top: 0; 113 | left: 0; 114 | bottom: 0; 115 | right: 0; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/Celebrations-bot-image-new-event.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/Celebrations-bot-image-new-event.jpg -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/0.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/1.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/10.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/2.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/3.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/4.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/5.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/6.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/7.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/8.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/carousel/9.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/celebration_bot_full-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/celebration_bot_full-color.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/empty-state-celebrations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/empty-state-celebrations.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/welcome-card-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/welcome-card-image.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Content/images/welcome-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-celebrations/a65cb401bee628549fc54b96573206705e6ef731/Source/Microsoft.Teams.Apps.Celebration/Content/images/welcome-dialog.png -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Controllers/AuthController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Controllers 6 | { 7 | using System; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Net.Http.Headers; 11 | using System.Security.Claims; 12 | using System.Threading.Tasks; 13 | using System.Web; 14 | using System.Web.Mvc; 15 | using Microsoft.Owin.Security.Cookies; 16 | using Microsoft.Teams.Apps.Common.Authentication; 17 | 18 | /// 19 | /// Authentication controller 20 | /// 21 | public class AuthController : Controller 22 | { 23 | /// 24 | /// Path to the login endpoint 25 | /// 26 | public const string TokenLoginEndpointPath = "/Auth/TokenLogin"; 27 | 28 | private readonly ITokenValidator tokenValidator; 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// The token validator 34 | public AuthController(ITokenValidator tokenValidator) 35 | { 36 | this.tokenValidator = tokenValidator; 37 | } 38 | 39 | /// 40 | /// Action to login using AAD ID token 41 | /// 42 | /// HTTP result 43 | // POST: TokenLogin 44 | [AllowAnonymous] 45 | [HttpPost] 46 | public async Task TokenLogin() 47 | { 48 | var headerString = this.Request.Headers?.GetValues("Authorization")?.FirstOrDefault(); 49 | if (string.IsNullOrEmpty(headerString)) 50 | { 51 | return new HttpStatusCodeResult(HttpStatusCode.Unauthorized); 52 | } 53 | 54 | try 55 | { 56 | var headerValue = AuthenticationHeaderValue.Parse(headerString); 57 | var claimsPrincipal = await this.tokenValidator.ValidateIdTokenAsync(headerValue.Parameter); 58 | var firstIdentity = claimsPrincipal.Identities.FirstOrDefault(); 59 | 60 | var context = this.Request.GetOwinContext(); 61 | context.Authentication.SignIn(new ClaimsIdentity(firstIdentity.Claims, CookieAuthenticationDefaults.AuthenticationType)); 62 | 63 | return new HttpStatusCodeResult(HttpStatusCode.OK); 64 | } 65 | catch (Exception) 66 | { 67 | return new HttpStatusCodeResult(HttpStatusCode.Unauthorized); 68 | } 69 | } 70 | 71 | /// 72 | /// Logout. 73 | /// 74 | /// HTTP result 75 | // GET: Logout 76 | [HttpGet] 77 | public ActionResult Logout() 78 | { 79 | var context = this.Request.GetOwinContext(); 80 | context.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType); 81 | 82 | return new HttpStatusCodeResult(HttpStatusCode.OK); 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Controllers/SharedSecretAuthenticationAttribute.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Controllers 6 | { 7 | using System; 8 | using System.Configuration; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using System.Web.Http.Controllers; 14 | using System.Web.Http.Filters; 15 | 16 | /// 17 | /// Action filter that authenticates an incoming request against a shared secret 18 | /// 19 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] 20 | public class SharedSecretAuthenticationAttribute : ActionFilterAttribute 21 | { 22 | /// 23 | /// Gets or sets the name of the setting that has the shared secret 24 | /// 25 | public string SharedSecretSettingName { get; set; } 26 | 27 | /// 28 | public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) 29 | { 30 | var authHeader = actionContext.Request.Headers.Authorization; 31 | if (authHeader == null || 32 | authHeader.Scheme != "SharedSecret" || 33 | authHeader.Parameter != ConfigurationManager.AppSettings[this.SharedSecretSettingName]) 34 | { 35 | actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); 36 | return Task.CompletedTask; 37 | } 38 | 39 | return base.OnActionExecutingAsync(actionContext, cancellationToken); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Controllers/TabAuthController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Controllers 6 | { 7 | using System.Web.Mvc; 8 | using Microsoft.Teams.Apps.Common; 9 | using Microsoft.Teams.Apps.Common.Configuration; 10 | 11 | /// 12 | /// Tab authentication controller 13 | /// 14 | public class TabAuthController : Controller 15 | { 16 | /// 17 | /// Path to the tab login page 18 | /// 19 | public const string LoginPath = "/TabAuth/Login"; 20 | 21 | private const string StartPath = "/TabAuth/Start"; 22 | private const string RedirectUriPath = "/TabAuth/Callback"; 23 | 24 | private readonly string tenantId; 25 | private readonly string clientId; 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// The configuration provider to use 31 | public TabAuthController(IConfigProvider configProvider) 32 | { 33 | this.tenantId = configProvider.GetSetting(CommonConfig.ActiveDirectoryTenant); 34 | this.clientId = configProvider.GetSetting(CommonConfig.ActiveDirectoryClientId); 35 | } 36 | 37 | /// 38 | /// Tab authentication login page 39 | /// 40 | /// Destination path 41 | /// View 42 | // GET: Login 43 | [AllowAnonymous] 44 | public ActionResult Login(string returnUrl) 45 | { 46 | this.ViewBag.TenantId = this.tenantId; 47 | this.ViewBag.ClientId = this.clientId; 48 | this.ViewBag.StartPath = StartPath; 49 | this.ViewBag.RedirectUriPath = RedirectUriPath; 50 | this.ViewBag.DestinationPath = returnUrl; 51 | this.ViewBag.TokenLoginEndpointPath = AuthController.TokenLoginEndpointPath; 52 | return this.View(); 53 | } 54 | 55 | /// 56 | /// Tab authentication flow start page 57 | /// 58 | /// View 59 | // GET: Start 60 | [AllowAnonymous] 61 | public ActionResult Start() 62 | { 63 | this.ViewBag.TenantId = this.tenantId; 64 | this.ViewBag.ClientId = this.clientId; 65 | this.ViewBag.RedirectUriPath = RedirectUriPath; 66 | return this.View(); 67 | } 68 | 69 | /// 70 | /// Tab authentication flow callback page 71 | /// 72 | /// View 73 | // GET: Callback 74 | [AllowAnonymous] 75 | public ActionResult Callback() 76 | { 77 | this.ViewBag.TenantId = this.tenantId; 78 | this.ViewBag.ClientId = this.clientId; 79 | return this.View(); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Dialog/DialogFactory.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Dialog 6 | { 7 | using System; 8 | using Autofac; 9 | using Microsoft.Bot.Builder.Internals.Fibers; 10 | 11 | /// 12 | /// Factory to create dialogs 13 | /// 14 | [Serializable] 15 | public class DialogFactory 16 | { 17 | private readonly IComponentContext scope; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// Lifetime scope 23 | public DialogFactory(IComponentContext scope) 24 | { 25 | SetField.NotNull(out this.scope, nameof(scope), scope); 26 | } 27 | 28 | /// 29 | /// Create a new instance of . 30 | /// 31 | /// New dialog instance 32 | public SkipEventDialog CreateSkipEventDialog() 33 | { 34 | return this.scope.Resolve(); 35 | } 36 | 37 | /// 38 | /// Create a new instance of . 39 | /// 40 | /// New dialog instance 41 | public ShareEventDialog CreateShareEventDialog() 42 | { 43 | return this.scope.Resolve(); 44 | } 45 | 46 | /// 47 | /// Create a new instance of . 48 | /// 49 | /// New dialog instance 50 | public IgnoreEventShareDialog CreateIgnoreEventShareDialog() 51 | { 52 | return this.scope.Resolve(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Dialog/IgnoreEventShareDialog.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Dialog 6 | { 7 | using System; 8 | using System.Threading.Tasks; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Connector; 11 | using Microsoft.Teams.Apps.Celebration.Helpers; 12 | using Microsoft.Teams.Apps.Celebration.Models; 13 | using Microsoft.Teams.Apps.Celebration.Resources; 14 | using Microsoft.Teams.Apps.Common.Logging; 15 | using Newtonsoft.Json.Linq; 16 | 17 | /// 18 | /// Ignore Event Sharing in team 19 | /// 20 | [Serializable] 21 | public class IgnoreEventShareDialog : IDialog 22 | { 23 | private readonly IConnectorClient connectorClient; 24 | private readonly ILogProvider logProvider; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// Connector client 30 | /// Logging component 31 | public IgnoreEventShareDialog(IConnectorClient connectorClient, ILogProvider logProvider) 32 | { 33 | this.connectorClient = connectorClient; 34 | this.logProvider = logProvider; 35 | } 36 | 37 | /// 38 | /// The start of the code that represents the conversational dialog 39 | /// 40 | /// The dialog context 41 | /// A task that represents the dialog start 42 | public Task StartAsync(IDialogContext context) 43 | { 44 | context.Wait(this.HandleIgnoreEventSharingAction); 45 | return Task.CompletedTask; 46 | } 47 | 48 | /// 49 | /// Handles Ignore event sharing action 50 | /// 51 | /// IDialogContext object 52 | /// IAwaitable message activity 53 | /// Task. 54 | public async Task HandleIgnoreEventSharingAction(IDialogContext context, IAwaitable activity) 55 | { 56 | var message = (Activity)await activity; 57 | 58 | if (message.Value != null) 59 | { 60 | var replyMessage = Strings.IgnoreEventShare; 61 | ShareEventPayload shareEventPayload = ((JObject)message.Value).ToObject(); 62 | 63 | // Update the card 64 | IMessageActivity updatedMessage = context.MakeMessage(); 65 | updatedMessage.Attachments.Add(CelebrationCard.GetShareEventAttachmentWithoutActionButton(shareEventPayload.TeamName)); 66 | updatedMessage.ReplyToId = message.ReplyToId; 67 | 68 | await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage); 69 | 70 | await context.PostAsync(replyMessage); 71 | context.Done(null); 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Dialog/RootDialog.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Dialog 6 | { 7 | using System; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Microsoft.Bot.Builder.Dialogs; 11 | using Microsoft.Bot.Connector; 12 | using Microsoft.Teams.Apps.Celebration.Models; 13 | using Newtonsoft.Json.Linq; 14 | 15 | /// 16 | /// Root Dialog 17 | /// 18 | [Serializable] 19 | public class RootDialog : IDialog 20 | { 21 | private static DialogFactory dialogFactory; 22 | 23 | /// 24 | /// Initializes a new instance of the class 25 | /// 26 | /// DialogFactory instance 27 | public RootDialog(DialogFactory dialogFactoryInstance) 28 | { 29 | dialogFactory = dialogFactoryInstance; 30 | } 31 | 32 | /// 33 | /// Handle the card actions 34 | /// 35 | /// IDialogContext object 36 | /// IAwaitable message activity 37 | /// Task. 38 | public async Task HandleCardActions(IDialogContext context, IAwaitable activity) 39 | { 40 | var message = (Activity)await activity; 41 | if (message?.Value != null) 42 | { 43 | var payload = ((JObject)message.Value).ToObject(); 44 | 45 | switch (payload.Action) 46 | { 47 | case "SkipEvent": 48 | await context.Forward(dialogFactory.CreateSkipEventDialog(), this.ResumeAfterCompletion, message, CancellationToken.None); 49 | break; 50 | case "ShareEvent": 51 | await context.Forward(dialogFactory.CreateShareEventDialog(), this.ResumeAfterCompletion, message, CancellationToken.None); 52 | break; 53 | case "IgnoreEventShare": 54 | await context.Forward(dialogFactory.CreateIgnoreEventShareDialog(), this.ResumeAfterCompletion, message, CancellationToken.None); 55 | break; 56 | } 57 | } 58 | else 59 | { 60 | context.Done(null); 61 | } 62 | } 63 | 64 | /// 65 | public Task StartAsync(IDialogContext context) 66 | { 67 | context.Wait(this.HandleCardActions); 68 | return Task.CompletedTask; 69 | } 70 | 71 | /// 72 | /// Handle the actions after completion of child dialog 73 | /// 74 | /// IDialogContext 75 | /// result 76 | /// A representing the asynchronous operation 77 | private Task ResumeAfterCompletion(IDialogContext context, IAwaitable result) 78 | { 79 | context.Wait(this.HandleCardActions); 80 | return Task.CompletedTask; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Dialog/SkipEventDialog.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Dialog 6 | { 7 | using System; 8 | using System.Threading.Tasks; 9 | using Microsoft.Bot.Builder.Dialogs; 10 | using Microsoft.Bot.Connector; 11 | using Microsoft.Teams.Apps.Celebration.Helpers; 12 | using Microsoft.Teams.Apps.Celebration.Models; 13 | using Microsoft.Teams.Apps.Celebration.Resources; 14 | using Microsoft.Teams.Apps.Common.Extensions; 15 | using Microsoft.Teams.Apps.Common.Logging; 16 | using Newtonsoft.Json.Linq; 17 | 18 | /// 19 | /// Handle the event skip action 20 | /// 21 | [Serializable] 22 | public class SkipEventDialog : IDialog 23 | { 24 | private readonly IEventDataProvider eventDataProvider; 25 | private readonly IConnectorClient connectorClient; 26 | private readonly ILogProvider logProvider; 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// Connector client 32 | /// Event data provider instance 33 | /// Logging component 34 | public SkipEventDialog(IConnectorClient connectorClient, IEventDataProvider eventDataProvider, ILogProvider logProvider) 35 | { 36 | this.connectorClient = connectorClient; 37 | this.eventDataProvider = eventDataProvider; 38 | this.logProvider = logProvider; 39 | } 40 | 41 | /// 42 | public Task StartAsync(IDialogContext context) 43 | { 44 | context.Wait(this.OnMessageReceivedAsync); 45 | return Task.CompletedTask; 46 | } 47 | 48 | // Handle the incoming message 49 | private async Task OnMessageReceivedAsync(IDialogContext context, IAwaitable activity) 50 | { 51 | var message = await activity; 52 | await this.HandleSkipEventAsync(context, message); 53 | 54 | // Ensure that we exit from the dialog 55 | context.Done(null); 56 | } 57 | 58 | // Handles event skip action 59 | private async Task HandleSkipEventAsync(IDialogContext context, IMessageActivity message) 60 | { 61 | var payload = ((JObject)message?.Value)?.ToObject(); 62 | if (payload == null) 63 | { 64 | this.logProvider.LogInfo("Received message with no payload"); 65 | return; 66 | } 67 | 68 | // Get the event 69 | var celebrationEvent = await this.eventDataProvider.GetEventByIdAsync(payload.EventId, payload.OwnerAadObjectId); 70 | if (celebrationEvent == null) 71 | { 72 | this.logProvider.LogInfo($"Could not find event {payload.EventId} for user {payload.OwnerAadObjectId}"); 73 | await context.PostAsync(Strings.EventDoesNotExistMessage); 74 | return; 75 | } 76 | 77 | // Get the occurrence 78 | var occurrence = await this.eventDataProvider.GetEventOccurrenceByIdAsync(payload.OccurrenceId, payload.EventId); 79 | if (occurrence == null) 80 | { 81 | this.logProvider.LogInfo($"Could not find occurrence {payload.OccurrenceId} of event {payload.EventId} for user {payload.OwnerAadObjectId} (likely out of date card)"); 82 | await context.PostAsync(Strings.SkippedStaleEventMessage); 83 | return; 84 | } 85 | 86 | // Check that the occurrence is still in the future 87 | if (occurrence.DateTime < DateTimeOffset.UtcNow) 88 | { 89 | await context.PostAsync(Strings.EventPassedMessage); 90 | return; 91 | } 92 | 93 | // Mark the occurrence as skipped 94 | occurrence.Status = EventStatus.Skipped; 95 | await this.eventDataProvider.UpdateEventOccurrenceAsync(occurrence); 96 | 97 | // Update the card 98 | var updatedMessage = context.MakeMessage(); 99 | updatedMessage.Attachments.Add(CelebrationCard.GetPreviewCard(celebrationEvent, payload.OccurrenceId, payload.OwnerName, false).ToAttachment()); 100 | await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage); 101 | 102 | await context.PostAsync(string.Format(Strings.EventSkippedMessage, celebrationEvent.Title)); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/ErrorHandler/AiHandleErrorAttribute.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.ErrorHandler { 6 | using System; 7 | using System.Web.Mvc; 8 | using Microsoft.ApplicationInsights; 9 | 10 | /// 11 | /// Application Insights error logger 12 | /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class AiHandleErrorAttribute : HandleErrorAttribute { /// 13 | public override void OnException(ExceptionContext filterContext) { if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null) { // If customError is Off, then AI HTTPModule will report the exception if (filterContext.HttpContext.IsCustomErrorEnabled) { var ai = new TelemetryClient(); ai.TrackException(filterContext.Exception); } } base.OnException(filterContext); } } } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.Teams.Apps.Celebration.Global" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Global.asax.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using System.Reflection; 8 | using System.Web.Http; 9 | using System.Web.Mvc; 10 | using System.Web.Optimization; 11 | using System.Web.Routing; 12 | using Autofac; 13 | using Autofac.Integration.Mvc; 14 | using Autofac.Integration.WebApi; 15 | using Microsoft.ApplicationInsights.Extensibility; 16 | using Microsoft.Bot.Builder.Azure; 17 | using Microsoft.Bot.Builder.Dialogs; 18 | using Microsoft.Teams.Apps.Common; 19 | using Microsoft.Teams.Apps.Common.Configuration; 20 | 21 | /// 22 | /// Define the events and data accessed globally in application. 23 | /// 24 | public class Global : System.Web.HttpApplication 25 | { 26 | /// 27 | /// Application start event. 28 | /// 29 | protected void Application_Start() 30 | { 31 | // Set Application Insights instrumentation key 32 | var configProvider = new LocalConfigProvider(); 33 | TelemetryConfiguration.Active.InstrumentationKey = configProvider.GetSetting(CommonConfig.ApplicationInsightsInstrumentationKey); 34 | 35 | Conversation.UpdateContainer( 36 | builder => 37 | { 38 | builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly())); 39 | builder.RegisterModule(new CelebrationsAppModule()); 40 | 41 | builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); 42 | builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration); 43 | 44 | builder.RegisterControllers(Assembly.GetExecutingAssembly()); 45 | }); 46 | 47 | GlobalConfiguration.Configure(WebApiConfig.Register); 48 | RouteConfig.RegisterRoutes(RouteTable.Routes); 49 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 50 | BundleConfig.RegisterBundles(BundleTable.Bundles); 51 | 52 | DependencyResolver.SetResolver(new AutofacDependencyResolver(Conversation.Container)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/AdaptiveCardExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using AdaptiveCards; 8 | using Microsoft.Bot.Connector; 9 | 10 | /// 11 | /// Store extension method for Adaptive card 12 | /// 13 | public static class AdaptiveCardExtensions 14 | { 15 | /// 16 | /// AdaptiveCard instance 17 | /// 18 | /// Adaptive card 19 | /// Attachment. 20 | public static Attachment ToAttachment(this AdaptiveCard card) 21 | { 22 | return new Attachment 23 | { 24 | ContentType = AdaptiveCard.ContentType, 25 | Content = card, 26 | }; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using System; 8 | using System.Configuration; 9 | 10 | /// 11 | /// Store Application settings 12 | /// 13 | public static class ApplicationSettings 14 | { 15 | /// 16 | /// Initializes static members of the class. 17 | /// 18 | static ApplicationSettings() 19 | { 20 | BaseUrl = ConfigurationManager.AppSettings[nameof(ApplicationConfig.BaseUrl)]; 21 | ManifestAppId = ConfigurationManager.AppSettings[nameof(ApplicationConfig.ManifestAppId)]; 22 | } 23 | 24 | /// 25 | /// Gets or sets base url 26 | /// 27 | public static string BaseUrl { get; set; } 28 | 29 | /// 30 | /// Gets manifest id which is Guid 31 | /// 32 | public static string ManifestAppId { get; } 33 | } 34 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/Common.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using Microsoft.Bot.Connector; 11 | using Microsoft.Teams.Apps.Celebration.Models; 12 | using Newtonsoft.Json; 13 | 14 | /// 15 | /// Contains common static methods used in project 16 | /// 17 | public static class Common 18 | { 19 | /// 20 | /// Entity ID of the events tab 21 | /// 22 | public const string EventsTabEntityId = "EventsTab"; 23 | 24 | /// 25 | /// Count no. of files in a directory 26 | /// 27 | /// directory path 28 | /// file count. 29 | public static int GetCountOfFilesInDirectory(string directoryPath) 30 | { 31 | return Directory.Exists(directoryPath) ? Directory.GetFiles(directoryPath).Length : 0; 32 | } 33 | 34 | /// 35 | /// Returns a list of TimeZoneDisplayInfo 36 | /// 37 | /// List of TimeZoneDisplayInfo 38 | public static IList GetTimeZoneList() 39 | { 40 | var timeZonelist = new List(); 41 | foreach (TimeZoneInfo info in TimeZoneInfo.GetSystemTimeZones()) 42 | { 43 | timeZonelist.Add(new TimeZoneDisplayInfo { TimeZoneDisplayName = info.DisplayName, TimeZoneId = info.Id }); 44 | } 45 | 46 | return timeZonelist; 47 | } 48 | 49 | /// 50 | /// Get absolute URL of image from relative path 51 | /// 52 | /// Image relative path 53 | /// image URL 54 | public static string GetImageUrlFromPath(string imageRelativePath) 55 | { 56 | return ApplicationSettings.BaseUrl + imageRelativePath; 57 | } 58 | 59 | /// 60 | /// Get absolute URL of image from file name 61 | /// 62 | /// Image name 63 | /// image URL 64 | public static string GetImageUrlFromName(string imageName) 65 | { 66 | return ApplicationSettings.BaseUrl + "/Content/images/" + imageName; 67 | } 68 | 69 | /// 70 | /// Construct deeplink URL to the Events tab 71 | /// 72 | /// optional sub entity id 73 | /// Deep link URL 74 | public static Uri GetDeeplinkToEventsTab(string subEntityId = null) 75 | { 76 | string context; 77 | if (!string.IsNullOrEmpty(subEntityId)) 78 | { 79 | var contextObject = new 80 | { 81 | subEntityId, 82 | }; 83 | context = "context=" + Uri.EscapeDataString(JsonConvert.SerializeObject(contextObject)); 84 | } 85 | else 86 | { 87 | context = string.Empty; 88 | } 89 | 90 | return new Uri(string.Format( 91 | "https://teams.microsoft.com/l/entity/{0}/{1}?{2}", 92 | ApplicationSettings.ManifestAppId, 93 | EventsTabEntityId, 94 | context)); 95 | } 96 | 97 | /// 98 | /// Get the next occurrence of the event that is after the given date. 99 | /// 100 | /// The month and day on which the event occurs every year. The time component is set to the time that it is supposed to occur. 101 | /// The occurrence calculated must be after this date and time 102 | /// The date of the next occurrence of the event that happens after . 103 | public static DateTime GetNextOccurrenceAfterDateTime(DateTime recurrenceReferenceDate, DateTime afterDateTime) 104 | { 105 | int differenceInYears = afterDateTime.Date.Year - recurrenceReferenceDate.Year; 106 | 107 | // If the occurrence is before the given "afterDate", add 1 year 108 | if (recurrenceReferenceDate.AddYears(differenceInYears) <= afterDateTime) 109 | { 110 | differenceInYears += 1; 111 | } 112 | 113 | return recurrenceReferenceDate.AddYears(differenceInYears); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/ConnectorClientFactory.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using System; 8 | using System.Collections.Concurrent; 9 | using Microsoft.Bot.Connector; 10 | 11 | /// 12 | /// Factory for 13 | /// 14 | public class ConnectorClientFactory : IConnectorClientFactory 15 | { 16 | private readonly ConcurrentDictionary clientsCache = new ConcurrentDictionary(); 17 | 18 | /// 19 | /// Returns the connector client to use for the specified service URL. 20 | /// 21 | /// The service URL 22 | /// The connector client instance to use 23 | public IConnectorClient GetConnectorClient(string serviceUrl) 24 | { 25 | MicrosoftAppCredentials.TrustServiceUrl(serviceUrl); 26 | return this.clientsCache.GetOrAdd(serviceUrl, (url) => 27 | { 28 | return new ConnectorClient(new Uri(url)); 29 | }); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/DocumentQueryExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | using Microsoft.Azure.Documents.Linq; 10 | 11 | /// 12 | /// stores extension methods for IDocumentQuery 13 | /// 14 | public static class DocumentQueryExtensions 15 | { 16 | /// 17 | /// Create and returns the list in asynchronously. 18 | /// 19 | /// Type 20 | /// Queryable 21 | /// A representing the asynchronous operation 22 | public static async Task> ToListAsync(this IDocumentQuery queryable) 23 | { 24 | var list = new List(); 25 | 26 | while (queryable.HasMoreResults) 27 | { 28 | list.AddRange(await queryable.ExecuteNextAsync()); 29 | } 30 | 31 | return list; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/IConnectorClientFactory.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using Microsoft.Bot.Connector; 8 | 9 | /// 10 | /// Factory for 11 | /// 12 | public interface IConnectorClientFactory 13 | { 14 | /// 15 | /// Returns the connector client to use for the specified service URL. 16 | /// 17 | /// The service URL 18 | /// The connector client instance to use 19 | IConnectorClient GetConnectorClient(string serviceUrl); 20 | } 21 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Helpers/IUserManagementHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Helpers 6 | { 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | using Microsoft.Teams.Apps.Celebration.Models; 10 | 11 | /// 12 | /// Stores methods to perform the crud operation in document DB 13 | /// 14 | public interface IUserManagementHelper 15 | { 16 | /// 17 | /// Get TeamDetails by team Id 18 | /// 19 | /// TeamId 20 | /// A representing the asynchronous operation 21 | Task GetTeamsDetailsByTeamIdAsync(string teamId); 22 | 23 | /// 24 | /// Get TeamDetails by list of team Ids 25 | /// 26 | /// List of TeamId 27 | /// A representing the asynchronous operation 28 | Task> GetTeamsDetailsByTeamIdsAsync(List teamIds); 29 | 30 | /// 31 | /// Save Team Details 32 | /// 33 | /// Team instance 34 | /// A representing the asynchronous operation 35 | Task SaveTeamDetailsAsync(Team team); 36 | 37 | /// 38 | /// Delete Team Detail 39 | /// 40 | /// TeamId 41 | /// A representing the asynchronous operation 42 | Task DeleteTeamDetailsAsync(string teamId); 43 | 44 | /// 45 | /// Get User by aadObjectId 46 | /// 47 | /// AadObjectId of user 48 | /// A representing the asynchronous operation 49 | Task GetUserByAadObjectIdAsync(string aadObjectId); 50 | 51 | /// 52 | /// Add or update user 53 | /// 54 | /// User object 55 | /// A representing the asynchronous operation 56 | Task SaveUserAsync(Models.User user); 57 | 58 | /// 59 | /// Delete user 60 | /// 61 | /// User teams Id 62 | /// A representing the asynchronous operation 63 | Task DeleteUserByTeamsIdAsync(string userTeamsId); 64 | 65 | /// 66 | /// Add record in UserTeamMembership collection 67 | /// 68 | /// UserTeamMembership object 69 | /// A representing the asynchronous operation 70 | Task AddUserTeamMembershipAsync(UserTeamMembership userTeamMembership); 71 | 72 | /// 73 | /// Delete UserTeamMembership record 74 | /// 75 | /// User's teamsId 76 | /// TeamId 77 | /// A representing the asynchronous operation 78 | Task DeleteUserTeamMembershipAsync(string userTeamsId, string teamId); 79 | 80 | /// 81 | /// Delete UserTeamMembership record 82 | /// 83 | /// TeamId 84 | /// A representing the asynchronous operation 85 | Task DeleteUserTeamMembershipByTeamIdAsync(string teamId); 86 | 87 | /// 88 | /// Returns UserTeamMembership list 89 | /// 90 | /// TeamId 91 | /// A representing the asynchronous operation 92 | Task> GetUserTeamMembershipByTeamIdAsync(string teamId); 93 | 94 | /// 95 | /// Returns UserTeamMembership list 96 | /// 97 | /// User's teamId 98 | /// A representing the asynchronous operation 99 | Task> GetUserTeamMembershipByTeamsIdAsync(string userTeamsId); 100 | } 101 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/BotScope.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | /// 8 | /// Defines Kind of conversation 9 | /// 10 | public enum BotScope 11 | { 12 | /// 13 | /// Represents conversation between a bot and a single user 14 | /// 15 | Personal, 16 | 17 | /// 18 | /// Represents channel conversation 19 | /// 20 | Team, 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/CelebrationEvent.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel.DataAnnotations; 10 | using Microsoft.Azure.Documents; 11 | using Newtonsoft.Json; 12 | 13 | /// 14 | /// Represent event data 15 | /// 16 | public class CelebrationEvent : Resource 17 | { 18 | /// 19 | /// Gets or sets type of event. Birthday/Anniversary/others 20 | /// 21 | [JsonProperty("type")] 22 | [Required] 23 | public EventTypes Type { get; set; } 24 | 25 | /// 26 | /// Gets or sets event title 27 | /// 28 | [JsonProperty("title")] 29 | [Required] 30 | public string Title { get; set; } 31 | 32 | /// 33 | /// Gets or sets message to post 34 | /// 35 | [JsonProperty("message")] 36 | public string Message { get; set; } 37 | 38 | /// 39 | /// Gets or Sets event Date 40 | /// 41 | [JsonProperty("date")] 42 | [Required] 43 | public DateTime Date { get; set; } 44 | 45 | /// 46 | /// Gets or sets timezone id given by TimeZoneInfo.Id 47 | /// 48 | [JsonProperty("timeZoneId")] 49 | [Required] 50 | public string TimeZoneId { get; set; } 51 | 52 | /// 53 | /// Gets or sets owner teamsId of event 54 | /// 55 | [JsonProperty("OwnerId")] 56 | public string OwnerTeamsId { get; set; } 57 | 58 | /// 59 | /// Gets or sets user AAD object id 60 | /// 61 | [JsonProperty("ownerAadObjectId")] 62 | public string OwnerAadObjectId { get; set; } 63 | 64 | /// 65 | /// Gets or sets image URL for event 66 | /// 67 | [JsonProperty("imageURL")] 68 | public string ImageUrl { get; set; } 69 | 70 | /// 71 | /// Gets month part of the event date 72 | /// 73 | [JsonProperty("eventMonth")] 74 | public int EventMonth 75 | { 76 | get { return this.Date.Month; } 77 | } 78 | 79 | /// 80 | /// Gets day part of the event date 81 | /// 82 | [JsonProperty("eventDay")] 83 | public int EventDay 84 | { 85 | get { return this.Date.Day; } 86 | } 87 | 88 | /// 89 | /// Gets or sets list of team information where bot is installed 90 | /// 91 | [JsonProperty("teams")] 92 | public List Teams { get; set; } 93 | } 94 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/EventNotificationData.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using Microsoft.Bot.Connector; 8 | using Microsoft.Teams.Apps.Celebration.Helpers; 9 | using Microsoft.Teams.Apps.Celebration.Resources; 10 | 11 | /// 12 | /// Represents the data needed to send an event notification card. 13 | /// 14 | public class EventNotificationData 15 | { 16 | /// 17 | /// Gets or sets the event occurrence 18 | /// 19 | public EventOccurrence Occurrence { get; set; } 20 | 21 | /// 22 | /// Gets or sets the event information 23 | /// 24 | public CelebrationEvent Event { get; set; } 25 | 26 | /// 27 | /// Gets or sets the owning user 28 | /// 29 | public User User { get; set; } 30 | 31 | /// 32 | /// Gets the message to send 33 | /// 34 | /// The message to send 35 | public string GetMessage() 36 | { 37 | return string.Format(Strings.SingleEventMessage, this.User.DisplayName, this.Event.Title); 38 | } 39 | 40 | /// 41 | /// Gets the card to send 42 | /// 43 | /// The card to send 44 | public Attachment GetCard() 45 | { 46 | return CelebrationCard.GetEventCard(this.Event, this.User.DisplayName).ToAttachment(); 47 | } 48 | 49 | /// 50 | /// Gets the mention for the user. 51 | /// 52 | /// The mention entity 53 | public Mention GetMention() 54 | { 55 | return new Mention 56 | { 57 | Text = $"{this.User.DisplayName}", 58 | Mentioned = new ChannelAccount() 59 | { 60 | Name = this.User.DisplayName, 61 | Id = this.User.TeamsId, 62 | }, 63 | }; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/EventOccurrence.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using System; 8 | using System.ComponentModel; 9 | using Microsoft.Azure.Documents; 10 | using Newtonsoft.Json; 11 | 12 | /// 13 | /// Represents the upcoming occurrences of a recurring event 14 | /// 15 | public class EventOccurrence : Resource 16 | { 17 | /// 18 | /// Gets or sets id, which is Id(Guid) in events collection 19 | /// 20 | [JsonProperty("eventId")] 21 | public string EventId { get; set; } 22 | 23 | /// 24 | /// Gets or sets the owner AAD object ID 25 | /// 26 | [JsonProperty("ownerAadObjectId")] 27 | public string OwnerAadObjectId { get; set; } 28 | 29 | /// 30 | /// Gets or sets UTC date and time of upcoming occurrence 31 | /// 32 | [JsonProperty("date")] 33 | public DateTimeOffset DateTime { get; set; } 34 | 35 | /// 36 | /// Gets or sets event's Status 37 | /// 38 | [JsonProperty("status")] 39 | [DefaultValue(EventStatus.Default)] 40 | public EventStatus Status { get; set; } 41 | 42 | /// 43 | /// Gets or sets the time to live in seconds. See https://docs.microsoft.com/en-us/azure/cosmos-db/time-to-live. 44 | /// 45 | [JsonProperty(PropertyName = "ttl", NullValueHandling = NullValueHandling.Ignore)] 46 | public int? TimeToLive { get; set; } 47 | 48 | /// 49 | /// Gets the last allowable time to send the notification. After this time, the event will simply be skipped. 50 | /// 51 | /// Last allowable time for notification 52 | public DateTimeOffset GetLastAllowableTimeToSendNotification() 53 | { 54 | return this.DateTime.AddHours(12); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/EventStatus.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | /// 8 | /// Represents the states in which events can be at any particular time 9 | /// 10 | public enum EventStatus 11 | { 12 | /// 13 | /// Continue to post/celebrate event 14 | /// 15 | Default, 16 | 17 | /// 18 | /// Skip the event for current year 19 | /// 20 | Skipped, 21 | 22 | /// 23 | /// Event occurrence has been sent 24 | /// 25 | Sent, 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/EventTypes.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | /// 8 | /// Store type of events 9 | /// 10 | public enum EventTypes 11 | { 12 | /// 13 | /// Any event except Birthday and Anniversary 14 | /// 15 | Other, 16 | 17 | /// 18 | /// Birthday 19 | /// 20 | Birthday, 21 | 22 | /// 23 | /// Anniversary 24 | /// 25 | Anniversary, 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/EventsTabViewModel.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using System.Collections.Generic; 8 | 9 | /// 10 | /// Model for Events tab view 11 | /// 12 | public class EventsTabViewModel 13 | { 14 | /// 15 | /// Gets or sets the list of events 16 | /// 17 | public IList Events { get; set; } 18 | 19 | /// 20 | /// Gets or sets the maximum number of events for a user 21 | /// 22 | public int MaxUserEventsCount { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/ManageEventViewModel.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | /// 11 | /// Model for Manage Event view 12 | /// 13 | public class ManageEventViewModel 14 | { 15 | /// 16 | /// Gets or sets event ID 17 | /// 18 | public string EventId { get; set; } 19 | 20 | /// 21 | /// Gets or sets client time zone 22 | /// 23 | public string ClientTimeZone { get; set; } 24 | 25 | /// 26 | /// Gets or sets CelebrationEvent 27 | /// 28 | public CelebrationEvent CelebrationEvent { get; set; } 29 | 30 | /// 31 | /// Gets or sets TeamDetails 32 | /// 33 | public IList TeamDetails { get; set; } 34 | 35 | /// 36 | /// Gets or sets list of windows timezones, from TimeZoneInfo API 37 | /// 38 | public IList TimeZoneList { get; set; } 39 | 40 | /// 41 | /// Gets or sets selected time zone id 42 | /// 43 | public string SelectedTimeZoneId { get; set; } 44 | 45 | /// 46 | /// Gets or sets the list of event types 47 | /// 48 | public IList> EventTypesInfo { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/MessageSendResult.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using System; 8 | 9 | /// 10 | /// Represents result of last message sent 11 | /// 12 | public class MessageSendResult 13 | { 14 | /// 15 | /// Gets or sets last attempt time 16 | /// 17 | public DateTimeOffset LastAttemptTime { get; set; } 18 | 19 | /// 20 | /// Gets or sets HTTP status code 21 | /// 22 | public int StatusCode { get; set; } 23 | 24 | /// 25 | /// Gets or sets Response body 26 | /// 27 | public string ResponseBody { get; set; } 28 | } 29 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/MessageType.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | /// 8 | /// Represents the MessageType, to take the appropriate action for sending the type of card 9 | /// 10 | public enum MessageType 11 | { 12 | /// 13 | /// Not defined or default 14 | /// 15 | Unknown, 16 | 17 | /// 18 | /// Preview 19 | /// 20 | Preview, 21 | 22 | /// 23 | /// Event 24 | /// 25 | Event, 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/PreviewCardPayload.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | namespace Microsoft.Teams.Apps.Celebration.Models 5 | { 6 | /// 7 | /// Preview card payload 8 | /// 9 | public class PreviewCardPayload : SubmitActionPayload 10 | { 11 | /// 12 | /// Gets or sets the event id 13 | /// 14 | public string EventId { get; set; } 15 | 16 | /// 17 | /// Gets or sets the occurrence id 18 | /// 19 | public string OccurrenceId { get; set; } 20 | 21 | /// 22 | /// Gets or sets OwnerAadObjectId 23 | /// 24 | public string OwnerAadObjectId { get; set; } 25 | 26 | /// 27 | /// Gets or sets OwnerName 28 | /// 29 | public string OwnerName { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/ShareEventPayload.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | /// 8 | /// Payload to share event with team 9 | /// 10 | public class ShareEventPayload : SubmitActionPayload 11 | { 12 | /// 13 | /// Gets or sets userAadObjectId 14 | /// 15 | public string UserAadObjectId { get; set; } 16 | 17 | /// 18 | /// Gets or sets teamId 19 | /// 20 | public string TeamId { get; set; } 21 | 22 | /// 23 | /// Gets or sets teamName 24 | /// 25 | public string TeamName { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/SubmitActionPayload.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using System; 8 | 9 | /// 10 | /// Payload for an adaptive card submit button 11 | /// 12 | [Serializable] 13 | public class SubmitActionPayload 14 | { 15 | /// 16 | /// Gets or sets adaptive card submit button action 17 | /// 18 | public string Action { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/Team.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using Microsoft.Azure.Documents; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Store the teams meta data 12 | /// 13 | public class Team : Resource 14 | { 15 | /// 16 | /// Gets or sets team name 17 | /// 18 | [JsonProperty("name")] 19 | public string Name { get; set; } 20 | 21 | /// 22 | /// Gets or sets the service url for the team 23 | /// 24 | [JsonProperty("serviceUrl")] 25 | public string ServiceUrl { get; set; } 26 | 27 | /// 28 | /// Gets or sets the tenant id 29 | /// 30 | [JsonProperty("tenantId")] 31 | public string TenantId { get; set; } 32 | 33 | /// 34 | /// Gets or sets installer name 35 | /// 36 | [JsonProperty("installerName")] 37 | public string InstallerName { get; set; } 38 | } 39 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/TimeZoneDisplayInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | /// 8 | /// Model to display timezone list 9 | /// 10 | public class TimeZoneDisplayInfo 11 | { 12 | /// 13 | /// Gets or sets timeZone display name 14 | /// 15 | public string TimeZoneDisplayName { get; set; } 16 | 17 | /// 18 | /// Gets or sets timeZoneId 19 | /// 20 | public string TimeZoneId { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/User.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using Microsoft.Azure.Documents; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Represents users who have installed the app, either personally or as part of a team 12 | /// 13 | public class User : Resource 14 | { 15 | /// 16 | /// Gets or sets user's AadObjectId 17 | /// 18 | [JsonProperty("aadObjectId")] 19 | public string AadObjectId { get; set; } 20 | 21 | /// 22 | /// Gets or sets user's teams id 23 | /// 24 | [JsonProperty("teamsId")] 25 | public string TeamsId { get; set; } 26 | 27 | /// 28 | /// Gets or sets scope of bot 29 | /// 30 | [JsonProperty("installationMethod")] 31 | public BotScope InstallationMethod { get; set; } 32 | 33 | /// 34 | /// Gets or sets conversation Id to start the communication between bot and user 35 | /// 36 | [JsonProperty("conversationId")] 37 | public string ConversationId { get; set; } 38 | 39 | /// 40 | /// Gets or sets service URL,required to instantiate connector service 41 | /// 42 | [JsonProperty("serviceUrl")] 43 | public string ServiceUrl { get; set; } 44 | 45 | /// 46 | /// Gets or sets the display name 47 | /// 48 | [JsonProperty("userName")] 49 | public string DisplayName { get; set; } 50 | 51 | /// 52 | /// Gets or sets Tenant Id of user 53 | /// 54 | [JsonProperty("tenantId")] 55 | public string TenantId { get; set; } 56 | } 57 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Models/UserTeamMembership.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration.Models 6 | { 7 | using Microsoft.Azure.Documents; 8 | using Newtonsoft.Json; 9 | 10 | /// 11 | /// Represents the membership of user within a team 12 | /// 13 | public class UserTeamMembership : Resource 14 | { 15 | /// 16 | /// Gets or sets user's teams id 17 | /// 18 | [JsonProperty("userTeamsId")] 19 | public string UserTeamsId { get; set; } 20 | 21 | /// 22 | /// Gets or sets id of team, user is member of 23 | /// 24 | [JsonProperty("teamId")] 25 | public string TeamId { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | // General Information about an assembly is controlled through the following . 9 | // set of attributes. Change these attribute values to modify the information 10 | // associated with an assembly. 11 | [assembly: AssemblyTitle("Microsoft.Teams.Apps.Celebration")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Microsoft.Teams.Apps.Celebration")] 16 | [assembly: AssemblyCopyright("Copyright © 2016")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: AssemblyCulture("")] 19 | 20 | // Setting ComVisible to false makes the types in this assembly not visible 21 | // to COM components. If you need to access a type in this assembly from 22 | // COM, set the ComVisible attribute to true on that type. 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | [assembly: Guid("a1a5f2f5-3a75-44df-af34-a48f224abb8e")] 27 | 28 | // Version information for an assembly consists of the following four values: 29 | // 30 | // Major Version 31 | // Minor Version 32 | // Build Number 33 | // Revision 34 | // 35 | // You can specify all the values or you can default the Revision and Build Numbers 36 | // by using the '*' as shown below: 37 | [assembly: AssemblyVersion("1.0.0.0")] 38 | [assembly: AssemblyFileVersion("1.0.0.0")] 39 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Scripts/custom-multiselect.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | function updateSelectedText(selector, options) { 3 | // This function can be used to customize the display of multiple selections 4 | } 5 | 6 | function addCustomCheckbox(selector) { 7 | selector.nextAll(".btn-group:first").find(".multiselect-container .checkbox").append(""); 8 | } 9 | 10 | function bindEvents(selector,options) { 11 | selector.closest(".multiselect-native-select").find(".multiselect.dropdown-toggle").click(function (event) { 12 | hideAllDropdowns(); 13 | selector.nextAll(".slimScrollDiv:first").toggleClass("show").toggleClass("hide"); 14 | $(this).closest(".multiselect-native-select").find(".multiselect-container").toggleClass("show"); 15 | event.stopPropagation(); 16 | }); 17 | 18 | selector.closest(".multiselect-native-select").find(".multiselect-container [type = 'checkbox']").change(function () { 19 | updateSelectedText($(this), options); 20 | }); 21 | } 22 | 23 | function hideAllDropdowns() { 24 | $(".dropdown-menu.show").removeClass("show").parent(".slimScrollDiv").removeClass("show").addClass("hide"); 25 | 26 | // hide any open date picker 27 | $(".datepicker.dropdown-menu").css({ "display": "none" }); 28 | } 29 | 30 | function singleSelectElementClick(selector) { 31 | var liElement = selector.closest(".multiselect-native-select").find(".multiselect-container li"); 32 | liElement.click(function () { 33 | liElement.removeClass("active"); 34 | setTimeout(function () { 35 | hideAllDropdowns(); 36 | }); 37 | }); 38 | } 39 | 40 | function setDropdownPosition(selector, options) { 41 | if (options.position === 'top'){ 42 | var topPosition = -Math.abs(options.maxHeight + 3); 43 | selector.closest(".multiselect-native-select").find(".multiselect-container").css({ "top": topPosition }) 44 | } 45 | } 46 | 47 | $.fn.customMultiselect = function (options) { 48 | this.multiselect(options); 49 | bindEvents(this, options); 50 | addCustomCheckbox(this); 51 | if (this.attr('multiple') === "multiple") { 52 | updateSelectedText(this, options); 53 | } else { 54 | singleSelectElementClick(this); 55 | } 56 | setDropdownPosition(this, options); 57 | return this; 58 | } 59 | 60 | $.fn.addFancyScrollbar = function (options) { 61 | var btnGroupElement = $(this).nextAll(".btn-group:first"); 62 | var multiselectDropDown = btnGroupElement.find('.multiselect-container'); 63 | btnGroupElement.after(multiselectDropDown); 64 | 65 | var selectedOptionsCount = $(this).closest(".multiselect-native-select").find(".multiselect-container li").length; 66 | var listHeight = 250; 67 | if (options.listMinHeight && options.listMaxHeight) { 68 | listHeight = (selectedOptionsCount > 4) ? options.listMaxHeight : +(options.listMinHeight * selectedOptionsCount) + +options.offset; 69 | } 70 | else { 71 | listHeight = (options.height) ? (options.height) : listHeight; 72 | } 73 | 74 | multiselectDropDown.slimScroll( 75 | { 76 | height: listHeight, 77 | } 78 | ); 79 | 80 | $(this).nextAll(".slimScrollDiv:first").addClass("hide").css({ 81 | "box-shadow": "0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)" 82 | }).height(listHeight).width($(this).nextAll(".btn-group:first").width()); 83 | 84 | return this; 85 | } 86 | 87 | }(jQuery)); 88 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Scripts/events.js: -------------------------------------------------------------------------------- 1 |  2 | var microsoftTeams; 3 | var tenantId; 4 | 5 | // Set up the tab and stuff. 6 | microsoftTeams.initialize(); 7 | 8 | // Get selected teams. 9 | function getSelectedTeams() { 10 | var teams = []; 11 | $("#eventTeam :selected").each(function () { 12 | teams.push({ 13 | Id: $(this).val() 14 | }); 15 | }); 16 | return teams; 17 | } 18 | 19 | // submit the data to task module opener. 20 | function submitForm(action, eventId) { 21 | var eventInfo = { 22 | Id: eventId, 23 | Type: $('#eventType :selected').val(), 24 | Title: $('#title').val(), 25 | Message: $('#message').val(), 26 | Date: $('#eventDate').val(), 27 | ImageUrl: $(".carousel-item.active > img").attr("src"), 28 | Teams: getSelectedTeams(), 29 | TimeZoneId: $('#timezonelist :selected').val(), 30 | }; 31 | if (isValidDate($('#eventDate').val())) { 32 | $("#invalidDate").hide(); 33 | } else { 34 | $("#invalidDate").show(); 35 | return false; 36 | } 37 | 38 | eventData = { 39 | Action: action, 40 | EventInfo: eventInfo 41 | }; 42 | 43 | microsoftTeams.tasks.submitTask(eventData); 44 | return true; 45 | } 46 | 47 | function isValidDate(date) { 48 | var separator = date.match(/[.\/\-\s].*?/), 49 | parts = date.split(/\W+/); 50 | if (!separator || !parts || parts.length < 3) { 51 | return false; 52 | } 53 | if (isNaN(Date.parse(date))) { 54 | return false; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | function closeTaskModule() { 61 | microsoftTeams.tasks.submitTask(); 62 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Scripts/theme.js: -------------------------------------------------------------------------------- 1 | const DARK = "dark"; 2 | const CONTRAST = "contrast"; 3 | 4 | // To set the theme to the page 5 | function setTheme(theme) { 6 | let bodyElement = $("body")[0]; 7 | switch (theme) { 8 | case DARK: 9 | bodyElement.className = "theme-dark"; 10 | break; 11 | case CONTRAST: 12 | bodyElement.className = "theme-highContrast"; 13 | break; 14 | default: 15 | bodyElement.className = "theme-default"; 16 | break; 17 | } 18 | } 19 | 20 | // To get the query string key and values 21 | function getQueryParameters() { 22 | let queryParams = {}; 23 | location.search.substr(1).split("&").forEach(function (item) { 24 | let s = item.split("="), 25 | k = s[0], 26 | v = s[1] && decodeURIComponent(s[1]); 27 | queryParams[k] = v; 28 | }); 29 | return queryParams; 30 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Startup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Celebration 6 | { 7 | using global::Owin; 8 | 9 | /// 10 | /// OWIN startup class 11 | /// 12 | public partial class Startup 13 | { 14 | /// 15 | /// Configuration entry point 16 | /// 17 | /// App builder 18 | public void Configuration(IAppBuilder app) 19 | { 20 | this.ConfigureAuth(app); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | @{ 3 | Layout = null; 4 | } 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 |
14 |

Error.

15 |

An error occurred while processing your request.

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title 7 | @RenderSection("stylesheets", required: false) 8 | @Styles.Render("~/bundles/css") 9 | 21 | 22 | 23 | @RenderBody() 24 | @Scripts.Render("~/bundles/jquery") 25 | @Scripts.Render("~/bundles/teams-js") 26 | @Scripts.Render("~/Scripts/theme.js") 27 | @RenderSection("scripts", required: false) 28 | 29 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/TabAuth/Callback.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Authentication Callback"; 3 | } 4 | 5 | @section scripts { 6 | @Scripts.Render("~/bundles/adal-js") 7 | 31 | } 32 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/TabAuth/Login.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Login"; 3 | } 4 | 5 | @section stylesheets { 6 | @Styles.Render("~/bundles/bootstrap3-css") 7 | } 8 | 9 | 10 | 21 | 22 | @section scripts { 23 | @Scripts.Render("~/bundles/adal-js") 24 | 109 | } 110 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/TabAuth/Start.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Authentication Start"; 3 | } 4 | 5 | @section scripts { 6 | @Scripts.Render("~/bundles/adal-js") 7 | 31 | } 32 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/Tabs/EventsData.cshtml: -------------------------------------------------------------------------------- 1 | @model Microsoft.Teams.Apps.Celebration.Models.EventsTabViewModel 2 | 3 | @if (@Model.Events.Count == 0) 4 | { 5 | // Empty state div 6 |
7 |
8 |
9 | 10 |
11 |
12 | Start celebrating! 13 |
14 |
15 | Share a special occasion with your team 16 |
17 |
18 | 19 |
20 |
21 |
22 | } 23 | else 24 | { 25 | // Represents events in the form of tiles. 26 |
27 | @foreach (var celebrationEvent in Model.Events) 28 | { 29 |
30 |
31 | @celebrationEvent.Date.ToString("MMMM dd") 32 |
33 | 51 |
52 | @celebrationEvent.Title 53 |
54 | 55 |
56 | 57 |
58 | 59 |
60 | Message 61 |
62 |
63 | @celebrationEvent.Message 64 |
65 |
66 | } 67 | 68 | @if (Model.Events.Count < Model.MaxUserEventsCount) 69 | { 70 | for (int i = 0; i < Model.MaxUserEventsCount - Model.Events.Count; i++) 71 | { 72 | // New event tile. It allows user to add new event 73 |
74 |
75 | Date 76 |
77 |
78 | Event title 79 |
80 |
81 | 82 |
83 |
84 | Message 85 |
86 |
87 | Say something about your event. 88 |
89 |
90 | 91 | } 92 | } 93 | else 94 | { 95 |
96 | I’ve reached the maximum number of events I can keep track of right now. If you want to add something new, delete an older one to make room. 97 |
98 | } 99 |
100 | } 101 | 102 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/Tabs/ManageEvent.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.Teams.Apps.Celebration.Models; 2 | @model ManageEventViewModel 3 | 4 | @{ 5 | ViewBag.Title = "Edit event"; 6 | } 7 | 8 | @section stylesheets { 9 | @Styles.Render("~/bundles/bootstrap4-css") 10 | @Styles.Render("~/bundles/bootstrap-multiselect-css") 11 | @Styles.Render("~/bundles/bootstrap-datepicker-css") 12 | @Styles.Render("~/content/fontawesome/css/font-awesome") 13 | } 14 | 15 |
16 | 19 | 20 |
21 |
22 |
23 | 24 | @section scripts { 25 | @Scripts.Render("~/bundles/jquery-validate") 26 | @Scripts.Render("~/bundles/jquery-slimscroll") 27 | @Scripts.Render("~/bundles/bootstrap4") 28 | @Scripts.Render("~/bundles/bootstrap-multiselect") 29 | @Scripts.Render("~/bundles/bootstrap-datepicker") 30 | @Scripts.Render("~/bundles/momentjs") 31 | 32 | @Scripts.Render("~/Scripts/events.js") 33 | @Scripts.Render("~/Scripts/custom-multiselect.js") 34 | 35 | 113 | } 114 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/Tabs/Tour.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Tour"; 3 | } 4 | 5 | @section stylesheets { 6 | @Styles.Render("~/bundles/bootstrap4-css") 7 | } 8 | 9 |
10 |
11 | Welcome image 12 |
13 |
14 |

Let the team celebrate with you!

15 |

16 | Tell me when your special occasions are, and what teams you want me to share them with. I’ll post a fun message when the time comes. 17 |

18 |
19 | 24 |
25 |
26 |
27 | Welcome image 28 |
29 |
30 |

Add your events once and rest easy!

31 |

32 | No need to do it again when you join a new team. If you want me to, I’ll share your events there, too. 33 |

34 |
35 | 43 |
44 | 45 | @section scripts { 46 | 83 | } 84 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Views/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/default.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |

Microsoft.Teams.Apps.Celebration

9 |

Celebration bot helps the team members to share their events with team. Birthday, Anniversary or any other special event.

10 |

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

11 | 12 | 13 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [ 5 | { 6 | "library": "twitter-bootstrap@4.3.1", 7 | "destination": "lib/bootstrap4/" 8 | }, 9 | { 10 | "library": "bootstrap-datepicker@1.9.0", 11 | "destination": "lib/bootstrap-datepicker/" 12 | }, 13 | { 14 | "library": "bootstrap-multiselect@0.9.15", 15 | "destination": "lib/bootstrap-multiselect/" 16 | }, 17 | { 18 | "library": "jquery@3.4.1", 19 | "destination": "lib/jquery/" 20 | }, 21 | { 22 | "library": "jQuery-slimScroll@1.3.8", 23 | "destination": "lib/jQuery-slimScroll/" 24 | }, 25 | { 26 | "library": "jquery-validate@1.19.1", 27 | "destination": "lib/jquery-validate/" 28 | }, 29 | { 30 | "library": "jquery-validation-unobtrusive@3.2.11", 31 | "destination": "lib/jquery-validation-unobtrusive/" 32 | }, 33 | { 34 | "provider": "unpkg", 35 | "library": "@microsoft/teams-js@1.5.2", 36 | "destination": "lib/teams-js/", 37 | "files": [ 38 | "dist/MicrosoftTeams.js", 39 | "dist/MicrosoftTeams.js.map", 40 | "dist/MicrosoftTeams.min.js" 41 | ] 42 | }, 43 | { 44 | "provider": "unpkg", 45 | "library": "bootstrap@3.4.1", 46 | "destination": "lib/bootstrap3/", 47 | "files": [ 48 | "dist/css/bootstrap-theme.css", 49 | "dist/css/bootstrap-theme.css.map", 50 | "dist/css/bootstrap-theme.min.css", 51 | "dist/css/bootstrap-theme.min.css.map", 52 | "dist/css/bootstrap.css", 53 | "dist/css/bootstrap.css.map", 54 | "dist/css/bootstrap.min.css", 55 | "dist/css/bootstrap.min.css.map", 56 | "dist/fonts/glyphicons-halflings-regular.eot", 57 | "dist/fonts/glyphicons-halflings-regular.svg", 58 | "dist/fonts/glyphicons-halflings-regular.ttf", 59 | "dist/fonts/glyphicons-halflings-regular.woff", 60 | "dist/fonts/glyphicons-halflings-regular.woff2", 61 | "dist/js/bootstrap.js", 62 | "dist/js/bootstrap.min.js", 63 | "dist/js/npm.js" 64 | ] 65 | }, 66 | { 67 | "provider": "unpkg", 68 | "library": "adal-angular@1.0.17", 69 | "destination": "lib/adal-angular/", 70 | "files": [ 71 | "lib/adal.js", 72 | "dist/adal.min.js" 73 | ] 74 | }, 75 | { 76 | "provider": "unpkg", 77 | "library": "font-awesome@4.0.3", 78 | "destination": "Content/fontawesome/", 79 | "files": [ 80 | "css/font-awesome.css", 81 | "css/font-awesome.min.css", 82 | "fonts/fontawesome-webfont.eot", 83 | "fonts/fontawesome-webfont.svg", 84 | "fonts/fontawesome-webfont.ttf", 85 | "fonts/fontawesome-webfont.woff", 86 | "fonts/FontAwesome.otf" 87 | ] 88 | }, 89 | { 90 | "library": "moment.js@2.24.0", 91 | "destination": "lib/moment.js/" 92 | } 93 | ] 94 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Celebration/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 6 | 7 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 8 | "settings": { 9 | "documentationRules": { 10 | "companyName": "Microsoft" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Authentication/ITokenValidator.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Authentication 6 | { 7 | using System.Security.Claims; 8 | using System.Threading.Tasks; 9 | 10 | /// 11 | /// Interface to validate tokens. 12 | /// 13 | public interface ITokenValidator 14 | { 15 | /// 16 | /// Validates a JWT token and extracts the claims principal and AAD objectId of the user. 17 | /// 18 | /// The token to validate. 19 | /// The claims principal or null if the token is not valid. 20 | Task ValidateIdTokenAsync(string token); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Authentication/TokenValidator.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Authentication 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Diagnostics; 10 | using System.IdentityModel.Tokens.Jwt; 11 | using System.Linq; 12 | using System.Net.Http; 13 | using System.Security.Claims; 14 | using System.Threading.Tasks; 15 | using Microsoft.IdentityModel.Protocols; 16 | using Microsoft.IdentityModel.Protocols.OpenIdConnect; 17 | using Microsoft.IdentityModel.Tokens; 18 | using Microsoft.Teams.Apps.Common.Configuration; 19 | 20 | /// 21 | /// The TokenValidator is an implementation of the interface. 22 | /// It uses AAD OAuth to validate a token and return the user's AAD objectId or an error. 23 | /// 24 | public class TokenValidator : ITokenValidator 25 | { 26 | private readonly string expectedAudience; 27 | private readonly ConfigurationManager openIdV1ConfigManager; 28 | private readonly ConfigurationManager openIdV2ConfigManager; 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// The expected audience value. 34 | public TokenValidator(string expectedAudience) 35 | { 36 | Debug.Assert(!string.IsNullOrEmpty(expectedAudience), "expectedAudience is null or empty"); 37 | this.expectedAudience = expectedAudience ?? throw new ArgumentException("expectedAudience is null or empty", nameof(expectedAudience)); 38 | 39 | var httpClient = new HttpClient(); 40 | this.openIdV1ConfigManager = new ConfigurationManager( 41 | "https://login.microsoftonline.com/common/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), httpClient); 42 | this.openIdV2ConfigManager = new ConfigurationManager( 43 | "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), httpClient); 44 | } 45 | 46 | /// 47 | public async Task ValidateIdTokenAsync(string token) 48 | { 49 | if (token == null) 50 | { 51 | return null; 52 | } 53 | 54 | var tokenPayload = this.ParseTokenPayload(token); 55 | if (tokenPayload == null) 56 | { 57 | return null; 58 | } 59 | 60 | var tidClaim = this.GetClaims(tokenPayload, "tid").FirstOrDefault(); 61 | if (tidClaim == null) 62 | { 63 | return null; 64 | } 65 | 66 | var issClaim = this.GetClaims(tokenPayload, "iss").FirstOrDefault(); 67 | if (issClaim == null) 68 | { 69 | return null; 70 | } 71 | 72 | // Test if the token is issued by the V2 AAD issuer 73 | var openIdConfig = await this.openIdV2ConfigManager.GetConfigurationAsync(); 74 | var expectedIssuer = openIdConfig.Issuer.Replace("{tenantid}", tidClaim.Value); 75 | if (!issClaim.Value.Equals(expectedIssuer, StringComparison.InvariantCulture)) 76 | { 77 | // If the token is not issued by the V2 AAD issuer, then try the V1 issuer. 78 | openIdConfig = await this.openIdV1ConfigManager.GetConfigurationAsync(); 79 | expectedIssuer = openIdConfig.Issuer.Replace("{tenantid}", tidClaim.Value); 80 | } 81 | 82 | TokenValidationParameters validationParameters = new TokenValidationParameters 83 | { 84 | ValidateAudience = true, 85 | ValidateIssuer = true, 86 | ValidateLifetime = true, 87 | IssuerSigningKeys = openIdConfig.SigningKeys, 88 | ValidAudience = this.expectedAudience, 89 | ValidIssuer = expectedIssuer, 90 | }; 91 | 92 | JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); 93 | try 94 | { 95 | return tokenHandler.ValidateToken(token, validationParameters, out _); 96 | } 97 | catch (Exception) 98 | { 99 | return null; 100 | } 101 | } 102 | 103 | private JwtSecurityToken ParseTokenPayload(string token) 104 | { 105 | if (token == null) 106 | { 107 | throw new ArgumentNullException(nameof(token)); 108 | } 109 | 110 | try 111 | { 112 | JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); 113 | if (!tokenHandler.CanReadToken(token)) 114 | { 115 | return null; 116 | } 117 | 118 | return tokenHandler.ReadJwtToken(token); 119 | } 120 | catch (Exception) 121 | { 122 | return null; 123 | } 124 | } 125 | 126 | private IEnumerable GetClaims(JwtSecurityToken token, string claimType) 127 | { 128 | return token.Claims.Where(c => c.Type.Equals(claimType, StringComparison.InvariantCultureIgnoreCase)); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/CommonConfig.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common 6 | { 7 | /// 8 | /// Configurations that are used across multiple projects. 9 | /// 10 | public enum CommonConfig 11 | { 12 | /// 13 | /// Represents the current deployment environment. 14 | /// 15 | Environment, 16 | 17 | /// 18 | /// Represents an active directory audience. 19 | /// 20 | ActiveDirectoryAudience, 21 | 22 | /// 23 | /// Represents an active directory authority. 24 | /// 25 | ActiveDirectoryAuthority, 26 | 27 | /// 28 | /// Represents an active directory certificate location. 29 | /// 30 | ActiveDirectoryCertificateLocation, 31 | 32 | /// 33 | /// Represents an active directory certificate name. 34 | /// 35 | ActiveDirectoryCertificateName, 36 | 37 | /// 38 | /// Represents an active directory client identifier. 39 | /// 40 | ActiveDirectoryClientId, 41 | 42 | /// 43 | /// Represents an active directory tenant. 44 | /// 45 | ActiveDirectoryTenant, 46 | 47 | /// 48 | /// Represents an application insights instrumentation key. 49 | /// 50 | ApplicationInsightsInstrumentationKey, 51 | 52 | /// 53 | /// Represents an application insights log level. 54 | /// 55 | ApplicationInsightsLogLevel, 56 | 57 | /// 58 | /// Determines whether to use the key vault provider. 59 | /// 60 | UseKeyVault, 61 | 62 | /// 63 | /// Represents a key vault uri. 64 | /// 65 | KeyVaultUri, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/CommonConstant.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common 6 | { 7 | using System; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | /// 11 | /// Store common constants used in project. 12 | /// 13 | [ExcludeFromCodeCoverage] 14 | public static class CommonConstant 15 | { 16 | /// 17 | /// The correlation identifier. 18 | /// 19 | public const string CorrelationId = "CorrelationId"; 20 | 21 | /// 22 | /// Azure storage partition key. 23 | /// 24 | public const string PartitionKey = "PartitionKey"; 25 | 26 | /// 27 | /// Azure storage row key. 28 | /// 29 | public const string RowKey = "RowKey"; 30 | 31 | /// 32 | /// The epoch value. 33 | /// 34 | public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Configuration/IConfigProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Configuration 6 | { 7 | using System; 8 | 9 | /// 10 | /// Interface for implementing get setting values. 11 | /// 12 | public interface IConfigProvider 13 | { 14 | /// 15 | /// Gets the setting value. 16 | /// 17 | /// The type of the configuration keys. 18 | /// The config key. 19 | /// The config value. 20 | string GetSetting(TConfigKeys key) 21 | where TConfigKeys : struct, IConvertible; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Configuration/LocalConfigProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Configuration 6 | { 7 | using System; 8 | using System.Configuration; 9 | using Microsoft.Teams.Apps.Common.Exceptions; 10 | 11 | /// 12 | /// Provides configuration that are stored locally. 13 | /// 14 | /// 15 | [Serializable] 16 | public class LocalConfigProvider : IConfigProvider 17 | { 18 | /// 19 | /// Gets the setting value. 20 | /// 21 | /// The type of the configuration keys. 22 | /// The config key. 23 | /// The config value. 24 | /// 25 | /// Specific key configuration not found. 26 | /// 27 | public string GetSetting(TConfigKeys key) 28 | where TConfigKeys : struct, IConvertible 29 | { 30 | string value = ConfigurationManager.AppSettings[key.ToString()]; 31 | if (value != null) 32 | { 33 | return value; 34 | } 35 | 36 | throw new SettingNotFoundException($"{key} configuration not found"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Exceptions/SettingDeserializationException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Exceptions 6 | { 7 | using System; 8 | using System.Diagnostics.CodeAnalysis; 9 | using System.Runtime.Serialization; 10 | 11 | /// 12 | /// Exception for setting deserialization. 13 | /// 14 | /// 15 | [Serializable] 16 | [ExcludeFromCodeCoverage] 17 | public class SettingDeserializationException : System.Exception 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public SettingDeserializationException() 23 | { 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The message that describes the error. 30 | public SettingDeserializationException(string message) 31 | : base(message) 32 | { 33 | } 34 | 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// 38 | /// The error message that explains the reason for the exception. 39 | /// 40 | /// The exception that is the cause of the current exception. 41 | /// If no inner exception is specified then returns null reference. 42 | /// 43 | public SettingDeserializationException(string message, System.Exception innerException) 44 | : base(message, innerException) 45 | { 46 | } 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | /// The SerializationInfo that holds the serialized object data about the exception being thrown. 52 | /// The StreamingContext that contains contextual information about the source or destination. 53 | protected SettingDeserializationException(SerializationInfo info, StreamingContext context) 54 | : base(info, context) 55 | { 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Exceptions/SettingNotFoundException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Exceptions 6 | { 7 | using System; 8 | using System.Diagnostics.CodeAnalysis; 9 | using System.Runtime.Serialization; 10 | 11 | /// 12 | /// Exception for setting not found. 13 | /// 14 | /// 15 | [Serializable] 16 | [ExcludeFromCodeCoverage] 17 | public class SettingNotFoundException : Exception 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public SettingNotFoundException() 23 | { 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The message that describes the error. 30 | public SettingNotFoundException(string message) 31 | : base(message) 32 | { 33 | } 34 | 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// 38 | /// The error message that explains the reason for the exception. 39 | /// 40 | /// The exception that is the cause of the current exception. 41 | /// If no inner exception is specified then returns null reference. 42 | /// 43 | public SettingNotFoundException(string message, Exception innerException) 44 | : base(message, innerException) 45 | { 46 | // Add any type-specific logic for inner exceptions. 47 | } 48 | 49 | /// 50 | /// Initializes a new instance of the class. 51 | /// 52 | /// The SerializationInfo that holds the serialized object data about the exception being thrown. 53 | /// The StreamingContext that contains contextual information about the source or destination. 54 | protected SettingNotFoundException(SerializationInfo info, StreamingContext context) 55 | : base(info, context) 56 | { 57 | // Implement type-specific serialization constructor logic. 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Extensions/ClaimsPrincipalExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Extensions 6 | { 7 | using System.Linq; 8 | using System.Security.Claims; 9 | 10 | /// 11 | /// Extension methods for . 12 | /// 13 | public static class ClaimsPrincipalExtensions 14 | { 15 | /// 16 | /// Get the value of the user principal name claim. 17 | /// 18 | /// The claims principal instance 19 | /// The UPN claim, if present, or null otherwise 20 | public static string GetUserPrincipalName(this ClaimsPrincipal principal) 21 | { 22 | return principal.Claims?.FirstOrDefault(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn")?.Value; 23 | } 24 | 25 | /// 26 | /// Get the value of the user object identifier claim. 27 | /// 28 | /// The claims principal instance 29 | /// The objectidentifier claim, if present, or null otherwise 30 | public static string GetUserObjectId(this ClaimsPrincipal principal) 31 | { 32 | return principal.Claims?.FirstOrDefault(x => x.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Extensions/EncryptedConfigAttribute.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Extensions 6 | { 7 | using System; 8 | 9 | /// 10 | /// The encrypted config attribute class. 11 | /// 12 | /// 13 | [AttributeUsage(AttributeTargets.All)] 14 | public sealed class EncryptedConfigAttribute : Attribute 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Extensions/TimeTracker.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Extensions 6 | { 7 | using System; 8 | using System.Diagnostics; 9 | using Microsoft.Teams.Apps.Common.Logging; 10 | 11 | /// 12 | /// The time tracker class for logger. 13 | /// 14 | /// 15 | public sealed class TimeTracker : IDisposable 16 | { 17 | private readonly string commandName; 18 | private readonly string dependencyName; 19 | private readonly ILogProvider logger; 20 | private readonly DateTime startTime = DateTime.UtcNow; 21 | private readonly Stopwatch stopwatch = Stopwatch.StartNew(); 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The logger object. 27 | /// Name of the dependency. 28 | /// Name of the command. 29 | private TimeTracker(ILogProvider logger, string dependencyName, string commandName) 30 | { 31 | this.logger = logger; 32 | this.dependencyName = dependencyName; 33 | this.commandName = commandName; 34 | } 35 | 36 | /// 37 | /// Creates the specified logger. 38 | /// 39 | /// The logger object. 40 | /// Name of the dependency. 41 | /// Name of the command. 42 | /// [Optional] Enable tracking with default value as true. 43 | /// Creates the time tracker for logger. 44 | public static TimeTracker Create(ILogProvider logger, string dependencyName, string commandName, bool enableTracking = true) 45 | { 46 | return enableTracking ? new TimeTracker(logger, dependencyName, commandName) : null; 47 | } 48 | 49 | /// 50 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 51 | /// 52 | public void Dispose() 53 | { 54 | this.stopwatch.Stop(); 55 | this.logger.LogDependency(null, this.dependencyName, this.commandName, this.startTime, this.stopwatch.Elapsed, true); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Http/HttpClientWrapper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Http 6 | { 7 | using System; 8 | using System.Net.Http; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | /// 13 | /// Wrapper for that implements . 14 | /// 15 | public class HttpClientWrapper : IHttpClient 16 | { 17 | private readonly HttpClient httpClient; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// The instance to wrap 23 | public HttpClientWrapper(HttpClient httpClient) 24 | { 25 | this.httpClient = httpClient; 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// The message handler 32 | public HttpClientWrapper(HttpMessageHandler handler) 33 | : this(new HttpClient(handler)) 34 | { 35 | } 36 | 37 | /// 38 | /// Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation. 39 | /// 40 | /// The Uri the request is sent to. 41 | /// An HTTP completion option value that indicates when the operation should be considered completed. 42 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. 43 | /// The task object representing the asynchronous operation. 44 | public Task GetAsync(Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken) 45 | { 46 | #pragma warning disable SecurityIntelliSenseCS // This code is a pass-through wrapper around HttpClient 47 | return this.httpClient.GetAsync(requestUri, completionOption, cancellationToken); 48 | #pragma warning restore SecurityIntelliSenseCS 49 | } 50 | 51 | /// 52 | /// Send an HTTP request as an asynchronous operation. 53 | /// 54 | /// The HTTP request message to send. 55 | /// When the operation should complete (as soon as a response is available or after reading the whole response content) 56 | /// The cancellation token to cancel operation. 57 | /// The task object representing the asynchronous operation. 58 | public Task SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) 59 | { 60 | return this.httpClient.SendAsync(request, completionOption, cancellationToken); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Http/IHttpClient.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Http 6 | { 7 | using System; 8 | using System.Net.Http; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | /// 13 | /// Interface for . 14 | /// 15 | public interface IHttpClient 16 | { 17 | /// 18 | /// Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation. 19 | /// 20 | /// The Uri the request is sent to. 21 | /// An HTTP completion option value that indicates when the operation should be considered completed. 22 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. 23 | /// The task object representing the asynchronous operation. 24 | Task GetAsync(Uri requestUri, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default(CancellationToken)); 25 | 26 | /// 27 | /// Send an HTTP request as an asynchronous operation. 28 | /// 29 | /// The HTTP request message to send. 30 | /// When the operation should complete (as soon as a response is available or after reading the whole response content) 31 | /// The cancellation token to cancel operation. 32 | /// The task object representing the asynchronous operation. 33 | Task SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default(CancellationToken)); 34 | } 35 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Logging/ILogProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Logging 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | /// 11 | /// Interface to implement different log information. 12 | /// 13 | public interface ILogProvider 14 | { 15 | /// 16 | /// Log a message of the given type. 17 | /// 18 | /// Type of log 19 | /// The debug message 20 | /// An exception to capture and correlate to the debug event 21 | /// A collection of properties that should be associated to the telemetry entry 22 | /// This is used for tracing a series of events. 23 | /// The source 24 | void Log(LogLevel logType, string message, Exception exception, Dictionary properties, Guid correlationId, Func source); 25 | 26 | /// 27 | /// Log a custom event. 28 | /// A custom event log provides insight to system administrators by logging information that may not be captured by other log levels. 29 | /// 30 | /// The information message 31 | /// A collection of properties that should be associated to the telemetry entry 32 | /// The metrics 33 | /// This is used for tracing a series of events. 34 | /// The source 35 | void LogEvent(string eventName, Dictionary properties, Dictionary metrics, Guid correlationId, Func source); 36 | 37 | /// 38 | /// Log a metric. Metrics are aggregated double values that can be processed for the system as a whole 39 | /// 40 | /// Metric Name 41 | /// The value that you are logging 42 | /// [Optional] A collection of properties that should be associated to the metric entry 43 | /// [Optional] The loglevel that this metric should be logged at 44 | void LogMetric(string name, double value, Dictionary properties = null, LogLevel logLevel = LogLevel.Metric); 45 | 46 | /// 47 | /// Log a dependency. 48 | /// 49 | /// External dependency type. Very low cardinality value for logical grouping and interpretation of fields. Examples are SQL, Azure table, and HTTP. 50 | /// Name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template. 51 | /// Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with all query parameters. 52 | /// The time when the dependency was called. 53 | /// The time taken by the external dependency to handle the call. 54 | /// True if the dependency call was handled successfully. 55 | void LogDependency(string dependencyTypeName, string dependencyName, string data, DateTimeOffset startTime, TimeSpan duration, bool success); 56 | } 57 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Logging/LogLevel.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Logging 6 | { 7 | /// 8 | /// Types of log level. 9 | /// 10 | public enum LogLevel 11 | { 12 | /// 13 | /// Represents debug. 14 | /// 15 | Debug, 16 | 17 | /// 18 | /// Represents information. 19 | /// 20 | Info, 21 | 22 | /// 23 | /// Represents warning. 24 | /// 25 | Warning, 26 | 27 | /// 28 | /// Represents metric. 29 | /// 30 | Metric, 31 | 32 | /// 33 | /// Represents an event. 34 | /// 35 | Event, 36 | 37 | /// 38 | /// Represents an error. 39 | /// 40 | Error, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Models/Token.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Models 6 | { 7 | using Newtonsoft.Json; 8 | 9 | /// 10 | /// Represents authentication token. 11 | /// 12 | public class Token 13 | { 14 | /// 15 | /// Gets or sets access token. 16 | /// 17 | [JsonProperty("access_token")] 18 | public string AccessToken { get; set; } 19 | 20 | /// 21 | /// Gets or sets type of token. 22 | /// 23 | [JsonProperty("token_type")] 24 | public string TokenType { get; set; } 25 | 26 | /// 27 | /// Gets or sets gets expire duration in milliseconds. 28 | /// 29 | [JsonProperty("expires_in")] 30 | public int ExpiresIn { get; set; } 31 | 32 | /// 33 | /// Gets or sets refresh token. 34 | /// 35 | [JsonProperty("refresh_token")] 36 | public string RefreshToken { get; set; } 37 | } 38 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | // General Information about an assembly is controlled through the following 9 | // set of attributes. Change these attribute values to modify the information 10 | // associated with an assembly. 11 | [assembly: AssemblyTitle("Microsoft.Teams.Apps.Common")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("HP Inc.")] 15 | [assembly: AssemblyProduct("Microsoft.Teams.Apps.Common")] 16 | [assembly: AssemblyCopyright("Copyright © HP Inc. 2019")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: AssemblyCulture("")] 19 | 20 | // Setting ComVisible to false makes the types in this assembly not visible 21 | // to COM components. If you need to access a type in this assembly from 22 | // COM, set the ComVisible attribute to true on that type. 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | [assembly: Guid("7fa2791f-e894-4876-8773-a5318631365c")] 27 | 28 | // Version information for an assembly consists of the following four values: 29 | // 30 | // Major Version 31 | // Minor Version 32 | // Build Number 33 | // Revision 34 | // 35 | // You can specify all the values or you can default the Build and Revision Numbers 36 | // by using the '*' as shown below: 37 | // [assembly: AssemblyVersion("1.0.*")] 38 | [assembly: AssemblyVersion("1.0.0.0")] 39 | [assembly: AssemblyFileVersion("1.0.0.0")] 40 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Telemetry/TelemetryEvent.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Telemetry 6 | { 7 | /// 8 | /// Event types 9 | /// 10 | public enum TelemetryEvent 11 | { 12 | /// 13 | /// Activity received by the bot 14 | /// 15 | UserActivity, 16 | 17 | /// 18 | /// Bot detected a command at the root dialog 19 | /// 20 | TopLevelCommand, 21 | 22 | /// 23 | /// User executed a command on a card 24 | /// 25 | CardCommand, 26 | 27 | /// 28 | /// Bot received input that it could not recognize 29 | /// 30 | UnrecognizedIntent, 31 | 32 | /// 33 | /// Events related to a dialog flow 34 | /// 35 | DialogEvent, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Telemetry/TelemetryProperty.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Telemetry 6 | { 7 | /// 8 | /// Telemetry properties 9 | /// 10 | public enum TelemetryProperty 11 | { 12 | /// 13 | /// Type of incoming activity 14 | /// 15 | ActivityType, 16 | 17 | /// 18 | /// The activity id 19 | /// 20 | ActivityId, 21 | 22 | /// 23 | /// The Teams-specific event type 24 | /// 25 | TeamsEventType, 26 | 27 | /// 28 | /// The 29:xxx user id of the user 29 | /// 30 | UserId, 31 | 32 | /// 33 | /// The AAD object id of the user 34 | /// 35 | UserAadObjectId, 36 | 37 | /// 38 | /// The conversation id 39 | /// 40 | ConversationId, 41 | 42 | /// 43 | /// The conversation type 44 | /// 45 | ConversationType, 46 | 47 | /// 48 | /// The client platform 49 | /// 50 | Platform, 51 | 52 | /// 53 | /// The client locale 54 | /// 55 | Locale, 56 | 57 | /// 58 | /// The command that was invoked 59 | /// 60 | CommandName, 61 | 62 | /// 63 | /// The dialog id 64 | /// 65 | DialogId, 66 | 67 | /// 68 | /// The dialog instance id 69 | /// 70 | DialogInstanceId, 71 | 72 | /// 73 | /// [OperationStep] The event step in a multi-step process 74 | /// 75 | Step, 76 | 77 | /// 78 | /// [OperationResult] The result of a multi-step process 79 | /// 80 | Result, 81 | 82 | /// 83 | /// The status/error code 84 | /// 85 | StatusCode, 86 | 87 | /// 88 | /// The kind of card that was invoked 89 | /// 90 | CardType, 91 | 92 | /// 93 | /// The command on the card that was invoked 94 | /// 95 | CardCommand, 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/Telemetry/UserTelemetryInitializer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Common.Telemetry 6 | { 7 | using System; 8 | using System.Web; 9 | using Microsoft.ApplicationInsights.Channel; 10 | using Microsoft.ApplicationInsights.Extensibility; 11 | 12 | /// 13 | /// Initializes user property of telemetry 14 | /// 15 | public class UserTelemetryInitializer : ITelemetryInitializer 16 | { 17 | private const string TelemetryUserId = "TelemetryUserId"; 18 | 19 | /// 20 | /// Set the user id in the platform HTTP context. 21 | /// 22 | /// The HTTP context 23 | /// The user id 24 | public static void SetTelemetryUserId(HttpContext platformContext, string userId) 25 | { 26 | platformContext.Items[TelemetryUserId] = userId; 27 | } 28 | 29 | /// 30 | public void Initialize(ITelemetry telemetry) 31 | { 32 | try 33 | { 34 | var platformContext = HttpContext.Current; 35 | if ((platformContext != null) && 36 | platformContext.Items[TelemetryUserId] is string userId) 37 | { 38 | telemetry.Context.User.Id = userId; 39 | } 40 | } 41 | catch (Exception) 42 | { 43 | // Ignore failure to add user id 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Common/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Source/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | preprocessor,pre-processor 5 | shortlived,short-lived 6 | 7 | 8 | 9 | 10 | 11 | 12 | \.g\.cs$ 13 | \.generated\.cs$ 14 | \.g\.i\.cs$ 15 | TemporaryGeneratedFile_.*\.cs$ 16 | AssemblyInfo.*\.cs$ 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | False 27 | 28 | 29 | 30 | 31 | 32 | as 33 | do 34 | id 35 | if 36 | in 37 | ip 38 | is 39 | mx 40 | my 41 | no 42 | on 43 | to 44 | ui 45 | vs 46 | x 47 | y 48 | z 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | False 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Source/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // 6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 7 | 8 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 9 | "settings": { 10 | "documentationRules": { 11 | "companyName": "Microsoft" 12 | } 13 | } 14 | } 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /deploy.app.cmd: -------------------------------------------------------------------------------- 1 | @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off 2 | 3 | :: ---------------------- 4 | :: KUDU Deployment Script 5 | :: Version: 1.0.17 6 | :: ---------------------- 7 | 8 | :: Prerequisites 9 | :: ------------- 10 | 11 | :: Verify node.js installed 12 | where node 2>nul >nul 13 | IF %ERRORLEVEL% NEQ 0 ( 14 | echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment. 15 | goto error 16 | ) 17 | 18 | :: Setup 19 | :: ----- 20 | 21 | setlocal enabledelayedexpansion 22 | 23 | SET ARTIFACTS=%~dp0%..\artifacts 24 | 25 | IF NOT DEFINED DEPLOYMENT_SOURCE ( 26 | SET DEPLOYMENT_SOURCE=%~dp0%. 27 | ) 28 | 29 | IF NOT DEFINED DEPLOYMENT_TARGET ( 30 | SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot 31 | ) 32 | 33 | IF NOT DEFINED NEXT_MANIFEST_PATH ( 34 | SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest 35 | 36 | IF NOT DEFINED PREVIOUS_MANIFEST_PATH ( 37 | SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest 38 | ) 39 | ) 40 | 41 | IF NOT DEFINED KUDU_SYNC_CMD ( 42 | :: Install kudu sync 43 | echo Installing Kudu Sync 44 | call npm install kudusync -g --silent 45 | IF !ERRORLEVEL! NEQ 0 goto error 46 | 47 | :: Locally just running "kuduSync" would also work 48 | SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd 49 | ) 50 | IF NOT DEFINED DEPLOYMENT_TEMP ( 51 | SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random% 52 | SET CLEAN_LOCAL_DEPLOYMENT_TEMP=true 53 | ) 54 | 55 | IF DEFINED CLEAN_LOCAL_DEPLOYMENT_TEMP ( 56 | IF EXIST "%DEPLOYMENT_TEMP%" rd /s /q "%DEPLOYMENT_TEMP%" 57 | mkdir "%DEPLOYMENT_TEMP%" 58 | ) 59 | 60 | SET MSBUILD_PATH=%MSBUILD_15_DIR%\MSBuild.exe 61 | 62 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 63 | :: Deployment 64 | :: ---------- 65 | 66 | echo Handling .NET Web Application deployment. 67 | 68 | :: 1. Restore NuGet packages 69 | IF /I "Source\Microsoft.Teams.Apps.FAQPlusPlus.sln" NEQ "" ( 70 | call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\Source\Microsoft.Teams.Apps.Celebration.sln" -MSBuildPath "%MSBUILD_15_DIR%" 71 | IF !ERRORLEVEL! NEQ 0 goto error 72 | ) 73 | 74 | :: 2. Build to the temporary path 75 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" ( 76 | call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\Source\Microsoft.Teams.Apps.Celebration\Microsoft.Teams.Apps.Celebration.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\Source\\" %SCM_BUILD_ARGS% 77 | ) ELSE ( 78 | call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\Source\Microsoft.Teams.Apps.Celebration\Microsoft.Teams.Apps.Celebration.csproj" /nologo /verbosity:m /t:Build /p:AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\Source\\" %SCM_BUILD_ARGS% 79 | ) 80 | 81 | IF !ERRORLEVEL! NEQ 0 goto error 82 | 83 | :: 3. KuduSync 84 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" ( 85 | call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd" 86 | IF !ERRORLEVEL! NEQ 0 goto error 87 | ) 88 | 89 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 90 | goto end 91 | 92 | :: Execute command routine that will echo out when error 93 | :ExecuteCmd 94 | setlocal 95 | set _CMD_=%* 96 | call %_CMD_% 97 | if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_% 98 | exit /b %ERRORLEVEL% 99 | 100 | :error 101 | endlocal 102 | echo An error has occurred during web site deployment. 103 | call :exitSetErrorLevel 104 | call :exitFromFunction 2>nul 105 | 106 | :exitSetErrorLevel 107 | exit /b 1 108 | 109 | :exitFromFunction 110 | () 111 | 112 | :end 113 | endlocal 114 | echo Finished successfully. 115 | -------------------------------------------------------------------------------- /deploy.cmd: -------------------------------------------------------------------------------- 1 | @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off 2 | 3 | IF "%SITE_ROLE%" == "app" ( 4 | deploy.app.cmd 5 | ) ELSE ( 6 | IF "%SITE_ROLE%" == "function" ( 7 | deploy.function.cmd 8 | ) ELSE ( 9 | echo You have to set SITE_ROLE setting to either "app" or "function" 10 | exit /b 1 11 | ) 12 | ) -------------------------------------------------------------------------------- /deploy.function.cmd: -------------------------------------------------------------------------------- 1 | @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off 2 | 3 | :: ---------------------- 4 | :: KUDU Deployment Script 5 | :: Version: 1.0.17 6 | :: ---------------------- 7 | 8 | :: Prerequisites 9 | :: ------------- 10 | 11 | :: Verify node.js installed 12 | where node 2>nul >nul 13 | IF %ERRORLEVEL% NEQ 0 ( 14 | echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment. 15 | goto error 16 | ) 17 | 18 | :: Setup 19 | :: ----- 20 | 21 | setlocal enabledelayedexpansion 22 | 23 | SET ARTIFACTS=%~dp0%..\artifacts 24 | 25 | IF NOT DEFINED DEPLOYMENT_SOURCE ( 26 | SET DEPLOYMENT_SOURCE=%~dp0%. 27 | ) 28 | 29 | IF NOT DEFINED DEPLOYMENT_TARGET ( 30 | SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot 31 | ) 32 | 33 | IF NOT DEFINED NEXT_MANIFEST_PATH ( 34 | SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest 35 | 36 | IF NOT DEFINED PREVIOUS_MANIFEST_PATH ( 37 | SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest 38 | ) 39 | ) 40 | 41 | IF NOT DEFINED KUDU_SYNC_CMD ( 42 | :: Install kudu sync 43 | echo Installing Kudu Sync 44 | call npm install kudusync -g --silent 45 | IF !ERRORLEVEL! NEQ 0 goto error 46 | 47 | :: Locally just running "kuduSync" would also work 48 | SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd 49 | ) 50 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 51 | :: Deployment 52 | :: ---------- 53 | echo Handling function App deployment with Msbuild. 54 | 55 | :: 1. Restore nuget packages 56 | call :ExecuteCmd nuget.exe restore "%DEPLOYMENT_SOURCE%\Source\Microsoft.Teams.Apps.Celebration.sln" -MSBuildPath "%MSBUILD_15_DIR%" 57 | IF !ERRORLEVEL! NEQ 0 goto error 58 | 59 | :: 2. Build and publish 60 | call :ExecuteCmd "%MSBUILD_15_DIR%\MSBuild.exe" "%DEPLOYMENT_SOURCE%\Source\Microsoft.Teams.Apps.Celebration.Notification.Func\Microsoft.Teams.Apps.Celebration.Notification.Func.csproj" /p:DeployOnBuild=true /p:configuration=Release /p:publishurl="%DEPLOYMENT_TEMP%" %SCM_BUILD_ARGS% 61 | IF !ERRORLEVEL! NEQ 0 goto error 62 | 63 | :: 3. KuduSync 64 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" ( 65 | call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd" 66 | IF !ERRORLEVEL! NEQ 0 goto error 67 | ) 68 | 69 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 70 | goto end 71 | 72 | :: Execute command routine that will echo out when error 73 | :ExecuteCmd 74 | setlocal 75 | set _CMD_=%* 76 | call %_CMD_% 77 | if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_% 78 | exit /b %ERRORLEVEL% 79 | 80 | :error 81 | endlocal 82 | echo An error has occurred during web site deployment. 83 | call :exitSetErrorLevel 84 | call :exitFromFunction 2>nul 85 | 86 | :exitSetErrorLevel 87 | exit /b 1 88 | 89 | :exitFromFunction 90 | () 91 | 92 | :end 93 | endlocal 94 | echo Finished successfully. 95 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.515" 4 | } 5 | } --------------------------------------------------------------------------------