Files
Sharepoint-Toolbox/SharepointToolbox/Views/Tabs/PermissionsView.xaml
Dev 2302cad531 feat(18-02): DataGrid visual differentiation + localization for elevated rows
- Add WasAutoElevated DataTrigger to DataGrid.RowStyle: amber background + tooltip
- Add warning icon (U+26A0) indicator column (width 24) before Object Type column
- Icon shown via DataTrigger on WasAutoElevated, hidden by default
- Add permissions.elevated.tooltip EN key to Strings.resx
- Add permissions.elevated.tooltip FR key to Strings.fr.resx
2026-04-09 14:33:00 +02:00

298 lines
15 KiB
XML

<UserControl x:Class="SharepointToolbox.Views.Tabs.PermissionsView"
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:models="clr-namespace:SharepointToolbox.Core.Models"
xmlns:converters="clr-namespace:SharepointToolbox.Core.Converters">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<converters:InvertBoolConverter x:Key="InvertBoolConverter" />
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="290" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Left panel: Scan configuration -->
<DockPanel Grid.Column="0" Grid.Row="0" Margin="8">
<!-- Scan Options GroupBox -->
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[grp.scan.opts]}"
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
<StackPanel>
<!-- Checkboxes -->
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.scan.folders]}"
IsChecked="{Binding ScanFolders}" Margin="0,0,0,4" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.inherited.perms]}"
IsChecked="{Binding IncludeInherited}" Margin="0,0,0,4" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.recursive]}"
IsChecked="{Binding IncludeSubsites}" Margin="0,0,0,8" />
<!-- Folder depth -->
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.folder.depth]}"
Margin="0,0,0,2" />
<TextBox Text="{Binding FolderDepth, UpdateSourceTrigger=PropertyChanged}"
Width="60" HorizontalAlignment="Left" Margin="0,0,0,4" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.max.depth]}"
IsChecked="{Binding IsMaxDepth, Mode=TwoWay}"
Margin="0,0,0,0" />
</StackPanel>
</GroupBox>
<!-- Display Options GroupBox (Phase 8: Simplified Permissions) -->
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[grp.display.opts]}"
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
<StackPanel>
<!-- Simplified Mode toggle -->
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.simplified.mode]}"
IsChecked="{Binding IsSimplifiedMode}" Margin="0,0,0,8" />
<!-- Detail Level radio buttons (only enabled when simplified mode is on) -->
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.detail.level]}"
Margin="0,0,0,4"
IsEnabled="{Binding IsSimplifiedMode}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSimplifiedMode}" Value="False">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[rad.detail.simple]}"
IsChecked="{Binding IsDetailView, Converter={StaticResource InvertBoolConverter}, Mode=TwoWay}"
IsEnabled="{Binding IsSimplifiedMode}"
Margin="0,0,0,2" />
<RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[rad.detail.detailed]}"
IsChecked="{Binding IsDetailView}"
IsEnabled="{Binding IsSimplifiedMode}"
Margin="0,0,0,0" />
</StackPanel>
</GroupBox>
<!-- Export Options GroupBox (Phase 16: Consolidation Toggle) -->
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.export]}"
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
<StackPanel>
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.merge.permissions]}"
IsChecked="{Binding MergePermissions}" />
</StackPanel>
</GroupBox>
<!-- Action buttons -->
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[btn.gen.perms]}"
Command="{Binding RunCommand}"
Margin="0,0,4,4" Padding="6,3" />
<Button Grid.Column="1"
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[btn.cancel]}"
Command="{Binding CancelCommand}"
Margin="0,0,0,4" Padding="6,3" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[rad.csv.perms]}"
Command="{Binding ExportCsvCommand}"
Margin="0,0,4,0" Padding="6,3" />
<Button Grid.Column="1"
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[rad.html.perms]}"
Command="{Binding ExportHtmlCommand}"
Margin="0,0,0,0" Padding="6,3" />
</Grid>
</StackPanel>
</DockPanel>
<!-- Right panel: Summary + Results -->
<Grid Grid.Column="1" Grid.Row="0" Margin="0,8,8,8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Summary panel (visible only in simplified mode) -->
<ItemsControl Grid.Row="0" ItemsSource="{Binding Summaries}"
Margin="0,0,0,8">
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSimplifiedMode}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="6" Padding="14,10" Margin="0,0,10,4" MinWidth="140">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F3F4F6" />
<Style.Triggers>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.High}">
<Setter Property="Background" Value="#FEE2E2" />
<Setter Property="BorderBrush" Value="#FECACA" />
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.Medium}">
<Setter Property="Background" Value="#FEF3C7" />
<Setter Property="BorderBrush" Value="#FDE68A" />
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.Low}">
<Setter Property="Background" Value="#D1FAE5" />
<Setter Property="BorderBrush" Value="#A7F3D0" />
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.ReadOnly}">
<Setter Property="Background" Value="#DBEAFE" />
<Setter Property="BorderBrush" Value="#BFDBFE" />
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<TextBlock Text="{Binding Count}" FontSize="22" FontWeight="Bold" />
<TextBlock Text="{Binding Label}" FontSize="11" Foreground="#555" />
<TextBlock FontSize="10" Foreground="#888" Margin="0,2,0,0">
<Run Text="{Binding DistinctUsers, Mode=OneWay}" />
<Run Text=" " /><Run Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.summary.users], Mode=OneWay}" />
</TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Results DataGrid -->
<DataGrid Grid.Row="1"
ItemsSource="{Binding ActiveItemsSource}"
AutoGenerateColumns="False"
IsReadOnly="True"
VirtualizingPanel.IsVirtualizing="True"
EnableRowVirtualization="True">
<DataGrid.Style>
<Style TargetType="DataGrid">
<Style.Triggers>
<!-- Hide DataGrid when simplified mode is on but detail view is off -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSimplifiedMode}" Value="True" />
<Condition Binding="{Binding IsDetailView}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
<!-- Row style: color-code by RiskLevel when in simplified mode; amber for auto-elevated rows -->
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.High}">
<Setter Property="Background" Value="#FEF2F2" />
</DataTrigger>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.Medium}">
<Setter Property="Background" Value="#FFFBEB" />
</DataTrigger>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.Low}">
<Setter Property="Background" Value="#ECFDF5" />
</DataTrigger>
<DataTrigger Binding="{Binding RiskLevel}" Value="{x:Static models:RiskLevel.ReadOnly}">
<Setter Property="Background" Value="#EFF6FF" />
</DataTrigger>
<!-- Phase 18: auto-elevated rows get amber background + tooltip -->
<DataTrigger Binding="{Binding WasAutoElevated}" Value="True">
<Setter Property="Background" Value="#FFF9E6" />
<Setter Property="ToolTip" Value="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[permissions.elevated.tooltip]}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!-- Phase 18: auto-elevated row indicator (warning icon) -->
<DataGridTemplateColumn Header="" Width="24" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="&#x26A0;" FontSize="12" HorizontalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding WasAutoElevated}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Object Type" Binding="{Binding ObjectType}" Width="100" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" Width="140" />
<DataGridTextColumn Header="URL" Binding="{Binding Url}" Width="200" />
<DataGridTextColumn Header="Unique Perms" Binding="{Binding HasUniquePermissions}" Width="90" />
<DataGridTextColumn Header="Users" Binding="{Binding Users}" Width="140" />
<DataGridTextColumn Header="Permission Levels" Binding="{Binding PermissionLevels}" Width="140" />
<!-- Simplified Labels column (only visible in simplified mode) -->
<DataGridTextColumn Header="Simplified" Binding="{Binding SimplifiedLabels}" Width="200">
<DataGridTextColumn.Visibility>
<Binding Path="DataContext.IsSimplifiedMode"
RelativeSource="{RelativeSource AncestorType=DataGrid}"
Converter="{StaticResource BoolToVis}" />
</DataGridTextColumn.Visibility>
</DataGridTextColumn>
<DataGridTextColumn Header="Granted Through" Binding="{Binding GrantedThrough}" Width="140" />
<DataGridTextColumn Header="Principal Type" Binding="{Binding PrincipalType}" Width="110" />
</DataGrid.Columns>
</DataGrid>
</Grid>
<!-- Bottom: status bar spanning both columns -->
<StatusBar Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1">
<StatusBarItem>
<ProgressBar Width="150" Height="14"
Value="{Binding ProgressValue}"
Minimum="0" Maximum="100" />
</StatusBarItem>
<StatusBarItem Content="{Binding StatusMessage}" />
</StatusBar>
</Grid>
</UserControl>