chore: release v2.4

- 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>
This commit is contained in:
Dev
2026-04-20 11:23:11 +02:00
parent 8f30a60d2a
commit f4cc81bb71
64 changed files with 3315 additions and 405 deletions
@@ -1,7 +1,8 @@
<UserControl x:Class="SharepointToolbox.Views.Tabs.UserAccessAuditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:SharepointToolbox.Localization">
xmlns:loc="clr-namespace:SharepointToolbox.Localization"
xmlns:common="clr-namespace:SharepointToolbox.Views.Common">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="290" />
@@ -13,7 +14,9 @@
</Grid.RowDefinitions>
<!-- Left panel -->
<DockPanel Grid.Column="0" Grid.Row="0" Margin="8">
<ScrollViewer Grid.Column="0" Grid.Row="0"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<DockPanel Margin="8">
<!-- Mode toggle -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,8">
@@ -41,7 +44,7 @@
</GroupBox.Style>
<StackPanel>
<TextBox Text="{Binding SearchQuery, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,2" />
<TextBlock Text="Searching..." FontStyle="Italic" FontSize="10" Foreground="Gray" Margin="0,0,0,2">
<TextBlock Text="Searching..." FontStyle="Italic" FontSize="10" Foreground="{DynamicResource TextMutedBrush}" Margin="0,0,0,2">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed" />
@@ -115,9 +118,9 @@
<!-- Status row: load status + user count -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,4">
<TextBlock Text="{Binding DirectoryLoadStatus}" FontStyle="Italic" Foreground="Gray" FontSize="10"
<TextBlock Text="{Binding DirectoryLoadStatus}" FontStyle="Italic" Foreground="{DynamicResource TextMutedBrush}" FontSize="10"
Margin="0,0,8,0" />
<TextBlock FontSize="10" Foreground="Gray">
<TextBlock FontSize="10" Foreground="{DynamicResource TextMutedBrush}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="DirectoryUserCount" />
@@ -130,7 +133,7 @@
<!-- Hint text -->
<TextBlock DockPanel.Dock="Bottom"
Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.hint.doubleclick]}"
FontStyle="Italic" Foreground="Gray" FontSize="10" Margin="0,4,0,0"
FontStyle="Italic" Foreground="{DynamicResource TextMutedBrush}" FontSize="10" Margin="0,4,0,0"
TextWrapping="Wrap" />
<!-- Directory DataGrid -->
@@ -142,7 +145,7 @@
CanUserSortColumns="True"
SelectionMode="Single" SelectionUnit="FullRow"
HeadersVisibility="Column" GridLinesVisibility="Horizontal"
BorderThickness="1" BorderBrush="#DDDDDD">
BorderThickness="1" BorderBrush="{DynamicResource BorderSoftBrush}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding DisplayName}" Width="120" />
@@ -181,19 +184,20 @@
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#EBF5FB" BorderBrush="#2980B9" BorderThickness="1"
CornerRadius="4" Padding="6,2" Margin="0,1">
CornerRadius="4" Padding="6,2" Margin="0,1"
TextElement.Foreground="{DynamicResource OnColoredBgBrush}">
<DockPanel>
<Button Content="x" DockPanel.Dock="Right" Padding="4,0"
Background="Transparent" BorderThickness="0"
Command="{Binding DataContext.RemoveUserCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}" />
<TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center" />
<TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center" Foreground="#1F2430" />
</DockPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="{Binding SelectedUsersLabel}" FontStyle="Italic" Foreground="Gray" FontSize="10" />
<TextBlock Text="{Binding SelectedUsersLabel}" FontStyle="Italic" Foreground="{DynamicResource TextMutedBrush}" FontSize="10" />
</StackPanel>
<!-- Scan Options (always visible) -->
@@ -247,6 +251,7 @@
</StackPanel>
</DockPanel>
</ScrollViewer>
<!-- Right panel -->
<Grid Grid.Column="1" Grid.Row="0" Margin="0,8,8,0">
@@ -258,37 +263,40 @@
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,8">
<Border Background="#EBF5FB" BorderBrush="#2980B9" BorderThickness="1"
CornerRadius="4" Padding="12,6" Margin="0,0,8,0">
CornerRadius="4" Padding="12,6" Margin="0,0,8,0"
TextElement.Foreground="{DynamicResource OnColoredBgBrush}">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding TotalAccessCount}" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" />
<TextBlock Text="{Binding TotalAccessCount}" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" Foreground="#1F2430" />
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.summary.total]}"
FontSize="10" Foreground="Gray" HorizontalAlignment="Center" />
FontSize="10" Foreground="#5B6472" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Background="#EAFAF1" BorderBrush="#27AE60" BorderThickness="1"
CornerRadius="4" Padding="12,6" Margin="0,0,8,0">
CornerRadius="4" Padding="12,6" Margin="0,0,8,0"
TextElement.Foreground="{DynamicResource OnColoredBgBrush}">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding SitesCount}" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" />
<TextBlock Text="{Binding SitesCount}" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" Foreground="#1F2430" />
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.summary.sites]}"
FontSize="10" Foreground="Gray" HorizontalAlignment="Center" />
FontSize="10" Foreground="#5B6472" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<Border Background="#FDEDEC" BorderBrush="#E74C3C" BorderThickness="1"
CornerRadius="4" Padding="12,6">
CornerRadius="4" Padding="12,6"
TextElement.Foreground="{DynamicResource OnColoredBgBrush}">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding HighPrivilegeCount}" FontSize="20" FontWeight="Bold"
HorizontalAlignment="Center" Foreground="#C0392B" />
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.summary.highPriv]}"
FontSize="10" Foreground="Gray" HorizontalAlignment="Center" />
FontSize="10" Foreground="#5B6472" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,4">
<TextBox Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}" Width="200" Margin="0,0,8,0" />
<ToggleButton IsChecked="{Binding IsGroupByUser}" Padding="8,3">
<ToggleButton IsChecked="{Binding IsGroupByUser}">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Style TargetType="ToggleButton" BasedOn="{StaticResource ThemeToggleButtonStyle}">
<Setter Property="Content" Value="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.toggle.bySite]}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
@@ -327,7 +335,7 @@
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="4,2">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="0,0,8,0" />
<TextBlock Text="{Binding ItemCount, StringFormat=({0})}" Foreground="Gray" />
<TextBlock Text="{Binding ItemCount, StringFormat=({0})}" Foreground="{DynamicResource TextMutedBrush}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
@@ -364,7 +372,7 @@
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#x26A0;" Foreground="#E74C3C" Margin="0,0,4,0"
<TextBlock Text="&#x26A0;" Foreground="{DynamicResource DangerBrush}" Margin="0,0,4,0"
FontSize="12" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
@@ -415,7 +423,8 @@
<!-- Status bar -->
<StatusBar Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1">
<StatusBarItem>
<ProgressBar Width="150" Height="14" Value="{Binding ProgressValue}" Minimum="0" Maximum="100" />
<common:Spinner Width="14" Height="14"
Visibility="{Binding IsRunning, Converter={StaticResource BoolToVisibilityConverter}}" />
</StatusBarItem>
<StatusBarItem Content="{Binding StatusMessage}" />
</StatusBar>