Cloud development - All about M365, .NET, SharePoint and more…
About Me
Contact Me
  • About Me
  • Contact Me
Cloud development - All about M365, .NET, SharePoint and more…
Development, Latest

Building SharePoint Online Remote Event Receivers for Document Review Flows

SharePoint Online has provided a myriad of collaboration and productivity tools for businesses. Among these, Remote Event Receivers and Microsoft Flow (now known as Power Automate) stand out as potent tools for automation and integration.

In this blog post, we will demonstrate how to use SharePoint Online Remote Event Receivers in combination with Power Automate to trigger a review Flow whenever a document’s status is set to “Pending” in a Document Library.

Understanding the Concepts

SharePoint Online Remote Event Receivers

Remote Event Receivers are components that handle events such as additions, updates, and deletions in SharePoint Online. Unlike their counterparts in SharePoint Server, they handle events remotely, meaning they respond to events in SharePoint Online from an external web service.

Power Automate (Flow)

Power Automate is a service offered by Microsoft that allows creation of automated workflows between apps and services to synchronise files, get notifications, collect data, and more.

Getting Started

The process is fairly straightforward and consists of the following steps:

  1. Creating an Azure Function
  2. Creating a Remote Event Receiver
  3. Creating a Power Automate Flow
  4. Attaching the Event Receiver to the Document Library

Please Note: The solution described in this post assumes you have access to and basic knowledge of Azure Functions, SharePoint Online, and Power Automate.

Step 1: Creating an Azure Function

Azure Functions is a serverless solution that allows you to write less code, maintain less infrastructure, and save costs. We’ll use it to host our Remote Event Receiver.

  1. Log into the Azure Portal.
  2. Create a new Function App.
  3. Once the Function App is created, create a new function within it. Use the HTTP trigger template, name your function and choose the Authorisation level (Function level should suffice).

Here is a basic example of how to structure your function:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    string content = await req.Content.ReadAsStringAsync();
    log.Info(content);
    // Add your code to trigger the Power Automate flow here
    return req.CreateResponse(HttpStatusCode.OK);
}

This function merely logs the incoming request content and returns an OK response. You will add the logic to trigger the Power Automate flow later.

Step 2: Creating a Remote Event Receiver

In SharePoint, go to the list settings for the Document Library you want to use. Under “General Settings”, click on “Advanced settings”, and allow the management of content types by choosing “Yes”.

Now, add a new column to the Document content type. Name it “Status” and make it a Choice type field with “Pending” as one of the choices.

Once done, we will add our Remote Event Receiver. SharePoint does not provide an out-of-the-box interface to add Remote Event Receivers to lists or content types, so you will have to use the Client Side Object Model (CSOM) to attach the Event Receiver to the list.

Here is an example PowerShell script using CSOM to attach the Event Receiver to your Document Library:

# Input parameters
$siteUrl = "https://your-domain.sharepoint.com/sites/your-site"
$listTitle = "Documents"
$username = "your-username@your-domain.com"
$password = "your-password"
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $securePassword
$addInOnly = $False
# Connect to the site
Connect-PnPOnline -Url $siteUrl -Credentials $credentials -CreateDrive
# Define the event receiver properties
$eventReceiverName = "ItemUpdated"
$eventReceiverUrl = "https://your-azure-function-url"
$eventReceiverType = [Microsoft.SharePoint.Client.EventReceiverType]::ItemUpdated
$synchronization = [Microsoft.SharePoint.Client.EventReceiverSynchronization]::Asynchronous
# Add the event receiver
Add-PnPEventReceiver -List $listTitle -Name $eventReceiverName -Url $eventReceiverUrl -EventReceiverType $eventReceiverType -Synchronization $synchronization

This script adds an ItemUpdated event receiver to your Document Library. This event receiver will trigger your Azure Function whenever an item in the Document Library is updated.

Step 3: Creating a Power Automate Flow

Next, we’ll create a Power Automate flow that will be triggered by the Azure Function whenever a document status is set to “Pending”.

  1. Go to Power Automate.
  2. Create a new Flow. Use the HTTP request trigger.
  3. Add a new step to send an email (or any other action you want the Flow to take).
  4. Save your Flow. You will be provided with an HTTP POST URL.

Step 4: Finalizing the Azure Function

Finally, we will modify the Azure Function to trigger the Power Automate flow whenever a document’s status is set to “Pending”.

  1. In the Azure Function, add logic to parse the incoming request content to get the item data.
  2. Check if the item’s status is set to “Pending”.
  3. If it is, send a POST request to the Power Automate flow’s URL to trigger the flow.

Here is the modified Azure Function:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    string content = await req.Content.ReadAsStringAsync();
    log.Info(content);
    // Parse the content as XML
    var doc = new XmlDocument();
    doc.LoadXml(content);
    var mgr = new XmlNamespaceManager(doc.NameTable);
    mgr.AddNamespace("s", "http://schemas.microsoft.com/sharepoint/soap/");
    mgr.AddNamespace("z", "#RowsetSchema");
    // Get the item data
    var itemDataNode = doc.SelectSingleNode("//s:Changes/s:Object/s:Properties/s:SharedFields", mgr);
    var itemData = itemDataNode.ChildNodes.Cast<XmlNode>().ToDictionary(n => n.LocalName, n => n.InnerText);
    // Check if the item's status is set to "Pending"
    if (itemData.ContainsKey("Status") && itemData["Status"] == "Pending")
    {
        // Trigger the Power Automate flow
        var flowUrl = "https://your-power-automate-flow-url";
        await new HttpClient().PostAsync(flowUrl, new StringContent(""));
    }
    return req.CreateResponse(HttpStatusCode.OK);
}

That’s it! You have now successfully set up a Remote Event Receiver in SharePoint Online that triggers a Power Automate flow whenever a document’s status is set to “Pending”.

This post provided a step-by-step guide on setting up this process, however, please note that this is a simplistic example and a real-world scenario would potentially be more complex.

June 4, 2023by Luke
Development

Working with SharePoint Online REST Search API 101

In SharePoint Online, the REST Search API is a powerful tool to query against SharePoint’s search index. It provides a comprehensive way to interact with SharePoint’s search capabilities programmatically. This blog post will guide you on how to use the SharePoint Online REST Search API to find, open, and view documents based on their metadata fields using the Graph API.

We’ll proceed in the following steps:

  1. Introduction to SharePoint Online REST Search API
  2. Authenticating with Microsoft Graph API
  3. Searching for Documents Based on Metadata
  4. Opening and Viewing Documents

You can use a tool like Postman to send these requests. The basics of sending POST or GET requests via REST API’s are not covered here, it’s expected you know how to set headers etc. as well.

Step 1: Introduction to SharePoint Online REST Search API

SharePoint Online REST Search API allows you to submit search queries and get search results programmatically. This API is useful for retrieving SharePoint data based on specific conditions or filters.

The primary endpoint of the REST Search API is /_api/search/query. This endpoint accepts various parameters to refine the search query, and returns results in JSON format.

Step 2: Authenticating with Microsoft Graph API

To interact with the REST Search API, you must first authenticate your application with Microsoft Graph API. You can do this by registering your application in Azure Active Directory (Azure AD) and obtaining an access token.

Here’s a brief overview of the steps involved:

  1. Register an application in the Azure portal.
  2. After registration, note the Application (client) ID and Directory (tenant) ID.
  3. Generate a new client secret under Certificates & secrets.
  4. Grant API permissions for SharePoint under API permissions.
  5. Get an access token. Send a POST request to the following URL:
https://login.microsoftonline.com/&lt;tenant-id>/oauth2/v2.0/token

Replace <tenant-id> with your Azure AD tenant ID.

The request body should include:

  • client_id: your application’s Client ID
  • scope: “https://graph.microsoft.com/.default“
  • client_secret: your application’s Client Secret
  • grant_type: “client_credentials”

The response will include an access_token. Use this token to authenticate your requests to the SharePoint REST Search API. An example in PowerShell which you could easily replicate in Postman for getting a auth token:

# Define the tenant ID, client ID and secret
$tenantId = "<tenant-id>"
$clientId = "<client-id>"
$clientSecret = "<client-secret>"
# Define the resource and token endpoint
$resource = "https://graph.microsoft.com"
$tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Prepare the request body
$body = @{
    client_id     = $clientId
    scope         = "https://graph.microsoft.com/.default"
    client_secret = $clientSecret
    grant_type    = "client_credentials"
}
# Request the token
$response = Invoke-RestMethod -Uri $tokenEndpoint -Method Post -Body $body
# Output the access token
Write-Output "Access Token: $($response.access_token)"

Step 3: Searching for Documents Based on Metadata

You can now send authenticated GET requests to the REST Search API endpoint. The search query string goes in the querytext parameter, and you can use other parameters to refine the search.

To find documents authored by a specific user, send a GET request to:

https://&lt;site-url>/_api/search/query?querytext='Author:&lt;author-name>+AND+IsDocument:True'

Replace <site-url> with your SharePoint site URL and <author-name> with the actual author’s name.

Include the access_token in the Authorization header:

GET /_api/search/query?querytext='Author:&lt;author-name>+AND+IsDocument:True' HTTP/1.1
Host: &lt;site-url>
Authorization: Bearer &lt;access_token>
Accept: application/json;odata=nometadata

A script that could use the token generated above would look something like:

# Define the SharePoint Online REST Search API endpoint and query
$siteUrl = "<site-url>"
$searchEndpoint = "https://$siteUrl/_api/search/query?querytext='IsDocument:True+AND+Author:John'"
# Prepare the request headers with the access token
$headers = @{
    Authorization = "Bearer $accessToken"
    Accept = "application/json;odata=nometadata"
}
# Send the authenticated GET request to the SharePoint Online REST Search API
$searchResponse = Invoke-RestMethod -Uri $searchEndpoint -Method Get -Headers $headers
# Output the search results
Write-Output $searchResponse

Step 4: Opening and Viewing Documents

The search results contain various properties for each document, including the document’s URL in the Path field. To open a document, copy the Path value and paste it into a web browser.

You’ve now learned how to use the SharePoint Online REST Search API to find, open, and view documents based on their metadata fields using the Graph API. By mastering these steps, you can efficiently interact with SharePoint Online programmatically. Happy coding!

June 4, 2023by Luke
Development, Latest, SharePoint

Using the SPO Search REST API to create a Webpart showing Recently Modified Documents containing specific keywords

SharePoint Online provides a powerful Search API that allows developers to query against SharePoint’s search index. In this blog post, we will create a custom web part that utilizes the Search API to retrieve and display the most recently modified documents matching specific keywords.

Our tool of choice for developing custom SharePoint Online web parts is the SharePoint Framework (SPFx). We will write our web part in TypeScript and use ReactJS for the front-end. To follow along, ensure you have administrative access to your SharePoint Online and Office 365 environment.

Here’s our roadmap:

  1. Setup the Development Environment
  2. Create a New SharePoint Framework Solution
  3. Develop the Web Part
  4. Deploy and Test the Web Part

Step 1: Setup the Development Environment

Firstly, we need to setup our development environment. This involves installing Node.js, Yeoman SharePoint generator, and Gulp. You can install these dependencies with the following commands:

npm install -g yo gulp
npm install -g @microsoft/generator-sharepoint

Step 2: Create a New SharePoint Framework Solution

After setting up the environment, navigate to the folder where you want to create the solution and run the following command:

yo @microsoft/sharepoint

When prompted:

  • For the solution name, enter “DocumentSearchWebPart”
  • Choose “Use the current folder” for where to place the files
  • Choose “Y” to allow tenant admin to deploy the solution to all sites immediately
  • Choose “N” for unique permissions to access web APIs
  • Choose “WebPart” as the component type
  • For the web part name, enter “DocumentSearch”
  • For the description, enter “A web part that displays recently modified documents based on search keywords”
  • For the framework, choose “React”

Step 3: Develop the Web Part

After creating the solution, navigate to the src/webparts/documentSearch/components/DocumentSearch.tsx file. This is where we’ll write the code to fetch and display documents from the SharePoint Search API.

import * as React from 'react';
import { IDocumentSearchProps } from './IDocumentSearchProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
export interface IDocumentSearchState {
  results: any[];
}
export default class DocumentSearch extends React.Component<IDocumentSearchProps, IDocumentSearchState> {
  constructor(props: IDocumentSearchProps) {
    super(props);
    this.state = {
      results: []
    };
  }
  public componentDidMount() {
    const keywords = 'your-keywords'; // replace this with your keywords
    this.props.context.spHttpClient.get(`${this.props.context.pageContext.web.absoluteUrl}/_api/search/query?querytext='FileType:docx+AND+${escape(keywords)}'&sortlist='LastModifiedTime:descending'&rowlimit=10`, SPHttpClient.configurations.v1)
      .then((response: SPHttpClientResponse) => {
        return response.json();
      })
      .then((json) => {
        this.setState({ results: json.PrimaryQueryResult.RelevantResults.Table.Rows });
      });
  }
  public render(): React.ReactElement<IDocumentSearchProps> {
    return (
      <div>
        {this.state.results.map((result, index) => {
          const cells = result.Cells.map((cell) => {
            return cell.Value;
          });
          return (
            <div key={index}>
              <h3>{cells.Title}</h3>
              <p>Last Modified: {new Date(cells.LastModifiedTime).toLocaleString()}</p>
              <a href={cells.Path}>Open Document</a>
            </div>
          );
        })}
      </div>
    );
  }
}

Here, we’re making a GET request to the SharePoint Search API, passing our query text and sort order as parameters. The query text includes the file type (docx in this case) and our search keywords, and we’re sorting the results by Last Modified Time in descending order, which will give us the most recently modified documents first. We’re limiting the results to the top 10.

Step 4: Deploy and Test the Web Part

Finally, we will deploy and test our web part.

To bundle the web part, run the following command:

gulp bundle --ship

And to package the solution:

gulp package-solution --ship

These commands create a .sppkg package in the sharepoint/solution folder. Upload this package to the SharePoint App Catalog. You can now add the web part to a page by editing the page and clicking the ‘+’ button to add a new web part.

And there you have it, a custom SharePoint Online web part that utilises the SharePoint Search API to display the most recently modified documents that match specific keywords. You can further customise this web part as needed. Happy coding!

June 4, 2023by Luke
Development, SharePoint

Creating a Custom SharePoint Online Webpart to Display Contact Details with Photos

One of the powerful capabilities of SharePoint Online is its ability to create and utilize custom web parts. These web parts can help to extend the platform’s functionalities to meet specific organizational needs. In this tutorial, we will walk you through the process of creating a custom web part that displays user contact details along with their photos.

Our web part will be created using the SharePoint Framework (SPFx), which is a page and web part model for SharePoint Online. We’ll use TypeScript for the back-end, and ReactJS for the front-end. The entire process involves the following steps:

  1. Setting up the Development Environment
  2. Creating a New SharePoint Framework Solution
  3. Developing the Web Part
  4. Deploying and Testing the Web Part

Please make sure you have administrative access to the SharePoint Online and Office 365 environment. Let’s get started.

Step 1: Setting up the Development Environment

To set up your SharePoint Framework development environment, you will need Node.js, Yeoman, and the SharePoint client-side solution generator, and Gulp. Open a command prompt and run the following commands to install these dependencies:

npm install -g yo gulp
npm install -g @microsoft/generator-sharepoint

Step 2: Creating a New SharePoint Framework Solution

Now, let’s create a new SharePoint Framework solution.

Navigate to the folder where you want to create the solution, and run the following command:

yo @microsoft/sharepoint

The Yeoman SharePoint generator will start prompting you for the following information:

  • What is your solution name?
  • Where do you want to place the files?
  • Do you want to allow the tenant admin the choice of being able to deploy the solution to all sites immediately without running any feature deployment or adding apps in sites?
  • Will the components in the solution require permissions to access web APIs that are unique and not shared with other components in the tenant?
  • Which type of client-side component to create?

For this example, choose the following:

  • Solution Name: UserContactWebPart
  • Baseline packages: Use the current folder
  • Deployment option: Select Y (Yes)
  • Permissions: N (No)
  • Component Type: WebPart
  • Web part name: UserContact
  • Description: A web part that displays user contact details
  • Framework: React

Step 3: Developing the Web Part

Navigate into the solution folder, and open the file src/webparts/userContact/components/UserContact.tsx

This file represents the frontend of your webpart.

You need to install the Microsoft Graph client library. To do this, run the following command:

npm install @microsoft/microsoft-graph-client

Now, let’s edit our UserContact.tsx file:

 --
import * as React from 'react';
import styles from './UserContact.module.scss';
import { IUserContactProps } from './IUserContactProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { MSGraphClient } from '@microsoft/sp-http';
export interface IUserContactState {
  users: any[];
}
export default class UserContact extends React.Component<IUserContactProps, IUserContactState> {
  constructor(props: IUserContactProps) {
    super(props);
    this.state = {
      users: []
    };
  }
  public componentDidMount() {
    this.props.context.msGraphClientFactory
      .getClient()
      .then((client: MSGraphClient): void => {
        // get users and their photos from Microsoft Graph
        client
          .api('/users')
          .version('v1.0')
          .get((err, res) => {
            if (err) {
              console.error(err);
              return;
            }
            this.setState({ users: res.value });
          });
      });
  }
  public render(): React.ReactElement<IUserContactProps> {
    return (
      <div className={styles.userContact}>
        {this.state.users.map((user, key) => {
          return (
            <div key={key}>
              <h2>{user.displayName}</h2>
              <p>{user.mail}</p>
              <img src={`/_layouts/15/userphoto.aspx?size=L&username=${user.mail}`} alt="User photo" />
            </div>
          );
        })}
      </div>
    );
  }
}

Step 4: Deploying and Testing the Web Part

Now that our web part is developed, let’s deploy and test it.

To build and bundle your web part, run the following command in your command prompt:

gulp bundle --ship

And to package the solution, use the following command:

gulp package-solution --ship

These commands create a .sppkg package in the sharepoint/solution folder. You can upload this package to the SharePoint App Catalog in your SharePoint Online site. After uploading, you can add your web part to a page by editing the page and clicking the ‘+’ button to add a new web part. Note you’ll need to have specific permissions assigned to query Graph API.

And there you have it, a custom SharePoint Online web part that displays user contact details along with their photos. You can customize this web part further as per your needs. Happy coding!

June 3, 2023by Luke

Recent Posts

  • Mastering SharePoint Online REST APIs A Step-by-Step Guide
  • Building SharePoint Online Remote Event Receivers for Document Review Flows
  • Working with SharePoint Online REST Search API 101
  • Using the SPO Search REST API to create a Webpart showing Recently Modified Documents containing specific keywords
  • Creating a Custom SharePoint Online Webpart to Display Contact Details with Photos

Categories

  • Development
  • Latest
  • Patching
  • SharePoint
  • Testing
  • Tools

“I believe in doing what you enjoy, what drives you, what keeps you coming back for more. Code is life! Well it at least it pays the bills and lets me blog about it in my spare time!”

© 2019 copyright lukeosborne.dev // All rights reserved // Privacy Policy