Esempio n. 1
0
        public void CrossSiteDelveTransformTest()
        {
            using (var targetClientContext = TestCommon.CreateClientContext(TestCommon.AppSetting("SPOTargetSiteUrl")))
            {
                using (var sourceClientContext = TestCommon.CreateClientContext("https://bertonline.sharepoint.com/portals/personal/bertjansen"))
                {
                    var pageTransformator = new DelvePageTransformator(sourceClientContext, targetClientContext);
                    pageTransformator.RegisterObserver(new UnitTestLogObserver());

                    var pages = sourceClientContext.Web.GetBlogsFromList("Pages", "Delve");

                    foreach (var page in pages)
                    {
                        DelvePageTransformationInformation pti = new DelvePageTransformationInformation(page)
                        {
                            // If target page exists, then overwrite it
                            Overwrite = true,

                            // Don't log test runs
                            SkipTelemetry = true,

                            KeepPageCreationModificationInformation = true,

                            SetAuthorInPageHeader = true,

                            PostAsNews = true,

                            PublishCreatedPage = true,

                            KeepSubTitle = true,

                            //TargetPageFolder = "Blogs",

                            //SkipUserMapping = true,

                            //AddTableListImageAsImageWebPart = true,

                            // Configure the page header, empty value means ClientSidePageHeaderType.None
                            //PageHeader = new ClientSidePageHeader(cc, ClientSidePageHeaderType.None, null),

                            // Replace embedded images and iframes with a placeholder and add respective images and video web parts at the bottom of the page
                            // HandleWikiImagesAndVideos = false,

                            // Callout to your custom code to allow for title overriding
                            //PageTitleOverride = titleOverride,

                            // Callout to your custom layout handler
                            //LayoutTransformatorOverride = layoutOverride,

                            // Callout to your custom content transformator...in case you fully want replace the model
                            //ContentTransformatorOverride = contentOverride,
                        };

                        pti.MappingProperties["SummaryLinksToQuickLinks"] = "true";
                        pti.MappingProperties["UseCommunityScriptEditor"] = "true";

                        var result = pageTransformator.Transform(pti);
                    }
                }
            }

            //Assert.Inconclusive(TestCommon.InconclusiveNoAutomatedChecksMessage);
        }
Esempio n. 2
0
        protected override void ExecuteCmdlet()
        {
            //Fix loading of modernization framework
            FixLocalAssemblyResolving();

            // Load the page to transform
            Identity.Library       = this.Library;
            Identity.Folder        = this.Folder;
            Identity.BlogPage      = this.BlogPage;
            Identity.DelveBlogPage = this.DelveBlogPage;

            if ((this.PublishingPage && this.BlogPage) ||
                (this.PublishingPage && this.DelveBlogPage) ||
                (this.BlogPage && this.DelveBlogPage))
            {
                throw new Exception($"The page is either a blog page, a publishing page or a Delve blog page. Setting PublishingPage, BlogPage and DelveBlogPage to true is not valid.");
            }

            ListItem page = null;

            if (this.PublishingPage)
            {
                page = Identity.GetPage(this.ClientContext.Web, CacheManager.Instance.GetPublishingPagesLibraryName(this.ClientContext));
            }
            else if (this.BlogPage)
            {
                // Blogs don't live in other libraries or sub folders
                Identity.Library = null;
                Identity.Folder  = null;
                page             = Identity.GetPage(this.ClientContext.Web, CacheManager.Instance.GetBlogListName(this.ClientContext));
            }
            else if (this.DelveBlogPage)
            {
                // Blogs don't live in other libraries or sub folders
                Identity.Library = null;
                Identity.Folder  = null;
                page             = Identity.GetPage(this.ClientContext.Web, "pPg");
            }
            else
            {
                if (this.Folder == null || !this.Folder.Equals(rootFolder, StringComparison.InvariantCultureIgnoreCase))
                {
                    page = Identity.GetPage(this.ClientContext.Web, "sitepages");
                }
            }

            if (page == null && (Folder == null || !this.Folder.Equals(rootFolder, StringComparison.InvariantCultureIgnoreCase)))
            {
                throw new Exception($"Page '{Identity?.Name}' does not exist");
            }

            // Publishing specific validation
            if (this.PublishingPage && string.IsNullOrEmpty(this.TargetWebUrl) && TargetConnection == null)
            {
                throw new Exception($"Publishing page transformation is only supported when transformating into another site collection. Use the -TargetWebUrl to specify a modern target site.");
            }

            // Blog specific validation
            if ((this.BlogPage || this.DelveBlogPage) && string.IsNullOrEmpty(this.TargetWebUrl) && TargetConnection == null)
            {
                throw new Exception($"Blog and Delve blog page transformation is only supported when transformating into another site collection. Use the -TargetWebUrl to specify a modern target site.");
            }

            // Load transformation models
            PageTransformation webPartMappingModel = null;

            if (string.IsNullOrEmpty(this.WebPartMappingFile))
            {
                webPartMappingModel = PageTransformator.LoadDefaultWebPartMapping();
                this.WriteVerbose("Using embedded webpartmapping file. Use Export-PnPClientSidePageMapping to get that file in case you want to base your version of the embedded version.");
            }

            // Validate webpartmappingfile
            if (!string.IsNullOrEmpty(this.WebPartMappingFile))
            {
                if (!System.IO.File.Exists(this.WebPartMappingFile))
                {
                    throw new Exception($"Provided webpartmapping file {this.WebPartMappingFile} does not exist");
                }
            }

            if (this.PublishingPage && !string.IsNullOrEmpty(this.PageLayoutMapping) && !System.IO.File.Exists(this.PageLayoutMapping))
            {
                throw new Exception($"Provided pagelayout mapping file {this.PageLayoutMapping} does not exist");
            }

            bool crossSiteTransformation = TargetConnection != null || !string.IsNullOrEmpty(TargetWebUrl);

            // Create target client context (when needed)
            ClientContext targetContext = null;

            if (TargetConnection == null)
            {
                if (!string.IsNullOrEmpty(TargetWebUrl))
                {
                    targetContext = this.ClientContext.Clone(TargetWebUrl);
                }
            }
            else
            {
                targetContext = TargetConnection.Context;
            }


            // Create transformator instance
            PageTransformator           pageTransformator           = null;
            PublishingPageTransformator publishingPageTransformator = null;
            DelvePageTransformator      delvePageTransformator      = null;

            if (!string.IsNullOrEmpty(this.WebPartMappingFile))
            {
                // Using custom web part mapping file
                if (this.PublishingPage)
                {
                    if (!string.IsNullOrEmpty(this.PageLayoutMapping))
                    {
                        // Using custom page layout mapping file + default one (they're merged together)
                        publishingPageTransformator = new PublishingPageTransformator(this.ClientContext, targetContext, this.WebPartMappingFile, this.PageLayoutMapping);
                    }
                    else
                    {
                        // Using default page layout mapping file
                        publishingPageTransformator = new PublishingPageTransformator(this.ClientContext, targetContext, this.WebPartMappingFile, null);
                    }
                }
                else if (this.DelveBlogPage)
                {
                    delvePageTransformator = new DelvePageTransformator(this.ClientContext, targetContext, this.WebPartMappingFile);
                }
                else
                {
                    // Use web part mapping file
                    pageTransformator = new PageTransformator(this.ClientContext, targetContext, this.WebPartMappingFile);
                }
            }
            else
            {
                // Using default web part mapping file
                if (this.PublishingPage)
                {
                    if (!string.IsNullOrEmpty(this.PageLayoutMapping))
                    {
                        // Load and validate the custom mapping file
                        PageLayoutManager pageLayoutManager = new PageLayoutManager();
                        var pageLayoutMappingModel          = pageLayoutManager.LoadPageLayoutMappingFile(this.PageLayoutMapping);

                        // Using custom page layout mapping file + default one (they're merged together)
                        publishingPageTransformator = new PublishingPageTransformator(this.ClientContext, targetContext, webPartMappingModel, pageLayoutMappingModel);
                    }
                    else
                    {
                        // Using default page layout mapping file
                        publishingPageTransformator = new PublishingPageTransformator(this.ClientContext, targetContext, webPartMappingModel, null);
                    }
                }
                else if (this.DelveBlogPage)
                {
                    delvePageTransformator = new DelvePageTransformator(this.ClientContext, targetContext, webPartMappingModel);
                }
                else
                {
                    // Use web part mapping model loaded from embedded mapping file
                    pageTransformator = new PageTransformator(this.ClientContext, targetContext, webPartMappingModel);
                }
            }

            // Setup logging
            if (this.LogType == ClientSidePageTransformatorLogType.File)
            {
                if (this.PublishingPage)
                {
                    publishingPageTransformator.RegisterObserver(new MarkdownObserver(folder: this.LogFolder, includeVerbose: this.LogVerbose, includeDebugEntries: this.LogVerbose));
                }
                else if (this.DelveBlogPage)
                {
                    delvePageTransformator.RegisterObserver(new MarkdownObserver(folder: this.LogFolder, includeVerbose: this.LogVerbose, includeDebugEntries: this.LogVerbose));
                }
                else
                {
                    pageTransformator.RegisterObserver(new MarkdownObserver(folder: this.LogFolder, includeVerbose: this.LogVerbose, includeDebugEntries: this.LogVerbose));
                }
            }
            else if (this.LogType == ClientSidePageTransformatorLogType.SharePoint)
            {
                if (this.PublishingPage)
                {
                    publishingPageTransformator.RegisterObserver(new MarkdownToSharePointObserver(targetContext ?? this.ClientContext, includeVerbose: this.LogVerbose, includeDebugEntries: this.LogVerbose));
                }
                else if (this.DelveBlogPage)
                {
                    delvePageTransformator.RegisterObserver(new MarkdownToSharePointObserver(targetContext ?? this.ClientContext, includeVerbose: this.LogVerbose, includeDebugEntries: this.LogVerbose));
                }
                else
                {
                    pageTransformator.RegisterObserver(new MarkdownToSharePointObserver(targetContext ?? this.ClientContext, includeVerbose: this.LogVerbose, includeDebugEntries: this.LogVerbose));
                }
            }
            else if (this.LogType == ClientSidePageTransformatorLogType.Console)
            {
                if (this.PublishingPage)
                {
                    publishingPageTransformator.RegisterObserver(new ConsoleObserver(includeDebugEntries: this.LogVerbose));
                }
                else if (this.DelveBlogPage)
                {
                    delvePageTransformator.RegisterObserver(new ConsoleObserver(includeDebugEntries: this.LogVerbose));
                }
                else
                {
                    pageTransformator.RegisterObserver(new ConsoleObserver(includeDebugEntries: this.LogVerbose));
                }
            }

            // Clear the client side component cache
            if (this.ClearCache)
            {
                CacheManager.Instance.ClearAllCaches();
            }

            string serverRelativeClientPageUrl = "";

            if (this.PublishingPage)
            {
                // Setup Transformation information
                PublishingPageTransformationInformation pti = new PublishingPageTransformationInformation(page)
                {
                    Overwrite = this.Overwrite,
                    KeepPageSpecificPermissions             = !this.SkipItemLevelPermissionCopyToClientSidePage,
                    PublishCreatedPage                      = !this.DontPublish,
                    KeepPageCreationModificationInformation = this.KeepPageCreationModificationInformation,
                    PostAsNews                             = this.PostAsNews,
                    DisablePageComments                    = this.DisablePageComments,
                    TargetPageName                         = !string.IsNullOrEmpty(this.PublishingTargetPageName) ? this.PublishingTargetPageName : this.TargetPageName,
                    SkipUrlRewrite                         = this.SkipUrlRewriting,
                    SkipDefaultUrlRewrite                  = this.SkipDefaultUrlRewriting,
                    UrlMappingFile                         = this.UrlMappingFile,
                    AddTableListImageAsImageWebPart        = this.AddTableListImageAsImageWebPart,
                    SkipUserMapping                        = this.SkipUserMapping,
                    UserMappingFile                        = this.UserMappingFile,
                    LDAPConnectionString                   = this.LDAPConnectionString,
                    TargetPageFolder                       = this.TargetPageFolder,
                    TargetPageFolderOverridesDefaultFolder = this.TargetPageFolderOverridesDefaultFolder,
                    TermMappingFile                        = TermMappingFile,
                    SkipTermStoreMapping                   = SkipTermStoreMapping,
                    RemoveEmptySectionsAndColumns          = this.RemoveEmptySectionsAndColumns
                };

                // Set mapping properties
                pti.MappingProperties["SummaryLinksToQuickLinks"] = (!SummaryLinksToHtml).ToString().ToLower();
                pti.MappingProperties["UseCommunityScriptEditor"] = UseCommunityScriptEditor.ToString().ToLower();

                try
                {
                    serverRelativeClientPageUrl = publishingPageTransformator.Transform(pti);
                }
                finally
                {
                    // Flush log
                    if (this.LogType != ClientSidePageTransformatorLogType.None && this.LogType != ClientSidePageTransformatorLogType.Console && !this.LogSkipFlush)
                    {
                        publishingPageTransformator.FlushObservers();
                    }
                }
            }
            else if (this.DelveBlogPage)
            {
                // Setup Transformation information
                DelvePageTransformationInformation pti = new DelvePageTransformationInformation(page)
                {
                    Overwrite = this.Overwrite,
                    KeepPageSpecificPermissions             = !this.SkipItemLevelPermissionCopyToClientSidePage,
                    PublishCreatedPage                      = !this.DontPublish,
                    KeepPageCreationModificationInformation = this.KeepPageCreationModificationInformation,
                    SetAuthorInPageHeader                   = this.SetAuthorInPageHeader,
                    PostAsNews                             = this.PostAsNews,
                    DisablePageComments                    = this.DisablePageComments,
                    TargetPageName                         = crossSiteTransformation ? this.TargetPageName : "",
                    SkipUrlRewrite                         = this.SkipUrlRewriting,
                    SkipDefaultUrlRewrite                  = this.SkipDefaultUrlRewriting,
                    UrlMappingFile                         = this.UrlMappingFile,
                    AddTableListImageAsImageWebPart        = this.AddTableListImageAsImageWebPart,
                    SkipUserMapping                        = this.SkipUserMapping,
                    UserMappingFile                        = this.UserMappingFile,
                    LDAPConnectionString                   = this.LDAPConnectionString,
                    TargetPageFolder                       = this.TargetPageFolder,
                    TargetPageFolderOverridesDefaultFolder = this.TargetPageFolderOverridesDefaultFolder,
                    RemoveEmptySectionsAndColumns          = this.RemoveEmptySectionsAndColumns
                };

                // Set mapping properties
                pti.MappingProperties["SummaryLinksToQuickLinks"] = (!SummaryLinksToHtml).ToString().ToLower();
                pti.MappingProperties["UseCommunityScriptEditor"] = UseCommunityScriptEditor.ToString().ToLower();

                try
                {
                    serverRelativeClientPageUrl = delvePageTransformator.Transform(pti);
                }
                finally
                {
                    // Flush log
                    if (this.LogType != ClientSidePageTransformatorLogType.None && this.LogType != ClientSidePageTransformatorLogType.Console && !this.LogSkipFlush)
                    {
                        pageTransformator.FlushObservers();
                    }
                }
            }
            else
            {
                Microsoft.SharePoint.Client.File fileToModernize = null;
                if (this.Folder != null && this.Folder.Equals(rootFolder, StringComparison.InvariantCultureIgnoreCase))
                {
                    // Load the page file from the site root folder
                    var webServerRelativeUrl = this.ClientContext.Web.EnsureProperty(p => p.ServerRelativeUrl);
                    fileToModernize = this.ClientContext.Web.GetFileByServerRelativeUrl($"{webServerRelativeUrl}/{this.Identity.Name}");
                    this.ClientContext.Load(fileToModernize);
                    this.ClientContext.ExecuteQueryRetry();
                }

                // Setup Transformation information
                PageTransformationInformation pti = new PageTransformationInformation(page)
                {
                    SourceFile = fileToModernize,
                    Overwrite  = this.Overwrite,
                    TargetPageTakesSourcePageName      = this.TakeSourcePageName,
                    ReplaceHomePageWithDefaultHomePage = this.ReplaceHomePageWithDefault,
                    KeepPageSpecificPermissions        = !this.SkipItemLevelPermissionCopyToClientSidePage,
                    CopyPageMetadata   = this.CopyPageMetadata,
                    PublishCreatedPage = !this.DontPublish,
                    KeepPageCreationModificationInformation = this.KeepPageCreationModificationInformation,
                    SetAuthorInPageHeader                  = this.SetAuthorInPageHeader,
                    PostAsNews                             = this.PostAsNews,
                    DisablePageComments                    = this.DisablePageComments,
                    TargetPageName                         = crossSiteTransformation ? this.TargetPageName : "",
                    SkipUrlRewrite                         = this.SkipUrlRewriting,
                    SkipDefaultUrlRewrite                  = this.SkipDefaultUrlRewriting,
                    UrlMappingFile                         = this.UrlMappingFile,
                    AddTableListImageAsImageWebPart        = this.AddTableListImageAsImageWebPart,
                    SkipUserMapping                        = this.SkipUserMapping,
                    UserMappingFile                        = this.UserMappingFile,
                    LDAPConnectionString                   = this.LDAPConnectionString,
                    TargetPageFolder                       = this.TargetPageFolder,
                    TargetPageFolderOverridesDefaultFolder = this.TargetPageFolderOverridesDefaultFolder,
                    ModernizationCenterInformation         = new ModernizationCenterInformation()
                    {
                        AddPageAcceptBanner = this.AddPageAcceptBanner
                    },
                    TermMappingFile               = TermMappingFile,
                    SkipTermStoreMapping          = SkipTermStoreMapping,
                    RemoveEmptySectionsAndColumns = this.RemoveEmptySectionsAndColumns
                };

                // Set mapping properties
                pti.MappingProperties["SummaryLinksToQuickLinks"] = (!SummaryLinksToHtml).ToString().ToLower();
                pti.MappingProperties["UseCommunityScriptEditor"] = UseCommunityScriptEditor.ToString().ToLower();

                try
                {
                    serverRelativeClientPageUrl = pageTransformator.Transform(pti);
                }
                finally
                {
                    // Flush log
                    if (this.LogType != ClientSidePageTransformatorLogType.None && this.LogType != ClientSidePageTransformatorLogType.Console && !this.LogSkipFlush)
                    {
                        pageTransformator.FlushObservers();
                    }
                }
            }

            // Output the server relative url to the newly created page
            if (!string.IsNullOrEmpty(serverRelativeClientPageUrl))
            {
                WriteObject(serverRelativeClientPageUrl);
            }
        }
Esempio n. 3
0
        public virtual Tuple <PageLayout, List <WebPartEntity> > AnalyzeAndTransform(DelvePageTransformationInformation pageTransformationInformation, ClientSidePage targetPage)
        {
            List <WebPartEntity> webparts = new List <WebPartEntity>();

            // Load the page
            string delvePageUrl = page[Constants.FileRefField].ToString();
            string fileContents = cc.Web.GetFileByServerRelativeUrlAsString(delvePageUrl);

            if (!string.IsNullOrEmpty(fileContents))
            {
                // Delve page is a json blob
                var pageJson = JToken.Parse(fileContents);

                string pageHeaderImage = null;

                foreach (var controlDataElement in pageJson["ControlData"])
                {
                    //Grab the first
                    var contentElement = controlDataElement.First;

                    string controlType = GetValueAsString(contentElement, "ControlName");

                    if (string.IsNullOrEmpty(controlType))
                    {
                        continue;
                    }

                    if (controlType.Equals("RichTextControl"))
                    {
                        int    subType     = GetSubType(contentElement["DataContext"]);
                        string textContent = GetValueAsString(contentElement["DataContext"], "Value");

                        // H1 header
                        if (subType == 0)
                        {
                            webparts.Add(CreateWikiTextPart($"<H2>{textContent}</H2>", 1, 1, GetOrder(controlDataElement.First, pageJson["ControlMap"]["Rows"])));
                        }
                        // Text
                        else if (subType == 1)
                        {
                            // Clean html
                            textContent = CleanHtml(textContent);
                            webparts.Add(CreateWikiTextPart(textContent, 1, 1, GetOrder(controlDataElement.First, pageJson["ControlMap"]["Rows"])));
                        }
                        // Pull quote
                        else if (subType == 2)
                        {
                            webparts.Add(CreateWikiTextPart($"<blockquote>{textContent}</blockquote>", 1, 1, GetOrder(controlDataElement.First, pageJson["ControlMap"]["Rows"])));
                        }
                    }
                    else if (controlType.Equals("ImageControl"))
                    {
                        Dictionary <string, string> props = new Dictionary <string, string>();

                        string imageSource  = GetValueAsString(contentElement["DataContext"], "ImageSource");
                        string imageCaption = GetValueAsString(contentElement["DataContext"], "CaptionText");

                        if (!string.IsNullOrEmpty(imageSource))
                        {
                            props.Add("Title", "Image in wiki text");
                            props.Add("Description", "");
                            props.Add("ImageUrl", imageSource.Replace("about://", ""));
                            props.Add("Width", "");
                            props.Add("Height", "");
                            props.Add("Anchor", "");
                            props.Add("Caption", imageCaption ?? "");
                        }

                        webparts.Add(CreateImagePart(props, 1, 1, GetOrder(controlDataElement.First, pageJson["ControlMap"]["Rows"])));
                    }
                    else if (controlType.Equals("VideoControl"))
                    {
                        Dictionary <string, string> props = new Dictionary <string, string>();

                        string videoSource  = GetValueAsString(contentElement["DataContext"], "VideoSource");
                        string videoCaption = GetValueAsString(contentElement["DataContext"], "CaptionText");

                        if (!string.IsNullOrEmpty(videoSource))
                        {
                            Uri    videoSourceUri  = new Uri(videoSource);
                            string queryString     = videoSourceUri.Query;
                            var    queryDictionary = System.Web.HttpUtility.ParseQueryString(queryString);

                            if (queryDictionary["chid"] != null && queryDictionary["vid"] != null)
                            {
                                string newVideoSource = $"{videoSourceUri.Scheme}://{videoSourceUri.DnsSafeHost}{videoSourceUri.LocalPath}".Replace("pointpublishing.aspx", "VideoEmbedHost.aspx", StringComparison.InvariantCultureIgnoreCase);
                                props.Add("Title", "Video in wiki text");
                                props.Add("Description", "");
                                props.Add("IFrameEmbed", $"<iframe width=640 height=360 src='{newVideoSource}?chId={queryDictionary["chid"]}&vId={queryDictionary["vid"]}&width=640&height=360&autoPlay=false&showInfo=false' allowfullscreen></iframe>");
                                props.Add("Source", videoSource);

                                webparts.Add(CreateVideoPart(props, 1, 1, GetOrder(controlDataElement.First, pageJson["ControlMap"]["Rows"])));
                            }
                        }
                    }
                    else if (controlType.Equals("DocumentHookControl"))
                    {
                        Dictionary <string, string> props = new Dictionary <string, string>();

                        string docSource = GetValueAsString(contentElement["DataContext"]["OfficeDocumentDataContext"], "FullDocumentUrl");

                        if (!string.IsNullOrEmpty(docSource))
                        {
                            props.Add("Title", "Document embed in wiki text");
                            props.Add("Description", "");
                            props.Add("DocumentUrl", docSource);
                            props.Add("Width", "");
                            props.Add("Height", "");
                        }

                        webparts.Add(CreateEmbedPart(props, 1, 1, GetOrder(controlDataElement.First, pageJson["ControlMap"]["Rows"])));
                    }
                    else if (controlType.Equals("ImageHeaderControl"))
                    {
                        pageHeaderImage = GetValueAsString(contentElement["DataContext"], "ImageSource");
                    }
                }

                // Configure page header
                targetPage.SetDefaultPageHeader();

                // Do we have an image page header?
                if (!string.IsNullOrEmpty(pageHeaderImage))
                {
                    string newHeaderImageServerRelativeUrl = "";
                    try
                    {
                        // make server relative image url
                        pageHeaderImage = new Uri(pageHeaderImage).PathAndQuery;

                        // Integrate asset transformator
                        AssetTransfer assetTransfer = new AssetTransfer(page.Context as ClientContext, targetPage.Context, base.RegisteredLogObservers);
                        newHeaderImageServerRelativeUrl = assetTransfer.TransferAsset(pageHeaderImage, System.IO.Path.GetFileNameWithoutExtension(page[Constants.TitleField].ToString()));
                    }
                    catch (Exception ex)
                    {
                        LogError(LogStrings.Error_HeaderImageAssetTransferFailed, LogStrings.Heading_PublishingPageHeader, ex);
                    }

                    if (!string.IsNullOrEmpty(newHeaderImageServerRelativeUrl))
                    {
                        LogInfo(string.Format(LogStrings.SettingHeaderImage, newHeaderImageServerRelativeUrl), LogStrings.Heading_PublishingPageHeader);
                        targetPage.SetCustomPageHeader(newHeaderImageServerRelativeUrl);
                    }
                }

                targetPage.PageTitle = pageJson["Title"].Value <string>();

                if (pageTransformationInformation.KeepSubTitle)
                {
                    var topicHeader = GetValueAsString(pageJson, "SubTitle");
                    if (!string.IsNullOrEmpty(topicHeader))
                    {
                        if (topicHeader.Length > 40)
                        {
                            topicHeader = topicHeader.Substring(0, 40);
                        }

                        targetPage.PageHeader.ShowTopicHeader = true;
                        targetPage.PageHeader.TopicHeader     = topicHeader;
                    }
                }

                if (pageTransformationInformation.PageTitleOverride != null)
                {
                    var title = pageTransformationInformation.PageTitleOverride(targetPage.PageTitle);
                    targetPage.PageTitle = title;

                    LogInfo($"{LogStrings.TransformPageTitleOverride} - page title: {title}", LogStrings.Heading_ArticlePageHandling);
                }
            }
            else
            {
                // Should never happen
            }

            return(new Tuple <PageLayout, List <WebPartEntity> >(PageLayout.Wiki_OneColumn, webparts));
        }