Friday, September 5, 2014

Provisioning ready for use OneNote notebook to Sharepoint site

OneNote notebooks can be manually saved to the Sharepoint document library so users may share the notebook in the work. It is useful feature e.g. for workspaces which are often used in Sharepoint. But as it often happen those actions which can be done manually are not always so simple when we will need to automate the process, e.g. when we need to provision ready for use OneNote notebook during creation of the site. In this article I will show how it can be done.

First of all we need to make some preparation work: we need to prepare notebook template which will be provisioned to our doclib. In order to do this create new document library manually and select “Microsoft OneNote 2010 Notebook” in Document template list:


So under New document you should see OneNote icon:


Create new document here. It will open OneNote client where you will need to specify name of the new notebook when it will unpack the new notebook:


During unpacking it will create local copy of the notebook in your file system under “C:\Users\username\Documents\OneNote Notebooks” location. Now we should save this notebook to the Sharepoint document library: File > Share > Add a place > Office 365 SharePoint and put url of your site in the opened dialog. By default it only shows “Office 365 SharePoint”, but if you will put url manually, it should also allow to save it in on-premise site (see Getting OneNote 2013 to Work With On-Premise SharePoint):


After that if you will go to the document library you will see the saved notebook there:


Internally OneNote notebooks are similar to document sets which in turn close to the folders. Moreover if you will check add Content type column to the doclib view, it will show that notebook has Folder content type as it shown on the picture above. Now open document library in Explorer view: it will show the notebook as regular folder. Copy this folder to the local file system. It will contain 2 files:

  1. New Section
  2. Open Notebook.onetoc2

This folder with files will be used as template when during provisioning. However here we will face with the problem: if we will just copy this folder back to the document library but with different name, we will see that it will become regular Sharepoint folder:


On this picture we see that original notebook saved from OneNote client has correct icon, while the same file copied from local file system looks and behaves like regular folder. I.e. when user will click on such copied notebook, then instead of opening OneNote app (web app or desktop client) it will just go inside the folder where 2 mentioned files will be shown. This is definitely not what we want.

Let’s check the difference between these 2 folders e.g. in Sharepoint client browser (similar to the Sharepoint manager, but works also for O365):


As you can see real notebook has HTML File type set to “OneNote.Notebook”. The same difference also can be found for the ProgId field:


Another found difference is that real notebook has Title specified, while copied – not. Now the fix is quite obvious: we need to set ProgId for the copied Notebook (and Title for safety) in order to make it “real” notebook. Here is the code which makes the trick:

   1: var list = web.GetList(SPUrlUtility.CombineUrl(web.ServerRelativeUrl,
   2:     "OneNote"));
   3: var folder = list.Folders.Cast<SPListItem>().FirstOrDefault(f =>
   4:     f.Url.EndsWith("Notebook"));
   5: folder[SPBuiltInFieldId.Title] = "Notebook";
   6: folder.ProgId = "OneNote.Notebook";
   7: folder.Update();

The same thing can be also done via client object model:

   1: var ctx = new ClientContext("");
   2: ctx.AuthenticationMode = ClientAuthenticationMode.Default;
   3: var pwd = new SecureString();
   4: foreach (var c in "password")
   5: {
   6:     pwd.AppendChar(c);
   7: }
   8: ctx.Credentials = new SharePointOnlineCredentials("username", pwd);
   9: var site = ctx.Site;
  10: ctx.Load(site);
  12: var web = site.OpenWeb("foo");
  13: ctx.Load(web);
  15: var lists = web.Lists;
  16: ctx.Load(lists);
  18: var list = lists.GetByTitle("OneNote");
  19: ctx.Load(list, l => l.RootFolder);
  21: var folders = list.RootFolder.Folders;
  22: ctx.Load(folders);
  24: var folder = folders.GetByUrl("OneNote/NotebookCopied");
  25: ctx.Load(folder, f => f.ListItemAllFields);
  26: ctx.ExecuteQuery();
  28: folder.ListItemAllFields["Title"] = "NotebookCopied";
  29: folder.ListItemAllFields["HTML_x0020_File_x0020_Type"] =
  30:     "OneNote.Notebook";
  31: folder.ListItemAllFields.Update();
  32: ctx.ExecuteQuery();

After fix copied notebook will look and behave like real OneNote notebook:


This code can be added to the feature receiver or to the web provisioned handler. Also you will need to provision 2 OneNote files via module to your document library before it will run:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Elements xmlns="">
   3:   <Module Name="MONotebook" Url="OneNote">
   4:     <File Path="MONotebook\New Section"
   5: Url="Notebook/New Section" />
   6:     <File Path="MONotebook\Open Notebook.onetoc2"
   7: Url="Notebook/Open Notebook.onetoc2" />
   8:   </Module>
   9: </Elements>

Provided approach works both in farms and sandbox solution, so you may use it also in Sharepoint online.

Update 2015-09-22: second part of this article is available here.

No comments:

Post a Comment