diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md
index bb2581c..fa5981e 100644
--- a/.planning/REQUIREMENTS.md
+++ b/.planning/REQUIREMENTS.md
@@ -48,24 +48,24 @@ Requirements for v2.3 Tenant Management & Report Enhancements. Each maps to road
| Requirement | Phase | Status |
|-------------|-------|--------|
-| APPREG-01 | — | Pending |
-| APPREG-02 | — | Pending |
-| APPREG-03 | — | Pending |
-| APPREG-04 | — | Pending |
-| APPREG-05 | — | Pending |
-| APPREG-06 | — | Pending |
-| OWN-01 | — | Pending |
-| OWN-02 | — | Pending |
-| RPT-01 | — | Pending |
-| RPT-02 | — | Pending |
-| RPT-03 | — | Pending |
-| RPT-04 | — | Pending |
+| APPREG-01 | Phase 19 | Pending |
+| APPREG-02 | Phase 19 | Pending |
+| APPREG-03 | Phase 19 | Pending |
+| APPREG-04 | Phase 19 | Pending |
+| APPREG-05 | Phase 19 | Pending |
+| APPREG-06 | Phase 19 | Pending |
+| OWN-01 | Phase 18 | Pending |
+| OWN-02 | Phase 18 | Pending |
+| RPT-01 | Phase 17 | Pending |
+| RPT-02 | Phase 17 | Pending |
+| RPT-03 | Phase 16 | Pending |
+| RPT-04 | Phase 15 | Pending |
**Coverage:**
- v2.3 requirements: 12 total
-- Mapped to phases: 0
-- Unmapped: 12
+- Mapped to phases: 12
+- Unmapped: 0
---
*Requirements defined: 2026-04-09*
-*Last updated: 2026-04-09 after initial definition*
+*Last updated: 2026-04-09 after roadmap created*
diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 2db97c8..b633085 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -5,6 +5,7 @@
- ✅ **v1.0 MVP** — Phases 1-5 (shipped 2026-04-07) — [archive](milestones/v1.0-ROADMAP.md)
- ✅ **v1.1 Enhanced Reports** — Phases 6-9 (shipped 2026-04-08) — [archive](milestones/v1.1-ROADMAP.md)
- ✅ **v2.2 Report Branding & User Directory** — Phases 10-14 (shipped 2026-04-09) — [archive](milestones/v2.2-ROADMAP.md)
+- 🔄 **v2.3 Tenant Management & Report Enhancements** — Phases 15-19 (in progress)
## Phases
@@ -40,6 +41,72 @@
+### v2.3 Tenant Management & Report Enhancements (Phases 15-19)
+
+- [ ] **Phase 15: Consolidation Data Model** — PermissionConsolidator service and merged-row model; zero API calls, pure data shapes
+- [ ] **Phase 16: Report Consolidation Toggle** — Export settings toggle wired to PermissionConsolidator; first user-visible consolidation behavior
+- [ ] **Phase 17: Group Expansion in HTML Reports** — Clickable group expansion in HTML exports with transitive membership resolution
+- [ ] **Phase 18: Auto-Take Ownership** — Global toggle and automatic site collection admin elevation on access denied
+- [ ] **Phase 19: App Registration & Removal** — Automated Entra app registration with guided fallback and clean removal
+
+## Phase Details
+
+### Phase 15: Consolidation Data Model
+**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 (no API calls, no UI dependencies)
+**Requirements**: RPT-04
+**Success Criteria** (what must be TRUE):
+ 1. A `ConsolidatedPermissionEntry` model exists that represents a single user's merged access across multiple locations with identical access levels
+ 2. A `PermissionConsolidator` service accepts a flat list of permission rows and returns a consolidated list where duplicate user+level rows are merged
+ 3. Consolidation logic has unit test coverage — a known 10-row input with 3 duplicate pairs produces the expected 7-row output
+ 4. Existing HTML export services compile and produce identical output when consolidation is not applied (opt-in, defaults off)
+**Plans**: TBD
+
+### Phase 16: Report Consolidation Toggle
+**Goal**: Users can choose to merge duplicate permission entries per export through a toggle in the export settings dialog
+**Depends on**: Phase 15
+**Requirements**: RPT-03
+**Success Criteria** (what must be TRUE):
+ 1. A consolidation toggle is visible in the export settings dialog (or export options panel) and defaults to OFF
+ 2. When the toggle is OFF, the exported HTML report is byte-for-byte identical to the pre-v2.3 output
+ 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. The toggle state is remembered for the session (does not reset between exports within the same session)
+**Plans**: TBD
+
+### Phase 17: Group Expansion in HTML Reports
+**Goal**: Users can expand SharePoint group entries in HTML reports to see the group's members, including members of nested groups
+**Depends on**: Phase 16
+**Requirements**: RPT-01, RPT-02
+**Success Criteria** (what must be TRUE):
+ 1. SharePoint group rows in the HTML report render as expandable — clicking a group name reveals its member list inline
+ 2. Member resolution includes transitive membership: nested groups are recursively resolved so every leaf user is shown
+ 3. Group expansion is triggered at export time via Graph API — the permission scan itself is unchanged
+ 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
+**Plans**: TBD
+
+### Phase 18: Auto-Take Ownership
+**Goal**: Users can enable automatic site collection admin elevation so that access-denied sites during scans no longer block audit progress
+**Depends on**: Phase 15
+**Requirements**: OWN-01, OWN-02
+**Success Criteria** (what must be TRUE):
+ 1. A global "Auto-take ownership on access denied" toggle exists in application settings and defaults to OFF
+ 2. When the toggle is OFF, access-denied sites produce the same error behavior as before v2.3 (no regression)
+ 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. 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)
+**Plans**: TBD
+
+### Phase 19: App Registration & Removal
+**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 18
+**Requirements**: APPREG-01, APPREG-02, APPREG-03, APPREG-04, APPREG-05, APPREG-06
+**Success Criteria** (what must be TRUE):
+ 1. A "Register App" action is available in the profile create/edit dialog and is the recommended path for new tenant onboarding
+ 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. 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 "Remove App" action in the profile dialog removes the Azure AD application registration from the target tenant
+ 5. After removal, all cached MSAL tokens and session state for that tenant are cleared, and subsequent operations require re-authentication
+**Plans**: TBD
+
## Progress
| Phase | Milestone | Plans | Status | Completed |
@@ -47,3 +114,8 @@
| 1-5 | v1.0 | 36/36 | Shipped | 2026-04-07 |
| 6-9 | v1.1 | 25/25 | Shipped | 2026-04-08 |
| 10-14 | v2.2 | 14/14 | Shipped | 2026-04-09 |
+| 15. Consolidation Data Model | v2.3 | 0/? | Not started | — |
+| 16. Report Consolidation Toggle | v2.3 | 0/? | Not started | — |
+| 17. Group Expansion in HTML Reports | v2.3 | 0/? | Not started | — |
+| 18. Auto-Take Ownership | v2.3 | 0/? | Not started | — |
+| 19. App Registration & Removal | v2.3 | 0/? | Not started | — |
diff --git a/.planning/STATE.md b/.planning/STATE.md
index bc11d07..5df8dd9 100644
--- a/.planning/STATE.md
+++ b/.planning/STATE.md
@@ -2,12 +2,12 @@
gsd_state_version: 1.0
milestone: v2.3
milestone_name: Tenant Management & Report Enhancements
-status: defining-requirements
-stopped_at: milestone started
+status: roadmap-ready
+stopped_at: roadmap created — ready for phase 15 planning
last_updated: "2026-04-09"
-last_activity: 2026-04-09 — Milestone v2.3 started
+last_activity: 2026-04-09 — Roadmap created for v2.3 (phases 15-19)
progress:
- total_phases: 0
+ total_phases: 5
completed_phases: 0
total_plans: 0
completed_plans: 0
@@ -20,14 +20,18 @@ progress:
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.
-**Current focus:** v2.3 Tenant Management & Report Enhancements
+**Current focus:** v2.3 Tenant Management & Report Enhancements — Phase 15 next
## Current Position
-Phase: Not started (defining requirements)
+Phase: 15 — Consolidation Data Model (not started)
Plan: —
-Status: Defining requirements
-Last activity: 2026-04-09 — Milestone v2.3 started
+Status: Roadmap approved — ready to plan Phase 15
+Last activity: 2026-04-09 — Roadmap created for v2.3 (phases 15-19)
+
+```
+v2.3 Progress: ░░░░░░░░░░ 0% (0/5 phases)
+```
## Shipped Milestones
@@ -35,12 +39,29 @@ Last activity: 2026-04-09 — Milestone v2.3 started
- v1.1 Enhanced Reports — Phases 6-9 (shipped 2026-04-08)
- v2.2 Report Branding & User Directory — Phases 10-14 (shipped 2026-04-09)
+## v2.3 Phase Map
+
+| Phase | Name | Requirements | Status |
+|-------|------|--------------|--------|
+| 15 | Consolidation Data Model | RPT-04 | Not started |
+| 16 | Report Consolidation Toggle | RPT-03 | Not started |
+| 17 | Group Expansion in HTML Reports | RPT-01, RPT-02 | Not started |
+| 18 | Auto-Take Ownership | OWN-01, OWN-02 | Not started |
+| 19 | App Registration & Removal | APPREG-01..06 | Not started |
+
## Accumulated Context
### Decisions
Decisions are logged in PROJECT.md Key Decisions table.
+**v2.3 notable constraints:**
+- Phase 19 has the highest blast radius (Entra changes) — must be last
+- Phase 15 is zero-API-call foundation; unblocks Phase 16 (consolidation) and Phase 18 (ownership) independently
+- Group expansion (Phase 17) calls Graph at export time, not at scan time — scan pipeline unchanged
+- Auto-take ownership uses PnP `Tenant.SetSiteAdmin` — requires Tenant Admin scope
+- App registration must be atomic with rollback; partial Entra state is worse than no state
+
### Pending Todos
None.
@@ -52,6 +73,6 @@ None.
## Session Continuity
Last session: 2026-04-09
-Stopped at: Milestone v2.3 started — defining requirements
+Stopped at: Roadmap created — ready to plan Phase 15
Resume file: None
-Next step: Define requirements, then create roadmap
+Next step: `/gsd:plan-phase 15`
diff --git a/.planning/phases/14-user-directory-view/14-01-PLAN.md b/.planning/phases/14-user-directory-view/14-01-PLAN.md
new file mode 100644
index 0000000..9d6538c
--- /dev/null
+++ b/.planning/phases/14-user-directory-view/14-01-PLAN.md
@@ -0,0 +1,275 @@
+---
+phase: 14-user-directory-view
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - SharepointToolbox/Localization/Strings.resx
+ - SharepointToolbox/Localization/Strings.fr.resx
+ - SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs
+ - SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
+ - SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs
+autonomous: true
+requirements:
+ - UDIR-05
+ - UDIR-01
+
+must_haves:
+ truths:
+ - "SelectDirectoryUserCommand takes a GraphDirectoryUser, converts it to GraphUserResult, adds it to SelectedUsers via existing logic"
+ - "After SelectDirectoryUserCommand, the user appears in SelectedUsers and can be audited with RunCommand"
+ - "SelectDirectoryUserCommand does not add duplicates (same UPN check as existing AddUserCommand)"
+ - "Localization keys for directory UI exist in both EN and FR resource files"
+ - "Code-behind has a DirectoryDataGrid_MouseDoubleClick handler that invokes SelectDirectoryUserCommand"
+ artifacts:
+ - path: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
+ provides: "SelectDirectoryUserCommand bridging directory selection to audit pipeline"
+ contains: "SelectDirectoryUserCommand"
+ - path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
+ provides: "Event handler for directory DataGrid double-click"
+ contains: "DirectoryDataGrid_MouseDoubleClick"
+ - path: "SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs"
+ provides: "Tests for SelectDirectoryUserCommand"
+ contains: "SelectDirectoryUser"
+ key_links:
+ - from: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
+ to: "SharepointToolbox/Core/Models/GraphDirectoryUser.cs"
+ via: "command parameter type"
+ pattern: "GraphDirectoryUser"
+ - from: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
+ to: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
+ via: "command invocation"
+ pattern: "SelectDirectoryUserCommand"
+---
+
+
+Add localization keys for directory UI, the SelectDirectoryUserCommand that bridges directory selection to the audit pipeline, and a code-behind event handler for DataGrid double-click.
+
+Purpose: Provides the infrastructure (localization, command, event handler) that Plan 14-02 needs to build the XAML view. SC2 requires selecting a directory user to trigger an audit — this command makes that possible.
+
+Output: Localization keys (EN+FR), SelectDirectoryUserCommand with tests, code-behind event handler.
+
+
+
+@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
+@C:/Users/dev/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/phases/14-user-directory-view/14-RESEARCH.md
+
+
+
+From SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs:
+```csharp
+public RelayCommand AddUserCommand { get; }
+
+private void ExecuteAddUser(GraphUserResult? user)
+{
+ if (user == null) return;
+ if (!SelectedUsers.Any(u => u.UserPrincipalName == user.UserPrincipalName))
+ {
+ SelectedUsers.Add(user);
+ }
+ SearchQuery = string.Empty;
+ SearchResults.Clear();
+}
+```
+
+
+From SharepointToolbox/Core/Models/GraphDirectoryUser.cs:
+```csharp
+public record GraphDirectoryUser(
+ string DisplayName, string UserPrincipalName,
+ string? Mail, string? Department, string? JobTitle, string? UserType);
+```
+
+
+From SharepointToolbox/Services/IGraphUserSearchService.cs:
+```csharp
+public record GraphUserResult(string DisplayName, string UserPrincipalName, string? Mail);
+```
+
+
+From SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs:
+```csharp
+private void SearchResultsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+{
+ if (sender is ListBox listBox && listBox.SelectedItem is GraphUserResult user)
+ {
+ var vm = (UserAccessAuditViewModel)DataContext;
+ if (vm.AddUserCommand.CanExecute(user))
+ vm.AddUserCommand.Execute(user);
+ listBox.SelectedItem = null;
+ }
+}
+```
+
+
+From Strings.resx:
+```xml
+Select Users
+Run Audit
+```
+
+
+
+
+
+
+ Task 1: Add localization keys for directory UI (EN + FR)
+
+ SharepointToolbox/Localization/Strings.resx,
+ SharepointToolbox/Localization/Strings.fr.resx
+
+
+ - Both resx files contain matching keys for directory browse UI
+
+
+ 1. Add to `Strings.resx` (EN):
+ - `audit.mode.search` = "Search"
+ - `audit.mode.browse` = "Browse Directory"
+ - `directory.grp.browse` = "User Directory"
+ - `directory.btn.load` = "Load Directory"
+ - `directory.btn.cancel` = "Cancel"
+ - `directory.filter.placeholder` = "Filter users..."
+ - `directory.chk.guests` = "Include guests"
+ - `directory.status.count` = "users"
+ - `directory.hint.doubleclick` = "Double-click a user to add to audit"
+ - `directory.col.name` = "Name"
+ - `directory.col.upn` = "Email"
+ - `directory.col.department` = "Department"
+ - `directory.col.jobtitle` = "Job Title"
+ - `directory.col.type` = "Type"
+
+ 2. Add to `Strings.fr.resx` (FR):
+ - `audit.mode.search` = "Recherche"
+ - `audit.mode.browse` = "Parcourir l'annuaire"
+ - `directory.grp.browse` = "Annuaire utilisateurs"
+ - `directory.btn.load` = "Charger l'annuaire"
+ - `directory.btn.cancel` = "Annuler"
+ - `directory.filter.placeholder` = "Filtrer les utilisateurs..."
+ - `directory.chk.guests` = "Inclure les invités"
+ - `directory.status.count` = "utilisateurs"
+ - `directory.hint.doubleclick` = "Double-cliquez sur un utilisateur pour l'ajouter à l'audit"
+ - `directory.col.name` = "Nom"
+ - `directory.col.upn` = "Courriel"
+ - `directory.col.department` = "Département"
+ - `directory.col.jobtitle` = "Poste"
+ - `directory.col.type` = "Type"
+
+
+ dotnet build --no-restore -warnaserror
+
+ 14 localization keys present in both EN and FR resource files.
+
+
+
+ Task 2: Add SelectDirectoryUserCommand to ViewModel
+
+ SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs,
+ SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs
+
+
+ - SelectDirectoryUserCommand is a RelayCommand
+ - It converts GraphDirectoryUser to GraphUserResult and adds to SelectedUsers
+ - Duplicate UPN check (same as AddUserCommand)
+ - Does NOT clear SearchQuery/SearchResults (not in search mode context)
+ - After execution, IsBrowseMode stays true — user can continue selecting from directory
+
+
+ 1. Add command declaration in ViewModel:
+ ```csharp
+ public RelayCommand SelectDirectoryUserCommand { get; }
+ ```
+
+ 2. Initialize in BOTH constructors:
+ ```csharp
+ SelectDirectoryUserCommand = new RelayCommand(ExecuteSelectDirectoryUser);
+ ```
+
+ 3. Implement the command method:
+ ```csharp
+ private void ExecuteSelectDirectoryUser(GraphDirectoryUser? dirUser)
+ {
+ if (dirUser == null) return;
+ var userResult = new GraphUserResult(dirUser.DisplayName, dirUser.UserPrincipalName, dirUser.Mail);
+ if (!SelectedUsers.Any(u => u.UserPrincipalName == userResult.UserPrincipalName))
+ {
+ SelectedUsers.Add(userResult);
+ }
+ }
+ ```
+
+ 4. Add tests to `UserAccessAuditViewModelDirectoryTests.cs`:
+ - Test: SelectDirectoryUserCommand adds user to SelectedUsers
+ - Test: SelectDirectoryUserCommand skips duplicates
+ - Test: SelectDirectoryUserCommand with null does nothing
+ - Test: After SelectDirectoryUser, user can be audited with RunCommand (integration: add user + check SelectedUsers.Count > 0)
+
+
+ dotnet build --no-restore -warnaserror && dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~UserAccessAuditViewModelDirectory" --no-build -q
+
+ SelectDirectoryUserCommand bridges directory selection to audit pipeline. Tests pass.
+
+
+
+ Task 3: Add code-behind event handler for directory DataGrid
+
+ SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
+
+
+ - DirectoryDataGrid_MouseDoubleClick handler extracts the clicked GraphDirectoryUser
+ - Invokes SelectDirectoryUserCommand with the selected user
+ - Uses the same pattern as SearchResultsListBox_SelectionChanged
+
+
+ 1. Add to `UserAccessAuditView.xaml.cs`:
+ ```csharp
+ private void DirectoryDataGrid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
+ {
+ if (sender is DataGrid grid && grid.SelectedItem is GraphDirectoryUser user)
+ {
+ var vm = (UserAccessAuditViewModel)DataContext;
+ if (vm.SelectDirectoryUserCommand.CanExecute(user))
+ vm.SelectDirectoryUserCommand.Execute(user);
+ }
+ }
+ ```
+
+ 2. Add the required using statement if not present:
+ ```csharp
+ using System.Windows.Controls; // Already present
+ using SharepointToolbox.Core.Models; // For GraphDirectoryUser
+ ```
+
+
+ dotnet build --no-restore -warnaserror
+
+ Code-behind event handler exists, ready to be wired in XAML (Plan 14-02).
+
+
+
+
+
+```bash
+dotnet build --no-restore -warnaserror
+dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~UserAccessAuditViewModelDirectory" --no-build -q
+```
+Both must pass with zero failures.
+
+
+
+- 14 localization keys in both EN and FR resx files
+- SelectDirectoryUserCommand converts GraphDirectoryUser → GraphUserResult → SelectedUsers
+- Duplicate UPN check prevents adding same user twice
+- Code-behind event handler for DataGrid double-click
+- All tests pass, build clean
+
+
+
diff --git a/.planning/phases/14-user-directory-view/14-02-PLAN.md b/.planning/phases/14-user-directory-view/14-02-PLAN.md
new file mode 100644
index 0000000..fb3d737
--- /dev/null
+++ b/.planning/phases/14-user-directory-view/14-02-PLAN.md
@@ -0,0 +1,338 @@
+---
+phase: 14-user-directory-view
+plan: 02
+type: execute
+wave: 2
+depends_on: [14-01]
+files_modified:
+ - SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml
+autonomous: true
+requirements:
+ - UDIR-05
+ - UDIR-01
+
+must_haves:
+ truths:
+ - "The left panel shows a mode toggle (two RadioButtons: Search / Browse Directory) at the top"
+ - "When Search mode is selected (IsBrowseMode=false), the existing people-picker GroupBox is visible and the directory panel is collapsed"
+ - "When Browse mode is selected (IsBrowseMode=true), the directory panel is visible and the people-picker GroupBox is collapsed"
+ - "The Scan Options GroupBox and Run/Export buttons remain visible in both modes"
+ - "The directory panel contains: Load Directory button, Cancel button, Include guests checkbox, filter TextBox, status text, user count, and a DataGrid"
+ - "The DataGrid is bound to DirectoryUsersView with columns: Name, Email, Department, Job Title, Type"
+ - "The DataGrid has MouseDoubleClick wired to DirectoryDataGrid_MouseDoubleClick code-behind handler"
+ - "While loading, the status text shows DirectoryLoadStatus and Load button is disabled"
+ - "A hint text tells users to double-click to add a user to the audit"
+ - "The SelectedUsers ItemsControl remains visible in both modes (users added from directory appear here)"
+ artifacts:
+ - path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
+ provides: "Complete directory browse UI with mode toggle, directory DataGrid, and loading UX"
+ contains: "DirectoryUsersView"
+ key_links:
+ - from: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
+ to: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
+ via: "data binding"
+ pattern: "IsBrowseMode|DirectoryUsersView|LoadDirectoryCommand|DirectoryFilterText|IncludeGuests"
+---
+
+
+Add the complete directory browse UI to UserAccessAuditView.xaml with mode toggle, directory DataGrid, loading indicators, and seamless integration with the existing audit workflow.
+
+Purpose: SC1-SC4 require visible UI for mode switching, directory display, loading progress, and cancellation. This plan wires all Phase 13 ViewModel properties to the View layer.
+
+Output: Updated UserAccessAuditView.xaml with full directory browse mode.
+
+
+
+@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
+@C:/Users/dev/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/phases/14-user-directory-view/14-RESEARCH.md
+
+
+
+From SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs:
+```csharp
+// Mode toggle
+public bool IsBrowseMode { get; set; }
+
+// Directory data
+public ObservableCollection DirectoryUsers { get; }
+public ICollectionView DirectoryUsersView { get; } // filtered + sorted
+public int DirectoryUserCount { get; } // computed filtered count
+
+// Directory commands
+public IAsyncRelayCommand LoadDirectoryCommand { get; }
+public RelayCommand CancelDirectoryLoadCommand { get; }
+public RelayCommand SelectDirectoryUserCommand { get; }
+
+// Directory state
+public bool IsLoadingDirectory { get; }
+public string DirectoryLoadStatus { get; }
+public bool IncludeGuests { get; set; }
+public string DirectoryFilterText { get; set; }
+
+// Existing (still visible in both modes)
+public ObservableCollection SelectedUsers { get; }
+public string SelectedUsersLabel { get; }
+public IAsyncRelayCommand RunCommand { get; }
+public RelayCommand CancelCommand { get; }
+public IAsyncRelayCommand ExportCsvCommand { get; }
+public IAsyncRelayCommand ExportHtmlCommand { get; }
+```
+
+
+- `{StaticResource BoolToVisibilityConverter}` — true→Visible, false→Collapsed
+- `{StaticResource InverseBoolConverter}` — inverts bool
+- `{StaticResource StringToVisibilityConverter}` — non-empty→Visible
+
+
+```
+DockPanel (290px, Margin 8)
+├── GroupBox "Select Users" (DockPanel.Dock="Top") — SEARCH MODE (hide when IsBrowseMode)
+│ └── SearchQuery, SearchResults, SelectedUsers, SelectedUsersLabel
+├── GroupBox "Scan Options" (DockPanel.Dock="Top") — ALWAYS VISIBLE
+│ └── CheckBoxes
+└── StackPanel (DockPanel.Dock="Top") — ALWAYS VISIBLE
+ └── Run/Cancel/Export buttons
+```
+
+
+```csharp
+private void DirectoryDataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
+{
+ if (sender is DataGrid grid && grid.SelectedItem is GraphDirectoryUser user)
+ {
+ var vm = (UserAccessAuditViewModel)DataContext;
+ if (vm.SelectDirectoryUserCommand.CanExecute(user))
+ vm.SelectDirectoryUserCommand.Execute(user);
+ }
+}
+```
+
+
+
+
+
+
+ Task 1: Restructure left panel with mode toggle and conditional panels
+
+ SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml
+
+
+ - At the top of the left panel DockPanel, a mode toggle section appears with two RadioButtons
+ - RadioButton "Search" is checked when IsBrowseMode=false (uses InverseBoolConverter)
+ - RadioButton "Browse Directory" is checked when IsBrowseMode=true
+ - Below the toggle: existing Search GroupBox (visible when IsBrowseMode=false) OR new Directory GroupBox (visible when IsBrowseMode=true)
+ - SelectedUsers ItemsControl + label extracted from Search GroupBox and placed in a shared section visible in BOTH modes
+ - Scan Options GroupBox and buttons remain always visible
+ - Directory GroupBox contains:
+ a) Two-button grid: Load Directory + Cancel (like Run/Cancel pattern)
+ b) CheckBox for IncludeGuests
+ c) Filter TextBox bound to DirectoryFilterText
+ d) Status/count row: DirectoryLoadStatus + DirectoryUserCount
+ e) DataGrid bound to DirectoryUsersView with 5 columns (Name, Email, Department, Job Title, Type)
+ f) Hint text: "Double-click a user to add to audit"
+ - DataGrid has MouseDoubleClick="DirectoryDataGrid_MouseDoubleClick"
+ - DataGrid uses AutoGenerateColumns="False", IsReadOnly="True", virtualization enabled
+ - DataGrid columns are DataGridTextColumn (simple text, sortable by default)
+ - Guest users highlighted with a subtle "Guest" badge in the Type column (orange, like the existing UserAccessAuditView pattern)
+
+
+ 1. Read the current `UserAccessAuditView.xaml` to get the exact current content.
+
+ 2. Replace the left panel DockPanel content with the new structure:
+
+ ```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [existing DataTrigger style]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [existing ItemTemplate with blue border badges + x remove button]
+
+
+
+
+
+
+ [existing checkboxes]
+
+
+
+
+ [existing button grids]
+
+ ```
+
+ IMPORTANT NOTES:
+ - The `BoolToVisibilityConverter` natively shows when true. For the Search panel (show when IsBrowseMode=false), we need inverse behavior. Two approaches:
+ a) Use a DataTrigger-based Style on Visibility (reliable)
+ b) Check if BoolToVisibilityConverter supports a ConverterParameter for inversion
+ Since we're not sure the converter supports inversion, use DataTrigger approach for the Search panel:
+ ```xml
+
+
+
+ ```
+ And for the Browse panel, use `BoolToVisibilityConverter` directly (shows when IsBrowseMode=true).
+
+ - The SelectedUsers ItemsControl must be EXTRACTED from the Search GroupBox and placed in a standalone section — it needs to remain visible when in Browse mode so users can see who they've selected from the directory.
+
+ - DataGrid column headers use localized bindings. Note: DataGridTextColumn.Header does NOT support binding in standard WPF — it's not a FrameworkElement. Instead, use DataGridTemplateColumn with HeaderTemplate for localized headers, OR set the Header as a plain string and skip localization for column headers (simpler approach). DECISION: Use plain English headers for DataGrid columns (they are technical column names that don't benefit from localization as much). This avoids the complex HeaderTemplate pattern. Use the localization keys in other UI elements.
+
+ Alternative if Header binding works (some WPF versions support it via x:Static): Test with `Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.name]}"` — if it compiles and works, great. If not, fall back to plain strings.
+
+
+ dotnet build --no-restore -warnaserror
+
+ UserAccessAuditView has full directory browse UI with mode toggle, conditional panels, directory DataGrid, loading status, and double-click selection. Build passes.
+
+
+
+
+
+```bash
+dotnet build --no-restore -warnaserror
+```
+Build must pass. Visual verification requires manual testing.
+
+
+
+- SC1: Mode toggle (RadioButtons) visibly switches left panel between search and browse
+- SC2: DataGrid double-click adds user to SelectedUsers; Run Audit button works as usual
+- SC3: Loading status shows DirectoryLoadStatus, Load button disabled while loading, Cancel button active
+- SC4: Cancel clears loading state; status returns to ready; no broken UI
+- SelectedUsers visible in both modes
+- DataGrid columns: Name, Email, Department, Job Title, Type (Guest highlighted in orange)
+- Filter TextBox and IncludeGuests checkbox functional
+- Build passes with zero warnings
+
+
+
diff --git a/.planning/phases/14-user-directory-view/14-RESEARCH.md b/.planning/phases/14-user-directory-view/14-RESEARCH.md
new file mode 100644
index 0000000..87327ff
--- /dev/null
+++ b/.planning/phases/14-user-directory-view/14-RESEARCH.md
@@ -0,0 +1,47 @@
+# Phase 14 Research: User Directory View
+
+## What Exists (Phase 13 Deliverables)
+
+### ViewModel Properties for Directory Browse
+- `IsBrowseMode` (bool) — toggles Search/Browse mode
+- `DirectoryUsers` (ObservableCollection) — raw directory list
+- `DirectoryUsersView` (ICollectionView) — filtered/sorted view, default sort DisplayName asc
+- `IsLoadingDirectory` (bool) — true while loading
+- `DirectoryLoadStatus` (string) — "Loading... X users" progress text
+- `IncludeGuests` (bool) — in-memory member/guest filter
+- `DirectoryFilterText` (string) — text filter on DisplayName, UPN, Department, JobTitle
+- `DirectoryUserCount` (int) — filtered count
+- `LoadDirectoryCommand` (IAsyncRelayCommand) — disabled while loading
+- `CancelDirectoryLoadCommand` (RelayCommand) — enabled only while loading
+
+### Existing People-Picker (Search Mode)
+- `SearchQuery` → debounced Graph search → `SearchResults` dropdown
+- `AddUserCommand(GraphUserResult)` → `SelectedUsers` collection
+- `RemoveUserCommand(GraphUserResult)` → removes from SelectedUsers
+- `RunCommand` → `RunOperationAsync` → audits SelectedUsers against GlobalSites
+
+### GAP: No SelectDirectoryUserCommand
+SC2 requires "selecting a user from directory list launches existing audit pipeline."
+Need a command that:
+1. Takes a `GraphDirectoryUser` from the directory DataGrid
+2. Converts it to `GraphUserResult` (same DisplayName + UPN)
+3. Adds to `SelectedUsers` via existing `ExecuteAddUser` logic
+This is ViewModel work — needs to be done before the View XAML.
+
+### Current View Structure (UserAccessAuditView.xaml)
+- Left panel (290px DockPanel): Users GroupBox + Options GroupBox + Buttons StackPanel
+- Right panel: Summary banners + Filter/Toggle row + DataGrid (ResultsView)
+- Status bar: ProgressBar + StatusMessage
+
+### Available Converters
+- `BoolToVisibilityConverter` — true→Visible, false→Collapsed
+- `InverseBoolConverter` — inverts bool
+- `StringToVisibilityConverter` — non-empty→Visible, empty→Collapsed
+
+### Localization
+- No directory.* keys exist — need to add ~10 keys for EN + FR
+
+## Plan Breakdown
+
+1. **14-01** (Wave 1): Add localization keys + `SelectDirectoryUserCommand` on ViewModel + code-behind event handler
+2. **14-02** (Wave 2): Full XAML changes — mode toggle, conditional Search/Browse panels, directory DataGrid, loading UX
diff --git a/.planning/phases/14-user-directory-view/14-VERIFICATION.md b/.planning/phases/14-user-directory-view/14-VERIFICATION.md
new file mode 100644
index 0000000..9cbc378
--- /dev/null
+++ b/.planning/phases/14-user-directory-view/14-VERIFICATION.md
@@ -0,0 +1,98 @@
+---
+phase: 14-user-directory-view
+verified: 2026-04-09T12:00:00Z
+status: passed
+score: 4/4 success criteria verified
+gaps: []
+---
+
+# Phase 14: User Directory View Verification Report
+
+**Phase Goal:** Administrators can toggle into directory browse mode from the user access audit tab, see the paginated user list with filters, and launch an access audit for a selected user.
+**Verified:** 2026-04-09
+**Status:** passed
+**Re-verification:** No -- initial verification
+
+## Goal Achievement
+
+### Observable Truths (Success Criteria)
+
+| # | Truth | Status | Evidence |
+|---|-------|--------|----------|
+| 1 | The user access audit tab shows a mode toggle control that visibly switches the left panel between the existing people-picker and the directory browse panel | VERIFIED | XAML lines 19-25: two RadioButtons (Search/Browse Directory) bound to IsBrowseMode via InverseBoolConverter. Search GroupBox uses DataTrigger to collapse when IsBrowseMode=true (lines 32-40). Browse GroupBox uses BoolToVisibilityConverter on IsBrowseMode (line 87). Both converters exist in App.xaml. |
+| 2 | In browse mode, selecting a user from the directory list and clicking Run Audit launches the existing audit pipeline for that user | VERIFIED | SelectDirectoryUserCommand (ViewModel line 554-562) converts GraphDirectoryUser to GraphUserResult and adds to SelectedUsers. DataGrid has MouseDoubleClick="DirectoryDataGrid_MouseDoubleClick" (XAML line 141). Code-behind handler (line 29-37) invokes SelectDirectoryUserCommand. RunCommand operates on SelectedUsers (line 244-246). Tests 17-20 confirm the full flow. |
+| 3 | While the directory is loading, the panel shows a "Loading... X users" counter and an active cancel button; the load button is disabled to prevent concurrent requests | VERIFIED | LoadDirectoryAsync sets DirectoryLoadStatus="Loading..." then updates via Progress callback "Loading... {count} users" (ViewModel lines 411-415). LoadDirectoryCommand CanExecute = !IsLoadingDirectory (line 192). CancelDirectoryLoadCommand CanExecute = IsLoadingDirectory (line 194). OnIsLoadingDirectoryChanged notifies both commands (lines 378-382). XAML binds status text (line 118) and both buttons (lines 98-103). |
+| 4 | When the directory load is cancelled or fails, the panel returns to a ready state with a clear status message and no broken UI | VERIFIED | Cancellation sets DirectoryLoadStatus="Load cancelled." (line 436). Failure sets "Failed: {message}" (line 440). Both paths set IsLoadingDirectory=false in finally block (line 445). Test 7 confirms cancellation flow. Tenant switch resets all directory state (lines 321-331, test 13). |
+
+**Score:** 4/4 success criteria verified
+
+### Required Artifacts
+
+| Artifact | Expected | Status | Details |
+|----------|----------|--------|---------|
+| `SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml` | Directory browse UI with mode toggle, DataGrid, loading UX | VERIFIED | 415 lines, complete implementation with mode toggle, search panel, browse panel, shared SelectedUsers, scan options, run/export buttons |
+| `SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs` | Code-behind with DirectoryDataGrid_MouseDoubleClick | VERIFIED | Handler at line 29, extracts GraphDirectoryUser, invokes SelectDirectoryUserCommand |
+| `SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs` | SelectDirectoryUserCommand, LoadDirectoryCommand, browse mode state | VERIFIED | 661 lines, all properties/commands present, full implementation (no stubs) |
+| `SharepointToolbox/Localization/Strings.resx` | 14 directory localization keys (EN) | VERIFIED | All 14 keys present (audit.mode.search/browse, directory.grp/btn/chk/col/hint/status/filter) |
+| `SharepointToolbox/Localization/Strings.fr.resx` | 14 directory localization keys (FR) | VERIFIED | All 14 keys present with French translations |
+| `SharepointToolbox/Core/Models/GraphDirectoryUser.cs` | Record with DisplayName, UPN, Mail, Department, JobTitle, UserType | VERIFIED | 6-field record, matches DataGrid column bindings |
+| `SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs` | Tests for directory commands and state | VERIFIED | 20 tests, all passing |
+
+### Key Link Verification
+
+| From | To | Via | Status | Details |
+|------|----|-----|--------|---------|
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: IsBrowseMode | WIRED | RadioButton IsChecked bindings (lines 21, 23), GroupBox visibility (lines 33-39, 87) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: DirectoryUsersView | WIRED | DataGrid ItemsSource="{Binding DirectoryUsersView}" (line 138) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: LoadDirectoryCommand | WIRED | Button Command="{Binding LoadDirectoryCommand}" (line 100) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: CancelDirectoryLoadCommand | WIRED | Button Command="{Binding CancelDirectoryLoadCommand}" (line 102) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: DirectoryFilterText | WIRED | TextBox Text="{Binding DirectoryFilterText}" (line 113) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: IncludeGuests | WIRED | CheckBox IsChecked="{Binding IncludeGuests}" (line 109) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: DirectoryLoadStatus | WIRED | TextBlock Text="{Binding DirectoryLoadStatus}" (line 118) |
+| UserAccessAuditView.xaml | UserAccessAuditViewModel.cs | Data binding: DirectoryUserCount | WIRED | MultiBinding with DirectoryUserCount (line 122) |
+| UserAccessAuditView.xaml | UserAccessAuditView.xaml.cs | MouseDoubleClick event | WIRED | MouseDoubleClick="DirectoryDataGrid_MouseDoubleClick" (line 141) |
+| UserAccessAuditView.xaml.cs | UserAccessAuditViewModel.cs | SelectDirectoryUserCommand | WIRED | Code-behind casts DataContext, invokes command (lines 31-36) |
+| UserAccessAuditViewModel.cs | GraphDirectoryUser.cs | Command parameter type | WIRED | RelayCommand (line 140), ExecuteSelectDirectoryUser parameter (line 554) |
+
+### Requirements Coverage
+
+| Requirement | Source Plan | Description | Status | Evidence |
+|-------------|------------|-------------|--------|----------|
+| UDIR-01 | 14-01, 14-02 | User can toggle between search mode and directory browse mode | SATISFIED | RadioButtons in XAML, IsBrowseMode property, conditional panel visibility |
+| UDIR-05 | 14-01, 14-02 | User can select users from directory to run audit | SATISFIED | SelectDirectoryUserCommand, DataGrid double-click, SelectedUsers shared panel |
+
+### Anti-Patterns Found
+
+No anti-patterns detected. No TODO/FIXME/HACK/PLACEHOLDER comments. No empty implementations. No console.log-only handlers.
+
+### Build and Test Results
+
+- **Build:** dotnet build --no-restore -warnaserror: 0 warnings, 0 errors
+- **Tests:** 20 passed, 0 failed, 0 skipped (160ms)
+
+### Human Verification Required
+
+### 1. Mode Toggle Visual Behavior
+**Test:** Click Browse Directory radio button, verify search panel collapses and directory panel appears. Click Search radio button, verify the reverse.
+**Expected:** Clean toggle with no layout jump or overlap. Both panels fully visible/collapsed.
+**Why human:** Visual layout and transition smoothness cannot be verified programmatically.
+
+### 2. Directory Load and Cancel UX
+**Test:** Click Load Directory, observe loading status updating with user count, then click Cancel before completion.
+**Expected:** Status shows "Loading... N users" incrementally, Cancel button is active during load, Load button is disabled. After cancel: "Load cancelled." message, both buttons return to normal state.
+**Why human:** Real-time progress display and button enable/disable transitions require visual observation.
+
+### 3. DataGrid Double-Click to Audit Flow
+**Test:** Load directory, double-click a user row. Verify user appears in SelectedUsers badges. Click Run Audit.
+**Expected:** User badge appears immediately. Audit runs and produces results identical to search-mode selection.
+**Why human:** End-to-end flow through actual Graph API and audit pipeline requires running application.
+
+### 4. Guest Highlighting
+**Test:** Load directory with Include Guests checked. Find a Guest-type user in the list.
+**Expected:** Guest users show "Guest" in orange semi-bold text in the Type column.
+**Why human:** Color and font rendering verification.
+
+---
+
+_Verified: 2026-04-09_
+_Verifier: Claude (gsd-verifier)_
diff --git a/.planning/phases/15-consolidation-data-model/15-CONTEXT.md b/.planning/phases/15-consolidation-data-model/15-CONTEXT.md
new file mode 100644
index 0000000..7e213ea
--- /dev/null
+++ b/.planning/phases/15-consolidation-data-model/15-CONTEXT.md
@@ -0,0 +1,70 @@
+---
+phase: 15
+title: Consolidation Data Model
+status: ready-for-planning
+created: 2026-04-09
+---
+
+# Phase 15 Context: Consolidation Data Model
+
+## Decided Areas (from prior research + STATE.md)
+
+These are locked — do not re-litigate during planning or execution.
+
+| Decision | Value |
+|---|---|
+| Consolidation scope | User access audit report only — site-centric permission report is unchanged |
+| Source model | `UserAccessEntry` (already normalized, one user per row) |
+| Consolidation is opt-in | Defaults to OFF; toggle wired in Phase 16 |
+| No API calls | Pure data transformation — no Graph or CSOM calls |
+| Existing exports unchanged | When consolidation is not applied, output is identical to pre-v2.3 |
+
+## Discussed Areas
+
+### 1. Consolidation Key (What Defines "Same Access")
+
+**Decision:** Merge rows only when all four fields match: `UserLogin` + `PermissionLevel` + `AccessType` + `GrantedThrough`.
+
+- Strictest matching — preserves the audit trail of how access was granted
+- A user with "Contribute (Direct)" on 3 sites and "Contribute (Group: Members)" on 2 sites produces 2 consolidated rows, not 1
+- `UserLogin` is the identity key (not `UserDisplayName`, which could vary)
+- `AccessType` enum values: Direct, Group, Inherited — all treated as distinct
+- `GrantedThrough` string comparison is exact (e.g., "SharePoint Group: Members" vs "SharePoint Group: Owners" are separate)
+
+### 2. Merged Locations Model
+
+**Decision:** `List` with a `LocationCount` convenience property.
+
+- `ConsolidatedPermissionEntry` holds all fields from the consolidation key plus a `List` containing each merged site's URL and title
+- `LocationInfo` is a lightweight record: `{ string SiteUrl, string SiteTitle, string ObjectTitle, string ObjectUrl, string ObjectType }`
+- `LocationCount` is a computed property (`Locations.Count`) — convenience for display and sorting
+- No information loss — all original location data is preserved in the list
+- Presentation decisions (how to render the list) are deferred to Phase 16
+
+### 3. Report Scope
+
+**Decision:** Consolidation applies to user access audit (`UserAccessEntry`) only.
+
+- The user access audit report is already user-centric and normalized (one user per row) — natural fit for "merge same user across locations"
+- The site-centric permission report (`PermissionEntry`) flows the opposite direction (site → users); consolidating it would mean "same permission set across sites" — a different feature entirely
+- `HtmlExportService` (site-centric) is untouched by this phase
+- `UserAccessHtmlExportService` will receive consolidated data in Phase 16; this phase only builds the model and service
+
+## Deferred Ideas (out of scope for Phase 15)
+
+- Consolidation toggle UI (Phase 16)
+- Consolidated view rendering in HTML exports (Phase 16)
+- Group expansion within consolidated rows (Phase 17)
+- Consolidation in CSV exports (out of scope per REQUIREMENTS.md)
+- "Same permission set across sites" consolidation for site-centric report (not planned)
+
+## code_context
+
+| Asset | Path | Reuse |
+|---|---|---|
+| UserAccessEntry model | `SharepointToolbox/Core/Models/UserAccessEntry.cs` | Source model — consolidated entry mirrors its fields + locations list |
+| UserAccessAuditService | `SharepointToolbox/Services/UserAccessAuditService.cs` | Produces the `UserAccessEntry` list that feeds the consolidator |
+| UserAccessHtmlExportService | `SharepointToolbox/Services/Export/UserAccessHtmlExportService.cs` | Downstream consumer in Phase 16 — must accept both flat and consolidated lists |
+| DuplicatesService grouping pattern | `SharepointToolbox/Services/DuplicatesService.cs` | Reference for composite-key grouping via `MakeKey()` pattern |
+| PermissionSummaryBuilder | `SharepointToolbox/Core/Helpers/PermissionSummaryBuilder.cs` | Reference for aggregation pattern over permission data |
+| Test project | `SharepointToolbox.Tests/` | New tests for PermissionConsolidator with known input/output pairs |
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/.msCoverageSourceRootsMapping_SharepointToolbox.Tests b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/.msCoverageSourceRootsMapping_SharepointToolbox.Tests
index 4838e7d..ef30594 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/.msCoverageSourceRootsMapping_SharepointToolbox.Tests and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/.msCoverageSourceRootsMapping_SharepointToolbox.Tests differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/CoverletSourceRootsMapping_SharepointToolbox.Tests b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/CoverletSourceRootsMapping_SharepointToolbox.Tests
index 4838e7d..ef30594 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/CoverletSourceRootsMapping_SharepointToolbox.Tests and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/CoverletSourceRootsMapping_SharepointToolbox.Tests differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.dll b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.dll
index 88e8f7e..ea99560 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.dll and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.dll differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.pdb b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.pdb
index 66abcc5..d808d96 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.pdb and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.Tests.pdb differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.dll b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.dll
index ed70295..232e1ed 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.dll and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.dll differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.exe b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.exe
index db78df3..7523997 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.exe and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.exe differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.pdb b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.pdb
index 83e2746..98971c7 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.pdb and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/SharepointToolbox.pdb differ
diff --git a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll
index ba0c126..abc98fc 100644
Binary files a/SharepointToolbox.Tests/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll and b/SharepointToolbox.Tests/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll differ
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfo.cs b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfo.cs
index c7acb2b..453bd50 100644
--- a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfo.cs
+++ b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfo.cs
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("SharepointToolbox.Tests")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+7c7d87d86b7dbd94b1c5591ea880fa33a1ee0827")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b9511bd2b07dc1b8f7a457602899a3644db4f099")]
[assembly: System.Reflection.AssemblyProductAttribute("SharepointToolbox.Tests")]
[assembly: System.Reflection.AssemblyTitleAttribute("SharepointToolbox.Tests")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfoInputs.cache b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfoInputs.cache
index 8c68299..ecf92e5 100644
--- a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfoInputs.cache
+++ b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.AssemblyInfoInputs.cache
@@ -1 +1 @@
-f9df09480b479069e5e6ae5f78b859fa720a12b4459d28036dfb96df77d53bef
+a6a103bebe57a485c13eef1c486d11ae19b7d31a857b43f59666705dc94a6cdb
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.GeneratedMSBuildEditorConfig.editorconfig b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.GeneratedMSBuildEditorConfig.editorconfig
index 05ad3a1..d72bd7f 100644
--- a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.GeneratedMSBuildEditorConfig.editorconfig
+++ b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.GeneratedMSBuildEditorConfig.editorconfig
@@ -15,7 +15,7 @@ build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = SharepointToolbox.Tests
-build_property.ProjectDir = c:\Users\dev\Documents\projets\Sharepoint\SharepointToolbox.Tests\
+build_property.ProjectDir = C:\Users\dev\Documents\projets\Sharepoint\SharepointToolbox.Tests\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.assets.cache b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.assets.cache
index 6d3ca11..3a8eb27 100644
Binary files a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.assets.cache and b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.assets.cache differ
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.csproj.AssemblyReference.cache b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.csproj.AssemblyReference.cache
index 081d1f9..1969c5c 100644
Binary files a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.csproj.AssemblyReference.cache and b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.csproj.AssemblyReference.cache differ
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.dll b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.dll
index 88e8f7e..ea99560 100644
Binary files a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.dll and b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.dll differ
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.genruntimeconfig.cache b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.genruntimeconfig.cache
index 78b3d6b..6f0207e 100644
--- a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.genruntimeconfig.cache
+++ b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.genruntimeconfig.cache
@@ -1 +1 @@
-a590f1603da7d8620e6edc276235fbd796db819f8f128515c72d60c0add97067
+17b6b482b078d0ca357cbc341151e0b1e20afe20c4b7bd849f6e0f34b62c2c26
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.pdb b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.pdb
index 66abcc5..d808d96 100644
Binary files a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.pdb and b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/SharepointToolbox.Tests.pdb differ
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/ref/SharepointToolbox.Tests.dll b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/ref/SharepointToolbox.Tests.dll
index c5b4ffe..b94bc20 100644
Binary files a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/ref/SharepointToolbox.Tests.dll and b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/ref/SharepointToolbox.Tests.dll differ
diff --git a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/refint/SharepointToolbox.Tests.dll b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/refint/SharepointToolbox.Tests.dll
index c5b4ffe..b94bc20 100644
Binary files a/SharepointToolbox.Tests/obj/Debug/net10.0-windows/refint/SharepointToolbox.Tests.dll and b/SharepointToolbox.Tests/obj/Debug/net10.0-windows/refint/SharepointToolbox.Tests.dll differ
diff --git a/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.dll b/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.dll
index 06ae29a..232e1ed 100644
Binary files a/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.dll and b/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.dll differ
diff --git a/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.exe b/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.exe
index db78df3..7523997 100644
Binary files a/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.exe and b/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.exe differ
diff --git a/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.pdb b/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.pdb
index 93da097..98971c7 100644
Binary files a/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.pdb and b/SharepointToolbox/bin/Debug/net10.0-windows/SharepointToolbox.pdb differ
diff --git a/SharepointToolbox/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll b/SharepointToolbox/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll
index ba0c126..abc98fc 100644
Binary files a/SharepointToolbox/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll and b/SharepointToolbox/bin/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfo.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfo.cs
index 8ac9094..d4f45b3 100644
--- a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfo.cs
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfo.cs
@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("SharepointToolbox")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+7c7d87d86b7dbd94b1c5591ea880fa33a1ee0827")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b9511bd2b07dc1b8f7a457602899a3644db4f099")]
[assembly: System.Reflection.AssemblyProductAttribute("SharepointToolbox")]
[assembly: System.Reflection.AssemblyTitleAttribute("SharepointToolbox")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfoInputs.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfoInputs.cache
index f52d983..1ae54ed 100644
--- a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfoInputs.cache
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.AssemblyInfoInputs.cache
@@ -1 +1 @@
-72f994ecac20797c56b9c39d5917ad31c134243b0218fc33af11e5587a50ed39
+92fb59486a9b4569136d423b674d1545abfe4aa70a8cb949969aac2f7c58c28c
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.fr.resources b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.fr.resources
index 7458b90..1663a43 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.fr.resources and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.fr.resources differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.resources b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.resources
index 6105ead..1b0d8ee 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.resources and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.Localization.Strings.resources differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.csproj.GenerateResource.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.csproj.GenerateResource.cache
index 45200e7..f9eda1e 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.csproj.GenerateResource.cache and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.csproj.GenerateResource.cache differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.dll b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.dll
index 06ae29a..232e1ed 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.dll and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.dll differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.g.resources b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.g.resources
index 50eaa8c..fc662eb 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.g.resources and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.g.resources differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.pdb b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.pdb
index 93da097..98971c7 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.pdb and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox.pdb differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.AssemblyInfo.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.AssemblyInfo.cs
new file mode 100644
index 0000000..14109dc
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.AssemblyInfo.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("SharepointToolbox.Tests")]
+[assembly: System.Reflection.AssemblyCompanyAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+e6ba2d8146484ab85e4b74b4640282d051e462e4")]
+[assembly: System.Reflection.AssemblyProductAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyTitleAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
+[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.AssemblyInfoInputs.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..5a8f1b4
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+0659bc7ce6bd4add20a40ec175f2ae2d4690e16312ba96cfa266940f89d22e4e
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.GeneratedMSBuildEditorConfig.editorconfig b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..2617eac
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,23 @@
+is_global = true
+build_property.MvvmToolkitEnableINotifyPropertyChangingSupport = true
+build_property._MvvmToolkitIsUsingWindowsRuntimePack = false
+build_property.CsWinRTComponent =
+build_property.CsWinRTAotOptimizerEnabled =
+build_property.CsWinRTAotWarningLevel =
+build_property.TargetFramework = net10.0-windows
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v10.0
+build_property.TargetPlatformMinVersion = 7.0
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = SharepointToolbox
+build_property.ProjectDir = C:\Users\dev\Documents\projets\Sharepoint\SharepointToolbox\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.CsWinRTUseWindowsUIXamlProjections = false
+build_property.EffectiveAnalysisLevelStyle = 10.0
+build_property.EnableCodeStyleSeverity =
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.GlobalUsings.g.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.GlobalUsings.g.cs
new file mode 100644
index 0000000..e6eff56
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.GlobalUsings.g.cs
@@ -0,0 +1,6 @@
+//
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.Threading;
+global using System.Threading.Tasks;
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.assets.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.assets.cache
new file mode 100644
index 0000000..ef9415f
Binary files /dev/null and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_ctutf4es_wpftmp.assets.cache differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.AssemblyInfo.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.AssemblyInfo.cs
new file mode 100644
index 0000000..ba82aed
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.AssemblyInfo.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("SharepointToolbox.Tests")]
+[assembly: System.Reflection.AssemblyCompanyAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+381081da18180dea03b0e69a260c69461f68a718")]
+[assembly: System.Reflection.AssemblyProductAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyTitleAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
+[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.AssemblyInfoInputs.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..4bf3d63
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+3fef37f623bcf5d17978ebf53360e98891ba93a92cd81406e5ed1d76ce4c14b7
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.GeneratedMSBuildEditorConfig.editorconfig b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..2617eac
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,23 @@
+is_global = true
+build_property.MvvmToolkitEnableINotifyPropertyChangingSupport = true
+build_property._MvvmToolkitIsUsingWindowsRuntimePack = false
+build_property.CsWinRTComponent =
+build_property.CsWinRTAotOptimizerEnabled =
+build_property.CsWinRTAotWarningLevel =
+build_property.TargetFramework = net10.0-windows
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v10.0
+build_property.TargetPlatformMinVersion = 7.0
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = SharepointToolbox
+build_property.ProjectDir = C:\Users\dev\Documents\projets\Sharepoint\SharepointToolbox\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.CsWinRTUseWindowsUIXamlProjections = false
+build_property.EffectiveAnalysisLevelStyle = 10.0
+build_property.EnableCodeStyleSeverity =
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.GlobalUsings.g.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.GlobalUsings.g.cs
new file mode 100644
index 0000000..e6eff56
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.GlobalUsings.g.cs
@@ -0,0 +1,6 @@
+//
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.Threading;
+global using System.Threading.Tasks;
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.assets.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.assets.cache
new file mode 100644
index 0000000..8f18477
Binary files /dev/null and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_jw2tcxqq_wpftmp.assets.cache differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.AssemblyInfo.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.AssemblyInfo.cs
new file mode 100644
index 0000000..aa15684
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.AssemblyInfo.cs
@@ -0,0 +1,25 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("SharepointToolbox.Tests")]
+[assembly: System.Reflection.AssemblyCompanyAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f11bfefe52e33fe70c456cb05cac1252b33b077f")]
+[assembly: System.Reflection.AssemblyProductAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyTitleAttribute("SharepointToolbox")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
+[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.AssemblyInfoInputs.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..41053b6
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+97d17a1cb043b978e1963dc14bb6a53e41f858e995ba4228fecd59ae19eb3360
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.GeneratedMSBuildEditorConfig.editorconfig b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..1914618
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,23 @@
+is_global = true
+build_property.MvvmToolkitEnableINotifyPropertyChangingSupport = true
+build_property._MvvmToolkitIsUsingWindowsRuntimePack = false
+build_property.CsWinRTComponent =
+build_property.CsWinRTAotOptimizerEnabled =
+build_property.CsWinRTAotWarningLevel =
+build_property.TargetFramework = net10.0-windows
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v10.0
+build_property.TargetPlatformMinVersion = 7.0
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = SharepointToolbox
+build_property.ProjectDir = c:\Users\dev\Documents\projets\Sharepoint\SharepointToolbox\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.CsWinRTUseWindowsUIXamlProjections = false
+build_property.EffectiveAnalysisLevelStyle = 10.0
+build_property.EnableCodeStyleSeverity =
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.GlobalUsings.g.cs b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.GlobalUsings.g.cs
new file mode 100644
index 0000000..e6eff56
--- /dev/null
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.GlobalUsings.g.cs
@@ -0,0 +1,6 @@
+//
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.Threading;
+global using System.Threading.Tasks;
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.assets.cache b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.assets.cache
new file mode 100644
index 0000000..ccd0c84
Binary files /dev/null and b/SharepointToolbox/obj/Debug/net10.0-windows/SharepointToolbox_nltrokkt_wpftmp.assets.cache differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.baml b/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.baml
index 46fc662..5e60980 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.baml and b/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.baml differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.g.cs b/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.g.cs
index bcc4003..d3c82ee 100644
--- a/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.g.cs
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/Views/Tabs/UserAccessAuditView.g.cs
@@ -1,4 +1,4 @@
-#pragma checksum "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "CBA7E811798D1605D43A084B6989D797CB13323D"
+#pragma checksum "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "0F51C9F8F2735BDED33D928B9D536B5267CB02CA"
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
@@ -41,13 +41,21 @@ namespace SharepointToolbox.Views.Tabs {
public partial class UserAccessAuditView : System.Windows.Controls.UserControl, System.Windows.Markup.IComponentConnector {
- #line 34 "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml"
+ #line 56 "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.ListBox SearchResultsListBox;
#line default
#line hidden
+
+ #line 137 "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml"
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
+ internal System.Windows.Controls.DataGrid DirectoryDataGrid;
+
+ #line default
+ #line hidden
+
private bool _contentLoaded;
///
@@ -81,9 +89,18 @@ namespace SharepointToolbox.Views.Tabs {
case 1:
this.SearchResultsListBox = ((System.Windows.Controls.ListBox)(target));
- #line 37 "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml"
+ #line 59 "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml"
this.SearchResultsListBox.SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.SearchResultsListBox_SelectionChanged);
+ #line default
+ #line hidden
+ return;
+ case 2:
+ this.DirectoryDataGrid = ((System.Windows.Controls.DataGrid)(target));
+
+ #line 141 "..\..\..\..\..\Views\Tabs\UserAccessAuditView.xaml"
+ this.DirectoryDataGrid.MouseDoubleClick += new System.Windows.Input.MouseButtonEventHandler(this.DirectoryDataGrid_MouseDoubleClick);
+
#line default
#line hidden
return;
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/apphost.exe b/SharepointToolbox/obj/Debug/net10.0-windows/apphost.exe
index db78df3..7523997 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/apphost.exe and b/SharepointToolbox/obj/Debug/net10.0-windows/apphost.exe differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.cs b/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.cs
index f738a3f..a9e5462 100644
--- a/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.cs
+++ b/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.cs
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("SharepointToolbox")]
[assembly: System.Reflection.AssemblyCopyrightAttribute(" ")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+7c7d87d86b7dbd94b1c5591ea880fa33a1ee0827")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b9511bd2b07dc1b8f7a457602899a3644db4f099")]
[assembly: System.Reflection.AssemblyProductAttribute("SharepointToolbox")]
[assembly: System.Reflection.AssemblyTitleAttribute("SharepointToolbox")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll b/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll
index ba0c126..abc98fc 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll and b/SharepointToolbox/obj/Debug/net10.0-windows/fr/SharepointToolbox.resources.dll differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/ref/SharepointToolbox.dll b/SharepointToolbox/obj/Debug/net10.0-windows/ref/SharepointToolbox.dll
index f366a09..333da32 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/ref/SharepointToolbox.dll and b/SharepointToolbox/obj/Debug/net10.0-windows/ref/SharepointToolbox.dll differ
diff --git a/SharepointToolbox/obj/Debug/net10.0-windows/refint/SharepointToolbox.dll b/SharepointToolbox/obj/Debug/net10.0-windows/refint/SharepointToolbox.dll
index f366a09..333da32 100644
Binary files a/SharepointToolbox/obj/Debug/net10.0-windows/refint/SharepointToolbox.dll and b/SharepointToolbox/obj/Debug/net10.0-windows/refint/SharepointToolbox.dll differ