I have recently come across a request to copy an entire OneNote Notebook programmatically from a SharePoint My Site (SP 2010) to a OneDrive location (SPO).
OneNote Notebook is not a standard Office document, as it is not a standalone file. It is associated with additional data that is stored locally by OneNote.
Therefore, copying the Notebook file alone in a straightforward fashion won't work.
OneNote's UI provides a way to achieve that by using the "Change Location" operation in the settings menu.
This operation modifies internal pointers from the current location to the desired destination.
Conveniently,OneNote also offers an API to do it programmatically in C#.
It should be noted that an SPO CSOM (client object model) requires .NET 4 to support certain methods (for example, "SharePointOnlineCredentials").
However, OneNote API does not work with .NET 4.
Consequently, I thought of two ways to get around this and make OneNote API and CSOM .NET 4 work together:
- Build two separate DLLs, one for .NET 4 and one for .NET 3.5.
- Have a single .NET 4 project, and set the "Embed Interop Types" reference property to "false", as shown below:
Personally, I prefer the latter, because it looks more elegant and keeps the code neater 🙂
To actually copy the Notebook itself I needed to manipulate both the file system and the OneDrive web, since OneDrive does not synchronize OneNote Notebooks:
- It was required to access both the OneDrive site and the SP 2010, so I created two different client context objects for both of the sites (each with its own permissions):
2. Created a folder for the Notebook in the file system.
3. Called OpenHierarchy and SyncHierarchy for the new Notebook, which is the new folder:
4. Changed the location for each individual section (only the files with the ".one" extension) in a OneNote Notebook as follows:
- Create an application object:
Microsoft.Office.Interop.OneNote.Application onApplication = new Microsoft.Office.Interop.OneNote.Application();
- Open the hierarchy of the source section:
onApplication.OpenHierarchy(absoluteUrl, string.Empty, out strId);
At this point, strId should contain a value for the section.
- Publish the section to the destination full location in the file system:
- Synchronize the hierarchy for this section:
The "publish" method didn't work consistently with an "https" location, so I used a OneDrive local synchronized folder to pass as a destination instead of a URL.
Note that the files with the extension "onetoc2" are "Open Notebook" files. We don't need to copy them.
Also note that this solution does not handle the "OneNote_RecycleBin" folder.
5. Finally in OneDrive web, set the Content Type of the Notebook folder, because otherwise it would appear as a regular folder.