# 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.ps1` lines 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.ps1` multiple 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.ps1` dedicated 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.ps1` PnP 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:** 1. User selects site(s) and report options in GUI (Permissions tab) 2. Click "Générer le rapport" triggers event handler at line 4068+ 3. Validation via `Validate-Inputs` (line 30) 4. GUI triggers runspace via `Start-Job` with user parameters 5. Runspace calls `Generate-PnPSitePermissionRpt` (line 1852) 6. `Generate-PnPSitePermissionRpt` connects to SharePoint via `Connect-PnPOnline` (line 1864) 7. Recursive permission scanning: - `Get-PnPWebPermission` (line 1944) for site/webs - `Get-PnPListPermission` (line 1912) for lists and libraries - `Get-PnPFolderPermission` (line 1882) for folders (if enabled) - `Get-PnPPermissions` (line 1786) extracts individual role assignments 8. Results accumulated in `$script:AllPermissions` array 9. Export based on format choice: - CSV: `Merge-PermissionRows` (line 1363) then `Export-Csv` - HTML: `Export-PermissionsToHTML` (line 1389) generates interactive report 10. Output file path returned to UI via synchronized hashtable 11. User can open report via `btnPermOpen` click handler **Storage Metrics Scan:** 1. User selects storage options and sites 2. Click "Générer les métriques" triggers runspace job 3. Job calls `Get-SiteStorageMetrics` (line 2004) 4. Per-site or per-library scanning: - Connect to web via `Connect-PnPOnline` - `Get-PnPList` retrieves document libraries (if per-library mode) - `Get-PnPFolderStorageMetric` for library/root metrics - `Collect-FolderStorage` (recursive nested function) walks folder tree to configured depth 5. Results accumulate in `$script:storageResults` with hierarchy intact 6. HTML or CSV export writes report file 7. File path communicated back to UI **Site Picker (Browse Sites):** 1. User clicks "Voir les sites" button 2. `Show-SitePicker` dialog opens (line 212) 3. User clicks "Charger les sites" button 4. Dialog initializes `$script:_pkl` state hashtable (line 315) 5. Runspace spawned in `btnLoad.Add_Click` (line 395) 6. Runspace connects to admin site and retrieves all sites via `Get-PnPTenantSite` 7. Results queued back to UI via synchronized `$script:_pkl.Sync` hashtable 8. Timer polls `$script:_pkl.Sync` and updates ListView asynchronously 9. User filters by text, sorts columns, checks/unchecks sites 10. Returns selected site URLs in `$script:SelectedSites` array **File Search:** 1. User enters search criteria (extensions, regex, date ranges, etc.) 2. Click "Lancer la recherche" triggers runspace 3. Runspace uses PnP Search API (KQL) with filters: - File extension filters via `fileExtension:ext1` OR syntax - Date range filters via `Created >= date` - Regex applied client-side after retrieval 4. Results paginated and accumulated 5. Exported to CSV or HTML with interactive filtering/sorting **Duplicate Detection:** 1. User chooses file or folder mode and comparison criteria 2. Click "Lancer le scan" triggers runspace 3. File duplicates: Search API with filename-based grouping 4. Folder duplicates: Enumerate all folders, compare attributes (size, dates, subfolder/file counts) 5. Results grouped by match criteria 6. HTML export shows grouped duplicates with visual indicators (green/orange for matching/differing fields) **Template Capture & Apply:** 1. Capture mode: `Show-TemplateManager` dialog (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` 2. 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 **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-SitePicker` and `Show-TemplateManager` create self-contained `Form` objects - State stored in `$script:_pkl` and `$script:_tpl` respectively - 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:LangDict` hashtable - Source: `lang/fr.json` contains 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-clipboard - `Export-StorageToHTML` (line 1621): Tree visualization, sorting, filtering - `Export-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.ps1` line 2992 - Triggers: Script execution via `.ps1` file 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 generation - `btnStorRun.Add_Click` → Storage metrics scan - `btnSearchRun.Add_Click` → File search - `btnDupRun.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:** 1. **Runspace Error Handling:** ```powershell try { $result = Get-PnPList } catch { Write-Log "Error: $($_.Exception.Message)" "Red" } ``` 2. **Connection Validation:** - `Validate-Inputs` (line 30) checks required fields before operation - `Connect-PnPOnline` fails if credentials invalid; caught and logged 3. **File I/O Protection:** ```powershell if (Test-Path $path) { try { $data = Get-Content $path -Raw | ConvertFrom-Json } catch {} # Silently ignore JSON parse errors } ``` 4. **UI Update Safety:** - `Write-Log` checks `if ($script:LogBox -and !$script:LogBox.IsDisposed)` before updating - Prevents access to disposed UI objects after form close 5. **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-Log` function (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.State` and 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*