How to Migrate SharePoint Online Modern Client-Side Pages? | Netwoven
Blog

How to Migrate SharePoint Online Modern Client-Side Pages?

By Saikat Ojha  |  Published on November 28, 2018

How to Migrate SharePoint Online Modern Client-Side Pages?

I have been working on all possible versions of SharePoint and deployed custom solutions for many projects. However, with the introduction of Modern UI in SharePoint Online, it has become a nightmare for developers to deploy solutions and move around sites from Dev to UAT or to Production. One of the most common activities that SharePoint developers need to perform is migrating a completely developed solution from the Development site to UAT. This was always an easier job in earlier versions of SharePoint Online sites that used Classic UI, either manually or with the use of 3rd Party tools. However, in the latest version, there are certain limitations.

While attempting to migrate a site, following Features and Limitations should be taken into consideration.

Available Feature

  • Copy or move a document library content from one site to the other.

Limitations to Consider

  • Cannot copy Site Pages library contents outside the source sites “Site Pages Library”
  • SharePoint does not provide OOB feature to copy Modern Site Pages

Workaround to Copy Site Pages Library Contents in Modern UI

Well, we can achieve this using CSOM and PnPOnline with C#. A sample Code is provided below:

Step 1. Create Solution Using Visual Studio

To Create a .Net framework console application for SharePoint Modern UI

How to Migrate SharePoint Online Modern Client-Side Pages?

Step2. Add NuGet packages as below

  • SharePointOnline.CSOM
  • SharePointPnPCoreOnline

Step3. Copy the code prototype below and customize as required.

using Microsoft.SharePoint.Client;
using OfficeDevPnP.Core;
using OfficeDevPnP.Core.Pages;
using System;
using System.Collections.Generic;
using System.Web;

namespace Contoso.Migration
{
    class Program
    {
        static string strUserName = "test@test.omnline.com";
        static string strPassword = "test123";
        static void Main(string[] args)
        {
            GetPageInformation();
        }

        private static void GetPageInformation()
        {
            string strSiteUrl = "https://contoso.sharepoint.com/sites/constoso1";


            // PnP component to set context  
            AuthenticationManager authManager = new AuthenticationManager();

            try
            {
                // Get and set the client context  
                // Connects to SharePoint online site using inputs provided  
                using (var clientContext = authManager.GetSharePointOnlineAuthenticatedContextTenant(strSiteUrl, strUserName, strPassword))
                {
                    // List Name input  
                    string listName = "Site Pages";
                    // Retrieves list object using title  
                    CamlQuery query = new CamlQuery();
                    query.ViewXml = @"<View><Query>
                                        <Where>
                                            <Eq><FieldRef Name='ID' /><Value Type='Integer'>1</Value></Eq>
                                        </Where>
                                </Query></View>";
                    //<Eq><FieldRef Name='PromotedState' /><Value Type='Number'>2</Value></Eq>


                    List = clientContext.Site.RootWeb.GetListByTitle(listName);
                    clientContext.Load(list);
                    clientContext.ExecuteQuery();

                    if (list != null)
                    {
                        ListItemCollection items = list.GetItems(query);
                        clientContext.Load(items);
                        clientContext.ExecuteQuery();

                        if (items != null)
                        {
                            if (items.Count > 0)
                            {
                                foreach (ListItem item in items)
                                {
                                    Console.WriteLine(item["FileLeafRef"].ToString());
                                    ClientSidePage sourcePage = clientContext.Web.LoadClientSidePage(item["FileLeafRef"].ToString());

                                    Console.WriteLine("Creating page in destination site");
                                    CreatePageInDestination(sourcePage, item);
                                    Console.WriteLine("Page created successfully in destination site");

                                    //break;
                                }
                            }
                        }

                        
                    }
                    else
                    {
                        Console.WriteLine("List is not available on the site");
                    }
                    //Console.ReadKey();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error Message: " + ex.Message);
                //Console.ReadKey();
            }
        }

        private static void CreatePageInDestination(ClientSidePage sourcePage, ListItem sourceItem)
        {
            string strSiteUrl = "https://contoso.sharepoint.com/sites/contoso2";


            // PnP component to set context  
            AuthenticationManager authManager = new AuthenticationManager();

            try
            {
                // Get and set the client context  
                // Connects to SharePoint online site using inputs provided  
                using (var clientContext = authManager.GetSharePointOnlineAuthenticatedContextTenant(strSiteUrl, strUserName, strPassword))
                {
                    ClientSidePage newPage = clientContext.Web.AddClientSidePage("Home-Test-constoso.aspx", true); //Convert.ToString(sourceItem["FileLeafRef"]);
                                      
                    if (sourcePage.PageHeader.ImageServerRelativeUrl != null)
                    {
                        newPage.PageHeader.ImageServerRelativeUrl = sourcePage.PageHeader.ImageServerRelativeUrl.ToLower().Replace("constoso1", "constoso2");
                    }
                    newPage.PageTitle = sourcePage.PageTitle;
                    newPage.LayoutType = sourcePage.LayoutType;

                    if (sourcePage.LayoutType == ClientSidePageLayoutType.Home)
                    {
                        newPage.RemovePageHeader();
                    }

                    if (!newPage.CommentsDisabled)
                    {
                        newPage.DisableComments();
                    }
                    if (!string.IsNullOrEmpty(Convert.ToString(sourceItem["PromotedState"])))
                    {
                        if (Convert.ToInt32(sourceItem["PromotedState"]) == 2)
                        {
                            newPage.PromoteAsNewsArticle();
                        }
                    }
                    newPage.Save();


                    ListItem newPageItem = newPage.PageListItem;
                    newPageItem["CanvasContent1"] = Convert.ToString(sourceItem["CanvasContent1"]).Replace("CONSTOSO1", "CONSTOSO2").Replace("constoso1", " constoso2");
                    if (sourcePage.LayoutType != ClientSidePageLayoutType.Home)
                    {
                        newPageItem["PromotedState"] = sourceItem["PromotedState"];
                    }
                    newPageItem["PageLayoutType"] = sourceItem["PageLayoutType"];
                    newPageItem["ClientSideApplicationId"] = sourceItem["ClientSideApplicationId"];
                    newPageItem["LayoutWebpartsContent"] = sourceItem["LayoutWebpartsContent"];
                    newPageItem.Update();
                    clientContext.ExecuteQuery();

                    newPage.Publish();

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error Message: " + ex.Message);
                //Console.ReadKey();
            }
        }
    }
}

Useful Information to Customize the Above Code

Find some useful information to customize the above code for your next project.

  • News page PromotedState is 2. (That means if we want to query all news pages and can filter PromotedState = 2)
  • Normal Site Pages PromotedState = 1
  • All page information is saved as page metadata. Field name “CanvasContent1”.
  • If we want to change a normal site page to the news page, we can use PromoteAsNewsArticle()
  • If we want to turn a site page as homepage we can use PromoteAsHomePage()
  • Two types of page layouts are available:
    • ClientSidePageLayoutType.Home or we can use “Home”
    • ClientSidePageLayoutType.Article or we can use “Article”

Limitations of the Above Code

If the web part properties have single or multiline text values, then the above code can set the SPFX web part property values automatically. However, if the web part has any asynchronous data loading properties, then we may need to edit the page and set the web part properties, based on how you want to develop the custom web part.

Summing Up

If you are looking to migrate or copy an existing SharePoint site with Modern UI, the above solution can be easily adopted. You can customize it to perform a clean migration and hence overcome the generic limitations in SharePoint Modern UI based Sites. Other migration requirements are available Out of the Box in SharePoint Online using the new ‘SharePoint Move or Copy’ feature.

1 comment

Leave a comment

Your email address will not be published. Required fields are marked *

Unravel The Complex
Stay Connected

Subscribe and receive the latest insights

Netwoven Inc. - Microsoft Solutions Partner

Get involved by tagging Netwoven experiences using our official hashtag #UnravelTheComplex