12dd1de9f2
- Add theme system (Dark/Light palettes, ModernTheme, ThemeManager) - Add InputDialog, Spinner common view - Add DuplicatesCsvExportService - Refresh views, dialogs, and view models across tabs - Update localization strings (en/fr) - Tweak services (transfer, permissions, search, user access, ownership elevation, bulk operations) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
198 lines
6.6 KiB
C#
198 lines
6.6 KiB
C#
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Controls.Primitives;
|
|
using SharepointToolbox.Core.Models;
|
|
using SharepointToolbox.Localization;
|
|
using SharepointToolbox.Services;
|
|
using SharepointToolbox.ViewModels.Dialogs;
|
|
|
|
namespace SharepointToolbox.Views.Dialogs;
|
|
|
|
/// <summary>
|
|
/// Dialog for selecting multiple SharePoint sites.
|
|
/// Delegates loading and filter/sort logic to <see cref="SitePickerDialogLogic"/>
|
|
/// so the code-behind only handles WPF plumbing.
|
|
/// </summary>
|
|
public partial class SitePickerDialog : Window
|
|
{
|
|
private readonly SitePickerDialogLogic _logic;
|
|
private List<SitePickerItem> _allItems = new();
|
|
private string _sortColumn = "Url";
|
|
private ListSortDirection _sortDirection = ListSortDirection.Ascending;
|
|
|
|
/// <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)
|
|
{
|
|
StorageUsedMb = i.StorageUsedMb,
|
|
StorageQuotaMb = i.StorageQuotaMb,
|
|
Template = i.Template
|
|
}).ToList();
|
|
|
|
public SitePickerDialog(ISiteListService siteListService, TenantProfile profile)
|
|
{
|
|
InitializeComponent();
|
|
_logic = new SitePickerDialogLogic(siteListService, profile);
|
|
}
|
|
|
|
private async void Window_Loaded(object sender, RoutedEventArgs e) => await LoadSitesAsync();
|
|
|
|
private async Task LoadSitesAsync()
|
|
{
|
|
StatusText.Text = TranslationSource.Instance["sitepicker.status.loading"];
|
|
LoadButton.IsEnabled = false;
|
|
try
|
|
{
|
|
var items = await _logic.LoadAsync(
|
|
new Progress<OperationProgress>(),
|
|
System.Threading.CancellationToken.None);
|
|
_allItems = items.ToList();
|
|
ApplyFilter();
|
|
StatusText.Text = string.Format(
|
|
CultureInfo.CurrentUICulture,
|
|
TranslationSource.Instance["sitepicker.status.loaded"],
|
|
_allItems.Count);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
StatusText.Text = ex.Message;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
StatusText.Text = string.Format(
|
|
CultureInfo.CurrentUICulture,
|
|
TranslationSource.Instance["sitepicker.status.error"],
|
|
ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
LoadButton.IsEnabled = true;
|
|
}
|
|
}
|
|
|
|
private void ApplyFilter()
|
|
{
|
|
var text = FilterBox.Text.Trim();
|
|
var minMb = SitePickerDialogLogic.ParseLongOrDefault(MinSizeBox.Text, 0);
|
|
var maxMb = SitePickerDialogLogic.ParseLongOrDefault(MaxSizeBox.Text, long.MaxValue);
|
|
var kindFilter = (TypeFilter.SelectedItem as ComboBoxItem)?.Tag as string ?? "All";
|
|
|
|
var filtered = SitePickerDialogLogic.ApplyFilter(_allItems, text, minMb, maxMb, kindFilter);
|
|
var sorted = SitePickerDialogLogic.ApplySort(filtered, _sortColumn, _sortDirection);
|
|
var list = sorted.ToList();
|
|
|
|
SiteList.ItemsSource = list;
|
|
if (_allItems.Count > 0)
|
|
StatusText.Text = string.Format(
|
|
CultureInfo.CurrentUICulture,
|
|
TranslationSource.Instance["sitepicker.status.shown"],
|
|
list.Count, _allItems.Count);
|
|
}
|
|
|
|
private void SiteList_ColumnHeaderClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (e.OriginalSource is not GridViewColumnHeader header) return;
|
|
if (header.Role == GridViewColumnHeaderRole.Padding) return;
|
|
if (header.Tag is not string column || string.IsNullOrEmpty(column)) return;
|
|
|
|
if (_sortColumn == column)
|
|
{
|
|
_sortDirection = _sortDirection == ListSortDirection.Ascending
|
|
? ListSortDirection.Descending
|
|
: ListSortDirection.Ascending;
|
|
}
|
|
else
|
|
{
|
|
_sortColumn = column;
|
|
_sortDirection = ListSortDirection.Ascending;
|
|
}
|
|
ApplyFilter();
|
|
}
|
|
|
|
private void FilterBox_TextChanged(object sender, TextChangedEventArgs e) => ApplyFilter();
|
|
private void SizeBox_TextChanged(object sender, TextChangedEventArgs e) => ApplyFilter();
|
|
private void TypeFilter_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
if (!IsLoaded) return;
|
|
ApplyFilter();
|
|
}
|
|
|
|
private void SelectAll_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (SiteList.ItemsSource is IEnumerable<SitePickerItem> visible)
|
|
{
|
|
foreach (var item in visible) item.IsSelected = true;
|
|
}
|
|
ApplyFilter();
|
|
}
|
|
|
|
private void DeselectAll_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (SiteList.ItemsSource is IEnumerable<SitePickerItem> visible)
|
|
{
|
|
foreach (var item in visible) 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 long StorageUsedMb { get; init; }
|
|
public long StorageQuotaMb { get; init; }
|
|
public string Template { get; init; } = string.Empty;
|
|
|
|
public SiteKind Kind => SiteKindHelper.FromTemplate(Template);
|
|
public string KindDisplay => SiteKindHelper.DisplayName(Kind);
|
|
public string SizeDisplay => FormatSize(StorageUsedMb);
|
|
|
|
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, long storageUsedMb = 0, long storageQuotaMb = 0, string template = "")
|
|
{
|
|
Url = url;
|
|
Title = title;
|
|
StorageUsedMb = storageUsedMb;
|
|
StorageQuotaMb = storageQuotaMb;
|
|
Template = template;
|
|
}
|
|
|
|
private static string FormatSize(long mb)
|
|
{
|
|
if (mb <= 0) return "—";
|
|
if (mb >= 1024) return $"{mb / 1024.0:F1} GB";
|
|
return $"{mb} MB";
|
|
}
|
|
}
|