/// <summary> /// Analyze the web /// </summary> /// <param name="cc">ClientContext of the web to be analyzed</param> /// <returns>Duration of the analysis</returns> public override TimeSpan Analyze(ClientContext cc) { try { base.Analyze(cc); // Ensure needed data is loaded Web web = cc.Web; web.EnsureProperties(p => p.UserCustomActions, p => p.AlternateCssUrl, p => p.CustomMasterUrl, p => p.MasterUrl, p => p.Features, p => p.WebTemplate, p => p.Configuration, p => p.HasUniqueRoleAssignments); // Log in Site scan data that the scanned web is a sub site if (web.IsSubSite()) { SiteScanResult siteScanData; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out siteScanData)) { if (!siteScanData.SubSites) { var clonedSiteScandata = siteScanData.Clone(); clonedSiteScandata.SubSites = true; if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } // Check if we've broken permission inheritance in this site collection if (web.HasUniqueRoleAssignments) { SiteScanResult siteScanData2; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out siteScanData2)) { if (!siteScanData2.SubSitesWithBrokenPermissionInheritance) { var clonedSiteScandata = siteScanData2.Clone(); clonedSiteScandata.SubSitesWithBrokenPermissionInheritance = true; if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData2)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } } } // Perform specific analysis work WebScanResult scanResult = new WebScanResult() { SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, }; // Log used web template if (web.WebTemplate != null) { scanResult.WebTemplate = $"{web.WebTemplate}#{web.Configuration}"; } // Page feature check: users can disable this to prevent modern page creation scanResult.ModernPageWebFeatureDisabled = web.Features.Where(f => f.DefinitionId == FeatureId_Web_ModernPage).Count() == 0; // List feature check: users can enabled this to prevent modern lists from working scanResult.ModernListWebBlockingFeatureEnabled = web.Features.Where(f => f.DefinitionId == FeatureId_Web_ModernList).Count() > 0; // Publishing web feature enabled scanResult.WebPublishingFeatureEnabled = web.Features.Where(f => f.DefinitionId == FeatureId_Web_Publishing).Count() > 0; // Site is using the publishing "Pages" library? if (scanResult.WebPublishingFeatureEnabled) { if (!(scanResult.WebTemplate.Equals("BICENTERSITE#0", StringComparison.InvariantCultureIgnoreCase) || scanResult.WebTemplate.Equals("BLANKINTERNET#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("SRCHCEN#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("CMSPUBLISHING#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("SRCHCENTERLITE#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("POINTPUBLISHINGHUB#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("POINTPUBLISHINGTOPIC#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("ENTERWIKI#0", StringComparison.InvariantCulture))) { var pagesLibrary = web.GetListsToScan().Where(p => p.BaseTemplate == 850).FirstOrDefault(); if (pagesLibrary != null) { if (pagesLibrary.ItemCount > 0) { scanResult.PublishingPagesLibraryContainsPages = true; } } } } // Log permission inheritance details if (web.IsSubSite() && web.HasUniqueRoleAssignments) { scanResult.BrokenPermissionInheritance = web.HasUniqueRoleAssignments; if (!this.ScanJob.SkipUserInformation) { scanResult.Owners = web.GetOwners(); scanResult.Members = web.GetMembers(); scanResult.Visitors = web.GetVisitors(); scanResult.EveryoneClaimsGranted = web.ClaimsHaveRoleAssignment(this.ScanJob.EveryoneClaim, this.ScanJob.EveryoneExceptExternalUsersClaim); } } // If the web template is STS#0, GROUP#0 or SITEPAGEPUBLISHING#0 then the feature was activated by SPO, other templates never got it scanResult.ModernPageFeatureWasEnabledBySPO = false; if (scanResult.WebTemplate.Equals("STS#0", StringComparison.InvariantCultureIgnoreCase) || scanResult.WebTemplate.Equals("STS#3", StringComparison.InvariantCultureIgnoreCase) || scanResult.WebTemplate.Equals("GROUP#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("SITEPAGEPUBLISHING#0", StringComparison.InvariantCulture)) { // Since we did not enable the feature for all STS#0 sites (sites with publishing active did not get it, nor sites having a large number of webpart/wiki pages) we // check if it should have been turned on by checking for the "site page" content type being added to the site pages library var listsToScan = web.GetListsToScan(); var sitePagesLibrary = listsToScan.Where(p => p.BaseTemplate == (int)ListTemplateType.WebPageLibrary).FirstOrDefault(); if (sitePagesLibrary != null) { cc.Load(sitePagesLibrary, p => p.ContentTypes.Include(c => c.Id)); cc.ExecuteQueryRetry(); var sitePageContentTypeFound = sitePagesLibrary.ContentTypes.Where(c => c.Id.StringValue.StartsWith(SitePageContentTypeId, StringComparison.InvariantCultureIgnoreCase)).OrderBy(c => c.Id.StringValue.Length).FirstOrDefault(); if (sitePageContentTypeFound != null) { scanResult.ModernPageFeatureWasEnabledBySPO = true; } } } // Get information about the master pages used if (!string.IsNullOrEmpty(web.MasterUrl) && !excludeMasterPage.Contains(web.MasterUrl.Substring(web.MasterUrl.LastIndexOf("/") + 1).ToLower())) { scanResult.MasterPage = web.MasterUrl; } if (!string.IsNullOrEmpty(web.CustomMasterUrl) && !excludeMasterPage.Contains(web.CustomMasterUrl.Substring(web.CustomMasterUrl.LastIndexOf("/") + 1).ToLower())) { scanResult.CustomMasterPage = web.CustomMasterUrl; } if (!string.IsNullOrEmpty(web.AlternateCssUrl)) { scanResult.AlternateCSS = web.AlternateCssUrl; } // Get the user custom actions scanResult.WebUserCustomActions = web.UserCustomActions.Analyze(this.SiteCollectionUrl, this.SiteUrl); // Get home page from the web and check whether it's a modern page or not scanResult.ModernHomePage = false; var homePageUrl = web.WelcomePage; if (string.IsNullOrEmpty(homePageUrl)) { // Will be case when the site home page is a web part page homePageUrl = "default.aspx"; } var homepageName = System.IO.Path.GetFileName(homePageUrl); var sitePagesLibraryForWeb = web.GetListsToScan().Where(p => p.BaseTemplate == (int)ListTemplateType.WebPageLibrary).FirstOrDefault(); if (sitePagesLibraryForWeb != null && homePageUrl.StartsWith("SitePages", StringComparison.InvariantCultureIgnoreCase)) { var homePageFile = web.GetFileByServerRelativeUrl($"{(web.ServerRelativeUrl.Length == 1 ? "" : web.ServerRelativeUrl)}/{homePageUrl}"); cc.Load(homePageFile, f => f.ListItemAllFields, f => f.Exists); cc.ExecuteQueryRetry(); if (homePageFile.Exists) { var item = homePageFile.ListItemAllFields; if (item.FieldValues.ContainsKey(ClientSideApplicationId) && item[ClientSideApplicationId] != null && item[ClientSideApplicationId].ToString().Equals(FeatureId_Web_ModernPage.ToString(), StringComparison.InvariantCultureIgnoreCase)) { scanResult.ModernHomePage = true; } } } // Push information from root web to respective SiteScanResult object if (!cc.Web.IsSubSite()) { SiteScanResult siteScanData; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out siteScanData)) { var clonedSiteScandata = siteScanData.Clone(); clonedSiteScandata.WebPublishingFeatureEnabled = scanResult.WebPublishingFeatureEnabled; clonedSiteScandata.ModernPageWebFeatureDisabled = scanResult.ModernPageWebFeatureDisabled; clonedSiteScandata.ModernPageFeatureWasEnabledBySPO = scanResult.ModernPageFeatureWasEnabledBySPO; clonedSiteScandata.ModernListWebBlockingFeatureEnabled = scanResult.ModernListWebBlockingFeatureEnabled; clonedSiteScandata.ModernHomePage = scanResult.ModernHomePage; clonedSiteScandata.MasterPage = (!String.IsNullOrEmpty(scanResult.MasterPage) || !String.IsNullOrEmpty(scanResult.CustomMasterPage)); clonedSiteScandata.AlternateCSS = !String.IsNullOrEmpty(scanResult.AlternateCSS); clonedSiteScandata.WebUserCustomActions = scanResult.WebUserCustomActions; if (scanResult.PublishingPagesLibraryContainsPages && clonedSiteScandata.PublishingPagesUsed == false) { clonedSiteScandata.PublishingPagesUsed = true; } if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } else { if (scanResult.PublishingPagesLibraryContainsPages) { if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out SiteScanResult siteScanData)) { var clonedSiteScandata = siteScanData.Clone(); if (clonedSiteScandata.PublishingPagesUsed == false) { clonedSiteScandata.PublishingPagesUsed = true; } if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } } // Persist web results if (!this.ScanJob.WebScanResults.TryAdd(this.SiteUrl, scanResult)) { ScanError error = new ScanError() { Error = $"Could not add web scan result for {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } if (Options.IncludeLists(this.ScanJob.Mode)) { // Kickoff the list analyzer var listAnalyzer = new ListAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob); listAnalyzer.Analyze(cc); } if (Options.IncludePage(this.ScanJob.Mode)) { // Kickoff the page analysing var pageAnalyzer = new PageAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob, this.pageSearchResults); pageAnalyzer.Analyze(cc); } if (Options.IncludePublishing(this.ScanJob.Mode)) { // Kickoff publishing analysis PublishingAnalyzer publishingAnalyzer = null; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out SiteScanResult siteScanData)) { publishingAnalyzer = new PublishingAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob, scanResult, siteScanData); } else { publishingAnalyzer = new PublishingAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob, scanResult, null); } // Assign the current masterpage gallery results publishingAnalyzer.MasterPageGalleryCustomization = this.MasterPageGalleryCustomization; // Run the publishing analyzer publishingAnalyzer.Analyze(cc); // Store the masterpage gallery results this.MasterPageGalleryCustomization = publishingAnalyzer.MasterPageGalleryCustomization; } if (Options.IncludeInfoPath(this.ScanJob.Mode)) { // Kick off InfoPath analysis var infoPathAnalyzer = new InfoPathAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob); infoPathAnalyzer.Analyze(cc); } if (Options.IncludeBlog(this.ScanJob.Mode)) { // Kick off Blog analysis var blogAnalyzer = new BlogAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob); blogAnalyzer.Analyze(cc); } // Place workflow as last scan as it's reloading the web.Lists with different properties. The GetListsToScan method will not reload and hence cause missing properties otherwise if (Options.IncludeWorkflow(this.ScanJob.Mode)) { // Kick off workflow analysis var workflowAnalyzer = new WorkflowAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob); workflowAnalyzer.Analyze(cc); } } finally { this.StopTime = DateTime.Now; } // return the duration of this scan return(new TimeSpan((this.StopTime.Subtract(this.StartTime).Ticks))); }
/// <summary> /// Analyze the web /// </summary> /// <param name="cc">ClientContext of the web to be analyzed</param> /// <returns>Duration of the analysis</returns> public override TimeSpan Analyze(ClientContext cc) { try { base.Analyze(cc); // Ensure needed data is loaded Web web = cc.Web; web.EnsureProperties(p => p.UserCustomActions, p => p.AlternateCssUrl, p => p.CustomMasterUrl, p => p.MasterUrl, p => p.Features, p => p.WebTemplate, p => p.Configuration, p => p.HasUniqueRoleAssignments, p => p.RootFolder); // Log in Site scan data that the scanned web is a sub site if (web.IsSubSite()) { SiteScanResult siteScanData; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out siteScanData)) { if (!siteScanData.SubSites) { var clonedSiteScandata = siteScanData.Clone(); clonedSiteScandata.SubSites = true; if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } // Check if we've broken permission inheritance in this site collection if (web.HasUniqueRoleAssignments) { SiteScanResult siteScanData2; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out siteScanData2)) { if (!siteScanData2.SubSitesWithBrokenPermissionInheritance) { var clonedSiteScandata = siteScanData2.Clone(); clonedSiteScandata.SubSitesWithBrokenPermissionInheritance = true; if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData2)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } } } // Perform specific analysis work WebScanResult scanResult = new WebScanResult() { SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, }; // Log used web template if (web.WebTemplate != null) { scanResult.WebTemplate = $"{web.WebTemplate}#{web.Configuration}"; } // Page feature check: users can disable this to prevent modern page creation scanResult.ModernPageWebFeatureDisabled = web.Features.Where(f => f.DefinitionId == FeatureId_Web_ModernPage).Count() == 0; // List feature check: users can enabled this to prevent modern lists from working scanResult.ModernListWebBlockingFeatureEnabled = web.Features.Where(f => f.DefinitionId == FeatureId_Web_ModernList).Count() > 0; // Publishing web feature enabled scanResult.WebPublishingFeatureEnabled = web.Features.Where(f => f.DefinitionId == FeatureId_Web_Publishing).Count() > 0; // Log permission inheritance details if (web.IsSubSite() && web.HasUniqueRoleAssignments) { scanResult.BrokenPermissionInheritance = web.HasUniqueRoleAssignments; scanResult.Owners = web.GetOwners(); scanResult.Members = web.GetMembers(); scanResult.Visitors = web.GetVisitors(); scanResult.EveryoneClaimsGranted = web.ClaimsHaveRoleAssignment(this.ScanJob.EveryoneClaim, this.ScanJob.EveryoneExceptExternalUsersClaim); } // If the web template is STS#0, GROUP#0 or SITEPAGEPUBLISHING#0 then the feature was activated by SPO, other templates never got it scanResult.ModernPageFeatureWasEnabledBySPO = false; if (scanResult.WebTemplate.Equals("STS#0", StringComparison.InvariantCultureIgnoreCase) || scanResult.WebTemplate.Equals("GROUP#0", StringComparison.InvariantCulture) || scanResult.WebTemplate.Equals("SITEPAGEPUBLISHING#0", StringComparison.InvariantCulture)) { // Since we did not enable the feature for all STS#0 sites (sites with publishing active did not get it, nor sites having a large number of webpart/wiki pages) we // check if it should have been turned on by checking for the "site page" content type being added to the site pages library var listsToScan = web.GetListsToScan(); var sitePagesLibrary = listsToScan.Where(p => p.BaseTemplate == (int)ListTemplateType.WebPageLibrary).FirstOrDefault(); if (sitePagesLibrary != null) { cc.Load(sitePagesLibrary, p => p.ContentTypes.Include(c => c.Id)); cc.ExecuteQueryRetry(); if (sitePagesLibrary.ContentTypes.BestMatch(SitePageContentTypeId) != null) { scanResult.ModernPageFeatureWasEnabledBySPO = true; } } } // Get information about the master pages used if (!string.IsNullOrEmpty(web.MasterUrl) && !excludeMasterPage.Contains(web.MasterUrl.Substring(web.MasterUrl.LastIndexOf("/") + 1).ToLower())) { scanResult.MasterPage = web.MasterUrl; } if (!string.IsNullOrEmpty(web.CustomMasterUrl) && !excludeMasterPage.Contains(web.CustomMasterUrl.Substring(web.CustomMasterUrl.LastIndexOf("/") + 1).ToLower())) { scanResult.CustomMasterPage = web.CustomMasterUrl; } if (!string.IsNullOrEmpty(web.AlternateCssUrl)) { scanResult.AlternateCSS = web.AlternateCssUrl; } // Get the user custom actions scanResult.WebUserCustomActions = web.UserCustomActions.Analyze(this.SiteCollectionUrl, this.SiteUrl); // Get home page from the web and check whether it's a modern page or not scanResult.ModernHomePage = false; var homePageUrl = web.RootFolder.WelcomePage; var homepageName = System.IO.Path.GetFileName(web.RootFolder.WelcomePage); if (string.IsNullOrEmpty(homepageName)) { homepageName = "Home.aspx"; } var sitePagesLibraryForWeb = web.GetListsToScan().Where(p => p.BaseTemplate == (int)ListTemplateType.WebPageLibrary).FirstOrDefault(); if (sitePagesLibraryForWeb != null) { var homePageFile = web.GetFileByServerRelativeUrl($"{sitePagesLibraryForWeb.RootFolder.ServerRelativeUrl}/{homepageName}"); cc.Load(homePageFile, f => f.ListItemAllFields, f => f.Exists); cc.ExecuteQueryRetry(); if (homePageFile.Exists) { var item = homePageFile.ListItemAllFields; if (item.FieldValues.ContainsKey(ClientSideApplicationId) && item[ClientSideApplicationId] != null && item[ClientSideApplicationId].ToString().Equals(FeatureId_Web_ModernPage.ToString(), StringComparison.InvariantCultureIgnoreCase)) { scanResult.ModernHomePage = true; } } } // Push information from root web to respective SiteScanResult object if (!cc.Web.IsSubSite()) { SiteScanResult siteScanData; if (this.ScanJob.SiteScanResults.TryGetValue(this.SiteCollectionUrl, out siteScanData)) { var clonedSiteScandata = siteScanData.Clone(); clonedSiteScandata.WebPublishingFeatureEnabled = scanResult.WebPublishingFeatureEnabled; clonedSiteScandata.ModernPageWebFeatureDisabled = scanResult.ModernPageWebFeatureDisabled; clonedSiteScandata.ModernPageFeatureWasEnabledBySPO = scanResult.ModernPageFeatureWasEnabledBySPO; clonedSiteScandata.ModernListWebBlockingFeatureEnabled = scanResult.ModernListWebBlockingFeatureEnabled; clonedSiteScandata.ModernHomePage = scanResult.ModernHomePage; clonedSiteScandata.MasterPage = (!String.IsNullOrEmpty(scanResult.MasterPage) || !String.IsNullOrEmpty(scanResult.CustomMasterPage)); clonedSiteScandata.AlternateCSS = !String.IsNullOrEmpty(scanResult.AlternateCSS); clonedSiteScandata.WebUserCustomActions = scanResult.WebUserCustomActions; if (!this.ScanJob.SiteScanResults.TryUpdate(this.SiteCollectionUrl, clonedSiteScandata, siteScanData)) { ScanError error = new ScanError() { Error = $"Could not add update site scan result for {this.SiteCollectionUrl} from web scan of {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } } } // Persist web results if (!this.ScanJob.WebScanResults.TryAdd(this.SiteUrl, scanResult)) { ScanError error = new ScanError() { Error = $"Could not add web scan result for {this.SiteUrl}", SiteColUrl = this.SiteCollectionUrl, SiteURL = this.SiteUrl, Field1 = "WebAnalyzer", }; this.ScanJob.ScanErrors.Push(error); } if (this.ScanJob.Mode == Mode.Full) { // Kickoff the page analysing var pageAnalyzer = new PageAnalyzer(this.SiteUrl, this.SiteCollectionUrl, this.ScanJob, this.pageSearchResults); pageAnalyzer.Analyze(cc); } } finally { this.StopTime = DateTime.Now; } // return the duration of this scan return(new TimeSpan((this.StopTime.Subtract(this.StartTime).Ticks))); }