Files
Sharepoint-Toolbox/.planning/phases/07-user-access-audit/07-07-PLAN.md
Dev 19e4c3852d docs(07): create phase plan - 8 plans across 5 waves
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:32:39 +02:00

313 lines
13 KiB
Markdown

---
phase: 07-user-access-audit
plan: 07
type: execute
wave: 4
depends_on: ["07-04", "07-05", "07-06"]
files_modified:
- SharepointToolbox/MainWindow.xaml
- SharepointToolbox/MainWindow.xaml.cs
- SharepointToolbox/App.xaml.cs
- SharepointToolbox/Localization/Strings.resx
- SharepointToolbox/Localization/Strings.fr.resx
autonomous: true
requirements:
- UACC-01
must_haves:
truths:
- "User Access Audit tab appears in MainWindow TabControl"
- "Tab content is wired to DI-resolved UserAccessAuditView"
- "All new services (IUserAccessAuditService, IGraphUserSearchService, export services) are registered in DI"
- "UserAccessAuditViewModel and UserAccessAuditView are registered in DI"
- "All localization keys used in UserAccessAuditView.xaml exist in both Strings.resx and Strings.fr.resx"
- "Site picker dialog factory is wired from MainWindow.xaml.cs"
artifacts:
- path: "SharepointToolbox/MainWindow.xaml"
provides: "New TabItem for User Access Audit"
contains: "UserAccessAuditTabItem"
- path: "SharepointToolbox/MainWindow.xaml.cs"
provides: "DI wiring for audit tab content and dialog factory"
contains: "UserAccessAuditView"
- path: "SharepointToolbox/App.xaml.cs"
provides: "DI registrations for all Phase 7 services and ViewModels"
contains: "UserAccessAuditService"
- path: "SharepointToolbox/Localization/Strings.resx"
provides: "English localization keys for audit tab"
contains: "tab.userAccessAudit"
- path: "SharepointToolbox/Localization/Strings.fr.resx"
provides: "French localization keys for audit tab"
contains: "tab.userAccessAudit"
key_links:
- from: "SharepointToolbox/MainWindow.xaml"
to: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
via: "TabItem.Content set from code-behind"
pattern: "UserAccessAuditTabItem"
- from: "SharepointToolbox/App.xaml.cs"
to: "SharepointToolbox/Services/UserAccessAuditService.cs"
via: "DI registration AddTransient<IUserAccessAuditService, UserAccessAuditService>"
pattern: "UserAccessAuditService"
---
<objective>
Wire the User Access Audit tab into the application: add TabItem to MainWindow, register all Phase 7 services in DI, set up dialog factories, and add all localization keys in English and French.
Purpose: Integration glue that makes all Phase 7 pieces discoverable and functional at runtime.
Output: Modified MainWindow.xaml, MainWindow.xaml.cs, App.xaml.cs, Strings.resx, Strings.fr.resx
</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/07-user-access-audit/07-CONTEXT.md
@.planning/phases/07-user-access-audit/07-04-SUMMARY.md
@.planning/phases/07-user-access-audit/07-05-SUMMARY.md
@.planning/phases/07-user-access-audit/07-06-SUMMARY.md
<interfaces>
<!-- Current MainWindow.xaml TabControl (add new TabItem before SettingsTabItem) -->
From SharepointToolbox/MainWindow.xaml (existing tabs):
```xml
<TabItem x:Name="TemplatesTabItem" Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[tab.templates]}" />
<!-- Settings tab: content set from code-behind via DI-resolved SettingsView -->
<TabItem x:Name="SettingsTabItem" Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[tab.settings]}" />
```
<!-- Current MainWindow.xaml.cs wiring pattern -->
From SharepointToolbox/MainWindow.xaml.cs:
```csharp
PermissionsTabItem.Content = serviceProvider.GetRequiredService<PermissionsView>();
StorageTabItem.Content = serviceProvider.GetRequiredService<StorageView>();
// ... etc
SettingsTabItem.Content = serviceProvider.GetRequiredService<SettingsView>();
```
<!-- Current App.xaml.cs DI registration pattern -->
From SharepointToolbox/App.xaml.cs:
```csharp
// Phase 2: Permissions
services.AddTransient<IPermissionsService, PermissionsService>();
services.AddTransient<CsvExportService>();
services.AddTransient<HtmlExportService>();
services.AddTransient<PermissionsViewModel>();
services.AddTransient<PermissionsView>();
```
<!-- Types to register -->
Services: IUserAccessAuditService -> UserAccessAuditService, IGraphUserSearchService -> GraphUserSearchService
Export: UserAccessCsvExportService, UserAccessHtmlExportService
ViewModel: UserAccessAuditViewModel
View: UserAccessAuditView
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Add DI registrations in App.xaml.cs</name>
<files>SharepointToolbox/App.xaml.cs</files>
<action>
In `App.xaml.cs`, add a new section in `RegisterServices` after the existing Phase 4 registrations and before `services.AddSingleton<MainWindow>()`:
```csharp
// Phase 7: User Access Audit
services.AddTransient<IUserAccessAuditService, UserAccessAuditService>();
services.AddTransient<IGraphUserSearchService, GraphUserSearchService>();
services.AddTransient<UserAccessCsvExportService>();
services.AddTransient<UserAccessHtmlExportService>();
services.AddTransient<UserAccessAuditViewModel>();
services.AddTransient<UserAccessAuditView>();
```
Add the necessary using statement at the top if not already present (Services.Export namespace is already imported via existing export services).
</action>
<verify>
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
</verify>
<done>App.xaml.cs registers all Phase 7 services, ViewModel, and View in the DI container.</done>
</task>
<task type="auto">
<name>Task 2: Add TabItem to MainWindow.xaml and wire in MainWindow.xaml.cs</name>
<files>SharepointToolbox/MainWindow.xaml, SharepointToolbox/MainWindow.xaml.cs</files>
<action>
**MainWindow.xaml**: Add a new TabItem before SettingsTabItem (after TemplatesTabItem):
```xml
<TabItem x:Name="UserAccessAuditTabItem"
Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[tab.userAccessAudit]}">
</TabItem>
```
**MainWindow.xaml.cs**: Add tab content wiring after the existing tab assignments, before SettingsTabItem:
```csharp
// Phase 7: User Access Audit
var auditView = serviceProvider.GetRequiredService<UserAccessAuditView>();
UserAccessAuditTabItem.Content = auditView;
// Wire site picker dialog factory for audit tab (same pattern as Permissions)
if (auditView.DataContext is UserAccessAuditViewModel auditVm)
{
auditVm.OpenSitePickerDialog = () =>
{
var factory = serviceProvider.GetRequiredService<Func<TenantProfile, SitePickerDialog>>();
return factory(auditVm.CurrentProfile ?? new TenantProfile());
};
}
```
Add `using SharepointToolbox.ViewModels.Tabs;` to MainWindow.xaml.cs if not already present (it should be via existing tab wiring, but the UserAccessAuditViewModel type needs to be resolved).
</action>
<verify>
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
</verify>
<done>MainWindow.xaml has UserAccessAuditTabItem. MainWindow.xaml.cs wires UserAccessAuditView content and site picker dialog factory.</done>
</task>
<task type="auto">
<name>Task 3: Add localization keys to Strings.resx and Strings.fr.resx</name>
<files>SharepointToolbox/Localization/Strings.resx, SharepointToolbox/Localization/Strings.fr.resx</files>
<action>
Add the following keys to both resx files. Add them at the end of the existing data entries, before the closing `</root>` tag.
**Strings.resx (English):**
```xml
<data name="tab.userAccessAudit" xml:space="preserve">
<value>User Access Audit</value>
</data>
<data name="audit.grp.users" xml:space="preserve">
<value>Select Users</value>
</data>
<data name="audit.grp.sites" xml:space="preserve">
<value>Target Sites</value>
</data>
<data name="audit.grp.options" xml:space="preserve">
<value>Scan Options</value>
</data>
<data name="audit.search.placeholder" xml:space="preserve">
<value>Search users by name or email...</value>
</data>
<data name="audit.users.selected" xml:space="preserve">
<value>{0} user(s) selected</value>
</data>
<data name="audit.btn.run" xml:space="preserve">
<value>Run Audit</value>
</data>
<data name="audit.btn.exportCsv" xml:space="preserve">
<value>Export CSV</value>
</data>
<data name="audit.btn.exportHtml" xml:space="preserve">
<value>Export HTML</value>
</data>
<data name="audit.summary.total" xml:space="preserve">
<value>Total Accesses</value>
</data>
<data name="audit.summary.sites" xml:space="preserve">
<value>Sites</value>
</data>
<data name="audit.summary.highPriv" xml:space="preserve">
<value>High Privilege</value>
</data>
<data name="audit.toggle.byUser" xml:space="preserve">
<value>By User</value>
</data>
<data name="audit.toggle.bySite" xml:space="preserve">
<value>By Site</value>
</data>
<data name="audit.filter.placeholder" xml:space="preserve">
<value>Filter results...</value>
</data>
<data name="audit.noUsers" xml:space="preserve">
<value>Select at least one user to audit.</value>
</data>
<data name="audit.noSites" xml:space="preserve">
<value>Select at least one site to scan.</value>
</data>
```
**Strings.fr.resx (French):**
```xml
<data name="tab.userAccessAudit" xml:space="preserve">
<value>Audit des acces utilisateur</value>
</data>
<data name="audit.grp.users" xml:space="preserve">
<value>Selectionner les utilisateurs</value>
</data>
<data name="audit.grp.sites" xml:space="preserve">
<value>Sites cibles</value>
</data>
<data name="audit.grp.options" xml:space="preserve">
<value>Options d'analyse</value>
</data>
<data name="audit.search.placeholder" xml:space="preserve">
<value>Rechercher par nom ou email...</value>
</data>
<data name="audit.users.selected" xml:space="preserve">
<value>{0} utilisateur(s) selectionne(s)</value>
</data>
<data name="audit.btn.run" xml:space="preserve">
<value>Lancer l'audit</value>
</data>
<data name="audit.btn.exportCsv" xml:space="preserve">
<value>Exporter CSV</value>
</data>
<data name="audit.btn.exportHtml" xml:space="preserve">
<value>Exporter HTML</value>
</data>
<data name="audit.summary.total" xml:space="preserve">
<value>Total des acces</value>
</data>
<data name="audit.summary.sites" xml:space="preserve">
<value>Sites</value>
</data>
<data name="audit.summary.highPriv" xml:space="preserve">
<value>Privileges eleves</value>
</data>
<data name="audit.toggle.byUser" xml:space="preserve">
<value>Par utilisateur</value>
</data>
<data name="audit.toggle.bySite" xml:space="preserve">
<value>Par site</value>
</data>
<data name="audit.filter.placeholder" xml:space="preserve">
<value>Filtrer les resultats...</value>
</data>
<data name="audit.noUsers" xml:space="preserve">
<value>Selectionnez au moins un utilisateur.</value>
</data>
<data name="audit.noSites" xml:space="preserve">
<value>Selectionnez au moins un site.</value>
</data>
```
Note: French accented characters (e with accent) should use proper Unicode characters in the actual file. Use the existing file's encoding pattern.
</action>
<verify>
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
</verify>
<done>Both Strings.resx and Strings.fr.resx contain all audit-related localization keys. Keys match those referenced in UserAccessAuditView.xaml.</done>
</task>
</tasks>
<verification>
- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors
- MainWindow shows User Access Audit tab in the TabControl
- App.xaml.cs has DI registrations for all Phase 7 types
- All localization keys used in XAML exist in both resx files
- Site picker dialog factory is wired for the audit ViewModel
</verification>
<success_criteria>
The User Access Audit feature is fully integrated into the application. The tab appears in MainWindow, all services resolve from DI, dialog factories work, and UI text is localized in both English and French.
</success_criteria>
<output>
After completion, create `.planning/phases/07-user-access-audit/07-07-SUMMARY.md`
</output>