197 Commits
v2.0 ... 2.3

Author SHA1 Message Date
Dev
5ed2f801af Merge branch 'main' of https://git.azuze.fr/kawa/Sharepoint-Toolbox 2026-04-09 16:48:32 +02:00
Dev
d8d25b967d Merge branch 'main' of https://git.azuze.fr/kawa/Sharepoint-Toolbox 2026-04-09 16:48:28 +02:00
d41ff78e21 Delete Sharepoint_Settings.json 2026-04-09 16:47:44 +02:00
Dev
7af9bf2d5e Merge branch 'main' of https://git.azuze.fr/kawa/Sharepoint-Toolbox 2026-04-09 16:46:44 +02:00
Dev
baa3c7562d chore: prepare for v2.0 release
- Remove bin/obj/publish from git tracking
- Update .gitignore for .NET project (source only)
- Add release.ps1 local publish script (replaces Gitea workflow)
- Remove .gitea/workflows/release.yml
- Fix duplicate group names to show library names
- Fix HTML export to show Name column in duplicates report
- Fix consolidated permissions HTML to show folder/library names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:46:37 +02:00
Dev
f41172c398 chore: prepare for v2.0 release
- Remove bin/obj/publish from git tracking
- Update .gitignore for .NET project (source only)
- Add release.ps1 local publish script (replaces Gitea workflow)
- Remove .gitea/workflows/release.yml
- Fix duplicate group names to show library names
- Fix HTML export to show Name column in duplicates report
- Fix consolidated permissions HTML to show folder/library names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:42:12 +02:00
Dev
10e5ae9125 docs(phase-19): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:23:58 +02:00
Dev
5d0b5cf85e docs(19-02): complete register/remove app UI plan
- 19-02-SUMMARY.md created
- STATE.md: progress 100%, decisions, session updated
- ROADMAP.md: phase 19 marked complete
- REQUIREMENTS.md: APPREG-01, APPREG-04, APPREG-05 marked complete
2026-04-09 15:20:55 +02:00
Dev
809ac8613b feat(19-02): add app registration UI to profile dialog and 7 ViewModel tests
- ProfileManagementDialog.xaml: height 750, new Row 4 with Register/Remove buttons
- BooleanToVisibilityConverter added to Window.Resources
- Fallback instructions panel bound to ShowFallbackInstructions
- RegistrationStatus text block with StringToVisibilityConverter
- Buttons row shifted to Row 5
- ProfileManagementViewModelRegistrationTests: 7 unit tests, all passing
- ProfileManagementViewModelLogoTests: updated to 5-param constructor
2026-04-09 15:19:37 +02:00
Dev
42b5eda460 feat(19-02): add RegisterApp/RemoveApp commands, DI wiring, EN/FR localization
- ProfileManagementViewModel: IAppRegistrationService injected, RegisterAppCommand/RemoveAppCommand added
- IsRegistering, ShowFallbackInstructions, RegistrationStatus observable properties
- HasRegisteredApp computed property, CanRegisterApp/CanRemoveApp guards
- RegisterAppAsync: admin check, fallback panel, AppId persistence
- RemoveAppAsync: removal + MSAL clear + AppId null + persistence
- App.xaml.cs: IAppRegistrationService singleton registered
- Strings.resx/fr.resx: 16 new localization keys for register/remove/fallback flow
2026-04-09 15:17:53 +02:00
Dev
69c9d77be3 docs(19-01): complete AppRegistrationService plan execution
- 19-01-SUMMARY.md: service layer implementation with rollback pattern
- STATE.md: progress 98%, decisions added, session updated
- ROADMAP.md: phase 19 in-progress (1/2 plans)
- REQUIREMENTS.md: APPREG-02, APPREG-03, APPREG-06 marked complete
2026-04-09 15:15:16 +02:00
Dev
8083cdf7f5 test(19-01): add unit tests for AppRegistrationService and models
- AppRegistrationResult factory methods (Success/Failure/FallbackRequired)
- TenantProfile.AppId null default and JSON round-trip
- AppRegistrationService implements IAppRegistrationService
- BuildRequiredResourceAccess structure (2 resources, 4+1 scopes, all Scope type)
2026-04-09 15:14:02 +02:00
Dev
93dbb8c5b0 feat(19-01): add AppRegistrationService with rollback, model, and interface
- AppRegistrationResult discriminated result (Success/Failure/FallbackRequired)
- TenantProfile.AppId nullable string for storing registered app ID
- IAppRegistrationService interface (IsGlobalAdminAsync, RegisterAsync, RemoveAsync, ClearMsalSessionAsync)
- AppRegistrationService: sequential registration with rollback, transitiveMemberOf admin check, MSAL eviction
2026-04-09 15:12:51 +02:00
Dev
7d200ecf3f docs(19): create phase plan for app registration and removal
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:48:49 +02:00
Dev
0d087ae4cd docs(phase-19): add research and validation strategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:44:07 +02:00
Dev
bb3ba7b177 docs(phase-19): research app registration & removal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:43:00 +02:00
Dev
9549314f22 docs(phase-18): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:37:18 +02:00
Dev
04a5b267b7 docs(18-02): complete scan-loop elevation plan
- 18-02-SUMMARY.md: elevation logic, DataGrid visual, 8 new tests
- STATE.md: position advanced, decisions recorded, session updated
- ROADMAP.md: phase 18 marked complete (2/2 summaries)
- REQUIREMENTS.md: OWN-02 marked complete
2026-04-09 14:34:34 +02:00
Dev
2302cad531 feat(18-02): DataGrid visual differentiation + localization for elevated rows
- Add WasAutoElevated DataTrigger to DataGrid.RowStyle: amber background + tooltip
- Add warning icon (U+26A0) indicator column (width 24) before Object Type column
- Icon shown via DataTrigger on WasAutoElevated, hidden by default
- Add permissions.elevated.tooltip EN key to Strings.resx
- Add permissions.elevated.tooltip FR key to Strings.fr.resx
2026-04-09 14:33:00 +02:00
Dev
6270fe4605 feat(18-02): scan-loop elevation logic + PermissionsViewModel wiring + tests
- Add _settingsService and _ownershipService fields to PermissionsViewModel
- Add SettingsService? and IOwnershipElevationService? to both constructors
- Add DeriveAdminUrl internal static helper for admin URL derivation
- Add IsAccessDenied helper catching ServerUnauthorizedAccessException + WebException 403
- Add IsAutoTakeOwnershipEnabled async helper reading toggle from SettingsService
- Refactor RunOperationAsync with try/catch elevation pattern (read toggle before loop)
- Tag elevated entries with WasAutoElevated=true via record with expression
- Add PermissionsViewModelOwnershipTests (8 tests): toggle OFF propagates, toggle ON elevates+retries, no elevation on success, WasAutoElevated tagging, elevation throw propagates, DeriveAdminUrl theory
2026-04-09 14:31:58 +02:00
Dev
11e835f586 docs(18-01): complete auto-take-ownership settings foundation plan
- 18-01-SUMMARY.md: plan execution summary
- STATE.md: progress updated to 98%, decisions recorded, stopped-at updated
- ROADMAP.md: phase 18 marked in-progress (1/2 summaries)
- REQUIREMENTS.md: OWN-01 marked complete
2026-04-09 14:25:47 +02:00
Dev
20948e4bac feat(18-01): SettingsView ownership checkbox + EN/FR localization keys
- SettingsView.xaml: Auto-Take Ownership section with CheckBox bound to AutoTakeOwnership
- Strings.resx: settings.ownership.title/auto/description keys (EN)
- Strings.fr.resx: matching French translations
2026-04-09 14:24:31 +02:00
Dev
36fb312b5a feat(18-01): models, SettingsService, OwnershipElevationService + tests
- AppSettings.AutoTakeOwnership bool property defaulting to false
- PermissionEntry.WasAutoElevated optional param (default false, last position)
- SettingsService.SetAutoTakeOwnershipAsync persists toggle
- IOwnershipElevationService interface + OwnershipElevationService wrapping Tenant.SetSiteAdmin
- SettingsViewModel.AutoTakeOwnership property loads and persists via SetAutoTakeOwnershipAsync
- DI registration in App.xaml.cs (Phase 18 section)
- 8 new tests: models, persistence, service, viewmodel
2026-04-09 14:23:08 +02:00
Dev
3479fff4c3 docs(18): complete phase research, validation, and plans
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:17:00 +02:00
Dev
dbb59d119b docs(18): create phase plan for auto-take-ownership
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:15:15 +02:00
Dev
997086cf07 docs(phase-17): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:13:46 +02:00
Dev
23ed46e614 docs(17-02): complete group expansion HTML reports plan
- 17-02-SUMMARY.md created
- STATE.md updated: session, decisions, progress
- ROADMAP.md: phase 17 marked complete (2/2 plans)
- REQUIREMENTS.md: RPT-01 marked complete
2026-04-09 13:11:21 +02:00
Dev
aab3aee3df feat(17-02): wire ISharePointGroupResolver into PermissionsViewModel export flow
- Add _groupResolver field (ISharePointGroupResolver?) with constructor injection
- ISharePointGroupResolver added as optional last parameter in main constructor
- ExportHtmlAsync resolves SharePoint group names before calling WriteAsync
- Gracefully handles resolution failure with LogWarning, exports without expansion
- Both WriteAsync call sites pass groupMembers dict (standard and simplified paths)
2026-04-09 13:10:31 +02:00
Dev
07ed6e2515 feat(17-02): extend HtmlExportService with expandable group pills and toggleGroup JS
- Add optional groupMembers parameter to both BuildHtml overloads and WriteAsync methods
- SharePoint group pills render as expandable with onclick toggleGroup when groupMembers provided
- Hidden member sub-rows injected after parent row with resolved member names
- Empty member list renders 'members unavailable' fallback label
- toggleGroup JS function added to inline script block in both overloads
- filterTable updated to skip data-group sub-rows
- CSS for .group-expandable added to both overloads
- Backward compatibility: null groupMembers produces identical output to pre-Phase 17
2026-04-09 13:09:38 +02:00
Dev
c35ee76987 test(17-02): add failing tests for group pill expansion and backward compatibility
- BuildHtml_NoGroupMembers_IdenticalToDefault
- BuildHtml_WithGroupMembers_RendersExpandablePill
- BuildHtml_WithGroupMembers_RendersHiddenMemberSubRow
- BuildHtml_WithEmptyMemberList_RendersMembersUnavailable
- BuildHtml_ContainsToggleGroupJs
- BuildHtml_Simplified_WithGroupMembers_RendersExpandablePill
2026-04-09 13:07:46 +02:00
Dev
7bebbbcc02 docs(17-01): complete SharePointGroupResolver service plan - SUMMARY, STATE, ROADMAP updated 2026-04-09 13:06:16 +02:00
Dev
1aa0d15e9a feat(17-01): register ISharePointGroupResolver in DI container (App.xaml.cs) 2026-04-09 13:05:09 +02:00
Dev
543b863283 feat(17-01): ResolvedMember model, ISharePointGroupResolver interface, SharePointGroupResolver CSOM+Graph implementation
- ResolvedMember record in Core/Models with DisplayName and Login
- ISharePointGroupResolver interface with ResolveGroupsAsync contract
- SharePointGroupResolver: CSOM group user loading + Graph transitive AAD resolution
- Internal static helpers IsAadGroup, ExtractAadGroupId, StripClaims (all green unit tests)
- Graceful error handling: exceptions return empty list per group, never throw
- OrdinalIgnoreCase result dict; lazy Graph client creation on first AAD group
2026-04-09 13:04:56 +02:00
Dev
0f8b1953e1 test(17-01): add failing tests for SharePointGroupResolver static helpers and empty-list contract 2026-04-09 13:03:27 +02:00
Dev
a374a4e1d3 docs(17): create phase plan for group expansion in HTML reports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:59:12 +02:00
Dev
57bfe3e5c1 docs(phase-17): add research and validation strategy 2026-04-09 12:53:15 +02:00
Dev
a2c213b72d docs(phase-17): research group expansion in HTML reports 2026-04-09 12:51:50 +02:00
Dev
ddb1a28a9f docs(phase-16): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:43:12 +02:00
Dev
1ff99f0bb7 docs(16-02): complete consolidated HTML export plan
- SUMMARY.md: BuildConsolidatedHtml with expandable location sub-lists, by-site view suppression, ViewModel wiring
- STATE.md: updated position, decisions, session
- ROADMAP.md: phase 16 marked Complete (2/2 plans with summaries)
2026-04-09 12:40:00 +02:00
Dev
0ebe707aca feat(16-02): implement consolidated HTML rendering path
- Add mergePermissions parameter to BuildHtml and WriteAsync
- Early-return branch calls PermissionConsolidator.Consolidate and delegates to BuildConsolidatedHtml
- BuildConsolidatedHtml: by-user table with Sites column, expandable [N sites] badge with toggleGroup, hidden sub-rows (data-group=locN), inline title for single-location entries
- By-site view and btn-site omitted when mergePermissions=true
- Wire UserAccessAuditViewModel.ExportHtmlAsync to pass MergePermissions
- Fix existing branding test call site to use named parameter
2026-04-09 12:38:19 +02:00
Dev
3d95d2aa8d test(16-02): add failing tests for RPT-03-b through RPT-03-e
- RPT-03-b: mergePermissions=false output identical to default
- RPT-03-c: mergePermissions=true contains Sites column header
- RPT-03-d: 2+ locations produce badge + hidden sub-rows with toggleGroup
- RPT-03-e: mergePermissions=true omits btn-site and view-site
2026-04-09 12:36:35 +02:00
Dev
8979becad2 docs(16-01): complete MergePermissions toggle and consolidated CSV export plan
- 16-01-SUMMARY.md created with all task outcomes and verification results
- STATE.md updated with decisions, session info, progress bar (98%)
- ROADMAP.md updated: phase 16 in-progress (1/2 summaries complete)
- REQUIREMENTS.md: RPT-03 marked complete
2026-04-09 12:35:07 +02:00
Dev
28714fbebc feat(16-01): implement consolidated CSV export path and wire ViewModel call site
- Added mergePermissions=false optional parameter to WriteSingleFileAsync
- Added early-return consolidated branch using PermissionConsolidator.Consolidate
- Consolidated CSV uses distinct header with Locations and LocationCount columns
- Locations column is semicolon-separated site titles for multi-location rows
- Existing non-consolidated code path is completely unchanged
- UserAccessAuditViewModel.ExportCsvAsync now passes MergePermissions to service
2026-04-09 12:33:54 +02:00
Dev
4f7a6e3faa test(16-01): add failing tests for RPT-03-f and RPT-03-g (consolidated CSV export)
- RPT-03-f: mergePermissions=false produces byte-identical output to default call
- RPT-03-g: mergePermissions=true writes consolidated header and merged rows
- Edge case: single-location entry has LocationCount=1 with no semicolons in Locations
2026-04-09 12:32:42 +02:00
Dev
db42047db1 feat(16-01): add Export Options GroupBox with MergePermissions checkbox to both XAML views
- Added Export Options GroupBox after Scan Options in UserAccessAuditView.xaml
- Added Export Options GroupBox after Display Options in PermissionsView.xaml
- Both checkboxes bind to MergePermissions with localized labels via TranslationSource
2026-04-09 12:32:08 +02:00
Dev
ed9f149b82 feat(16-01): add MergePermissions property to both ViewModels and localization keys
- Added [ObservableProperty] _mergePermissions (defaults false) to UserAccessAuditViewModel
- Added [ObservableProperty] _mergePermissions (no-op placeholder) to PermissionsViewModel
- Added audit.grp.export and chk.merge.permissions keys to Strings.resx (EN)
- Added audit.grp.export and chk.merge.permissions keys to Strings.fr.resx (FR)
2026-04-09 12:31:46 +02:00
Dev
720a419788 docs(16-report-consolidation-toggle): create phase plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:19:06 +02:00
Dev
68b123ff6c docs(16): add research and validation strategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:13:30 +02:00
Dev
0336f4341f docs(phase-16): research report consolidation toggle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 12:12:13 +02:00
Dev
8f11699527 docs(16): gather phase context via discuss-phase
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:07:43 +02:00
Dev
9c588a4389 docs(phase-15): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:49:34 +02:00
Dev
fd67ee8b76 docs(15-02): complete PermissionConsolidator unit tests plan
- 9 tests pass covering RPT-04-a through RPT-04-i
- Full solution builds with 0 errors, 321 tests pass
- STATE.md updated, ROADMAP.md phase 15 marked Complete
2026-04-09 11:46:43 +02:00
Dev
7b9f3e17aa test(15-02): add PermissionConsolidatorTests with 9 test cases (RPT-04-a through RPT-04-i)
- RPT-04-a: empty input returns empty list
- RPT-04-b: single entry -> 1 row with 1 location
- RPT-04-c: 3 entries same key -> 1 row with 3 locations
- RPT-04-d: different PermissionLevel -> separate rows
- RPT-04-e: case-insensitive key merges ALICE@ and alice@
- RPT-04-f: MakeKey produces pipe-delimited lowercase format
- RPT-04-g: 11-row input with 3 merge groups -> 7 consolidated rows
- RPT-04-h: LocationCount equals Locations.Count
- RPT-04-i: IsHighPrivilege/IsExternalUser preserved from first entry
2026-04-09 11:45:22 +02:00
Dev
9bfdfb77dd docs(15-01): complete consolidation data model plan
- Add 15-01-SUMMARY.md with task commits, decisions, and next phase readiness
- Update STATE.md with decisions and session position
- Update ROADMAP.md phase 15 progress (1/2 plans complete)
- Mark requirement RPT-04 complete in REQUIREMENTS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:42:47 +02:00
Dev
440b2474e9 feat(15-01): add PermissionConsolidator static helper
- MakeKey builds pipe-delimited case-insensitive key from UserLogin+PermissionLevel+AccessType+GrantedThrough
- Consolidate groups UserAccessEntry list by key, merges into ConsolidatedPermissionEntry rows
- Empty input short-circuits to Array.Empty
- Output ordered by UserLogin then PermissionLevel for deterministic results
2026-04-09 11:41:26 +02:00
Dev
270329bd82 feat(15-01): add LocationInfo and ConsolidatedPermissionEntry model records
- LocationInfo record holds five location fields (SiteUrl, SiteTitle, ObjectTitle, ObjectUrl, ObjectType)
- ConsolidatedPermissionEntry record holds key fields plus IReadOnlyList<LocationInfo> Locations
- LocationCount computed property returns Locations.Count
2026-04-09 11:41:05 +02:00
Dev
f5b3f08f88 docs(15): create consolidation data model phase plans
Two plans for Phase 15: models + consolidator service (wave 1), unit tests + build verification (wave 2).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:36:42 +02:00
Dev
9031fd3473 docs(15): research phase domain for consolidation data model
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:32:23 +02:00
Dev
e3ff27a673 docs: create milestone v2.3 roadmap (5 phases, 15-19)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:31:54 +02:00
Dev
d967a8bb65 docs: define milestone v2.3 requirements (12 requirements)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:11:25 +02:00
Dev
4ad5f078c9 docs: synthesize v2.3 research summary
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:00:02 +02:00
Dev
853f47c4a6 docs: complete v2.3 project research (STACK, FEATURES, ARCHITECTURE, PITFALLS)
Research covers all five v2.3 features: automated app registration, app removal,
auto-take ownership, group expansion in HTML reports, and report consolidation toggle.
No new NuGet packages required. Build order and phase implications documented.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:58:58 +02:00
Dev
9318bb494d docs: start milestone v2.3 Tenant Management & Report Enhancements
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:43:56 +02:00
Dev
f41dbd333e chore: archive v2.2 Report Branding & User Directory milestone
Some checks failed
Release SharePoint Toolbox v2 / release (push) Failing after 14s
5 phases (10-14), 14 plans, 11/11 requirements complete.
Key features: HTML report branding with MSP/client logos, user directory
browse mode with paginated load and member/guest filtering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:27:33 +02:00
Dev
b9511bd2b0 docs(14): mark phase 14 plan checkboxes complete in roadmap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:31:52 +02:00
Dev
febb67ab64 docs(14-02): complete directory browse UI plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:31:08 +02:00
Dev
1a1e83cfad feat(14-02): add directory browse mode UI with mode toggle, DataGrid, and loading UX
- Mode toggle (Search/Browse) RadioButtons at top of left panel
- Search panel uses DataTrigger inverse visibility (collapses when IsBrowseMode=true)
- Browse panel with Load/Cancel buttons, IncludeGuests checkbox, filter TextBox, status/count
- Directory DataGrid with 5 columns (Name, Email, Department, Job Title, Type)
- Guest users highlighted in orange via DataTrigger on UserType
- SelectedUsers extracted to shared section visible in both modes
- DataGrid wired to DirectoryDataGrid_MouseDoubleClick handler
- Scan Options and Run/Export buttons remain always visible

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:29:59 +02:00
Dev
f11bfefe52 docs(14-01): complete directory UI infrastructure plan
- SUMMARY.md with 3 tasks, 4 commits, 5 files modified
- STATE.md updated with position and decisions
- ROADMAP.md updated with phase 14 progress (1/2 plans)
- REQUIREMENTS.md: UDIR-05 marked complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:27:44 +02:00
Dev
d1282cea5d feat(14-01): add DirectoryDataGrid_MouseDoubleClick code-behind handler
- Extracts GraphDirectoryUser from DataGrid.SelectedItem on double-click
- Invokes SelectDirectoryUserCommand to add user to audit pipeline
- Using added for SharepointToolbox.Core.Models

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:26:41 +02:00
Dev
e6ba2d8146 feat(14-01): add SelectDirectoryUserCommand bridging directory to audit pipeline
- RelayCommand<GraphDirectoryUser> converts to GraphUserResult and adds to SelectedUsers
- Duplicate UPN check prevents adding same user twice
- Initialized in both DI and test constructors
- 4 new tests pass (add, skip duplicate, null, auditable)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:26:12 +02:00
Dev
381081da18 test(14-01): add failing tests for SelectDirectoryUserCommand
- Test 17: adds user to SelectedUsers
- Test 18: skips duplicates
- Test 19: null does nothing
- Test 20: user is auditable after selection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:25:18 +02:00
Dev
70e8d121fd feat(14-01): add 14 localization keys for directory browse UI (EN + FR)
- audit.mode.search, audit.mode.browse for mode toggle labels
- directory.grp.browse, directory.btn.load, directory.btn.cancel
- directory.filter.placeholder, directory.chk.guests, directory.status.count
- directory.hint.doubleclick, directory.col.name/upn/department/jobtitle/type

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:24:54 +02:00
Dev
df6f4949a8 docs(13-02): complete User Directory ViewModel plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:44:56 +02:00
Dev
4ba4de6106 feat(13-02): add directory browse mode with paginated load, member/guest filter, and sortable ICollectionView
- Inject IGraphUserDirectoryService into UserAccessAuditViewModel (both constructors)
- Add IsBrowseMode toggle, DirectoryUsers collection, DirectoryUsersView with sort/filter
- Add LoadDirectoryCommand with progress reporting, cancellation, and error handling
- Add IncludeGuests toggle for in-memory member/guest filtering (no new Graph request)
- Add DirectoryFilterText for DisplayName/UPN/Department/JobTitle text search
- Add DirectoryUserCount computed property reflecting filtered view count
- Update OnTenantSwitched to clear all directory state
- Add 16 comprehensive unit tests covering all directory browse behaviors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:07:53 +02:00
Dev
cb7995ab31 docs(13-01): complete user directory model and service extension plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:02:45 +02:00
Dev
9a98371edd feat(13-01): extend GraphDirectoryUser with UserType and add includeGuests parameter to directory service
- Add string? UserType as last positional parameter to GraphDirectoryUser record
- Add bool includeGuests = false parameter to IGraphUserDirectoryService.GetUsersAsync
- Branch Graph filter: members-only (default) vs all users when includeGuests=true
- Add userType to Graph Select array for MapUser population
- Update MapUser to include UserType from Graph User object
- Add MapUser_PopulatesUserType and MapUser_NullUserType tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:01:46 +02:00
Dev
0baa3695fe docs(12-03): complete client logo section in ProfileManagementDialog plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:21:54 +02:00
Dev
46c8467c92 docs(12-02): complete MSP logo section plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:21:34 +02:00
Dev
ba81ea3cb7 feat(12-03): add client logo section with live preview to ProfileManagementDialog
- Increase dialog height from 480 to 620 to accommodate logo section
- Add new Row 3 with logo preview, Import/Clear/Pull from Entra buttons
- Image bound to ClientLogoPreview via Base64ToImageConverter
- Placeholder text shown when no logo configured via DataTrigger
- ValidationMessage displays feedback below logo buttons
- All logo buttons auto-disable when no profile selected

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:21:12 +02:00
Dev
b035e91120 feat(12-02): add MSP logo section with live preview to SettingsView
- Add Separator and MSP Logo label after data folder section
- Add Border with Grid containing Image preview and placeholder TextBlock
- Image bound to MspLogoPreview via Base64ToImageConverter with max 80x240
- DataTrigger toggles placeholder visibility when logo is null
- Import/Clear buttons bound to BrowseMspLogoCommand/ClearMspLogoCommand
- StatusMessage TextBlock in red, visible only when set

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:20:47 +02:00
Dev
c12ca4b813 docs(12-01): complete Base64ToImageSourceConverter and ClientLogoPreview plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:19:42 +02:00
Dev
6a4cd8ab56 feat(12-01): add Base64ToImageSourceConverter, localization keys, and ClientLogoPreview property
- Base64ToImageSourceConverter converts data URI strings to BitmapImage with null-safe error handling
- Registered converter in App.xaml as Base64ToImageConverter global resource
- Added 9 localization keys (EN+FR) for logo UI labels in Settings and Profile dialogs
- Added ClientLogoPreview string property to ProfileManagementViewModel with FormatLogoPreview helper
- Updated OnSelectedProfileChanged, BrowseClientLogoAsync, ClearClientLogoAsync, AutoPullClientLogoAsync
- 17 tests pass (6 converter + 11 profile VM logo tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:18:38 +02:00
Dev
0bc0babaf8 docs(phase-11): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:56:13 +02:00
Dev
5d3fdee9da docs(11-03): complete ViewModel branding wiring plan
- Create 11-03-SUMMARY.md: IBrandingService wired into all 5 export ViewModels
- Update STATE.md: decisions, session record, progress
- Update ROADMAP.md: Phase 11 marked complete (4/4 plans, all summaries present)
2026-04-08 14:51:56 +02:00
Dev
816fb5e3b5 feat(11-03): inject IBrandingService into all 5 export ViewModels and assemble branding in ExportHtmlAsync
- Add IBrandingService field and DI constructor parameter to all 5 ViewModels
- Add optional IBrandingService? parameter to test constructors (PermissionsViewModel, StorageViewModel, UserAccessAuditViewModel)
- Assemble ReportBranding from GetMspLogoAsync + _currentProfile.ClientLogo before each WriteAsync call
- Pass branding as last parameter to WriteAsync in all ExportHtmlAsync methods
- Guard clause: branding assembly skipped (branding = null) when _brandingService is null (test constructors)
- Build: 0 warnings, 0 errors; tests: 254 passed / 0 failed / 26 skipped
2026-04-08 14:50:54 +02:00
Dev
e77455f03f docs(11-02): complete HTML export branding injection plan
- SUMMARY.md created for 11-02 plan
- STATE.md updated with decisions and progress
- ROADMAP.md updated with phase 11 plan progress (3/4 summaries)
2026-04-08 14:46:55 +02:00
Dev
d8b66169e6 feat(11-02): extend export tests to verify branding injection across all 5 services
- HtmlExportServiceTests: 3 new tests (MSP logo only, null branding no img, both logos)
- SearchExportServiceTests: 1 new branding test (img tag present when branding provided)
- StorageHtmlExportServiceTests: 1 new branding test (img tag present)
- DuplicatesHtmlExportServiceTests: 1 new branding test (img tag present)
- UserAccessHtmlExportServiceTests: 1 new branding test (img tag present)
- MakeBranding helper added to each test class
- All 45 export tests pass; full suite 247/247 with 0 failures
2026-04-08 14:45:55 +02:00
Dev
2233fb86a9 feat(11-02): add optional ReportBranding parameter to all 5 HTML export services
- Added ReportBranding? branding = null to BuildHtml on all 5 services
- Added ReportBranding? branding = null after CancellationToken ct on all WriteAsync overloads
- Injected BrandingHtmlHelper.BuildBrandingHeader(branding) between <body> and <h1> in each
- StorageHtmlExportService both overloads updated (nodes-only and nodes+fileTypeMetrics)
- HtmlExportService both overloads updated (PermissionEntry and SimplifiedPermissionEntry)
- Build passes with 0 warnings — all existing callers compile unchanged via default null
2026-04-08 14:44:23 +02:00
Dev
2e8ceea279 docs(11-04): complete logo management commands plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:40:50 +02:00
Dev
b02b75e5bc feat(11-04): add logo management commands to SettingsViewModel and ProfileManagementViewModel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:40:08 +02:00
Dev
d4fa402f04 docs(11-01): complete ReportBranding and BrandingHtmlHelper plan
- Create 11-01-SUMMARY.md with execution results
- Update STATE.md: decisions, progress, session continuity
- Update ROADMAP.md: phase 11 in progress (1/4 plans complete)
- Mark BRAND-05 requirement complete in REQUIREMENTS.md
2026-04-08 14:36:08 +02:00
Dev
212c43915e feat(11-01): add ReportBranding model and BrandingHtmlHelper with tests
- Add ReportBranding positional record bundling MspLogo and ClientLogo
- Add BrandingHtmlHelper static class generating flex branding header HTML
- Add BrandingHtmlHelperTests covering all 4 logo states (null, both null, single, both)
- Add InternalsVisibleTo for SharepointToolbox.Tests in project file
2026-04-08 14:34:45 +02:00
Dev
9e850b07f2 feat(11-04): add UpdateProfileAsync to ProfileService and ImportLogoFromBytesAsync to BrandingService
- ProfileService.UpdateProfileAsync: replaces profile by name and persists the change
- IBrandingService: add ImportLogoFromBytesAsync to interface contract
- BrandingService.ImportLogoFromBytesAsync: validates magic bytes, compresses if > 512KB, returns LogoData
- BrandingService.ImportLogoAsync: refactored to delegate to ImportLogoFromBytesAsync
- ProfileServiceTests: 2 new tests (UpdateProfileAsync happy path + KeyNotFoundException)
- BrandingServiceTests: 2 new tests (ImportLogoFromBytesAsync valid PNG + invalid bytes)
- Tests.csproj: suppress NU1701 for pre-existing LiveCharts2/OpenTK transitive warnings
2026-04-08 14:34:11 +02:00
Dev
1ab2f2e426 docs(11): create phase plan for HTML export branding and ViewModel integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:23:01 +02:00
Dev
0ab0a65e7a docs(11): research html export branding and viewmodel integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:11:54 +02:00
Dev
e9a1530120 docs(phase-10): complete phase execution and verification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:30:23 +02:00
Dev
9176ae7db9 docs(10-03): complete branding-data-foundation plan 03
- 10-03-SUMMARY.md: DI registration for Phase 10 services
- STATE.md: advanced position, added decision, updated session
- ROADMAP.md: phase 10 marked complete (3/3 plans)
2026-04-08 12:37:15 +02:00
Dev
7e8e228155 feat(10-03): register Phase 10 services in DI container
- Add BrandingRepository as Singleton with branding.json path
- Add IBrandingService/BrandingService as Singleton
- Add IGraphUserDirectoryService/GraphUserDirectoryService as Transient
- 224 tests pass, 26 integration tests skipped (live Graph)
2026-04-08 12:36:12 +02:00
Dev
61d7ada945 docs(10-01): complete branding-data-foundation plan 01
- Add 10-01-SUMMARY.md with task commits, deviation doc, and dependency graph
- Update STATE.md: decisions logged, session updated
- Update ROADMAP.md: phase 10 In Progress (1/3 plans complete)
- Mark BRAND-01, BRAND-03 complete in REQUIREMENTS.md
2026-04-08 12:33:57 +02:00
Dev
188a8a7fff docs(10-02): complete Graph user directory service plan
- SUMMARY: GraphDirectoryUser model, IGraphUserDirectoryService, GraphUserDirectoryService with PageIterator
- STATE: decisions added, session updated, progress bar updated
- ROADMAP: phase 10 marked In Progress (2/3 summaries)
- REQUIREMENTS: BRAND-06 marked complete
- Deferred: BrandingServiceTests.cs blocking test compilation (pre-existing, plan 10-01 artifact)
2026-04-08 12:33:33 +02:00
Dev
130386622f feat(10-01): create BrandingService with magic byte validation and auto-compression
- Add IBrandingService interface with ImportLogoAsync, Save/Clear/GetMspLogoAsync
- Add BrandingService: PNG/JPEG magic byte detection, rejects unsupported formats with
  descriptive error, auto-compresses files over 512 KB using WPF PresentationCore imaging
- Add BrandingServiceTests: 9 tests covering validation, rejection, compression, CRUD
- Deviation: used WPF BitmapEncoder/TransformedBitmap instead of System.Drawing.Bitmap
  (System.Drawing.Common not available without new NuGet package; WPF PresentationCore
  is in the existing stack per architectural decisions)
2026-04-08 12:32:23 +02:00
Dev
3ba574612f feat(10-02): implement GraphUserDirectoryService with PageIterator and unit tests
- GraphUserDirectoryService uses PageIterator<User, UserCollectionResponse> for pagination
- Filter: accountEnabled eq true and userType eq 'Member' (no ConsistencyLevel header)
- Cancellation checked in PageIterator callback (return false stops iteration)
- Progress reported via IProgress<int> with running count per user
- MapUser extracted as internal static for direct unit test coverage
- Tests: 5 unit tests for MapUser field mapping and fallback logic
- Integration-level tests (pagination/cancellation) skipped with rationale documented
- Note: test project compilation blocked by pre-existing BrandingServiceTests.cs (10-01 artifact)
2026-04-08 12:32:04 +02:00
Dev
2280f12eab feat(10-01): create logo models, BrandingRepository, and repository tests
- Add LogoData record with Base64 and MimeType init properties
- Add BrandingSettings class with nullable MspLogo property
- Extend TenantProfile with nullable ClientLogo property (additive)
- Add BrandingRepository mirroring SettingsRepository pattern (write-then-replace)
- Add BrandingRepositoryTests: 5 tests covering load defaults, round-trip, dir creation, and TenantProfile serialization
2026-04-08 12:29:53 +02:00
Dev
5e56a96cd0 feat(10-02): add GraphDirectoryUser model and IGraphUserDirectoryService interface
- GraphDirectoryUser positional record with DisplayName, UPN, Mail, Department, JobTitle
- IGraphUserDirectoryService.GetUsersAsync with clientId, IProgress<int>?, CancellationToken
- Follows existing GraphUserSearchService namespace pattern
2026-04-08 12:29:19 +02:00
Dev
1ffd71243e docs(10): create phase plan - 3 plans in 2 waves
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:50:59 +02:00
Dev
464b70ddcc docs(phase-10): add context, research, and validation strategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:44:24 +02:00
Dev
e6fdccf19c docs(phase-10): research branding data foundation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 11:43:07 +02:00
Dev
59ff5184ff docs: create milestone v2.2 roadmap (5 phases, 11 requirements)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:22:05 +02:00
Dev
5ccf1688ea docs: define milestone v2.2 requirements (11 requirements)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:00:59 +02:00
Dev
5f59e339ee docs(research): synthesize v2.2 research into SUMMARY.md
Adds v2.2 milestone section (Report Branding & User Directory) while
preserving the original v1.0 summary. Covers stack additions (none),
feature table stakes vs. differentiators, architecture integration
points with dependency-aware build order, top 6 critical pitfalls with
prevention strategies, suggested roadmap phase structure, open product
questions, and confidence assessment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:58:57 +02:00
Dev
8447e78db9 docs: start milestone v2.2 Report Branding & User Directory
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:57:27 +02:00
Dev
fd442f3b4c chore: archive v1.1 Enhanced Reports milestone
Some checks failed
Release SharePoint Toolbox v2 / release (push) Failing after 14s
v1.1 shipped with 4 phases (25 plans), 10/10 requirements complete:
- Global site selection (toolbar picker, all tabs consume)
- User access audit (Graph people-picker, direct/group/inherited)
- Simplified permissions (plain-language labels, risk levels, detail toggle)
- Storage visualization (LiveCharts2 pie/donut + bar charts)

Post-phase polish: centralized site selection (removed per-tab pickers),
claims prefix stripping, StorageMetrics backfill, chart tooltip fix,
summary stats in app + HTML exports.

205 tests passing, 10,484 LOC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:21:02 +02:00
Dev
fa793c5489 docs(phase-09): mark phase complete in roadmap — 4/4 plans executed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:42:50 +02:00
Dev
713cf91d00 docs(09-04): complete StorageViewModel chart unit tests plan
- SUMMARY.md with 7 passing tests documented
- STATE.md updated to plan 4/4, phase 9 complete
- ROADMAP.md phase 09 marked complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:41:55 +02:00
Dev
712b949eb2 test(09-04): add StorageViewModel chart unit tests
- 7 tests covering chart series from metrics, bar series structure,
  donut/bar toggle, top-10+Other aggregation, no-Other for <=10,
  tenant switch cleanup, and empty data handling
- Added LiveChartsCore.SkiaSharpView.WPF to test project
- Uses reflection to set FileTypeMetrics (private setter) directly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:40:26 +02:00
Dev
e2321666c6 docs(09-03): complete ViewModel chart properties and View XAML plan summary
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:37:20 +02:00
Dev
a8d79a8241 feat(09-03): add chart panel to StorageView with toggle and localization
- Update StorageView.xaml: DataGrid top, GridSplitter, chart panel bottom
- Add PieChart and CartesianChart with MultiDataTrigger visibility
- Add radio buttons for donut/bar chart toggle in left panel
- Create BytesLabelConverter for chart tooltip formatting
- Add stor.chart.* localization keys in EN and FR resx files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:35:35 +02:00
Dev
70048ddcdf feat(09-03): extend StorageViewModel with chart data properties and toggle
- Add IsDonutChart toggle, FileTypeMetrics collection, PieChartSeries, BarChartSeries, BarXAxes, BarYAxes
- Add UpdateChartSeries method with top-10 + Other aggregation
- Call CollectFileTypeMetricsAsync after storage scan in RunOperationAsync
- Clear chart data on tenant switch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:27:54 +02:00
Dev
3ec776ba81 docs(09-02): complete CollectFileTypeMetricsAsync plan
- SUMMARY.md with implementation details and deviation log
- STATE.md updated to plan 2 of 4, 92% progress
- ROADMAP.md and REQUIREMENTS.md updated (VIZZ-02 complete)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:25:25 +02:00
Dev
81e3dcac6d feat(09-02): implement CollectFileTypeMetricsAsync in StorageService
- CamlQuery with RecursiveAll scope enumerates files across all non-hidden document libraries
- Paginated 500-item batches avoid list view threshold issues
- Files grouped by extension (case-insensitive) with summed size and count
- Results returned as IReadOnlyList<FileTypeMetric> sorted by TotalSizeBytes descending
- Existing CollectStorageAsync, LoadFolderNodeAsync, CollectSubfoldersAsync unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:24:09 +02:00
Dev
18fe97f975 docs(09-01): complete LiveCharts2 foundation plan
- Add 09-01-SUMMARY.md with task details and self-check
- Update STATE.md position to Phase 9, Plan 1 of 4
- Update ROADMAP.md and REQUIREMENTS.md progress

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:22:50 +02:00
Dev
39c31dadfa feat(09-01): extend IStorageService with CollectFileTypeMetricsAsync
- Add CollectFileTypeMetricsAsync method signature to IStorageService
- Returns IReadOnlyList<FileTypeMetric> for chart visualization data
- Existing CollectStorageAsync signature unchanged
- CS0535 expected until StorageService implements in Plan 09-02

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:21:02 +02:00
Dev
60cbb977bf feat(09-01): add LiveCharts2 NuGet and FileTypeMetric data model
- Add LiveChartsCore.SkiaSharpView.WPF 2.0.0-rc5.4 package reference
- Create FileTypeMetric record with Extension, TotalSizeBytes, FileCount
- Include DisplayLabel computed property for chart label binding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:20:38 +02:00
Dev
a63a698282 docs(09-storage-visualization): create phase plan — 4 plans in 4 waves
Wave 1: LiveCharts2 NuGet + FileTypeMetric model + IStorageService extension
Wave 2: StorageService file-type enumeration implementation
Wave 3: ViewModel chart properties + View XAML + localization
Wave 4: Unit tests for chart ViewModel behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:16:16 +02:00
Dev
666e918810 docs(08-06): complete unit tests for simplified permissions plan
- SUMMARY.md with 17 tests added across 3 test files
- STATE.md updated: Phase 08 complete (6/6 plans)
- ROADMAP.md updated: Phase 08 marked complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:22:35 +02:00
Dev
22a51c05ef test(08-06): add simplified mode tests to PermissionsViewModelTests
- IsSimplifiedMode default false, toggle rebuilds SimplifiedResults
- IsDetailView toggle does not re-compute simplified data
- Summaries contains correct risk breakdown after toggle
- Helper method CreateViewModelWithResults for test reuse

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:21:06 +02:00
Dev
0f25fd67f8 test(08-06): add PermissionLevelMapping and PermissionSummaryBuilder unit tests
- 9 tests for PermissionLevelMapping: known roles, unknown fallback, case insensitivity, splitting, risk ranking, labels
- 4 tests for PermissionSummaryBuilder: risk levels, empty input, distinct users, wrapping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:20:12 +02:00
Dev
a8a58f1ffc docs(08-05): complete localization keys and export wiring plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:18:37 +02:00
Dev
f503e6c0ca feat(08-05): wire export commands to use simplified overloads
- ExportCsvAsync branches on IsSimplifiedMode to call simplified WriteAsync overload
- ExportHtmlAsync branches on IsSimplifiedMode to call simplified WriteAsync overload
- Standard PermissionEntry export path unchanged when simplified mode is off

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:17:14 +02:00
Dev
60ddcd781f feat(08-05): add EN/FR localization keys for simplified permissions UI
- Add 6 keys to Strings.resx: chk.simplified.mode, grp.display.opts, lbl.detail.level, rad.detail.detailed, rad.detail.simple, lbl.summary.users
- Add matching French translations to Strings.fr.resx with proper XML entities for accented characters
- Wire hardcoded "user(s)" text in PermissionsView.xaml summary cards to lbl.summary.users localization key

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:16:40 +02:00
Dev
1f5aa2b668 docs(08-03): complete Permissions View Simplified Mode UI plan
- Created 08-03-SUMMARY.md with task results and self-check
- Updated STATE.md with metrics and decisions
- Updated ROADMAP.md plan progress for phase 08

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:14:42 +02:00
Dev
12d4932484 docs(08-04): complete export services simplified overloads plan
- SUMMARY.md with task commits and decisions
- STATE.md updated to plan 4 of 6
- ROADMAP.md progress updated

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:14:26 +02:00
Dev
899ab7d175 feat(08-04): add simplified export overloads to HtmlExportService
- Add RiskLevelColors helper for risk-level color coding
- Add BuildHtml(IReadOnlyList<SimplifiedPermissionEntry>) with risk summary cards, Simplified column, and color-coded Risk badges
- Add WriteAsync overload for simplified entries
- Original PermissionEntry methods unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:13:08 +02:00
Dev
163c506e0b feat(08-03): add simplified mode UI to PermissionsView
- Add Display Options GroupBox with Simplified Mode toggle and Simple/Detailed radio buttons
- Add summary panel with color-coded risk level cards bound to Summaries collection
- DataGrid binds to ActiveItemsSource, rows color-coded by RiskLevel via DataTriggers
- SimplifiedLabels column visible only in simplified mode via BooleanToVisibilityConverter
- DataGrid collapses in Simple mode via MultiDataTrigger on IsSimplifiedMode+IsDetailView
- Create InvertBoolConverter for radio button inverse binding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:12:57 +02:00
Dev
fe19249f82 feat(08-04): add simplified export overloads to CsvExportService
- Add BuildCsv(IReadOnlyList<SimplifiedPermissionEntry>) overload with SimplifiedLabels and RiskLevel columns
- Add WriteAsync overload for simplified entries
- Original PermissionEntry methods unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:12:18 +02:00
Dev
c970342497 docs(08-02): complete ViewModel Toggle Logic plan summary
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:11:08 +02:00
Dev
e2c94bf6d1 feat(08-02): add simplified mode properties to PermissionsViewModel
- IsSimplifiedMode toggle switches between raw and simplified labels
- IsDetailView toggle controls individual vs summary row display
- SimplifiedResults and Summaries computed from cached Results
- ActiveItemsSource provides correct collection for DataGrid binding
- Mode toggles rebuild from cache without re-running scan
- OnTenantSwitched resets simplified state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:09:57 +02:00
Dev
3c70884022 docs(08-01): complete Permission Data Models and Mapping Layer plan
- SUMMARY.md with self-check passed
- STATE.md updated to Phase 8, Plan 1 complete
- ROADMAP.md progress updated for Phase 8
- SIMP-01 and SIMP-02 requirements marked complete

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:08:03 +02:00
Dev
6609f2a70a feat(08-01): add SimplifiedPermissionEntry wrapper and PermissionSummary model
- SimplifiedPermissionEntry wraps PermissionEntry with computed labels and risk level
- Passthrough properties preserve DataGrid binding compatibility
- PermissionSummary record for grouped risk-level counts
- PermissionSummaryBuilder always returns all 4 risk levels for consistent UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:06:47 +02:00
Dev
f1390eaa1c feat(08-01): add RiskLevel enum and PermissionLevelMapping helper
- RiskLevel enum with High, Medium, Low, ReadOnly tiers
- PermissionLevelMapping maps 11 standard SharePoint roles to plain-language labels
- Case-insensitive lookup with Medium fallback for unknown roles
- GetHighestRisk and GetSimplifiedLabels for row-level formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:06:17 +02:00
Dev
c871effa87 docs(08-simplified-permissions): create phase plan (6 plans, 5 waves)
Plans cover plain-language permission labels, risk-level color coding,
summary counts, detail-level toggle, export integration, and unit tests.
PermissionEntry record is NOT modified — uses wrapper pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:00:08 +02:00
Dev
dcdbd8662d docs(phase-07): complete phase execution — human verified and approved
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 13:45:08 +02:00
Dev
00252fd137 fix(07): fix people picker selection and audit service authentication
People picker ListBox used MouseBinding which fires before SelectedItem
updates, causing null CommandParameter. Replaced with SelectionChanged
event handler in code-behind.

AuditUsersAsync created TenantProfile with empty ClientId, causing
ArgumentException in SessionManager. Added currentProfile parameter
to pass the authenticated tenant's ClientId through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 13:44:53 +02:00
Dev
0af73df65c docs(07-10): complete debounced search test plan summary
- Created 07-10-SUMMARY.md documenting gap closure for verification gap 3
- Updated STATE.md: progress 100%, metrics recorded, decision logged, session updated
- Updated ROADMAP.md: Phase 7 complete (10/10 plans, 10/10 summaries)
2026-04-07 13:16:26 +02:00
Dev
d7ff32ee94 docs(07-09): complete DataGrid visual indicators plan summary 2026-04-07 13:15:23 +02:00
Dev
67a2053a94 test(07-10): add debounced search unit test for UserAccessAuditViewModel
- Extended CreateViewModel helper to return (vm, auditMock, graphMock) 3-tuple
- Updated all 8 existing tests to use _ discard for the new graphMock slot
- Added Test 9: SearchQuery_debounced_calls_SearchUsersAsync verifying that
  setting SearchQuery to "Ali" calls SearchUsersAsync after 300ms debounce
- All 9 ViewModel tests pass; full suite 177 passed / 22 skipped
2026-04-07 13:15:16 +02:00
Dev
33833dce5d feat(07-09): add guest badge, warning icon, and ObjectType column to DataGrid
- Convert User column to DataGridTemplateColumn with orange 'Guest' pill badge on IsExternalUser=true
- Add ObjectType DataGridTextColumn between Object and Permission Level
- Convert Permission Level column to DataGridTemplateColumn with red warning icon on IsHighPrivilege=true
2026-04-07 13:14:29 +02:00
Dev
855e4df49b docs(07-08): complete unit tests plan summary
- 07-08-SUMMARY.md: 32 tests across 4 files, all passing
- STATE.md: advance plan, record metrics and decisions
- ROADMAP.md: phase 7 complete (8/8 plans)
2026-04-07 13:00:18 +02:00
Dev
35b2c2a109 test(07-08): add export and ViewModel unit tests
- UserAccessCsvExportServiceTests (5): summary section, data header, RFC 4180
  quote escaping, 7-column count, WriteSingleFileAsync multi-user output
- UserAccessHtmlExportServiceTests (7): DOCTYPE, stats cards, dual-view sections,
  access type badges, filterTable JS, toggleView JS, HTML entity encoding
- UserAccessAuditViewModelTests (8): AuditUsersAsync invocation, results population,
  summary properties computation, tenant switch reset, GlobalSitesChanged update,
  override guard, CanExport false/true states
2026-04-07 12:58:58 +02:00
Dev
5df95032ee test(07-08): add UserAccessAuditService unit tests
- 12 tests: user filtering, claim format matching, Direct/Group/Inherited
  access type classification, Full Control + SCA high-privilege detection,
  external user flagging (#EXT#), semicolon user/level splitting, multi-site scan
2026-04-07 12:57:21 +02:00
Dev
34c1776dcc docs(07-07): complete integration wiring plan summary
- Add 07-07-SUMMARY.md for MainWindow/DI/localization integration
- Update STATE.md: progress 92%, new decisions, session record
- Update ROADMAP.md: phase 7 showing 7/8 summaries
2026-04-07 12:55:02 +02:00
Dev
a2531ea33f feat(07-07): add localization keys for User Access Audit tab in English and French
- Add 17 audit.* keys and tab.userAccessAudit to Strings.resx (English)
- Add matching French translations with proper Unicode accented characters to Strings.fr.resx
2026-04-07 12:53:37 +02:00
Dev
df796ee956 feat(07-07): add UserAccessAuditTabItem to MainWindow and wire dialog factory
- Add UserAccessAuditTabItem to MainWindow.xaml TabControl before SettingsTabItem
- Wire UserAccessAuditView content and SitePickerDialog factory in MainWindow.xaml.cs
2026-04-07 12:53:04 +02:00
Dev
2ed8a0cb12 feat(07-07): add DI registrations for Phase 7 services and create UserAccessAuditView
- Register IUserAccessAuditService, IGraphUserSearchService, export services, ViewModel and View in App.xaml.cs
- Create UserAccessAuditView.xaml with two-panel layout: people picker, site picker, scan options, color-coded DataGrid with grouping, summary banner
- Create UserAccessAuditView.xaml.cs code-behind with ViewModel constructor injection
- [Rule 3] UserAccessAuditView was missing (07-05 not executed); created inline to unblock 07-07
2026-04-07 12:52:36 +02:00
Dev
c42140db1a docs(07-05): complete UserAccessAuditView plan
- 07-05-SUMMARY.md: view with people picker, summary banner, color-coded DataGrid
- STATE.md: progress updated to 85% (11/13), decisions recorded, session updated
- ROADMAP.md: phase 7 in progress with 6/8 summaries complete
2026-04-07 12:50:53 +02:00
Dev
975762dee4 feat(07-05): create UserAccessAuditView code-behind
- UserControl with UserAccessAuditViewModel constructor injection, sets DataContext
- Wires SearchResults.CollectionChanged to show/hide autocomplete ListBox
- OnSearchResultClicked handler invokes AddUserCommand for mouse-based user selection
2026-04-07 12:49:41 +02:00
Dev
bb9ba9d310 feat(07-05): create UserAccessAuditView XAML layout
- Two-panel layout (290px left + * right) following PermissionsView pattern
- Left panel: people picker with autocomplete list + removable user pills, site picker button, scan option checkboxes, run/cancel/export buttons
- Right panel: 3-card summary banner (TotalAccessCount, SitesCount, HighPrivilegeCount), filter TextBox, group-by ToggleButton, color-coded DataGrid
- DataGrid: color-coded rows by AccessType (Direct=blue, Group=green, Inherited=gray), warning icon for high privilege, Guest badge for external users, access type icons
- GroupStyle with Expander headers showing group name + item count
- Status bar with ProgressBar + StatusMessage
2026-04-07 12:49:37 +02:00
Dev
72349d8415 docs(07-04): complete UserAccessAuditViewModel plan
- Add 07-04-SUMMARY.md with task commits and decisions
- Update STATE.md: progress 77%, session record, decisions
- Update ROADMAP.md: phase 7 plan count updated to 5/8 summaries
2026-04-07 12:45:14 +02:00
Dev
3de737ac3f feat(07-04): implement UserAccessAuditViewModel
- Extends FeatureViewModelBase with RunOperationAsync calling IUserAccessAuditService.AuditUsersAsync
- People picker with 300ms debounced Graph search via IGraphUserSearchService.SearchUsersAsync
- SelectedUsers ObservableCollection<GraphUserResult> with AddUserCommand/RemoveUserCommand
- Results ObservableCollection<UserAccessEntry> with CollectionViewSource grouping (by user/site) and FilterText predicate
- Summary banner properties: TotalAccessCount, SitesCount, HighPrivilegeCount (computed from Results)
- ExportCsvCommand/ExportHtmlCommand using UserAccessCsvExportService/UserAccessHtmlExportService
- Site selection with _hasLocalSiteOverride + OnGlobalSitesChanged pattern from PermissionsViewModel
- Dual constructors (DI + internal test constructor omitting export services)
- OnTenantSwitched resets all state (results, users, search, sites)
2026-04-07 12:44:02 +02:00
Dev
5c4a285473 docs(07-06): complete export services plan
- UserAccessCsvExportService and UserAccessHtmlExportService implemented
- SUMMARY.md created with task commits, decisions, self-check
- STATE.md updated: progress 69%, session, metrics, decisions
- ROADMAP.md updated: phase 7 showing 4/8 summaries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 12:42:00 +02:00
Dev
85712ad3ba docs(07-02): complete UserAccessAuditService plan
- Add 07-02-SUMMARY.md with implementation details
- Update STATE.md: progress 62%, decisions, session
- Update ROADMAP.md: phase 7 now 3/8 plans complete
2026-04-07 12:40:56 +02:00
Dev
3146a04ad8 feat(07-06): implement UserAccessHtmlExportService
- BuildHtml produces self-contained HTML with inline CSS and JS
- Stats cards: Total Accesses, Users Audited, Sites Scanned, High Privilege, External Users
- Per-user summary cards with high-privilege border highlight and guest badge
- Dual-view toggle (By User / By Site) with JS toggleView()
- Collapsible group headers per user and per site via toggleGroup()
- Sortable columns via sortTable() within each group
- Text filter via filterTable() scoping to active view
- Color-coded access type badges: Direct (blue), Group (green), Inherited (gray)
- High-privilege rows with bold text and warning icon
- External user guest badge (orange pill)
- UTF-8 without BOM encoding (matching HtmlExportService pattern)
2026-04-07 12:40:51 +02:00
Dev
cc513777ec docs(07-03): complete GraphUserSearchService plan
- Add 07-03-SUMMARY.md with implementation details and decisions
- Update STATE.md: progress 54%, decisions, session, metrics
- Update ROADMAP.md: phase 07 now 2/8 summaries
2026-04-07 12:40:22 +02:00
Dev
44b238e07a feat(07-02): implement UserAccessAuditService
- Scans permissions via IPermissionsService.ScanSiteAsync per site
- Filters PermissionEntry results to matching target user logins (case-insensitive contains)
- Splits semicolon-delimited users/logins/levels into per-user UserAccessEntry rows
- Classifies AccessType: Inherited (!HasUniquePermissions), Group (GrantedThrough), Direct
- Flags IsHighPrivilege (Full Control, Site Collection Administrator) and IsExternalUser (#EXT#)
2026-04-07 12:39:57 +02:00
Dev
9f891aa512 feat(07-06): implement UserAccessCsvExportService
- BuildCsv per-user CSV with summary section (user, totals, sites, high-privilege, date)
- WriteAsync groups entries by UserLogin, writes one file per user (audit_{email}_{date}.csv)
- WriteSingleFileAsync combines all users in one file for SaveFileDialog export
- RFC 4180 CSV escaping, UTF-8 with BOM for Excel compatibility
- SanitizeFileName strips invalid path chars from email addresses
2026-04-07 12:39:35 +02:00
Dev
026b8294de feat(07-03): implement GraphUserSearchService for people-picker autocomplete
- Queries Graph /users with startsWith filter on displayName, mail, UPN
- Requires minimum 2 chars to prevent overly broad queries
- Sets ConsistencyLevel=eventual + Count=true (required for advanced filter)
- Escapes single quotes to prevent OData injection
- Returns up to maxResults (default 10) GraphUserResult records
2026-04-07 12:39:22 +02:00
Dev
7e6f3e7fc0 docs(07-01): complete data models and service interfaces plan
- UserAccessEntry, AccessType, IUserAccessAuditService, IGraphUserSearchService
- UACC-01, UACC-02 requirements marked complete
- STATE.md updated with position and decisions
- ROADMAP.md Phase 7 progress updated (1/8 plans)
2026-04-07 12:38:19 +02:00
Dev
1a6989a9bb feat(07-01): add IUserAccessAuditService and IGraphUserSearchService interfaces
- IUserAccessAuditService.AuditUsersAsync: scan sites and filter by user logins
- IGraphUserSearchService.SearchUsersAsync: Graph API people-picker autocomplete
- GraphUserResult record: DisplayName, UserPrincipalName, Mail
2026-04-07 12:37:26 +02:00
Dev
e08df0f658 feat(07-01): add UserAccessEntry model and AccessType enum
- UserAccessEntry record with 12 fields for user-centric audit results
- AccessType enum: Direct, Group, Inherited
- Pre-computed IsHighPrivilege and IsExternalUser fields for grid display
2026-04-07 12:37:00 +02:00
Dev
19e4c3852d docs(07): create phase plan - 8 plans across 5 waves
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:32:39 +02:00
Dev
91058bc2e4 docs(state): record phase 7 context session
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:22:02 +02:00
Dev
ab253ca80a docs(07): capture phase context for user access audit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:21:57 +02:00
Dev
e96ca3edfe test(06): complete UAT - 8/8 passed
All Phase 6 global site selection features verified:
- Toolbar button, site count label, single/multi-site pre-fill
- Transfer pre-fill, local override, clear-reverts, tenant switch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:01:33 +02:00
Dev
4846915c80 fix(site-list): fix parsing error and double-auth in SiteListService
- Replace GetSitePropertiesFromSharePoint("", true) with modern
  GetSitePropertiesFromSharePointByFilters using null StartIndex
- Use ctx.Clone(adminUrl) instead of creating new AuthenticationManager
  for admin URL, eliminating second browser auth prompt

Resolves: UAT issue "Must specify valid information for parsing in the string"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 11:00:54 +02:00
Dev
5666565ac1 test(06): complete UAT - 0 passed, 3 issues, 7 skipped
Fix two pre-existing blockers found during UAT:
- ProfileManagementViewModel: add NotifyCanExecuteChanged on property changes
- SessionManager: open browser in openBrowserCallback (was no-op)

Remaining blocker: SitePickerDialog parsing error from PnP Framework.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:41:39 +02:00
Dev
52670bd262 docs(phase-06): complete phase verification and update state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:18:14 +02:00
Dev
9add2592b3 docs(06-05): complete GlobalSiteSelectionTests plan — phase 6 done
- SUMMARY.md created with test coverage details and decision rationale
- STATE.md updated: progress 100%, decisions recorded, session logged
- ROADMAP.md phase 6 marked Complete (5/5 plans with summaries)
2026-04-07 10:14:48 +02:00
Dev
80ef092a2e test(06-05): add GlobalSiteSelectionTests with 10 passing tests
- Message broadcast: GlobalSitesChangedMessage carries site list to receivers
- Base class: FeatureViewModelBase.GlobalSites updated on message receive
- Storage tab: SiteUrl pre-filled from first global site
- Storage tab: local override prevents global from overwriting SiteUrl
- Storage tab: clearing SiteUrl reverts to global site (override reset)
- Permissions tab: SelectedSites pre-populated from global sites
- Permissions tab: local picker override blocks subsequent global updates
- Tenant switch: resets local override so new global sites apply cleanly
- Transfer tab: SourceSiteUrl pre-filled from first global site
- MainWindowViewModel: GlobalSitesSelectedLabel reflects site count
2026-04-07 10:13:31 +02:00
Dev
da905b6ec0 docs(06-04): complete tab-vms global site consumption plan
- Add 06-04-SUMMARY.md with all task details and self-check
- Update STATE.md: progress bar 80%, decisions, session record
- Update ROADMAP.md: phase 6 now 4/5 plans complete (In Progress)
- Mark SITE-02 complete in REQUIREMENTS.md
2026-04-07 10:10:18 +02:00
Dev
0a91dd4ff3 feat(06-04): update TransferViewModel for global site consumption; confirm BulkMembers excluded
- TransferViewModel: add _hasLocalSourceSiteOverride field
- Override OnGlobalSitesChanged to pre-fill SourceSiteUrl from first global site
- Add OnSourceSiteUrlChanged partial to detect local user input
- Reset _hasLocalSourceSiteOverride on tenant switch
- BulkMembersViewModel confirmed excluded: no SiteUrl field, CSV-driven, no OnGlobalSitesChanged override added
2026-04-07 10:08:52 +02:00
Dev
9a4365bd32 docs(06-03): complete toolbar UI, localization, and dialog factory wiring plan
- SUMMARY.md created for plan 06-03
- STATE.md updated: progress 60%, decisions logged, session recorded
- ROADMAP.md updated: phase 6 now 3/5 summaries (In Progress)
2026-04-07 10:08:51 +02:00
Dev
6a2e4d1d89 feat(06-04): update single-site tab VMs for global site consumption
- StorageViewModel: add OnGlobalSitesChanged, OnSiteUrlChanged, _hasLocalSiteOverride
- SearchViewModel: add OnGlobalSitesChanged, OnSiteUrlChanged, _hasLocalSiteOverride
- DuplicatesViewModel: add OnGlobalSitesChanged, OnSiteUrlChanged, _hasLocalSiteOverride
- FolderStructureViewModel: add OnGlobalSitesChanged, OnSiteUrlChanged, _hasLocalSiteOverride
- All four VMs pre-fill SiteUrl from first global site; local typing sets override flag
- Tenant switch resets _hasLocalSiteOverride in all four VMs
2026-04-07 10:08:19 +02:00
Dev
45eb531128 feat(06-03): add global site picker button and count label to toolbar
- Add Separator + Select Sites button (bound to OpenGlobalSitePickerCommand) to ToolBar
- Add TextBlock bound to GlobalSitesSelectedLabel for site count display
- Wire viewModel.OpenGlobalSitePickerDialog factory in MainWindow.xaml.cs using DI
- Add using SharepointToolbox.Core.Models for TenantProfile in code-behind
2026-04-07 10:07:35 +02:00
Dev
467a940c6f feat(06-03): localize GlobalSitesSelectedLabel in MainWindowViewModel
- Replace hardcoded EN strings with TranslationSource.Instance lookups
- Uses toolbar.globalSites.count (formatted) and toolbar.globalSites.none keys
- Follows same pattern as PermissionsViewModel.SitesSelectedLabel
2026-04-07 10:06:57 +02:00
Dev
1bf47b5c4e feat(06-04): update PermissionsViewModel for multi-site global consumption
- Add _hasLocalSiteOverride field to track local user selection
- Override OnGlobalSitesChanged to pre-populate SelectedSites from global sites
- Set _hasLocalSiteOverride=true when user picks sites via site picker dialog
- Reset _hasLocalSiteOverride=false on tenant switch (OnTenantSwitched)
2026-04-07 10:06:57 +02:00
Dev
185642f4af feat(06-03): add EN/FR localization keys for global site picker toolbar
- Add toolbar.selectSites, toolbar.selectSites.tooltip, toolbar.selectSites.tooltipDisabled
- Add toolbar.globalSites.count and toolbar.globalSites.none to both Strings.resx and Strings.fr.resx
2026-04-07 10:06:40 +02:00
Dev
a39c87d43e docs(06-01): complete GlobalSitesChangedMessage and FeatureViewModelBase plan
- 06-01-SUMMARY.md created with deviations and decisions documented
- STATE.md updated: progress 40%, decisions added, session recorded
- ROADMAP.md updated: phase 6 in-progress (2/5 summaries)
2026-04-07 10:05:16 +02:00
Dev
95bf9c2eed docs(06-02): complete MainWindowViewModel global site selection plan
- Add 06-02-SUMMARY.md with execution results and dependency graph
- Update STATE.md: progress 20%, decision logged, session recorded
- Update ROADMAP.md: phase 6 in progress (1/5 plans complete)
- Mark SITE-01 requirement complete in REQUIREMENTS.md
2026-04-07 10:04:36 +02:00
Dev
d4fe169bd8 feat(06-01): extend FeatureViewModelBase with GlobalSites support
- Add protected GlobalSites property (IReadOnlyList<SiteInfo>) initialized to Array.Empty
- Register GlobalSitesChangedMessage in OnActivated alongside TenantSwitchedMessage
- Add private OnGlobalSitesReceived to update GlobalSites and invoke virtual hook
- Add protected virtual OnGlobalSitesChanged for derived VMs to override
- [Rule 3 - Blocking] Fix MainWindowViewModel missing ExecuteOpenGlobalSitePicker and BroadcastGlobalSites stubs referenced in constructor (pre-existing partial state from earlier TODO commit)
2026-04-07 10:03:40 +02:00
Dev
a10f03edc8 feat(06-02): add global site selection state, command, and broadcast to MainWindowViewModel
- Add OpenGlobalSitePickerDialog factory property (dialog factory pattern)
- Add GlobalSelectedSites ObservableCollection<SiteInfo>
- Add GlobalSitesSelectedLabel computed property for toolbar display
- Add OpenGlobalSitePickerCommand (disabled when no profile selected)
- Broadcast GlobalSitesChangedMessage via WeakReferenceMessenger on collection change
- Clear GlobalSelectedSites on tenant switch (OnSelectedProfileChanged)
- Clear GlobalSelectedSites on session clear (ClearSessionAsync)
- Add using SharepointToolbox.Views.Dialogs for SitePickerDialog cast
2026-04-07 10:03:30 +02:00
Dev
7874fa8524 feat(06-01): create GlobalSitesChangedMessage
- New ValueChangedMessage<IReadOnlyList<SiteInfo>> following TenantSwitchedMessage pattern
- Carries snapshot of globally selected sites (IReadOnlyList — immutable by design)
2026-04-07 10:02:20 +02:00
Dev
6ae3629301 docs(06): create phase plan for global site selection (5 plans, 3 waves)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:57:15 +02:00
Dev
59efdfe3f0 docs: create milestone v1.1 roadmap (4 phases)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:41:49 +02:00
Dev
04a307b69c docs: define milestone v1.1 requirements (10 requirements)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:40:02 +02:00
Dev
81da0f6a99 docs: start milestone v1.1 Enhanced Reports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:38:28 +02:00
Dev
0fb35de80f docs: capture todo - Add global multi-site selection option
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:33:05 +02:00
Dev
724fdc550d chore: complete v1.0 milestone
Archive 5 phases (36 plans) to milestones/v1.0-phases/.
Archive roadmap, requirements, and audit to milestones/.
Evolve PROJECT.md with shipped state and validated requirements.
Collapse ROADMAP.md to one-line milestone summary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:19:03 +02:00
1040 changed files with 40037 additions and 7602 deletions

View File

@@ -1,46 +0,0 @@
#Wkf
name: Release zip package
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: native
steps:
- name: Checkout
run: |
git clone "$GITEA_SERVER_URL/$GITEA_REPO.git" repo
cd repo && git checkout "$GITEA_REF_NAME"
env:
GITEA_SERVER_URL: ${{ gitea.server_url }}
GITEA_REPO: ${{ gitea.repository }}
GITEA_REF_NAME: ${{ gitea.ref_name }}
- name: Build zip
run: |
cd repo
VERSION="${{ gitea.ref_name }}"
ZIP="SharePoint_ToolBox_${VERSION}.zip"
zip -r "../${ZIP}" Sharepoint_ToolBox.ps1 lang/ examples/
echo "ZIP=${ZIP}" >> "$GITHUB_ENV"
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
- name: Create release
run: |
RELEASE_ID=$(curl -sf -X POST \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"${{ env.VERSION }}\",\"name\":\"SharePoint ToolBox ${{ env.VERSION }}\",\"body\":\"1. Download and extract the archive\\n2. Launch Sharepoint_ToolBox.ps1 with PowerShell\\n\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "RELEASE_ID=${RELEASE_ID}" >> "$GITHUB_ENV"
- name: Upload asset
run: |
curl -sf -X POST \
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${{ env.RELEASE_ID }}/assets" \
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
-F "attachment=@${{ env.ZIP }}"

31
.gitignore vendored
View File

@@ -1,10 +1,21 @@
.claude # Build outputs
*.html bin/
*.json obj/
!lang/ publish/
!lang/*.json
!.planning/ # IDE
!.planning/** .vs/
!wiki/ *.user
!wiki/*.html *.suo
!wiki/*.md
# Claude Code
.claude/
# OS
Thumbs.db
Desktop.ini
# Secrets
*.pfx
appsettings.*.json
Sharepoint_Settings.json

79
.planning/10-CONTEXT.md Normal file
View File

@@ -0,0 +1,79 @@
---
phase: 10
title: Branding Data Foundation
status: ready-for-planning
created: 2026-04-08
---
# Phase 10 Context: Branding Data Foundation
## Decided Areas (from prior research + STATE.md)
These are locked — do not re-litigate during planning or execution.
| Decision | Value |
|---|---|
| Logo storage format | Base64 strings in JSON (not file paths) |
| MSP logo location | `BrandingSettings.cs` model → `branding.json` |
| Client logo location | On `TenantProfile` model (per-tenant) |
| File path after import | Discarded — only base64 persists |
| SVG support | Rejected (XSS risk) — PNG/JPG only |
| User directory service | New `GraphUserDirectoryService`, separate from `GraphUserSearchService` |
| Directory auto-load | No — explicit "Load Directory" button required |
| New NuGet packages | None — existing stack covers everything |
| Export service signature | Optional `ReportBranding? branding = null` parameter on existing export methods |
## Discussed Areas
### 1. Logo Metadata Model
**Decision:** Store base64 + MIME type as separate fields in a shared `LogoData` record.
- Shared model: `LogoData { string Base64, string MimeType }` — used by both MSP logo (in `BrandingSettings`) and client logo (on `TenantProfile`)
- `MimeType` is `"image/png"` or `"image/jpeg"`, determined at import time from magic bytes
- No other metadata stored — no original filename, dimensions, or import date
- Export services concatenate `data:{MimeType};base64,{Base64}` for HTML `<img>` tags
- WPF preview converts `Base64` bytes to `BitmapImage` directly
### 2. Logo Validation & Compression
**Decision:** Validate format via magic bytes, auto-compress oversized files silently.
- **Format detection:** Read file header magic bytes only — ignore file extension entirely
- PNG signature: `89 50 4E 47` (first 4 bytes)
- JPEG signature: `FF D8 FF` (first 3 bytes)
- Anything else → reject with specific error message (e.g., "File format is BMP, only PNG and JPG are accepted")
- **Size handling:** If file exceeds 512 KB, auto-compress silently (no user notification)
- Strategy: resize dimensions (e.g., max 300px width/height) + reduce quality
- Keep original format — PNG stays PNG, JPEG stays JPEG (no format conversion)
- Compress until under 512 KB
- **Dimension limits:** None — the 512 KB cap and compression handle naturally
- **Validation errors:** Specific messages for format rejection (format-related only, since size is auto-handled)
### 3. Profile Deletion & Duplication Behavior
**Decision:** Warn about logo loss on deletion; do NOT copy logo on duplication.
- **Deletion:** When deleting a tenant profile that has a client logo, the confirmation message explicitly mentions it: "This will also remove its client logo." The logo is embedded in the profile JSON, so deletion is automatic — no orphaned files.
- **Duplication:** Duplicating a tenant profile copies connection fields (Name, TenantUrl, ClientId) but starts with a blank client logo. The user must re-import or auto-pull for the new profile. Rationale: duplicated profiles are typically for different tenants, so the logo shouldn't carry over.
## Deferred Ideas (out of scope for Phase 10)
- Logo preview in Settings UI (Phase 12)
- Auto-pull client logo from Entra branding API (Phase 11/12)
- Report header layout with logos side-by-side (Phase 11)
- "Load Directory" button placement decision (Phase 14)
- Session-scoped directory cache (UDIR-F01, deferred)
## code_context
| Asset | Path | Reuse |
|---|---|---|
| TenantProfile model | `SharepointToolbox/Core/Models/TenantProfile.cs` | Extend with `LogoData? ClientLogo` property |
| AppSettings model | `SharepointToolbox/Core/Models/AppSettings.cs` | Reference for BrandingSettings pattern |
| SettingsRepository | `SharepointToolbox/Infrastructure/Persistence/SettingsRepository.cs` | Clone pattern for BrandingRepository (write-then-replace + SemaphoreSlim) |
| ProfileRepository | `SharepointToolbox/Infrastructure/Persistence/ProfileRepository.cs` | Already handles TenantProfile persistence — will serialize new logo field |
| GraphUserSearchService | `SharepointToolbox/Services/GraphUserSearchService.cs` | Reference for Graph SDK usage, auth, and query patterns |
| GraphClientFactory | `SharepointToolbox/Infrastructure/Auth/GraphClientFactory.cs` | Provides `GraphServiceClient` for new directory service |
| DI registration | `SharepointToolbox/App.xaml.cs` (lines 73-163) | Register new BrandingRepository, BrandingService, GraphUserDirectoryService |
| Profile deletion UI | `SharepointToolbox/ViewModels/ProfileManagementViewModel.cs` | Update deletion confirmation message to mention logo |

View File

@@ -0,0 +1,155 @@
# Milestone Audit: SharePoint Toolbox v2 — v1 Release
**Audited:** 2026-04-07
**Milestone:** v1 (5 phases, 42 requirements)
**Verdict:** PASSED — all requirements satisfied, all phases integrated, build and tests green
---
## Phase Verification Summary
| Phase | Status | Score | Verification |
|-------|--------|-------|-------------|
| 01 — Foundation | PASSED | 11/11 | 01-VERIFICATION.md |
| 02 — Permissions | HUMAN_NEEDED | 7/7 automated | 02-VERIFICATION.md (2 human items pending) |
| 03 — Storage & File Ops | **MISSING** | No VERIFICATION.md | Summaries exist for all 8 plans; integration checker confirmed all wiring |
| 04 — Bulk Ops & Provisioning | HUMAN_NEEDED | 12/12 automated | 04-VERIFICATION.md (7 human items pending) |
| 05 — Distribution & Hardening | HUMAN_NEEDED | 6/6 automated | 05-VERIFICATION.md (2 human items pending) |
### Gap: Phase 03 Missing Verification
Phase 03 has no `03-VERIFICATION.md` file. All 8 plan summaries exist and confirm code was delivered. The integration checker independently verified:
- All 3 Phase 3 service interfaces (IStorageService, ISearchService, IDuplicatesService) registered in DI
- All 5 export services registered and wired to ViewModels
- All 4 Phase 3 tabs (Storage, Search, Duplicates + exports) wired in MainWindow
- 13 Phase 3 requirements (STOR-0105, SRCH-0104, DUPL-0103) covered
**Recommendation:** Run a retroactive phase verification for Phase 03 or accept integration checker evidence as sufficient.
---
## Requirements Coverage
All 42 v1 requirements are marked complete in REQUIREMENTS.md with phase traceability:
| Category | IDs | Count | Status |
|----------|-----|-------|--------|
| Foundation | FOUND-01 to FOUND-12 | 12 | All SATISFIED |
| Permissions | PERM-01 to PERM-07 | 7 | All SATISFIED |
| Storage | STOR-01 to STOR-05 | 5 | All SATISFIED |
| File Search | SRCH-01 to SRCH-04 | 4 | All SATISFIED |
| Duplicates | DUPL-01 to DUPL-03 | 3 | All SATISFIED |
| Templates | TMPL-01 to TMPL-04 | 4 | All SATISFIED |
| Folder Structure | FOLD-01 to FOLD-02 | 2 | All SATISFIED |
| Bulk Operations | BULK-01 to BULK-05 | 5 | All SATISFIED |
| **Total** | | **42** | **42/42 mapped and complete** |
**Orphaned requirements:** None
**Unmapped requirements:** None
---
## Cross-Phase Integration
Integration checker ran full verification. Results:
| Check | Status |
|-------|--------|
| DI wiring (all 5 phases) | PASS — all services registered in App.xaml.cs |
| MainWindow tabs (10 tabs) | PASS — all declared and wired from DI |
| FeatureViewModelBase inheritance (10 VMs) | PASS |
| SessionManager usage (9 ViewModels + SiteListService) | PASS |
| ExecuteQueryRetryHelper (9 CSOM services, 40+ call sites) | PASS |
| SharePointPaginationHelper (2 services using list enumeration) | PASS |
| TranslationSource localization (15 XAML files, 170 bindings) | PASS |
| TenantSwitchedMessage propagation | PASS |
| Export chain completeness (all features) | PASS |
| Build | PASS — 0 warnings, 0 errors |
| Tests | PASS — 134 passed, 22 skipped (live CSOM), 0 failed |
| EN/FR key parity | PASS — 199/199 keys |
**Orphaned code:** `FeatureTabBase.xaml` — Phase 1 placeholder, now superseded by full tab views. Harmless dead code.
---
## Tech Debt & Deferred Items
### From Phase Verifications
| Item | Source | Severity | Description |
|------|--------|----------|-------------|
| Hardcoded export button text | Phase 2 | Info | `PermissionsView.xaml` uses `Content="Export CSV"` / `"Export HTML"` instead of `rad.csv.perms` / `rad.html.perms` localization keys. French users see English button labels. |
| Missing Designer.cs property | Phase 2 | Info | `Strings.Designer.cs` lacks `tab_permissions` typed accessor. Runtime binding via `TranslationSource` works fine. |
| No invalid-row highlighting | Phase 4 | Warning | `BulkMembersView.xaml`, `BulkSitesView.xaml`, `FolderStructureView.xaml` show IsValid as text column but lack `RowStyle` + `DataTrigger` for visual red highlighting on invalid rows. |
| FeatureTabBase dead code | Phase 1→all | Info | `Views/Controls/FeatureTabBase.xaml` is no longer imported by any tab view after all phases replaced stubs. |
| Cancel test locale mismatch | Phase 3 (03-08) | Info | `FeatureViewModelBaseTests.CancelCommand_DuringOperation_SetsStatusMessageToCancelled` asserts `.Contains("cancel")` but app returns French string "Opération annulée". Pre-existing; deferred. |
### Deferred v2 Requirements
These are explicitly out of scope for v1 and tracked in REQUIREMENTS.md:
- UACC-01/02: User access audit across sites
- SIMP-01/02/03: Simplified plain-language permission reports
- VIZZ-01/02/03: Storage metrics graphs (pie/bar chart)
---
## Human Verification Backlog
11 items across 3 phases require human confirmation (runtime UI/locale checks that cannot be automated):
### Phase 2 (2 items)
1. Full Permissions tab UI visual checkpoint (layout, disabled states, French locale)
2. Export button localization decision (accept hardcoded English or bind to resx keys)
### Phase 4 (7 items)
1. Application launches with all 10 tabs visible
2. Bulk Members — Load Example populates DataGrid with 7 rows
3. Bulk Sites — semicolon CSV auto-detection works
4. Invalid row display in DataGrid (IsValid=False, Errors column)
5. Confirmation dialog appears before bulk operations
6. Transfer tab — two-step browse flow (SitePickerDialog → FolderBrowserDialog)
7. Templates tab — 5 capture checkboxes visible and checked by default
### Phase 5 (2 items)
1. Clean-machine EXE launch (no .NET runtime installed)
2. French locale runtime rendering (diacritics display correctly in all tabs)
---
## Build & Test Summary
| Metric | Value |
|--------|-------|
| Build | 0 errors, 0 warnings |
| Tests passed | 134 |
| Tests skipped | 22 (live CSOM — expected) |
| Tests failed | 0 |
| EN locale keys | 199 |
| FR locale keys | 199 |
| Published EXE | 200.9 MB self-contained |
| Phases complete | 5/5 |
| Requirements satisfied | 42/42 |
---
## Verdict
**PASSED** — The milestone has achieved its definition of done:
1. All 42 v1 requirements are implemented with real code and verified by phase-level checks
2. All cross-phase integration points are wired (DI, messaging, shared infrastructure)
3. Build compiles cleanly with zero warnings
4. 134 automated tests pass with zero failures
5. Self-contained 200.9 MB EXE produced successfully
6. Full EN/FR locale parity (199 keys each)
**Remaining actions before shipping:**
- [ ] Complete 11 human verification items (UI visual checks, clean-machine launch)
- [ ] Decide on Phase 03 retroactive verification (or accept integration check as sufficient)
- [ ] Address 3 Warning-level tech debt items (invalid-row highlighting in bulk DataGrids)
- [ ] Optionally clean up FeatureTabBase dead code and fix cancel test locale mismatch
---
*Audited: 2026-04-07*
*Auditor: Claude (milestone audit)*

23
.planning/MILESTONES.md Normal file
View File

@@ -0,0 +1,23 @@
# Milestones
## v1.0 MVP (Shipped: 2026-04-07)
**Phases completed:** 5 phases, 36 plans | 164 commits | 10,071 LOC (C# + XAML)
**Timeline:** 28 days (2026-03-10 → 2026-04-07)
**Tests:** 134 pass, 22 skip (live CSOM), 0 fail
**Key accomplishments:**
- Full C#/WPF rewrite of 6,400-line PowerShell tool into 10,071-line MVVM application
- Multi-tenant MSAL authentication with per-tenant token caching and instant switching
- SharePoint permissions scanner with multi-site support, CSV/HTML export, 5,000-item pagination
- Storage metrics, file search, and duplicate detection with configurable depth and exports
- Bulk operations (members, sites, file transfer) with per-item error reporting, retry, and cancellation
- Self-contained 200 MB EXE with full EN/FR localization (199 keys each)
**Archives:**
- [v1.0-ROADMAP.md](milestones/v1.0-ROADMAP.md)
- [v1.0-REQUIREMENTS.md](milestones/v1.0-REQUIREMENTS.md)
- [v1.0-MILESTONE-AUDIT.md](milestones/v1.0-MILESTONE-AUDIT.md)
---

View File

@@ -2,66 +2,122 @@
## What This Is ## What This Is
A full C#/WPF rewrite of an existing PowerShell-based SharePoint Online administration and auditing tool. The app lets IT administrators manage permissions, analyze storage, search files, detect duplicates, manage site templates, and perform bulk operations across SharePoint Online and Teams sites. It's a local desktop tool used by MSPs and IT teams managing multiple client tenants. A C#/WPF desktop application for IT administrators and MSPs to audit and manage SharePoint Online permissions, storage, files, and sites across multiple client tenants. Replaces a 6,400-line monolithic PowerShell script with a structured 10,071-line MVVM application shipping as a single self-contained EXE.
## Core Value ## Core Value
Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application. Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application.
## Current State
**Shipped:** v2.2 Report Branding & User Directory (2026-04-09)
**Status:** Active — v2.3 Tenant Management & Report Enhancements
## Current Milestone: v2.3 Tenant Management & Report Enhancements
**Goal:** Streamline tenant onboarding with automated app registration, add self-healing ownership for access-denied sites, and enhance report output with group expansion and entry consolidation.
**Target features:**
- App registration on target tenant (auto via Graph API + guided fallback) during profile create/edit
- App removal from target tenant
- Auto-take ownership of SharePoint sites on access denied (global toggle)
- Expand groups in HTML reports (clickable to show members)
- Report consolidation toggle (merge duplicate user entries across locations)
<details>
<summary>v2.2 shipped features</summary>
- HTML report branding with MSP logo (global) and client logo (per tenant)
- Auto-pull client logo from Entra branding API
- Logo validation (PNG/JPG, 512 KB limit) with auto-compression
- User directory browse mode in user access audit tab with paginated load
- Member/guest filter and department/job title columns
- Directory user selection triggers existing audit pipeline
</details>
<details>
<summary>v1.1 shipped features</summary>
- Global multi-site selection in toolbar (pick sites once, all tabs use them)
- User access audit tab with Graph API people-picker, direct/group/inherited access distinction
- Simplified permissions with plain-language labels, color-coded risk levels, detail-level toggle
- Storage visualization with LiveCharts2 pie/donut and bar charts by file type
</details>
Tech stack: C# / WPF / .NET 10 / PnP Framework / Microsoft Graph SDK / MSAL / Serilog / CommunityToolkit.Mvvm / LiveCharts2
Tests: 285 automated (xUnit), 26 skipped (require live SharePoint tenant)
Distribution: 200 MB self-contained EXE (win-x64)
LOC: ~16,900 C#
## Requirements ## Requirements
### Validated ### Validated
(None yet — ship to validate) - Full C#/WPF rewrite of all existing PowerShell features — v1.0
- Multi-tenant authentication with cached sessions — v1.0
- Thorough error handling (per-item reporting, no silent failures) — v1.0
- Modular architecture (separate files per feature area, DI, MVVM) — v1.0
- Self-contained single EXE distribution — v1.0
### Active ### Shipped in v1.1
- [ ] Full C#/WPF rewrite of all existing PowerShell features - [x] Global multi-site selection in toolbar (SITE-01/02) — v1.1
- [ ] Multi-tenant authentication with cached sessions (switch between client tenants instantly) - [x] Export all SharePoint/Teams accesses a specific user has across selected sites (UACC-01/02) — v1.1
- [ ] Export all SharePoint/Teams accesses a specific user has across selected sites - [x] Simplified permissions reports (plain language, summary views) (SIMP-01/02/03) — v1.1
- [ ] Simplified permissions reports (plain language, summary views, reduced jargon for untrained users) - [x] Storage metrics graph by file type (pie/donut and bar chart, toggleable) (VIZZ-01/02/03) — v1.1
- [ ] Storage metrics graph by file type (pie/donut and bar chart, toggleable) in Storage Metrics tab
- [ ] Thorough error handling cleanup (eliminate silent failures, proper error reporting) ### Shipped in v2.2
- [ ] Modular architecture (separate files per feature area)
- [ ] Self-contained single EXE distribution (no .NET runtime dependency) - [x] HTML report branding with MSP and client logos (BRAND-01/02/03/04/05/06) — v2.2
- [x] User directory browse mode in user access audit tab (UDIR-01/02/03/04/05) — v2.2
### Active in v2.3
- [ ] Automated app registration on target tenant with guided fallback
- [ ] App removal from target tenant
- [ ] Auto-take ownership of sites on access denied (global toggle)
- [ ] Expand groups in HTML reports
- [ ] Report consolidation toggle (merge duplicate entries)
### Out of Scope ### Out of Scope
- Cross-platform support (Mac/Linux) — Windows-only desktop tool, MAUI/Avalonia not justified - Cross-platform support (Mac/Linux) — WPF is Windows-only; not justified for current user base
- SQLite or database storage — JSON sufficient for config, profiles, and templates - SQLite or database storage — JSON sufficient for config, profiles, and templates
- Web-based UI — must remain a local desktop application - Web-based UI — must remain a local desktop application
- Cloud/SaaS deployment — local tool by design - Cloud/SaaS deployment — local tool by design
- Mobile support — desktop admin tool - Mobile support — desktop admin tool
- Real-time monitoring / alerts — requires background service, beyond scope
- Automated remediation (auto-revoke) — liability risk
- Content migration between tenants — separate product category
## Context ## Context
- **Existing codebase:** 6,400-line monolithic PowerShell script (`Sharepoint_ToolBox.ps1`) with WinForms UI - **v1.0 shipped** with full feature parity: permissions, storage, search, duplicates, bulk operations, templates, folder provisioning
- **Current features to port:** Permissions reports, storage metrics, site templates, file search, duplicate detection, bulk operations (transfer, site creation, member addition), folder structure creation, localization (EN/FR) - **v1.1 shipped** with enhanced reports: user access audit, simplified permissions, storage charts, global site selection
- **SharePoint integration:** Currently uses PnP.PowerShell module; C# rewrite will use PnP Framework / Microsoft Graph SDK - **v2.2 shipped** with report branding (logos in HTML exports) and user directory browse mode
- **Authentication:** Currently interactive Azure AD OAuth via PnP; new version needs multi-tenant session caching - **Localization:** 230+ EN/FR keys, full parity verified
- **Known issues in current app:** 38 silent catch blocks, 27 error suppressions, resource cleanup issues, UI freezes on large datasets, no operation cancellation - **Architecture:** 140+ C# files + 17 XAML files across Core/Infrastructure/Services/ViewModels/Views layers
- **Localization:** English and French supported, key-based translation system
- **Report exports:** CSV and interactive HTML reports with embedded JS for sorting/filtering
## Constraints ## Constraints
- **Platform:** Windows desktop only — WPF requires Windows - **Platform:** Windows desktop only — WPF requires Windows
- **Distribution:** Self-contained EXE (~150MB) — no .NET runtime dependency for end users - **Distribution:** Self-contained EXE (~200 MB) — no .NET runtime dependency
- **Auth method:** Interactive browser-based Azure AD login (no client secrets or certificates stored) - **Auth method:** Interactive browser-based Azure AD login (no client secrets stored)
- **Data storage:** JSON files for profiles, settings, templates — same format as current app for migration - **Data storage:** JSON files for profiles, settings, templates
- **SharePoint API:** PnP Framework / Microsoft Graph SDK for C# (replaces PnP.PowerShell) - **SharePoint API:** PnP Framework / Microsoft Graph SDK
- **Local only:** No telemetry, no cloud services, no external dependencies at runtime - **Local only:** No telemetry, no cloud services, no external dependencies at runtime
## Key Decisions ## Key Decisions
| Decision | Rationale | Outcome | | Decision | Rationale | Outcome |
|----------|-----------|---------| |----------|-----------|---------|
| Rewrite to C#/WPF instead of improving PowerShell | Better async/await, proper OOP, richer UI, better tooling — worth the investment for long-term maintainability | — Pending | | Rewrite to C#/WPF instead of improving PowerShell | Better async/await, proper OOP, richer UI, better tooling | ✓ Good — 10k LOC structured app vs 6.4k monolithic script |
| WPF over WinForms | Modern data binding, MVVM pattern, richer styling for better UX | — Pending | | WPF over WinForms | Modern data binding, MVVM pattern, richer styling | ✓ Good — clean separation of concerns |
| Self-contained EXE | Users shouldn't need to install .NET runtime — simplifies distribution to clients | — Pending | | Self-contained EXE | Users shouldn't need to install .NET runtime | ✓ Good — 200 MB single file, zero dependencies |
| Keep JSON storage | Simple, human-readable, sufficient for config/profiles — no need for SQLite complexity | — Pending | | Keep JSON storage | Simple, human-readable, sufficient for config/profiles | ✓ Good — atomic write-then-replace pattern works well |
| Multi-tenant session caching | MSP workflow requires fast switching between client tenants without re-authenticating each time | — Pending | | Multi-tenant session caching | MSP workflow requires fast switching between tenants | ✓ Good — per-clientId MSAL PCA with MsalCacheHelper |
| Pie + bar chart toggle for storage | Gives users flexibility to view data in preferred format | — Pending | | BulkOperationRunner pattern | Continue-on-error with per-item results for all bulk ops | ✓ Good — consistent error handling across 4 bulk features |
| Wave 0 scaffold pattern | Models + interfaces + test stubs before implementation | ✓ Good — all phases had test targets from day 1 |
--- ---
*Last updated: 2026-04-02 after initialization* *Last updated: 2026-04-09 after v2.3 milestone started*

View File

@@ -1,168 +1,71 @@
# Requirements: SharePoint Toolbox v2 # Requirements: SharePoint Toolbox v2.3
**Defined:** 2026-04-02 **Defined:** 2026-04-09
**Core Value:** Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application. **Core Value:** Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application.
## v1 Requirements ## v2.3 Requirements
Requirements for initial release. Each maps to roadmap phases. Requirements for v2.3 Tenant Management & Report Enhancements. Each maps to roadmap phases.
### Foundation ### App Registration
- [x] **FOUND-01**: Application built with C#/WPF (.NET 10 LTS) using MVVM architecture - [x] **APPREG-01**: User can register the app on a target tenant from the profile create/edit dialog
- [x] **FOUND-02**: Multi-tenant profile registry — user can create, rename, delete, and switch between tenant profiles (tenant URL, client ID, display name) - [x] **APPREG-02**: App auto-detects if user has Global Admin permissions before attempting registration
- [x] **FOUND-03**: Multi-tenant session caching — user stays authenticated across tenant switches without re-logging in (MSAL token cache per tenant) - [x] **APPREG-03**: App creates Azure AD application + service principal + grants required permissions atomically (with rollback on failure)
- [x] **FOUND-04**: Interactive Azure AD OAuth login via browser — no client secrets or certificates stored - [x] **APPREG-04**: User sees guided fallback instructions when auto-registration is not possible (insufficient permissions)
- [x] **FOUND-05**: All long-running operations report progress to the UI in real-time - [x] **APPREG-05**: User can remove the app registration from a target tenant
- [x] **FOUND-06**: User can cancel any long-running operation mid-execution - [x] **APPREG-06**: App clears cached tokens and sessions when app registration is removed
- [x] **FOUND-07**: All errors surface to the user with actionable messages — no silent failures
- [x] **FOUND-08**: Structured logging for diagnostics (Serilog or equivalent)
- [x] **FOUND-09**: Localization system supporting English and French with dynamic language switching
- [x] **FOUND-10**: JSON-based local storage for profiles, settings, and templates (compatible with current app's format for migration)
- [x] **FOUND-11**: Self-contained single EXE distribution — no .NET runtime dependency for end users
- [x] **FOUND-12**: Configurable data output folder for exports
### Permissions ### Site Ownership
- [x] **PERM-01**: User can scan permissions on a single SharePoint site with configurable depth - [x] **OWN-01**: User can enable/disable auto-take-ownership in application settings (global toggle, OFF by default)
- [x] **PERM-02**: User can scan permissions across multiple selected sites in one operation - [x] **OWN-02**: App automatically takes site collection admin ownership when encountering access denied during scans (when toggle is ON)
- [x] **PERM-03**: Permissions scan includes owners, members, guests, external users, and broken inheritance
- [x] **PERM-04**: User can choose to include or exclude inherited permissions
- [x] **PERM-05**: User can export permissions report to CSV (raw data)
- [x] **PERM-06**: User can export permissions report to interactive HTML (sortable, filterable, groupable by user)
- [x] **PERM-07**: SharePoint 5,000-item list view threshold handled via pagination — no silent failures on large libraries
### Storage ### Report Enhancements
- [x] **STOR-01**: User can view storage consumption per library on a site - [x] **RPT-01**: User can expand SharePoint groups in HTML reports to see group members
- [x] **STOR-02**: User can view storage consumption per site with configurable folder depth - [x] **RPT-02**: Group member resolution uses transitive membership to include nested group members
- [x] **STOR-03**: Storage metrics include total size, version size, item count, and last modified date - [x] **RPT-03**: User can enable/disable entry consolidation per export (toggle in export settings)
- [x] **STOR-04**: User can export storage metrics to CSV - [x] **RPT-04**: Consolidated reports merge rows for the same user with identical access levels across multiple locations into a single row
- [x] **STOR-05**: User can export storage metrics to interactive HTML with collapsible tree view
### File Search ## Future Requirements
- [x] **SRCH-01**: User can search files across sites using multiple criteria (extension, name/regex, dates, creator, editor) ### Site Ownership (deferred)
- [x] **SRCH-02**: User can configure maximum search results (up to 50,000)
- [x] **SRCH-03**: User can export search results to CSV
- [x] **SRCH-04**: User can export search results to interactive HTML (sortable, filterable)
### Duplicate Detection - **OWN-03**: Persistent cleanup-pending list tracking sites where ownership was elevated
- **OWN-04**: Startup warning when stale ownership entries exist from previous sessions
- [x] **DUPL-01**: User can scan for duplicate files by name, size, creation date, modification date
- [x] **DUPL-02**: User can scan for duplicate folders by name, subfolder count, file count
- [x] **DUPL-03**: User can export duplicate report to HTML with grouped display and visual indicators
### Site Templates
- [x] **TMPL-01**: User can capture site structure (libraries, folders, permission groups, logo, settings) as a template
- [x] **TMPL-02**: User can apply template to create new Communication or Teams site
- [x] **TMPL-03**: Templates persist locally as JSON
- [x] **TMPL-04**: User can manage templates (create, rename, delete)
### Folder Structure
- [x] **FOLD-01**: User can create folder structures on a site from a CSV template
- [x] **FOLD-02**: Example CSV templates provided for common structures
### Bulk Operations
- [x] **BULK-01**: User can transfer files and folders between sites with progress tracking
- [x] **BULK-02**: User can add members to groups in bulk from CSV
- [x] **BULK-03**: User can create multiple sites in bulk from CSV
- [x] **BULK-04**: All bulk operations support cancellation mid-execution
- [x] **BULK-05**: Bulk operation errors are reported per-item (not silently skipped)
## v2 Requirements
Deferred to after v1 parity is confirmed. New features from project goals.
### User Access Audit
- **UACC-01**: User can export all SharePoint/Teams accesses a specific user has across selected sites
- **UACC-02**: Export includes direct assignments, group memberships, and inherited access
### Simplified Permissions
- **SIMP-01**: User can toggle plain-language permission labels (e.g., "Can edit files" instead of "Contribute")
- **SIMP-02**: Permissions report includes summary counts and color coding for untrained readers
- **SIMP-03**: Configurable detail level (simple/detailed) for reports
### Storage Visualization
- **VIZZ-01**: Storage Metrics tab includes a graph showing space by file type
- **VIZZ-02**: User can toggle between pie/donut chart and bar chart views
- **VIZZ-03**: Graph updates when storage scan completes
## Out of Scope ## Out of Scope
| Feature | Reason | | Feature | Reason |
|---------|--------| |---------|--------|
| Cross-platform (Mac/Linux) | WPF is Windows-only; not justified for current user base | | Auto-revoke permissions | Liability risk — read-only auditing tool, not remediation |
| Real-time monitoring / alerts | Requires background service, webhooks — turns desktop tool into a service | | Real-time ownership monitoring | Requires background service, beyond scope of desktop tool |
| Automated remediation (auto-revoke) | Liability risk; one wrong rule destroys client access | | Group expansion in CSV reports | CSV format doesn't support expandable sections; consolidation covers the dedup need |
| SQLite / database storage | Breaks single-EXE distribution; JSON sufficient | | Custom permission scope selection for app registration | Fixed scope set covers all Toolbox features; custom scopes add complexity without value |
| Cloud sync / shared profiles | Requires server infrastructure — out of scope for local tool |
| AI-powered recommendations | Competes with Microsoft's own Copilot roadmap |
| Content migration between tenants | Separate product category (ShareGate territory) |
| Mobile app | Desktop admin tool |
| OAuth with client secrets/certificates | Interactive login only — no stored credentials |
| Version history management | Deep separate problem; surface totals in storage metrics only |
## Traceability ## Traceability
Which phases cover which requirements. Updated during roadmap creation.
| Requirement | Phase | Status | | Requirement | Phase | Status |
|-------------|-------|--------| |-------------|-------|--------|
| FOUND-01 | Phase 1 | Complete | | APPREG-01 | Phase 19 | Complete |
| FOUND-02 | Phase 1 | Complete | | APPREG-02 | Phase 19 | Complete |
| FOUND-03 | Phase 1 | Complete | | APPREG-03 | Phase 19 | Complete |
| FOUND-04 | Phase 1 | Complete | | APPREG-04 | Phase 19 | Complete |
| FOUND-05 | Phase 1 | Complete | | APPREG-05 | Phase 19 | Complete |
| FOUND-06 | Phase 1 | Complete | | APPREG-06 | Phase 19 | Complete |
| FOUND-07 | Phase 1 | Complete | | OWN-01 | Phase 18 | Complete |
| FOUND-08 | Phase 1 | Complete | | OWN-02 | Phase 18 | Complete |
| FOUND-09 | Phase 1 | Complete | | RPT-01 | Phase 17 | Complete |
| FOUND-10 | Phase 1 | Complete | | RPT-02 | Phase 17 | Complete |
| FOUND-11 | Phase 5 | Complete | | RPT-03 | Phase 16 | Complete |
| FOUND-12 | Phase 1 | Complete | | RPT-04 | Phase 15 | Complete |
| PERM-01 | Phase 2 | Complete |
| PERM-02 | Phase 2 | Complete |
| PERM-03 | Phase 2 | Complete |
| PERM-04 | Phase 2 | Complete |
| PERM-05 | Phase 2 | Complete |
| PERM-06 | Phase 2 | Complete |
| PERM-07 | Phase 2 | Complete |
| STOR-01 | Phase 3 | Complete |
| STOR-02 | Phase 3 | Complete |
| STOR-03 | Phase 3 | Complete |
| STOR-04 | Phase 3 | Complete |
| STOR-05 | Phase 3 | Complete |
| SRCH-01 | Phase 3 | Complete |
| SRCH-02 | Phase 3 | Complete |
| SRCH-03 | Phase 3 | Complete |
| SRCH-04 | Phase 3 | Complete |
| DUPL-01 | Phase 3 | Complete |
| DUPL-02 | Phase 3 | Complete |
| DUPL-03 | Phase 3 | Complete |
| TMPL-01 | Phase 4 | Complete |
| TMPL-02 | Phase 4 | Complete |
| TMPL-03 | Phase 4 | Complete |
| TMPL-04 | Phase 4 | Complete |
| FOLD-01 | Phase 4 | Complete |
| FOLD-02 | Phase 4 | Complete |
| BULK-01 | Phase 4 | Complete |
| BULK-02 | Phase 4 | Complete |
| BULK-03 | Phase 4 | Complete |
| BULK-04 | Phase 4 | Complete |
| BULK-05 | Phase 4 | Complete |
**Coverage:** **Coverage:**
- v1 requirements: 42 total - v2.3 requirements: 12 total
- Mapped to phases: 42 - Mapped to phases: 12
- Unmapped: 0 - Unmapped: 0
--- ---
*Requirements defined: 2026-04-02* *Requirements defined: 2026-04-09*
*Last updated: 2026-04-02 after roadmap creation — all 42 v1 requirements mapped* *Last updated: 2026-04-09 after roadmap created*

View File

@@ -0,0 +1,53 @@
# Retrospective
## Milestone: v1.0 — MVP
**Shipped:** 2026-04-07
**Phases:** 5 | **Plans:** 36 | **Commits:** 164 | **LOC:** 10,071
### What Was Built
- Complete C#/WPF rewrite replacing 6,400-line PowerShell monolith
- 10 feature tabs: Permissions, Storage, Search, Duplicates, Transfer, Bulk Members, Bulk Sites, Folder Structure, Templates, Settings
- Multi-tenant MSAL authentication with per-tenant token caching
- CSV and interactive HTML export across all scan features
- Bulk operations with continue-on-error, per-item reporting, retry, and cancellation
- Self-contained 200 MB EXE with full EN/FR localization (199 keys)
### What Worked
- **Wave 0 scaffold pattern**: Creating models, interfaces, and test stubs before implementation gave every phase testable targets from day 1
- **FeatureViewModelBase contract**: Establishing the async/cancel/progress pattern in Phase 1 meant Phases 2-4 had zero friction adding new features
- **BulkOperationRunner abstraction**: One shared helper gave consistent error semantics across 4 different bulk operations
- **Phase dependency ordering**: Foundation → Permissions → Storage → Bulk → Hardening prevented rework
- **Atomic commits per task**: Each plan produced clear, reviewable commit history
### What Was Inefficient
- **Phase 03 missing verification**: The only phase without a VERIFICATION.md — caught during milestone audit, but should have been produced during execution
- **Stale audit notes**: Phase 2 verification reported export buttons as hardcoded English, but they were actually already localized by the time of the audit — suggests verification reads code at a point-in-time snapshot that may not reflect later fixes
- **Cancel test locale mismatch**: The French locale test failure was flagged in Phase 3 plan 08 summary but deferred until post-milestone cleanup — should have been fixed inline
### Patterns Established
- Write-then-replace JSON persistence with SemaphoreSlim for thread safety
- TranslationSource singleton with PropertyChanged(string.Empty) for runtime culture switching
- ExecuteQueryRetryHelper for throttle-aware CSOM calls (429/503 detection)
- SharePointPaginationHelper with ListItemCollectionPosition for 5,000+ item lists
- CsvValidationService with auto-delimiter detection (comma/semicolon) and BOM handling
- DataGrid RowStyle DataTrigger for invalid-row visual highlighting
### Key Lessons
- Establish shared infrastructure patterns (auth, retry, pagination, progress) in Phase 1 — every subsequent phase benefits
- Test scaffolds (Wave 0) eliminate the "no tests until the end" anti-pattern
- Phase verifications should be mandatory during execution, not optional — catching Phase 03's gap at audit time is late
- Localization tests (key parity + diacritic spot-checks) are cheap and catch real bugs
---
## Cross-Milestone Trends
| Metric | v1.0 |
|--------|------|
| Phases | 5 |
| Plans | 36 |
| LOC | 10,071 |
| Tests | 134 pass / 22 skip |
| Timeline | 28 days |
| Commits | 164 |

View File

@@ -1,147 +1,136 @@
# Roadmap: SharePoint Toolbox v2 # Roadmap: SharePoint Toolbox v2
## Overview ## Milestones
A full C#/WPF rewrite of a 6,400-line PowerShell-based SharePoint Online administration tool. The -**v1.0 MVP** — Phases 1-5 (shipped 2026-04-07) — [archive](milestones/v1.0-ROADMAP.md)
project delivers a self-contained Windows desktop application that lets MSP administrators audit -**v1.1 Enhanced Reports** — Phases 6-9 (shipped 2026-04-08) — [archive](milestones/v1.1-ROADMAP.md)
and manage permissions, storage, and site provisioning across multiple client tenants from a single -**v2.2 Report Branding & User Directory** — Phases 10-14 (shipped 2026-04-09) — [archive](milestones/v2.2-ROADMAP.md)
tool. Foundation infrastructure (multi-tenant auth, async patterns, error handling, DI) must be - 🔄 **v2.3 Tenant Management & Report Enhancements** — Phases 15-19 (in progress)
solid before any feature work begins — all 10 identified pitfalls in the existing codebase map
entirely to Phase 1. Subsequent phases deliver complete, verifiable feature areas in dependency
order: permissions first (validates auth and pagination), then storage and search (reuse those
patterns), then bulk and provisioning operations (highest write-risk features last), then
hardening and packaging.
## Phases ## Phases
**Phase Numbering:** <details>
- Integer phases (1, 2, 3): Planned milestone work <summary>✅ v1.0 MVP (Phases 1-5) — SHIPPED 2026-04-07</summary>
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
Decimal phases appear between their surrounding integers in numeric order. - [x] Phase 1: Foundation (8/8 plans) — completed 2026-04-02
- [x] Phase 2: Permissions (7/7 plans) — completed 2026-04-02
- [x] Phase 3: Storage and File Operations (8/8 plans) — completed 2026-04-02
- [x] Phase 4: Bulk Operations and Provisioning (10/10 plans) — completed 2026-04-03
- [x] Phase 5: Distribution and Hardening (3/3 plans) — completed 2026-04-03
- [x] **Phase 1: Foundation** - WPF shell, multi-tenant auth, DI, async patterns, error handling, logging, localization, JSON persistence (completed 2026-04-02) </details>
- [x] **Phase 2: Permissions** - Permissions scan (single and multi-site), CSV and HTML report export
- [x] **Phase 3: Storage and File Operations** - Storage metrics, file search, and duplicate detection (completed 2026-04-02) <details>
- [x] **Phase 4: Bulk Operations and Provisioning** - Bulk member/site/transfer operations, site templates, folder structure provisioning (completed 2026-04-03) <summary>✅ v1.1 Enhanced Reports (Phases 6-9) — SHIPPED 2026-04-08</summary>
- [x] **Phase 5: Distribution and Hardening** - Self-contained EXE packaging, end-to-end validation, FR locale completeness (completed 2026-04-03)
- [x] Phase 6: Global Site Selection (5/5 plans) — completed 2026-04-07
- [x] Phase 7: User Access Audit (10/10 plans) — completed 2026-04-07
- [x] Phase 8: Simplified Permissions (6/6 plans) — completed 2026-04-07
- [x] Phase 9: Storage Visualization (4/4 plans) — completed 2026-04-07
</details>
<details>
<summary>✅ v2.2 Report Branding & User Directory (Phases 10-14) — SHIPPED 2026-04-09</summary>
- [x] Phase 10: Branding Data Foundation (3/3 plans) — completed 2026-04-08
- [x] Phase 11: HTML Export Branding + ViewModel Integration (4/4 plans) — completed 2026-04-08
- [x] Phase 12: Branding UI Views (3/3 plans) — completed 2026-04-08
- [x] Phase 13: User Directory ViewModel (2/2 plans) — completed 2026-04-08
- [x] Phase 14: User Directory View (2/2 plans) — completed 2026-04-09
</details>
### v2.3 Tenant Management & Report Enhancements (Phases 15-19)
- [x] **Phase 15: Consolidation Data Model** (2 plans) — PermissionConsolidator service and merged-row model; zero API calls, pure data shapes (completed 2026-04-09)
- [x] **Phase 16: Report Consolidation Toggle** (2 plans) — Export settings toggle wired to PermissionConsolidator; first user-visible consolidation behavior (completed 2026-04-09)
- [x] **Phase 17: Group Expansion in HTML Reports** (2 plans) — Clickable group expansion in HTML exports with transitive membership resolution (completed 2026-04-09)
- [x] **Phase 18: Auto-Take Ownership** (2 plans) — Global toggle and automatic site collection admin elevation on access denied (completed 2026-04-09)
- [x] **Phase 19: App Registration & Removal** (2 plans) — Automated Entra app registration with guided fallback and clean removal (completed 2026-04-09)
## Phase Details ## Phase Details
### Phase 1: Foundation ### Phase 15: Consolidation Data Model
**Goal**: The application shell runs, users can authenticate to multiple tenants and switch between them without re-logging in, all long-running operations are cancellable and report progress, all errors surface visibly, and the infrastructure patterns that prevent the existing app's 10 known pitfalls are in place before any feature work begins. **Goal**: The data shape and merge logic for report consolidation exist and are fully testable in isolation before any UI touches them
**Depends on**: Nothing (first phase) **Depends on**: Nothing (no API calls, no UI dependencies)
**Requirements**: FOUND-01, FOUND-02, FOUND-03, FOUND-04, FOUND-05, FOUND-06, FOUND-07, FOUND-08, FOUND-09, FOUND-10, FOUND-12 **Requirements**: RPT-04
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. User can create, rename, delete, and switch between tenant profiles via the UI — each profile stores tenant URL, client ID, and display name in a JSON file 1. A `ConsolidatedPermissionEntry` model exists that represents a single user's merged access across multiple locations with identical access levels
2. User can authenticate to a tenant via interactive browser login and the session persists across tenant switches without re-entering credentials (MSAL token cache per tenant) 2. A `PermissionConsolidator` service accepts a flat list of permission rows and returns a consolidated list where duplicate user+level rows are merged
3. User can see real-time progress on any long-running operation and cancel it mid-execution with a button — the operation stops cleanly with no silent continuation 3. Consolidation logic has unit test coverage — a known 10-row input with 3 duplicate pairs produces the expected 7-row output
4. When any operation fails, the user sees an actionable error message in the UI — no operation fails silently or swallows an exception 4. Existing HTML export services compile and produce identical output when consolidation is not applied (opt-in, defaults off)
5. UI language switches between English and French dynamically without restarting the application **Plans:** 2/2 plans complete
**Plans**: 8 plans
Plans: Plans:
- [ ] 01-01-PLAN.md — Solution scaffold: WPF project + xUnit test project with Generic Host entry point - [x] 15-01-PLAN.md — Models (LocationInfo, ConsolidatedPermissionEntry) + PermissionConsolidator service
- [ ] 01-02-PLAN.md — Core layer: models, messages, pagination helper, retry helper, LogPanelSink - [x] 15-02-PLAN.md — Unit tests (10 test cases) + full solution build verification
- [ ] 01-03-PLAN.md — Persistence layer: ProfileRepository + SettingsRepository + services + unit tests
- [ ] 01-04-PLAN.md — Auth layer: MsalClientFactory + SessionManager + unit tests
- [ ] 01-05-PLAN.md — Localization + Serilog: TranslationSource, EN/FR resx, integration tests
- [ ] 01-06-PLAN.md — ViewModels + WPF shell: FeatureViewModelBase, MainWindow XAML, global exception handlers
- [ ] 01-07-PLAN.md — UI dialogs: ProfileManagementDialog + SettingsView wired into shell
- [ ] 01-08-PLAN.md — Checkpoint: full test suite + visual verification of running application
### Phase 2: Permissions ### Phase 16: Report Consolidation Toggle
**Goal**: Users can scan SharePoint permissions on one or many sites and export the results as both a raw CSV and a sortable, filterable HTML report — with no silent failures on large libraries and full control over scan scope. **Goal**: Users can choose to merge duplicate permission entries per export through a toggle in the export settings dialog
**Depends on**: Phase 1 **Depends on**: Phase 15
**Requirements**: PERM-01, PERM-02, PERM-03, PERM-04, PERM-05, PERM-06, PERM-07 **Requirements**: RPT-03
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. User can select one site or multiple sites and run a permissions scan that returns owners, members, guests, external users, and broken inheritance items 1. A consolidation toggle is visible in the export settings dialog (or export options panel) and defaults to OFF
2. User can choose configurable scan depth and whether to include or exclude inherited permissions before running 2. When the toggle is OFF, the exported HTML report is byte-for-byte identical to the pre-v2.3 output
3. User can export the permissions results to a CSV file with all raw permission data 3. When the toggle is ON, the exported HTML report merges rows for the same user with identical access levels into a single row showing all affected locations
4. User can export the permissions results to an interactive HTML report where rows are sortable, filterable, and groupable by user 4. The toggle state is remembered for the session (does not reset between exports within the same session)
5. Scanning a library with more than 5,000 items completes successfully — the tool paginates automatically and does not silently truncate or fail **Plans:** 2/2 plans complete
**Plans**: 7 plans
Plans: Plans:
- [x] 02-01-PLAN.md — Wave 0: test scaffolds (PermissionsService, ViewModel, classification, CSV, HTML export tests) + PermissionEntryHelper - [ ] 16-01-PLAN.md — ViewModel properties + XAML Export Options GroupBox + localization + CSV consolidation
- [x] 02-02-PLAN.md — Core models + PermissionsService scan engine (PermissionEntry, ScanOptions, IPermissionsService, PermissionsService) - [ ] 16-02-PLAN.md — HTML consolidated rendering with expandable location sub-lists + full test verification
- [x] 02-03-PLAN.md — SiteListService: tenant admin site listing for multi-site picker (ISiteListService, SiteListService, SiteInfo)
- [x] 02-04-PLAN.md — Export services: CsvExportService (with row merging) + HtmlExportService (self-contained HTML)
- [x] 02-05-PLAN.md — Localization: 15 Phase 2 EN/FR keys in Strings.resx, Strings.fr.resx, Strings.Designer.cs
- [x] 02-06-PLAN.md — PermissionsViewModel + SitePickerDialog (XAML + code-behind)
- [x] 02-07-PLAN.md — DI wiring + PermissionsView XAML + MainWindow tab replacement + visual checkpoint
### Phase 3: Storage and File Operations ### Phase 17: Group Expansion in HTML Reports
**Goal**: Users can view and export storage metrics per site and library, search for files across sites using multiple criteria, and detect duplicate files and folders — all with consistent export options and no silent failures on large datasets. **Goal**: Users can expand SharePoint group entries in HTML reports to see the group's members, including members of nested groups
**Depends on**: Phase 2 **Depends on**: Phase 16
**Requirements**: STOR-01, STOR-02, STOR-03, STOR-04, STOR-05, SRCH-01, SRCH-02, SRCH-03, SRCH-04, DUPL-01, DUPL-02, DUPL-03 **Requirements**: RPT-01, RPT-02
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. User can view storage consumption per library and per site (with configurable folder depth), including total size, version size, item count, and last modified date 1. SharePoint group rows in the HTML report render as expandable — clicking a group name reveals its member list inline
2. User can export storage metrics to CSV and to an interactive HTML with a collapsible tree view 2. Member resolution includes transitive membership: nested groups are recursively resolved so every leaf user is shown
3. User can search for files across sites using at least extension, name/regex, date range, creator, and editor as criteria — with a configurable result cap up to 50,000 items 3. Group expansion is triggered at export time via Graph API — the permission scan itself is unchanged
4. User can export file search results to CSV and to an interactive sortable/filterable HTML 4. When Graph cannot resolve a group's members (throttled or insufficient scope), the report shows the group row with a "members unavailable" label rather than failing the export
5. User can scan for duplicate files (by name, size, creation date, modification date) and duplicate folders (by name, subfolder count, file count) and export the results to an HTML with grouped display **Plans:** 2/2 plans complete
**Plans**: 8 plans
Plans: Plans:
- [ ] 03-01-PLAN.md — Wave 0: test scaffolds + models (StorageNode, SearchResult, DuplicateGroup/Item, options) + interfaces (IStorageService, ISearchService, IDuplicatesService) + export stubs - [ ] 17-01-PLAN.md — ResolvedMember model + ISharePointGroupResolver service (CSOM + Graph transitive resolution) + DI registration
- [ ] 03-02-PLAN.md — StorageService: CSOM StorageMetrics scan engine (recursive folder tree, library-level aggregation) - [ ] 17-02-PLAN.md — HtmlExportService expandable group pills + toggleGroup JS + PermissionsViewModel wiring
- [ ] 03-03-PLAN.md — Storage export services: StorageCsvExportService + StorageHtmlExportService (collapsible tree HTML)
- [ ] 03-04-PLAN.md — SearchService (KQL pagination, client-side Regex) + DuplicatesService (composite key grouping, CAML folder scan)
- [ ] 03-05-PLAN.md — Search and Duplicate export services: SearchCsvExportService + SearchHtmlExportService + DuplicatesHtmlExportService
- [ ] 03-06-PLAN.md — Localization: all Phase 3 EN/FR keys for Storage, File Search, and Duplicates tabs
- [ ] 03-07-PLAN.md — StorageViewModel + StorageView XAML + DI wiring (Storage tab replaces FeatureTabBase stub)
- [ ] 03-08-PLAN.md — SearchViewModel + SearchView + DuplicatesViewModel + DuplicatesView + DI wiring + visual checkpoint
### Phase 4: Bulk Operations and Provisioning ### Phase 18: Auto-Take Ownership
**Goal**: Users can execute bulk write operations (member additions, site creation, file transfer) with per-item error reporting and cancellation, capture site structures as reusable templates, apply templates to create new sites, and provision folder structures from CSV — all without silent partial failures. **Goal**: Users can enable automatic site collection admin elevation so that access-denied sites during scans no longer block audit progress
**Depends on**: Phase 3 **Depends on**: Phase 15
**Requirements**: BULK-01, BULK-02, BULK-03, BULK-04, BULK-05, TMPL-01, TMPL-02, TMPL-03, TMPL-04, FOLD-01, FOLD-02 **Requirements**: OWN-01, OWN-02
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. User can transfer files and folders between sites with real-time progress tracking and can cancel mid-operation — transferred items are confirmed and failures are reported per-item 1. A global "Auto-take ownership on access denied" toggle exists in application settings and defaults to OFF
2. User can add members to groups in bulk from a CSV file — each row that fails is reported individually, not silently skipped 2. When the toggle is OFF, access-denied sites produce the same error behavior as before v2.3 (no regression)
3. User can create multiple sites in bulk from a CSV file with per-site error reporting and mid-operation cancellation 3. When the toggle is ON and a scan hits access denied on a site, the app automatically calls `Tenant.SetSiteAdmin` to elevate ownership and retries the site without interrupting the scan
4. User can capture an existing site's structure (libraries, folders, permission groups, logo, settings) as a named template stored in JSON, then apply that template to create a new Communication or Teams site 4. The scan result for an auto-elevated site is visually distinguishable from a normally-scanned site (e.g., a flag or icon in the results)
5. User can manage saved templates (create, rename, delete) and create folder structures on a target site from a CSV template **Plans:** 2/2 plans complete
**Plans**: 10 plans
Plans: Plans:
- [ ] 04-01-PLAN.md — Dependencies + core models + interfaces + BulkOperationRunner + test scaffolds - [ ] 18-01-PLAN.md — Settings toggle + OwnershipElevationService + PermissionEntry.WasAutoElevated flag
- [ ] 04-02-PLAN.md — CsvValidationService + TemplateRepository with unit tests - [ ] 18-02-PLAN.md — Scan-loop elevation logic + DataGrid visual differentiation
- [ ] 04-03-PLAN.md — FileTransferService (CSOM MoveCopyUtil, conflict policies)
- [ ] 04-04-PLAN.md — BulkMemberService (Graph SDK batch + CSOM fallback)
- [ ] 04-05-PLAN.md — BulkSiteService (PnP Framework site creation)
- [ ] 04-06-PLAN.md — TemplateService + FolderStructureService
- [ ] 04-07-PLAN.md — Localization + shared dialogs + example CSV resources
- [ ] 04-08-PLAN.md — TransferViewModel + TransferView
- [ ] 04-09-PLAN.md — BulkMembersViewModel + BulkSitesViewModel + FolderStructureViewModel + Views
- [ ] 04-10-PLAN.md — TemplatesViewModel + TemplatesView + DI registration + MainWindow wiring + visual checkpoint
### Phase 5: Distribution and Hardening ### Phase 19: App Registration & Removal
**Goal**: The application ships as a single self-contained EXE that runs on a machine with no .NET runtime installed, all previously identified reliability constraints are verified end-to-end (5,000-item pagination, JSON corruption recovery, throttling retry, cancellation), and the French locale is complete and tested. **Goal**: Users can register and remove the Toolbox's Azure AD application on a target tenant directly from the profile dialog, with a guided fallback when permissions are insufficient
**Depends on**: Phase 4 **Depends on**: Phase 18
**Requirements**: FOUND-11 **Requirements**: APPREG-01, APPREG-02, APPREG-03, APPREG-04, APPREG-05, APPREG-06
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. Running the published EXE on a clean machine with no .NET runtime installed launches the application and all features function correctly 1. A "Register App" action is available in the profile create/edit dialog and is the recommended path for new tenant onboarding
2. The application recovers gracefully when a SharePoint API call is throttled (429/503) — the user sees a retry progress message and the operation eventually completes or surfaces a clear failure 2. Before attempting registration, the app checks for Global Admin role and surfaces a clear message if the signed-in user lacks the required permissions, then presents step-by-step manual registration instructions as a fallback
3. The French locale is complete for all UI strings — no English fallback text appears when the language is set to French 3. Registration creates the Azure AD application, service principal, and grants all required API permissions in a single atomic operation — if any step fails, all partial changes are rolled back and the user sees a specific error explaining what failed and why
4. A scan against a library with more than 5,000 items returns complete, correct results with no silent truncation verified against a known dataset 4. A "Remove App" action in the profile dialog removes the Azure AD application registration from the target tenant
**Plans**: 3 plans 5. After removal, all cached MSAL tokens and session state for that tenant are cleared, and subsequent operations require re-authentication
**Plans:** 2/2 plans complete
Plans: Plans:
- [ ] 05-01-PLAN.md — Helper visibility changes + retry/pagination/locale unit tests - [ ] 19-01-PLAN.md — IAppRegistrationService + AppRegistrationResult model + TenantProfile.AppId + service implementation + unit tests
- [ ] 05-02-PLAN.md — FR diacritic corrections + self-contained publish configuration - [ ] 19-02-PLAN.md — ViewModel RegisterApp/RemoveApp commands + XAML dialog UI + fallback panel + localization + VM tests
- [ ] 05-03-PLAN.md — Full test suite verification + publish smoke test + human checkpoint
## Progress ## Progress
**Execution Order:** | Phase | Milestone | Plans | Status | Completed |
Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 |-------|-----------|-------|--------|-----------|
| 1-5 | v1.0 | 36/36 | Shipped | 2026-04-07 |
| Phase | Plans Complete | Status | Completed | | 6-9 | v1.1 | 25/25 | Shipped | 2026-04-08 |
|-------|----------------|--------|-----------| | 10-14 | v2.2 | 14/14 | Shipped | 2026-04-09 |
| 1. Foundation | 8/8 | Complete | 2026-04-02 | | 15. Consolidation Data Model | v2.3 | 2/2 | Complete | 2026-04-09 |
| 2. Permissions | 7/7 | Complete | 2026-04-02 | | 16. Report Consolidation Toggle | v2.3 | 2/2 | Complete | 2026-04-09 |
| 3. Storage and File Operations | 8/8 | Complete | 2026-04-02 | | 17. Group Expansion in HTML Reports | 2/2 | Complete | 2026-04-09 | — |
| 4. Bulk Operations and Provisioning | 10/10 | Complete | 2026-04-03 | | 18. Auto-Take Ownership | 2/2 | Complete | 2026-04-09 | — |
| 5. Distribution and Hardening | 3/3 | Complete | 2026-04-03 | | 19. App Registration & Removal | 2/2 | Complete | 2026-04-09 | — |

View File

@@ -1,204 +1,99 @@
--- ---
gsd_state_version: 1.0 gsd_state_version: 1.0
milestone: v1.0 milestone: v2.3
milestone_name: milestone milestone_name: Tenant Management & Report Enhancements
status: completed status: planning
stopped_at: Completed 05-03-PLAN.md — Phase 5 and project fully complete stopped_at: Completed 19-02-PLAN.md
last_updated: "2026-04-07T06:51:37.208Z" last_updated: "2026-04-09T13:23:47.593Z"
last_activity: 2026-04-03Plan 05-03 complete — 134 tests pass, EXE verified, French locale approved by human last_activity: 2026-04-09Roadmap created for v2.3 (phases 15-19)
progress: progress:
total_phases: 5 total_phases: 5
completed_phases: 5 completed_phases: 5
total_plans: 36 total_plans: 10
completed_plans: 36 completed_plans: 10
percent: 100
--- ---
# Project State # Project State
## Project Reference ## Project Reference
See: .planning/PROJECT.md (updated 2026-04-02) See: .planning/PROJECT.md (updated 2026-04-09)
**Core value:** Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application. **Core value:** Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application.
**Current focus:** Phase 4 — Bulk Operations and Provisioning (not yet planned) **Current focus:** v2.3 Tenant Management & Report Enhancements — Phase 15 next
## Current Position ## Current Position
Phase: 5 of 5 (Distribution and Hardening) — COMPLETE Phase: 15 — Consolidation Data Model (not started)
Plan: 3 of 3 in phase 05 — Integration gate passed, human smoke test approved Plan:
Status: ALL PHASES COMPLETE — application ready for distribution Status: Roadmap approved — ready to plan Phase 15
Last activity: 2026-04-03Plan 05-03 complete — 134 tests pass, EXE verified, French locale approved by human Last activity: 2026-04-09Roadmap created for v2.3 (phases 15-19)
Progress: [██████████] 100% ```
v2.3 Progress: ░░░░░░░░░░ 0% (0/5 phases)
```
## Phase 3 Wave Structure ## Shipped Milestones
| Wave | Plans | Autonomous | Description | - v1.0 MVP — Phases 1-5 (shipped 2026-04-07)
|------|-------|------------|-------------| - v1.1 Enhanced Reports — Phases 6-9 (shipped 2026-04-08)
| 0 | 03-01 | yes | Models, interfaces, export stubs, test scaffolds | - v2.2 Report Branding & User Directory — Phases 10-14 (shipped 2026-04-09)
| 1 | 03-02 | yes | StorageService implementation |
| 2 | 03-03, 03-04, 03-06 | yes | Storage exports + Search/Duplicates services + Localization (parallel) |
| 3 | 03-05, 03-07 | yes | Search/Duplicate exports + StorageViewModel/View (parallel) |
| 4 | 03-08 | no (checkpoint) | SearchViewModel + DuplicatesViewModel + Views + visual checkpoint |
## Performance Metrics ## v2.3 Phase Map
**Velocity:** | Phase | Name | Requirements | Status |
- Total plans completed: 0 |-------|------|--------------|--------|
- Average duration: — | 15 | Consolidation Data Model | RPT-04 | Not started |
- Total execution time: 0 hours | 16 | Report Consolidation Toggle | RPT-03 | Not started |
| 17 | Group Expansion in HTML Reports | RPT-01, RPT-02 | Not started |
**By Phase:** | 18 | Auto-Take Ownership | OWN-01, OWN-02 | Not started |
| 19 | App Registration & Removal | APPREG-01..06 | Not started |
| Phase | Plans | Total | Avg/Plan |
|-------|-------|-------|----------|
| - | - | - | - |
**Recent Trend:**
- Last 5 plans: —
- Trend: —
*Updated after each plan completion*
| Phase 01-foundation P01 | 4 | 2 tasks | 14 files |
| Phase 01-foundation P02 | 1 | 2 tasks | 7 files |
| Phase 01-foundation P03 | 8 | 2 tasks | 7 files |
| Phase 01-foundation P05 | 4min | 2 tasks | 8 files |
| Phase 01-foundation P04 | 4 | 2 tasks | 4 files |
| Phase 01-foundation P06 | 5 | 2 tasks | 12 files |
| Phase 01-foundation P07 | 3 | 2 tasks | 8 files |
| Phase 01-foundation P08 | 5 | 1 tasks | 1 files |
| Phase 01-foundation P08 | 15 | 2 tasks | 3 files |
| Phase 02-permissions P05 | 1min | 1 tasks | 3 files |
| Phase 02-permissions P03 | 1min | 1 tasks | 5 files |
| Phase 02-permissions P01 | 5min | 2 tasks | 9 files |
| Phase 02-permissions P02 | 7min | 2 tasks | 4 files |
| Phase 02-permissions P04 | 1min | 2 tasks | 2 files |
| Phase 02-permissions P06 | 4min | 2 tasks | 6 files |
| Phase 02-permissions P07 | 30min | 2 tasks | 6 files |
| Phase 03-storage P01 | 10min | 2 tasks | 22 files |
| Phase 03-storage P03 | 2min | 2 tasks | 2 files |
| Phase 03-storage P06 | 5min | 1 tasks | 3 files |
| Phase 03-storage P04 | 2min | 2 tasks | 2 files |
| Phase 03-storage P07 | 4min | 2 tasks | 10 files |
| Phase 03-storage P05 | 4min | 2 tasks | 3 files |
| Phase 03 P08 | 4min | 3 tasks | 9 files |
| Phase 04-bulk-operations-and-provisioning P01 | 7min | 2 tasks | 27 files |
| Phase 04-bulk-operations-and-provisioning P05 | 6min | 2 tasks | 3 files |
| Phase 04-bulk-operations-and-provisioning P03 | 7min | 2 tasks | 2 files |
| Phase 04-bulk-operations-and-provisioning P02 | 25 | 2 tasks | 4 files |
| Phase 04-bulk-operations-and-provisioning P04 | 7min | 2 tasks | 3 files |
| Phase 04-bulk-operations-and-provisioning P06 | 10min | 2 tasks | 4 files |
| Phase 04-bulk-operations-and-provisioning P07 | 15 | 2 tasks | 11 files |
| Phase 04-bulk-operations-and-provisioning P08 | 20min | 2 tasks | 6 files |
| Phase 04-bulk-operations-and-provisioning P10 | 25min | 2 tasks | 7 files |
| Phase 05-distribution-and-hardening P02 | 3min | 2 tasks | 2 files |
| Phase 05-distribution-and-hardening P01 | 2min | 2 tasks | 5 files |
| Phase 05-distribution-and-hardening P03 | 15min | 2 tasks | 0 files |
## Accumulated Context ## Accumulated Context
### Decisions ### Decisions
Decisions are logged in PROJECT.md Key Decisions table. Decisions are logged in PROJECT.md Key Decisions table.
Recent decisions affecting current work:
- Foundation: Use PnP.Framework 1.18.0 (not PnP.Core SDK) — PnP Provisioning Engine lives only in PnP.Framework **v2.3 notable constraints:**
- Foundation: Use MsalCacheHelper for per-tenant token cache serialization — scope IPublicClientApplication per ClientId - Phase 19 has the highest blast radius (Entra changes) — must be last
- Foundation: Never set PublishTrimmed=true — PnP.Framework and MSAL use reflection; accept ~150-200 MB EXE - Phase 15 is zero-API-call foundation; unblocks Phase 16 (consolidation) and Phase 18 (ownership) independently
- Foundation: Establish AsyncRelayCommand + IProgress<T> + CancellationToken patterns before any feature work — retrofitting is the most expensive WPF refactor - Group expansion (Phase 17) calls Graph at export time, not at scan time — scan pipeline unchanged
- [Phase 01-foundation]: Upgraded MSAL from 4.83.1 to 4.83.3 — Extensions.Msal 4.83.3 requires MSAL >= 4.83.3; minor patch with no behavioral difference - Auto-take ownership uses PnP `Tenant.SetSiteAdmin` — requires Tenant Admin scope
- [Phase 01-foundation]: Test project targets net10.0-windows with UseWPF=true — required to reference WPF main project; net10.0 is framework-incompatible - App registration must be atomic with rollback; partial Entra state is worse than no state
- [Phase 01-foundation]: Solution uses .slnx format (new .NET 10 XML solution) — dotnet new sln creates .slnx by default in .NET 10 SDK - [Phase 15]: MakeKey declared internal for test access via InternalsVisibleTo without exposing as public API
- [Phase 01-foundation]: TenantProfile is a plain mutable class (not record) — System.Text.Json requires settable properties; field names Name/TenantUrl/ClientId match JSON schema exactly - [Phase 15]: LINQ GroupBy+Select for consolidation merge instead of mutable dictionary — consistent with functional codebase style
- [Phase 01-foundation]: SharePointPaginationHelper uses [EnumeratorCancellation] on ct — required for correct WithCancellation() forwarding in async iterators - [Phase 15-consolidation-data-model]: RPT-04-g test data uses 11 rows (not 10) to produce 7 consolidated rows — plan description had a counting error; 4 unique rows + 3 merged groups = 7
- [Phase 01-foundation]: Explicit System.IO using required in WPF project — WPF temp build project does not include System.IO in implicit usings; all persistence classes need explicit import - [Phase 16-01]: Consolidated branch uses early-return pattern inside WriteSingleFileAsync to leave existing code path untouched
- [Phase 01-foundation]: SettingsService validates only 'en' and 'fr' language codes — throws ArgumentException for unsupported codes - [Phase 16-01]: PermissionsViewModel gets MergePermissions as no-op placeholder reserved for future use
- [Phase 01-foundation]: LoadAsync on corrupt JSON throws InvalidDataException (not silent empty) — explicit failure protects against silent data loss - [Phase 16-report-consolidation-toggle]: BuildConsolidatedHtml is a private method via early-return in BuildHtml — existing code path completely untouched
- [Phase 01-foundation]: Strings.Designer.cs maintained manually — ResXFileCodeGenerator is VS-only, not run by dotnet build; only ResourceManager accessor needed - [Phase 16-report-consolidation-toggle]: Separate locIdx counter for location groups (loc0, loc1...) distinct from grpIdx for user groups (ugrp0...) prevents ID collision
- [Phase 01-foundation]: EmbeddedResource uses Update not Include in SDK-style project — SDK auto-includes all .resx; Include causes NETSDK1022 duplicate error - [Phase 17]: Static helpers IsAadGroup/ExtractAadGroupId/StripClaims declared internal to enable unit testing via InternalsVisibleTo without polluting public API
- [Phase 01-foundation]: MsalClientFactory stores MsalCacheHelper per clientId and exposes GetCacheHelper() — PnP creates its own internal PCA so tokenCacheCallback is the bridge for shared persistent cache - [Phase 17]: Graph client created lazily on first AAD group encountered to avoid unnecessary auth overhead for groups with no nested AAD members
- [Phase 01-foundation]: SessionManager is the single holder of ClientContext instances — callers must not store returned contexts - [Phase 17]: groupMembers optional param in HtmlExportService — null produces identical pre-Phase-17 output; ISharePointGroupResolver injected as optional last param in PermissionsViewModel; resolution failure degrades gracefully with LogWarning
- [Phase 01-foundation]: CacheDirectory is a constructor parameter (no-arg defaults to AppData) — enables test isolation without real filesystem writes - [Phase 18-auto-take-ownership]: OwnershipElevationService uses Tenant.SetSiteAdmin from PnP.Framework
- [Phase 01-foundation]: Interactive login test marked Skip in unit suite — browser/WAM MSAL flow cannot run in automated CI - [Phase 18-auto-take-ownership]: WasAutoElevated last positional param with default=false preserves all existing PermissionEntry callsites
- [Phase 01-foundation]: ObservableRecipient lambda receivers need explicit cast to FeatureViewModelBase for virtual dispatch - [Phase 18-auto-take-ownership]: AutoTakeOwnership ViewModel setter uses fire-and-forget pattern matching DataFolder
- [Phase 01-foundation]: FeatureViewModelBase declared as abstract partial class — CommunityToolkit.Mvvm source generator requires partial keyword - [Phase 18-auto-take-ownership]: Toggle read before scan loop (not in exception filter) — await in when clause unsupported; pre-read bool preserves semantics
- [Phase 01-foundation]: OpenFolderDialog (Microsoft.Win32) used in WPF instead of FolderBrowserDialog (System.Windows.Forms) - [Phase 18-auto-take-ownership]: WasAutoElevated DataTrigger last in RowStyle.Triggers — amber wins over RiskLevel color
- [Phase 01-foundation]: LogPanel exposed via GetLogPanel() method — x:Name generates field in XAML partial class, property with same name causes CS0102 - [Phase 19-app-registration-removal]: AppRegistrationService uses AppGraphClientFactory alias to disambiguate from Microsoft.Graph.GraphClientFactory
- [Phase 01-foundation]: ProfileManagementViewModel dialog factory pattern — ViewModel exposes Func<Window>? OpenProfileManagementDialog set by View layer; avoids Window/DI coupling in ViewModel - [Phase 19-app-registration-removal]: BuildRequiredResourceAccess declared internal to enable direct unit testing without live Graph calls
- [Phase 01-foundation]: IServiceProvider injected into MainWindow constructor — resolves DI-registered ProfileManagementDialog and SettingsView at runtime - [Phase 19-app-registration-removal]: SharePoint AllSites.FullControl GUID marked LOW confidence — must be verified against live tenant
- [Phase 01-foundation]: ProfileManagementDialog and SettingsView registered as Transient — fresh instance with fresh ViewModel per dialog open or tab init - [Phase 19-app-registration-removal]: ProfileManagementViewModel constructor gains IAppRegistrationService as last param — existing logo tests updated to 5-param
- [Phase 01-foundation]: Solution file is .slnx (not .sln) — dotnet build/test commands must use SharepointToolbox.slnx - [Phase 19-app-registration-removal]: TranslationSource.Instance used directly in ViewModel for status strings (consistent with runtime locale switching)
- [Phase 01-foundation]: 45 tests total: 44 pass, 1 skip (interactive MSAL GetOrCreateContextAsync_CreatesContext — browser/WAM flow excluded from automated suite) - [Phase 19-app-registration-removal]: BooleanToVisibilityConverter declared in Window.Resources (WPF built-in, no custom converter needed)
- [Phase 02-permissions]: DeriveAdminUrl is internal static — enables direct unit testing of admin URL regex without live tenant
- [Phase 02-permissions]: InternalsVisibleTo added to AssemblyInfo.cs — required for test project to access internal DeriveAdminUrl; plan omitted this assembly attribute
- [Phase 02-permissions]: Export service stubs created in Plan 02-01 so test project compiles before Plan 03 implementation
- [Phase 02-permissions]: Principal.Email removed from CSOM load expression — Email only exists on User subtype, not Principal base class
- [Phase 02-permissions]: Folder is not a SecurableObject in CSOM — ListItem used for permission extraction — Required by CSOM type system; Folder inherits from ClientObject not SecurableObject
- [Phase 02-permissions]: Principal.Email excluded from CSOM Include — email not needed for PermissionEntry — Principal base type has no Email property; only User subtype does; avoids CS1061
- [Phase 02-permissions]: CsvExportService uses UTF-8 with BOM for Excel compatibility; HtmlExportService uses UTF-8 without BOM
- [Phase 02-permissions]: ISessionManager interface extracted from concrete SessionManager — required for Moq-based unit testing of PermissionsViewModel
- [Phase 02-permissions]: PermissionsView code-behind wires Func<TenantProfile, SitePickerDialog> factory via DI — avoids Window coupling in ViewModel, keeps ViewModel testable
- [Phase 02-permissions]: ISessionManager -> SessionManager DI registration was missing from App.xaml.cs — added in plan 02-07 (auto-detected Rule 3 blocker)
- [Phase 02-permissions]: MainWindow.xaml uses x:Name on Permissions TabItem; MainWindow.xaml.cs sets Content at runtime from DI — same pattern as SettingsView
- [Phase 03-storage]: Storage display uses flat DataGrid with IndentLevel -> Margin IValueConverter (not WPF TreeView) — better UI virtualization for large sites
- [Phase 03-storage]: StorageNode.VersionSizeBytes is a derived property (TotalSizeBytes - FileStreamSizeBytes, Math.Max 0) — not stored separately
- [Phase 03-storage]: SearchService uses KeywordQuery + SearchExecutor (Microsoft.SharePoint.Client.Search.Query) — transitive dep of PnP.Framework; no new NuGet package
- [Phase 03-storage]: Search pagination: StartRow += 500, hard cap StartRow <= 50,000 (SharePoint Search boundary) = 50,000 max results
- [Phase 03-storage]: DuplicatesService uses CAML FSObjType=1 (not FileSystemObjectType) for folder queries — wrong name returns zero results silently
- [Phase 03-storage]: Duplicate detection uses composite key grouping (name+size+dates), no content hashing — matches PS reference and DUPL-01/02/03 requirements exactly
- [Phase 03-storage]: Phase 3 export services are separate classes from Phase 2 (StorageCsvExportService, SearchCsvExportService, etc.) — different schemas
- [Phase 03-storage]: StorageNode.VersionSizeBytes is a derived property (Math.Max(0, TotalSizeBytes - FileStreamSizeBytes)) — not stored separately
- [Phase 03-storage]: MakeKey composite key logic tested inline in DuplicatesServiceTests before Plan 03-04 creates the real class — avoids skipping all duplicate logic tests
- [Phase 03-storage]: Export service stubs return string.Empty until implemented — compile-only skeletons for Plans 03-03 and 03-05
- [Phase 03-storage 03-02]: StorageService.LastModified uses StorageMetrics.LastModified with fallback to Folder.TimeLastModified — StorageMetrics.LastModified may be DateTime.MinValue for empty libraries
- [Phase 03-storage 03-02]: System folder filter uses Forms/ and _-prefix heuristic — matches SharePoint standard hidden folder naming convention
- [Phase 03-storage]: Explicit System.IO using required in StorageCsvExportService and StorageHtmlExportService — WPF project does not include System.IO in implicit usings (established project pattern)
- [Phase 03-storage 03-04]: SearchService uses SelectProperties.Add per-item loop — StringCollection has no AddRange(string[]) overload in this SDK version
- [Phase 03-storage 03-04]: DuplicatesService.MakeKey internal static method matches inline test helper in DuplicatesServiceTests exactly — deliberate design to ensure test parity
- [Phase 03-storage 03-04]: DuplicatesService file mode re-implements pagination inline — avoids coupling between services with different result models (DuplicateItem vs SearchResult)
- [Phase 03-storage]: ClientContext.Url is read-only in CSOM — site URL override done via new TenantProfile with site URL for GetOrCreateContextAsync
- [Phase 03-storage]: IndentConverter/BytesConverter/InverseBoolConverter registered in App.xaml Application.Resources — accessible to all views without per-UserControl declaration
- [Phase 03-storage]: SearchCsvExportService uses UTF-8 BOM for Excel compatibility — consistent with Phase 2 CsvExportService pattern
- [Phase 03-storage]: DuplicatesHtmlExportService always uses badge-dup (red) for all groups — ok/diff distinction removed from final DUPL-03 spec
- [Phase 03]: SearchViewModel and DuplicatesViewModel use TenantProfile site URL override pattern — ctx.Url is read-only in CSOM (established pattern from StorageViewModel)
- [Phase 03]: DuplicateRow flat DTO wraps DuplicateItem with GroupName and GroupSize for DataGrid display
- [Phase 04-bulk-operations-and-provisioning]: ITemplateService uses ModelSiteTemplate alias — SiteTemplate is ambiguous between SharepointToolbox.Core.Models and Microsoft.SharePoint.Client; resolved with using alias
- [Phase 04-bulk-operations-and-provisioning]: ICsvValidationService and BulkResultCsvExportService require explicit System.IO using — WPF project does not include System.IO in implicit usings (established project pattern)
- [Phase 04-bulk-operations-and-provisioning]: BulkSiteService uses Core.Helpers.ExecuteQueryRetryHelper not Infrastructure.Auth — plan had wrong using; correct namespace is Core.Helpers (established pattern from Phase 2/3 services)
- [Phase 04-bulk-operations-and-provisioning]: Design-time MSBuild compile used for build verification — dotnet build WinFX BAML step fails in bash shell; C# compilation verified via dotnet msbuild -t:Compile -p:DesignTimeBuild=true with 0 errors; DLL-based test run confirms 4 pass / 3 skip
- [Phase 04-bulk-operations-and-provisioning]: CsvValidationService uses DetectDelimiter=true — handles both comma and semicolon CSV files without format-specific code paths
- [Phase 04-bulk-operations-and-provisioning]: TemplateRepository uses same atomic write pattern as SettingsRepository (tmp + File.Move + round-trip JSON validation)
- [Phase 04-bulk-operations-and-provisioning 04-04]: GraphClientFactory uses GetOrCreateAsync (async) — MsalClientFactory only exposes async method with SemaphoreSlim locking; plan had incorrect sync reference GetOrCreateClient
- [Phase 04-bulk-operations-and-provisioning 04-04]: AuthGraphClientFactory alias resolves CS0104 — Microsoft.Graph.GraphClientFactory conflicts with SharepointToolbox.Infrastructure.Auth.GraphClientFactory when both namespaces imported
- [Phase 04-bulk-operations-and-provisioning 04-04]: Microsoft.SharePoint.Client.Group? fully qualified in AddToClassicGroupAsync — Microsoft.Graph.Models.Group also in scope in BulkMemberService; explicit namespace resolves ambiguity
- [Phase 04-bulk-operations-and-provisioning]: TemplateService uses ModelSiteTemplate alias — consistent with ITemplateService; CSOM SiteTemplate and Core.Models.SiteTemplate are both in scope
- [Phase 04-bulk-operations-and-provisioning]: FolderStructureService.BuildUniquePaths sorts by slash count for parent-first ordering — ensures intermediate folders exist before children when using Folders.Add
- [Phase 04-bulk-operations-and-provisioning]: TemplateService.ApplyTemplateAsync creates new ClientContext for new site URL — adminCtx.Url points to admin site, new site needs separate context
- [Phase 04-bulk-operations-and-provisioning]: FolderBrowserDialog uses Core.Helpers.ExecuteQueryRetryHelper (not Infrastructure.Auth) — consistent with established project namespace pattern
- [Phase 04-bulk-operations-and-provisioning]: Example CSV files placed in Resources/ and registered as EmbeddedResource — accessible via Assembly.GetManifestResourceStream without file system dependency
- [Phase 04-bulk-operations-and-provisioning]: SitePickerDialog.SelectedUrls.FirstOrDefault() used for single-site transfer selection — avoids new dialog variant while reusing existing dialog
- [Phase 04-bulk-operations-and-provisioning]: EnumBoolConverter added for RadioButton-to-enum binding (TransferMode); ConverterParameter=EnumValueName pattern established for future ViewModels
- [Phase 04-bulk-operations-and-provisioning 04-09]: BulkMembersViewModel passes _currentProfile.ClientId to AddMembersAsync — IBulkMemberService interface requires clientId for Graph API authentication; plan code omitted this parameter
- [Phase 04-bulk-operations-and-provisioning 04-09]: Duplicate standalone converter files (EnumBoolConverter.cs etc.) removed — already defined in IndentConverter.cs which is the established project pattern for all converters
- [Phase 04-bulk-operations-and-provisioning]: TemplatesView uses RenameInputDialog (custom WPF Window) instead of Microsoft.VisualBasic.Interaction.InputBox — avoids additional framework dependency
- [Phase 04-bulk-operations-and-provisioning]: All value converters (EnumBoolConverter, StringToVisibilityConverter, ListToStringConverter) added to IndentConverter.cs — consistent co-location pattern
- [Phase 05-distribution-and-hardening]: PublishSingleFile PropertyGroup is conditional on '' == 'true' — regular dotnet build and dotnet test are unaffected
- [Phase 05-distribution-and-hardening]: IncludeNativeLibrariesForSelfExtract=true required — PnP.Framework has native binaries that must bundle into the EXE
- [Phase 05-distribution-and-hardening]: IsThrottleException only checks top-level Message (not InnerException) — documented via nested-throttle test asserting false
- [Phase 05-distribution-and-hardening]: FR diacritics already present in Strings.fr.resx — FrStrings_ContainExpectedDiacritics test passes immediately (no diacritic repair needed for those 5 keys)
### Pending Todos ### Pending Todos
None yet. None.
### Blockers/Concerns ### Blockers/Concerns
- Phase 4 planning: PnP Provisioning Engine behavior for Teams-connected modern sites — edge cases need validation spike before planning None.
- Phase 5: User access export (v2 requirement UACC-01/02) depends on Phase 2 PermissionsService — confirm scope before Phase 5 planning
## Session Continuity ## Session Continuity
Last session: 2026-04-03T15:00:00.000Z Last session: 2026-04-09T13:20:36.865Z
Stopped at: Completed 05-03-PLAN.md — Phase 5 and project fully complete Stopped at: Completed 19-02-PLAN.md
Resume file: None Resume file: None
Next step: `/gsd:plan-phase 15`

View File

@@ -0,0 +1,89 @@
---
status: awaiting_human_verify
trigger: "SitePickerDialog shows 'Must specify valid information for parsing in the string' error when trying to load sites after a successful tenant connection."
created: 2026-04-07T00:00:00Z
updated: 2026-04-07T00:00:00Z
---
## Current Focus
hypothesis: ROOT CAUSE CONFIRMED — two bugs in SiteListService.GetSitesAsync
test: code reading confirmed via PnP source
expecting: fixing both issues will resolve the error
next_action: apply fix to SiteListService.cs
## Symptoms
expected: After connecting to a SharePoint tenant (https://contoso.sharepoint.com format), clicking "Select Sites" opens SitePickerDialog and loads the list of tenant sites.
actual: SitePickerDialog opens but shows error "Must specify valid information for parsing in the string" instead of loading sites.
errors: "Must specify valid information for parsing in the string" — this is an ArgumentException thrown by CSOM when it tries to parse an empty string as a site URL cursor
reproduction: 1) Launch app 2) Add profile with valid tenant URL 3) Connect 4) Authenticate 5) Click Select Sites 6) Error appears in StatusText
started: First time testing this flow after Phase 6 wiring was added.
## Eliminated
- hypothesis: Error comes from PnP's AuthenticationManager.GetContextAsync URI parsing
evidence: GetContextAsync line 1090 does new Uri(siteUrl) which is valid for "https://contoso-admin.sharepoint.com"
timestamp: 2026-04-07
- hypothesis: Error from MSAL constructing auth URL with empty component
evidence: MSAL uses organizations authority or tenant-specific, both valid; no empty strings involved
timestamp: 2026-04-07
- hypothesis: UriFormatException from new Uri("") in our own code
evidence: No Uri.Parse or new Uri() calls in SiteListService or SessionManager
timestamp: 2026-04-07
## Evidence
- timestamp: 2026-04-07
checked: PnP Framework 1.18.0 GetContextAsync source (line 1090)
found: Calls new Uri(siteUrl) — valid for admin URL
implication: Error not from GetContextAsync itself
- timestamp: 2026-04-07
checked: PnP TenantExtensions.GetSiteCollections source
found: Uses GetSitePropertiesFromSharePointByFilters with StartIndex = null (for first page); OLD commented-out approach used GetSitePropertiesFromSharePoint(null, includeDetail) — note: null, not ""
implication: SiteListService passes "" which is wrong — should be null for first page
- timestamp: 2026-04-07
checked: Error message "Must specify valid information for parsing in the string"
found: This is ArgumentException thrown by Enum.Parse or string cursor parsing when given "" (empty string); CSOM's GetSitePropertiesFromSharePoint internally parses the startIndex string as a URL/cursor; passing "" triggers parse failure
implication: Direct cause of exception confirmed
- timestamp: 2026-04-07
checked: How PnP creates admin context from regular context
found: PnP uses clientContext.Clone(adminSiteUrl) — clones existing authenticated context to admin URL without triggering new auth flow
implication: SiteListService creates a SECOND AuthenticationManager and triggers second interactive login unnecessarily; should use Clone instead
## Resolution
root_cause: |
SiteListService.GetSitesAsync has two bugs:
BUG 1 (direct cause of error): Line 50 calls tenant.GetSitePropertiesFromSharePoint("", true)
with empty string "". CSOM expects null for the first page (no previous cursor), not "".
Passing "" causes CSOM to attempt parsing it as a URL cursor, throwing
ArgumentException: "Must specify valid information for parsing in the string."
BUG 2 (design problem): GetSitesAsync creates a separate TenantProfile for the admin URL
and calls SessionManager.GetOrCreateContextAsync(adminProfile) which creates a NEW
AuthenticationManager with interactive login. This triggers a SECOND browser auth flow
just to access the admin URL. The correct approach is to clone the existing authenticated
context to the admin URL using clientContext.Clone(adminUrl), which reuses the same tokens.
fix: |
1. Replace GetOrCreateContextAsync(adminProfile) with GetOrCreateContextAsync(profile) to
get the regular context, then clone it to the admin URL.
2. Replace GetSitePropertiesFromSharePointByFilters with proper pagination (StartIndex=null).
The admin URL context is obtained via: adminCtx = ctx.Clone(adminUrl)
The site listing uses: GetSitePropertiesFromSharePointByFilters with proper filter object.
verification: |
Build succeeds (0 errors). 144 tests pass, 0 failures.
Fix addresses both root causes:
1. No longer calls GetOrCreateContextAsync with admin profile — uses Clone() instead
2. Uses GetSitePropertiesFromSharePointByFilters (modern API) instead of GetSitePropertiesFromSharePoint("")
files_changed:
- SharepointToolbox/Services/SiteListService.cs

View File

@@ -0,0 +1,155 @@
# Milestone Audit: SharePoint Toolbox v2 — v1 Release
**Audited:** 2026-04-07
**Milestone:** v1 (5 phases, 42 requirements)
**Verdict:** PASSED — all requirements satisfied, all phases integrated, build and tests green
---
## Phase Verification Summary
| Phase | Status | Score | Verification |
|-------|--------|-------|-------------|
| 01 — Foundation | PASSED | 11/11 | 01-VERIFICATION.md |
| 02 — Permissions | HUMAN_NEEDED | 7/7 automated | 02-VERIFICATION.md (2 human items pending) |
| 03 — Storage & File Ops | **MISSING** | No VERIFICATION.md | Summaries exist for all 8 plans; integration checker confirmed all wiring |
| 04 — Bulk Ops & Provisioning | HUMAN_NEEDED | 12/12 automated | 04-VERIFICATION.md (7 human items pending) |
| 05 — Distribution & Hardening | HUMAN_NEEDED | 6/6 automated | 05-VERIFICATION.md (2 human items pending) |
### Gap: Phase 03 Missing Verification
Phase 03 has no `03-VERIFICATION.md` file. All 8 plan summaries exist and confirm code was delivered. The integration checker independently verified:
- All 3 Phase 3 service interfaces (IStorageService, ISearchService, IDuplicatesService) registered in DI
- All 5 export services registered and wired to ViewModels
- All 4 Phase 3 tabs (Storage, Search, Duplicates + exports) wired in MainWindow
- 13 Phase 3 requirements (STOR-0105, SRCH-0104, DUPL-0103) covered
**Recommendation:** Run a retroactive phase verification for Phase 03 or accept integration checker evidence as sufficient.
---
## Requirements Coverage
All 42 v1 requirements are marked complete in REQUIREMENTS.md with phase traceability:
| Category | IDs | Count | Status |
|----------|-----|-------|--------|
| Foundation | FOUND-01 to FOUND-12 | 12 | All SATISFIED |
| Permissions | PERM-01 to PERM-07 | 7 | All SATISFIED |
| Storage | STOR-01 to STOR-05 | 5 | All SATISFIED |
| File Search | SRCH-01 to SRCH-04 | 4 | All SATISFIED |
| Duplicates | DUPL-01 to DUPL-03 | 3 | All SATISFIED |
| Templates | TMPL-01 to TMPL-04 | 4 | All SATISFIED |
| Folder Structure | FOLD-01 to FOLD-02 | 2 | All SATISFIED |
| Bulk Operations | BULK-01 to BULK-05 | 5 | All SATISFIED |
| **Total** | | **42** | **42/42 mapped and complete** |
**Orphaned requirements:** None
**Unmapped requirements:** None
---
## Cross-Phase Integration
Integration checker ran full verification. Results:
| Check | Status |
|-------|--------|
| DI wiring (all 5 phases) | PASS — all services registered in App.xaml.cs |
| MainWindow tabs (10 tabs) | PASS — all declared and wired from DI |
| FeatureViewModelBase inheritance (10 VMs) | PASS |
| SessionManager usage (9 ViewModels + SiteListService) | PASS |
| ExecuteQueryRetryHelper (9 CSOM services, 40+ call sites) | PASS |
| SharePointPaginationHelper (2 services using list enumeration) | PASS |
| TranslationSource localization (15 XAML files, 170 bindings) | PASS |
| TenantSwitchedMessage propagation | PASS |
| Export chain completeness (all features) | PASS |
| Build | PASS — 0 warnings, 0 errors |
| Tests | PASS — 134 passed, 22 skipped (live CSOM), 0 failed |
| EN/FR key parity | PASS — 199/199 keys |
**Orphaned code:** `FeatureTabBase.xaml` — Phase 1 placeholder, now superseded by full tab views. Harmless dead code.
---
## Tech Debt & Deferred Items
### From Phase Verifications
| Item | Source | Severity | Description |
|------|--------|----------|-------------|
| Hardcoded export button text | Phase 2 | Info | `PermissionsView.xaml` uses `Content="Export CSV"` / `"Export HTML"` instead of `rad.csv.perms` / `rad.html.perms` localization keys. French users see English button labels. |
| Missing Designer.cs property | Phase 2 | Info | `Strings.Designer.cs` lacks `tab_permissions` typed accessor. Runtime binding via `TranslationSource` works fine. |
| No invalid-row highlighting | Phase 4 | Warning | `BulkMembersView.xaml`, `BulkSitesView.xaml`, `FolderStructureView.xaml` show IsValid as text column but lack `RowStyle` + `DataTrigger` for visual red highlighting on invalid rows. |
| FeatureTabBase dead code | Phase 1→all | Info | `Views/Controls/FeatureTabBase.xaml` is no longer imported by any tab view after all phases replaced stubs. |
| Cancel test locale mismatch | Phase 3 (03-08) | Info | `FeatureViewModelBaseTests.CancelCommand_DuringOperation_SetsStatusMessageToCancelled` asserts `.Contains("cancel")` but app returns French string "Opération annulée". Pre-existing; deferred. |
### Deferred v2 Requirements
These are explicitly out of scope for v1 and tracked in REQUIREMENTS.md:
- UACC-01/02: User access audit across sites
- SIMP-01/02/03: Simplified plain-language permission reports
- VIZZ-01/02/03: Storage metrics graphs (pie/bar chart)
---
## Human Verification Backlog
11 items across 3 phases require human confirmation (runtime UI/locale checks that cannot be automated):
### Phase 2 (2 items)
1. Full Permissions tab UI visual checkpoint (layout, disabled states, French locale)
2. Export button localization decision (accept hardcoded English or bind to resx keys)
### Phase 4 (7 items)
1. Application launches with all 10 tabs visible
2. Bulk Members — Load Example populates DataGrid with 7 rows
3. Bulk Sites — semicolon CSV auto-detection works
4. Invalid row display in DataGrid (IsValid=False, Errors column)
5. Confirmation dialog appears before bulk operations
6. Transfer tab — two-step browse flow (SitePickerDialog → FolderBrowserDialog)
7. Templates tab — 5 capture checkboxes visible and checked by default
### Phase 5 (2 items)
1. Clean-machine EXE launch (no .NET runtime installed)
2. French locale runtime rendering (diacritics display correctly in all tabs)
---
## Build & Test Summary
| Metric | Value |
|--------|-------|
| Build | 0 errors, 0 warnings |
| Tests passed | 134 |
| Tests skipped | 22 (live CSOM — expected) |
| Tests failed | 0 |
| EN locale keys | 199 |
| FR locale keys | 199 |
| Published EXE | 200.9 MB self-contained |
| Phases complete | 5/5 |
| Requirements satisfied | 42/42 |
---
## Verdict
**PASSED** — The milestone has achieved its definition of done:
1. All 42 v1 requirements are implemented with real code and verified by phase-level checks
2. All cross-phase integration points are wired (DI, messaging, shared infrastructure)
3. Build compiles cleanly with zero warnings
4. 134 automated tests pass with zero failures
5. Self-contained 200.9 MB EXE produced successfully
6. Full EN/FR locale parity (199 keys each)
**Remaining actions before shipping:**
- [ ] Complete 11 human verification items (UI visual checks, clean-machine launch)
- [ ] Decide on Phase 03 retroactive verification (or accept integration check as sufficient)
- [ ] Address 3 Warning-level tech debt items (invalid-row highlighting in bulk DataGrids)
- [ ] Optionally clean up FeatureTabBase dead code and fix cancel test locale mismatch
---
*Audited: 2026-04-07*
*Auditor: Claude (milestone audit)*

View File

@@ -0,0 +1,177 @@
# Requirements Archive: v1.0 MVP
**Archived:** 2026-04-07
**Status:** SHIPPED
For current requirements, see `.planning/REQUIREMENTS.md`.
---
# Requirements: SharePoint Toolbox v2
**Defined:** 2026-04-02
**Core Value:** Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application.
## v1 Requirements
Requirements for initial release. Each maps to roadmap phases.
### Foundation
- [x] **FOUND-01**: Application built with C#/WPF (.NET 10 LTS) using MVVM architecture
- [x] **FOUND-02**: Multi-tenant profile registry — user can create, rename, delete, and switch between tenant profiles (tenant URL, client ID, display name)
- [x] **FOUND-03**: Multi-tenant session caching — user stays authenticated across tenant switches without re-logging in (MSAL token cache per tenant)
- [x] **FOUND-04**: Interactive Azure AD OAuth login via browser — no client secrets or certificates stored
- [x] **FOUND-05**: All long-running operations report progress to the UI in real-time
- [x] **FOUND-06**: User can cancel any long-running operation mid-execution
- [x] **FOUND-07**: All errors surface to the user with actionable messages — no silent failures
- [x] **FOUND-08**: Structured logging for diagnostics (Serilog or equivalent)
- [x] **FOUND-09**: Localization system supporting English and French with dynamic language switching
- [x] **FOUND-10**: JSON-based local storage for profiles, settings, and templates (compatible with current app's format for migration)
- [x] **FOUND-11**: Self-contained single EXE distribution — no .NET runtime dependency for end users
- [x] **FOUND-12**: Configurable data output folder for exports
### Permissions
- [x] **PERM-01**: User can scan permissions on a single SharePoint site with configurable depth
- [x] **PERM-02**: User can scan permissions across multiple selected sites in one operation
- [x] **PERM-03**: Permissions scan includes owners, members, guests, external users, and broken inheritance
- [x] **PERM-04**: User can choose to include or exclude inherited permissions
- [x] **PERM-05**: User can export permissions report to CSV (raw data)
- [x] **PERM-06**: User can export permissions report to interactive HTML (sortable, filterable, groupable by user)
- [x] **PERM-07**: SharePoint 5,000-item list view threshold handled via pagination — no silent failures on large libraries
### Storage
- [x] **STOR-01**: User can view storage consumption per library on a site
- [x] **STOR-02**: User can view storage consumption per site with configurable folder depth
- [x] **STOR-03**: Storage metrics include total size, version size, item count, and last modified date
- [x] **STOR-04**: User can export storage metrics to CSV
- [x] **STOR-05**: User can export storage metrics to interactive HTML with collapsible tree view
### File Search
- [x] **SRCH-01**: User can search files across sites using multiple criteria (extension, name/regex, dates, creator, editor)
- [x] **SRCH-02**: User can configure maximum search results (up to 50,000)
- [x] **SRCH-03**: User can export search results to CSV
- [x] **SRCH-04**: User can export search results to interactive HTML (sortable, filterable)
### Duplicate Detection
- [x] **DUPL-01**: User can scan for duplicate files by name, size, creation date, modification date
- [x] **DUPL-02**: User can scan for duplicate folders by name, subfolder count, file count
- [x] **DUPL-03**: User can export duplicate report to HTML with grouped display and visual indicators
### Site Templates
- [x] **TMPL-01**: User can capture site structure (libraries, folders, permission groups, logo, settings) as a template
- [x] **TMPL-02**: User can apply template to create new Communication or Teams site
- [x] **TMPL-03**: Templates persist locally as JSON
- [x] **TMPL-04**: User can manage templates (create, rename, delete)
### Folder Structure
- [x] **FOLD-01**: User can create folder structures on a site from a CSV template
- [x] **FOLD-02**: Example CSV templates provided for common structures
### Bulk Operations
- [x] **BULK-01**: User can transfer files and folders between sites with progress tracking
- [x] **BULK-02**: User can add members to groups in bulk from CSV
- [x] **BULK-03**: User can create multiple sites in bulk from CSV
- [x] **BULK-04**: All bulk operations support cancellation mid-execution
- [x] **BULK-05**: Bulk operation errors are reported per-item (not silently skipped)
## v2 Requirements
Deferred to after v1 parity is confirmed. New features from project goals.
### User Access Audit
- **UACC-01**: User can export all SharePoint/Teams accesses a specific user has across selected sites
- **UACC-02**: Export includes direct assignments, group memberships, and inherited access
### Simplified Permissions
- **SIMP-01**: User can toggle plain-language permission labels (e.g., "Can edit files" instead of "Contribute")
- **SIMP-02**: Permissions report includes summary counts and color coding for untrained readers
- **SIMP-03**: Configurable detail level (simple/detailed) for reports
### Storage Visualization
- **VIZZ-01**: Storage Metrics tab includes a graph showing space by file type
- **VIZZ-02**: User can toggle between pie/donut chart and bar chart views
- **VIZZ-03**: Graph updates when storage scan completes
## Out of Scope
| Feature | Reason |
|---------|--------|
| Cross-platform (Mac/Linux) | WPF is Windows-only; not justified for current user base |
| Real-time monitoring / alerts | Requires background service, webhooks — turns desktop tool into a service |
| Automated remediation (auto-revoke) | Liability risk; one wrong rule destroys client access |
| SQLite / database storage | Breaks single-EXE distribution; JSON sufficient |
| Cloud sync / shared profiles | Requires server infrastructure — out of scope for local tool |
| AI-powered recommendations | Competes with Microsoft's own Copilot roadmap |
| Content migration between tenants | Separate product category (ShareGate territory) |
| Mobile app | Desktop admin tool |
| OAuth with client secrets/certificates | Interactive login only — no stored credentials |
| Version history management | Deep separate problem; surface totals in storage metrics only |
## Traceability
Which phases cover which requirements. Updated during roadmap creation.
| Requirement | Phase | Status |
|-------------|-------|--------|
| FOUND-01 | Phase 1 | Complete |
| FOUND-02 | Phase 1 | Complete |
| FOUND-03 | Phase 1 | Complete |
| FOUND-04 | Phase 1 | Complete |
| FOUND-05 | Phase 1 | Complete |
| FOUND-06 | Phase 1 | Complete |
| FOUND-07 | Phase 1 | Complete |
| FOUND-08 | Phase 1 | Complete |
| FOUND-09 | Phase 1 | Complete |
| FOUND-10 | Phase 1 | Complete |
| FOUND-11 | Phase 5 | Complete |
| FOUND-12 | Phase 1 | Complete |
| PERM-01 | Phase 2 | Complete |
| PERM-02 | Phase 2 | Complete |
| PERM-03 | Phase 2 | Complete |
| PERM-04 | Phase 2 | Complete |
| PERM-05 | Phase 2 | Complete |
| PERM-06 | Phase 2 | Complete |
| PERM-07 | Phase 2 | Complete |
| STOR-01 | Phase 3 | Complete |
| STOR-02 | Phase 3 | Complete |
| STOR-03 | Phase 3 | Complete |
| STOR-04 | Phase 3 | Complete |
| STOR-05 | Phase 3 | Complete |
| SRCH-01 | Phase 3 | Complete |
| SRCH-02 | Phase 3 | Complete |
| SRCH-03 | Phase 3 | Complete |
| SRCH-04 | Phase 3 | Complete |
| DUPL-01 | Phase 3 | Complete |
| DUPL-02 | Phase 3 | Complete |
| DUPL-03 | Phase 3 | Complete |
| TMPL-01 | Phase 4 | Complete |
| TMPL-02 | Phase 4 | Complete |
| TMPL-03 | Phase 4 | Complete |
| TMPL-04 | Phase 4 | Complete |
| FOLD-01 | Phase 4 | Complete |
| FOLD-02 | Phase 4 | Complete |
| BULK-01 | Phase 4 | Complete |
| BULK-02 | Phase 4 | Complete |
| BULK-03 | Phase 4 | Complete |
| BULK-04 | Phase 4 | Complete |
| BULK-05 | Phase 4 | Complete |
**Coverage:**
- v1 requirements: 42 total
- Mapped to phases: 42
- Unmapped: 0
---
*Requirements defined: 2026-04-02*
*Last updated: 2026-04-02 after roadmap creation — all 42 v1 requirements mapped*

View File

@@ -0,0 +1,147 @@
# Roadmap: SharePoint Toolbox v2
## Overview
A full C#/WPF rewrite of a 6,400-line PowerShell-based SharePoint Online administration tool. The
project delivers a self-contained Windows desktop application that lets MSP administrators audit
and manage permissions, storage, and site provisioning across multiple client tenants from a single
tool. Foundation infrastructure (multi-tenant auth, async patterns, error handling, DI) must be
solid before any feature work begins — all 10 identified pitfalls in the existing codebase map
entirely to Phase 1. Subsequent phases deliver complete, verifiable feature areas in dependency
order: permissions first (validates auth and pagination), then storage and search (reuse those
patterns), then bulk and provisioning operations (highest write-risk features last), then
hardening and packaging.
## Phases
**Phase Numbering:**
- Integer phases (1, 2, 3): Planned milestone work
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
Decimal phases appear between their surrounding integers in numeric order.
- [x] **Phase 1: Foundation** - WPF shell, multi-tenant auth, DI, async patterns, error handling, logging, localization, JSON persistence (completed 2026-04-02)
- [x] **Phase 2: Permissions** - Permissions scan (single and multi-site), CSV and HTML report export
- [x] **Phase 3: Storage and File Operations** - Storage metrics, file search, and duplicate detection (completed 2026-04-02)
- [x] **Phase 4: Bulk Operations and Provisioning** - Bulk member/site/transfer operations, site templates, folder structure provisioning (completed 2026-04-03)
- [x] **Phase 5: Distribution and Hardening** - Self-contained EXE packaging, end-to-end validation, FR locale completeness (completed 2026-04-03)
## Phase Details
### Phase 1: Foundation
**Goal**: The application shell runs, users can authenticate to multiple tenants and switch between them without re-logging in, all long-running operations are cancellable and report progress, all errors surface visibly, and the infrastructure patterns that prevent the existing app's 10 known pitfalls are in place before any feature work begins.
**Depends on**: Nothing (first phase)
**Requirements**: FOUND-01, FOUND-02, FOUND-03, FOUND-04, FOUND-05, FOUND-06, FOUND-07, FOUND-08, FOUND-09, FOUND-10, FOUND-12
**Success Criteria** (what must be TRUE):
1. User can create, rename, delete, and switch between tenant profiles via the UI — each profile stores tenant URL, client ID, and display name in a JSON file
2. User can authenticate to a tenant via interactive browser login and the session persists across tenant switches without re-entering credentials (MSAL token cache per tenant)
3. User can see real-time progress on any long-running operation and cancel it mid-execution with a button — the operation stops cleanly with no silent continuation
4. When any operation fails, the user sees an actionable error message in the UI — no operation fails silently or swallows an exception
5. UI language switches between English and French dynamically without restarting the application
**Plans**: 8 plans
Plans:
- [ ] 01-01-PLAN.md — Solution scaffold: WPF project + xUnit test project with Generic Host entry point
- [ ] 01-02-PLAN.md — Core layer: models, messages, pagination helper, retry helper, LogPanelSink
- [ ] 01-03-PLAN.md — Persistence layer: ProfileRepository + SettingsRepository + services + unit tests
- [ ] 01-04-PLAN.md — Auth layer: MsalClientFactory + SessionManager + unit tests
- [ ] 01-05-PLAN.md — Localization + Serilog: TranslationSource, EN/FR resx, integration tests
- [ ] 01-06-PLAN.md — ViewModels + WPF shell: FeatureViewModelBase, MainWindow XAML, global exception handlers
- [ ] 01-07-PLAN.md — UI dialogs: ProfileManagementDialog + SettingsView wired into shell
- [ ] 01-08-PLAN.md — Checkpoint: full test suite + visual verification of running application
### Phase 2: Permissions
**Goal**: Users can scan SharePoint permissions on one or many sites and export the results as both a raw CSV and a sortable, filterable HTML report — with no silent failures on large libraries and full control over scan scope.
**Depends on**: Phase 1
**Requirements**: PERM-01, PERM-02, PERM-03, PERM-04, PERM-05, PERM-06, PERM-07
**Success Criteria** (what must be TRUE):
1. User can select one site or multiple sites and run a permissions scan that returns owners, members, guests, external users, and broken inheritance items
2. User can choose configurable scan depth and whether to include or exclude inherited permissions before running
3. User can export the permissions results to a CSV file with all raw permission data
4. User can export the permissions results to an interactive HTML report where rows are sortable, filterable, and groupable by user
5. Scanning a library with more than 5,000 items completes successfully — the tool paginates automatically and does not silently truncate or fail
**Plans**: 7 plans
Plans:
- [x] 02-01-PLAN.md — Wave 0: test scaffolds (PermissionsService, ViewModel, classification, CSV, HTML export tests) + PermissionEntryHelper
- [x] 02-02-PLAN.md — Core models + PermissionsService scan engine (PermissionEntry, ScanOptions, IPermissionsService, PermissionsService)
- [x] 02-03-PLAN.md — SiteListService: tenant admin site listing for multi-site picker (ISiteListService, SiteListService, SiteInfo)
- [x] 02-04-PLAN.md — Export services: CsvExportService (with row merging) + HtmlExportService (self-contained HTML)
- [x] 02-05-PLAN.md — Localization: 15 Phase 2 EN/FR keys in Strings.resx, Strings.fr.resx, Strings.Designer.cs
- [x] 02-06-PLAN.md — PermissionsViewModel + SitePickerDialog (XAML + code-behind)
- [x] 02-07-PLAN.md — DI wiring + PermissionsView XAML + MainWindow tab replacement + visual checkpoint
### Phase 3: Storage and File Operations
**Goal**: Users can view and export storage metrics per site and library, search for files across sites using multiple criteria, and detect duplicate files and folders — all with consistent export options and no silent failures on large datasets.
**Depends on**: Phase 2
**Requirements**: STOR-01, STOR-02, STOR-03, STOR-04, STOR-05, SRCH-01, SRCH-02, SRCH-03, SRCH-04, DUPL-01, DUPL-02, DUPL-03
**Success Criteria** (what must be TRUE):
1. User can view storage consumption per library and per site (with configurable folder depth), including total size, version size, item count, and last modified date
2. User can export storage metrics to CSV and to an interactive HTML with a collapsible tree view
3. User can search for files across sites using at least extension, name/regex, date range, creator, and editor as criteria — with a configurable result cap up to 50,000 items
4. User can export file search results to CSV and to an interactive sortable/filterable HTML
5. User can scan for duplicate files (by name, size, creation date, modification date) and duplicate folders (by name, subfolder count, file count) and export the results to an HTML with grouped display
**Plans**: 8 plans
Plans:
- [ ] 03-01-PLAN.md — Wave 0: test scaffolds + models (StorageNode, SearchResult, DuplicateGroup/Item, options) + interfaces (IStorageService, ISearchService, IDuplicatesService) + export stubs
- [ ] 03-02-PLAN.md — StorageService: CSOM StorageMetrics scan engine (recursive folder tree, library-level aggregation)
- [ ] 03-03-PLAN.md — Storage export services: StorageCsvExportService + StorageHtmlExportService (collapsible tree HTML)
- [ ] 03-04-PLAN.md — SearchService (KQL pagination, client-side Regex) + DuplicatesService (composite key grouping, CAML folder scan)
- [ ] 03-05-PLAN.md — Search and Duplicate export services: SearchCsvExportService + SearchHtmlExportService + DuplicatesHtmlExportService
- [ ] 03-06-PLAN.md — Localization: all Phase 3 EN/FR keys for Storage, File Search, and Duplicates tabs
- [ ] 03-07-PLAN.md — StorageViewModel + StorageView XAML + DI wiring (Storage tab replaces FeatureTabBase stub)
- [ ] 03-08-PLAN.md — SearchViewModel + SearchView + DuplicatesViewModel + DuplicatesView + DI wiring + visual checkpoint
### Phase 4: Bulk Operations and Provisioning
**Goal**: Users can execute bulk write operations (member additions, site creation, file transfer) with per-item error reporting and cancellation, capture site structures as reusable templates, apply templates to create new sites, and provision folder structures from CSV — all without silent partial failures.
**Depends on**: Phase 3
**Requirements**: BULK-01, BULK-02, BULK-03, BULK-04, BULK-05, TMPL-01, TMPL-02, TMPL-03, TMPL-04, FOLD-01, FOLD-02
**Success Criteria** (what must be TRUE):
1. User can transfer files and folders between sites with real-time progress tracking and can cancel mid-operation — transferred items are confirmed and failures are reported per-item
2. User can add members to groups in bulk from a CSV file — each row that fails is reported individually, not silently skipped
3. User can create multiple sites in bulk from a CSV file with per-site error reporting and mid-operation cancellation
4. User can capture an existing site's structure (libraries, folders, permission groups, logo, settings) as a named template stored in JSON, then apply that template to create a new Communication or Teams site
5. User can manage saved templates (create, rename, delete) and create folder structures on a target site from a CSV template
**Plans**: 10 plans
Plans:
- [ ] 04-01-PLAN.md — Dependencies + core models + interfaces + BulkOperationRunner + test scaffolds
- [ ] 04-02-PLAN.md — CsvValidationService + TemplateRepository with unit tests
- [ ] 04-03-PLAN.md — FileTransferService (CSOM MoveCopyUtil, conflict policies)
- [ ] 04-04-PLAN.md — BulkMemberService (Graph SDK batch + CSOM fallback)
- [ ] 04-05-PLAN.md — BulkSiteService (PnP Framework site creation)
- [ ] 04-06-PLAN.md — TemplateService + FolderStructureService
- [ ] 04-07-PLAN.md — Localization + shared dialogs + example CSV resources
- [ ] 04-08-PLAN.md — TransferViewModel + TransferView
- [ ] 04-09-PLAN.md — BulkMembersViewModel + BulkSitesViewModel + FolderStructureViewModel + Views
- [ ] 04-10-PLAN.md — TemplatesViewModel + TemplatesView + DI registration + MainWindow wiring + visual checkpoint
### Phase 5: Distribution and Hardening
**Goal**: The application ships as a single self-contained EXE that runs on a machine with no .NET runtime installed, all previously identified reliability constraints are verified end-to-end (5,000-item pagination, JSON corruption recovery, throttling retry, cancellation), and the French locale is complete and tested.
**Depends on**: Phase 4
**Requirements**: FOUND-11
**Success Criteria** (what must be TRUE):
1. Running the published EXE on a clean machine with no .NET runtime installed launches the application and all features function correctly
2. The application recovers gracefully when a SharePoint API call is throttled (429/503) — the user sees a retry progress message and the operation eventually completes or surfaces a clear failure
3. The French locale is complete for all UI strings — no English fallback text appears when the language is set to French
4. A scan against a library with more than 5,000 items returns complete, correct results with no silent truncation verified against a known dataset
**Plans**: 3 plans
Plans:
- [ ] 05-01-PLAN.md — Helper visibility changes + retry/pagination/locale unit tests
- [ ] 05-02-PLAN.md — FR diacritic corrections + self-contained publish configuration
- [ ] 05-03-PLAN.md — Full test suite verification + publish smoke test + human checkpoint
## Progress
**Execution Order:**
Phases execute in numeric order: 1 → 2 → 3 → 4 → 5
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Foundation | 8/8 | Complete | 2026-04-02 |
| 2. Permissions | 7/7 | Complete | 2026-04-02 |
| 3. Storage and File Operations | 8/8 | Complete | 2026-04-02 |
| 4. Bulk Operations and Provisioning | 10/10 | Complete | 2026-04-03 |
| 5. Distribution and Hardening | 3/3 | Complete | 2026-04-03 |

Some files were not shown because too many files have changed in this diff Show More