13 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 18-auto-take-ownership | 01 | execute | 1 |
|
true |
|
|
Purpose: Establish the data model changes and settings persistence so Plan 02 can wire the scan-loop elevation logic. Output: AppSettings extended, SettingsViewModel wired, SettingsView checkbox visible, OwnershipElevationService ready for injection.
<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>
@.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/18-auto-take-ownership/18-RESEARCH.mdFrom SharepointToolbox/Core/Models/AppSettings.cs:
public class AppSettings
{
public string DataFolder { get; set; } = string.Empty;
public string Lang { get; set; } = "en";
// ADD: public bool AutoTakeOwnership { get; set; } = false;
}
From SharepointToolbox/Core/Models/PermissionEntry.cs:
public record PermissionEntry(
string ObjectType,
string Title,
string Url,
bool HasUniquePermissions,
string Users,
string UserLogins,
string PermissionLevels,
string GrantedThrough,
string PrincipalType
// ADD: bool WasAutoElevated = false -- MUST be last, with default
);
From SharepointToolbox/Services/SettingsService.cs:
public class SettingsService
{
public Task<AppSettings> GetSettingsAsync();
public async Task SetLanguageAsync(string cultureCode);
public async Task SetDataFolderAsync(string path);
// ADD: public async Task SetAutoTakeOwnershipAsync(bool enabled);
}
From SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs:
public partial class SettingsViewModel : FeatureViewModelBase
{
// Constructor: SettingsService settingsService, IBrandingService brandingService, ILogger logger
// ADD: AutoTakeOwnership property following DataFolder/SelectedLanguage pattern
// ADD: Load in LoadAsync(), persist on set via _settingsService.SetAutoTakeOwnershipAsync
}
From SharepointToolbox/Views/Tabs/SettingsView.xaml:
<!-- Existing: StackPanel with Language, Data folder, MSP Logo sections -->
<!-- ADD: Auto-Take Ownership section with CheckBox after MSP Logo, before StatusMessage -->
-
Append
bool WasAutoElevated = falseas the LAST positional parameter in thePermissionEntryrecord. Must be last with default to avoid breaking existing callsites. -
Add
SetAutoTakeOwnershipAsync(bool enabled)toSettingsService.csfollowing the exact pattern ofSetLanguageAsync:public async Task SetAutoTakeOwnershipAsync(bool enabled) { var settings = await _repository.LoadAsync(); settings.AutoTakeOwnership = enabled; await _repository.SaveAsync(settings); } -
Create
Services/IOwnershipElevationService.cs:public interface IOwnershipElevationService { Task ElevateAsync(ClientContext tenantAdminCtx, string siteUrl, string loginName, CancellationToken ct); } -
Create
Services/OwnershipElevationService.cs:using Microsoft.Online.SharePoint.TenantAdministration; public class OwnershipElevationService : IOwnershipElevationService { public async Task ElevateAsync(ClientContext tenantAdminCtx, string siteUrl, string loginName, CancellationToken ct) { var tenant = new Tenant(tenantAdminCtx); tenant.SetSiteAdmin(siteUrl, loginName, isSiteAdmin: true); await tenantAdminCtx.ExecuteQueryAsync(); } } -
Register in
App.xaml.csDI:services.AddTransient<IOwnershipElevationService, OwnershipElevationService>();(place near the ISharePointGroupResolver registration). -
Create test files:
OwnershipElevationServiceTests.cs: Type-check test thatOwnershipElevationServiceimplementsIOwnershipElevationService. No CSOM mock needed for the interface contract test.SettingsViewModelOwnershipTests.cs: Test that SettingsViewModel loads AutoTakeOwnership from settings and that setting it calls SetAutoTakeOwnershipAsync. Use a mock/fake SettingsService (follow existing test patterns in the test project).
-
Run
dotnet build SharepointToolbox.slnto confirm zero breakage from PermissionEntry change. All existing callsites use positional args without specifying WasAutoElevated, so the default kicks in. dotnet build SharepointToolbox.sln && dotnet test SharepointToolbox.Tests --no-build --filter "FullyQualifiedNameOwnershipElevation|FullyQualifiedNameSettingsViewModelOwnership" AppSettings has AutoTakeOwnership (default false), PermissionEntry has WasAutoElevated (default false), SettingsService has SetAutoTakeOwnershipAsync, IOwnershipElevationService + OwnershipElevationService exist and are DI-registered, all tests pass, full solution builds with zero errors.
-
In
LoadAsync(), after loading_dataFolder, add:_autoTakeOwnership = settings.AutoTakeOwnership; OnPropertyChanged(nameof(AutoTakeOwnership)); -
Add localization keys to
Strings.resx:settings.ownership.title= "Site Ownership"settings.ownership.auto= "Automatically take site collection admin ownership on access denied"settings.ownership.description= "When enabled, the app will automatically elevate to site collection admin when a scan encounters an access denied error. Requires Tenant Admin permissions."
-
Add matching French translations to
Strings.fr.resx:settings.ownership.title= "Propri\u00e9t\u00e9 du site"settings.ownership.auto= "Prendre automatiquement la propri\u00e9t\u00e9 d'administrateur de collection de sites en cas de refus d'acc\u00e8s"settings.ownership.description= "Lorsqu'activ\u00e9, l'application prendra automatiquement les droits d'administrateur de collection de sites lorsqu'un scan rencontre une erreur de refus d'acc\u00e8s. N\u00e9cessite les permissions d'administrateur de tenant."
-
In
SettingsView.xaml, add a new section AFTER the MSP Logo section (after the logo StackPanel and before the StatusMessage TextBlock):<Separator Margin="0,12" /> <!-- Auto-Take Ownership --> <Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.ownership.title]}" /> <CheckBox IsChecked="{Binding AutoTakeOwnership}" Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.ownership.auto]}" Margin="0,4,0,0" /> <TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.ownership.description]}" Foreground="#666666" FontSize="11" TextWrapping="Wrap" Margin="20,4,0,0" />
<success_criteria>
- AppSettings.AutoTakeOwnership exists and defaults to false
- PermissionEntry.WasAutoElevated exists with default false, all existing tests still pass
- SettingsService.SetAutoTakeOwnershipAsync persists the toggle
- IOwnershipElevationService + OwnershipElevationService registered in DI
- SettingsViewModel loads and persists AutoTakeOwnership
- SettingsView.xaml shows checkbox for auto-take-ownership
- All EN + FR localization keys present
- Full test suite green </success_criteria>