How to get “order” and “level” for pages in OneNote Graph API

Quick tip: To include the order  and level  properties when getting pages with the Graph OneNote API, you need to add ?pagelevel=true  to your request. This is not mentioned in the documentation.

Posted in Development, Tips | Tagged , | Leave a comment

Moving Term Sets Accross Site Collection Maintaining Term ID:s

In SharePoint we have the Term Store for managing reusable terms. Besides tenant level term groups, which are available to all site collections, each site can have their own group. There are situations when you need to move term sets between different site collections or to/from the tenant level. If the terms you are moving are in use by site fields it is important to maintain the term IDs. Luckily, when creating new terms with code you can specify the ID. But all terms are stored in the same database, so you can’t pick an ID that is already in use somewhere else. Here is how to manage such a move.

In this example I’m moving a local term group to the tenant level using PnP PowerShell:

  • Extract the terms: Export-PnPTermGroupToXml -Out "C:\Temp\terms.xml" -Identity "Site Collection - contoso.sharepoint.com-sites-MySiteCollection"
  • Open terms.xml
    • I recommend to verify that all terms are included
    • On the second line, edit the TermGroup attributes:
      • Set SiteCollectionTermGroup  to "false"
      • Change the Name  to something more suitable
    • Do a find and replace to change SourceTermId into ID:
      • Find ID="00000000-0000-0000-0000-000000000000" and remove all instances
      • Find all SourceTermId  and replace with ID
    • Save the file
  • Now delete the original term set in SharePoint(!)
  • Apply the template to re-create the term group in the new location: Import-PnPTermGroupFromXml -Path "C:\Temp\terms.xml"

Finally, on the original site, edit all site fields that were using the affected term sets. They will have lost their connection to the original term sets, so you need to set it again, pointing on the new one!

Posted in Development, Tips | Tagged , , | Leave a comment

New-MgOauth2PermissionGrant : Missing property: clientId

I was trying to add some permissions to the (new) SPFx app. This is done using the New-MgOauth2PermissionGrant  cmdlet. All examples shows using it with -BodyParameter :

But this did not work for me. All I got back was an error

New-MgOauth2PermissionGrant : Missing property: clientId 

I got it working by passing each property by itself instead:

 

Posted in Development, Tips | Tagged , , , | Leave a comment

Azure Functions suddenly returning 403 when called from SPFx

Microsoft has recently made a change to SharePoint Framework that might break existing solutions that use an Azure backend (e.g. a Function App) that is secured to only allow requests from SPFx apps. For us, our Azure Functions suddenly started returning 403 Forbidden.

Previously you would secure your Azure apps to only allow requests from SharePoint Online Client Extensibility Web Application Principal. With the change, you need to change it to allow SharePoint Online Web Client Extensibility instead. Unlike the old app, the new app is global and has the Client ID 08e18876-6177-487e-b8b5-cf950c1e598c. Allowing this ID fixed our problems!

Posted in Development, Tips | Tagged , , , , , | Leave a comment

Securing an Azure Function with Entra ID and calling it from SPFx

This post outlines how to limit access to an Azure Functions App so that only tenant users can access it, and how to then consume this function from an SPFx app in SharePoint.

Updated: Use SharePoint Online Web Client Extensibility instead of SharePoint Online Client Extensibility Web Application Principal

Preparations

  • Your functions should use  AuthorizationLevel.Anonymous  since the underlying App Service will be taking care of authentication.
  • Enable CORS on the Function App in Azure and allow the SharePoint domain.
  • On the App Registrations blade in Azure Entra ID, find SharePoint Online Client Extensibility Web Application Principal and copy the “Application (Client) ID”.
  • On Enterprise Applications blade in Azure Entra ID, find SharePoint Online Web Client Extensibility and copy the (Client) ID (should be: 08e18876-6177-487e-b8b5-cf950c1e598c).

Secure the Functions App in Azure

  • On the Authentication blade, select Add Identity Provider.
  • Choose Microsoft.
  • Under Client application requirement, select Allow requests from specific client applications and add the Client ID for the SharePoint app you copied above.
  • Other settings can be left pretty much as is.
  • When you click Add a new Entra ID App is created.
  • Copy the App (Client) ID and App Display Name of the app.

Optional: Azure created some settings that we will not use. These can be removed to avoid confusion:

  • On the Environment settings blade in the Function App, remove the  MICROSOFT_PROVIDER_AUTHENTICATION_SECRET  entry.
  • In Entra ID, go to App Registrations and find the newly created app. On the Authentication blade, remove the “Web” platform. On the Certificates & Secrets blade, remove the “Client Secret”.

Setup SPFx Permissions

  • Open  package-solution.json  in your SPFx solution. Add the following to the  solution  section:

  • Build your solution ( gulp clean && gulp bundle --ship && gulp package-solution --ship ). A SharePoint package (.sppkg) file is created in the  SharePoint/solution  folder.
  • Open your tenant app catalog and upload and upload the package. Enable it when asked.
  • When done, click Go to API access page. Find your app and Approve it.

Note: At this point you can remove your app from the app catalog while you continue developing it locally.

Call the function

Finally, to call the function from your SPFx code, use AadHttpClient :

Learnings

The normal authentication flow is to redirect the user to a login prompt. If each SPFx web parts used this flow it would not be a good user experience. Instead, SharePoint has a special app, the “SharePoint Online Web Client Extensibility”. This app is pre-authenticated and we can use it to make secure. So when implementing authentication in our own APIs we need to allow the SharePoint app instead of our own.

Posted in Development, Tutorials | Tagged , , , , , | 2 Comments

Using emojis for easier JavaScript debugging

I normally don’t care much for emojis, but lately I’ve found them to be helpful when working with JavaScript. The JavaScript console may not be the most elegant way to debug, but it’s the quickest and sometimes only reasonable way to figure out what is happening in your code when async actions fly by. But it can quickly get quite crowded in the console. Here I found that emojis can help. Simply prefixing your logs with symbols of different color and shape is an easy way to make them stand out and give some context!

Tip: In Windows, press  Win + .  to open the Emoji insert dialog in any text input field.

 

Posted in Development, Tips | Tagged , , , | Leave a comment

Attaching a context menu to DetailsList column headers in Fluent UI

I wanted to show a menu when clicking the column headers of a DetailsList, similar to how SharePoint does it. Fluent UI 8 does not have any built in support for this, so I had to figure out a way to do it myself.

Here is a sample project showing my solution. When clicking the column headers you get a menu allowing you to sort the list based on that column. Not very existing, but it shows how to dynamically build and position the context menu, and how to handle when the user selects a menu option depending on the column.

Get the full project code on GitHub

Most of the files are just generated boilerplace code. The interesting part is App.tsx:

 

Posted in Development | Tagged , , , , | 1 Comment

Better cache management in ASP.Net Core (LazyCache/FusionCache)

LazyCache and the newer FusionCache are two great libraries that simplifies working with caching in ASP.Net Core. Something I’ve often been missing though is the ability to dispose of the entire cache, not just single items. (My understanding is that this limitation stems from them being built on top of the distributed cache in ASP.Net Core.) You could solve it by keeping track of all cached items, but that seems overkill. Let see how we can solve it in a simple way with some clever code!

Here is a typical way to use fetch an item from the database:

Notice  $"product-{id}"  which is the key by which the cache framework looks up data from the cache. Let’s add a helper class and move the key there instead:

Now we can use  CacheHelper.ProductKey  instead of the hard coded key:

Now if we want to clear the cache we can simply call CacheHelper.BustCache() ! We’re not actually clearing the cache, just changing the identifiers and thus forcing the cache to repopulate. The old cache items will go stale and eventually be flushed by the API.

Let’s improve the code a bit more. It’s usually a good idea to put logic and business rules in one place. Composing the cache key manually ( $"{CacheHelper.ProductKey}-{id}" ) and setting the duration every time we want to get a product item sounds like an accident waiting to happen, so we’ll move everything to the CacheHelper class:

Then to use it:

CacheHelper.GetProductKey(id)  makes it easy to get the correctly formatted key whenever we need it. For more advanced keys we can even add multiple parameters to the method. And when someone asks how long items are cached you can quickly look it up! This could also be a good place to add some comments on cache logic implemented elsewhere.

With just a few changes we’ve made cache management easier and more robust. Of course the code can be improved in many ways, but hopefully it gives some ideas on how to go  about!

Posted in Development, Tips | Tagged , , , , | Leave a comment

Modifying SharePoint list permissions with PnP Core

Working with SharePoint permissions is not always straight forward. This code snippet is an exercise using PnP Core to modify permissions on lists with unique (broken) permissions. All assigned permissions are changed to read only, except for owners group of the site. The code is hopefully a good starting point for similar tasks, and should be easy to adapt to list items if needed.

 

Posted in Development | Tagged , , , , | Leave a comment

Show React dialogs fluently with hooks and promises

Suppose you have a React component that displays a modal dialog and returns the result:

The typical way to use it would be to have a state variable controlling if it should be displayed or not, and a function for handling the return value. Something like this:

Nothing special, but I don’t like how the logic is located in different functions instead of a continuous flow. And what if we wanted to have multiple dialogs? We would typically repeat everything for each dialog…

What if we could open the dialog in a more fluent way, similar to JavaScript confirm:

Let’s implement a hook that wraps the dialog component in a promise!

Now we can open the dialog and handle the return value in a single flow, even with nested dialogs:

Pretty neat! :-)

Posted in Development, Tutorials | Tagged , , , , , , , , | Leave a comment