April 14, 2020

Cloning OneNote Tab without /clone REST endpoint

Series

This is series of blog articles showing how to clone Teams Channels and Tabs without using the Clone REST endpoint or direct REST queries. Except we use the Microsoft.Graph (3.1.0). First post discusses things in general, details about different tab types are separated to individual articles.

  1. Cloning Teams Channels and Tabs without /clone REST endpoint
  2. Cloning Planner Tab without /clone REST endpoint
  3. Cloning OneNote Tab without /clone REST endpoint <<YOU ARE HERE>>
  4. Cloning Web Tab without /clone REST endpoint (TBD)
  5. Copying Teams Files tab content using MoveCopyUtil

Solution

In this piece of code, we first determine current source tab is of type OneNote. We must first create new Notebook for the tab, and for this we add simple retry logic as you cannot have Notebooks with duplicate name. This retry logic adds running integer to Notebook name until the creation succeeds.

Now, after we have successfully created the Notebook, it is time to create the Tab. Do note the special format of the EntityId parameter for the tab, as well as rather identical URLs.

Finally, note that you may end up getting ServiceException although tab creation succeeds, thus the scary Exception swallowing.

Using term cloning when it comes to this tab type can be a bit misleading, as we’re not cloning the tab content, but only the tab and creating new content. Perhaps think of this as cloning from the Team perspective, while the individual tabs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
if (sourceTab.TeamsApp.Id.Equals(TeamsAppId.OneNote))
{
logger?.LogInformation($"Creating OneNote. Channel: {sourceChannel.DisplayName}. {newTeamId}");

var newNotebook = await retry.ExecuteAsync(async () =>
{
    var i = 0;
    Notebook nb = null;
    string nbName;

    while (i < 100 && null == nb)
    {
        nbName = $"{TextTools.RemoveSpecialCharactersForNotebook(requestData.title)}{(i > 0 ? $" {i}" : "")} Notebook";

        try
        {
            nb = await graphClient.Groups[newGroup.Id].Onenote.Notebooks.Request().AddAsync(new Notebook()
            {
                DisplayName = nbName
            });

            break;
        }
        catch (ServiceException ex)
        {
            if ((int)ex.StatusCode == 409)
            {
                // this is fine, Notebook with such name already exists, let's find first available by appending integers to name
                logger?.LogWarning($"Notebook with name '{nbName}' already exists, trying next integer. Channel: {sourceChannel.DisplayName}. {newTeamId}");
            }
        }
        finally
        {
            i++;
        }
    }

    return nb;
});

try
{
    logger?.LogInformation($"Adding OneNote tab. Channel: {sourceChannel.DisplayName}. {newTeamId}");

    await retry.ExecuteAsync(async () =>
    {
        await graphClient.Teams[newTeamId].Channels[channelId].Tabs.Request().AddAsync(new TeamsTab()
        {
            ODataType = null,
            DisplayName = newNotebook.DisplayName,
            Configuration = new TeamsTabConfiguration()
            {
                EntityId = $"{Guid.NewGuid()}_{newNotebook.Id}",
                ContentUrl = $"https://www.onenote.com/teams/TabContent?entityid=%7BentityId%7D&subentityid=%7BsubEntityId%7D&auth_upn=%7Bupn%7D&notebookSource=New&notebookSelfUrl=https%3A%2F%2Fwww.onenote.com%2Fapi%2Fv1.0%2FmyOrganization%2Fgroups%2F{{groupId}}%2Fnotes%2Fnotebooks%2F{newNotebook.Id}&oneNoteWebUrl={newNotebook.Links.OneNoteWebUrl.Href}&notebookName={newNotebook.DisplayName}&ui={{locale}}&tenantId={{tid}}",
                RemoveUrl = $"https://www.onenote.com/teams/TabRemove?entityid=%7BentityId%7D&subentityid=%7BsubEntityId%7D&auth_upn=%7Bupn%7D&notebookSource=New&notebookSelfUrl=https%3A%2F%2Fwww.onenote.com%2Fapi%2Fv1.0%2FmyOrganization%2Fgroups%2F{{groupId}}%2Fnotes%2Fnotebooks%2F{newNotebook.Id}&oneNoteWebUrl={newNotebook.Links.OneNoteWebUrl.Href}&notebookName={newNotebook.DisplayName}&ui={{locale}}&tenantId={{tid}}",
                WebsiteUrl = $"https://www.onenote.com/teams/TabRedirect?redirectUrl={newNotebook.Links.OneNoteWebUrl.Href}"
            },
            AdditionalData = new Dictionary<string, object>()
            {
                {
                    "teamsApp@odata.bind", $"https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/{sourceTab.TeamsApp.Id}"
                }
            }
        });
    });
}
catch (ServiceException ex)
{
    HandleTabCreationException(ex, logger, sourceTab, newTeamId);
}
}

No comments:

Post a Comment