├── .deployment ├── .gitignore ├── Build ├── Analyzer.ruleset └── stylecop.json ├── CODE_OF_CONDUCT.md ├── Deployment └── azuredeploy.json ├── LICENSE ├── Manifest ├── color.png ├── manifest.json └── outline.png ├── README.md ├── SECURITY.md ├── Source ├── Microsoft.Teams.Apps.Grow.sln └── Microsoft.Teams.Apps.Grow │ ├── Authentication │ ├── AuthenticationPolicy │ │ ├── MustBeTeamMemberUserPolicyHandler.cs │ │ ├── MustBeTeamMemberUserPolicyRequirement.cs │ │ └── PolicyNames.cs │ └── AuthenticationServiceCollectionExtensions.cs │ ├── Bot │ ├── GrowActivityHandler.cs │ ├── GrowActivityMiddleware.cs │ ├── GrowAdapterWithErrorHandler.cs │ └── GrowLocalizationCultureProvider.cs │ ├── Cards │ ├── CarouselCard.cs │ ├── UserNotificationCard.cs │ └── WelcomeCard.cs │ ├── ClientApp │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── locales │ │ │ └── en-US │ │ │ └── translation.json │ ├── src │ │ ├── api │ │ │ ├── acquired-skills-api.ts │ │ │ ├── authentication-metadata-api.ts │ │ │ ├── axios-decorator.ts │ │ │ ├── discover-api.ts │ │ │ └── teams-config-tab-api.ts │ │ ├── app.tsx │ │ ├── components │ │ │ ├── card-view │ │ │ │ ├── author-label-wrapper.tsx │ │ │ │ ├── card.tsx │ │ │ │ ├── discover-teams-wrapper-page.tsx │ │ │ │ ├── discover-wrapper-page.tsx │ │ │ │ ├── filter-no-post-content-page.tsx │ │ │ │ ├── more-menu-content.tsx │ │ │ │ ├── no-post-added-page.tsx │ │ │ │ ├── popup-more-menu.tsx │ │ │ │ ├── tag.tsx │ │ │ │ ├── thumbnail.tsx │ │ │ │ ├── type-label-card.tsx │ │ │ │ ├── type-label.tsx │ │ │ │ └── upvotes.tsx │ │ │ ├── checkbox-wrapper.tsx │ │ │ ├── close-project │ │ │ │ ├── close-project-dialog.tsx │ │ │ │ ├── close-project-table.tsx │ │ │ │ ├── close-project-wrapper.tsx │ │ │ │ └── skills.tsx │ │ │ ├── configure-preference-dialog │ │ │ │ ├── no-tag-found.tsx │ │ │ │ └── preferences-suggestion-list.tsx │ │ │ ├── edit-project-dialog │ │ │ │ ├── edit-project-dialog-content.tsx │ │ │ │ ├── edit-project-dialog.tsx │ │ │ │ └── edit-team-member.tsx │ │ │ ├── error-page.tsx │ │ │ ├── filter-bar-teams │ │ │ │ └── title-bar-teams.tsx │ │ │ ├── filter-bar │ │ │ │ ├── command-bar.tsx │ │ │ │ ├── filter-bar.tsx │ │ │ │ └── title-bar.tsx │ │ │ ├── join-project-dialog │ │ │ │ ├── join-project-dialog-content.tsx │ │ │ │ ├── join-project-dialog-wrapper-title.tsx │ │ │ │ ├── join-project-dialog-wrapper.tsx │ │ │ │ ├── join-project-dialog.tsx │ │ │ │ └── join-project-success.tsx │ │ │ ├── my-joined-projects │ │ │ │ └── my-joined-projects.tsx │ │ │ ├── my-projects │ │ │ │ ├── my-created-projects.tsx │ │ │ │ └── my-projects.tsx │ │ │ ├── new-project-dialog │ │ │ │ ├── date-picker.tsx │ │ │ │ ├── document-url.tsx │ │ │ │ ├── new-project-dialog-content.tsx │ │ │ │ └── new-project-dialog.tsx │ │ │ ├── notification-message │ │ │ │ └── notification-message.tsx │ │ │ ├── popup-menu │ │ │ │ ├── popup-menu-checkboxes-content.tsx │ │ │ │ ├── popup-menu-radiogroup-content.tsx │ │ │ │ └── popup-menu-wrapper.tsx │ │ │ ├── radiogroup-wrapper.tsx │ │ │ ├── redirect.tsx │ │ │ ├── signin │ │ │ │ ├── signin-end.tsx │ │ │ │ ├── signin-start.tsx │ │ │ │ └── signin.tsx │ │ │ ├── skills-aquired-tab │ │ │ │ ├── skills-aquired-table.tsx │ │ │ │ ├── skills-aquired-wrapper.tsx │ │ │ │ └── user-avatar.tsx │ │ │ └── teams-config-page.tsx │ │ ├── configVariables.ts │ │ ├── constants │ │ │ └── resources.ts │ │ ├── helpers │ │ │ ├── DarkCustomizations.ts │ │ │ ├── DatePickerStyles.ts │ │ │ ├── DefaultCustomizations.ts │ │ │ ├── PeoplePickerStyles.ts │ │ │ └── helper.ts │ │ ├── i18n.ts │ │ ├── index.tsx │ │ ├── react-app-env.d.ts │ │ ├── router │ │ │ └── router.tsx │ │ └── styles │ │ │ ├── alert.css │ │ │ ├── card.css │ │ │ ├── close-project.css │ │ │ ├── command-bar.css │ │ │ ├── configure-preferences.css │ │ │ ├── edit-dialog.css │ │ │ ├── filter-bar.css │ │ │ ├── join-project-dialog.css │ │ │ ├── join-project-taskmodule-view.css │ │ │ ├── more-menu-content.css │ │ │ ├── more-menu.css │ │ │ ├── new-project-dialog.css │ │ │ ├── no-post-added-page.css │ │ │ ├── popup-menu.css │ │ │ ├── private-list.css │ │ │ ├── projects-cards.css │ │ │ ├── signin.css │ │ │ ├── site.css │ │ │ └── teams-config-tab.css │ ├── tsconfig.json │ └── tslint.json │ ├── Common │ ├── CacheKeysConstants.cs │ ├── Constants.cs │ ├── Interfaces │ │ ├── IAcquiredSkillStorageProvider.cs │ │ ├── IMessagingExtensionHelper.cs │ │ ├── IProjectHelper.cs │ │ ├── IProjectSearchService.cs │ │ ├── IProjectStorageProvider.cs │ │ ├── ITeamSkillHelper.cs │ │ ├── ITeamSkillStorageProvider.cs │ │ ├── ITeamStorageProvider.cs │ │ ├── ITeamsInfoHelper.cs │ │ └── IUserDetailProvider.cs │ ├── Providers │ │ ├── AcquiredSkillStorageProvider.cs │ │ ├── BaseStorageProvider.cs │ │ ├── ProjectStorageProvider.cs │ │ ├── TeamSkillStorageProvider.cs │ │ ├── TeamStorageProvider.cs │ │ └── UserDetailProvider.cs │ └── SearchServices │ │ └── ProjectSearchService.cs │ ├── Controllers │ ├── AcquiredSkillController.cs │ ├── AuthenticationMetadataController.cs │ ├── BaseGrowController.cs │ ├── BotController.cs │ ├── ProjectController.cs │ ├── ProjectWorkflowController.cs │ ├── TeamProjectController.cs │ └── TeamSkillsController.cs │ ├── Helpers │ ├── CustomValidations │ │ ├── DocumentLinksValidationAttribute.cs │ │ ├── ProjectSkillsValidationAttribute.cs │ │ └── TeamSkillsValidationAttribute.cs │ ├── MessagingExtensionHelper.cs │ ├── NotificationHelper.cs │ ├── ProjectHelper.cs │ ├── ProjectStatusHelper.cs │ ├── TeamSkillHelper.cs │ └── TeamsInfoHelper.cs │ ├── Microsoft.Teams.Apps.Grow.csproj │ ├── Models │ ├── AcquiredSkillsEntity.cs │ ├── BotCommand.cs │ ├── Card │ │ └── AdaptiveSubmitActionData.cs │ ├── CloseProjectModel.cs │ ├── Configuration │ │ ├── AzureActiveDirectorySettings.cs │ │ ├── BotSettings.cs │ │ ├── SearchServiceSetting.cs │ │ ├── StorageSetting.cs │ │ └── TelemetrySetting.cs │ ├── ConversationTypes.cs │ ├── ErrorResponse.cs │ ├── JoinProject.cs │ ├── ProjectEntity.cs │ ├── ProjectParticipantModel.cs │ ├── ProjectSearchScope.cs │ ├── ProjectStatus.cs │ ├── ProjectStatusDisplayInfo.cs │ ├── TeamEntity.cs │ ├── TeamSkillEntity.cs │ ├── UserConversationState.cs │ └── UserDetailEntity.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Resources │ ├── Strings.Designer.cs │ └── Strings.resx │ ├── ServicesExtension.cs │ ├── Startup.cs │ ├── appsettings.json │ └── wwwroot │ └── Artifacts │ ├── activeStatusDot.png │ ├── applicationLogo.png │ ├── blockedStatusDot.png │ ├── carouselImage1.jpg │ ├── carouselImage2.jpg │ ├── carouselImage3.jpg │ ├── closedStatusDot.png │ ├── notStartedStatusDot.png │ └── peopleAvatar.png ├── deploy.cmd └── global.json /.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | command = deploy.cmd -------------------------------------------------------------------------------- /Build/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "Microsoft" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /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-growyourskills/201faece1b8ca98af2012f52b7d4768ff9731ee5/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": "94ef83a1-f72e-4fff-8358-0dd0465eae55", 6 | "packageName": "com.microsoft.teams.apptemplates.growyourskills", 7 | "developer": { 8 | "name": "", 9 | "websiteUrl": "", 10 | "privacyUrl": "", 11 | "termsOfUseUrl": "" 12 | }, 13 | "icons": { 14 | "color": "color.png", 15 | "outline": "outline.png" 16 | }, 17 | "name": { 18 | "short": "Grow Your Skills", 19 | "full": "Grow Your Skills" 20 | }, 21 | "description": { 22 | "short": "Encourage employees to discover opportunities, contribute and acquire new skills", 23 | "full": "Grow Your Skills is a platform for employees to discover new projects/opportunities based on their interests, learn and acquire skills based upon the competencies required and qualifications." 24 | }, 25 | "accentColor": "#B8FF4A", 26 | "configurableTabs": [ 27 | { 28 | "configurationUrl": "/configtab", 29 | "canUpdateConfiguration": true, 30 | "scopes": [ "team" ] 31 | } 32 | ], 33 | "staticTabs": [ 34 | { 35 | "entityId": "DiscoverProjectsTab", 36 | "name": "Discover", 37 | "contentUrl": "", 38 | "websiteUrl": "", 39 | "scopes": [ "personal" ] 40 | }, 41 | { 42 | "entityId": "YourProjectsTab", 43 | "name": "Your projects", 44 | "contentUrl": "/my-projects", 45 | "websiteUrl": "/my-projects", 46 | "scopes": [ "personal" ] 47 | }, 48 | { 49 | "entityId": "AcquiredSkillsTab", 50 | "name": "Skills acquired", 51 | "contentUrl": "/skill-acquired", 52 | "websiteUrl": "/skill-acquired", 53 | "scopes": [ "personal" ] 54 | } 55 | ], 56 | "bots": [ 57 | { 58 | "botId": "", 59 | "scopes": [ "personal", "team" ], 60 | "commandLists": [ 61 | { 62 | "scopes": [ "personal" ], 63 | "commands": [ 64 | { 65 | "title": "Help", 66 | "description": "Display commands and what they do." 67 | } 68 | ] 69 | } 70 | ], 71 | "supportsFiles": false, 72 | "isNotificationOnly": false 73 | } 74 | ], 75 | "composeExtensions": [ 76 | { 77 | "botId": "", 78 | "canUpdateConfiguration": false, 79 | "commands": [ 80 | { 81 | "id": "allProjects", 82 | "title": "All", 83 | "description": "Show all created projects.", 84 | "parameters": [ 85 | { 86 | "name": "searchText", 87 | "title": "allProjects", 88 | "description": "Search projects" 89 | } 90 | ], 91 | "initialRun": true 92 | }, 93 | { 94 | "id": "joinedProjects", 95 | "title": "Projects joined", 96 | "description": "Show current user joined projects.", 97 | "parameters": [ 98 | { 99 | "name": "searchText", 100 | "title": "Projects joined", 101 | "description": "Search projects" 102 | } 103 | ], 104 | "initialRun": true 105 | }, 106 | { 107 | "id": "createdProjectsByMe", 108 | "title": "Projects created", 109 | "description": "Show projects created by current user.", 110 | "parameters": [ 111 | { 112 | "name": "searchText", 113 | "title": "Projects created", 114 | "description": "Search projects" 115 | } 116 | ], 117 | "initialRun": true 118 | } 119 | ] 120 | } 121 | ], 122 | "validDomains": [ 123 | "" 124 | ], 125 | "webApplicationInfo": { 126 | "id": "", 127 | "resource": "" 128 | } 129 | } -------------------------------------------------------------------------------- /Manifest/outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-apps-growyourskills/201faece1b8ca98af2012f52b7d4768ff9731ee5/Manifest/outline.png -------------------------------------------------------------------------------- /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 definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), 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 [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 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Teams.Apps.Grow", "Microsoft.Teams.Apps.Grow\Microsoft.Teams.Apps.Grow.csproj", "{C9724202-3D68-4421-AED3-F1D0083E6A7B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Debug|x64.ActiveCfg = Debug|Any CPU 21 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Debug|x64.Build.0 = Debug|Any CPU 22 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Debug|x86.ActiveCfg = Debug|Any CPU 23 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Debug|x86.Build.0 = Debug|Any CPU 24 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Release|x64.ActiveCfg = Release|Any CPU 27 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Release|x64.Build.0 = Release|Any CPU 28 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Release|x86.ActiveCfg = Release|Any CPU 29 | {C9724202-3D68-4421-AED3-F1D0083E6A7B}.Release|x86.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {F6C8B0E2-D8EA-40F5-A577-3DC19586CAEE} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/Authentication/AuthenticationPolicy/MustBeTeamMemberUserPolicyRequirement.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Grow.Authentication.AuthenticationPolicy 6 | { 7 | using Microsoft.AspNetCore.Authorization; 8 | 9 | /// 10 | /// This authorization class implements the marker interface 11 | /// to check if user meets teams member specific requirements 12 | /// for accesing resources. 13 | /// 14 | public class MustBeTeamMemberUserPolicyRequirement : IAuthorizationRequirement 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/Authentication/AuthenticationPolicy/PolicyNames.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Grow.Authentication.AuthenticationPolicy 6 | { 7 | /// 8 | /// This class list the policy name of custom authorizations implemented in project. 9 | /// 10 | public static class PolicyNames 11 | { 12 | /// 13 | /// The name of the authorization policy, MustBeTeamMemberUserPolicy. 14 | /// Indicates that user is a part of team and has permission to edit created project. 15 | /// 16 | public const string MustBeTeamMemberUserPolicy = "MustBeTeamMemberUserPolicy"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/Bot/GrowAdapterWithErrorHandler.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Grow.Bot 6 | { 7 | using System; 8 | using Microsoft.Bot.Builder; 9 | using Microsoft.Bot.Builder.Integration.AspNet.Core; 10 | using Microsoft.Bot.Connector.Authentication; 11 | using Microsoft.Extensions.Localization; 12 | using Microsoft.Extensions.Logging; 13 | 14 | /// 15 | /// Class that implements error handler. 16 | /// 17 | public class GrowAdapterWithErrorHandler : BotFrameworkHttpAdapter 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// Credential provider for bot. 23 | /// Logger implementation to send logs to the logger service. 24 | /// Framework channel service. 25 | /// Represents middleware that can operate on incoming activities. 26 | /// The current cultures' string localizer. 27 | /// A state management object for conversation state. 28 | public GrowAdapterWithErrorHandler( 29 | ICredentialProvider credentialProvider, 30 | ILogger logger, 31 | IChannelProvider channelProvider, 32 | GrowActivityMiddleware growActivityMiddleware, 33 | IStringLocalizer localizer, 34 | ConversationState conversationState = null) 35 | : base(credentialProvider, channelProvider: channelProvider, logger: logger) 36 | { 37 | growActivityMiddleware = growActivityMiddleware ?? throw new NullReferenceException(nameof(growActivityMiddleware)); 38 | 39 | // Add activity middleware to the adapter's middleware pipeline 40 | this.Use(growActivityMiddleware); 41 | 42 | this.OnTurnError = async (turnContext, exception) => 43 | { 44 | // Log any leaked exception from the application. 45 | logger.LogError(exception, $"Exception caught : {exception.Message}"); 46 | 47 | // Send a catch-all apology to the user. 48 | await turnContext.SendActivityAsync(localizer.GetString("ErrorMessage")); 49 | 50 | if (conversationState != null) 51 | { 52 | try 53 | { 54 | // Delete the conversationState for the current conversation to prevent the 55 | // bot from getting stuck in a error-loop caused by being in a bad state. 56 | // ConversationState should be thought of as similar to "cookie-state" in a Web pages. 57 | await conversationState.DeleteAsync(turnContext); 58 | } 59 | catch (Exception ex) 60 | { 61 | logger.LogError(ex, $"Exception caught on attempting to delete conversation state : {ex.Message}"); 62 | throw; 63 | } 64 | } 65 | }; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/Bot/GrowLocalizationCultureProvider.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Grow.Bot 6 | { 7 | using System; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using Microsoft.AspNetCore.Http; 13 | using Microsoft.AspNetCore.Localization; 14 | using Microsoft.Bot.Schema; 15 | using Newtonsoft.Json; 16 | using Newtonsoft.Json.Linq; 17 | 18 | /// 19 | /// This class is responsible for implementing the for Bot Activities 20 | /// received from BotFramework. 21 | /// 22 | internal sealed class GrowLocalizationCultureProvider : IRequestCultureProvider 23 | { 24 | /// 25 | /// Get the culture of current request. 26 | /// 27 | /// The current HTTP request. 28 | /// A Task resolving to the culture info if found, null otherwise. 29 | public async Task DetermineProviderCultureResult(HttpContext httpContext) 30 | { 31 | if (httpContext?.Request?.Body?.CanRead != true) 32 | { 33 | return null; 34 | } 35 | 36 | var isBotFrameworkUserAgent = 37 | httpContext.Request.Headers["User-Agent"] 38 | .Any(userAgent => userAgent.Contains("Microsoft-BotFramework", StringComparison.OrdinalIgnoreCase)); 39 | 40 | try 41 | { 42 | if (isBotFrameworkUserAgent) 43 | { 44 | // Wrap the request stream so that we can rewind it back to the start for regular request processing. 45 | httpContext.Request.EnableBuffering(); 46 | 47 | // Read the request body, parse out the activity object, and set the parsed culture information. 48 | var streamReader = new StreamReader(httpContext.Request.Body, Encoding.UTF8, true, 1024, leaveOpen: true); 49 | using (var jsonReader = new JsonTextReader(streamReader)) 50 | { 51 | var obj = await JObject.LoadAsync(jsonReader); 52 | var activity = obj.ToObject(); 53 | 54 | var result = new ProviderCultureResult(activity.Locale); 55 | httpContext.Request.Body.Seek(0, SeekOrigin.Begin); 56 | return result; 57 | } 58 | } 59 | } 60 | catch (Exception) 61 | { 62 | throw; 63 | } 64 | 65 | return null; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/Cards/CarouselCard.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | namespace Microsoft.Teams.Apps.Grow.Cards 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using Microsoft.Bot.Schema; 10 | 11 | /// 12 | /// Carousal card for help command. 13 | /// 14 | public static class CarouselCard 15 | { 16 | /// 17 | /// Create the set of cards that comprise the user help carousel. 18 | /// 19 | /// Application base path to get the logo of the application. 20 | /// The cards that comprise the user tour. 21 | public static IEnumerable GetUserHelpCards(string applicationBasePath) 22 | { 23 | return new List() 24 | { 25 | GetCarouselCards(string.Empty, Strings.CarouselCard1Text, applicationBasePath + "/Artifacts/carouselImage1.jpg"), 26 | GetCarouselCards(string.Empty, Strings.CarouselCard2Text, applicationBasePath + "/Artifacts/carouselImage2.jpg"), 27 | GetCarouselCards(string.Empty, string.Format(CultureInfo.InvariantCulture, Strings.CarouselCard3Text, $"{Strings.ApplicationName}"), applicationBasePath + "/Artifacts/carouselImage3.jpg"), 28 | }; 29 | } 30 | 31 | private static Attachment GetCarouselCards(string title, string text, string imageUri) 32 | { 33 | HeroCard userHelpCarouselCard = new HeroCard() 34 | { 35 | Title = title, 36 | Text = text, 37 | Images = new List() 38 | { 39 | new CardImage(imageUri), 40 | }, 41 | }; 42 | 43 | return userHelpCarouselCard.ToAttachment(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Grow", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fluentui/react": "^7.115.4", 7 | "@fluentui/react-icons-northstar": "^0.49.0", 8 | "@fluentui/react-northstar": "^0.49.0", 9 | "@microsoft/teams-js": "1.6.0", 10 | "@uifabric/icons": "7.3.47", 11 | "@uifabric/variants": "^7.1.82", 12 | "axios": "0.19.2", 13 | "bootstrap": "4.5.0", 14 | "i18next": "19.4.5", 15 | "i18next-xhr-backend": "^3.2.2", 16 | "moment": "^2.26.0", 17 | "node-sass": "^4.14.1", 18 | "react": "16.13.1", 19 | "react-bootstrap": "1.0.1", 20 | "react-dom": "16.13.1", 21 | "react-i18next": "11.5.0", 22 | "react-infinite-scroller": "^1.2.4", 23 | "react-router-dom": "5.2.0", 24 | "react-scripts": "3.4.1", 25 | "typescript": "3.9.3" 26 | }, 27 | "scripts": { 28 | "start": "react-scripts start", 29 | "build": "react-scripts build", 30 | "test": "react-scripts test", 31 | "eject": "react-scripts eject" 32 | }, 33 | "eslintConfig": { 34 | "extends": "react-app" 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.2%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 1 chrome version", 44 | "last 1 firefox version", 45 | "last 1 safari version" 46 | ] 47 | }, 48 | "devDependencies": { 49 | "@testing-library/jest-dom": "^4.2.4", 50 | "@testing-library/react": "^9.4.0", 51 | "@testing-library/user-event": "^7.2.1", 52 | "@types/jest": "^24.0.24", 53 | "@types/node": "^12.12.21", 54 | "@types/react": "^16.9.17", 55 | "@types/react-dom": "^16.9.4", 56 | "@types/react-router-dom": "^4.3.3" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/public/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Grow 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/src/api/acquired-skills-api.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | import axios from "./axios-decorator"; 6 | import { getBaseUrl } from '../configVariables'; 7 | 8 | let baseAxiosUrl = getBaseUrl() + '/api'; 9 | 10 | /** 11 | * Get acquired skills for tab. 12 | */ 13 | export const getUserAcquiredSkills = async (): Promise => { 14 | let url = `${baseAxiosUrl}/acquiredskill/acquired-skills`; 15 | return await axios.get(url); 16 | } 17 | 18 | /** 19 | * Leave a project selected by user. 20 | * @param post Project details data to leave a project. 21 | */ 22 | export const leaveProject = async (project: any): Promise => { 23 | 24 | let url = `${baseAxiosUrl}/project-workflow/leave-project?projectId=${project.projectId}&createdByUserId=${project.createdByUserId}`; 25 | return await axios.post(url); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/src/api/authentication-metadata-api.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | import axios from "./axios-decorator"; 6 | import { getBaseUrl } from '../configVariables'; 7 | 8 | let baseAxiosUrl = getBaseUrl() + '/api'; 9 | 10 | /** 11 | * Get authentication metadata from API 12 | * @param {String} windowLocationOriginDomain Application base URL 13 | * @param {String} login_hint Login hint for SSO 14 | */ 15 | export const getAuthenticationConsentMetadata = async (windowLocationOriginDomain: string, login_hint: string): Promise => { 16 | let url = `${baseAxiosUrl}/authenticationMetadata/consentUrl?windowLocationOriginDomain=${windowLocationOriginDomain}&loginhint=${login_hint}`; 17 | return await axios.get(url, undefined, false); 18 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/src/api/teams-config-tab-api.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | import axios from "./axios-decorator"; 6 | import { getBaseUrl } from '../configVariables'; 7 | 8 | let baseAxiosUrl = getBaseUrl() + '/api'; 9 | 10 | /** 11 | * Project config skills for a team. 12 | * @param postContent Skills to be saved. 13 | */ 14 | export const submitConfiguredSkills = async (postContent: any): Promise => { 15 | let url = `${baseAxiosUrl}/teamskills`; 16 | return await axios.post(url, postContent); 17 | } 18 | 19 | /** 20 | * Get configured skills for a team. 21 | * @param teamId Team Id for which configured skills needs to be fetched. 22 | */ 23 | export const getConfigSkills = async (teamId: string): Promise => { 24 | let url = `${baseAxiosUrl}/teamskills?teamId=${teamId}`; 25 | return await axios.get(url); 26 | } 27 | 28 | /** 29 | * Filter skills as per user search input. 30 | * @param searchText Search text entered by user for filtering skills. 31 | */ 32 | export const filterSkills = async (searchText: string): Promise => { 33 | let url = `${baseAxiosUrl}/project/unique-skills?searchText=${encodeURIComponent(searchText)}`; 34 | return await axios.get(url); 35 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/src/app.tsx: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | import * as React from "react"; 6 | import { AppRoute } from "./router/router"; 7 | import Resources from "./constants/resources"; 8 | import * as microsoftTeams from "@microsoft/teams-js"; 9 | import { Provider, themes } from "@fluentui/react-northstar"; 10 | 11 | import "./styles/site.css"; 12 | 13 | export interface IAppState { 14 | theme: string; 15 | } 16 | 17 | export default class App extends React.Component<{}, IAppState> { 18 | theme?: string | null; 19 | 20 | constructor(props: any) { 21 | super(props); 22 | let search = window.location.search; 23 | let params = new URLSearchParams(search); 24 | this.theme = params.get("theme"); 25 | 26 | this.state = { 27 | theme: this.theme ? this.theme : Resources.default, 28 | } 29 | } 30 | 31 | componentDidMount() { 32 | microsoftTeams.initialize(); 33 | microsoftTeams.getContext((context: microsoftTeams.Context) => { 34 | this.setState({ theme: context.theme! }); 35 | }); 36 | 37 | microsoftTeams.registerOnThemeChangeHandler((theme: string) => { 38 | this.setState({ theme: theme! }, () => { 39 | this.forceUpdate(); 40 | }); 41 | }); 42 | } 43 | 44 | public setThemeComponent = () => { 45 | if (this.state.theme === Resources.dark) { 46 | return ( 47 | 48 |
49 | {this.getAppDom()} 50 |
51 |
52 | ); 53 | } 54 | else if (this.state.theme === Resources.contrast) { 55 | return ( 56 | 57 |
58 | {this.getAppDom()} 59 |
60 |
61 | ); 62 | } else { 63 | return ( 64 | 65 |
66 | {this.getAppDom()} 67 |
68 |
69 | ); 70 | } 71 | } 72 | 73 | public getAppDom = () => { 74 | return ( 75 |
76 | 77 |
); 78 | } 79 | 80 | /** 81 | * Renders the component 82 | */ 83 | public render(): JSX.Element { 84 | return ( 85 |
86 | {this.setThemeComponent()} 87 |
88 | ); 89 | } 90 | } -------------------------------------------------------------------------------- /Source/Microsoft.Teams.Apps.Grow/ClientApp/src/components/card-view/author-label-wrapper.tsx: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft. All rights reserved. 3 | // 4 | 5 | import * as React from "react"; 6 | import { Label, Text } from "@fluentui/react-northstar"; 7 | 8 | interface IAuthorProps { 9 | authorName: string; 10 | authorImageUrl?: string; 11 | showImage: boolean; 12 | } 13 | 14 | const AuthorLabelWrapper: React.FunctionComponent = props => { 15 | if (props.showImage) { 16 | return ( 17 |