Skip to content

Instantly share code, notes, and snippets.

@kliemohn
Last active July 25, 2019 20:38
Show Gist options
  • Save kliemohn/2dad7c66b2f82c80615149377005591c to your computer and use it in GitHub Desktop.
Save kliemohn/2dad7c66b2f82c80615149377005591c to your computer and use it in GitHub Desktop.
Code to add the author / byline to a modern page (immediately below the title)
/// <summary>
/// Sets the Author Byline for a modern page
/// </summary>
/// <remarks>
/// While there is a _AuthorByline field on the page list item and an AuthorBylineId and AuthorByline properties on the ClientSidePage,
/// these currently cannot be used to set the author byline. For that we have to use LayoutWebpartsContent on the list item.
/// </remarks>
/// <param name="context"></param>
/// <param name="page"></param>
/// <param name="userName"></param>
/// <param name="pageName"></param>
public void AddModernPageByline(ClientContext context, ClientSidePage page, string userName, string pageName)
{
try
{
// Only add the author byline if we can get the actual creator - if they don't resolve, then skip this
User authorBylineUser = GetUser(context, userName);
if (authorBylineUser != null)
{
// We have the actual creator resolved in SharePoint - try to get the LayoutWebPartsContent
// Load the LayoutWebpartsContent page list item property - this is what needs to contain the author byline
context.Load(page.PageListItem, p => p["LayoutWebpartsContent"]);
context.ExecuteQuery();
object layoutWebPartsContent = page.PageListItem["LayoutWebpartsContent"];
if (layoutWebPartsContent != null)
{
// The LayoutWebPartsContent is HTML that we need to parse
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(layoutWebPartsContent.ToString());
HtmlNode node = doc.DocumentNode.SelectSingleNode("/div/div");
string controlDataAttribute = node.GetAttributeValue("data-sp-controldata", "");
if (!String.IsNullOrEmpty(controlDataAttribute))
{
// This attribute has HTML encoded JSON - lets get the JSON out
string controlDataAttributeDecoded = System.Web.HttpUtility.HtmlDecode(controlDataAttribute);
JObject controlDataJSON = JObject.Parse(controlDataAttributeDecoded);
// Now we need to update that JSON - it needs to be a JSON array containing the id, upn, name, and role for the user (we believe role can be blank)
JArray authorBylineJArray = JArray.Parse(string.Format("[{{ \"id\": \"{0}\", \"upn\": \"{1}\", \"name\": \"{2}\", \"role\": \"\" }}]",
authorBylineUser.LoginName, authorBylineUser.Email, authorBylineUser.Title));
// Set the authors property - yes, there is an authorByline property, but it appears that authors is the one to set
controlDataJSON["properties"]["authors"] = authorBylineJArray;
// Now that the JSON is in order, serialize it, Html encode it, and set it to our HTML attribute in the doc
string updatedControlDataAttributeDecoded = JsonConvert.SerializeObject(controlDataJSON, Formatting.Indented);
string updatedControlDataAttribute = System.Web.HttpUtility.HtmlEncode(updatedControlDataAttributeDecoded);
node.SetAttributeValue("data-sp-controldata", updatedControlDataAttribute);
// Now that the doc has the updated author byline, put that back into the LayoutWebpartsContent field and save
page.PageListItem["LayoutWebpartsContent"] = doc.DocumentNode.OuterHtml;
page.PageListItem.Update();
context.ExecuteQuery();
}
}
}
}
catch (Exception ex)
{
// TODO - log or throw...
string errMsg = string.Format("Unable to set Author Byline of page '{0}' to '{1}'. {2}", pageName, userName, ex.Message);
}
}
private User GetUser(ClientContext clientContext, string userName)
{
User returnValue = null;
try
{
User newUser = clientContext.Web.EnsureUser(userName);
clientContext.Load(newUser);
clientContext.ExecuteQuery();
if (newUser != null)
{
returnValue = newUser;
}
}
catch
{
// TODO - log or throw...
string errMsg = string.Format("User '{0}' not found.", userName);
}
return returnValue;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment