SOLVED

Changing a Page Banner Image inside a Modern Page using PnP

Bronze Contributor

How can I change the picture in the banner on top of a Modern Page?

 

Using https://msdn.microsoft.com/en-us/pnp_articles/modern-experience-customizations-customize-pages I managed to create modern pages using PnP. I have also managed to add text using the ClientSideText control. But how can I change the picture in the banner on top of the page? I have found examples how to add a separate Image control, but I haven't found a solution how to change the banner on top of a page (where also the title is displayed).

Any examples how to do that?

12 Replies

Hi,

 

When you go to the edit mode of the page; You will find  "Add Image" icon on the left, just click on it and add the image from your site or upload it. 

 

Check the attached screenshot. 

 

Thanks for your reply @Suhail Jamaldeen. Sadly it doesn't anwer my question.

The method you described, is what I use when I manually update Modern Page using the SharePoint User Interface. But I'm looking for a way to change a Page Banner using PnP, so using a programmatically / scripted way to realize this.

 

Any ideas?

Do you know what size image it supports or reccomended size? Some images dont show but it appears smaller do. 

Any idea how to realize this programmatically / scripted @Vesa Juvonen, @Bert Jansen, @Erwin van Hunen?  

Here is a code in JavaScript (JSOM).

First, let's say you've added some picture that should be used on the page. For example, to Site Assets.

 

Then, you can run next code:

 

var ctx = SP.ClientContext.get_current(); // current context
var web = ctx.get_web(); // current web
var list = web.get_lists().getByTitle('Site Pages'); // getting Site Pages library
var items = list.getItems(SP.CamlQuery.createAllItemsQuery()); // all pages
ctx.load(items); // loading items
ctx.executeQueryAsync(function() {
    var item = items.get_item(6); // in my case I'll work with some page under index 6
    //
    // LayoutWebpartsContent field contains settings for the page layout
    //
    var layoutWebpartsContent = item.get_item('LayoutWebpartsContent');
    console.log(layoutWebpartsContent); // let's look at it
    
    // providing image source
    layoutWebpartsContent = layoutWebpartsContent.replace(/%22imageSources%22:%7B[^%7D]*%7D/gmi, '%22imageSources%22:%7B%22imageSource%22:%22server-relative-url-to-img-goes-here%22%7D');
    // changing imageSourceType from 4 to 4 if needed
    layoutWebpartsContent = layoutWebpartsContent.replace(/%22imageSourceType%22:4/gmi, '%22imageSourceType%22:2');
    
    // saving changes
    item.set_item('LayoutWebpartsContent', layoutWebpartsContent)
    item.update();
    ctx.executeQueryAsync();
});

So, basically

 

  • you can get a page as a list item
  • change LayoutWebpartsContent property
  • update list item

In LayoutWebpartsContent first thing to change is imageSource. It may be absent (for new created page) or contain server relative url to current page.

The second thing to change is imageSourceType. For new created page without image it's equal to 4 and we need to change it to 2.

 

Of course, it's more like a hack. But it works for your scenario.

Thank you for the detailed hack / work around Aleksei!
best response confirmed by VI_Migration (Silver Contributor)
Solution

My initial Regular Expression works only when banner is not set. So it will not work for the pages with banner.

Below is the code to process any pages:

var ctx = SP.ClientContext.get_current(); // SP context
var web = ctx.get_web(); // current web
var list = web.get_lists().getByTitle('Site Pages'); // pages library
var items = list.getItems(SP.CamlQuery.createAllItemsQuery()); // all items
ctx.load(items); // loading items from server...
ctx.executeQueryAsync(function() {
    var item = items.get_item(7); // here I'm working with single item by index. But you can iterate through all pages here
    var layoutWebpartsContent = item.get_item('LayoutWebpartsContent'); // getting content
    console.log(layoutWebpartsContent); // let's display the content
    
    var dataAttrContent = /data-sp-controldata="([^"]+)"/gmi.exec(layoutWebpartsContent); // getting data-sp-controldata content
    
    if (dataAttrContent.length) { 
        // we found the attribute.
        // Let's unescape and parse it to JSON
        // the content of the attribute is a 'group' in RegExp result. It will be located as second entry (with index 1)
        var unescaped = unescape(dataAttrContent[1]) // unescape
        //unescaped = unescape(unescaped);
        var content = JSON.parse(unescaped.replace(/:/gmi, ':')); // replace : with :

        //
        // changing imageSource
        //
        if (!content.serverProcessedContent) {
            content.serverProcessedContent = {};
        }
        if (!content.serverProcessedContent.imageSources) {
            content.serverProcessedContent.imageSources = {};
        }
        content.serverProcessedContent.imageSources.imageSource = '/sites/documents-aggregator/SiteAssets/SitePages/1200px-MarvelLogo.svg.png';

        //
        // Changing imageSourceType
        //
        if (!content.properties) {
            content.properties = {};
        }
        content.properties.imageSourceType = 2;

        //
        // escaping back and updating item
        //
        debugger;
        var newContent = JSON.stringify(content);
        newContent = escape(newContent); // escaping
        newContent = newContent.replace(/%3A/gmi, ':').replace(/%2C/gmi, ','); // we need to replace %3A (:) with : and %2C (,) with ,
        layoutWebpartsContent = layoutWebpartsContent.replace(dataAttrContent[1], newContent);
        item.set_item('LayoutWebpartsContent', layoutWebpartsContent);
        item.update();
        ctx.executeQueryAsync(function() {
            console.log('success');
        }, function() {
            console.log('fail');
        });
    }
});
Not got my code handy but I believe you can set the bannerimageurl property on the page.

Support for changing the Page Banner Image url of a Modern Page using PnP is on the todo list, as mentioned on GitHub: https://github.com/SharePoint/PnP-Sites-Core/issues/1579

Re-opening this - why when I create a new page using 

 ClientSidePage page = new ClientSidePage(ctx);

 

is the LayoutWebpartsContent empty?

 

I've waited for a number of minutes, tried publishing the page, tried getting the attribute in pnp PowerShell and it's still empty!

 

 

1 best response

Accepted Solutions
best response confirmed by VI_Migration (Silver Contributor)
Solution

My initial Regular Expression works only when banner is not set. So it will not work for the pages with banner.

Below is the code to process any pages:

var ctx = SP.ClientContext.get_current(); // SP context
var web = ctx.get_web(); // current web
var list = web.get_lists().getByTitle('Site Pages'); // pages library
var items = list.getItems(SP.CamlQuery.createAllItemsQuery()); // all items
ctx.load(items); // loading items from server...
ctx.executeQueryAsync(function() {
    var item = items.get_item(7); // here I'm working with single item by index. But you can iterate through all pages here
    var layoutWebpartsContent = item.get_item('LayoutWebpartsContent'); // getting content
    console.log(layoutWebpartsContent); // let's display the content
    
    var dataAttrContent = /data-sp-controldata="([^"]+)"/gmi.exec(layoutWebpartsContent); // getting data-sp-controldata content
    
    if (dataAttrContent.length) { 
        // we found the attribute.
        // Let's unescape and parse it to JSON
        // the content of the attribute is a 'group' in RegExp result. It will be located as second entry (with index 1)
        var unescaped = unescape(dataAttrContent[1]) // unescape
        //unescaped = unescape(unescaped);
        var content = JSON.parse(unescaped.replace(/:/gmi, ':')); // replace : with :

        //
        // changing imageSource
        //
        if (!content.serverProcessedContent) {
            content.serverProcessedContent = {};
        }
        if (!content.serverProcessedContent.imageSources) {
            content.serverProcessedContent.imageSources = {};
        }
        content.serverProcessedContent.imageSources.imageSource = '/sites/documents-aggregator/SiteAssets/SitePages/1200px-MarvelLogo.svg.png';

        //
        // Changing imageSourceType
        //
        if (!content.properties) {
            content.properties = {};
        }
        content.properties.imageSourceType = 2;

        //
        // escaping back and updating item
        //
        debugger;
        var newContent = JSON.stringify(content);
        newContent = escape(newContent); // escaping
        newContent = newContent.replace(/%3A/gmi, ':').replace(/%2C/gmi, ','); // we need to replace %3A (:) with : and %2C (,) with ,
        layoutWebpartsContent = layoutWebpartsContent.replace(dataAttrContent[1], newContent);
        item.set_item('LayoutWebpartsContent', layoutWebpartsContent);
        item.update();
        ctx.executeQueryAsync(function() {
            console.log('success');
        }, function() {
            console.log('fail');
        });
    }
});

View solution in original post