teams tab
9 TopicsDisplay "unread" message count on badge in Teams tab application
Hi, We are giving chat options in Teams through a custom Tab app. Whenever i get new message in my Tab app, I would like to display the number of unread messages as a badge on the icon of the Tab app, just like the unread count in a Team channel, so the user doens't have to actively check the page, but is informed of new things to do through the badge. I can't find a method on how to make this possible. Anyone can help me out? Is it even possible for a custom tab app?5.5KViews0likes3CommentsCalling adaptive card from the channel message level using the button (Message created from Tab).
Hello, I am struggling with one problem, I am adding a message to a channel using a Tab I created, I would like the message to contain a button that would trigger an adaptive card popup. This popup would need to dynamically fetch data from an external API. For any answers, thank you1KViews0likes4CommentsShow "unread" message count on badge in Teams tab
Hi We are showing a intranet webpage in Teams through a custom Tab. when ever i get new message in my app . I would like to display the number of unread messages as a badge on the icon of the app, just like the unread count in a Team channel, so the user doens't have to actively check the page, but is informed of new things to do through the badge. I can't find a method on how to make this possible. Anyone can help me out? Search engines don't help me out either. Is it even possible for a custom app/tab?32KViews1like4CommentsTeams Integration with Dynamics 365 for an Entity showing not connected.
I need to be able to programmatically add a tab to teams that uses the Dynamics 365 teams app for an entity and I need it to show as connected to dynamics. Here is the requests I have built for both the Graph SDK and the CRM SDK side of things as well as some black magic I caught from fiddler that makes me sad. First lets start with some things that are used anytime anyone wants to make a dynamics tab. You need the teams app id for the Dynamics 365 Teams Integration App which happens to be the same on all tenants (yay) which iscd2d8695-bdc9-4d8e-9620-cc963ed81f41 next you are going to need all kinds of Organization/Environment specific info OrganizationUrl = https://orgXXXXXXXX.crm.dynamics.com OrganizationId (Guid) from Power Platform Admin Center is easiest Dynamics App Id (Guid) for the app your entity is from ie Sales Hub for an Opportunity Entity The Entity Id (Guid) for the entity you wish to connect to The Entity Type Code (int) from the Entity Metadata for ex Opportunity is 3 and Contact is 2 The above are all things you use the CRM API to go and get. You also need the following from Graph SDK. Team.Id (Guid) for the team you are adding a tab too Team.InternalId (string looks like 19:32characters@thread.tacv2) "19:00000000111122223333444444444444@thread.tacv2" also for the team you are adding the tab too Channel.Id (string looks like 19:32characters@thread.tacv2) for the channel you want to add the tab too A Display Name for the tab of your choice. A Fully Qualified Entity Name, not sure what the Dynamics devs call it but that is what it looks like, its format looks like this OrganizationId.ToString() + "|" + DynamicsAppId.ToString() + "|" + EntityId.ToString() + "|" + EntityObjectTypeCode.ToString() + "|" + EntityDisplayName Lastly you need a formatted content url to the dynamics entity you want to connect too I did it with this string.Format("{0}main.aspx?appid={1}&pageType=entityrecord&etn=opportunity&id={2}", OrganizationUrl, DynamicsAppId.ToString(), EntityId.ToString()) Because this is a fair amount of data to manage I made a little class to hold it all public class TeamTabInfo { public string DisplayName { get; set; } public Guid EntityId { get; set; } public string OrganizationUrl { get; set; } public Guid OrganizationId { get; set; } public Guid DynamicsAppId { get; set; } public int EntityObjectTypeCode { get; set; } public string EntityWebsiteUrl { get; set; } public string FormattedEntityWebsiteUrl { get { return string.Format(EntityWebsiteUrl, OrganizationUrl, DynamicsAppId.ToString(), EntityId.ToString(), EntityObjectTypeCode.ToString()); } } public string FormattedFQEN { get { return OrganizationId.ToString() + "|" + DynamicsAppId.ToString() + "|" + EntityId.ToString() + "|" + EntityObjectTypeCode.ToString() + "|" + DisplayName; } } } Now that I have something to hold the info that makes up a tab I needed a function that would actually make the objects for Graph to use. public static TeamsTab CreateTabRequest(TeamTabInfo tabInfo) { TeamsTabConfiguration ttc = new TeamsTabConfiguration(); TeamsTab tt = new TeamsTab(); ttc.EntityId = tabInfo.FormattedFQEN; ttc.WebsiteUrl = tabInfo.FormattedEntityWebsiteUrl; ttc.ContentUrl = $"https://msteamstabintegration.crm.dynamics.com/Home/Bootstrapper#pageType=dynamics-host&dynamicsUrl={ WebUtility.UrlEncode(tabInfo.FormattedEntityWebsiteUrl + "&navbar=off&flags=FCB.AllowLegacyDialogsInUci=false") }"; ttc.RemoveUrl = "https://msteamstabintegration.crm.dynamics.com/Home/Bootstrapper#pageType=tab-remove"; tt.Configuration = ttc; tt.DisplayName = tabInfo.DisplayName; tt.ODataBind = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/cd2d8695-bdc9-4d8e-9620-cc963ed81f41"; return tt; } TeamsTabConfiguration and TeamsTab come from the GraphSDK. Now you see those magic links in there? They apparently hide the secret to success here but we will come back to that. Here is the code I use to get the info from CRM and GraphSDK I need to be able to create the Tab objects. GraphServiceClient gsc = await gf.GetClient(tenantUser, tenantPass); var myTeamsResult = await gsc.Me.JoinedTeams.Request().GetAsync(); string contosoCoffeeTeamsId = myTeamsResult.Where(t => string.Compare(t.DisplayName, "My Team", true) == 0).FirstOrDefault()?.Id; var contosoCoffeeTeamResult = await gsc.Teams[contosoCoffeeTeamsId].Request().GetAsync(); var channelsResult = await gsc.Teams[contosoCoffeeTeamsId].Channels.Request().GetAsync(); string espressoId = channelsResult.Where(c => string.Compare(c.DisplayName, "Group 1", true) == 0).FirstOrDefault()?.Id; var installedAppsResult = await gsc.Teams[contosoCoffeeTeamsId].InstalledApps.Request().Expand("teamsAppDefinition").GetAsync(); bool dynamicsAppInstalled = installedAppsResult.Where(i => i.TeamsAppDefinition.TeamsAppId == "cd2d8695-bdc9-4d8e-9620-cc963ed81f41").Any(); if(!dynamicsAppInstalled) { var teamsAppInstallation = new TeamsAppInstallation { AdditionalData = new Dictionary<string, object>() { //well known dynamics teams app id = cd2d8695-bdc9-4d8e-9620-cc963ed81f41 {"teamsApp@odata.bind", "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/cd2d8695-bdc9-4d8e-9620-cc963ed81f41"} } }; await gsc.Teams[contosoCoffeeTeamsId].InstalledApps .Request() .AddAsync(teamsAppInstallation); } string orgUrl = await gf.GetOrganizationUrl(tenantName, tenantPass); CrmServiceClient csc = gf.ConnectCRMAdminLogin(tenantName, tenantPass, orgUrl); EntityMetadata opportunityEm = csc.GetEntityMetadata("opportunity", EntityFilters.Entity); QueryExpression queryPages = new QueryExpression("appmodule"); queryPages.ColumnSet = new ColumnSet(new String[] { "appmoduleid", "name", "url", "description" }); queryPages.Criteria.AddCondition("name", ConditionOperator.In, new string[] { "Sales Hub" }); TeamTabInfo cafeAutoTab = new TeamTabInfo(); cafeAutoTab.DisplayName = "Opportunity 1"; cafeAutoTab.EntityWebsiteUrl = "{0}main.aspx?appid={1}&pageType=entityrecord&etn=opportunity&id={2}"; cafeAutoTab.OrganizationId = csc.ConnectedOrgId; cafeAutoTab.OrganizationUrl = orgUrl; cafeAutoTab.EntityObjectTypeCode = opportunityEm.ObjectTypeCode.Value; cafeAutoTab.DynamicsAppId = queryResult.Entities.Where(w => w.Attributes.Values.Contains("Sales Hub")).FirstOrDefault().Id; queryPages = new QueryExpression("opportunity"); queryPages.ColumnSet = new ColumnSet(new String[] { "opportunityid", "name", "description" }); queryPages.Criteria.AddCondition("name", ConditionOperator.Equal, "Opportunity 1"); queryResult = csc.RetrieveMultiple(queryPages); cafeAutoTab.EntityId = queryResult.Entities.FirstOrDefault().Id; var tabResult = await gsc.Teams[contosoCoffeeTeamsId].Channels[espressoId].Tabs.Request().AddAsync(GraphFunctions.CreateTabRequest(cafeAutoTab)); Assume GraphServiceClient gsc and CrmServiceClient csc are functional as I have other huge functions that do all the auth stuff and give me back a functional client. The above code adds a tab to teams but apparently it is only half added, just the teams half. There is a CRM half I learned and this involves an insertion into thelogical dataverse entity table "msdyn_teamscollaboration" and that ends up looking a bit like the following Microsoft.Xrm.Sdk.Entity teamsCollabToAdd = new Microsoft.Xrm.Sdk.Entity(); teamsCollabToAdd.LogicalName = "msdyn_teamscollaboration"; teamsCollabToAdd.Attributes.Add("msdyn_teamid", contosoCoffeeTeamResult.InternalId); teamsCollabToAdd.Attributes.Add("regardingobjecttypename", "opportunity"); teamsCollabToAdd.Attributes.Add("msdyn_groupid", new Guid(contosoCoffeeTeamsId)); teamsCollabToAdd.Attributes.Add("msdyn_tenantid", csc.TenantId); teamsCollabToAdd.Attributes.Add("msdyn_channelid", espressoId); teamsCollabToAdd.Attributes.Add("regardingobjectid", cafeAutoTab.EntityId); teamsCollabToAdd.Attributes.Add("statecode", "Active"); teamsCollabToAdd.Attributes.Add("msdyn_channelfolderrelativeurl", "/sites/My Team/Shared Documents/Group 1"); teamsCollabToAdd.Attributes.Add("msdyn_teamsiteurl", string.Format("https://{0}.sharepoint.com/sites/My%20Team", tenantName.ToLower())); teamsCollabToAdd.Attributes.Add("msdyn_channelname", "Group 1"); teamsCollabToAdd.Attributes.Add("msdyn_channeltype", "regular"); teamsCollabToAdd.Attributes.Add("msdyn_teamname", "My Team"); teamsCollabToAdd.Attributes.Add("regardingobjecttypecode", cafeAutoTab.EntityObjectTypeCode); teamsCollabToAdd.Attributes.Add("msdyn_appid", "cd2d8695-bdc9-4d8e-9620-cc963ed81f41"); teamsCollabToAdd.Attributes.Add("msdyn_pipedentityid", cafeAutoTab.FormattedFQEN); teamsCollabToAdd.Attributes.Add("msdyn_weburl", cafeAutoTab.FormattedEntityWebsiteUrl); teamsCollabToAdd.Attributes.Add("msdyn_contenturl", $"https://msteamstabintegration.crm.dynamics.com/Home/Bootstrapper#pageType=dynamics-host&dynamicsUrl={WebUtility.UrlEncode(cafeAutoTab.FormattedEntityWebsiteUrl)}"); CreateRequest cdsRequest = new CreateRequest(); cdsRequest.Target = teamsCollabToAdd; CreateResponse cdsResponse = (CreateResponse)csc.Execute(cdsRequest); Now this also successfully runs and I get a record added to the Dataverse. Not only do I get a record, but if I remove this created tab and use the UI I get the exact same record added to the Dataverse if everyway except a new primary key. Everything looks in place and it should be solid, but all is not well in the land of no documentation. Remember that magic link I mentioned before? Well it turns out that if you use my code you get a tab added that has an angry bit. This record is not connected to Dynamics 365. Repin the tab and try again. And that learn more points tohttps://msteamstabintegration.crm.dynamics.com/Home/undefinedwhich goes nowhere and looks suspiciously familiar to a magic link. If you use the UI you get This record is successfully connected to Dynamics 365. and that Learn More points tohttps://go.microsoft.com/fwlink/?linkid=2019594which is at least a functional. So I did what anyone else would do when you have functional UI and non functional API calls after making sure the backend data I had access too was identical. I hit it with Fiddler. A behold a call is made to the following API endpoint. https://msteamstabintegration.crm.dynamics.com/api/TeamsTabService/PostPinTasks This is a secure endpoint we don't have access too. This appears the difference between using the UI and the 2 API calls to add the tab and the record tomsdyn_teamscollaboration is the call to the above endpoint. So after all the preparation and all the hunting this is the question for the MS group that owns https://msteamstabintegration.crm.dynamics.com WHAT DOES THAT ENDPOINT DO?What are these "Post Pin Tasks"? BTW pinning a view (savedquery entity) doesn't have this issue since it isn't kept in sync the same way, it is specific to Direct Entity Connections. ************************************************************* UPDATE 05/24/2022 ************************************************************* I have an update and there is now an api endpoint that can be used to attach an entity record and get the connection symbol to show up correctly. First you must follow the directions atEnable linking of Dynamics 365 records to Microsoft Teams channels | Microsoft Docsand consent to the permissions that are asked when the options are turned on. Once that is setup you can then make a call that matches the following criteria. Syntax: The programmatic support for the Collaborate functionality is implemented as a custom API in Dynamics. POST <org-url>/api/data/v9.0/msdyn_associateRecordToChannel Request Body In the request body, supply a JSON representation of the following properties. Property Type Description msdyn_recordId string Unique identifier of the entity record or view msdyn_entityLogicalName string Entity Logical Name of the record or view to be pinned msdyn_teamId string Unique Identifier of the Team to which the channel belongs msdyn_teamInternalId string Team Internal Id of the Team to which the channel belongs msdyn_teamDisplayName string Display Name of the team to which the channel belongs msdyn_channelId string Unique Identifier of the channel where record is to be pinned msdyn_channelDisplayName string Display Name of the channel msdyn_channelType string Type of the channel (standard or private) msdyn_appModuleId string App Id of the Model-Driven App in Dynamics msdyn_orgUrl string Dynamics Environment URL msdyn_pageType string Specifies whether the record is a entity record or a view(entityrecord or entitylist) 1. Pin an entity record in a channel in Teams Request POSThttps://graph.microsoft.com/v1.0/teams/57fb72d0-d811-46f4-8947-305e6072eaa5/channels Content-type: application/json { "msdyn_recordId": "f3372c45-1a83-ec11-8d20-00224805bb02", "msdyn_entityLogicalName": "account", "msdyn_teamId": "511298c7-f02d-43a7-94ec-cc8d3822fab6", "msdyn_teamInternalId": "19:54d5461eadef4e1bbf1581a08bb31d1b@thread.skype", "msdyn_teamDisplayName": "New Teamz456", "msdyn_channelId": "19:54d5461eadef4e1bbf1581a08bb31d1b@thread.skype", "msdyn_channelDisplayName":"General", "msdyn_channelType": "standard", "msdyn_appModuleId": "440eaf39-997e-ec11-8d20-000d3a3b2a9b", "msdyn_orgUrl":https://tp2sg851meenaltestorg.crm10.dynamics.com, "msdyn_pageType":"entityrecord" } Programmatic issues that are still present in the views of automation. 1. I don't know how to set the settings and consent to the permissions programmatically, I can set the settings in the Organization table in the dataverse but that does NOT consent to the permissions and so the api call does not work. 2. When using the UI there is an option to "Post" to the channel as an announcement. This post has a special embedded link that links to the newly created tab. The formatting appears to go through the Skype api even though it is in teams and I have not figured out how to replicate this announcement functionality at this time.4KViews1like8CommentsIn-meeting-app and tabs app are not able to access mic
I am trying to build a tab and In-meeting-app leveraging web speech API that needs mic access to hear the user, but I think Teams not permitting my app to access the mic. How can get access to the mic inside my In-meeting-app and tabs app?1.5KViews0likes3CommentsGet last url loaded in the iframe in microsoft teams custom app
I have created an custom application in microsoft teams. And loaded our organization website in that tab. As in the organization website it have multiple routes which user can go while accessing website from tab. So, is there any way in teams js sdk which can give the last route which was accessed by the user in the tab.Solved1.6KViews1like2CommentsApp removal notification for a channel
I have a Teams tab based app configured in a team channel. I'd like to get notification within my app when the app is removed from the channel. How do I achieve this? If I can achieve this through an outgoing webhook, what messages I should be looking for in my callback URL? Is there any documentation for the same? Pls help.835Views0likes0CommentsTeams One Note tab, still shows deleted Notebook
I deleted a OneNote Notebook in Teams once, deleted it, and cleared it from the recycle bin too, nevertheless it still shows in the query when I want to open a new notebook. This is really frustrating because it makes it very difficult to find the notebook you are looking for because it is so cluttered with notebooks that were deleted and do not exist anymore, that however are still shown.Solved2.4KViews1like3CommentsGraph API to create Tab in MS Teams with custom SPFx WebPart
Hello Experts, I have created SPFx WebPart and deployed it in SharePoint. I have enabled "Team" scope and this works fine along with team context when I add it in MS Teams manually. Now, I am trying to create Tab programmatically using Graph API endpoints as below, API: https://graph.microsoft.com/v1.0/teams/xxxx-9532-4b0c-997f-60704a3f8cd8/channels/19:c09b7d2b2cc546xxxc3f3a0e6d979f65@thread.skype/tabs Body: { "displayName": "Opportunity Summary", "canUpdateConfiguration":true, "teamsApp@odata.bind" : "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('8f4b04a6-a79e-4677-b81c-dff4e34d9989')", "configuration": {"entityId": "d500-452a-885d-b091d9151dbe", "contentUrl": "https://xxxxx.sharepoint.com/sites/team0042/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/sites/team0042/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=30A56BEF-D500-452A-885D-B091D9151DBE", "websiteUrl": null, "removeUrl": null } } The tab is getting created successfully but unable to load WebPart in the tab. Below is a response when I hit this API call, { "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams('xxxxxx-9532-4b0c-997f-60704a3f8cd8')/channels('19%3Ac09b7d2b2cc546cfbc3f3a0e6d979f65%40thread.skype')/tabs/$entity", "id": "181f19b8-87f2-484e-be2f-59bc896a45f1", "displayName": "Opportunity Summary", "webUrl": "https://teams.microsoft.com/l/entity/xxxxxxx-a79e-4677-b81c-dff4e34d9989/_djb2_msteams_prefix_2382545309?label=Opportunity+Summary&context=%7b%0d%0a++%22canvasUrl%22%3a+%22https%3a%2f%2fhexawaretek.sharepoint.com%2fsites%2fteam0042%2f_layouts%2f15%2fTeamsLogon.aspx%3fSPFX%3dtrue%26dest%3d%2fsites%2fteam0042%2f_layouts%2f15%2fteamshostedapp.aspx%253FopenPropertyPane%3dtrue%2526teams%2526componentId%3d30A56BEF-D500-452A-885D-B091D9151DBE%22%2c%0d%0a++%22channelId%22%3a+%2219%3ac09b7d2b2cc546cfbc3f3a0e6d979f65%40thread.skype%22%2c%0d%0a++%22subEntityId%22%3a+null%0d%0a%7d&groupId=6880a93f-9532-4b0c-997f-60704a3f8cd8&tenantId=3d661275-2f86-4c66-a403-89012031c616", "configuration": { "entityId": "d500-452a-885d-b091d9151dbe", "contentUrl": "https://xxxxx.sharepoint.com/sites/team0042/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/sites/team0042/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=30A56BEF-D500-452A-885D-B091D9151DBE", "removeUrl": null, "websiteUrl": null } } I search a lot to define configuration entity in the body for custom deployed SPFx Teams App but did not find any solution. Further analysis, I tried to find out manually deployed app configuration entity but seems that list guid, id and webinstanceId is generating dynamically as below, { "id": "a75956d0-131c-44e3-a484-f59dd2b6ea7e", "displayName": "SPFx Summary WebPart", "webUrl": "https://teams.microsoft.com/l/entity/xxxxx-a79e-4677-b81c-dff4e34d9989/_djb2_msteams_prefix_70e2b7d4-a4e8-48a0-aff9-02f03885f93e?label=SPFx+Summary+WebPart&context=%7b%0d%0a++%22canvasUrl%22%3a+%22https%3a%2f%2fxxxxx.sharepoint.com%2fsites%2fteam0042%2f_layouts%2f15%2fTeamsLogon.aspx%3fSPFX%3dtrue%26dest%3d%2fsites%2fteam0042%2f_layouts%2f15%2fteamshostedapp.aspx%253Flist%3d1ef02590-ede8-4825-a70f-43002f62bcf9%2526id%3d1%2526webPartInstanceId%3d731b7934-fdd2-4d3b-8ba1-2132cb4b14c9%22%2c%0d%0a++%22channelId%22%3a+%2219%3ac09b7d2b2cc546cfbc3f3a0e6d979f65%40thread.skype%22%2c%0d%0a++%22subEntityId%22%3a+null%0d%0a%7d&groupId=6880a93f-9532-4b0c-997f-60704a3f8cd8&tenantId=3d661275-2f86-4c66-a403-89012031c616", "configuration": { "entityId": "sharepointtab_45b23925-551a-4d81-a0b9-9d46297098c1", "contentUrl": "https://xxxxx.sharepoint.com/sites/team0042/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/sites/team0042/_layouts/15/teamshostedapp.aspx%3Flist=1ef02590-ede8-4825-a70f-43002f62bcf9%26id=1%26webPartInstanceId=731b7934-fdd2-4d3b-8ba1-2132cb4b14c9", "removeUrl": "https://xxxxx.sharepoint.com/sites/team0042/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/sites/team0042/_layouts/15/teamshostedapp.aspx%3Flist=1ef02590-ede8-4825-a70f-43002f62bcf9%26id=1%26webPartInstanceId=731b7934-fdd2-4d3b-8ba1-2132cb4b14c9%26removeTab", "websiteUrl": null, "dateAdded": "2019-11-04T07:08:09.086Z" } } Please guide me to defined configuration parameters in the body of Create Tab Graph API endpoints for deployed custom SPFx Teams App. Thanks, Uday G2.8KViews0likes3Comments