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); }
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); } }
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)); }