- PermissionsViewModel extends FeatureViewModelBase, implements RunOperationAsync - Multi-site mode: loops SelectedSites; single-site mode: uses SiteUrl - ExportCsvCommand and ExportHtmlCommand enabled only when Results.Count > 0 - OpenSitePickerCommand uses dialog factory pattern (Func<Window>?) - OnTenantSwitched clears Results, SiteUrl, SelectedSites - Flat ObservableProperty booleans (IncludeInherited, ScanFolders, etc.) build ScanOptions record - SitePickerDialog XAML: filterable list with CheckBox column, Title, URL columns - SitePickerDialog code-behind: loads sites on Window.Loaded, exposes SelectedUrls - ISessionManager interface extracted for testability (SessionManager implements it) - StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl test passes (60/60 + 3 skip)
128 lines
3.8 KiB
C#
128 lines
3.8 KiB
C#
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using SharepointToolbox.Core.Models;
|
|
using SharepointToolbox.Services;
|
|
|
|
namespace SharepointToolbox.Views.Dialogs;
|
|
|
|
/// <summary>
|
|
/// Dialog for selecting multiple SharePoint sites.
|
|
/// Loads sites from ISiteListService, shows them in a filterable list with checkboxes.
|
|
/// </summary>
|
|
public partial class SitePickerDialog : Window
|
|
{
|
|
private readonly ISiteListService _siteListService;
|
|
private readonly TenantProfile _profile;
|
|
private List<SitePickerItem> _allItems = new();
|
|
|
|
/// <summary>
|
|
/// Returns the list of sites the user checked before clicking OK.
|
|
/// </summary>
|
|
public IReadOnlyList<SiteInfo> SelectedUrls =>
|
|
_allItems.Where(i => i.IsSelected).Select(i => new SiteInfo(i.Url, i.Title)).ToList();
|
|
|
|
public SitePickerDialog(ISiteListService siteListService, TenantProfile profile)
|
|
{
|
|
InitializeComponent();
|
|
_siteListService = siteListService;
|
|
_profile = profile;
|
|
}
|
|
|
|
private async void Window_Loaded(object sender, RoutedEventArgs e) => await LoadSitesAsync();
|
|
|
|
private async Task LoadSitesAsync()
|
|
{
|
|
StatusText.Text = "Loading sites...";
|
|
LoadButton.IsEnabled = false;
|
|
try
|
|
{
|
|
var sites = await _siteListService.GetSitesAsync(
|
|
_profile,
|
|
new Progress<OperationProgress>(),
|
|
System.Threading.CancellationToken.None);
|
|
|
|
_allItems = sites.Select(s => new SitePickerItem(s.Url, s.Title)).ToList();
|
|
ApplyFilter();
|
|
StatusText.Text = $"{_allItems.Count} sites loaded.";
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
StatusText.Text = ex.Message;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
StatusText.Text = $"Error: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
LoadButton.IsEnabled = true;
|
|
}
|
|
}
|
|
|
|
private void ApplyFilter()
|
|
{
|
|
var filter = FilterBox.Text.Trim();
|
|
SiteList.ItemsSource = string.IsNullOrEmpty(filter)
|
|
? (IEnumerable<SitePickerItem>)_allItems
|
|
: _allItems.Where(i =>
|
|
i.Url.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
|
|
i.Title.Contains(filter, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
}
|
|
|
|
private void FilterBox_TextChanged(object sender, TextChangedEventArgs e) => ApplyFilter();
|
|
|
|
private void SelectAll_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
foreach (var item in _allItems) item.IsSelected = true;
|
|
ApplyFilter();
|
|
}
|
|
|
|
private void DeselectAll_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
foreach (var item in _allItems) item.IsSelected = false;
|
|
ApplyFilter();
|
|
}
|
|
|
|
private async void LoadButton_Click(object sender, RoutedEventArgs e) => await LoadSitesAsync();
|
|
|
|
private void OK_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
DialogResult = true;
|
|
Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mutable wrapper for a site entry shown in the SitePickerDialog list.
|
|
/// Supports two-way CheckBox binding via INotifyPropertyChanged.
|
|
/// </summary>
|
|
public class SitePickerItem : INotifyPropertyChanged
|
|
{
|
|
private bool _isSelected;
|
|
|
|
public string Url { get; init; } = string.Empty;
|
|
public string Title { get; init; } = string.Empty;
|
|
|
|
public bool IsSelected
|
|
{
|
|
get => _isSelected;
|
|
set
|
|
{
|
|
if (_isSelected == value) return;
|
|
_isSelected = value;
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
|
|
}
|
|
}
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
public SitePickerItem(string url, string title)
|
|
{
|
|
Url = url;
|
|
Title = title;
|
|
}
|
|
}
|