diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 90f6531..23edeff 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -75,7 +75,14 @@ Plans: 2. The report shows summary counts per permission level with color indicators distinguishing high, medium, and low access levels 3. A detail-level selector (simple / detailed) controls whether individual item-level rows are shown or collapsed into summary rows 4. Toggling modes and detail level does not require re-running the scan — it re-renders from the already-fetched data -**Plans**: TBD +**Plans:** 6 plans +Plans: +- [ ] 08-01-PLAN.md — RiskLevel enum, PermissionLevelMapping, SimplifiedPermissionEntry, PermissionSummary (Wave 1) +- [ ] 08-02-PLAN.md — PermissionsViewModel simplified mode, detail toggle, summary computation (Wave 2) +- [ ] 08-03-PLAN.md — PermissionsView XAML: toggles, summary panel, color-coded DataGrid (Wave 3) +- [ ] 08-04-PLAN.md — HTML + CSV export simplified overloads (Wave 3) +- [ ] 08-05-PLAN.md — Localization keys (EN/FR) + export command wiring (Wave 4) +- [ ] 08-06-PLAN.md — Unit tests: mapping, summary, ViewModel toggle behavior (Wave 5) ### Phase 9: Storage Visualization **Goal**: The Storage Metrics tab displays an interactive chart of space consumption by file type, togglable between pie/donut and bar chart views @@ -99,5 +106,5 @@ Plans: | 5. Distribution and Hardening | v1.0 | 3/3 | Complete | 2026-04-03 | | 6. Global Site Selection | 5/5 | Complete | 2026-04-07 | - | | 7. User Access Audit | 10/10 | Complete | 2026-04-07 | - | -| 8. Simplified Permissions | v1.1 | 0/? | Not started | - | +| 8. Simplified Permissions | v1.1 | 0/6 | Planned | - | | 9. Storage Visualization | v1.1 | 0/? | Not started | - | diff --git a/.planning/phases/08-simplified-permissions/08-01-PLAN.md b/.planning/phases/08-simplified-permissions/08-01-PLAN.md new file mode 100644 index 0000000..385b64b --- /dev/null +++ b/.planning/phases/08-simplified-permissions/08-01-PLAN.md @@ -0,0 +1,404 @@ +--- +phase: 08-simplified-permissions +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - SharepointToolbox/Core/Models/RiskLevel.cs + - SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs + - SharepointToolbox/Core/Models/PermissionSummary.cs + - SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs +autonomous: true +requirements: + - SIMP-01 + - SIMP-02 +must_haves: + truths: + - "RiskLevel enum distinguishes High, Medium, Low, and ReadOnly access tiers" + - "PermissionLevelMapping maps all standard SharePoint role names to plain-language labels and risk levels" + - "SimplifiedPermissionEntry wraps PermissionEntry with computed simplified labels and risk level without modifying the original record" + - "PermissionSummary groups permission entries by risk level with counts" + - "Unknown/custom role names fall back to the raw name with a Medium risk level" + artifacts: + - path: "SharepointToolbox/Core/Models/RiskLevel.cs" + provides: "Risk level classification enum" + contains: "enum RiskLevel" + - path: "SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs" + provides: "Static mapping from SP role names to plain-language labels" + contains: "class PermissionLevelMapping" + - path: "SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs" + provides: "Presentation wrapper for PermissionEntry with simplified fields" + contains: "class SimplifiedPermissionEntry" + - path: "SharepointToolbox/Core/Models/PermissionSummary.cs" + provides: "Aggregation model for summary counts by risk level" + contains: "record PermissionSummary" + key_links: + - from: "SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs" + to: "SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs" + via: "Static method call to resolve labels and risk level" + pattern: "PermissionLevelMapping\\.Get" + - from: "SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs" + to: "SharepointToolbox/Core/Models/PermissionEntry.cs" + via: "Wraps original entry as Inner property" + pattern: "PermissionEntry Inner" +--- + + +Define the data models and mapping layer for simplified permissions: RiskLevel enum, PermissionLevelMapping helper, SimplifiedPermissionEntry wrapper, and PermissionSummary aggregation model. + +Purpose: All subsequent plans import these types. The mapping layer is the core of SIMP-01 (plain-language labels) and SIMP-02 (risk level color coding). PermissionEntry is immutable and NOT modified — SimplifiedPermissionEntry wraps it as a presentation concern. +Output: RiskLevel.cs, PermissionLevelMapping.cs, SimplifiedPermissionEntry.cs, PermissionSummary.cs + + + +@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/STATE.md + + + +From SharepointToolbox/Core/Models/PermissionEntry.cs: +```csharp +namespace SharepointToolbox.Core.Models; + +public record PermissionEntry( + string ObjectType, // "Site Collection" | "Site" | "List" | "Folder" + string Title, + string Url, + bool HasUniquePermissions, + string Users, // Semicolon-joined display names + string UserLogins, // Semicolon-joined login names + string PermissionLevels, // Semicolon-joined role names (Limited Access already removed) + string GrantedThrough, // "Direct Permissions" | "SharePoint Group: " + string PrincipalType // "SharePointGroup" | "User" | "External User" +); +``` + +From SharepointToolbox/Core/Helpers/PermissionEntryHelper.cs: +```csharp +public static class PermissionEntryHelper +{ + public static bool IsExternalUser(string loginName); + public static IReadOnlyList FilterPermissionLevels(IEnumerable levels); + public static bool IsSharingLinksGroup(string loginName); +} +``` + + + + + + + Task 1: Create RiskLevel enum and PermissionLevelMapping helper + SharepointToolbox/Core/Models/RiskLevel.cs, SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs + + Create `SharepointToolbox/Core/Models/RiskLevel.cs`: + + ```csharp + namespace SharepointToolbox.Core.Models; + + /// + /// Classifies a SharePoint permission level by its access risk. + /// Used for color coding in both WPF DataGrid and HTML export. + /// + public enum RiskLevel + { + /// Full Control, Site Collection Administrator — can delete site, manage permissions. + High, + /// Contribute, Edit, Design — can modify content. + Medium, + /// Read, Restricted View — can view but not modify. + Low, + /// View Only — most restricted legitimate access. + ReadOnly + } + ``` + + Create `SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs`: + + ```csharp + using SharepointToolbox.Core.Models; + + namespace SharepointToolbox.Core.Helpers; + + /// + /// Maps SharePoint built-in permission level names to human-readable labels and risk levels. + /// Used by SimplifiedPermissionEntry and export services to translate raw role names + /// into plain-language descriptions that non-technical users can understand. + /// + public static class PermissionLevelMapping + { + /// + /// Result of looking up a SharePoint role name. + /// + public record MappingResult(string Label, RiskLevel RiskLevel); + + /// + /// Known SharePoint built-in permission level mappings. + /// Keys are case-insensitive via the dictionary comparer. + /// + private static readonly Dictionary Mappings = new(StringComparer.OrdinalIgnoreCase) + { + // High risk — full administrative access + ["Full Control"] = new("Full control (can manage everything)", RiskLevel.High), + ["Site Collection Administrator"] = new("Site collection admin (full control)", RiskLevel.High), + + // Medium risk — can modify content + ["Contribute"] = new("Can edit files and list items", RiskLevel.Medium), + ["Edit"] = new("Can edit files, lists, and pages", RiskLevel.Medium), + ["Design"] = new("Can edit pages and use design tools", RiskLevel.Medium), + ["Approve"] = new("Can approve content and list items", RiskLevel.Medium), + ["Manage Hierarchy"] = new("Can create sites and manage pages", RiskLevel.Medium), + + // Low risk — read access + ["Read"] = new("Can view files and pages", RiskLevel.Low), + ["Restricted Read"] = new("Can view pages only (no download)", RiskLevel.Low), + + // Read-only — most restricted + ["View Only"] = new("Can view files in browser only", RiskLevel.ReadOnly), + ["Restricted View"] = new("Restricted view access", RiskLevel.ReadOnly), + }; + + /// + /// Gets the human-readable label and risk level for a SharePoint role name. + /// Returns the mapped result for known roles; for unknown/custom roles, + /// returns the raw name as-is with Medium risk level. + /// + public static MappingResult GetMapping(string roleName) + { + if (string.IsNullOrWhiteSpace(roleName)) + return new MappingResult(roleName, RiskLevel.Low); + + return Mappings.TryGetValue(roleName.Trim(), out var result) + ? result + : new MappingResult(roleName.Trim(), RiskLevel.Medium); + } + + /// + /// Resolves a semicolon-delimited PermissionLevels string into individual mapping results. + /// This handles the PermissionEntry.PermissionLevels format (e.g. "Full Control; Contribute"). + /// + public static IReadOnlyList GetMappings(string permissionLevels) + { + if (string.IsNullOrWhiteSpace(permissionLevels)) + return Array.Empty(); + + return permissionLevels + .Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(GetMapping) + .ToList(); + } + + /// + /// Returns the highest (most dangerous) risk level from a semicolon-delimited permission levels string. + /// Used for row-level color coding when an entry has multiple roles. + /// + public static RiskLevel GetHighestRisk(string permissionLevels) + { + var mappings = GetMappings(permissionLevels); + if (mappings.Count == 0) return RiskLevel.Low; + + // High < Medium < Low < ReadOnly in enum order — Min gives highest risk + return mappings.Min(m => m.RiskLevel); + } + + /// + /// Converts a semicolon-delimited PermissionLevels string into a simplified labels string. + /// E.g. "Full Control; Contribute" becomes "Full control (can manage everything); Can edit files and list items" + /// + public static string GetSimplifiedLabels(string permissionLevels) + { + var mappings = GetMappings(permissionLevels); + return string.Join("; ", mappings.Select(m => m.Label)); + } + } + ``` + + Design notes: + - Case-insensitive lookup handles variations in SharePoint role name casing + - Unknown/custom roles default to Medium (conservative — forces admin review) + - GetHighestRisk uses enum ordering (High=0 is most dangerous) for row-level color + - Semicolon-split methods handle the PermissionEntry.PermissionLevels format directly + + + cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5 + + RiskLevel.cs contains 4-value enum (High, Medium, Low, ReadOnly). PermissionLevelMapping.cs has GetMapping, GetMappings, GetHighestRisk, and GetSimplifiedLabels. All standard SP roles mapped. Unknown roles fallback to Medium. Project compiles. + + + + Task 2: Create SimplifiedPermissionEntry wrapper and PermissionSummary model + SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs, SharepointToolbox/Core/Models/PermissionSummary.cs + + Create `SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs`: + + ```csharp + using SharepointToolbox.Core.Helpers; + + namespace SharepointToolbox.Core.Models; + + /// + /// Presentation wrapper around PermissionEntry that adds simplified labels + /// and risk level classification without modifying the immutable source record. + /// Used as the DataGrid ItemsSource when simplified mode is active. + /// + public class SimplifiedPermissionEntry + { + /// The original immutable PermissionEntry. + public PermissionEntry Inner { get; } + + /// + /// Human-readable labels for the permission levels. + /// E.g. "Can edit files and list items" instead of "Contribute". + /// + public string SimplifiedLabels { get; } + + /// + /// The highest risk level across all permission levels on this entry. + /// Used for row-level color coding. + /// + public RiskLevel RiskLevel { get; } + + /// + /// Individual mapping results for each permission level in the entry. + /// Used when detailed breakdown per-role is needed. + /// + public IReadOnlyList Mappings { get; } + + // ── Passthrough properties for DataGrid binding ── + + public string ObjectType => Inner.ObjectType; + public string Title => Inner.Title; + public string Url => Inner.Url; + public bool HasUniquePermissions => Inner.HasUniquePermissions; + public string Users => Inner.Users; + public string UserLogins => Inner.UserLogins; + public string PermissionLevels => Inner.PermissionLevels; + public string GrantedThrough => Inner.GrantedThrough; + public string PrincipalType => Inner.PrincipalType; + + public SimplifiedPermissionEntry(PermissionEntry entry) + { + Inner = entry; + Mappings = PermissionLevelMapping.GetMappings(entry.PermissionLevels); + SimplifiedLabels = PermissionLevelMapping.GetSimplifiedLabels(entry.PermissionLevels); + RiskLevel = PermissionLevelMapping.GetHighestRisk(entry.PermissionLevels); + } + + /// + /// Creates SimplifiedPermissionEntry wrappers for a collection of entries. + /// + public static IReadOnlyList WrapAll( + IEnumerable entries) + { + return entries.Select(e => new SimplifiedPermissionEntry(e)).ToList(); + } + } + ``` + + Create `SharepointToolbox/Core/Models/PermissionSummary.cs`: + + ```csharp + namespace SharepointToolbox.Core.Models; + + /// + /// Summary counts of permission entries grouped by risk level. + /// Displayed in the summary panel when simplified mode is active. + /// + public record PermissionSummary( + /// Label for this group (e.g. "High Risk", "Read Only"). + string Label, + /// The risk level this group represents. + RiskLevel RiskLevel, + /// Number of permission entries at this risk level. + int Count, + /// Number of distinct users at this risk level. + int DistinctUsers + ); + + /// + /// Computes PermissionSummary groups from SimplifiedPermissionEntry collections. + /// + public static class PermissionSummaryBuilder + { + /// + /// Risk level display labels. + /// + private static readonly Dictionary Labels = new() + { + [RiskLevel.High] = "High Risk", + [RiskLevel.Medium] = "Medium Risk", + [RiskLevel.Low] = "Low Risk", + [RiskLevel.ReadOnly] = "Read Only", + }; + + /// + /// Builds summary counts grouped by risk level from a collection of simplified entries. + /// Always returns all 4 risk levels, even if count is 0, for consistent UI binding. + /// + public static IReadOnlyList Build( + IEnumerable entries) + { + var grouped = entries + .GroupBy(e => e.RiskLevel) + .ToDictionary(g => g.Key, g => g.ToList()); + + return Enum.GetValues() + .Select(level => + { + var items = grouped.GetValueOrDefault(level, new List()); + var distinctUsers = items + .SelectMany(e => e.UserLogins.Split(';', StringSplitOptions.RemoveEmptyEntries)) + .Select(u => u.Trim()) + .Where(u => u.Length > 0) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Count(); + + return new PermissionSummary( + Label: Labels[level], + RiskLevel: level, + Count: items.Count, + DistinctUsers: distinctUsers); + }) + .ToList(); + } + } + ``` + + Design notes: + - SimplifiedPermissionEntry is a class (not record) so it can have passthrough properties for DataGrid binding + - All original PermissionEntry fields are exposed as passthrough properties — DataGrid columns bind identically + - SimplifiedLabels and RiskLevel are computed once at construction — no per-render cost + - PermissionSummaryBuilder.Build always returns 4 entries (one per RiskLevel) for consistent summary panel layout + - DistinctUsers uses case-insensitive comparison for login deduplication + + + cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5 + + SimplifiedPermissionEntry wraps PermissionEntry with SimplifiedLabels, RiskLevel, Mappings, and all passthrough properties. PermissionSummary + PermissionSummaryBuilder provide grouped counts. Project compiles cleanly. PermissionEntry.cs is NOT modified. + + + + + +- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors +- RiskLevel.cs has High, Medium, Low, ReadOnly values +- PermissionLevelMapping has 11 known role mappings with labels and risk levels +- SimplifiedPermissionEntry wraps PermissionEntry (Inner property) without modifying it +- PermissionSummaryBuilder.Build returns 4 summary entries (one per risk level) +- No changes to PermissionEntry.cs + + + +All 4 files compile cleanly. The mapping and wrapper layer is complete: downstream plans (08-02 through 08-05) can import RiskLevel, PermissionLevelMapping, SimplifiedPermissionEntry, and PermissionSummary without ambiguity. PermissionEntry remains immutable and unmodified. + + + +After completion, create `.planning/phases/08-simplified-permissions/08-01-SUMMARY.md` + diff --git a/.planning/phases/08-simplified-permissions/08-02-PLAN.md b/.planning/phases/08-simplified-permissions/08-02-PLAN.md new file mode 100644 index 0000000..6372e22 --- /dev/null +++ b/.planning/phases/08-simplified-permissions/08-02-PLAN.md @@ -0,0 +1,265 @@ +--- +phase: 08-simplified-permissions +plan: 02 +type: execute +wave: 2 +depends_on: ["08-01"] +files_modified: + - SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs +autonomous: true +requirements: + - SIMP-01 + - SIMP-02 + - SIMP-03 +must_haves: + truths: + - "IsSimplifiedMode toggle switches between raw and simplified permission labels in the DataGrid" + - "IsDetailView toggle controls whether individual rows are shown or collapsed into summary rows" + - "Toggling modes does NOT re-run the scan — it re-renders from existing Results data" + - "Summary counts per risk level are available as observable properties when simplified mode is on" + - "SimplifiedResults collection is computed from Results whenever Results changes or mode toggles" + - "ActiveItemsSource provides the correct collection for DataGrid binding depending on current mode" + artifacts: + - path: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" + provides: "Extended PermissionsViewModel with simplified mode, detail toggle, and summary" + contains: "IsSimplifiedMode" + key_links: + - from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" + to: "SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs" + via: "SimplifiedPermissionEntry.WrapAll uses PermissionLevelMapping internally" + pattern: "SimplifiedPermissionEntry\\.WrapAll" + - from: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" + to: "SharepointToolbox/Core/Models/PermissionSummary.cs" + via: "PermissionSummaryBuilder.Build computes summary from simplified entries" + pattern: "PermissionSummaryBuilder\\.Build" +--- + + +Extend PermissionsViewModel with IsSimplifiedMode toggle, IsDetailView toggle, SimplifiedResults collection, summary statistics, and an ActiveItemsSource that the DataGrid binds to. All toggles re-render from cached data — no re-scan required. + +Purpose: This is the ViewModel logic for all three SIMP requirements. The View (08-03) binds to these new properties. +Output: Updated PermissionsViewModel.cs + + + +@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/08-simplified-permissions/08-01-SUMMARY.md + + + +From SharepointToolbox/Core/Models/RiskLevel.cs: +```csharp +public enum RiskLevel { High, Medium, Low, ReadOnly } +``` + +From SharepointToolbox/Core/Helpers/PermissionLevelMapping.cs: +```csharp +public static class PermissionLevelMapping +{ + public record MappingResult(string Label, RiskLevel RiskLevel); + public static MappingResult GetMapping(string roleName); + public static IReadOnlyList GetMappings(string permissionLevels); + public static RiskLevel GetHighestRisk(string permissionLevels); + public static string GetSimplifiedLabels(string permissionLevels); +} +``` + +From SharepointToolbox/Core/Models/SimplifiedPermissionEntry.cs: +```csharp +public class SimplifiedPermissionEntry +{ + public PermissionEntry Inner { get; } + public string SimplifiedLabels { get; } + public RiskLevel RiskLevel { get; } + public IReadOnlyList Mappings { get; } + // Passthrough: ObjectType, Title, Url, HasUniquePermissions, Users, UserLogins, + // PermissionLevels, GrantedThrough, PrincipalType + public static IReadOnlyList WrapAll(IEnumerable entries); +} +``` + +From SharepointToolbox/Core/Models/PermissionSummary.cs: +```csharp +public record PermissionSummary(string Label, RiskLevel RiskLevel, int Count, int DistinctUsers); + +public static class PermissionSummaryBuilder +{ + public static IReadOnlyList Build(IEnumerable entries); +} +``` + + +From SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs: +```csharp +public partial class PermissionsViewModel : FeatureViewModelBase +{ + // Existing fields and services — unchanged + [ObservableProperty] private ObservableCollection _results = new(); + + // Existing commands — unchanged + public IAsyncRelayCommand ExportCsvCommand { get; } + public IAsyncRelayCommand ExportHtmlCommand { get; } + public RelayCommand OpenSitePickerCommand { get; } + + // Full constructor and test constructor (internal) +} +``` + + + + + + + Task 1: Add simplified mode properties and summary computation to PermissionsViewModel + SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs + + Modify `SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs` to add simplified mode support. Add the following new using statements at the top: + + ```csharp + using SharepointToolbox.Core.Helpers; + ``` + + Add these new observable properties to the class (in the "Observable properties" section): + + ```csharp + /// + /// When true, displays simplified plain-language labels instead of raw SharePoint role names. + /// Toggling does not re-run the scan. + /// + [ObservableProperty] + private bool _isSimplifiedMode; + + /// + /// When true, shows individual item-level rows (detailed view). + /// When false, shows only summary rows grouped by risk level (simple view). + /// Only meaningful when IsSimplifiedMode is true. + /// + [ObservableProperty] + private bool _isDetailView = true; + ``` + + Add these computed collection properties (NOT ObservableProperty — manually raised): + + ```csharp + /// + /// Simplified wrappers computed from Results. Rebuilt when Results changes. + /// + private IReadOnlyList _simplifiedResults = Array.Empty(); + public IReadOnlyList SimplifiedResults + { + get => _simplifiedResults; + private set => SetProperty(ref _simplifiedResults, value); + } + + /// + /// Summary counts grouped by risk level. Rebuilt when SimplifiedResults changes. + /// + private IReadOnlyList _summaries = Array.Empty(); + public IReadOnlyList Summaries + { + get => _summaries; + private set => SetProperty(ref _summaries, value); + } + + /// + /// The collection the DataGrid actually binds to. Returns: + /// - Results (raw) when simplified mode is OFF + /// - SimplifiedResults when simplified mode is ON and detail view is ON + /// - (View handles summary display separately via Summaries property) + /// + public object ActiveItemsSource => IsSimplifiedMode + ? (object)SimplifiedResults + : Results; + ``` + + Add partial methods triggered by property changes: + + ```csharp + partial void OnIsSimplifiedModeChanged(bool value) + { + if (value && Results.Count > 0) + RebuildSimplifiedData(); + OnPropertyChanged(nameof(ActiveItemsSource)); + } + + partial void OnIsDetailViewChanged(bool value) + { + OnPropertyChanged(nameof(ActiveItemsSource)); + } + ``` + + Add a private method to rebuild simplified data from existing Results: + + ```csharp + /// + /// Recomputes SimplifiedResults and Summaries from the current Results collection. + /// Called when Results changes or when simplified mode is toggled on. + /// + private void RebuildSimplifiedData() + { + SimplifiedResults = SimplifiedPermissionEntry.WrapAll(Results); + Summaries = PermissionSummaryBuilder.Build(SimplifiedResults); + } + ``` + + Modify the existing `RunOperationAsync` method: after the line that sets `Results = new ObservableCollection(allEntries);` (both in the dispatcher branch and the else branch), add: + + ```csharp + if (IsSimplifiedMode) + RebuildSimplifiedData(); + OnPropertyChanged(nameof(ActiveItemsSource)); + ``` + + So the end of RunOperationAsync becomes (both branches): + ```csharp + Results = new ObservableCollection(allEntries); + if (IsSimplifiedMode) + RebuildSimplifiedData(); + OnPropertyChanged(nameof(ActiveItemsSource)); + ``` + + Modify `OnTenantSwitched` to also reset simplified state: + After `Results = new ObservableCollection();` add: + ```csharp + SimplifiedResults = Array.Empty(); + Summaries = Array.Empty(); + OnPropertyChanged(nameof(ActiveItemsSource)); + ``` + + Do NOT change: + - Constructor signatures (both full and test constructors remain unchanged) + - Existing properties (SiteUrl, IncludeInherited, ScanFolders, etc.) + - ExportCsvCommand and ExportHtmlCommand implementations (export updates are in plan 08-04) + - OpenSitePickerCommand + - _hasLocalSiteOverride / OnGlobalSitesChanged logic + + + cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5 + + PermissionsViewModel has IsSimplifiedMode, IsDetailView, SimplifiedResults, Summaries, and ActiveItemsSource properties. Toggling IsSimplifiedMode rebuilds simplified data from cached Results without re-scanning. Toggling IsDetailView triggers ActiveItemsSource change notification. Existing tests still compile (no constructor changes). + + + + + +- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors +- `dotnet test SharepointToolbox.Tests/ --filter PermissionsViewModelTests` passes (no constructor changes) +- PermissionsViewModel has IsSimplifiedMode, IsDetailView, SimplifiedResults, Summaries, ActiveItemsSource +- Toggling IsSimplifiedMode calls RebuildSimplifiedData + raises ActiveItemsSource changed +- RunOperationAsync calls RebuildSimplifiedData when IsSimplifiedMode is true +- OnTenantSwitched resets SimplifiedResults and Summaries + + + +The ViewModel is the orchestration layer for SIMP-01/02/03. All mode toggles re-render from cached data. The View (08-03) can bind to IsSimplifiedMode, IsDetailView, ActiveItemsSource, and Summaries. Export services (08-04) can access SimplifiedResults and IsSimplifiedMode. + + + +After completion, create `.planning/phases/08-simplified-permissions/08-02-SUMMARY.md` + diff --git a/.planning/phases/08-simplified-permissions/08-03-PLAN.md b/.planning/phases/08-simplified-permissions/08-03-PLAN.md new file mode 100644 index 0000000..e0f7f87 --- /dev/null +++ b/.planning/phases/08-simplified-permissions/08-03-PLAN.md @@ -0,0 +1,464 @@ +--- +phase: 08-simplified-permissions +plan: 03 +type: execute +wave: 3 +depends_on: ["08-02"] +files_modified: + - SharepointToolbox/Views/Tabs/PermissionsView.xaml +autonomous: true +requirements: + - SIMP-01 + - SIMP-02 + - SIMP-03 +must_haves: + truths: + - "A Simplified Mode toggle checkbox appears in the left panel scan options" + - "A Detail Level selector (Simple/Detailed) appears when simplified mode is on" + - "When simplified mode is on, the Permission Levels column shows plain-language labels instead of raw role names" + - "Permission level cells are color-coded by risk level (red=High, orange=Medium, green=Low, blue=ReadOnly)" + - "A summary panel shows counts per risk level with color indicators above the DataGrid" + - "When detail level is Simple, the DataGrid is hidden and only the summary panel is visible" + - "When detail level is Detailed, both summary panel and DataGrid rows are visible" + artifacts: + - path: "SharepointToolbox/Views/Tabs/PermissionsView.xaml" + provides: "Updated permissions view with toggles, color coding, and summary panel" + contains: "IsSimplifiedMode" + key_links: + - from: "SharepointToolbox/Views/Tabs/PermissionsView.xaml" + to: "SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs" + via: "DataBinding to IsSimplifiedMode, IsDetailView, ActiveItemsSource, Summaries" + pattern: "Binding IsSimplifiedMode" +--- + + +Update PermissionsView.xaml to add the simplified mode toggle, detail level selector, color-coded permission cells, and summary panel with risk level counts. + +Purpose: This is the visual layer for SIMP-01 (plain labels), SIMP-02 (color-coded summary), and SIMP-03 (detail level toggle). Binds to ViewModel properties created in 08-02. +Output: Updated PermissionsView.xaml + + + +@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/08-simplified-permissions/08-01-SUMMARY.md +@.planning/phases/08-simplified-permissions/08-02-SUMMARY.md + + + +From PermissionsViewModel (updated): +```csharp +// New toggle properties +[ObservableProperty] private bool _isSimplifiedMode; +[ObservableProperty] private bool _isDetailView = true; + +// Computed collections +public IReadOnlyList SimplifiedResults { get; } +public IReadOnlyList Summaries { get; } +public object ActiveItemsSource { get; } // Switches between Results and SimplifiedResults + +// Existing (unchanged) +public ObservableCollection Results { get; } +``` + +From SimplifiedPermissionEntry: +```csharp +public string ObjectType { get; } +public string Title { get; } +public string Url { get; } +public bool HasUniquePermissions { get; } +public string Users { get; } +public string PermissionLevels { get; } // Raw role names +public string SimplifiedLabels { get; } // Plain-language labels +public RiskLevel RiskLevel { get; } // High/Medium/Low/ReadOnly +public string GrantedThrough { get; } +public string PrincipalType { get; } +``` + +From PermissionSummary: +```csharp +public record PermissionSummary(string Label, RiskLevel RiskLevel, int Count, int DistinctUsers); +``` + +From RiskLevel: +```csharp +public enum RiskLevel { High, Medium, Low, ReadOnly } +``` + + + + + + + Task 1: Add toggles, summary panel, and color-coded DataGrid to PermissionsView.xaml + SharepointToolbox/Views/Tabs/PermissionsView.xaml + + Replace the entire content of `SharepointToolbox/Views/Tabs/PermissionsView.xaml` with the updated XAML below. Key changes from the original: + + 1. Added `xmlns:models` namespace for RiskLevel enum reference in DataTriggers + 2. Added "Display Options" GroupBox in left panel with Simplified Mode toggle and Detail Level radio buttons + 3. Added summary panel (ItemsControl bound to Summaries) between left panel and DataGrid + 4. DataGrid now binds to `ActiveItemsSource` instead of `Results` + 5. Added "Simplified Labels" column visible only in simplified mode (via DataTrigger on Visibility) + 6. Permission Levels column cells are color-coded by RiskLevel using DataTrigger + 7. DataGrid visibility controlled by IsDetailView when in simplified mode + 8. Summary panel visibility controlled by IsSimplifiedMode + + ```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +