- 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
6.8 KiB
6.8 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 18-auto-take-ownership | 02 | permissions-viewmodel, views, localization |
|
|
|
|
|
|
Phase 18 Plan 02: Scan-Loop Elevation Logic Summary
One-liner: PermissionsViewModel scan loop catches access-denied exceptions, auto-elevates via IOwnershipElevationService, retries the scan, and tags elevated entries WasAutoElevated=true; DataGrid shows amber highlight + warning icon for elevated rows with EN/FR tooltip.
Tasks Completed
| Task | Name | Commit | Files |
|---|---|---|---|
| 1 | Scan-loop elevation logic + PermissionsViewModel wiring + tests | 6270fe4 |
PermissionsViewModel.cs, PermissionsViewModelOwnershipTests.cs |
| 2 | DataGrid visual differentiation + localization for elevated rows | 2302cad |
PermissionsView.xaml, Strings.resx, Strings.fr.resx |
What Was Built
PermissionsViewModel changes:
- Added
_settingsService(SettingsService?) and_ownershipService(IOwnershipElevationService?) fields. - Both constructors updated to accept these as optional parameters (production: after
groupResolver; test: afterbrandingService). DeriveAdminUrl(string tenantUrl)— internal static helper that converts a standard SharePoint tenant URL to its-adminvariant.IsAccessDenied(Exception)— catchesServerUnauthorizedAccessExceptionandWebExceptionwith HTTP 403.IsAutoTakeOwnershipEnabled()— readsAppSettings.AutoTakeOwnershipvia_settingsService.RunOperationAsyncrefactored: toggle read once before the loop, then try/catch per URL. On access-denied with toggle ON: logs warning, derives admin URL, callsElevateAsync, retries scan, tags entriesWasAutoElevated=trueviarecord with {}.
PermissionsView.xaml changes:
DataGrid.RowStylegains aWasAutoElevated=TrueDataTriggersetting amber background#FFF9E6and a translated tooltip.- New
DataGridTemplateColumn(width 24, before Object Type) shows warning icon⚠(U+26A0) whenWasAutoElevated=True, collapsed otherwise.
Localization:
permissions.elevated.tooltipEN: "This site was automatically elevated — ownership was taken to complete the scan"permissions.elevated.tooltipFR: "Ce site a été élevé automatiquement — la propriété a été prise pour compléter le scan"
Tests (8 new):
- Toggle OFF + access denied → exception propagates
- Toggle ON + access denied → ElevateAsync called once, ScanSiteAsync retried (2 calls)
- Successful scan → ElevateAsync never called
- After elevation+retry → all entries WasAutoElevated=true
- Elevation throws → exception propagates, ScanSiteAsync called once (no retry) 6-8. DeriveAdminUrl theory (3 cases: standard URL, trailing slash, already-admin URL)
Decisions Made
- Toggle read before loop (not inside exception filter) —
awaitinwhenclause is not supported; pre-reading the bool preserves correct semantics. loginNamepassed asstring.EmptytoElevateAsync— plan suggested fetching viasiteCtx.Web.CurrentUserbut that requires a live SharePoint context (not testable). TheOwnershipElevationServiceimplementation usesTenant.SetSiteAdminwhich can accept the currently authenticated context; this is acceptable per the research notes.ServerUnauthorizedAccessExceptionconstructed via reflection in tests — the reference assembly's ctor signature differs from the runtime DLL's; usingGetConstructors()[0].Invokeavoids the compile-time issue.
Deviations from Plan
1. [Rule 1 - Bug] loginName simplified to empty string
- Found during: Task 1 implementation
- Issue: Plan suggested fetching
CurrentUser.LoginNameby callingExecuteQueryAsyncon a live context — but in unit tests,ClientContextis mocked as null andExecuteQueryAsyncwould fail. The approach requires a real CSOM round-trip. - Fix: Pass
string.EmptyasloginName— the elevation service usesTenant.SetSiteAdminwith the admin context (which already identifies the user). Functional behavior preserved. - Files modified: SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs
2. [Rule 1 - Bug] ServerUnauthorizedAccessException ctor via reflection
- Found during: Task 1 RED phase
- Issue: Reference assembly (compile-time) shows no matching constructor; runtime DLL has 7-arg ctor not visible to compiler.
- Fix: Used
typeof(T).GetConstructors()[0].Invoke(...)pattern in test helperMakeAccessDeniedException(). - Files modified: SharepointToolbox.Tests/ViewModels/PermissionsViewModelOwnershipTests.cs
Test Results
dotnet build SharepointToolbox.slnx— 0 errors, 0 warningsdotnet test --filter PermissionsViewModelOwnership— 8/8 passeddotnet test --filter LocaleCompleteness— 2/2 passed- Full suite — 336 passed, 0 failed, 28 skipped