chore: complete v1.0 milestone
Archive 5 phases (36 plans) to milestones/v1.0-phases/. Archive roadmap, requirements, and audit to milestones/. Evolve PROJECT.md with shipped state and validated requirements. Collapse ROADMAP.md to one-line milestone summary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
252
.planning/milestones/v1.0-phases/02-permissions/02-07-PLAN.md
Normal file
252
.planning/milestones/v1.0-phases/02-permissions/02-07-PLAN.md
Normal file
@@ -0,0 +1,252 @@
|
||||
---
|
||||
phase: 02-permissions
|
||||
plan: 07
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on:
|
||||
- 02-06
|
||||
files_modified:
|
||||
- SharepointToolbox/Views/Tabs/PermissionsView.xaml
|
||||
- SharepointToolbox/Views/Tabs/PermissionsView.xaml.cs
|
||||
- SharepointToolbox/App.xaml.cs
|
||||
- SharepointToolbox/MainWindow.xaml
|
||||
- SharepointToolbox/MainWindow.xaml.cs
|
||||
autonomous: false
|
||||
requirements:
|
||||
- PERM-01
|
||||
- PERM-02
|
||||
- PERM-03
|
||||
- PERM-04
|
||||
- PERM-05
|
||||
- PERM-06
|
||||
- PERM-07
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "The Permissions tab in the running application shows PermissionsView — not the 'Coming soon' FeatureTabBase stub"
|
||||
- "User can enter a site URL, click Generate Report, see progress, and results appear in a DataGrid"
|
||||
- "User can click Export CSV and Export HTML — file save dialog appears and file is created"
|
||||
- "Scan Options panel shows checkboxes for Scan Folders, Include Inherited Permissions, and a Folder Depth input"
|
||||
- "View Sites button opens SitePickerDialog and selected sites appear as '{N} site(s) selected' label"
|
||||
- "Cancel button stops the scan mid-operation"
|
||||
artifacts:
|
||||
- path: "SharepointToolbox/Views/Tabs/PermissionsView.xaml"
|
||||
provides: "Complete Permissions tab UI"
|
||||
- path: "SharepointToolbox/Views/Tabs/PermissionsView.xaml.cs"
|
||||
provides: "Code-behind: sets DataContext, wires dialog factory"
|
||||
- path: "SharepointToolbox/App.xaml.cs"
|
||||
provides: "DI registration for Phase 2 services"
|
||||
contains: "PermissionsViewModel"
|
||||
- path: "SharepointToolbox/MainWindow.xaml"
|
||||
provides: "Permissions TabItem uses PermissionsView instead of FeatureTabBase stub"
|
||||
key_links:
|
||||
- from: "PermissionsView.xaml.cs"
|
||||
to: "PermissionsViewModel"
|
||||
via: "DataContext = ServiceProvider.GetRequiredService<PermissionsViewModel>()"
|
||||
pattern: "GetRequiredService.*PermissionsViewModel"
|
||||
- from: "PermissionsView.xaml.cs"
|
||||
to: "SitePickerDialog"
|
||||
via: "viewModel.OpenSitePickerDialog factory"
|
||||
pattern: "OpenSitePickerDialog"
|
||||
- from: "App.xaml.cs"
|
||||
to: "PermissionsViewModel, PermissionsService, SiteListService, CsvExportService, HtmlExportService"
|
||||
via: "services.AddTransient / AddScoped"
|
||||
pattern: "AddTransient.*Permissions"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create PermissionsView XAML, wire it into MainWindow replacing the FeatureTabBase stub, register all Phase 2 services in DI, and checkpoint with a human visual verification of the running application.
|
||||
|
||||
Purpose: This is the integration plan — all services exist, ViewModel exists, now wire everything together and confirm the full feature works end-to-end in the UI.
|
||||
Output: PermissionsView.xaml + .cs, updated App.xaml.cs DI, updated MainWindow.xaml.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/phases/02-permissions/02-RESEARCH.md
|
||||
|
||||
<interfaces>
|
||||
<!-- DI registration pattern from Phase 1 (App.xaml.cs) -->
|
||||
<!-- ProfileManagementDialog and SettingsView are registered as Transient -->
|
||||
<!-- MainWindowViewModel is registered as Singleton -->
|
||||
<!-- IServiceProvider is injected into MainWindow constructor -->
|
||||
|
||||
From MainWindow.xaml (current stub — line 45 is the Permissions tab):
|
||||
```xml
|
||||
<!-- TabControl: 7 stub tabs use FeatureTabBase; Settings tab wired in plan 01-07 -->
|
||||
<!-- Tab order: Permissions, Storage, File Search, Duplicates, Templates, Folder Structure, Bulk Ops -->
|
||||
<TabItem Header="Permissions">
|
||||
<controls:FeatureTabBase /> <!-- REPLACE THIS with <views:PermissionsView /> -->
|
||||
</TabItem>
|
||||
```
|
||||
|
||||
PermissionsView code-behind wiring pattern (same as SettingsView from Phase 1):
|
||||
```csharp
|
||||
public partial class PermissionsView : UserControl
|
||||
{
|
||||
public PermissionsView(IServiceProvider serviceProvider)
|
||||
{
|
||||
InitializeComponent();
|
||||
var vm = serviceProvider.GetRequiredService<PermissionsViewModel>();
|
||||
DataContext = vm;
|
||||
// Wire dialog factory — avoids Window/DI coupling in ViewModel (Phase 1 pattern)
|
||||
vm.OpenSitePickerDialog = () => serviceProvider.GetRequiredService<SitePickerDialog>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
SitePickerDialog needs the current TenantProfile — pass it via a factory:
|
||||
```csharp
|
||||
// In PermissionsView code-behind, the dialog factory must pass the current profile from the ViewModel:
|
||||
vm.OpenSitePickerDialog = () => serviceProvider.GetRequiredService<Func<TenantProfile, SitePickerDialog>>()(vm.CurrentProfile!);
|
||||
// Register in DI: services.AddTransient<Func<TenantProfile, SitePickerDialog>>(sp =>
|
||||
// profile => new SitePickerDialog(sp.GetRequiredService<ISiteListService>(), profile));
|
||||
```
|
||||
|
||||
PermissionsView DataGrid columns (results binding):
|
||||
- Object Type (ObjectType)
|
||||
- Title
|
||||
- URL (as hyperlink or plain text)
|
||||
- Has Unique Permissions (HasUniquePermissions — bool, display as Yes/No)
|
||||
- Users
|
||||
- Permission Levels (PermissionLevels)
|
||||
- Granted Through (GrantedThrough)
|
||||
- Principal Type (PrincipalType)
|
||||
|
||||
All text in XAML uses TranslationSource binding: `{Binding [btn.gen.perms], Source={x:Static loc:TranslationSource.Instance}}`
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create PermissionsView XAML + code-behind and register DI</name>
|
||||
<files>
|
||||
SharepointToolbox/Views/Tabs/PermissionsView.xaml
|
||||
SharepointToolbox/Views/Tabs/PermissionsView.xaml.cs
|
||||
SharepointToolbox/App.xaml.cs
|
||||
SharepointToolbox/MainWindow.xaml
|
||||
SharepointToolbox/MainWindow.xaml.cs
|
||||
</files>
|
||||
<action>
|
||||
READ App.xaml.cs and MainWindow.xaml before modifying to understand existing structure.
|
||||
|
||||
Step 1 — Create PermissionsView.xaml:
|
||||
WPF UserControl. Layout with a Grid split into:
|
||||
- Left panel (~280px): Scan configuration
|
||||
- GroupBox "Scan Options" (bound to `[grp.scan.opts]`):
|
||||
- TextBlock + TextBox for SiteUrl (bound to `{Binding SiteUrl}`)
|
||||
- Button "View Sites" (bound to `{Binding [btn.view.sites]}`, Command=`{Binding OpenSitePickerCommand}`)
|
||||
- TextBlock showing `{Binding SitesSelectedLabel}` (e.g., "3 site(s) selected") — expose this as [ObservableProperty] in ViewModel
|
||||
- CheckBox "Scan Folders" (bound to `{Binding ScanFolders}`)
|
||||
- CheckBox "Include Inherited Permissions" (bound to `{Binding IncludeInherited}`)
|
||||
- CheckBox "Recursive (subsites)" (bound to `{Binding IncludeSubsites}`)
|
||||
- Label + TextBox for FolderDepth (bound to `{Binding FolderDepth}`)
|
||||
- CheckBox "Maximum (all levels)" — when checked sets FolderDepth to 999
|
||||
- Buttons row: "Generate Report" (bound to `{Binding RunCommand}`), "Cancel" (bound to `{Binding CancelCommand}`)
|
||||
- Buttons row: "Export CSV" (bound to `{Binding ExportCsvCommand}`), "Export HTML" (bound to `{Binding ExportHtmlCommand}`)
|
||||
- Right panel (remaining space): Results DataGrid
|
||||
- DataGrid bound to `{Binding Results}`, AutoGenerateColumns=False, IsReadOnly=True, VirtualizingPanel.IsVirtualizing=True, EnableRowVirtualization=True
|
||||
- Columns: ObjectType, Title, Url, HasUniquePermissions (display Yes/No via StringFormat or converter), Users, PermissionLevels, GrantedThrough, PrincipalType
|
||||
- Bottom StatusBar: ProgressBar (bound to `{Binding ProgressValue}`) + TextBlock (bound to `{Binding StatusMessage}`)
|
||||
|
||||
Step 2 — Create PermissionsView.xaml.cs code-behind:
|
||||
```csharp
|
||||
public partial class PermissionsView : UserControl
|
||||
{
|
||||
public PermissionsView(IServiceProvider serviceProvider)
|
||||
{
|
||||
InitializeComponent();
|
||||
var vm = serviceProvider.GetRequiredService<PermissionsViewModel>();
|
||||
DataContext = vm;
|
||||
vm.OpenSitePickerDialog = () =>
|
||||
{
|
||||
var factory = serviceProvider.GetRequiredService<Func<TenantProfile, SitePickerDialog>>();
|
||||
return factory(vm.CurrentProfile ?? new TenantProfile());
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Step 3 — Update App.xaml.cs DI registrations. Add inside the `ConfigureServices` method:
|
||||
```csharp
|
||||
// Phase 2: Permissions
|
||||
services.AddTransient<IPermissionsService, PermissionsService>();
|
||||
services.AddTransient<ISiteListService, SiteListService>();
|
||||
services.AddTransient<CsvExportService>();
|
||||
services.AddTransient<HtmlExportService>();
|
||||
services.AddTransient<PermissionsViewModel>();
|
||||
services.AddTransient<PermissionsView>();
|
||||
services.AddTransient<SitePickerDialog>();
|
||||
services.AddTransient<Func<TenantProfile, SitePickerDialog>>(sp =>
|
||||
profile => new SitePickerDialog(sp.GetRequiredService<ISiteListService>(), profile));
|
||||
```
|
||||
|
||||
Step 4 — Update MainWindow.xaml: replace the FIRST `<controls:FeatureTabBase />` (the Permissions tab) with:
|
||||
```xml
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{Binding [tab.permissions], Source={x:Static loc:TranslationSource.Instance}}"/>
|
||||
</TabItem.Header>
|
||||
<views:PermissionsView />
|
||||
</TabItem>
|
||||
```
|
||||
Add `xmlns:views="clr-namespace:SharepointToolbox.Views.Tabs"` to the Window namespaces if not already present.
|
||||
Add localization key `tab.permissions` = "Permissions" (EN) / "Permissions" (FR — same word) to resx files and Strings.Designer.cs.
|
||||
|
||||
Step 5 — Update MainWindow.xaml.cs if needed to resolve PermissionsView from DI (same pattern used for SettingsView — check existing code for how the Settings tab UserControl is created).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>dotnet build C:/Users/dev/Documents/projets/Sharepoint/SharepointToolbox.slnx 2>&1 | tail -5</automated>
|
||||
</verify>
|
||||
<done>dotnet build succeeds with 0 errors. All services registered in DI. PermissionsView compiles. MainWindow.xaml has `<views:PermissionsView />` instead of `<controls:FeatureTabBase />` for the Permissions tab.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<name>Checkpoint: Visual verification of Permissions tab in running application</name>
|
||||
<action>Human verifies the running application visually as described in how-to-verify below.</action>
|
||||
<verify>
|
||||
<automated>HUMAN — run app and confirm checklist: tab visible, scan options present, export buttons disabled, French locale works</automated>
|
||||
</verify>
|
||||
<done>Human types "approved" confirming all 7 checklist items pass.</done>
|
||||
<what-built>
|
||||
Full Permissions tab: scan configuration panel, DataGrid results display, export buttons, site picker dialog. All Phase 2 services registered in DI. The tab replaces the previous "Coming soon" stub.
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Run the application (F5 or `dotnet run --project SharepointToolbox`)
|
||||
2. The Permissions tab is visible in the tab bar — it shows the scan options panel and an empty DataGrid (not "Coming soon")
|
||||
3. The Scan Options panel shows: Site URL input, View Sites button, Scan Folders checkbox, Include Inherited Permissions checkbox, Recursive checkbox, Folder Depth input, Generate Report button, Cancel button, Export CSV button, Export HTML button
|
||||
4. Click "View Sites" — SitePickerDialog opens (may fail with auth error if not connected to a tenant — that is expected; verify the dialog opens and shows a loading state or error, not a crash)
|
||||
5. Export CSV / Export HTML buttons are disabled (grayed out) when no results are loaded
|
||||
6. Switch the language to French (Settings tab) — all Permissions tab labels change to French text (no English fallback visible)
|
||||
7. The Cancel button exists and is disabled when no scan is running
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" if all verifications pass, or describe what is wrong</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `dotnet build C:/Users/dev/Documents/projets/Sharepoint/SharepointToolbox.slnx` → 0 errors
|
||||
- `dotnet test C:/Users/dev/Documents/projets/Sharepoint/SharepointToolbox.slnx` → all tests pass (Phase 1 + Phase 2)
|
||||
- MainWindow.xaml no longer has `<controls:FeatureTabBase />` for the Permissions tab position
|
||||
- App.xaml.cs contains `AddTransient<IPermissionsService, PermissionsService>()`
|
||||
- Human confirms: Permissions tab visible and functional in running app
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Running application shows Permissions tab with full UI (not a stub)
|
||||
- All Phase 2 services registered in DI: IPermissionsService, ISiteListService, CsvExportService, HtmlExportService
|
||||
- Language switching works — all Phase 2 labels translate to French
|
||||
- Export buttons are disabled when no results; enabled after scan completes
|
||||
- Full test suite passes (Phase 1 + Phase 2: target ~50+ tests passing)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/02-permissions/02-07-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user