Plans cover plain-language permission labels, risk-level color coding, summary counts, detail-level toggle, export integration, and unit tests. PermissionEntry record is NOT modified — uses wrapper pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
266 lines
10 KiB
Markdown
266 lines
10 KiB
Markdown
---
|
|
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"
|
|
---
|
|
|
|
<objective>
|
|
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
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/phases/08-simplified-permissions/08-01-SUMMARY.md
|
|
|
|
<interfaces>
|
|
<!-- From 08-01: New types this plan consumes -->
|
|
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<MappingResult> 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<PermissionLevelMapping.MappingResult> Mappings { get; }
|
|
// Passthrough: ObjectType, Title, Url, HasUniquePermissions, Users, UserLogins,
|
|
// PermissionLevels, GrantedThrough, PrincipalType
|
|
public static IReadOnlyList<SimplifiedPermissionEntry> WrapAll(IEnumerable<PermissionEntry> 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<PermissionSummary> Build(IEnumerable<SimplifiedPermissionEntry> entries);
|
|
}
|
|
```
|
|
|
|
<!-- Current PermissionsViewModel — the file being modified -->
|
|
From SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs:
|
|
```csharp
|
|
public partial class PermissionsViewModel : FeatureViewModelBase
|
|
{
|
|
// Existing fields and services — unchanged
|
|
[ObservableProperty] private ObservableCollection<PermissionEntry> _results = new();
|
|
|
|
// Existing commands — unchanged
|
|
public IAsyncRelayCommand ExportCsvCommand { get; }
|
|
public IAsyncRelayCommand ExportHtmlCommand { get; }
|
|
public RelayCommand OpenSitePickerCommand { get; }
|
|
|
|
// Full constructor and test constructor (internal)
|
|
}
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Add simplified mode properties and summary computation to PermissionsViewModel</name>
|
|
<files>SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs</files>
|
|
<action>
|
|
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
|
|
/// <summary>
|
|
/// When true, displays simplified plain-language labels instead of raw SharePoint role names.
|
|
/// Toggling does not re-run the scan.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private bool _isSimplifiedMode;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private bool _isDetailView = true;
|
|
```
|
|
|
|
Add these computed collection properties (NOT ObservableProperty — manually raised):
|
|
|
|
```csharp
|
|
/// <summary>
|
|
/// Simplified wrappers computed from Results. Rebuilt when Results changes.
|
|
/// </summary>
|
|
private IReadOnlyList<SimplifiedPermissionEntry> _simplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
|
|
public IReadOnlyList<SimplifiedPermissionEntry> SimplifiedResults
|
|
{
|
|
get => _simplifiedResults;
|
|
private set => SetProperty(ref _simplifiedResults, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Summary counts grouped by risk level. Rebuilt when SimplifiedResults changes.
|
|
/// </summary>
|
|
private IReadOnlyList<PermissionSummary> _summaries = Array.Empty<PermissionSummary>();
|
|
public IReadOnlyList<PermissionSummary> Summaries
|
|
{
|
|
get => _summaries;
|
|
private set => SetProperty(ref _summaries, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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)
|
|
/// </summary>
|
|
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
|
|
/// <summary>
|
|
/// Recomputes SimplifiedResults and Summaries from the current Results collection.
|
|
/// Called when Results changes or when simplified mode is toggled on.
|
|
/// </summary>
|
|
private void RebuildSimplifiedData()
|
|
{
|
|
SimplifiedResults = SimplifiedPermissionEntry.WrapAll(Results);
|
|
Summaries = PermissionSummaryBuilder.Build(SimplifiedResults);
|
|
}
|
|
```
|
|
|
|
Modify the existing `RunOperationAsync` method: after the line that sets `Results = new ObservableCollection<PermissionEntry>(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<PermissionEntry>(allEntries);
|
|
if (IsSimplifiedMode)
|
|
RebuildSimplifiedData();
|
|
OnPropertyChanged(nameof(ActiveItemsSource));
|
|
```
|
|
|
|
Modify `OnTenantSwitched` to also reset simplified state:
|
|
After `Results = new ObservableCollection<PermissionEntry>();` add:
|
|
```csharp
|
|
SimplifiedResults = Array.Empty<SimplifiedPermissionEntry>();
|
|
Summaries = Array.Empty<PermissionSummary>();
|
|
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
|
|
</action>
|
|
<verify>
|
|
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>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).</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `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
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
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.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/08-simplified-permissions/08-02-SUMMARY.md`
|
|
</output>
|