Files
Sharepoint-Toolbox/.planning/phases/18-auto-take-ownership/18-01-PLAN.md
Dev dbb59d119b docs(18): create phase plan for auto-take-ownership
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:15:15 +02:00

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
SharepointToolbox/Core/Models/AppSettings.cs
SharepointToolbox/Core/Models/PermissionEntry.cs
SharepointToolbox/Services/SettingsService.cs
SharepointToolbox/Services/IOwnershipElevationService.cs
SharepointToolbox/Services/OwnershipElevationService.cs
SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs
SharepointToolbox/Views/Tabs/SettingsView.xaml
SharepointToolbox/Localization/Strings.resx
SharepointToolbox/Localization/Strings.fr.resx
SharepointToolbox/App.xaml.cs
SharepointToolbox.Tests/Services/OwnershipElevationServiceTests.cs
SharepointToolbox.Tests/ViewModels/SettingsViewModelOwnershipTests.cs
true
OWN-01
truths artifacts key_links
AutoTakeOwnership defaults to false in AppSettings
Setting round-trips through SettingsRepository JSON persistence
SettingsViewModel exposes AutoTakeOwnership and persists on toggle
Settings UI shows an auto-take-ownership checkbox, OFF by default
PermissionEntry.WasAutoElevated exists with default false, zero callsite breakage
IOwnershipElevationService contract exists for Tenant.SetSiteAdmin wrapping
path provides contains
SharepointToolbox/Core/Models/AppSettings.cs AutoTakeOwnership bool property defaulting to false AutoTakeOwnership
path provides contains
SharepointToolbox/Core/Models/PermissionEntry.cs WasAutoElevated flag on PermissionEntry record WasAutoElevated
path provides contains
SharepointToolbox/Services/IOwnershipElevationService.cs Elevation service interface interface IOwnershipElevationService
path provides contains
SharepointToolbox/Services/OwnershipElevationService.cs Tenant.SetSiteAdmin wrapper class OwnershipElevationService
path provides contains
SharepointToolbox/Services/SettingsService.cs SetAutoTakeOwnershipAsync method SetAutoTakeOwnershipAsync
path provides contains
SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs AutoTakeOwnership observable property AutoTakeOwnership
path provides contains
SharepointToolbox/Views/Tabs/SettingsView.xaml CheckBox for auto-take-ownership toggle AutoTakeOwnership
from to via pattern
SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs SharepointToolbox/Services/SettingsService.cs SetAutoTakeOwnershipAsync call on property change SetAutoTakeOwnershipAsync
from to via pattern
SharepointToolbox/Views/Tabs/SettingsView.xaml SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs CheckBox IsChecked binding to AutoTakeOwnership AutoTakeOwnership
Add the auto-take-ownership settings toggle (OWN-01), the PermissionEntry.WasAutoElevated flag, and the IOwnershipElevationService contract+implementation that wraps Tenant.SetSiteAdmin.

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.md

From 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 -->
Task 1: Models + SettingsService + OwnershipElevationService + tests SharepointToolbox/Core/Models/AppSettings.cs, SharepointToolbox/Core/Models/PermissionEntry.cs, SharepointToolbox/Services/SettingsService.cs, SharepointToolbox/Services/IOwnershipElevationService.cs, SharepointToolbox/Services/OwnershipElevationService.cs, SharepointToolbox.Tests/Services/OwnershipElevationServiceTests.cs, SharepointToolbox.Tests/ViewModels/SettingsViewModelOwnershipTests.cs - Test: AppSettings.AutoTakeOwnership defaults to false - Test: AppSettings with AutoTakeOwnership=true round-trips through JSON serialization - Test: SettingsService.SetAutoTakeOwnershipAsync persists the value (load -> set -> load -> verify) - Test: PermissionEntry with no WasAutoElevated arg defaults to false (backward compat) - Test: PermissionEntry with WasAutoElevated=true returns true - Test: PermissionEntry `with { WasAutoElevated = true }` produces correct copy - Test: OwnershipElevationService implements IOwnershipElevationService (type check) - Test: SettingsViewModel.AutoTakeOwnership loads false from default settings - Test: SettingsViewModel.AutoTakeOwnership set to true calls SetAutoTakeOwnershipAsync 1. Add `public bool AutoTakeOwnership { get; set; } = false;` to `AppSettings.cs`.
  1. Append bool WasAutoElevated = false as the LAST positional parameter in the PermissionEntry record. Must be last with default to avoid breaking existing callsites.

  2. Add SetAutoTakeOwnershipAsync(bool enabled) to SettingsService.cs following the exact pattern of SetLanguageAsync:

    public async Task SetAutoTakeOwnershipAsync(bool enabled)
    {
        var settings = await _repository.LoadAsync();
        settings.AutoTakeOwnership = enabled;
        await _repository.SaveAsync(settings);
    }
    
  3. Create Services/IOwnershipElevationService.cs:

    public interface IOwnershipElevationService
    {
        Task ElevateAsync(ClientContext tenantAdminCtx, string siteUrl, string loginName, CancellationToken ct);
    }
    
  4. 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();
        }
    }
    
  5. Register in App.xaml.cs DI: services.AddTransient<IOwnershipElevationService, OwnershipElevationService>(); (place near the ISharePointGroupResolver registration).

  6. Create test files:

    • OwnershipElevationServiceTests.cs: Type-check test that OwnershipElevationService implements IOwnershipElevationService. 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).
  7. Run dotnet build SharepointToolbox.sln to 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.

Task 2: SettingsViewModel property + SettingsView XAML + localization SharepointToolbox/ViewModels/Tabs/SettingsViewModel.cs, SharepointToolbox/Views/Tabs/SettingsView.xaml, SharepointToolbox/Localization/Strings.resx, SharepointToolbox/Localization/Strings.fr.resx 1. In `SettingsViewModel.cs`, add the `AutoTakeOwnership` property following the exact DataFolder pattern: ```csharp private bool _autoTakeOwnership; public bool AutoTakeOwnership { get => _autoTakeOwnership; set { if (_autoTakeOwnership == value) return; _autoTakeOwnership = value; OnPropertyChanged(); _ = _settingsService.SetAutoTakeOwnershipAsync(value); } } ```
  1. In LoadAsync(), after loading _dataFolder, add:

    _autoTakeOwnership = settings.AutoTakeOwnership;
    OnPropertyChanged(nameof(AutoTakeOwnership));
    
  2. 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."
  3. 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."
  4. 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" />
    
dotnet build SharepointToolbox.sln && dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~LocaleCompleteness" --no-build Settings tab shows "Site Ownership" section with checkbox bound to AutoTakeOwnership, defaults unchecked, French locale keys present, LocaleCompletenessTests pass. 1. `dotnet build SharepointToolbox.sln` — zero errors, zero warnings 2. `dotnet test SharepointToolbox.Tests --no-build` — full suite green (no regressions from PermissionEntry change) 3. `dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~OwnershipElevation|FullyQualifiedName~SettingsViewModelOwnership" --no-build` — new tests pass 4. `dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~LocaleCompleteness" --no-build` — locale keys complete

<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>
After completion, create `.planning/phases/18-auto-take-ownership/18-01-SUMMARY.md`