Modern Site Pages (Site Page content type) have a constraint to associate custom metadata with it. In other words, the “Site Page” content type cannot have other site columns added to it as can be seen below.

SitePageContentTypeMissing

On another note, even though we can create a child content types from Site Page content type, the New Site page creation (screenshot below) process doesn’t associate the new content type when the Page is created. So, the fields from the child content type couldn’t be associated.

For eg. In the below screenshot, we have created a new site page – test.aspx using “Intranet Site Page Content Type” which is a child of “Site Page” content type. After the page is created, it gets associated to Site Page Content type instead of Intranet Site Page Content type. We can edit it again to get it associated to Intranet Page content type but that adds another step for end users to do and added training effort.

 

 

 

Solution Approach:

To overcome the above constraints, we implemented a solution to associate custom metadata into Modern Site Pages creation using SharePoint Framework (SPFx) List View Command Set extension and Azure Function. In this blog, I am going to briefly talk about the approach so it could be useful for anyone trying to do the same.

  1. Create a List View Command Item for creating site pages, editing properties of site pages and promoting site pages to news
  2. Create an Azure function that will create the Page using SharePoint Online CSOM
  3. Call the Azure Function from the SPFx command.

A brief screenshot of the resulting SPFx extension dialog is below.

NewSitePage

Steps:

To override the process for modern page creation, we will use an Azure Function with SharePoint Online PnP core CSOM. Below is an extract of the code for the same. On a broad level, the Azure Function basically does the following

  1. Get the value of the Site Url and Page name from the Query parameters
  2. Check if the Site page is absent
  3. Create the page if absent
  4. Save the page

Note: The below code also includes the code to check if the page exists.


// parse query parameter
string siteURL = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "siteURL", true) == 0)
.Value;
string pageName = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "pageName", true) == 0)
.Value;
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Set name to query string or body data
siteURL = siteURL ?? data?.siteURL;
pageName = pageName ?? data?.pageName;
OfficeDevPnP.Core.AuthenticationManager am = new OfficeDevPnP.Core.AuthenticationManager();
using (var cc = am.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userName, secpass))
{
bool pageMissing = false;
try
{
ClientSidePage.Load(cc, pageName);
} catch(Exception ex1)
{
pageMissing = true;
}
if (pageMissing)
{
ClientSidePage newPage = new ClientSidePage(cc);
newPage.LayoutType = ClientSidePageLayoutType.Article;
newPage.Save(pageName);
log.Info("Site Page is created");
}

Next, create a SPFx extension list view command and SP dialog component that will allow us to call the Azure Function from Site Pages Library to create pages. The code uses ‘fetch api’ to call the Azure Function and pass the parameters for the Site Url and page name required for the Azure Function to create the page. After the page is created, the Azure function will respond with a success status, which can be used to confirm the page creation.

Note: Make sure that the dialog is locked while this operation is working. So, implement the code to stop closing or resubmitting the form.


var functionUrl : string = "https://%5BfunctionName%5D.azurewebsites.net/api/%5BFunctionMethod%5D?code=%5BcodeValue%5D&quot;
const requestHeaders: Headers = new Headers();
requestHeaders.append("Content-type", "application/json");
requestHeaders.append("Cache-Control", "no-cache");
const postOptions : RequestInit = {
headers: requestHeaders,
body: `{\r\n siteURL: '${siteUrl}',\r\n pageName: '${pageName}' \r\n}`,
method: "POST"
};
let responseText: string = "";
let createPageStatus: string = "";
console.log("Wait started for Creating page");
await fetch(functionUrl, postOptions).then((response) => {
console.log("Response returned");
if (response.ok) {
return response.json()
}
else
{
var errMsg = "Error detected while adding site page. Server response wasn't OK ";
console.log(errMsg);
}
}).then((responseJSON: JSON) => {
responseText = JSON.stringify(responseJSON).trim();
console.log(responseText);
if(responseText.toLowerCase().indexOf("uccess") > 0)
{
console.log("success feedback");
}
if(responseText.toLowerCase().indexOf("rror") > 0)
{
console.log("web call errored");
}
}
).catch ((response: any) => {
let errMsg: string = `WARNING – error when calling URL ${functionUrl}. Error = ${response.message}`;
console.log(errMsg);
});
console.log("wait finished");

After the pages are created, lets update the properties of the item using PnP JS library using the below code.


var id = await pnp.sp.web.lists.getByTitle("Site Pages").items.filter("FileLeafRef+eq+\'" + pageName + "\'").select("Id").get();
console.log("Calling update Page properties called for – " + id[0].Id);
if(id.length > 0)
{
await pnp.sp.web.lists.getByTitle("Site Pages").items.getById(id[0].Id).update({
Field1: value1 != "" ? value1 : null,
Field2: value2 != "" ? value2 : null,
PeopleField1: peopleValID != "" ? peopleValID : null,
PeopleMultiUserField1: multiusers.length > 0 ? {
results: multiusers
} : {results : null},
Title : title
}).then((iar: ItemUpdateResult) => {
console.log(iar);
});

Conclusion:

As we can see above, we have overridden the Page Creation process using our own Azure Function using SPFx List View command and PnP JS. I will be detailing the SP dialog for SPFx extension in another upcoming blog, so keep an eye for it.

There are still some limitations of the above approach as below. Please get business approval for the same.

  1. Cannot hide the out of the box ‘New Page’ option from within the extension.
  2. Cannot rearrange order of the Command control. It will come last in the order of the out of the box SharePoint elements.

Leave a comment