Merge branch 'main' of https://git.azuze.fr/kawa/Sharepoint-Toolbox
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<Window x:Class="SharepointToolbox.Views.Dialogs.LibraryPickerDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:loc="clr-namespace:SharepointToolbox.Localization"
|
||||
Title="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[librarypicker.title]}"
|
||||
Width="420" Height="520" WindowStartupLocation="CenterOwner"
|
||||
Background="{DynamicResource AppBgBrush}"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
TextOptions.TextFormattingMode="Ideal"
|
||||
ResizeMode="CanResizeWithGrip">
|
||||
<DockPanel Margin="10">
|
||||
<TextBlock x:Name="StatusText" DockPanel.Dock="Top" Margin="0,0,0,8"
|
||||
Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[librarypicker.loading]}" />
|
||||
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,6">
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[librarypicker.selectAll]}"
|
||||
Click="SelectAll_Click" Margin="0,0,6,0" Padding="6,2" />
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[librarypicker.selectNone]}"
|
||||
Click="SelectNone_Click" Padding="6,2" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"
|
||||
HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderbrowser.cancel]}"
|
||||
Width="80" Margin="0,0,8,0" IsCancel="True" Click="Cancel_Click" />
|
||||
<Button x:Name="OkButton"
|
||||
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderbrowser.select]}"
|
||||
Width="80" IsDefault="True" IsEnabled="False" Click="Ok_Click" />
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl x:Name="LibrariesList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox Content="{Binding Title}" IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
||||
Margin="2,4" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,105 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using Microsoft.SharePoint.Client;
|
||||
using SharepointToolbox.Services;
|
||||
|
||||
namespace SharepointToolbox.Views.Dialogs;
|
||||
|
||||
public partial class LibraryPickerDialog : Window
|
||||
{
|
||||
private readonly ClientContext _ctx;
|
||||
private readonly IVersionCleanupService _libraryLister;
|
||||
private readonly ObservableCollection<LibraryItem> _items = new();
|
||||
|
||||
public IReadOnlyList<string> SelectedLibraryTitles { get; private set; } = Array.Empty<string>();
|
||||
|
||||
public LibraryPickerDialog(
|
||||
ClientContext ctx,
|
||||
IVersionCleanupService libraryLister,
|
||||
IReadOnlyCollection<string>? preselected = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_ctx = ctx;
|
||||
_libraryLister = libraryLister;
|
||||
LibrariesList.ItemsSource = _items;
|
||||
Loaded += async (_, _) => await LoadAsync(preselected ?? Array.Empty<string>());
|
||||
}
|
||||
|
||||
private async Task LoadAsync(IReadOnlyCollection<string> preselected)
|
||||
{
|
||||
try
|
||||
{
|
||||
var titles = await _libraryLister.ListLibraryTitlesAsync(_ctx, CancellationToken.None);
|
||||
var preset = new HashSet<string>(preselected, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var t in titles)
|
||||
{
|
||||
var item = new LibraryItem { Title = t, IsSelected = preset.Contains(t) };
|
||||
item.PropertyChanged += OnItemChanged;
|
||||
_items.Add(item);
|
||||
}
|
||||
StatusText.Text = string.Format(
|
||||
Localization.TranslationSource.Instance["librarypicker.loaded"],
|
||||
_items.Count);
|
||||
UpdateOkEnabled();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusText.Text = $"Error: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(LibraryItem.IsSelected)) UpdateOkEnabled();
|
||||
}
|
||||
|
||||
private void UpdateOkEnabled()
|
||||
=> OkButton.IsEnabled = _items.Any(i => i.IsSelected);
|
||||
|
||||
private void SelectAll_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (var i in _items) i.IsSelected = true;
|
||||
}
|
||||
|
||||
private void SelectNone_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (var i in _items) i.IsSelected = false;
|
||||
}
|
||||
|
||||
private void Ok_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectedLibraryTitles = _items.Where(i => i.IsSelected).Select(i => i.Title).ToList();
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void Cancel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
foreach (var i in _items) i.PropertyChanged -= OnItemChanged;
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
public class LibraryItem : INotifyPropertyChanged
|
||||
{
|
||||
private bool _isSelected;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Profile list -->
|
||||
@@ -58,8 +59,31 @@
|
||||
Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.clientid.hint]}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Profile CRUD buttons (placed under fields for natural flow) -->
|
||||
<Grid Grid.Row="3" Margin="0,4,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.add]}"
|
||||
Command="{Binding AddCommand}"
|
||||
MinWidth="90" Padding="10,4" Margin="0,0,6,0"
|
||||
ToolTip="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.add.tooltip]}" />
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.save]}"
|
||||
Command="{Binding SaveCommand}"
|
||||
MinWidth="90" Padding="10,4"
|
||||
ToolTip="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.save.tooltip]}" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.delete]}"
|
||||
Command="{Binding DeleteCommand}"
|
||||
MinWidth="90" Padding="10,4"
|
||||
Foreground="{DynamicResource DangerBrush}"
|
||||
ToolTip="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.delete.tooltip]}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Client Logo -->
|
||||
<StackPanel Grid.Row="3" Margin="0,8,0,8">
|
||||
<StackPanel Grid.Row="4" Margin="0,8,0,8">
|
||||
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.logo.title]}" Padding="0,0,0,4" />
|
||||
<Border BorderBrush="{DynamicResource BorderSoftBrush}" BorderThickness="1" Padding="8" CornerRadius="4"
|
||||
HorizontalAlignment="Left" MinWidth="200" MinHeight="50">
|
||||
@@ -96,7 +120,7 @@
|
||||
</StackPanel>
|
||||
|
||||
<!-- App Registration -->
|
||||
<StackPanel Grid.Row="4" Margin="0,8,0,8">
|
||||
<StackPanel Grid.Row="5" Margin="0,8,0,8">
|
||||
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.fallback.title]}"
|
||||
FontWeight="SemiBold" Padding="0,0,0,4"
|
||||
Visibility="{Binding ShowFallbackInstructions, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
@@ -134,16 +158,10 @@
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.add]}"
|
||||
Command="{Binding AddCommand}" Width="60" Margin="4,0" />
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.save]}"
|
||||
Command="{Binding SaveCommand}" MinWidth="80" Padding="6,0" Margin="4,0" />
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.delete]}"
|
||||
Command="{Binding DeleteCommand}" Width="60" Margin="4,0" />
|
||||
<!-- Close button -->
|
||||
<StackPanel Grid.Row="6" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[common.close]}"
|
||||
Width="60" Margin="4,0"
|
||||
MinWidth="80" Padding="10,4"
|
||||
Click="CloseButton_Click" IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -9,6 +9,15 @@ public partial class ProfileManagementDialog : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
viewModel.ConfirmRegisterApp = msg =>
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
this, msg,
|
||||
Localization.TranslationSource.Instance["profile.register"],
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Information);
|
||||
return result == MessageBoxResult.OK;
|
||||
};
|
||||
Loaded += async (_, _) => await viewModel.LoadAsync();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user