Cloning Teams and Configuring Tabs via Microsoft Graph: Configuring the OneNote tab
Last updated on October 16, 2021
Welcome back to this blog post series where we clone a team with its tab configurations! So far we’ve looked at the reasons why this topic is important, and how you can use Microsoft Graph to do the basic cloning of a team. In my previous blog post, I also showed you how you can loop through the template team channels and their tabs to collect all the required information for configuring the corresponding tabs in the new team.
I also mentioned that each different kind of tab needs to be configured in a slightly different way, and hence we need individual logic for each of the tab types. In this blog post, I’ll show you how you can configure one of the most commonly used tabs: OneNote.
Published articles in this blog post series
- Part 0: Prelude
- Part 1: Cloning a Team
- Part 2: Configuring tabs – The Fundamentals
- Part 3: Configuring the OneNote tab (you are here)
- Part 4: Configuring the Planner tab
- Part 5: Configuring the SharePoint and Files tabs
Table of Contents
- OneNote App ID
- Deducing which notebook to show
- Forming the body for the update request
- Supportive methods
- Afterword
OneNote App ID
People can have different opinions on how the OneNote tabs should be configured. Should the group default OneNote file be used on all of the tabs, and just show a different section? Or should each of the tabs have a separate file? I opted for the latter with a little twist.
Before we start doing anything else, we need to be able to identify what kind of tab-type we are dealing with. Open the project that you’ve been working on so far, and create a new class called TeamsAppId and add the OneNote app ID to it.
namespace MyProject { public class TeamsAppId { public static readonly string OneNote = "0d820ecd-def2-4297-adad-78056cde7c78"; } }
Deducing which notebook to show
Now the execution can proceed to the correct if-block based on the tab type. What we basically want to do here is to reconfigure the tabs that have been displaying to the template team/group default notebook to display the new team/group default notebook. And if some other notebook was displayed in the template tab, we create a brand new notebook in the new team and display that.
If you are following my blog series and actually used the code from the previous blog post, you can replace the TO DO comment in the ConfigureClonedTeamsTabs method with this bit.
if (teamsAppId == TeamsAppId.OneNote) { JToken notebook; if (await IsDefaultNotebook(templateTeamId, WebUtility.UrlDecode(templateTabDisplayName))) { notebook = await MicrosoftGraph.GetDefaultNotebook(newTeamId); } else { notebook = await MicrosoftGraph.CreateNotebook(newTeamId, WebUtility.UrlDecode(newTab.SelectToken("displayName").ToString())); } body = ConfigureOneNoteTab(newTeamId, notebook); }
If the group default notebook has been pinned as a tab in the template team, its name is “group name Notebook” by default. We will first check if the template tab name starts with the team name. We don’t want to check for the whole tab name, because Notebook might be replaced with something else depending on localization settings.
private static async Task<bool> IsDefaultNotebook(string teamId, string tabName) { var teamName = (await MicrosoftGraph.GetGroup(teamId)).SelectToken("displayName").ToString(); return tabName.StartsWith(teamName); //We just check the start because "Notebook" could be replaced with something else due to localization settings }
If the tab name starts with the template team name, we’ll fetch the default notebook of the new team/group via Microsoft Graph for displaying it in the new tab. In the past, someone always had to click on the Notebook link in the SharePoint team site left-hand side navigation before the actual OneNote file was generated. Luckily, these days the OneNote file is already there right after the group-connected team site has been created. Because of this, we are actually able to configure our Teams tab to point to this default OneNote file.
However, if we deduce that the template tab isn’t showing the default notebook, we create a new notebook which name matches the tab name. For these new notebooks, a folder Notebooks is automatically created in the Documents library root.
Forming the body for the update request
After we have either fetched the group default notebook or created a brand new one, we’ll finally proceed to display it in the tab. Our goal here is to format the value for the contentUrl
property of the new tab. The URL uses a certain pattern, and we just need to fill in the blanks, so to speak. The information we need for the URL are the notebookId
, notebookName
, oneNoteWebUrl
and siteUrl
. And all of these we can get from the notebook we just fetched or created.
In addition to setting the contentUrl
, which does the trick for actually showing the correct file in the tab, I also want to set the tab name. By default, the template tab names are transferred to the new team as-is (you can see that from this picture). So, if the template tab is displaying the default notebook, and the template team name is mentioned in the tab name, it will still be there in the new team unless we do something about it. And because we already have the notebookName
available, it is a simple task of setting it as the tab displayName
.
And with that, our request body is complete, and we can return it to the method that makes the actual call to Microsoft Graph to update the tab. Check the previous blog post for that piece of code.
private static string ConfigureOneNoteTab(string teamId, JToken notebook) { var notebookId = notebook.SelectToken("id").ToString(); var notebookName = notebook.SelectToken("displayName").ToString(); var oneNoteWebUrl = notebook.SelectToken("links.oneNoteWebUrl.href").ToString(); var siteUrl = string.Join("/", oneNoteWebUrl.Split('/').Take(5)); return $"{{ 'displayName': '{notebookName}', 'configuration': {{ 'contentUrl': 'https://www.onenote.com/teams/TabContent?entityid=%7BentityId%7D&subentityid=%7BsubEntityId%7D&auth_upn=%7Bupn%7D¬ebookSource=Pick¬ebookSelfUrl=https%3A%2F%2Fwww.onenote.com%2Fapi%2Fv1.0%2FmyOrganization%2Fgroups%2F{teamId}%2Fnotes%2Fnotebooks%2F{notebookId}&oneNoteWebUrl={oneNoteWebUrl}¬ebookName={notebookName}&siteUrl={siteUrl}&ui={{locale}}&tenantId={{tid}}' }} }}"; }
Supportive methods
In addition to the code above, you’ll also need to add the following methods to the MicrosoftGraph helper class we created earlier.
public static async Task<JToken> GetGroup(string groupId) { return await HttpRequest.GetResponseBodyAsJson($"https://graph.microsoft.com/v1.0/groups/{groupId}", HttpRequest.Method.Get); } public static async Task<JToken> GetDefaultNotebook(string groupId) { return (await HttpRequest.GetResponseBodyAsJson($"https://graph.microsoft.com/v1.0/groups/{groupId}/onenote/notebooks?$orderby=createdDateTime", HttpRequest.Method.Get)).SelectToken("value[0]"); } public static async Task<JToken> CreateNotebook(string groupId, string notebookName) { var body = $"{{ 'displayName': '{notebookName}' }}"; return await HttpRequest.GetResponseBodyAsJson($"https://graph.microsoft.com/v1.0/groups/{groupId}/onenote/notebooks", HttpRequest.Method.Post, body); }
Afterword
I find configuring tabs automatically quite interesting. There isn’t necessarily just one way of doing it; different people can have different views on how it should be done. And that is most likely the reason why this kind of configuration doesn’t come built-in. What are your thoughts? Have you already implemented something like this? Was your logic similar to mine or did you have another idea on how it should be done? Let me know in the comments!
And if you’d like to be notified when the next tab configurations are released, follow me on Twitter! I share some other useful tidbits there, too. 😉
Thank you for reading — it is much appreciated as always — and until next time!
Laura
Finally got it working, however for a web, i think the app id has to be
public static readonly string webid = “com.microsoft.teamspace.tab.web”; inside appid class
Do we have to investigate a sample tab made of web in dev tools or is there a working example.
thanks
Hi Nabajyoti,
Yes, all the different tab types (OneNote, Planner, Web) have their own IDs that you need to use.
I have not shared an example of how to use the Web tab in this blog post series, but the same working principles should apply.
Laura
Hi Laura,
Thanks for this great post. It is very helpful!
One thing i’m running into is that the default OneNote notebook is not created in the SiteAssets list, even though the teamsite and group have allready created. It seems I still have to manually click on the Notebook link in the SharePoint team site left-hand side navigation before the actual OneNote file get’s generated.
Are you sure that these days the OneNote file is already there right after the group-connected team site has been created?
Wouter
Hi Wouter,
How are you attempting to fetch the OneNote notebook? Via Graph as described in this article?
Laura
Yes, like this.
return (await HttpRequest.GetResponseBodyAsJson($”https://graph.microsoft.com/v1.0/groups/{groupId}/onenote/notebooks?$orderby=createdDateTime”, HttpRequest.Method.Get)).SelectToken(“value[0]”);
I see the default notebook only get’s created in the SiteAssets folder once I have clicked the Notebook link in the SharePoint team site left-hand side navigation.
OK, good to know, thanks! 🙂
Laura
A little it late but if you want the site assets onenote to be created, you can start the OneNote site feature
POST : [You site url]/_api/web/features/add(‘f151bb39-7c3b-414f-bb36-6bf18872052f’)
Thanks for your excellent article! I am trying to configure a document library tab (cloned from a template team). However I notice that the PATCH request fails when using a token obtained via client credentials flow but succeeds when using token acquired via the resource owner password flow? Has that also been your experience?
Cheers,
Raj
Hi Raj,
Yes, I’ve run into that issue too. The underlying reason is that when you clone a team with client credentials, the tabs don’t get cloned, so there’s nothing to configure.
Laura
Great post!
Could you also provide a working example of creating a tab for document library (“com.microsoft.teamspace.tab.files.sharepoint”)?
https://github.com/microsoftgraph/microsoft-graph-docs/blob/master/concepts/teams-configuring-builtin-tabs.md did not work for me.
Hi Gennadiy,
Absolutely! I’ll add that on my todo list. 🙂
Laura