303 lines
13 KiB
Markdown
303 lines
13 KiB
Markdown
# 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*
|