diff --git a/.github/workflows/build-complete-samples.yml b/.github/workflows/build-complete-samples.yml index 12e6238e3e..240a0fbee1 100644 --- a/.github/workflows/build-complete-samples.yml +++ b/.github/workflows/build-complete-samples.yml @@ -168,6 +168,10 @@ jobs: name: 'app-region-selection' version: '6.0.x' + - project_path: 'samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Microsoft.Teams.Samples.TabAppNavigation.Web.csproj' + name: 'tab-app-navigation' + version: '6.0.x' + - project_path: 'samples/tab-external-auth/csharp/TabExternalAuth/TabExternalAuth.csproj' name: 'tab-external-auth' version: '6.0.x' @@ -200,6 +204,10 @@ jobs: name: 'tab-channel-group-config-page-auth' version: '6.0.x' + - project_path: 'samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj' + name: 'tab-channel-group-quickstart' + version: '6.0.x' + - project_path: 'samples/tab-deeplink/csharp/DeepLinkBot/DeepLinkBot.csproj' name: 'tab-deeplink' version: '6.0.x' diff --git a/README.md b/README.md index e75a786bfb..9de71977b4 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,8 @@ The [Teams Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDev |----|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|-----------------------------------------|----------------------------------------------|---------------------------------------------|--------------------------------------------------------------------| | 1 | Personal tabs | Sample app showing custom personal Tab with ASP.NET Core. | Basic | [MVC][personal-tab#cs#mvc], [Razor][personal-tab#cs#razor] | [Yeoman Generator](https://docs.microsoft.com/en-us/microsoftteams/platform/get-started/get-started-yeoman) | | [View](/samples/tab-personal/mvc-csharp/demo-manifest/tab-personal.zip) | | 2 | Personal tab quick-start | Sample personal tab quick-start app. | Basic | | [View][personal-tab-quickstart#ts] ![toolkit-icon](assets/toolkit-icon.png) | [View][personal-tab-quickstart#js] ![toolkit-icon](assets/toolkit-icon.png) | | -| 3 | Channel and group tabs | Sample app showing custom group and channel Tab with ASP.NET Core. | Basic | [MVC][group-channel-tab#cs#mvc], [Razor][group-channel-tab#cs#razor] | [Yeoman Generator](https://docs.microsoft.com/en-us/microsoftteams/platform/get-started/get-started-yeoman) | [View][tab-channel-group#js] ![toolkit-icon](assets/toolkit-icon.png) | [View](/samples/tab-channel-group/mvc-csharp/demo-manifest/tab-channel-group.zip) | -| 4 | Channel and group tab quick-start | Sample channel and group tab hello world app. | Basic | | [View][group-tab-quickstart#ts] ![toolkit-icon](assets/toolkit-icon.png) | [View][group-tab-quickstart#js] ![toolkit-icon](assets/toolkit-icon.png) | [View](/samples/tab-channel-group-quickstart/js/demo-manifest/tab-channel-group-quickstart.zip) | +| 3 | Channel and group tabs | Sample app showing custom group and channel Tab with ASP.NET Core. | Basic | [MVC][group-channel-tab#cs#mvc], [Razor][group-channel-tab#cs#razor] | [Yeoman Generator](https://docs.microsoft.com/en-us/microsoftteams/platform/get-started/get-started-yeoman) | | [View](/samples/tab-channel-group/mvc-csharp/demo-manifest/tab-channel-group.zip) | +| 4 | Channel and group tab quick-start | Sample channel and group tab hello world app. | Basic | [View][group-tab-quickstart#csharp] | [View][group-tab-quickstart#ts] ![toolkit-icon](assets/toolkit-icon.png) | [View][group-tab-quickstart#js] ![toolkit-icon](assets/toolkit-icon.png) | [View](/samples/tab-channel-group-quickstart/js/demo-manifest/tab-channel-group-quickstart.zip) | | 5 | SPFx Tab | Sample app showing Microsoft Teams tabs using SharePoint Framework. | Basic | | [View][group-channel-tab#ts#spfx] | | | | 6 | Tab people picker | This is a tab app that shows the feature of the client SDK people picker. | Basic | [View][tab-people-picker#csharp] | | [View][tab-people-picker#nodejs] ![toolkit-icon](assets/toolkit-icon.png) | [View](/samples/tab-people-picker/csharp/demo-manifest/Tab-People-Picker.zip) | | 7 | Tab channel context | This sample shows the contents of the tab context object in a private and shared channel. | Basic | | [View][tab-channel-context#nodejs] ![toolkit-icon](assets/toolkit-icon.png) | | | @@ -86,6 +86,7 @@ The [Teams Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDev | 26 | Tab Meeting Recording and transcript with auto recording | This sample shows meeting recording and transcript with auto recording. | Advanced | [View][tab-meeting-auto-recording#csharp] | | | | | 27 | Tab External Auth | This sample illustrates how to implement Google auth using external auth providers. | Advanced | [View][tab-external-auth#csharp] | | | | | 28 | Tab Meeting Recording and transcript with auto recording | This sample shows meeting recording and transcript with auto recording. | Advanced | [View][tab-meeting-auto-recording#csharp] | | | | +| 29 | Tab App Navigation | Sample app demonstrating tab navigation features in Microsoft Teams using ASP.NET Core. | Basic | [View][tab-app-navigation#csharp] | | | | ## [Bots samples](https://docs.microsoft.com/microsoftteams/platform/bots/what-are-bots) (using the v4 SDK) >NOTE: >Visit the [Bot Framework Samples repository][botframework] to view Microsoft Bot Framework v4 SDK task-focused samples for C#, JavaScript, TypeScript, and Python. @@ -278,6 +279,7 @@ The [Teams Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDev [personal-tab-sso-quickstart#ts]:samples/tab-personal-sso-quickstart/ts [personal-tab-sso-quickstart#csharp]:samples/tab-personal-sso-quickstart/csharp_dotnetcore [personal-tab-sso-quickstart#js]:samples/tab-personal-sso-quickstart/js +[group-tab-quickstart#csharp]:samples/tab-channel-group-quickstart/csharp [group-tab-quickstart#ts]:samples/tab-channel-group-quickstart/ts [group-tab-quickstart#js]:samples/tab-channel-group-quickstart/js [group-tab-sso-quickstart#ts]:samples/tab-channel-group-sso-quickstart/ts @@ -295,6 +297,7 @@ The [Teams Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDev [app-complete-auth#cs]:samples/app-complete-auth/csharp [app-complete-auth#nodejs]:samples/app-complete-auth/nodejs +[tab-app-navigation#csharp]:samples/tab-app-navigation/csharp [tab-conversation#nodejs]:samples/tab-conversations/nodejs [tab-stage-view#js]:samples/tab-stage-view/nodejs [tab-stage-view#csharp]:samples/tab-stage-view/csharp diff --git a/samples/tab-app-navigation/csharp/.gitignore b/samples/tab-app-navigation/csharp/.gitignore new file mode 100644 index 0000000000..763e8697f9 --- /dev/null +++ b/samples/tab-app-navigation/csharp/.gitignore @@ -0,0 +1,288 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppManifest/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs diff --git a/samples/tab-app-navigation/csharp/M365Agent/M365Agent.ttkproj b/samples/tab-app-navigation/csharp/M365Agent/M365Agent.ttkproj new file mode 100644 index 0000000000..a1fb20da33 --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/M365Agent.ttkproj @@ -0,0 +1,13 @@ + + + + 728ab578-560b-485d-843f-a5b234c86f2b + + + + + + + + + \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/M365Agent/appPackage/color.png b/samples/tab-app-navigation/csharp/M365Agent/appPackage/color.png new file mode 100644 index 0000000000..b8cf81afbe Binary files /dev/null and b/samples/tab-app-navigation/csharp/M365Agent/appPackage/color.png differ diff --git a/samples/tab-app-navigation/csharp/M365Agent/appPackage/manifest.json b/samples/tab-app-navigation/csharp/M365Agent/appPackage/manifest.json new file mode 100644 index 0000000000..d71e662aa3 --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/appPackage/manifest.json @@ -0,0 +1,84 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.19/MicrosoftTeams.schema.json", + "manifestVersion": "1.19", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Contoso", + "websiteUrl": "https://www.microsoft.com", + "privacyUrl": "https://www.microsoft.com/privacy", + "termsOfUseUrl": "https://www.microsoft.com/termsofuse" + }, + "name": { + "short": "Tab App Navigation", + "full": "Tab App Navigation App" + }, + "description": { + "short": "Sample showcasing tab navigation features within a Teams application.", + "full": "This sample illustrates the tab navigation feature in a Microsoft Teams application, enabling smooth transitions between different tabs. Designed for use with Node.js, it highlights how users can effectively navigate within the app for an enhanced experience." + }, + "icons": { + "outline": "outline.png", + "color": "color.png" + }, + "accentColor": "#60A18E", + "configurableTabs": [ + { + "configurationUrl": "https://${{BOT_DOMAIN}}/configure", + "canUpdateConfiguration": true, + "scopes": [ + "team" + ] + } + ], + "staticTabs": [ + { + "entityId": "default_tab", + "name": "Default Tab", + "contentUrl": "https://${{BOT_DOMAIN}}/default_tab", + "websiteUrl": "https://${{BOT_DOMAIN}}/default_tab", + "scopes": [ + "personal", + "team" + ] + }, + { + "entityId": "page1", + "name": "Page1", + "contentUrl": "https://${{BOT_DOMAIN}}/page1", + "websiteUrl": "https://${{BOT_DOMAIN}}/page1", + "scopes": [ + "personal", + "team" + ] + }, + { + "entityId": "page2", + "name": "Page2", + "contentUrl": "https://${{BOT_DOMAIN}}/page2", + "websiteUrl": "https://${{BOT_DOMAIN}}/page2", + "scopes": [ + "personal", + "team" + ] + }, + { + "entityId": "page3", + "name": "Page3", + "contentUrl": "https://${{BOT_DOMAIN}}/page3", + "websiteUrl": "https://${{BOT_DOMAIN}}/page3", + "scopes": [ + "personal", + "team" + ] + } + ], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [ + "localhost", + "${{BOT_DOMAIN}}" + ] +} \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/M365Agent/appPackage/outline.png b/samples/tab-app-navigation/csharp/M365Agent/appPackage/outline.png new file mode 100644 index 0000000000..2c3bf6fa65 Binary files /dev/null and b/samples/tab-app-navigation/csharp/M365Agent/appPackage/outline.png differ diff --git a/samples/tab-app-navigation/csharp/M365Agent/env/.env.local b/samples/tab-app-navigation/csharp/M365Agent/env/.env.local new file mode 100644 index 0000000000..7691b38433 --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/env/.env.local @@ -0,0 +1,8 @@ +# This file includes environment variables that can be committed to git. It's gitignored by default because it represents your local development environment. + +# Built-in environment variables +TEAMSFX_ENV=local +APP_NAME_SUFFIX=local + +# Generated during provision, you can also add your own variables. + diff --git a/samples/tab-app-navigation/csharp/M365Agent/infra/azure.bicep b/samples/tab-app-navigation/csharp/M365Agent/infra/azure.bicep new file mode 100644 index 0000000000..c3ce051b3d --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/infra/azure.bicep @@ -0,0 +1,44 @@ +@maxLength(20) +@minLength(4) +@description('Used to generate names for all resources in this file') +param resourceBaseName string + +@description('Required when create Azure Bot service') +param botAadAppClientId string + +param botAppDomain string + +@maxLength(42) +param botDisplayName string + +param botServiceName string = resourceBaseName +param botServiceSku string = 'F0' +param microsoftAppType string +param microsoftAppTenantId string + +// Register your web service as a bot with the Bot Framework +resource botService 'Microsoft.BotService/botServices@2021-03-01' = { + kind: 'azurebot' + location: 'global' + name: botServiceName + properties: { + displayName: botDisplayName + endpoint: 'https://${botAppDomain}/api/messages' + msaAppId: botAadAppClientId + msaAppType: microsoftAppType + msaAppTenantId: microsoftAppType == 'SingleTenant' ? microsoftAppTenantId : '' + } + sku: { + name: botServiceSku + } +} + +// Connect the bot service to Microsoft Teams +resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { + parent: botService + location: 'global' + name: 'MsTeamsChannel' + properties: { + channelName: 'MsTeamsChannel' + } +} diff --git a/samples/tab-app-navigation/csharp/M365Agent/infra/azure.parameters.json b/samples/tab-app-navigation/csharp/M365Agent/infra/azure.parameters.json new file mode 100644 index 0000000000..c99441f286 --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/infra/azure.parameters.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceBaseName": { + "value": "bot${{RESOURCE_SUFFIX}}" + }, + "botAadAppClientId": { + "value": "${{AAD_APP_CLIENT_ID}}" + }, + "botAppDomain": { + "value": "${{BOT_DOMAIN}}" + }, + "botDisplayName": { + "value": "tab-app-navigation" + }, + "microsoftAppType": { + "value": "${{MICROSOFT_APP_TYPE}}" + }, + "microsoftAppTenantId": { + "value": "${{MICROSOFT_APP_TENANT_ID}}" + } + } +} \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/M365Agent/launchSettings.json b/samples/tab-app-navigation/csharp/M365Agent/launchSettings.json new file mode 100644 index 0000000000..0e348677ed --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/launchSettings.json @@ -0,0 +1,23 @@ +{ + "profiles": { + // Debug project within Teams + "Microsoft Teams (browser)": { + "commandName": "Project", + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + // Launch project within Teams without prepare app dependencies + "Microsoft Teams (browser) (skip update app)": { + "commandName": "Project", + "environmentVariables": { "UPDATE_TEAMS_APP": "false" }, + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + "Microsoft 365 app (browser)": { + "commandName": "Project", + "launchUrl": "https://www.office.com/m365apps/${{M365_APP_ID}}?auth=2&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + "Outlook (browser)": { + "commandName": "Project", + "launchUrl": "https://outlook.office.com/host/${{M365_APP_ID}}?login_hint=${{TEAMSFX_M365_USER_NAME}}" + } + } +} \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/M365Agent/m365agents.local.yml b/samples/tab-app-navigation/csharp/M365Agent/m365agents.local.yml new file mode 100644 index 0000000000..3885468e8a --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/m365agents.local.yml @@ -0,0 +1,96 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.2 + +additionalMetadata: + sampleTag: Microsoft-Teams-Samples:tab-app-navigation-csharp + +provision: + - uses: aadApp/create # Creates a new Azure Active Directory (AAD) app to authenticate users if the environment variable that stores clientId is empty + with: + name: tab-app-navigation-aad # Note: when you run aadApp/update, the AAD app name will be updated based on the definition in manifest. If you don't want to change the name, make sure the name in AAD manifest is the same with the name defined here. + generateClientSecret: true # If the value is false, the action will not generate client secret for you + signInAudience: "AzureADMultipleOrgs" # Multitenant + writeToEnvironmentFile: # Write the information of created resources into environment file for the specified environment variable(s). + clientId: AAD_APP_CLIENT_ID + clientSecret: SECRET_AAD_APP_CLIENT_SECRET # Environment variable that starts with `SECRET_` will be stored to the .env.{envName}.user environment file + objectId: AAD_APP_OBJECT_ID + tenantId: AAD_APP_TENANT_ID + authority: AAD_APP_OAUTH_AUTHORITY + authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST + + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: app-tab-navigation-${{TEAMSFX_ENV}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + - uses: script + with: + run: + echo "::set-teamsfx-env MICROSOFT_APP_TYPE=SingleTenant"; + echo "::set-teamsfx-env MICROSOFT_APP_TENANT_ID=${{AAD_APP_TENANT_ID}}"; + + # Generate runtime appsettings to JSON file + - uses: file/createOrUpdateJsonFile + with: + target: ../Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.json + content: + MicrosoftAppType: SingleTenant + MicrosoftAppId: ${{AAD_APP_CLIENT_ID}} + MicrosoftAppPassword: ${{SECRET_AAD_APP_CLIENT_SECRET}} + MicrosoftAppTenantId: ${{AAD_APP_TENANT_ID}} + ExternalAppId: "" # Optional - you can get the external appId from teams admin portal. + + - uses: arm/deploy # Deploy given ARM templates parallelly. + with: + subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} # The AZURE_SUBSCRIPTION_ID is a built-in environment variable. TeamsFx will ask you select one subscription if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select subscription if it's empty in this case. + resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} # The AZURE_RESOURCE_GROUP_NAME is a built-in environment variable. TeamsFx will ask you to select or create one resource group if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select or create resource grouop if it's empty in this case. + templates: + - path: ./infra/azure.bicep + parameters: ./infra/azure.parameters.json + deploymentName: Create-resources-for-bot + bicepCliVersion: v0.9.1 # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, will use bicep CLI in PATH if you remove this config. + + # Validate using manifest schema + - uses: teamsApp/validateManifest + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + + # Build Teams app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputJsonPath: ./appPackage/build/manifest.${{TEAMSFX_ENV}}.json + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + + # Apply the Teams app manifest to an existing Teams app in + # Developer Portal. + # Will use the app id in manifest file to determine which Teams app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + + # Extend your Teams app to Outlook and the Microsoft 365 app + - uses: teamsApp/extendToM365 + with: + # Relative path to the build app package. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + titleId: M365_TITLE_ID + appId: M365_APP_ID \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/M365Agent/m365agents.yml b/samples/tab-app-navigation/csharp/M365Agent/m365agents.yml new file mode 100644 index 0000000000..efd1224f81 --- /dev/null +++ b/samples/tab-app-navigation/csharp/M365Agent/m365agents.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.2 + +additionalMetadata: + sampleTag: Microsoft-Teams-Samples:tab-app-navigation + +environmentFolderPath: ./env +projectId: diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/.gitignore b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/.gitignore new file mode 100644 index 0000000000..7466c01f9c --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/.gitignore @@ -0,0 +1,25 @@ +# TeamsFx files +build +appPackage/build +env/.env.*.user +env/.env.local +appsettings.Development.json +.deployment + +# User-specific files +*.user + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Notification local store +.notification.localstore.json \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Controllers/HomeController.cs b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Controllers/HomeController.cs new file mode 100644 index 0000000000..cf073c9443 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Controllers/HomeController.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Microsoft.AspNetCore.Mvc provides attributes and classes for building web APIs and MVC web apps in ASP.NET Core. +using Microsoft.AspNetCore.Mvc; + +namespace TabAppNavigation +{ + /// + /// HomeController is responsible for handling routes related to the main pages of the application. + /// This includes rendering the main index page, hello page, and other specific views. + /// + public class HomeController : Controller + { + /// + /// Displays the home page (Index view). + /// + /// Returns the Index view. + [Route("")] + public ActionResult Index() + { + return View(); + } + /// + /// Displays the default tab view. + /// + /// Returns the default tab view. + [Route("default_tab")] + public ActionResult defaultTab() + { + return View("TabAppNavigation"); + } + + + /// + /// Displays Page1. + /// + /// Returns the Page1 view. + [Route("page1")] + public IActionResult Page1() + { + return View(); + } + + /// + /// Displays Page2. + /// + /// Returns the Page2 view. + [Route("page2")] + public IActionResult Page2() + { + return View(); + } + + /// + /// Displays Page3. + /// + /// Returns the Page3 view. + [Route("page3")] + public IActionResult Page3() + { + return View(); + } + + /// + /// Displays the TabConfig page. + /// + /// Returns the TabConfig view. + [Route("configure")] + public IActionResult TabConfig() + { + return View("TabConfig"); + } + } +} \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/1.Default_Tab.png b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/1.Default_Tab.png new file mode 100644 index 0000000000..3cbf24e15b Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/1.Default_Tab.png differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/2.Tab_One.png b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/2.Tab_One.png new file mode 100644 index 0000000000..23e8e437fe Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/2.Tab_One.png differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/3.Tab_Two.png b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/3.Tab_Two.png new file mode 100644 index 0000000000..5abcbb4597 Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/3.Tab_Two.png differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/4.Tab_Three.png b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/4.Tab_Three.png new file mode 100644 index 0000000000..ec06c52f9c Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/4.Tab_Three.png differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/ExternalAppId.PNG b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/ExternalAppId.PNG new file mode 100644 index 0000000000..47580238f2 Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/ExternalAppId.PNG differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/install.png b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/install.png new file mode 100644 index 0000000000..04703d5738 Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/install.png differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/tab_app_navigation_module.gif b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/tab_app_navigation_module.gif new file mode 100644 index 0000000000..2b5aaee2f7 Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/tab_app_navigation_module.gif differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Microsoft.Teams.Samples.TabAppNavigation.Web.csproj b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Microsoft.Teams.Samples.TabAppNavigation.Web.csproj new file mode 100644 index 0000000000..6b672fd6ba --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Microsoft.Teams.Samples.TabAppNavigation.Web.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + latest + + + + + + + + + + + Always + + + \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Program.cs b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Program.cs new file mode 100644 index 0000000000..a743012004 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Program.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Used for configuring and hosting ASP.NET Core web applications. +using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Hosting; + +namespace TabAppNavigation +{ + /// The entry point for the application, responsible for configuring and starting the web host. + public class Program + { + /// + /// Main method to configure and start the web application. + /// + /// Command-line arguments passed to the application. + public static void Main(string[] args) + { + // Build and run the web host + CreateHostBuilder(args).Build().Run(); + } + + /// + /// Configures the web host builder with the required settings. + /// + /// Command-line arguments passed to the application. + /// The configured IHostBuilder. + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + // Specify the Startup class to be used for configuration + webBuilder.UseStartup(); + }); + } +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Startup.cs b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Startup.cs new file mode 100644 index 0000000000..0bfcce094b --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Startup.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Used for configuring, building, and hosting ASP.NET Core web applications and services. +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace TabAppNavigation +{ + /// + /// Configures services and middleware for the application. + /// This class is used by the runtime to set up the application's request pipeline. + /// + public class Startup + { + /// + /// Initializes a new instance of the class. + /// + /// Configuration for setting up the application environment. + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + /// + /// Gets the configuration for the application. + /// + public IConfiguration Configuration { get; } + + /// + /// Configures services for the application. + /// + /// A collection of services to be added to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add controllers and MVC services to the container + services.AddControllers(); // Adds controllers to the service collection + services.AddMvc(); // Adds MVC services for handling views and routes + } + + /// + /// Configures the HTTP request pipeline for the application. + /// + /// The application's builder for configuring the middleware pipeline. + /// The environment information for the application. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // Enable developer exception page if the environment is development + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + // Enable HTTPS and HSTS for production environments + app.UseHsts(); // HTTP Strict Transport Security for enhanced security + app.UseHttpsRedirection(); // Redirect HTTP requests to HTTPS + } + + // Configure static files, default files, and WebSocket support + app.UseDefaultFiles(); // Serves default files (e.g., index.html) + app.UseStaticFiles(); // Serves static files (e.g., images, CSS, JS) + app.UseWebSockets(); // Enable WebSocket support + + // Configure routing for the application + app.UseRouting(); // Enables routing capabilities + + // Configure endpoint mapping + app.UseEndpoints(endpoints => + { + // Map controller endpoints + endpoints.MapControllers(); + + // Map default route for controller actions + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); // Default route for Home controller + }); + } + } +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Index.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Index.cshtml new file mode 100644 index 0000000000..607a447b72 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Index.cshtml @@ -0,0 +1,42 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +
+
+
+
Tab App Navigation
+
+
+
+
+
Your App is ready!
+
+ You can test your app
+ by installing the manifest in Teams. +
+
+
+ +
+ +
+ +@section Scripts { + +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page1.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page1.cshtml new file mode 100644 index 0000000000..2f0cb89219 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page1.cshtml @@ -0,0 +1,81 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@* This Razor view is part of a Microsoft Teams application that demonstrates navigation between tabs and pages within the app. *@ +@* Imports the configuration namespace to access app settings and configuration values in the Razor view. *@ +@using Microsoft.Extensions.Configuration +@inject IConfiguration Configuration + +@{ + ViewData["Title"] = "Page1"; + var externalAppId = Configuration["ExternalAppId"] ?? "ExternalAppId Not Configured Add in appsettings.json"; +} +

Tab One

+ +
+ +

+ +

+ +
+ +
+ @* Uncomment the line below to see the external app ID in action. *@ + @* ExternalAppId: @externalAppId *@ +
+ +@section Scripts { + +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page2.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page2.cshtml new file mode 100644 index 0000000000..43e70dd599 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page2.cshtml @@ -0,0 +1,78 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@* Imports the configuration namespace to access app settings and configuration values in the Razor view. *@ +@using Microsoft.Extensions.Configuration +@inject IConfiguration Configuration + +@{ + ViewData["Title"] = "Page2"; + var externalAppId = Configuration["ExternalAppId"] ?? "ExternalAppId Not Configured Add in appsettings.json"; +} +

Tab Two

+ +
+ +

+ +

+ +
+ +
+ @* Uncomment the line below to see the external app ID in action. *@ + @* ExternalAppId: @externalAppId *@ +
+ +@section Scripts { + +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page3.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page3.cshtml new file mode 100644 index 0000000000..331675c7c2 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/Page3.cshtml @@ -0,0 +1,65 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@* Imports the configuration namespace to access app settings and configuration values in the Razor view. *@ +@using Microsoft.Extensions.Configuration +@inject IConfiguration Configuration + +@{ + ViewData["Title"] = "Page3"; + var externalAppId = Configuration["ExternalAppId"] ?? "ExternalAppId Not Configured Add in appsettings.json"; +} +

Tab Three

+ +
+ +

+ +
+ +
+ @* Uncomment the line below to see the external app ID in action. *@ + @* ExternalAppId: @externalAppId *@ +
+ +@section Scripts { + +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/TabAppNavigation.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/TabAppNavigation.cshtml new file mode 100644 index 0000000000..430b71d041 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/TabAppNavigation.cshtml @@ -0,0 +1,58 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@* This is a Razor view for a Microsoft Teams application that demonstrates navigation between tabs and pages within the app. + It includes buttons to navigate to different pages and uses the Microsoft Teams SDK for navigation functionality. *@ +
+
+

Tab App Navigation - Navigate Between Tabs

+

+
+
+ +
+ + + +
+ +@section Scripts { + +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/TabConfig.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/TabConfig.cshtml new file mode 100644 index 0000000000..3c613c117a --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Home/TabConfig.cshtml @@ -0,0 +1,45 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@{ + ViewData["Title"] = "Tab Config"; +} +

Tab Configuration

+
+

Navigate within a tab app

+

This sample shows the tab navigation feature such as navigation between tabs within the app.

+

Please click save button to proceed.

+
+ +@section Scripts { + +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Shared/_Layout.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..cfd8cb8f6a --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/Shared/_Layout.cshtml @@ -0,0 +1,26 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + + + + + Microsoft Teams Hello World Sample App + + + + + @RenderSection("scripts", required: false) + + + +
+
+ @RenderBody() +
+
+ + + + diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/_ViewStart.cshtml b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..802384394e --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Views/_ViewStart.cshtml @@ -0,0 +1,6 @@ +@* Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.Development.json b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.Development.json new file mode 100644 index 0000000000..b49abfc201 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } + } diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.json b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.json new file mode 100644 index 0000000000..c4419bf2ac --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/appsettings.json @@ -0,0 +1,7 @@ +{ + "MicrosoftAppId": "{{ Microsoft-App-Id }}", + "MicrosoftAppPassword": "{{ Microsoft-App-Password }}", + "ExternalAppId": "{{ External-App-Id }}", + "MicrosoftAppType": "SingleTenant", + "MicrosoftAppTenantId": "{{ Microsoft-App-Tenant-Id }}" +} \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/favicon.ico b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/favicon.ico new file mode 100644 index 0000000000..a3a799985c Binary files /dev/null and b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/favicon.ico differ diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Content/Site.css b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Content/Site.css new file mode 100644 index 0000000000..c927291bfe --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Content/Site.css @@ -0,0 +1,23 @@ +html, body, div.surface, div.panel { + height: 100%; + margin: 0; +} + +div.panel { + padding: 15px; +} + +button.primary { + background-color: #6264A7; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background 0.2s; +} + +button.primary:hover { + background-color: #464775; +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Content/msteams-16.css b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Content/msteams-16.css new file mode 100644 index 0000000000..54f033cae0 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Content/msteams-16.css @@ -0,0 +1,1272 @@ +.theme-light .surface { + background-color: #F0F2F4; + color: #16233A; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.25rem +} + +.theme-light .panel { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background-color: #FFFFFF; + border-color: transparent; + border-radius: 0.1875rem; + border-style: solid; + border-width: 0.125rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden +} + +.theme-light .panel-header { + flex: 0 0 auto; + margin-left: 2rem; + margin-right: 2rem; + margin-top: 2rem +} + +.theme-light .panel-body { + flex: 1 1 auto; + margin-left: 2rem; + margin-right: 2rem; + overflow: auto +} + +.theme-light .panel-footer { + flex: 0 0 auto; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem +} + +.theme-light .button-primary { + background: #5558AF; + border: 0.125rem solid; + border-color: transparent; + border-radius: 0.1875rem; + color: #FFFFFF; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-light .button-primary:hover:enabled { + background: #4C509D; + border-color: transparent; + color: #FFFFFF + } + + .theme-light .button-primary:active { + background: #454A92; + border-color: transparent; + color: #FFFFFF + } + + .theme-light .button-primary:disabled { + background: #F3F4F5; + border-color: transparent; + color: #ABB0B8 + } + + .theme-light .button-primary:focus { + background: #4C509D; + border-color: transparent; + color: #FFFFFF; + outline: 0.125rem solid #FFFFFF; + outline-offset: -0.25rem + } + +.theme-light .button-secondary { + background: #FFFFFF; + border: 0.125rem solid; + border-color: #ABB0B8; + border-radius: 0.1875rem; + color: #525C6D; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-light .button-secondary:hover:enabled { + background: #ABB0B8; + border-color: transparent; + color: #16233A + } + + .theme-light .button-secondary:active { + background: #858C98; + border-color: transparent; + color: #16233A + } + + .theme-light .button-secondary:disabled { + background: #FFFFFF; + border-color: #F3F4F5; + color: #ABB0B8 + } + + .theme-light .button-secondary:focus { + background: #ABB0B8; + border-color: transparent; + color: #16233A; + outline: 0.125rem solid #16233A; + outline-offset: -0.25rem + } + +.theme-light .radio-container { + align-items: center; + background: transparent; + border: none; + display: flex; + outline: none +} + + .theme-light .radio-container + .radio-container { + margin-top: 0.5rem + } + +.theme-light .radio-button { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + background: transparent; + border: 0.0625rem solid; + border-color: #525C6D; + border-radius: 100%; + cursor: pointer; + display: inline-block; + font: inherit; + height: 0.75rem; + margin: 0.125rem; + margin-left: 0.375rem; + padding: 0; + position: relative; + width: 0.75rem +} + + .theme-light .radio-button:hover { + background: transparent; + border-color: #525C6D + } + + .theme-light .radio-button:disabled { + background: #F0F2F4; + border-color: #ABB0B8 + } + + .theme-light .radio-button:disabled + label { + color: #ABB0B8; + cursor: default + } + + .theme-light .radio-button:focus { + box-shadow: 0 0 0 0.125rem #9FA4FE; + outline: none + } + +.theme-light .hidden-input:checked + .radio-button { + background: #5558AF; + border-color: #5558AF +} + + .theme-light .hidden-input:checked + .radio-button + label { + color: #16233A + } + +.theme-light .radio-label { + color: #525C6D; + cursor: pointer; + font-size: 0.75rem; + line-height: 1rem; + margin-left: 0.625rem +} + +.theme-light .radio-group { + display: inline-block +} + +.theme-light .tab-group { + border-bottom: 0.0625rem solid #F3F4F5; + margin: 0; + padding: 0; + width: 100% +} + + .theme-light .tab-group .tab { + background: 0; + border: 0; + border-bottom: transparent 0.25rem solid; + color: #525C6D; + cursor: pointer; + display: inline-block; + font: inherit; + margin: 0; + margin-right: 1.25rem; + outline: none; + padding: 0.25rem + } + + .theme-light .tab-group .tab:hover { + border-bottom-color: #9496CA + } + + .theme-light .tab-group .tab:focus { + background-color: #9FA4FE; + color: #FFFFFF + } + + .theme-light .tab-group .tab-active { + border-bottom-color: #5558AF; + color: #5558AF + } + +.theme-light .tab-active:focus { + border-bottom-color: #FFFFFF +} + +.theme-light .hidden-input { + display: none +} + +.theme-light .toggle { + display: inline-block; + line-height: 1 +} + +.theme-light .toggle-ball { + background-color: #F0F2F4; + border: 0; + border-radius: 1.25rem; + cursor: pointer; + height: 1.25rem; + margin: 0.125rem; + outline: none; + padding: 0; + position: relative; + width: 3.75rem +} + + .theme-light .toggle-ball:before { + background-color: #454A92; + border-radius: 50%; + content: ""; + height: 0.875rem; + left: 0.1875rem; + position: absolute; + top: 0.18750000000000003rem; + transition: 0.2s; + width: 0.875rem + } + +.theme-light .hidden-input:checked + .toggle-ball:before { + background-color: #4C509D; + transform: translateX(2.5rem) +} + +.theme-light .toggle-ball:focus { + box-shadow: 0 0 0 0.125rem #5558AF; + outline: none +} + +.theme-light .hidden-input:checked + .toggle-ball { + background-color: #7FBA00 +} + +.theme-light .font-title { + font-size: 1.5rem; + line-height: 2rem +} + +.theme-light .font-title2 { + font-size: 1.125rem; + line-height: 1.5rem +} + +.theme-light .font-base { + font-size: 0.875rem; + line-height: 1.25rem +} + +.theme-light .font-caption { + font-size: 0.75rem; + line-height: 1rem +} + +.theme-light .font-xsmall { + font-size: 0.625rem; + line-height: 0.6875rem +} + +.theme-light .font-semilight { + font-family: 'Segoe UI Light', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 300 +} + +.theme-light .font-regular { + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 400 +} + +.theme-light .font-semibold { + font-family: 'Segoe UI Semibold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 600 +} + +.theme-light .font-bold { + font-family: 'Segoe UI Bold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 700 +} + +.theme-light .input-container { + overflow: hidden; + position: relative +} + +.theme-light .input-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #F0F2F4; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #525C6D; + font: inherit; + height: 2rem; + margin: 0; + outline: none; + padding: 0.5rem 0.75rem; + width: 100% +} + +.theme-light .input-error-icon { + bottom: 0.5625rem; + color: #C50E2E; + position: absolute; + right: 0.75rem +} + +.theme-light .label { + border: 0; + color: #4E586A; + display: inline-block; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-light .error-label { + border: 0; + color: #C50E2E; + float: right; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-light .textarea-container { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative +} + +.theme-light .textarea-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #F0F2F4; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #525C6D; + flex: 1; + font: inherit; + margin: 0; + min-height: 3.75rem; + outline: none; + padding: 0.5rem 0.75rem; + resize: none +} + + .theme-light .input-field:hover:inactive:enabled, .theme-light .textarea-field:hover:inactive:enabled { + background: #F0F2F4; + border-bottom-color: transparent + } + + .theme-light .input-field:disabled, .theme-light .textarea-field:disabled { + background: #F3F4F5; + border-bottom-color: transparent; + color: #DEE0E3 + } + + .theme-light .input-field:active:enabled, .theme-light .input-field:focus, .theme-light .textarea-field:active:enabled, .theme-light .textarea-field:focus { + background: #F0F2F4; + border-bottom-color: #5558AF + } + +.theme-light .textarea-error-icon { + color: #C50E2E; + position: absolute; + right: 0.75rem; + top: 50% +} + +.theme-dark .surface { + background-color: #2B2B30; + color: #FFFFFF; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.25rem +} + +.theme-dark .panel { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background-color: #404045; + border-color: transparent; + border-radius: 0.1875rem; + border-style: solid; + border-width: 0.125rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden +} + +.theme-dark .panel-header { + flex: 0 0 auto; + margin-left: 2rem; + margin-right: 2rem; + margin-top: 2rem +} + +.theme-dark .panel-body { + flex: 1 1 auto; + margin-left: 2rem; + margin-right: 2rem; + overflow: auto +} + +.theme-dark .panel-footer { + flex: 0 0 auto; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem +} + +.theme-dark .button-primary { + background: #9FA4FE; + border: 0.125rem solid; + border-color: transparent; + border-radius: 0.1875rem; + color: #2B2B30; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-dark .button-primary:hover:enabled { + background: #AEB2FF; + border-color: transparent; + color: #2B2B30 + } + + .theme-dark .button-primary:active { + background: #B8BBFF; + border-color: transparent; + color: #2B2B30 + } + + .theme-dark .button-primary:disabled { + background: #35353A; + border-color: transparent; + color: #77777A + } + + .theme-dark .button-primary:focus { + background: #9FA4FE; + border-color: transparent; + color: #2B2B30; + outline: 0.125rem solid #2B2B30; + outline-offset: -0.25rem + } + +.theme-dark .button-secondary { + background: #404045; + border: 0.125rem solid; + border-color: #77777A; + border-radius: 0.1875rem; + color: #C8C8C9; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-dark .button-secondary:hover:enabled { + background: #77777A; + border-color: transparent; + color: #FFFFFF + } + + .theme-dark .button-secondary:active { + background: #48484D; + border-color: transparent; + color: #FFFFFF + } + + .theme-dark .button-secondary:disabled { + background: #404045; + border-color: #35353A; + color: #77777A + } + + .theme-dark .button-secondary:focus { + background: #77777A; + border-color: transparent; + color: #FFFFFF; + outline: 0.125rem solid #FFFFFF; + outline-offset: -0.25rem + } + +.theme-dark .radio-container { + align-items: center; + background: transparent; + border: none; + display: flex; + outline: none +} + + .theme-dark .radio-container + .radio-container { + margin-top: 0.5rem + } + +.theme-dark .radio-button { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + background: transparent; + border: 0.0625rem solid; + border-color: #C8C8C9; + border-radius: 100%; + cursor: pointer; + display: inline-block; + font: inherit; + height: 0.75rem; + margin: 0.125rem; + margin-left: 0.375rem; + padding: 0; + position: relative; + width: 0.75rem +} + + .theme-dark .radio-button:hover { + background: transparent; + border-color: #C8C8C9 + } + + .theme-dark .radio-button:disabled { + background: #404045; + border-color: #77777A + } + + .theme-dark .radio-button:disabled + label { + color: #77777A; + cursor: default + } + + .theme-dark .radio-button:focus { + box-shadow: 0 0 0 0.125rem #5558AF; + outline: none + } + +.theme-dark .hidden-input:checked + .radio-button { + background: #9FA4FE; + border-color: #9FA4FE +} + + .theme-dark .hidden-input:checked + .radio-button + label { + color: #FFFFFF + } + +.theme-dark .radio-label { + color: #C8C8C9; + cursor: pointer; + font-size: 0.75rem; + line-height: 1rem; + margin-left: 0.625rem +} + +.theme-dark .radio-group { + display: inline-block +} + +.theme-dark .tab-group { + border-bottom: 0.0625rem solid #000000; + margin: 0; + padding: 0; + width: 100% +} + + .theme-dark .tab-group .tab { + background: 0; + border: 0; + border-bottom: transparent 0.25rem solid; + color: #C8C8C9; + cursor: pointer; + display: inline-block; + font: inherit; + margin: 0; + margin-right: 1.25rem; + outline: none; + padding: 0.25rem + } + + .theme-dark .tab-group .tab:hover { + border-bottom-color: #7174AA + } + + .theme-dark .tab-group .tab:focus { + background-color: #5558AF; + color: #FFFFFF + } + + .theme-dark .tab-group .tab-active { + border-bottom-color: #9FA4FE; + color: #9FA4FE + } + +.theme-dark .tab-active:focus { + border-bottom-color: #FFFFFF +} + +.theme-dark .hidden-input { + display: none +} + +.theme-dark .toggle { + display: inline-block; + line-height: 1 +} + +.theme-dark .toggle-ball { + background-color: #2B2B30; + border: 0; + border-radius: 1.25rem; + cursor: pointer; + height: 1.25rem; + margin: 0.125rem; + outline: none; + padding: 0; + position: relative; + width: 3.75rem +} + + .theme-dark .toggle-ball:before { + background-color: #C8C8C9; + border-radius: 50%; + content: ""; + height: 0.875rem; + left: 0.1875rem; + position: absolute; + top: 0.18750000000000003rem; + transition: 0.2s; + width: 0.875rem + } + +.theme-dark .hidden-input:checked + .toggle-ball:before { + background-color: #FFFFFF; + transform: translateX(2.5rem) +} + +.theme-dark .toggle-ball:focus { + box-shadow: 0 0 0 0.125rem #9FA4FE; + outline: none +} + +.theme-dark .hidden-input:checked + .toggle-ball { + background-color: #88BC2B +} + +.theme-dark .font-title { + font-size: 1.5rem; + line-height: 2rem +} + +.theme-dark .font-title2 { + font-size: 1.125rem; + line-height: 1.5rem +} + +.theme-dark .font-base { + font-size: 0.875rem; + line-height: 1.25rem +} + +.theme-dark .font-caption { + font-size: 0.75rem; + line-height: 1rem +} + +.theme-dark .font-xsmall { + font-size: 0.625rem; + line-height: 0.6875rem +} + +.theme-dark .font-semilight { + font-family: 'Segoe UI Light', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 300 +} + +.theme-dark .font-regular { + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 400 +} + +.theme-dark .font-semibold { + font-family: 'Segoe UI Semibold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 600 +} + +.theme-dark .font-bold { + font-family: 'Segoe UI Bold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 700 +} + +.theme-dark .input-container { + overflow: hidden; + position: relative +} + +.theme-dark .input-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #2B2B30; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #C8C8C9; + font: inherit; + height: 2rem; + margin: 0; + outline: none; + padding: 0.5rem 0.75rem; + width: 100% +} + +.theme-dark .input-error-icon { + bottom: 0.5625rem; + color: #ED1B3E; + position: absolute; + right: 0.75rem +} + +.theme-dark .label { + border: 0; + color: #FFFFFF; + display: inline-block; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-dark .error-label { + border: 0; + color: #ED1B3E; + float: right; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-dark .textarea-container { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative +} + +.theme-dark .textarea-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #2B2B30; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #C8C8C9; + flex: 1; + font: inherit; + margin: 0; + min-height: 3.75rem; + outline: none; + padding: 0.5rem 0.75rem; + resize: none +} + + .theme-dark .input-field:hover:inactive:enabled, .theme-dark .textarea-field:hover:inactive:enabled { + background: #2B2B30; + border-bottom-color: transparent + } + + .theme-dark .input-field:disabled, .theme-dark .textarea-field:disabled { + background: #35353A; + border-bottom-color: transparent; + color: #48484D + } + + .theme-dark .input-field:active:enabled, .theme-dark .input-field:focus, .theme-dark .textarea-field:active:enabled, .theme-dark .textarea-field:focus { + background: #2B2B30; + border-bottom-color: #9FA4FE + } + +.theme-dark .textarea-error-icon { + color: #ED1B3E; + position: absolute; + right: 0.75rem; + top: 50% +} + +.theme-contrast .surface { + background-color: #000000; + color: #FFFFFF; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.25rem +} + +.theme-contrast .panel { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background-color: #000000; + border-color: #FFFFFF; + border-radius: 0.1875rem; + border-style: solid; + border-width: 0.125rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden +} + +.theme-contrast .panel-header { + flex: 0 0 auto; + margin-left: 2rem; + margin-right: 2rem; + margin-top: 2rem +} + +.theme-contrast .panel-body { + flex: 1 1 auto; + margin-left: 2rem; + margin-right: 2rem; + overflow: auto +} + +.theme-contrast .panel-footer { + flex: 0 0 auto; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem +} + +.theme-contrast .button-primary { + background: #FFFFFF; + border: 0.125rem solid; + border-color: transparent; + border-radius: 0.1875rem; + color: #000000; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-contrast .button-primary:disabled { + background: #30F42C; + border-color: transparent; + color: #000000 + } + +.theme-contrast .button-secondary { + background: #000000; + border: 0.125rem solid; + border-color: #FFFFFF; + border-radius: 0.1875rem; + color: #FFFFFF; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-contrast .button-primary:hover:enabled, .theme-contrast .button-primary:active, .theme-contrast .button-secondary:hover:enabled, .theme-contrast .button-secondary:active { + background: #FFFF00; + border-color: transparent; + color: #000000 + } + + .theme-contrast .button-secondary:disabled { + background: #000000; + border-color: #30F42C; + color: #30F42C + } + + .theme-contrast .button-primary:focus, .theme-contrast .button-secondary:focus { + background: #FFFF00; + border-color: transparent; + color: #000000; + outline: 0.125rem solid transparent; + outline-offset: -0.25rem + } + +.theme-contrast .radio-container { + align-items: center; + background: transparent; + border: none; + display: flex; + outline: none +} + + .theme-contrast .radio-container + .radio-container { + margin-top: 0.5rem + } + +.theme-contrast .radio-button { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + background: transparent; + border: 0.0625rem solid; + border-color: #FFFFFF; + border-radius: 100%; + cursor: pointer; + display: inline-block; + font: inherit; + height: 0.75rem; + margin: 0.125rem; + margin-left: 0.375rem; + padding: 0; + position: relative; + width: 0.75rem +} + + .theme-contrast .radio-button:hover { + background: transparent; + border-color: #FFFFFF + } + + .theme-contrast .radio-button:disabled { + background: transparent; + border-color: #30F42C + } + + .theme-contrast .radio-button:disabled + label { + color: #30F42C; + cursor: default + } + + .theme-contrast .radio-button:focus { + box-shadow: 0 0 0 0.125rem #FFFF00; + outline: none + } + +.theme-contrast .hidden-input:checked + .radio-button { + background: #00EBFF; + border-color: #00EBFF +} + + .theme-contrast .hidden-input:checked + .radio-button + label { + color: #FFFFFF + } + +.theme-contrast .radio-label { + color: #FFFFFF; + cursor: pointer; + font-size: 0.75rem; + line-height: 1rem; + margin-left: 0.625rem +} + +.theme-contrast .radio-group { + display: inline-block +} + +.theme-contrast .tab-group { + border-bottom: 0.0625rem solid #30F42C; + margin: 0; + padding: 0; + width: 100% +} + + .theme-contrast .tab-group .tab { + background: 0; + border: 0; + border-bottom: transparent 0.25rem solid; + color: #FFFFFF; + cursor: pointer; + display: inline-block; + font: inherit; + margin: 0; + margin-right: 1.25rem; + outline: none; + padding: 0.25rem + } + + .theme-contrast .tab-group .tab:hover { + border-bottom-color: #FFFF00 + } + + .theme-contrast .tab-group .tab:focus { + background-color: #FFFF00; + color: #000000 + } + + .theme-contrast .tab-group .tab-active { + border-bottom-color: #00EBFF; + color: #FFFFFF + } + +.theme-contrast .tab-active:focus { + border-bottom-color: #000000 +} + +.theme-contrast .hidden-input { + display: none +} + +.theme-contrast .toggle { + display: inline-block; + line-height: 1 +} + +.theme-contrast .toggle-ball { + background-color: #FFFFFF; + border: 0; + border-radius: 1.25rem; + cursor: pointer; + height: 1.25rem; + margin: 0.125rem; + outline: none; + padding: 0; + position: relative; + width: 3.75rem +} + + .theme-contrast .toggle-ball:before { + background-color: #FFFF00; + border-radius: 50%; + content: ""; + height: 0.875rem; + left: 0.1875rem; + position: absolute; + top: 0.18750000000000003rem; + transition: 0.2s; + width: 0.875rem + } + +.theme-contrast .hidden-input:checked + .toggle-ball:before { + background-color: #4C509D; + transform: translateX(2.5rem) +} + +.theme-contrast .toggle-ball:focus { + box-shadow: 0 0 0 0.125rem #30F42C; + outline: none +} + +.theme-contrast .hidden-input:checked + .toggle-ball { + background-color: #7FBA00 +} + +.theme-contrast .font-title { + font-size: 1.5rem; + line-height: 2rem +} + +.theme-contrast .font-title2 { + font-size: 1.125rem; + line-height: 1.5rem +} + +.theme-contrast .font-base { + font-size: 0.875rem; + line-height: 1.25rem +} + +.theme-contrast .font-caption { + font-size: 0.75rem; + line-height: 1rem +} + +.theme-contrast .font-xsmall { + font-size: 0.625rem; + line-height: 0.6875rem +} + +.theme-contrast .font-semilight { + font-family: 'Segoe UI Light', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 300 +} + +.theme-contrast .font-regular { + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 400 +} + +.theme-contrast .font-semibold { + font-family: 'Segoe UI Semibold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 600 +} + +.theme-contrast .font-bold { + font-family: 'Segoe UI Bold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 700 +} + +.theme-contrast .input-container { + overflow: hidden; + position: relative +} + +.theme-contrast .input-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #000000; + border: 0.125rem solid #FFFFFF; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #FFFFFF; + font: inherit; + height: 2rem; + margin: 0; + outline: none; + padding: 0.5rem 0.75rem; + width: 100% +} + +.theme-contrast .input-error-icon { + bottom: 0.5625rem; + color: #FFFF00; + position: absolute; + right: 0.75rem +} + +.theme-contrast .label { + border: 0; + color: #FFFFFF; + display: inline-block; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-contrast .error-label { + border: 0; + color: #FFFF00; + float: right; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-contrast .textarea-container { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative +} + +.theme-contrast .textarea-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #000000; + border: 0.125rem solid #FFFFFF; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #FFFFFF; + flex: 1; + font: inherit; + margin: 0; + min-height: 3.75rem; + outline: none; + padding: 0.5rem 0.75rem; + resize: none +} + + .theme-contrast .input-field:hover:inactive:enabled, .theme-contrast .textarea-field:hover:inactive:enabled { + background: #000000; + border-bottom-color: transparent + } + + .theme-contrast .input-field:disabled, .theme-contrast .textarea-field:disabled { + background: #30F42C; + border-bottom-color: #FFFFFF; + color: #FFFFFF + } + + .theme-contrast .input-field:active:enabled, .theme-contrast .input-field:focus, .theme-contrast .textarea-field:active:enabled, .theme-contrast .textarea-field:focus { + background: #000000; + border-bottom-color: #FFFF00 + } + +.theme-contrast .textarea-error-icon { + color: #FFFF00; + position: absolute; + right: 0.75rem; + top: 50% +} diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Scripts/teamsapp.js b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Scripts/teamsapp.js new file mode 100644 index 0000000000..028e0a975e --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/wwwroot/Scripts/teamsapp.js @@ -0,0 +1,63 @@ +(function () { + 'use strict'; + + // Call the initialize API first + microsoftTeams.initialize(); + + // Check the initial theme user chose and respect it + microsoftTeams.getContext(function (context) { + if (context && context.theme) { + setTheme(context.theme); + } + }); + + // Handle theme changes + microsoftTeams.registerOnThemeChangeHandler(function (theme) { + setTheme(theme); + }); + + // Save configuration changes + microsoftTeams.settings.registerOnSaveHandler(function (saveEvent) { + // Let the Microsoft Teams platform know what you want to load based on + // what the user configured on this page + microsoftTeams.settings.setSettings({ + contentUrl: createTabUrl(), // Mandatory parameter + entityId: createTabUrl() // Mandatory parameter + }); + + // Tells Microsoft Teams platform that we are done saving our settings. Microsoft Teams waits + // for the app to call this API before it dismisses the dialog. If the wait times out, you will + // see an error indicating that the configuration settings could not be saved. + saveEvent.notifySuccess(); + }); + + // Logic to let the user configure what they want to see in the tab being loaded + document.addEventListener('DOMContentLoaded', function () { + var tabChoice = document.getElementById('tabChoice'); + if (tabChoice) { + tabChoice.onchange = function () { + var selectedTab = this[this.selectedIndex].value; + + // This API tells Microsoft Teams to enable the 'Save' button. Since Microsoft Teams always assumes + // an initial invalid state, without this call the 'Save' button will never be enabled. + microsoftTeams.settings.setValidityState(selectedTab === 'first' || selectedTab === 'second'); + }; + } + }); + + // Set the desired theme + function setTheme(theme) { + if (theme) { + // Possible values for theme: 'default', 'light', 'dark' and 'contrast' + document.body.className = 'theme-' + (theme === 'default' ? 'light' : theme); + } + } + + // Create the URL that Microsoft Teams will load in the tab. You can compose any URL even with query strings. + function createTabUrl() { + var tabChoice = document.getElementById('tabChoice'); + var selectedTab = tabChoice[tabChoice.selectedIndex].value; + + return window.location.protocol + '//' + window.location.host + '/' + selectedTab; + } +})(); diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.sln b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.sln new file mode 100644 index 0000000000..2d54582340 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34814.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Teams.Samples.TabAppNavigation.Web", "Microsoft.Teams.Samples.TabAppNavigation.Web\Microsoft.Teams.Samples.TabAppNavigation.Web.csproj", "{783D6E6F-7339-4D74-89D4-B240496CC7A3}" +EndProject +Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "M365Agent", "M365Agent\M365Agent.ttkproj", "{728AB578-560B-485D-843F-A5B234C86F2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{27BD3092-6CF0-42B4-BBC4-E2EB4388CCDB}" + ProjectSection(SolutionItems) = preProject + Microsoft.Teams.Samples.TabAppNavigation.slnLaunch.user = Microsoft.Teams.Samples.TabAppNavigation.slnLaunch.user + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Release|Any CPU.Build.0 = Release|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C0264D40-8A26-4CAF-9DE9-0CFF497234D8} + EndGlobalSection +EndGlobal diff --git a/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.slnLaunch.user b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.slnLaunch.user new file mode 100644 index 0000000000..1d28e277e6 --- /dev/null +++ b/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.slnLaunch.user @@ -0,0 +1,27 @@ +[ + { + "Name": "Microsoft Teams (browser)", + "Projects": [ + { + "Path": "M365Agent\\M365Agent.ttkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft Teams (browser)" + }, + { + "Path": "Microsoft.Teams.Samples.TabAppNavigation.Web\\Microsoft.Teams.Samples.TabAppNavigation.Web.csproj", + "Action": "Start", + "DebugTarget": "Microsoft.Teams.Samples.TabAppNavigation.Web" + } + ] + }, + { + "Name": "Microsoft Teams (browser) (skip update app)", + "Projects": [ + { + "Path": "M365Agent\\M365Agent.ttkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft Teams (browser) (skip update app)" + } + ] + } +] \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/README.md b/samples/tab-app-navigation/csharp/README.md new file mode 100644 index 0000000000..f254798327 --- /dev/null +++ b/samples/tab-app-navigation/csharp/README.md @@ -0,0 +1,163 @@ +--- +page_type: sample +description: This sample illustrates the tab navigation feature in a Microsoft Teams application using C#/.NET, enabling smooth transitions between different tabs. Designed for use with .NET, it highlights how users can effectively navigate within the app for an enhanced experience. +products: +- office-teams +- office +- office-365 +languages: +- csharp +extensions: + contentType: samples + createdDate: "03/07/2025 12:00:00 AM" +urlFragment: officedev-microsoft-teams-samples-tab-app-navigation-csharp +--- + +## Tab App Navigation + +Explore a sample Microsoft Teams application that showcases tab navigation capabilities using C#/.NET, allowing users to seamlessly move between various tabs within the app. Built with .NET, this example provides insights into creating intuitive navigation flows that enhance user engagement and improve overall app functionality. + +## Included Features +* Tab Navigation + + +**Interaction with App** +![TabAppNavigation](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/tab_app_navigation_module.gif) + +## Prerequisites +- Microsoft Teams is installed and you have an account (not a guest account) +- [.NET Core SDK](https://dotnet.microsoft.com/download) version 6.0 + + ```bash + # determine dotnet version + dotnet --version + ``` +- [dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) or [ngrok](https://ngrok.com/) latest version or equivalent tunneling solution +- [M365 developer account](https://docs.microsoft.com/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant) or access to a Teams account with the appropriate permissions for uploading custom apps +- [Microsoft 365 Agents Toolkit for Visual Studio](https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/toolkit-v4/install-teams-toolkit-vs?pivots=visual-studio-v17-7) + +- **Note** This feature `(Tab App Navigation)` is only supported in new Teams `T2.1`. + +## Run the app (Using Microsoft 365 Agents Toolkit for Visual Studio) + +The simplest way to run this sample in Teams is to use Microsoft 365 Agents Toolkit for Visual Studio. +1. Install Visual Studio 2022 **Version 17.14 or higher** [Visual Studio](https://visualstudio.microsoft.com/downloads/) +1. Install Microsoft 365 Agents Toolkit for Visual Studio [Microsoft 365 Agents Toolkit extension](https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/toolkit-v4/install-teams-toolkit-vs?pivots=visual-studio-v17-7) +1. **Update Placeholder For External App ID** + - Get the external app id from teams admin portal.(https://admin.teams.microsoft.com/policies/manage-apps) + - Navigate to the appropriate C# file in your project where the external app id is referenced and replace placeholder `<>` with your actual external app id which you can get from teams admin portal. + + ![External App Id](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/ExternalAppId.PNG) + +1. In the debug dropdown menu of Visual Studio, select Dev Tunnels > Create A Tunnel (set authentication type to Public) or select an existing public dev tunnel. +1. Right-click the 'M365Agent' project in Solution Explorer and select **Microsoft 365 Agents Toolkit > Select Microsoft 365 Account** +1. Sign in to Microsoft 365 Agents Toolkit with a **Microsoft 365 work or school account** +1. Set `Startup Item` as `Microsoft Teams (browser)`. +1. Press F5, or select Debug > Start Debugging menu in Visual Studio to start your app +
![image](https://raw.githubusercontent.com/OfficeDev/TeamsFx/dev/docs/images/visualstudio/debug/debug-button.png) +1. In the opened web browser, select Add button to install the app in Teams +> If you do not have permission to upload custom apps (uploading), Microsoft 365 Agents Toolkit will recommend creating and using a Microsoft 365 Developer Program account - a free program to get your own dev environment sandbox that includes Teams. + +### Setup +> Note these instructions are for running the sample on your local machine. + +1) Register a new application in the [Microsoft Entra ID – App Registrations](https://go.microsoft.com/fwlink/?linkid=2083908) portal. +**NOTE:** When you create app registration, you will create an App ID and App password - make sure you keep these for later. + +2) Setup NGROK + - Run ngrok - point to port 5000 + + ```bash + ngrok http 5000 --host-header="localhost:5000" + ``` + Alternatively, you can also use the `dev tunnels`. Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below: + + ```bash + devtunnel host -p 5000 --allow-anonymous + ``` + +3) Setup for code +- Clone the repository + + ```bash + git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git + ``` +- In a terminal, navigate to `samples/tab-app-navigation/csharp` + +- **Update Placeholder For External App ID** + - Get the external app id from teams admin portal (https://admin.teams.microsoft.com/policies/manage-apps) + - Navigate to the appropriate C# file in your project where the external app id is referenced and replace placeholder `<>` with your actual external app id which you can get from teams admin portal. + +- Modify the `/appsettings.json` and fill in the following details: + - `{{Microsoft-App-Id}}` - Generated from Step 1 is the application app id + - `{{ Microsoft-App-Password}}` - Generated from Step 1, also referred to as Client secret + - `{{ Application Base Url }}` - Your application's base url. E.g. https://12345.ngrok-free.app if you are using ngrok and if you are using dev tunnels, your URL will be https://12345.devtunnels.ms. + +- Run the app from a terminal or from Visual Studio: + + i- From a terminal, navigate to `Microsoft.Teams.Samples.TabAppNavigation.Web` + + ```bash + # run the app + dotnet run + ``` + + ii- Or from Visual Studio + + - Launch Visual Studio + - File -> Open -> Project/Solution + - Navigate to `Microsoft.Teams.Samples.TabAppNavigation.Web` folder + - Select `Microsoft.Teams.Samples.TabAppNavigation.Web.csproj` file + - Press `F5` to run the project + +4) __*This step is specific to Teams.*__ + - **Edit** the `manifest.json` contained in the `appManifest` folder to replace your `{{Microsoft-App-Id}}` (that was created in step1 and is the same value of MicrosoftAppId) *everywhere* you see the place holder string `{{Microsoft-App-Id}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) + - **Edit** the `manifest.json` for `validDomains` and replace `{{domain-name}}` with base Url of your domain. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your domain-name will be `1234.ngrok-free.app` and if you are using dev tunnels then your domain will be like: `12345.devtunnels.ms`. + - **Zip** up the contents of the `appManifest` folder to create a `manifest.zip` (Make sure that zip file does not contains any subfolder otherwise you will get error while uploading your .zip package) + - **Upload** the `manifest.zip` to Teams (In Teams Apps/Manage your apps click "Upload an app". Browse to and Open the .zip file. At the next dialog, click the Add button.) + - Upload the app in `Teams Scope` (Supported scopes: Teams) + + **Note:** If you want to test your app across multi hub like: Outlook/Office.com, please update the `manifest.json` in the `/AppManifest_Hub` folder with the required values. + +## Deploy to Teams +Start debugging the project by hitting the `F5` key or click the debug icon in Visual Studio and click the `Start Debugging` green arrow button. + +### NOTE: First time debug step +On the first time running and debugging your app you need allow the localhost certificate. After starting debugging when the browser is launched and you have installed your app it will fail to load. + +- Open a new tab `in the same browser window that was opened` +- Navigate to `https://localhost:5000/tab` (or your configured port) +- Click the `Advanced` button +- Select the `Continue to localhost` + +### NOTE: Debugging +Ensure you have the appropriate debugging tools configured in Visual Studio for .NET development. + +## Running the sample + +**Add Application To Teams** +![Configure Tab](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/install.png) + +**Tab Default To Navigate** +![Tab Menu](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/1.Default_Tab.png) + +**Navigated Within Apps** +![Tab Navigation](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/2.Tab_One.png) + +**Navigated Within Apps** +![Tab Navigation](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/3.Tab_Two.png) + +**Navigated Within Apps** +![Tab Navigation](Microsoft.Teams.Samples.TabAppNavigation.Web/Images/4.Tab_Three.png) + + +## Deploy the app to Azure + +To learn more about deploying an app to Azure, see [Deploy your app to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions. + +## Further reading + +- [Navigate within a tab app](https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/tab-navigation#navigate-between-tabs) +- [Extend Teams apps across Microsoft 365](https://learn.microsoft.com/en-us/microsoftteams/platform/m365-apps/overview) + + \ No newline at end of file diff --git a/samples/tab-app-navigation/csharp/assets/sample.json b/samples/tab-app-navigation/csharp/assets/sample.json new file mode 100644 index 0000000000..8f76adef48 --- /dev/null +++ b/samples/tab-app-navigation/csharp/assets/sample.json @@ -0,0 +1,68 @@ +[ + { + "name": "officedev-microsoft-teams-samples-tab-app-navigation-csharp", + "source": "officeDev", + "title": "Tab App Navigation", + "shortDescription": "This sample demonstrates tab navigation functionality, allowing users to switch between tabs in a Teams application.", + "url": "https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/tab-app-navigation/csharp", + "longDescription": [ + "This sample illustrates the tab navigation feature in a Microsoft Teams application, enabling smooth transitions between different tabs. Designed for use with .NET, it highlights how users can effectively navigate within the app for an enhanced experience." + ], + "creationDateTime": "2025-07-03", + "updateDateTime": "2025-07-03", + "products": [ + "Teams" + ], + "metadata": [ + { + "key": "TEAMS-SAMPLE-SOURCE", + "value": "OfficeDev" + }, + { + "key": "TEAMS-SERVER-LANGUAGE", + "value": "csharp" + }, + { + "key": "TEAMS-SERVER-PLATFORM", + "value": "netframework" + }, + { + "key": "TEAMS-FEATURES", + "value": "tab,bot,msgext" + } + ], + "thumbnails": [ + { + "type": "image", + "order": 100, + "url": "https://raw.githubusercontent.com/OfficeDev/Microsoft-Teams-Samples/main/samples/tab-app-navigation/csharp/Microsoft.Teams.Samples.TabAppNavigation.Web/Images/tab_app_navigation_module.gif", + "alt": "Solution UX showing microsoft teams sample for tab app navigation" + } + ], + "authors": [ + { + "gitHubAccount": "OfficeDev", + "pictureUrl": "https://avatars.githubusercontent.com/u/6789362?s=200&v=4", + "name": "OfficeDev" + } + ], + "references": [ + { + "name": "Teams developer documentation", + "url": "https://aka.ms/TeamsPlatformDocs" + }, + { + "name": "Teams developer questions", + "url": "https://aka.ms/TeamsPlatformFeedback" + }, + { + "name": "Teams development videos from Microsoft", + "url": "https://aka.ms/sample-ref-teams-vids-from-microsoft" + }, + { + "name": "Teams development videos from the community", + "url": "https://aka.ms/community/videos/m365powerplatform" + } + ] + } +] \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/M365Agent.ttkproj b/samples/tab-channel-group-quickstart/csharp/M365Agent/M365Agent.ttkproj new file mode 100644 index 0000000000..a1fb20da33 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/M365Agent.ttkproj @@ -0,0 +1,13 @@ + + + + 728ab578-560b-485d-843f-a5b234c86f2b + + + + + + + + + \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/color.png b/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/color.png new file mode 100644 index 0000000000..b8cf81afbe Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/color.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/manifest.json b/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/manifest.json new file mode 100644 index 0000000000..c905b1faf8 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/manifest.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.19/MicrosoftTeams.schema.json", + "manifestVersion": "1.19", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Contoso", + "websiteUrl": "https://www.microsoft.com", + "privacyUrl": "https://www.microsoft.com/privacy", + "termsOfUseUrl": "https://www.microsoft.com/termsofuse" + }, + "name": { + "short": "Tab Channel Group Quick Start", + "full": "Tab Channel Group Quick Start App" + }, + "description": { + "short": "Simple app showcasing channel and group tabs in Microsoft Teams.", + "full": "This sample application illustrates how to create channel and group tabs in Microsoft Teams, providing collaborative spaces for web content. It includes detailed setup instructions, prerequisites, and guidance for deploying the app within your Teams environment." + }, + "icons": { + "outline": "outline.png", + "color": "color.png" + }, + "accentColor": "#60A18E", + "configurableTabs": [ + { + "configurationUrl": "https://${{BOT_DOMAIN}}/config", + "canUpdateConfiguration": true, + "scopes": [ + "team", + "groupChat" + ] + } + ], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [ + "localhost", + "${{BOT_DOMAIN}}" + ] +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/outline.png b/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/outline.png new file mode 100644 index 0000000000..2c3bf6fa65 Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/M365Agent/appPackage/outline.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/env/.env.local b/samples/tab-channel-group-quickstart/csharp/M365Agent/env/.env.local new file mode 100644 index 0000000000..b3c3835e4a --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/env/.env.local @@ -0,0 +1,7 @@ +# This file includes environment variables that can be committed to git. It's gitignored by default because it represents your local development environment. + +# Built-in environment variables +TEAMSFX_ENV=local +APP_NAME_SUFFIX=local + +# Generated during provision, you can also add your own variables. diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/infra/azure.bicep b/samples/tab-channel-group-quickstart/csharp/M365Agent/infra/azure.bicep new file mode 100644 index 0000000000..c3ce051b3d --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/infra/azure.bicep @@ -0,0 +1,44 @@ +@maxLength(20) +@minLength(4) +@description('Used to generate names for all resources in this file') +param resourceBaseName string + +@description('Required when create Azure Bot service') +param botAadAppClientId string + +param botAppDomain string + +@maxLength(42) +param botDisplayName string + +param botServiceName string = resourceBaseName +param botServiceSku string = 'F0' +param microsoftAppType string +param microsoftAppTenantId string + +// Register your web service as a bot with the Bot Framework +resource botService 'Microsoft.BotService/botServices@2021-03-01' = { + kind: 'azurebot' + location: 'global' + name: botServiceName + properties: { + displayName: botDisplayName + endpoint: 'https://${botAppDomain}/api/messages' + msaAppId: botAadAppClientId + msaAppType: microsoftAppType + msaAppTenantId: microsoftAppType == 'SingleTenant' ? microsoftAppTenantId : '' + } + sku: { + name: botServiceSku + } +} + +// Connect the bot service to Microsoft Teams +resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { + parent: botService + location: 'global' + name: 'MsTeamsChannel' + properties: { + channelName: 'MsTeamsChannel' + } +} diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/infra/azure.parameters.json b/samples/tab-channel-group-quickstart/csharp/M365Agent/infra/azure.parameters.json new file mode 100644 index 0000000000..04bf8e337c --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/infra/azure.parameters.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceBaseName": { + "value": "bot${{RESOURCE_SUFFIX}}" + }, + "botAadAppClientId": { + "value": "${{AAD_APP_CLIENT_ID}}" + }, + "botAppDomain": { + "value": "${{BOT_DOMAIN}}" + }, + "botDisplayName": { + "value": "tab-channel-group-quickstart" + }, + "microsoftAppType": { + "value": "${{MICROSOFT_APP_TYPE}}" + }, + "microsoftAppTenantId": { + "value": "${{MICROSOFT_APP_TENANT_ID}}" + } + } +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/launchSettings.json b/samples/tab-channel-group-quickstart/csharp/M365Agent/launchSettings.json new file mode 100644 index 0000000000..0e348677ed --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/launchSettings.json @@ -0,0 +1,23 @@ +{ + "profiles": { + // Debug project within Teams + "Microsoft Teams (browser)": { + "commandName": "Project", + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + // Launch project within Teams without prepare app dependencies + "Microsoft Teams (browser) (skip update app)": { + "commandName": "Project", + "environmentVariables": { "UPDATE_TEAMS_APP": "false" }, + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + "Microsoft 365 app (browser)": { + "commandName": "Project", + "launchUrl": "https://www.office.com/m365apps/${{M365_APP_ID}}?auth=2&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + "Outlook (browser)": { + "commandName": "Project", + "launchUrl": "https://outlook.office.com/host/${{M365_APP_ID}}?login_hint=${{TEAMSFX_M365_USER_NAME}}" + } + } +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/m365agents.local.yml b/samples/tab-channel-group-quickstart/csharp/M365Agent/m365agents.local.yml new file mode 100644 index 0000000000..7b5f98262e --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/m365agents.local.yml @@ -0,0 +1,95 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.2 + +additionalMetadata: + sampleTag: Microsoft-Teams-Samples:tab-channel-group-quickstart-csharp + +provision: + - uses: aadApp/create # Creates a new Azure Active Directory (AAD) app to authenticate users if the environment variable that stores clientId is empty + with: + name: tab-channel-group-quickstart-aad # Note: when you run aadApp/update, the AAD app name will be updated based on the definition in manifest. If you don't want to change the name, make sure the name in AAD manifest is the same with the name defined here. + generateClientSecret: true # If the value is false, the action will not generate client secret for you + signInAudience: "AzureADMultipleOrgs" # Multitenant + writeToEnvironmentFile: # Write the information of created resources into environment file for the specified environment variable(s). + clientId: AAD_APP_CLIENT_ID + clientSecret: SECRET_AAD_APP_CLIENT_SECRET # Environment variable that starts with `SECRET_` will be stored to the .env.{envName}.user environment file + objectId: AAD_APP_OBJECT_ID + tenantId: AAD_APP_TENANT_ID + authority: AAD_APP_OAUTH_AUTHORITY + authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST + + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: app-tab-channel-group-quickstart-${{TEAMSFX_ENV}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + - uses: script + with: + run: + echo "::set-teamsfx-env MICROSOFT_APP_TYPE=SingleTenant"; + echo "::set-teamsfx-env MICROSOFT_APP_TENANT_ID=${{AAD_APP_TENANT_ID}}"; + + # Generate runtime appsettings to JSON file + - uses: file/createOrUpdateJsonFile + with: + target: ../Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.json + content: + MicrosoftAppType: SingleTenant + MicrosoftAppId: ${{AAD_APP_CLIENT_ID}} + MicrosoftAppPassword: ${{SECRET_AAD_APP_CLIENT_SECRET}} + MicrosoftAppTenantId: ${{AAD_APP_TENANT_ID}} + + - uses: arm/deploy # Deploy given ARM templates parallelly. + with: + subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} # The AZURE_SUBSCRIPTION_ID is a built-in environment variable. TeamsFx will ask you select one subscription if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select subscription if it's empty in this case. + resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} # The AZURE_RESOURCE_GROUP_NAME is a built-in environment variable. TeamsFx will ask you to select or create one resource group if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select or create resource grouop if it's empty in this case. + templates: + - path: ./infra/azure.bicep + parameters: ./infra/azure.parameters.json + deploymentName: Create-resources-for-bot + bicepCliVersion: v0.9.1 # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, will use bicep CLI in PATH if you remove this config. + + # Validate using manifest schema + - uses: teamsApp/validateManifest + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + + # Build Teams app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputJsonPath: ./appPackage/build/manifest.${{TEAMSFX_ENV}}.json + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + + # Apply the Teams app manifest to an existing Teams app in + # Developer Portal. + # Will use the app id in manifest file to determine which Teams app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + + # Extend your Teams app to Outlook and the Microsoft 365 app + - uses: teamsApp/extendToM365 + with: + # Relative path to the build app package. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + titleId: M365_TITLE_ID + appId: M365_APP_ID \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/M365Agent/m365agents.yml b/samples/tab-channel-group-quickstart/csharp/M365Agent/m365agents.yml new file mode 100644 index 0000000000..bad5a4f57c --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/M365Agent/m365agents.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.2 + +additionalMetadata: + sampleTag: Microsoft-Teams-Samples:tab-channel-group-quickstart + +environmentFolderPath: ./env +projectId: diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/.gitignore b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/.gitignore new file mode 100644 index 0000000000..7466c01f9c --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/.gitignore @@ -0,0 +1,25 @@ +# TeamsFx files +build +appPackage/build +env/.env.*.user +env/.env.local +appsettings.Development.json +.deployment + +# User-specific files +*.user + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Notification local store +.notification.localstore.json \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/color.png b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/color.png new file mode 100644 index 0000000000..b8cf81afbe Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/color.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/manifest.json b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/manifest.json new file mode 100644 index 0000000000..dd7a8483e6 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/manifest.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.16/MicrosoftTeams.schema.json", + "manifestVersion": "1.16", + "version": "1.0.0", + "id": "<>", + "packageName": "com.contoso.TabChannelGroupQuickstart", + "developer": { + "name": "Contoso", + "websiteUrl": "https://www.microsoft.com", + "privacyUrl": "https://www.microsoft.com/privacy", + "termsOfUseUrl": "https://www.microsoft.com/termsofuse" + }, + "name": { + "short": "Tab Channel Group Quick Start", + "full": "Tab Channel Group Quick Start App" + }, + "description": { + "short": "Simple app showcasing channel and group tabs in Microsoft Teams.", + "full": "This sample application illustrates how to create channel and group tabs in Microsoft Teams, providing collaborative spaces for web content. It includes detailed setup instructions, prerequisites, and guidance for deploying the app within your Teams environment." + }, + "icons": { + "outline": "outline.png", + "color": "color.png" + }, + "accentColor": "#60A18E", + "staticTabs": [ + { + "entityId": "com.contoso.tabchannelgroupquickstart.tab", + "name": "Tab Channel Group Quick Start", + "contentUrl": "https://{{domain-name}}/tab", + "scopes": [ + "personal" + ] + } + ], + "composeExtensions": [ + { + "botId": "<>", + "commands": [ + { + "id": "getRandomText", + "description": "Gets some random text and images that you can insert in messages for fun.", + "title": "Get some random text for fun", + "initialRun": true, + "parameters": [ + { + "name": "cardTitle", + "description": "Card title to use", + "title": "Card title" + } + ] + } + ] + } + ], + "permissions": [], + "validDomains": [ + "{{domain-name}}" + ] +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/outline.png b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/outline.png new file mode 100644 index 0000000000..2c3bf6fa65 Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/AppManifest_Hub/outline.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Controllers/HomeController.cs b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Controllers/HomeController.cs new file mode 100644 index 0000000000..b7b2b8f3f9 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Controllers/HomeController.cs @@ -0,0 +1,44 @@ +// Copyright(c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.AspNetCore.Mvc; + +namespace TabChannelGroupQuickstart +{ + /// + /// HomeController is responsible for handling routes related to the main pages of the application. + /// This includes rendering the main index page, hello page, and other specific views. + /// + public class HomeController : Controller + { + /// + /// Displays the home page (Index view). + /// + /// Returns the Index view. + [Route("")] + public ActionResult Index() + { + return View(); + } + + /// + /// Displays the Tab page. + /// + /// Returns the Tab view. + [Route("tab")] + public IActionResult Tab() + { + return View(); + } + + /// + /// Displays the Tab Configuration page. + /// + /// Returns the TabConfig view. + [Route("config")] + public IActionResult TabConfig() + { + return View("TabConfig"); + } + } +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/hello_world.png b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/hello_world.png new file mode 100644 index 0000000000..4e05f6c90c Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/hello_world.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/install.png b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/install.png new file mode 100644 index 0000000000..ce13bfef7f Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/install.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/setup_tab.png b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/setup_tab.png new file mode 100644 index 0000000000..dc0f4c92a7 Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/setup_tab.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_channel_group_module.gif b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_channel_group_module.gif new file mode 100644 index 0000000000..16d1addd49 Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_channel_group_module.gif differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_configure.png b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_configure.png new file mode 100644 index 0000000000..5f6f648bd7 Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_configure.png differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj new file mode 100644 index 0000000000..d4fafb1854 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + latest + true + + + + + + + + + + + + + + + Always + + + \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Program.cs b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Program.cs new file mode 100644 index 0000000000..c9e411cbc8 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Program.cs @@ -0,0 +1,36 @@ +// Copyright(c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2 +using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Hosting; + +namespace TabChannelGroupQuickstart +{ + /// The entry point for the application, responsible for configuring and starting the web host. + public class Program + { + /// + /// Main method to configure and start the web application. + /// + /// Command-line arguments passed to the application. + public static void Main(string[] args) + { + // Build and run the web host + CreateHostBuilder(args).Build().Run(); + } + + /// + /// Configures the web host builder with the required settings. + /// + /// Command-line arguments passed to the application. + /// The configured IHostBuilder. + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + // Specify the Startup class to be used for configuration + webBuilder.UseStartup(); + }); + } +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Properties/launchSettings.json b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..ff9d8fe153 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "Start Project": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7130;http://localhost:5130", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "hotReloadProfile": "aspnetcore" + } + } +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Startup.cs b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Startup.cs new file mode 100644 index 0000000000..2413b92e04 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Startup.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.6.2 +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace TabChannelGroupQuickstart +{ + /// + /// Configures services and middleware for the application. + /// This class is used by the runtime to set up the application's request pipeline. + /// + public class Startup + { + /// + /// Initializes a new instance of the class. + /// + /// Configuration for setting up the application environment. + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + /// + /// Gets the configuration for the application. + /// + public IConfiguration Configuration { get; } + + /// + /// Configures services for the application. + /// + /// A collection of services to be added to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add controllers and MVC services to the container + services.AddControllers(); // Adds controllers to the service collection + services.AddMvc(); // Adds MVC services for handling views and routes + } + + /// + /// Configures the HTTP request pipeline for the application. + /// + /// The application's builder for configuring the middleware pipeline. + /// The environment information for the application. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // Enable developer exception page if the environment is development + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + // Enable HTTPS and HSTS for production environments + app.UseHsts(); // HTTP Strict Transport Security for enhanced security + app.UseHttpsRedirection(); // Redirect HTTP requests to HTTPS + } + + // Configure static files, default files, and WebSocket support + app.UseDefaultFiles(); // Serves default files (e.g., index.html) + app.UseStaticFiles(); // Serves static files (e.g., images, CSS, JS) + app.UseWebSockets(); // Enable WebSocket support + + // Configure routing for the application + app.UseRouting(); // Enables routing capabilities + + // Configure endpoint mapping + app.UseEndpoints(endpoints => + { + // Map controller endpoints + endpoints.MapControllers(); + + // Map default route for controller actions + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); // Default route for Home controller + }); + } + } +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/Index.cshtml b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/Index.cshtml new file mode 100644 index 0000000000..ec5fe3c32a --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/Index.cshtml @@ -0,0 +1,38 @@ +@* Copyright(c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License *@ + +
+ Hello, World +
+

+ Welcome to the Microsoft Teams Sample App! + this is a sample Microsoft Teams app built with ASP.NET Core MVC. + It demonstrates how to create a simple tab that can be added to a team or group chat. + The app uses the Microsoft Teams JavaScript SDK to initialize and interact with the Teams environment. + You can customize this app further by adding more features and functionality as needed. +

+
+ +
+ +@section Scripts { + +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/Tab.cshtml b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/Tab.cshtml new file mode 100644 index 0000000000..696b4ab41b --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/Tab.cshtml @@ -0,0 +1,38 @@ +@* Copyright(c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License *@ + +@{ + ViewData["Title"] = "Tab"; +} +

Hello World!

+

+

This is the tab you made :-)

+

+ +@section Scripts { + +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/TabConfig.cshtml b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/TabConfig.cshtml new file mode 100644 index 0000000000..002bd3c7a0 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Home/TabConfig.cshtml @@ -0,0 +1,57 @@ +@* Copyright(c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License *@ + +@{ + ViewData["Title"] = "Tab Config"; +} +

Tab Configuration

+
+

+ This page is used to configure the tab settings for your Microsoft Teams application. + When you add this tab to a team or group chat, users will see the configuration options + defined here. +

+

+ You can customize the tab's display name, content URL, and other settings that will be + presented to users when they add this tab to their Teams experience. +

+

+ The configuration options will be saved when the user clicks the "Save" button in the Teams + tab configuration dialog. +

+

+ Make sure to test the configuration by adding this tab to a team or group chat in Microsoft Teams. +

+
+ +@section Scripts { + +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Shared/_Layout.cshtml b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..ebfdb3405c --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/Shared/_Layout.cshtml @@ -0,0 +1,26 @@ +@* Copyright(c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License *@ + + + + + Microsoft Teams Sample App + + + + + @RenderSection("scripts", required: false) + + + +
+
+ @RenderBody() +
+
+ + + + diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/_ViewStart.cshtml b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..7a4d2fe274 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Views/_ViewStart.cshtml @@ -0,0 +1,6 @@ +@* Copyright(c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. *@ + +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.Development.json b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.Development.json new file mode 100644 index 0000000000..b49abfc201 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } + } diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.json b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.json new file mode 100644 index 0000000000..22016589b0 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/appsettings.json @@ -0,0 +1,6 @@ +{ + "MicrosoftAppId": "YOUR_NEW_APP_ID", + "MicrosoftAppPassword": "YOUR_NEW_APP_PASSWORD", + "MicrosoftAppType": "YOUR_NEW_APP_TYPE", + "MicrosoftAppTenantId": "YOUR_NEW_TENANT_ID" +} \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/favicon.ico b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/favicon.ico new file mode 100644 index 0000000000..a3a799985c Binary files /dev/null and b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/favicon.ico differ diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Content/Site.css b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Content/Site.css new file mode 100644 index 0000000000..6c4c11c6b4 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Content/Site.css @@ -0,0 +1,8 @@ +html, body, div.surface, div.panel { + height: 100%; + margin: 0; +} + +div.panel { + padding: 15px; +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Content/msteams-16.css b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Content/msteams-16.css new file mode 100644 index 0000000000..54f033cae0 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Content/msteams-16.css @@ -0,0 +1,1272 @@ +.theme-light .surface { + background-color: #F0F2F4; + color: #16233A; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.25rem +} + +.theme-light .panel { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background-color: #FFFFFF; + border-color: transparent; + border-radius: 0.1875rem; + border-style: solid; + border-width: 0.125rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden +} + +.theme-light .panel-header { + flex: 0 0 auto; + margin-left: 2rem; + margin-right: 2rem; + margin-top: 2rem +} + +.theme-light .panel-body { + flex: 1 1 auto; + margin-left: 2rem; + margin-right: 2rem; + overflow: auto +} + +.theme-light .panel-footer { + flex: 0 0 auto; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem +} + +.theme-light .button-primary { + background: #5558AF; + border: 0.125rem solid; + border-color: transparent; + border-radius: 0.1875rem; + color: #FFFFFF; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-light .button-primary:hover:enabled { + background: #4C509D; + border-color: transparent; + color: #FFFFFF + } + + .theme-light .button-primary:active { + background: #454A92; + border-color: transparent; + color: #FFFFFF + } + + .theme-light .button-primary:disabled { + background: #F3F4F5; + border-color: transparent; + color: #ABB0B8 + } + + .theme-light .button-primary:focus { + background: #4C509D; + border-color: transparent; + color: #FFFFFF; + outline: 0.125rem solid #FFFFFF; + outline-offset: -0.25rem + } + +.theme-light .button-secondary { + background: #FFFFFF; + border: 0.125rem solid; + border-color: #ABB0B8; + border-radius: 0.1875rem; + color: #525C6D; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-light .button-secondary:hover:enabled { + background: #ABB0B8; + border-color: transparent; + color: #16233A + } + + .theme-light .button-secondary:active { + background: #858C98; + border-color: transparent; + color: #16233A + } + + .theme-light .button-secondary:disabled { + background: #FFFFFF; + border-color: #F3F4F5; + color: #ABB0B8 + } + + .theme-light .button-secondary:focus { + background: #ABB0B8; + border-color: transparent; + color: #16233A; + outline: 0.125rem solid #16233A; + outline-offset: -0.25rem + } + +.theme-light .radio-container { + align-items: center; + background: transparent; + border: none; + display: flex; + outline: none +} + + .theme-light .radio-container + .radio-container { + margin-top: 0.5rem + } + +.theme-light .radio-button { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + background: transparent; + border: 0.0625rem solid; + border-color: #525C6D; + border-radius: 100%; + cursor: pointer; + display: inline-block; + font: inherit; + height: 0.75rem; + margin: 0.125rem; + margin-left: 0.375rem; + padding: 0; + position: relative; + width: 0.75rem +} + + .theme-light .radio-button:hover { + background: transparent; + border-color: #525C6D + } + + .theme-light .radio-button:disabled { + background: #F0F2F4; + border-color: #ABB0B8 + } + + .theme-light .radio-button:disabled + label { + color: #ABB0B8; + cursor: default + } + + .theme-light .radio-button:focus { + box-shadow: 0 0 0 0.125rem #9FA4FE; + outline: none + } + +.theme-light .hidden-input:checked + .radio-button { + background: #5558AF; + border-color: #5558AF +} + + .theme-light .hidden-input:checked + .radio-button + label { + color: #16233A + } + +.theme-light .radio-label { + color: #525C6D; + cursor: pointer; + font-size: 0.75rem; + line-height: 1rem; + margin-left: 0.625rem +} + +.theme-light .radio-group { + display: inline-block +} + +.theme-light .tab-group { + border-bottom: 0.0625rem solid #F3F4F5; + margin: 0; + padding: 0; + width: 100% +} + + .theme-light .tab-group .tab { + background: 0; + border: 0; + border-bottom: transparent 0.25rem solid; + color: #525C6D; + cursor: pointer; + display: inline-block; + font: inherit; + margin: 0; + margin-right: 1.25rem; + outline: none; + padding: 0.25rem + } + + .theme-light .tab-group .tab:hover { + border-bottom-color: #9496CA + } + + .theme-light .tab-group .tab:focus { + background-color: #9FA4FE; + color: #FFFFFF + } + + .theme-light .tab-group .tab-active { + border-bottom-color: #5558AF; + color: #5558AF + } + +.theme-light .tab-active:focus { + border-bottom-color: #FFFFFF +} + +.theme-light .hidden-input { + display: none +} + +.theme-light .toggle { + display: inline-block; + line-height: 1 +} + +.theme-light .toggle-ball { + background-color: #F0F2F4; + border: 0; + border-radius: 1.25rem; + cursor: pointer; + height: 1.25rem; + margin: 0.125rem; + outline: none; + padding: 0; + position: relative; + width: 3.75rem +} + + .theme-light .toggle-ball:before { + background-color: #454A92; + border-radius: 50%; + content: ""; + height: 0.875rem; + left: 0.1875rem; + position: absolute; + top: 0.18750000000000003rem; + transition: 0.2s; + width: 0.875rem + } + +.theme-light .hidden-input:checked + .toggle-ball:before { + background-color: #4C509D; + transform: translateX(2.5rem) +} + +.theme-light .toggle-ball:focus { + box-shadow: 0 0 0 0.125rem #5558AF; + outline: none +} + +.theme-light .hidden-input:checked + .toggle-ball { + background-color: #7FBA00 +} + +.theme-light .font-title { + font-size: 1.5rem; + line-height: 2rem +} + +.theme-light .font-title2 { + font-size: 1.125rem; + line-height: 1.5rem +} + +.theme-light .font-base { + font-size: 0.875rem; + line-height: 1.25rem +} + +.theme-light .font-caption { + font-size: 0.75rem; + line-height: 1rem +} + +.theme-light .font-xsmall { + font-size: 0.625rem; + line-height: 0.6875rem +} + +.theme-light .font-semilight { + font-family: 'Segoe UI Light', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 300 +} + +.theme-light .font-regular { + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 400 +} + +.theme-light .font-semibold { + font-family: 'Segoe UI Semibold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 600 +} + +.theme-light .font-bold { + font-family: 'Segoe UI Bold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 700 +} + +.theme-light .input-container { + overflow: hidden; + position: relative +} + +.theme-light .input-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #F0F2F4; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #525C6D; + font: inherit; + height: 2rem; + margin: 0; + outline: none; + padding: 0.5rem 0.75rem; + width: 100% +} + +.theme-light .input-error-icon { + bottom: 0.5625rem; + color: #C50E2E; + position: absolute; + right: 0.75rem +} + +.theme-light .label { + border: 0; + color: #4E586A; + display: inline-block; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-light .error-label { + border: 0; + color: #C50E2E; + float: right; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-light .textarea-container { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative +} + +.theme-light .textarea-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #F0F2F4; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #525C6D; + flex: 1; + font: inherit; + margin: 0; + min-height: 3.75rem; + outline: none; + padding: 0.5rem 0.75rem; + resize: none +} + + .theme-light .input-field:hover:inactive:enabled, .theme-light .textarea-field:hover:inactive:enabled { + background: #F0F2F4; + border-bottom-color: transparent + } + + .theme-light .input-field:disabled, .theme-light .textarea-field:disabled { + background: #F3F4F5; + border-bottom-color: transparent; + color: #DEE0E3 + } + + .theme-light .input-field:active:enabled, .theme-light .input-field:focus, .theme-light .textarea-field:active:enabled, .theme-light .textarea-field:focus { + background: #F0F2F4; + border-bottom-color: #5558AF + } + +.theme-light .textarea-error-icon { + color: #C50E2E; + position: absolute; + right: 0.75rem; + top: 50% +} + +.theme-dark .surface { + background-color: #2B2B30; + color: #FFFFFF; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.25rem +} + +.theme-dark .panel { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background-color: #404045; + border-color: transparent; + border-radius: 0.1875rem; + border-style: solid; + border-width: 0.125rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden +} + +.theme-dark .panel-header { + flex: 0 0 auto; + margin-left: 2rem; + margin-right: 2rem; + margin-top: 2rem +} + +.theme-dark .panel-body { + flex: 1 1 auto; + margin-left: 2rem; + margin-right: 2rem; + overflow: auto +} + +.theme-dark .panel-footer { + flex: 0 0 auto; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem +} + +.theme-dark .button-primary { + background: #9FA4FE; + border: 0.125rem solid; + border-color: transparent; + border-radius: 0.1875rem; + color: #2B2B30; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-dark .button-primary:hover:enabled { + background: #AEB2FF; + border-color: transparent; + color: #2B2B30 + } + + .theme-dark .button-primary:active { + background: #B8BBFF; + border-color: transparent; + color: #2B2B30 + } + + .theme-dark .button-primary:disabled { + background: #35353A; + border-color: transparent; + color: #77777A + } + + .theme-dark .button-primary:focus { + background: #9FA4FE; + border-color: transparent; + color: #2B2B30; + outline: 0.125rem solid #2B2B30; + outline-offset: -0.25rem + } + +.theme-dark .button-secondary { + background: #404045; + border: 0.125rem solid; + border-color: #77777A; + border-radius: 0.1875rem; + color: #C8C8C9; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-dark .button-secondary:hover:enabled { + background: #77777A; + border-color: transparent; + color: #FFFFFF + } + + .theme-dark .button-secondary:active { + background: #48484D; + border-color: transparent; + color: #FFFFFF + } + + .theme-dark .button-secondary:disabled { + background: #404045; + border-color: #35353A; + color: #77777A + } + + .theme-dark .button-secondary:focus { + background: #77777A; + border-color: transparent; + color: #FFFFFF; + outline: 0.125rem solid #FFFFFF; + outline-offset: -0.25rem + } + +.theme-dark .radio-container { + align-items: center; + background: transparent; + border: none; + display: flex; + outline: none +} + + .theme-dark .radio-container + .radio-container { + margin-top: 0.5rem + } + +.theme-dark .radio-button { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + background: transparent; + border: 0.0625rem solid; + border-color: #C8C8C9; + border-radius: 100%; + cursor: pointer; + display: inline-block; + font: inherit; + height: 0.75rem; + margin: 0.125rem; + margin-left: 0.375rem; + padding: 0; + position: relative; + width: 0.75rem +} + + .theme-dark .radio-button:hover { + background: transparent; + border-color: #C8C8C9 + } + + .theme-dark .radio-button:disabled { + background: #404045; + border-color: #77777A + } + + .theme-dark .radio-button:disabled + label { + color: #77777A; + cursor: default + } + + .theme-dark .radio-button:focus { + box-shadow: 0 0 0 0.125rem #5558AF; + outline: none + } + +.theme-dark .hidden-input:checked + .radio-button { + background: #9FA4FE; + border-color: #9FA4FE +} + + .theme-dark .hidden-input:checked + .radio-button + label { + color: #FFFFFF + } + +.theme-dark .radio-label { + color: #C8C8C9; + cursor: pointer; + font-size: 0.75rem; + line-height: 1rem; + margin-left: 0.625rem +} + +.theme-dark .radio-group { + display: inline-block +} + +.theme-dark .tab-group { + border-bottom: 0.0625rem solid #000000; + margin: 0; + padding: 0; + width: 100% +} + + .theme-dark .tab-group .tab { + background: 0; + border: 0; + border-bottom: transparent 0.25rem solid; + color: #C8C8C9; + cursor: pointer; + display: inline-block; + font: inherit; + margin: 0; + margin-right: 1.25rem; + outline: none; + padding: 0.25rem + } + + .theme-dark .tab-group .tab:hover { + border-bottom-color: #7174AA + } + + .theme-dark .tab-group .tab:focus { + background-color: #5558AF; + color: #FFFFFF + } + + .theme-dark .tab-group .tab-active { + border-bottom-color: #9FA4FE; + color: #9FA4FE + } + +.theme-dark .tab-active:focus { + border-bottom-color: #FFFFFF +} + +.theme-dark .hidden-input { + display: none +} + +.theme-dark .toggle { + display: inline-block; + line-height: 1 +} + +.theme-dark .toggle-ball { + background-color: #2B2B30; + border: 0; + border-radius: 1.25rem; + cursor: pointer; + height: 1.25rem; + margin: 0.125rem; + outline: none; + padding: 0; + position: relative; + width: 3.75rem +} + + .theme-dark .toggle-ball:before { + background-color: #C8C8C9; + border-radius: 50%; + content: ""; + height: 0.875rem; + left: 0.1875rem; + position: absolute; + top: 0.18750000000000003rem; + transition: 0.2s; + width: 0.875rem + } + +.theme-dark .hidden-input:checked + .toggle-ball:before { + background-color: #FFFFFF; + transform: translateX(2.5rem) +} + +.theme-dark .toggle-ball:focus { + box-shadow: 0 0 0 0.125rem #9FA4FE; + outline: none +} + +.theme-dark .hidden-input:checked + .toggle-ball { + background-color: #88BC2B +} + +.theme-dark .font-title { + font-size: 1.5rem; + line-height: 2rem +} + +.theme-dark .font-title2 { + font-size: 1.125rem; + line-height: 1.5rem +} + +.theme-dark .font-base { + font-size: 0.875rem; + line-height: 1.25rem +} + +.theme-dark .font-caption { + font-size: 0.75rem; + line-height: 1rem +} + +.theme-dark .font-xsmall { + font-size: 0.625rem; + line-height: 0.6875rem +} + +.theme-dark .font-semilight { + font-family: 'Segoe UI Light', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 300 +} + +.theme-dark .font-regular { + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 400 +} + +.theme-dark .font-semibold { + font-family: 'Segoe UI Semibold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 600 +} + +.theme-dark .font-bold { + font-family: 'Segoe UI Bold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 700 +} + +.theme-dark .input-container { + overflow: hidden; + position: relative +} + +.theme-dark .input-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #2B2B30; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #C8C8C9; + font: inherit; + height: 2rem; + margin: 0; + outline: none; + padding: 0.5rem 0.75rem; + width: 100% +} + +.theme-dark .input-error-icon { + bottom: 0.5625rem; + color: #ED1B3E; + position: absolute; + right: 0.75rem +} + +.theme-dark .label { + border: 0; + color: #FFFFFF; + display: inline-block; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-dark .error-label { + border: 0; + color: #ED1B3E; + float: right; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-dark .textarea-container { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative +} + +.theme-dark .textarea-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #2B2B30; + border: 0.125rem solid transparent; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #C8C8C9; + flex: 1; + font: inherit; + margin: 0; + min-height: 3.75rem; + outline: none; + padding: 0.5rem 0.75rem; + resize: none +} + + .theme-dark .input-field:hover:inactive:enabled, .theme-dark .textarea-field:hover:inactive:enabled { + background: #2B2B30; + border-bottom-color: transparent + } + + .theme-dark .input-field:disabled, .theme-dark .textarea-field:disabled { + background: #35353A; + border-bottom-color: transparent; + color: #48484D + } + + .theme-dark .input-field:active:enabled, .theme-dark .input-field:focus, .theme-dark .textarea-field:active:enabled, .theme-dark .textarea-field:focus { + background: #2B2B30; + border-bottom-color: #9FA4FE + } + +.theme-dark .textarea-error-icon { + color: #ED1B3E; + position: absolute; + right: 0.75rem; + top: 50% +} + +.theme-contrast .surface { + background-color: #000000; + color: #FFFFFF; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.25rem +} + +.theme-contrast .panel { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background-color: #000000; + border-color: #FFFFFF; + border-radius: 0.1875rem; + border-style: solid; + border-width: 0.125rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow: hidden +} + +.theme-contrast .panel-header { + flex: 0 0 auto; + margin-left: 2rem; + margin-right: 2rem; + margin-top: 2rem +} + +.theme-contrast .panel-body { + flex: 1 1 auto; + margin-left: 2rem; + margin-right: 2rem; + overflow: auto +} + +.theme-contrast .panel-footer { + flex: 0 0 auto; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem +} + +.theme-contrast .button-primary { + background: #FFFFFF; + border: 0.125rem solid; + border-color: transparent; + border-radius: 0.1875rem; + color: #000000; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-contrast .button-primary:disabled { + background: #30F42C; + border-color: transparent; + color: #000000 + } + +.theme-contrast .button-secondary { + background: #000000; + border: 0.125rem solid; + border-color: #FFFFFF; + border-radius: 0.1875rem; + color: #FFFFFF; + cursor: pointer; + font: inherit; + height: 2rem; + min-width: 6rem; + padding: 0.25rem; + white-space: nowrap +} + + .theme-contrast .button-primary:hover:enabled, .theme-contrast .button-primary:active, .theme-contrast .button-secondary:hover:enabled, .theme-contrast .button-secondary:active { + background: #FFFF00; + border-color: transparent; + color: #000000 + } + + .theme-contrast .button-secondary:disabled { + background: #000000; + border-color: #30F42C; + color: #30F42C + } + + .theme-contrast .button-primary:focus, .theme-contrast .button-secondary:focus { + background: #FFFF00; + border-color: transparent; + color: #000000; + outline: 0.125rem solid transparent; + outline-offset: -0.25rem + } + +.theme-contrast .radio-container { + align-items: center; + background: transparent; + border: none; + display: flex; + outline: none +} + + .theme-contrast .radio-container + .radio-container { + margin-top: 0.5rem + } + +.theme-contrast .radio-button { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + background: transparent; + border: 0.0625rem solid; + border-color: #FFFFFF; + border-radius: 100%; + cursor: pointer; + display: inline-block; + font: inherit; + height: 0.75rem; + margin: 0.125rem; + margin-left: 0.375rem; + padding: 0; + position: relative; + width: 0.75rem +} + + .theme-contrast .radio-button:hover { + background: transparent; + border-color: #FFFFFF + } + + .theme-contrast .radio-button:disabled { + background: transparent; + border-color: #30F42C + } + + .theme-contrast .radio-button:disabled + label { + color: #30F42C; + cursor: default + } + + .theme-contrast .radio-button:focus { + box-shadow: 0 0 0 0.125rem #FFFF00; + outline: none + } + +.theme-contrast .hidden-input:checked + .radio-button { + background: #00EBFF; + border-color: #00EBFF +} + + .theme-contrast .hidden-input:checked + .radio-button + label { + color: #FFFFFF + } + +.theme-contrast .radio-label { + color: #FFFFFF; + cursor: pointer; + font-size: 0.75rem; + line-height: 1rem; + margin-left: 0.625rem +} + +.theme-contrast .radio-group { + display: inline-block +} + +.theme-contrast .tab-group { + border-bottom: 0.0625rem solid #30F42C; + margin: 0; + padding: 0; + width: 100% +} + + .theme-contrast .tab-group .tab { + background: 0; + border: 0; + border-bottom: transparent 0.25rem solid; + color: #FFFFFF; + cursor: pointer; + display: inline-block; + font: inherit; + margin: 0; + margin-right: 1.25rem; + outline: none; + padding: 0.25rem + } + + .theme-contrast .tab-group .tab:hover { + border-bottom-color: #FFFF00 + } + + .theme-contrast .tab-group .tab:focus { + background-color: #FFFF00; + color: #000000 + } + + .theme-contrast .tab-group .tab-active { + border-bottom-color: #00EBFF; + color: #FFFFFF + } + +.theme-contrast .tab-active:focus { + border-bottom-color: #000000 +} + +.theme-contrast .hidden-input { + display: none +} + +.theme-contrast .toggle { + display: inline-block; + line-height: 1 +} + +.theme-contrast .toggle-ball { + background-color: #FFFFFF; + border: 0; + border-radius: 1.25rem; + cursor: pointer; + height: 1.25rem; + margin: 0.125rem; + outline: none; + padding: 0; + position: relative; + width: 3.75rem +} + + .theme-contrast .toggle-ball:before { + background-color: #FFFF00; + border-radius: 50%; + content: ""; + height: 0.875rem; + left: 0.1875rem; + position: absolute; + top: 0.18750000000000003rem; + transition: 0.2s; + width: 0.875rem + } + +.theme-contrast .hidden-input:checked + .toggle-ball:before { + background-color: #4C509D; + transform: translateX(2.5rem) +} + +.theme-contrast .toggle-ball:focus { + box-shadow: 0 0 0 0.125rem #30F42C; + outline: none +} + +.theme-contrast .hidden-input:checked + .toggle-ball { + background-color: #7FBA00 +} + +.theme-contrast .font-title { + font-size: 1.5rem; + line-height: 2rem +} + +.theme-contrast .font-title2 { + font-size: 1.125rem; + line-height: 1.5rem +} + +.theme-contrast .font-base { + font-size: 0.875rem; + line-height: 1.25rem +} + +.theme-contrast .font-caption { + font-size: 0.75rem; + line-height: 1rem +} + +.theme-contrast .font-xsmall { + font-size: 0.625rem; + line-height: 0.6875rem +} + +.theme-contrast .font-semilight { + font-family: 'Segoe UI Light', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 300 +} + +.theme-contrast .font-regular { + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 400 +} + +.theme-contrast .font-semibold { + font-family: 'Segoe UI Semibold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 600 +} + +.theme-contrast .font-bold { + font-family: 'Segoe UI Bold', 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-weight: 700 +} + +.theme-contrast .input-container { + overflow: hidden; + position: relative +} + +.theme-contrast .input-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #000000; + border: 0.125rem solid #FFFFFF; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #FFFFFF; + font: inherit; + height: 2rem; + margin: 0; + outline: none; + padding: 0.5rem 0.75rem; + width: 100% +} + +.theme-contrast .input-error-icon { + bottom: 0.5625rem; + color: #FFFF00; + position: absolute; + right: 0.75rem +} + +.theme-contrast .label { + border: 0; + color: #FFFFFF; + display: inline-block; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-contrast .error-label { + border: 0; + color: #FFFF00; + float: right; + font-family: 'Segoe UI', Tahoma, Helvetica, Sans-Serif; + font-size: 0.75rem; + font-weight: 400; + line-height: 1rem; + margin-bottom: 0.5rem; + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0 +} + +.theme-contrast .textarea-container { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative +} + +.theme-contrast .textarea-field { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + background: #000000; + border: 0.125rem solid #FFFFFF; + border-radius: 0.1875rem; + box-sizing: border-box; + color: #FFFFFF; + flex: 1; + font: inherit; + margin: 0; + min-height: 3.75rem; + outline: none; + padding: 0.5rem 0.75rem; + resize: none +} + + .theme-contrast .input-field:hover:inactive:enabled, .theme-contrast .textarea-field:hover:inactive:enabled { + background: #000000; + border-bottom-color: transparent + } + + .theme-contrast .input-field:disabled, .theme-contrast .textarea-field:disabled { + background: #30F42C; + border-bottom-color: #FFFFFF; + color: #FFFFFF + } + + .theme-contrast .input-field:active:enabled, .theme-contrast .input-field:focus, .theme-contrast .textarea-field:active:enabled, .theme-contrast .textarea-field:focus { + background: #000000; + border-bottom-color: #FFFF00 + } + +.theme-contrast .textarea-error-icon { + color: #FFFF00; + position: absolute; + right: 0.75rem; + top: 50% +} diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Scripts/teamsapp.js b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Scripts/teamsapp.js new file mode 100644 index 0000000000..028e0a975e --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/wwwroot/Scripts/teamsapp.js @@ -0,0 +1,63 @@ +(function () { + 'use strict'; + + // Call the initialize API first + microsoftTeams.initialize(); + + // Check the initial theme user chose and respect it + microsoftTeams.getContext(function (context) { + if (context && context.theme) { + setTheme(context.theme); + } + }); + + // Handle theme changes + microsoftTeams.registerOnThemeChangeHandler(function (theme) { + setTheme(theme); + }); + + // Save configuration changes + microsoftTeams.settings.registerOnSaveHandler(function (saveEvent) { + // Let the Microsoft Teams platform know what you want to load based on + // what the user configured on this page + microsoftTeams.settings.setSettings({ + contentUrl: createTabUrl(), // Mandatory parameter + entityId: createTabUrl() // Mandatory parameter + }); + + // Tells Microsoft Teams platform that we are done saving our settings. Microsoft Teams waits + // for the app to call this API before it dismisses the dialog. If the wait times out, you will + // see an error indicating that the configuration settings could not be saved. + saveEvent.notifySuccess(); + }); + + // Logic to let the user configure what they want to see in the tab being loaded + document.addEventListener('DOMContentLoaded', function () { + var tabChoice = document.getElementById('tabChoice'); + if (tabChoice) { + tabChoice.onchange = function () { + var selectedTab = this[this.selectedIndex].value; + + // This API tells Microsoft Teams to enable the 'Save' button. Since Microsoft Teams always assumes + // an initial invalid state, without this call the 'Save' button will never be enabled. + microsoftTeams.settings.setValidityState(selectedTab === 'first' || selectedTab === 'second'); + }; + } + }); + + // Set the desired theme + function setTheme(theme) { + if (theme) { + // Possible values for theme: 'default', 'light', 'dark' and 'contrast' + document.body.className = 'theme-' + (theme === 'default' ? 'light' : theme); + } + } + + // Create the URL that Microsoft Teams will load in the tab. You can compose any URL even with query strings. + function createTabUrl() { + var tabChoice = document.getElementById('tabChoice'); + var selectedTab = tabChoice[tabChoice.selectedIndex].value; + + return window.location.protocol + '//' + window.location.host + '/' + selectedTab; + } +})(); diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.sln b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.sln new file mode 100644 index 0000000000..236e46f619 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34814.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web", "Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web\Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj", "{783D6E6F-7339-4D74-89D4-B240496CC7A3}" +EndProject +Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "M365Agent", "M365Agent\M365Agent.ttkproj", "{728AB578-560B-485D-843F-A5B234C86F2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{27BD3092-6CF0-42B4-BBC4-E2EB4388CCDB}" + ProjectSection(SolutionItems) = preProject + Microsoft.Teams.Samples.TabChannelGroupQuickstart.slnLaunch.user = Microsoft.Teams.Samples.TabChannelGroupQuickstart.slnLaunch.user + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {783D6E6F-7339-4D74-89D4-B240496CC7A3}.Release|Any CPU.Build.0 = Release|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {728AB578-560B-485D-843F-A5B234C86F2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C0264D40-8A26-4CAF-9DE9-0CFF497234D8} + EndGlobalSection +EndGlobal diff --git a/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.slnLaunch.user b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.slnLaunch.user new file mode 100644 index 0000000000..6c64d22ece --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.slnLaunch.user @@ -0,0 +1,27 @@ +[ + { + "Name": "Microsoft Teams (browser)", + "Projects": [ + { + "Path": "M365Agent\\M365Agent.ttkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft Teams (browser)" + }, + { + "Path": "Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web\\Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj", + "Action": "Start", + "DebugTarget": "Start Project" + } + ] + }, + { + "Name": "Microsoft Teams (browser) (skip update app)", + "Projects": [ + { + "Path": "M365Agent\\M365Agent.ttkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft Teams (browser) (skip update app)" + } + ] + } +] \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/README.md b/samples/tab-channel-group-quickstart/csharp/README.md new file mode 100644 index 0000000000..8080cae8ba --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/README.md @@ -0,0 +1,154 @@ +--- +page_type: sample +description: This sample application illustrates how to create channel and group tabs in Microsoft Teams using C#/.NET, providing collaborative spaces for web content. It includes detailed setup instructions, prerequisites, and guidance for deploying the app within your Teams environment. +products: +- office-teams +- office +- office-365 +languages: +- csharp +extensions: + contentType: samples + createdDate: "01/07/2025 10:02:21 PM" +urlFragment: officedev-microsoft-teams-samples-tab-channel-group-quickstart-csharp +--- + +# Tabs quick start + +Explore this simple hello world app that showcases channel and group tabs in Microsoft Teams using C#/.NET, designed to enhance collaboration around web-based content. This sample includes comprehensive setup steps, requirements, and deployment instructions, allowing you to easily integrate and run the app in your Teams environment. + +## Included Features +* Tabs + +## Interaction with app +![HelloTabGif](Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_channel_group_module.gif) + +## Try it yourself - experience the App in your Microsoft Teams client +Please find below demo manifest which is deployed on Microsoft Azure and you can try it yourself by uploading the app package (.zip file link below) to your teams and/or as a personal app. (Uploading must be enabled for your tenant, [see steps here](https://docs.microsoft.com/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant#enable-custom-teams-apps-and-turn-on-custom-app-uploading)). + +**Tab Channel quick start:** [Manifest](/samples/tab-channel-group-quickstart/csharp/demo-manifest/tab-channel-group-quickstart.zip) + +## Prerequisites +- Microsoft Teams is installed and you have an account (not a guest account) +- [.NET Core SDK](https://dotnet.microsoft.com/download) version 6.0 + + ```bash + # determine dotnet version + dotnet --version + ``` +- [dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) or [ngrok](https://ngrok.com/) latest version or equivalent tunneling solution +- [M365 developer account](https://docs.microsoft.com/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant) or access to a Teams account with the appropriate permissions for uploading custom apps +- [Microsoft 365 Agents Toolkit for Visual Studio](https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/toolkit-v4/install-teams-toolkit-vs?pivots=visual-studio-v17-7) + +## Run the app (Using Microsoft 365 Agents Toolkit for Visual Studio) + +The simplest way to run this sample in Teams is to use Microsoft 365 Agents Toolkit for Visual Studio. +1. Install Visual Studio 2022 **Version 17.14 or higher** [Visual Studio](https://visualstudio.microsoft.com/downloads/) +1. Install Microsoft 365 Agents Toolkit for Visual Studio [Microsoft 365 Agents Toolkit extension](https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/toolkit-v4/install-teams-toolkit-vs?pivots=visual-studio-v17-7) +1. In the debug dropdown menu of Visual Studio, select Dev Tunnels > Create A Tunnel (set authentication type to Public) or select an existing public dev tunnel. +1. Right-click the 'M365Agent' project in Solution Explorer and select **Microsoft 365 Agents Toolkit > Select Microsoft 365 Account** +1. Sign in to Microsoft 365 Agents Toolkit with a **Microsoft 365 work or school account** +1. Set `Startup Item` as `Microsoft Teams (browser)`. +1. Press F5, or select Debug > Start Debugging menu in Visual Studio to start your app +1. In the opened web browser, select Add button to install the app in Teams +> If you do not have permission to upload custom apps (uploading), Microsoft 365 Agents Toolkit will recommend creating and using a Microsoft 365 Developer Program account - a free program to get your own dev environment sandbox that includes Teams. + +## Setup + +1. Register a new application in the [Microsoft Entra ID – App Registrations](https://go.microsoft.com/fwlink/?linkid=2083908) portal. +**NOTE:** When you create app registration, you will create an App ID and App password - make sure you keep these for later. + +2. Setup NGROK + - Run ngrok - point to port 5000 + + ```bash + ngrok http 5000 --host-header="localhost:5000" + ``` + Alternatively, you can also use the `dev tunnels`. Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below: + + ```bash + devtunnel host -p 5000 --allow-anonymous + ``` + +3. Setup for code +- Clone the repository + + ```bash + git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git + ``` +- In a terminal, navigate to `samples/tab-channel-group-quickstart/csharp` + +- Run the app from a terminal or from Visual Studio: + + A) From a terminal, navigate to `Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web` + + ```bash + # run the app + dotnet run + ``` + + B) Or from Visual Studio + + - Launch Visual Studio + - File -> Open -> Project/Solution + - Navigate to `Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web` folder + - Select `Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web.csproj` file + - Press `F5` to run the project + +4. Setup Manifest for Teams +- **This step is specific to Teams.** + - Edit the `manifest.json` contained in the `appPackage/` folder to replace with your MicrosoftAppId (that was created in step1 and is the same value of MicrosoftAppId) *everywhere* you see the place holder string `{{MicrosoftAppId}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) + - **Edit** the `manifest.json` for `validDomains` and replace `{{domain-name}}` with base Url of your domain. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your domain-name will be `1234.ngrok-free.app` and if you are using dev tunnels then your domain will be like: `12345.devtunnels.ms`. + - Zip up the contents of the `appPackage/` folder to create a `manifest.zip` + - Upload the `manifest.zip` to Teams (in the left-bottom *Apps* view, click "Upload a custom app") + + **Note:** If you want to test your app across multi hub like: Outlook/Office.com, please update the `manifest.json` in the `/AppManifest_Hub` folder with the required values. + +- Modify the `/appsettings.json` and fill in the following details: + - `{{MicrosoftAppId}}` - Generated from Step 1 is the application app id + - `{{MicrosoftAppPassword}}` - Generated from Step 1, also referred to as Client secret + - `{{MicrosoftAppTenantId}}` - Generated from Step 1, also referred to as Directory (tenant) ID + +## Deploy to Teams +Start debugging the project by hitting the `F5` key or click the debug icon in Visual Studio and click the `Start Debugging` green arrow button. + +### NOTE: First time debug step +On the first time running and debugging your app you need allow the localhost certificate. After starting debugging when the browser is launched and you have installed your app it will fail to load. + +- Open a new tab `in the same browser window that was opened` +- Navigate to `https://localhost:5000/tab` (or your configured port) +- Click the `Advanced` button +- Select the `Continue to localhost` + +### NOTE: Debugging +Ensure you have the appropriate debugging tools configured in Visual Studio for .NET development. + + +## Running the sample + +**Install App:** + +![InstallApp](Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/install.png) + +**Set Up Tab:** + +![SetupTab](Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/setup_tab.png) + +**Configure Tab:** + +![TabConfigure](Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_configure.png) + +**Hello World UI:** + +![HelloWorld](Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/hello_world.png) + +## Deploy the app to Azure + +To learn more about deploying an app to Azure, see [Deploy your app to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions. + +## Further reading + +- [Create a group tab](https://learn.microsoft.com/microsoftteams/platform/tabs/how-to/create-channel-group-tab?pivots=node-java-script) +- [Extend Teams apps across Microsoft 365](https://learn.microsoft.com/en-us/microsoftteams/platform/m365-apps/overview) + + \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/csharp/assets/sample.json b/samples/tab-channel-group-quickstart/csharp/assets/sample.json new file mode 100644 index 0000000000..d036311311 --- /dev/null +++ b/samples/tab-channel-group-quickstart/csharp/assets/sample.json @@ -0,0 +1,68 @@ +[ + { + "name": "officedev-microsoft-teams-samples-tab-channel-group-quickstart-csharp", + "source": "officeDev", + "title": "Microsoft Teams C# Helloworld Sample", + "shortDescription": "A Microsoft Teams Hello World sample app built with .NET/C# that demonstrates essential features like tabs, bots, and messaging extensions for seamless interaction within the Teams environment.", + "url": "https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/tab-channel-group-quickstart/csharp", + "longDescription": [ + "This sample application illustrates how to create channel and group tabs in Microsoft Teams, providing collaborative spaces for web content. It includes detailed setup instructions, prerequisites, and guidance for deploying the app within your Teams environment." + ], + "creationDateTime": "2025-07-01", + "updateDateTime": "2025-07-01", + "products": [ + "Teams" + ], + "metadata": [ + { + "key": "TEAMS-SAMPLE-SOURCE", + "value": "OfficeDev" + }, + { + "key": "TEAMS-SERVER-LANGUAGE", + "value": "csharp" + }, + { + "key": "TEAMS-SERVER-PLATFORM", + "value": "netframework" + }, + { + "key": "TEAMS-FEATURES", + "value": "tab,bot,msgext" + } + ], + "thumbnails": [ + { + "type": "image", + "order": 100, + "url": "https://raw.githubusercontent.com/OfficeDev/Microsoft-Teams-Samples/main/samples/tab-channel-group-quickstart/csharp/Microsoft.Teams.Samples.TabChannelGroupQuickstart.Web/Images/tab_channel_group_module.gif", + "alt": "Solution UX showing starter solution: channel/group tab in C#" + } + ], + "authors": [ + { + "gitHubAccount": "OfficeDev", + "pictureUrl": "https://avatars.githubusercontent.com/u/6789362?s=200&v=4", + "name": "OfficeDev" + } + ], + "references": [ + { + "name": "Teams developer documentation", + "url": "https://aka.ms/TeamsPlatformDocs" + }, + { + "name": "Teams developer questions", + "url": "https://aka.ms/TeamsPlatformFeedback" + }, + { + "name": "Teams development videos from Microsoft", + "url": "https://aka.ms/sample-ref-teams-vids-from-microsoft" + }, + { + "name": "Teams development videos from the community", + "url": "https://aka.ms/community/videos/m365powerplatform" + } + ] + } +] \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/ts/Images/Helloworld.png b/samples/tab-channel-group-quickstart/ts/Images/Helloworld.png deleted file mode 100644 index 1e87d8d91e..0000000000 Binary files a/samples/tab-channel-group-quickstart/ts/Images/Helloworld.png and /dev/null differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/TabChannelGroupModule.gif b/samples/tab-channel-group-quickstart/ts/Images/TabChannelGroupModule.gif deleted file mode 100644 index add34c9a66..0000000000 Binary files a/samples/tab-channel-group-quickstart/ts/Images/TabChannelGroupModule.gif and /dev/null differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/hello_world.png b/samples/tab-channel-group-quickstart/ts/Images/hello_world.png new file mode 100644 index 0000000000..7a9b23b282 Binary files /dev/null and b/samples/tab-channel-group-quickstart/ts/Images/hello_world.png differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/install.png b/samples/tab-channel-group-quickstart/ts/Images/install.png new file mode 100644 index 0000000000..7a0cbe8ef1 Binary files /dev/null and b/samples/tab-channel-group-quickstart/ts/Images/install.png differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/setup_tab.png b/samples/tab-channel-group-quickstart/ts/Images/setup_tab.png new file mode 100644 index 0000000000..70292b9686 Binary files /dev/null and b/samples/tab-channel-group-quickstart/ts/Images/setup_tab.png differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/setuptab.png b/samples/tab-channel-group-quickstart/ts/Images/setuptab.png deleted file mode 100644 index afb101d5be..0000000000 Binary files a/samples/tab-channel-group-quickstart/ts/Images/setuptab.png and /dev/null differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/tab_channel_group_module.gif b/samples/tab-channel-group-quickstart/ts/Images/tab_channel_group_module.gif new file mode 100644 index 0000000000..7060c3b106 Binary files /dev/null and b/samples/tab-channel-group-quickstart/ts/Images/tab_channel_group_module.gif differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/tab_configure.png b/samples/tab-channel-group-quickstart/ts/Images/tab_configure.png new file mode 100644 index 0000000000..8492c940eb Binary files /dev/null and b/samples/tab-channel-group-quickstart/ts/Images/tab_configure.png differ diff --git a/samples/tab-channel-group-quickstart/ts/Images/tabconfigure.png b/samples/tab-channel-group-quickstart/ts/Images/tabconfigure.png deleted file mode 100644 index 087957a6d9..0000000000 Binary files a/samples/tab-channel-group-quickstart/ts/Images/tabconfigure.png and /dev/null differ diff --git a/samples/tab-channel-group-quickstart/ts/README.md b/samples/tab-channel-group-quickstart/ts/README.md index b78b535427..b07af5d21f 100644 --- a/samples/tab-channel-group-quickstart/ts/README.md +++ b/samples/tab-channel-group-quickstart/ts/README.md @@ -11,18 +11,19 @@ languages: extensions: contentType: samples createdDate: "07/07/2021 01:38:27 PM" + updatedDate: "07/08/2025 3:41:25 PM" urlFragment: officedev-microsoft-teams-samples-tab-channel-group-quickstart-ts --- # Tabs quick start -Explore this simple hello world app that showcases channel and group tabs in Microsoft Teams, designed to enhance collaboration around web-based content. This sample includes comprehensive setup steps, requirements, and deployment instructions, allowing you to easily integrate and run the app in your Teams environment. +Explore this simple app that showcases channel and group tabs in Microsoft Teams, designed to enhance collaboration around web-based content. This sample includes comprehensive setup steps, requirements, and deployment instructions, allowing you to easily integrate and run the app in your Teams environment. ## Included Features * Tabs ## Interaction with app. -![tabconfigure](Images/TabChannelGroupModule.gif) +![tabconfigure](Images/tab_channel_group_module.gif) ## Try it yourself - experience the App in your Microsoft Teams client Please find below demo manifest which is deployed on Microsoft Azure and you can try it yourself by uploading the app package (.zip file link below) to your teams and/or as a personal app. (Uploading must be enabled for your tenant, [see steps here](https://docs.microsoft.com/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant#enable-custom-teams-apps-and-turn-on-custom-app-uploading)). @@ -128,12 +129,13 @@ Ensure you have the Debugger for Chrome/Edge extension installed for Visual Stud `npm run build` ## Running the sample. +![install](Images/install.png) -![tabconfigure](Images/tabconfigure.png) +![tabconfigure](Images/tab_configure.png) -![setuptab](Images/setuptab.png) +![setuptab](Images/setup_tab.png) -![Helloworld](Images/Helloworld.png) +![Helloworld](Images/hello_world.png) Builds the app for production to the `build` folder.\ It correctly bundles React in production mode and optimizes the build for the best performance. @@ -148,5 +150,4 @@ See the section about [deployment](https://facebook.github.io/create-react-app/d [Create a group tab](https://learn.microsoft.com/microsoftteams/platform/tabs/how-to/create-channel-group-tab?pivots=node-java-script) - \ No newline at end of file diff --git a/samples/tab-channel-group-quickstart/ts/assets/sample.json b/samples/tab-channel-group-quickstart/ts/assets/sample.json index 28a3303125..94df1a3d7e 100644 --- a/samples/tab-channel-group-quickstart/ts/assets/sample.json +++ b/samples/tab-channel-group-quickstart/ts/assets/sample.json @@ -35,7 +35,7 @@ { "type": "image", "order": 100, - "url": "https://raw.githubusercontent.com/OfficeDev/Microsoft-Teams-Samples/main/samples/tab-channel-group-quickstart/ts/Images/TabChannelGroupModule.gif", + "url": "https://raw.githubusercontent.com/OfficeDev/Microsoft-Teams-Samples/main/samples/tab-channel-group-quickstart/ts/Images/tab_channel_group_module.gif", "alt": "Solution UX showing starter solution: channel/group tab in typescript" } ], diff --git a/samples/tab-channel-group-quickstart/ts/env/.env.local b/samples/tab-channel-group-quickstart/ts/env/.env.local index 9d078886fd..b15ca0ed65 100644 --- a/samples/tab-channel-group-quickstart/ts/env/.env.local +++ b/samples/tab-channel-group-quickstart/ts/env/.env.local @@ -5,15 +5,6 @@ TEAMSFX_ENV=local # Generated during provision, you can also add your own variables. -TAB_DOMAIN= -TAB_ENDPOINT= -TEAMS_APP_ID= -TEAMS_APP_PACKAGE_PATH= -# Generated during deploy, you can also add your own variables. -SSL_CRT_FILE= -SSL_KEY_FILE= -TEAMS_APP_TENANT_ID= -M365_TITLE_ID= -M365_APP_ID= \ No newline at end of file +# Generated during deploy, you can also add your own variables. diff --git a/samples/tab-channel-group-quickstart/ts/m365agents.yml b/samples/tab-channel-group-quickstart/ts/m365agents.yml index 316e5689b2..32e2f2327c 100644 --- a/samples/tab-channel-group-quickstart/ts/m365agents.yml +++ b/samples/tab-channel-group-quickstart/ts/m365agents.yml @@ -6,4 +6,5 @@ version: v1.2 additionalMetadata: sampleTag: Microsoft-Teams-Samples:tab-channel-group-quickstart-ts -environmentFolderPath: ./env \ No newline at end of file +environmentFolderPath: ./env +projectId: