13 KiB
Architecture
Analysis Date: 2026-04-02
Pattern Overview
Overall: Monolithic PowerShell Application with WinForms UI and Async Runspace Pattern
Key Characteristics:
- Single-file PowerShell script (6408 lines) serving as entry point
- Native WinForms GUI (no external UI framework dependencies)
- Asynchronous operations via dedicated PowerShell runspaces to prevent UI blocking
- Hashtable-based state management for inter-runspace communication
- PnP.PowerShell module for all SharePoint Online interactions
- Profile and template persistence via JSON files
- Region-based code organization for logical grouping
Layers
Presentation Layer (GUI):
- Purpose: User interface and interaction handling
- Location:
Sharepoint_ToolBox.ps1lines 2990-3844 (GUI setup) + event handlers - Contains: WinForms controls, dialogs, input validation, visual updates
- Depends on: Shared helpers, Settings layer
- Used by: Event handlers, runspace callbacks via synchronized hashtable
Application Layer (Business Logic):
- Purpose: Core operations for each feature (permissions, storage, templates, search, duplicates)
- Location:
Sharepoint_ToolBox.ps1multiple regions:- Permissions: lines 1784-2001
- Storage: lines 2002-2110
- File Search: lines 2112-2233
- Duplicates: lines 2235-2408
- Templates: lines 475-1360
- Transfer/Bulk: lines 2410-3000
- Contains: PnP API calls, data aggregation, report generation
- Depends on: PnP.PowerShell module, Presentation feedback
- Used by: Event handlers via runspaces, HTML/CSV export functions
Data Access Layer:
- Purpose: File I/O, persistence, caching
- Location:
Sharepoint_ToolBox.ps1dedicated regions:- Profile Management: lines 48-127
- Settings: lines 129-154
- Template Management: lines 475-533
- Contains: JSON serialization/deserialization, profile CRUD, settings management
- Depends on: File system access
- Used by: Application layer, GUI initialization
Export & Reporting Layer:
- Purpose: Transform data to CSV and interactive HTML
- Location:
Sharepoint_ToolBox.ps1:- Permissions HTML: lines 1361-1617
- Storage HTML: lines 1619-1784
- Search HTML: lines 2112-2233
- Duplicates HTML: lines 2235-2408
- Transfer HTML: lines 2412-2547
- Contains: HTML template generation, JavaScript for interactivity, CSV formatting
- Depends on: Application layer data, System.Drawing for styling
- Used by: Feature implementations for export operations
Integration Layer:
- Purpose: External service communication (SharePoint, PnP.PowerShell)
- Location:
Sharepoint_ToolBox.ps1PnP function regions - Contains: Connect-PnPOnline, Get-PnP* cmdlets, authentication handling
- Depends on: PnP.PowerShell module, credentials from user input
- Used by: Application layer operations
Utilities & Helpers:
- Purpose: Cross-cutting formatting, UI helpers, internationalization
- Location:
Sharepoint_ToolBox.ps1:- Shared Helpers: lines 4-46
- Internationalization: lines 2732-2989
- UI Control Factories: lines 3119-3146
- Contains: Write-Log, Format-Bytes, EscHtml, T() translator, control builders
- Depends on: System.Windows.Forms, language JSON file
- Used by: All other layers
Data Flow
Permissions Report Generation:
- User selects site(s) and report options in GUI (Permissions tab)
- Click "Générer le rapport" triggers event handler at line 4068+
- Validation via
Validate-Inputs(line 30) - GUI triggers runspace via
Start-Jobwith user parameters - Runspace calls
Generate-PnPSitePermissionRpt(line 1852) Generate-PnPSitePermissionRptconnects to SharePoint viaConnect-PnPOnline(line 1864)- Recursive permission scanning:
Get-PnPWebPermission(line 1944) for site/websGet-PnPListPermission(line 1912) for lists and librariesGet-PnPFolderPermission(line 1882) for folders (if enabled)Get-PnPPermissions(line 1786) extracts individual role assignments
- Results accumulated in
$script:AllPermissionsarray - Export based on format choice:
- CSV:
Merge-PermissionRows(line 1363) thenExport-Csv - HTML:
Export-PermissionsToHTML(line 1389) generates interactive report
- CSV:
- Output file path returned to UI via synchronized hashtable
- User can open report via
btnPermOpenclick handler
Storage Metrics Scan:
- User selects storage options and sites
- Click "Générer les métriques" triggers runspace job
- Job calls
Get-SiteStorageMetrics(line 2004) - Per-site or per-library scanning:
- Connect to web via
Connect-PnPOnline Get-PnPListretrieves document libraries (if per-library mode)Get-PnPFolderStorageMetricfor library/root metricsCollect-FolderStorage(recursive nested function) walks folder tree to configured depth
- Connect to web via
- Results accumulate in
$script:storageResultswith hierarchy intact - HTML or CSV export writes report file
- File path communicated back to UI
Site Picker (Browse Sites):
- User clicks "Voir les sites" button
Show-SitePickerdialog opens (line 212)- User clicks "Charger les sites" button
- Dialog initializes
$script:_pklstate hashtable (line 315) - Runspace spawned in
btnLoad.Add_Click(line 395) - Runspace connects to admin site and retrieves all sites via
Get-PnPTenantSite - Results queued back to UI via synchronized
$script:_pkl.Synchashtable - Timer polls
$script:_pkl.Syncand updates ListView asynchronously - User filters by text, sorts columns, checks/unchecks sites
- Returns selected site URLs in
$script:SelectedSitesarray
File Search:
- User enters search criteria (extensions, regex, date ranges, etc.)
- Click "Lancer la recherche" triggers runspace
- Runspace uses PnP Search API (KQL) with filters:
- File extension filters via
fileExtension:ext1OR syntax - Date range filters via
Created >= date - Regex applied client-side after retrieval
- File extension filters via
- Results paginated and accumulated
- Exported to CSV or HTML with interactive filtering/sorting
Duplicate Detection:
- User chooses file or folder mode and comparison criteria
- Click "Lancer le scan" triggers runspace
- File duplicates: Search API with filename-based grouping
- Folder duplicates: Enumerate all folders, compare attributes (size, dates, subfolder/file counts)
- Results grouped by match criteria
- HTML export shows grouped duplicates with visual indicators (green/orange for matching/differing fields)
Template Capture & Apply:
- Capture mode:
Show-TemplateManagerdialog (line 542)- User selects "Capture from site"
- Runspace scans site structure via
Get-PnPList,Get-PnPFolderItem,Get-PnPWebPermission - Captures libraries, folders, permission groups, site logo, title
- Persisted to
Sharepoint_Templates.json
- Apply mode: User selects template and target site
- Runspace creates lists/libraries via
New-PnPList - Replicates folder structure via
New-PnPFolder - Applies permission groups if selected
- Logs creation results
- Runspace creates lists/libraries via
State Management:
$script:variables hold state across runspace calls (profiles, sites, results, settings)- Synchronized hashtables (
$script:_pkl,$script:_sync) enable runspace-to-UI communication - Timer at line 3850-3870 polls synchronized hashtable and updates GUI with progress/results
- Event handlers trigger jobs but don't block waiting for completion (asynchronous pattern)
Key Abstractions
Runspace Encapsulation:
- Purpose: Execute long-running SharePoint operations without freezing GUI
- Pattern:
$job = Start-Job -ScriptBlock { ... } -RunspacePool $rsPool - Example:
Start-NextStorageScan(line 4536) manages storage scan runspace jobs - Trade-off: Requires careful state management via shared hashtables; no direct closures
Hashtable-Based State:
- Purpose: Share mutable state between main runspace and job runspaces
- Pattern:
$sync = @{ Data = @(); Status = "Running" }passed to job - Example:
$script:_pkl(line 315) manages site picker state across checkbox events - Benefit: Avoids closure complexity; timer can poll changes safely
Dialog Modal Isolation:
- Purpose: Site picker and template manager as isolated UI contexts
- Pattern:
Show-SitePickerandShow-TemplateManagercreate self-containedFormobjects - State stored in
$script:_pkland$script:_tplrespectively - Returns result arrays (selected sites, template data) to main form
Language Translation System:
- Purpose: Internationalization without external dependencies
- Pattern:
T("key")function (line 2908) looks up keys in$script:LangDicthashtable - Source:
lang/fr.jsoncontains French translations; English is hardcoded - Used throughout: All UI labels, buttons, messages use
T()for localization
HTML Export Templates:
- Purpose: Dynamically generate interactive HTML reports with embedded JavaScript
- Pattern: String templates with
@"heredoc syntax containing HTML/CSS/JS - Examples:
Export-PermissionsToHTML(line 1389): Responsive table, collapsible groups, copy-to-clipboardExport-StorageToHTML(line 1621): Tree visualization, sorting, filteringExport-DuplicatesToHTML(line 2235): Grouped duplicates with visual indicators
- Benefit: No external libraries; reports are self-contained single-file HTML
Entry Points
Main GUI Form:
- Location:
Sharepoint_ToolBox.ps1line 2992 - Triggers: Script execution via
.ps1file or PowerShell IDE - Responsibilities:
- Initialize WinForms components (form, controls, menus)
- Load and populate profiles/settings from JSON
- Register event handlers for all buttons and controls
- Run main event loop
[void]$form.ShowDialog()
Feature Event Handlers:
- Location: Various in lines 4068+ (Event Handlers region)
- Examples:
btnPermRun.Add_Click→ Permissions report generationbtnStorRun.Add_Click→ Storage metrics scanbtnSearchRun.Add_Click→ File searchbtnDupRun.Add_Click→ Duplicate detection
- Pattern: Validate inputs, start runspace job, launch progress animation, register cleanup callback
Background Runspaces:
- Entry:
Start-Job -ScriptBlock { Generate-PnPSitePermissionRpt ... } - Execution: PnP cmdlets execute within runspace's isolated context
- Completion: Job completion callback writes results to synchronized hashtable; timer detects and updates UI
Language Switch:
- Location: Menu → Language submenu (line 3011+)
- Handler:
Switch-AppLanguage(line 4167) - Updates: All UI labels via
Update-UILanguage(line 2951)
Error Handling
Strategy: Try/Catch with graceful degradation; errors logged to UI RichTextBox
Patterns:
-
Runspace Error Handling:
try { $result = Get-PnPList } catch { Write-Log "Error: $($_.Exception.Message)" "Red" } -
Connection Validation:
Validate-Inputs(line 30) checks required fields before operationConnect-PnPOnlinefails if credentials invalid; caught and logged
-
File I/O Protection:
if (Test-Path $path) { try { $data = Get-Content $path -Raw | ConvertFrom-Json } catch {} # Silently ignore JSON parse errors } -
UI Update Safety:
Write-Logchecksif ($script:LogBox -and !$script:LogBox.IsDisposed)before updating- Prevents access to disposed UI objects after form close
-
Missing Configuration Handling:
- Settings default to English + current directory if file missing
- Profiles default to empty array if file missing
- Templates default to empty if file corrupted
Cross-Cutting Concerns
Logging:
- Framework:
Write-Logfunction (line 6) - Pattern: Writes colored messages to RichTextBox + host console
- Usage: All operations log status (connecting, scanning, exporting)
- Timestamps:
Get-Date -Format 'HH:mm:ss'prefixes each message
Validation:
- Entry point:
Validate-Inputs(line 30) checks ClientID and Site URL - Pattern: Early return if missing; user sees MessageBox with missing field hint
- Localization: Error messages use
T()function for i18n
Authentication:
- Method: Interactive browser login via
Connect-PnPOnline -Interactive - Pattern: PnP module opens browser for Azure AD consent; token cached within session
- Credential scope: Per site connection; multiple connections supported (for multi-site operations)
- Token management: Automatic via PnP.PowerShell; no manual handling
Asynchronous Progress:
- Animation:
Start-ProgressAnim(line 3845) flashes "Running..." in status label - Polling: Timer at line 3850-3870 checks
$job.Stateand synchronized hashtable every 300ms - Cleanup:
Stop-ProgressAnim(line 3850) stops animation when job completes
UI Responsiveness:
- Pattern:
[System.Windows.Forms.Application]::DoEvents()called during long operations - Benefit: Allows UI events (button clicks, close) to process while waiting
- Cost: Runspace jobs recommended for truly long operations (>5 second operations)
Architecture analysis: 2026-04-02