From ae705f930a6e654d60e56720cb6ff868f11c518c Mon Sep 17 00:00:00 2001 From: Gunjan Datta Date: Wed, 22 Jul 2026 12:05:02 -0400 Subject: [PATCH 1/7] Updated the dlp overshared group property to be generic, now that it's used in other reports. --- spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts | 12 ++++++------ spfx/src/webparts/siteAdmin/loc/en-us.js | 2 +- spfx/src/webparts/siteAdmin/loc/mystrings.d.ts | 2 +- src/tabs/reports.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts b/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts index 4e900e9..f0a6aa4 100644 --- a/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts +++ b/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts @@ -67,10 +67,10 @@ export interface ISiteAdminWebPartProps { MaxStorageDescription: string; ReportsDocRententionYears: string; ReportsDLPFileExt: string; - ReportsDLPGroups: string; ReportsDocSearchFileExt: string; ReportsDocSearchKeywords: string; ReportsDocSearchRegexPatterns: string; + ReportsOversharedGroups: string; SensitivityLabelFileExt: string; SiteAttestation: boolean; SiteAttestationText: string; @@ -202,10 +202,10 @@ declare const SiteAdmin: { reportProps?: { docRententionYears?: string; dlpFileExt?: string; - dlpGroups?: string[]; docSearchFileExt?: string; docSearchKeywords?: string; docSearchRegexPatterns?: string; + oversharedGroups?: string[]; sensitivityLabelFileExt?: string; } searchProps?: { @@ -336,11 +336,11 @@ export default class SiteAdminWebPart extends BaseClientSideWebPart group.trim()), docRententionYears: this.properties.ReportsDocRententionYears, docSearchFileExt: this.properties.ReportsDocSearchFileExt, docSearchKeywords: this.properties.ReportsDocSearchKeywords, docSearchRegexPatterns: this.properties.ReportsDocSearchRegexPatterns, + oversharedGroups: (this.properties.ReportsOversharedGroups || "").split(",").map(group => group.trim()), sensitivityLabelFileExt: this.properties.SensitivityLabelFileExt }, searchProps: { @@ -664,12 +664,12 @@ export default class SiteAdminWebPart extends BaseClientSideWebPart Date: Wed, 22 Jul 2026 12:07:08 -0400 Subject: [PATCH 2/7] Added search documents to have the target web option. --- src/tabs/reports.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tabs/reports.ts b/src/tabs/reports.ts index 4dff1ab..7759b6f 100644 --- a/src/tabs/reports.ts +++ b/src/tabs/reports.ts @@ -214,6 +214,7 @@ export class ReportsTab { case ReportTypes.ExternalUsers: case ReportTypes.Permissions: case ReportTypes.SearchAgents: + case ReportTypes.SearchDocs: case ReportTypes.SearchEEEU: case ReportTypes.SearchUsers: case ReportTypes.SensitivityLabels: From a388bba964abddbd2e45eea65ceeb59065534b22 Mon Sep 17 00:00:00 2001 From: Gunjan Datta Date: Wed, 22 Jul 2026 14:05:36 -0400 Subject: [PATCH 3/7] Fixed issue where if file extensions were not provided, it didn't process any of them. --- src/reports/dlp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reports/dlp.ts b/src/reports/dlp.ts index f88bb3e..c5da566 100644 --- a/src/reports/dlp.ts +++ b/src/reports/dlp.ts @@ -105,7 +105,7 @@ export class DLP { let analyzeFile = true; // See if the file extensions are provided - if (fileExtensions) { + if (fileExtensions && fileExtensions.length > 0) { // Default the flag analyzeFile = false From 0143c603982dfc20ede7b360bd6a54c42e64fe21 Mon Sep 17 00:00:00 2001 From: Gunjan Datta Date: Wed, 22 Jul 2026 17:33:52 -0400 Subject: [PATCH 4/7] Added logic to hide the changes tab if features, management and webs are hidden. --- src/tabs/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tabs/index.ts b/src/tabs/index.ts index cdce3a5..cbfa6a0 100644 --- a/src/tabs/index.ts +++ b/src/tabs/index.ts @@ -135,7 +135,7 @@ export class Tabs { } }); } - if (!(auditOnly || loadOneDrive)) { + if (!(auditOnly || loadOneDrive) && !(appProps.hideTabs.features && appProps.hideTabs.management && appProps.hideTabs.webs)) { // Add the changes items.push({ tabName: "Changes", From f42f39d047ee9ba09f85e4a0517dc0e6b23f060b Mon Sep 17 00:00:00 2001 From: Gunjan Datta Date: Wed, 22 Jul 2026 18:12:09 -0400 Subject: [PATCH 5/7] Added a button to render in edit mode. --- .../webparts/siteAdmin/SiteAdminWebPart.ts | 265 ++++++++++-------- 1 file changed, 150 insertions(+), 115 deletions(-) diff --git a/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts b/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts index f0a6aa4..c2a0199 100644 --- a/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts +++ b/spfx/src/webparts/siteAdmin/SiteAdminWebPart.ts @@ -1,4 +1,4 @@ -import { Version } from '@microsoft/sp-core-library'; +import { DisplayMode, Version } from '@microsoft/sp-core-library'; import { IPropertyPaneConfiguration, IPropertyPaneGroup, PropertyPaneButton, PropertyPaneDropdown, PropertyPaneHorizontalRule, @@ -237,127 +237,38 @@ declare const SiteAdmin: { }; export default class SiteAdminWebPart extends BaseClientSideWebPart { + private _hasRendered: boolean = false; + public render(): void { - const propValues = (this.properties as any); - const siteProps: { - [key: string]: { - description: string; - disabled: boolean; - label: string; - } - } = {}; - const webProps: { - [key: string]: { - description: string; - disabled: boolean; - label: string; - } - } = {}; + // Do nothing if we have already rendered the webpart + if (this._hasRendered) { return; } - // Parse the site properties - for (let i = 0; i < this._siteProps.length; i++) { - const propName = this._siteProps[i]; - const key = propName.replace("SiteProp", ""); + // See if the page is in edit mode + if (this.displayMode === DisplayMode.Edit) { + // Don't render the application + this.domElement.classList.add("bs"); + this.domElement.innerHTML = ` +

The page is in edit mode and will not render the application.

+ + `; + this.domElement.querySelector("input")?.addEventListener("click", () => { + // Open the property pane + this.context.propertyPane.open(); + }); - // Add the property - siteProps[key] = { - description: propValues[propName + "Description"], - disabled: propValues[propName], - label: propValues[propName + "Label"] - }; + // Set the flag + this._hasRendered = true; + return; } - // Parse the web properties - for (let i = 0; i < this._webProps.length; i++) { - const propName = this._webProps[i]; - const key = propName.replace("WebProp", ""); - - // Add the property - webProps[key] = { - description: propValues[propName + "Description"], - disabled: propValues[propName], - label: propValues[propName + "Label"] - }; - } + // Render the app + if (!this._hasRendered) { + // Render the app + this.renderApp(); - // Determine the size - let maxStorageSize = this.properties.MaxStorage; - if (maxStorageSize) { - // See if this is in TB - if (maxStorageSize.toString().indexOf("TB") > 0) { - // Remove the TB and convert to a number - maxStorageSize = parseInt(maxStorageSize.toString().replace("TB", "")); - } - // Else, see if this is in GB - else if (maxStorageSize.toString().indexOf("GB") > 0) { - // Remove the GB, and convert to a number in TB - maxStorageSize = parseInt(maxStorageSize.toString().replace("GB", "")) / 1000; - } + // Set the flag + this._hasRendered = true; } - - // Render the solution - SiteAdmin.render({ - auditOnly: this.properties.AuditOnly, - context: this.context, - el: this.domElement, - disableSensitivityLabelOverride: this.properties.DisableSensitivityLabelOverride ? true : false, - hideCreateSiteBtn: this.properties.HideCreateSiteBtn ? true : false, - hideLoadAdminOwnerBtn: this.properties.HideLoadAdminOwnerBtn ? true : false, - hideLoadOneDriveBtn: this.properties.HideLoadOneDriveBtn ? true : false, - hideReports: { - dlp: this.properties.HideReportDLP ? true : false, - docRetention: this.properties.HideReportDocRetention ? true : false, - externalShares: this.properties.HideReportExternalShares ? true : false, - externalUsers: this.properties.HideReportExternalUsers ? true : false, - permissions: this.properties.HideReportPermissions ? true : false, - retention: this.properties.HideReportRetention ? true : false, - searchAgents: this.properties.HideReportSearchAgents ? true : false, - searchDocs: this.properties.HideReportSearchDocs ? true : false, - searchEEEU: this.properties.HideReportSearchEEEU ? true : false, - searchProp: this.properties.HideReportSearchProp ? true : false, - searchUsers: this.properties.HideReportSearchUsers ? true : false, - sensitivityLabels: this.properties.HideReportSensitivityLabels ? true : false, - sharingLinks: this.properties.HideReportSharingLinks ? true : false, - uniquePermissions: this.properties.HideReportUniquePermissions ? true : false - }, - hideTabs: { - appPermissions: this.properties.HideAppPermissionsTab ? true : false, - auditTools: this.properties.HideAuditToolsTab ? true : false, - features: this.properties.HideFeaturesTab ? true : false, - lists: this.properties.HideListsTab ? true : false, - management: this.properties.HideManagementTab ? true : false, - search: this.properties.HideSearchTab ? true : false, - webs: this.properties.HideWebsTab ? true : false - }, - imageReferences, - maxBatchSize: this.properties.MaxBatchSize, - maxRequests: this.properties.MaxRequests, - maxStorageDesc: this.properties.MaxStorageDescription, - maxStorageSize, - reportProps: { - dlpFileExt: this.properties.ReportsDLPFileExt, - docRententionYears: this.properties.ReportsDocRententionYears, - docSearchFileExt: this.properties.ReportsDocSearchFileExt, - docSearchKeywords: this.properties.ReportsDocSearchKeywords, - docSearchRegexPatterns: this.properties.ReportsDocSearchRegexPatterns, - oversharedGroups: (this.properties.ReportsOversharedGroups || "").split(",").map(group => group.trim()), - sensitivityLabelFileExt: this.properties.SensitivityLabelFileExt - }, - searchProps: { - description: this.properties.WebPropSearchPropertyDescription, - key: this.properties.WebPropSearchPropertyKey, - label: this.properties.WebPropSearchPropertyLabel, - managedProperty: this.properties.WebPropSearchPropertyManagedProperty, - reportName: this.properties.WebPropSearchPropertyReportName, - tabName: this.properties.WebPropSearchPropertyTabName, - values: this.properties.WebPropSearchPropertyValues - }, - siteAttestation: this.properties.SiteAttestation, - siteAttestationText: this.properties.SiteAttestationText, - siteProps, - title: this.properties.AppTitle, - webProps - }); } protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void { @@ -906,4 +817,128 @@ export default class SiteAdminWebPart extends BaseClientSideWebPart 0) { + // Remove the TB and convert to a number + maxStorageSize = parseInt(maxStorageSize.toString().replace("TB", "")); + } + // Else, see if this is in GB + else if (maxStorageSize.toString().indexOf("GB") > 0) { + // Remove the GB, and convert to a number in TB + maxStorageSize = parseInt(maxStorageSize.toString().replace("GB", "")) / 1000; + } + } + + // Render the solution + SiteAdmin.render({ + auditOnly: this.properties.AuditOnly, + context: this.context, + el: this.domElement, + disableSensitivityLabelOverride: this.properties.DisableSensitivityLabelOverride ? true : false, + hideCreateSiteBtn: this.properties.HideCreateSiteBtn ? true : false, + hideLoadAdminOwnerBtn: this.properties.HideLoadAdminOwnerBtn ? true : false, + hideLoadOneDriveBtn: this.properties.HideLoadOneDriveBtn ? true : false, + hideReports: { + dlp: this.properties.HideReportDLP ? true : false, + docRetention: this.properties.HideReportDocRetention ? true : false, + externalShares: this.properties.HideReportExternalShares ? true : false, + externalUsers: this.properties.HideReportExternalUsers ? true : false, + permissions: this.properties.HideReportPermissions ? true : false, + retention: this.properties.HideReportRetention ? true : false, + searchAgents: this.properties.HideReportSearchAgents ? true : false, + searchDocs: this.properties.HideReportSearchDocs ? true : false, + searchEEEU: this.properties.HideReportSearchEEEU ? true : false, + searchProp: this.properties.HideReportSearchProp ? true : false, + searchUsers: this.properties.HideReportSearchUsers ? true : false, + sensitivityLabels: this.properties.HideReportSensitivityLabels ? true : false, + sharingLinks: this.properties.HideReportSharingLinks ? true : false, + uniquePermissions: this.properties.HideReportUniquePermissions ? true : false + }, + hideTabs: { + appPermissions: this.properties.HideAppPermissionsTab ? true : false, + auditTools: this.properties.HideAuditToolsTab ? true : false, + features: this.properties.HideFeaturesTab ? true : false, + lists: this.properties.HideListsTab ? true : false, + management: this.properties.HideManagementTab ? true : false, + search: this.properties.HideSearchTab ? true : false, + webs: this.properties.HideWebsTab ? true : false + }, + imageReferences, + maxBatchSize: this.properties.MaxBatchSize, + maxRequests: this.properties.MaxRequests, + maxStorageDesc: this.properties.MaxStorageDescription, + maxStorageSize, + reportProps: { + dlpFileExt: this.properties.ReportsDLPFileExt, + docRententionYears: this.properties.ReportsDocRententionYears, + docSearchFileExt: this.properties.ReportsDocSearchFileExt, + docSearchKeywords: this.properties.ReportsDocSearchKeywords, + docSearchRegexPatterns: this.properties.ReportsDocSearchRegexPatterns, + oversharedGroups: (this.properties.ReportsOversharedGroups || "").split(",").map(group => group.trim()), + sensitivityLabelFileExt: this.properties.SensitivityLabelFileExt + }, + searchProps: { + description: this.properties.WebPropSearchPropertyDescription, + key: this.properties.WebPropSearchPropertyKey, + label: this.properties.WebPropSearchPropertyLabel, + managedProperty: this.properties.WebPropSearchPropertyManagedProperty, + reportName: this.properties.WebPropSearchPropertyReportName, + tabName: this.properties.WebPropSearchPropertyTabName, + values: this.properties.WebPropSearchPropertyValues + }, + siteAttestation: this.properties.SiteAttestation, + siteAttestationText: this.properties.SiteAttestationText, + siteProps, + title: this.properties.AppTitle, + webProps + }); + } } From 5c4b019f899e85048207e460abe6385f7432c4e4 Mon Sep 17 00:00:00 2001 From: Gunjan Datta Date: Wed, 22 Jul 2026 18:12:30 -0400 Subject: [PATCH 6/7] Updated the long running methods to ensure the _stopFl is returned to stop REST api calls. --- src/reports/searchAgents.ts | 3 +++ src/reports/searchDocs.ts | 3 +++ src/reports/searchEEEU.ts | 2 +- src/reports/sensitivityLabels.ts | 2 +- src/reports/uniquePermissions.ts | 2 +- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/reports/searchAgents.ts b/src/reports/searchAgents.ts index 91b6dde..7ebe0c5 100644 --- a/src/reports/searchAgents.ts +++ b/src/reports/searchAgents.ts @@ -61,6 +61,9 @@ export class SearchAgents { WebUrl: web.Url }); } + + // Return the stop flag + return this._stopFl; }) }).then(() => { resolve(); }); }); diff --git a/src/reports/searchDocs.ts b/src/reports/searchDocs.ts index ba999ca..4242451 100644 --- a/src/reports/searchDocs.ts +++ b/src/reports/searchDocs.ts @@ -396,6 +396,9 @@ export class SearchDocs { // Ensure the process is running worker.start(); + + // Return the stop floag + return this._stopFl; }).then(() => { // Set the flag allFilesLoaded = true; diff --git a/src/reports/searchEEEU.ts b/src/reports/searchEEEU.ts index 089683d..fa6ccc5 100644 --- a/src/reports/searchEEEU.ts +++ b/src/reports/searchEEEU.ts @@ -79,7 +79,7 @@ export class SearchEEEU { this._elSubNav.children[1].innerHTML = `Creating Batch Requests - Processed ${++itemCounter} items...`; // See if this item doesn't have unique permissions - if (!item.HasUniqueRoleAssignments) { return; } + if (!item.HasUniqueRoleAssignments) { return this._stopFl; } // Get the permissions batch.Items(item.Id).RoleAssignments().query({ diff --git a/src/reports/sensitivityLabels.ts b/src/reports/sensitivityLabels.ts index f0db1bd..e39ccf1 100644 --- a/src/reports/sensitivityLabels.ts +++ b/src/reports/sensitivityLabels.ts @@ -95,7 +95,7 @@ export class SensitivityLabels { // See if we are filter for a label if (this._filterLabels.length > 0) { // See if this is a target label - if (this._filterLabels.indexOf(file.sensitivityLabel.id) < 0) { return; } + if (this._filterLabels.indexOf(file.sensitivityLabel.id) < 0) { return this._stopFl; } } let fileInfo = file.name.split('.'); diff --git a/src/reports/uniquePermissions.ts b/src/reports/uniquePermissions.ts index 859991d..8106fc4 100644 --- a/src/reports/uniquePermissions.ts +++ b/src/reports/uniquePermissions.ts @@ -71,7 +71,7 @@ export class UniquePermissions { this._elSubNav.children[1].innerHTML = `Creating Batch Requests - Processed ${++itemCounter} items...`; // See if this item doesn't have unique permissions - if (!item.HasUniqueRoleAssignments) { return; } + if (!item.HasUniqueRoleAssignments) { return this._stopFl; } // Get the permissions batch.Items(item.Id).RoleAssignments().query({ From 8ec1e00895b84daa2bb532fb92d35861612f64bc Mon Sep 17 00:00:00 2001 From: Gunjan Datta Date: Wed, 22 Jul 2026 18:13:47 -0400 Subject: [PATCH 7/7] Updated to remove dev reference. --- spfx/config/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spfx/config/config.json b/spfx/config/config.json index 8934d92..a7d7af3 100644 --- a/spfx/config/config.json +++ b/spfx/config/config.json @@ -14,7 +14,7 @@ "externals": { "main-lib": { "globalName": "main-lib", - "path": "../dist/site-admin.js" + "path": "../dist/site-admin.min.js" } }, "localizedResources": {