Some checks failed
Release SharePoint Toolbox v2 / release (push) Failing after 14s
v1.1 shipped with 4 phases (25 plans), 10/10 requirements complete: - Global site selection (toolbar picker, all tabs consume) - User access audit (Graph people-picker, direct/group/inherited) - Simplified permissions (plain-language labels, risk levels, detail toggle) - Storage visualization (LiveCharts2 pie/donut + bar charts) Post-phase polish: centralized site selection (removed per-tab pickers), claims prefix stripping, StorageMetrics backfill, chart tooltip fix, summary stats in app + HTML exports. 205 tests passing, 10,484 LOC. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
295 lines
14 KiB
XML
295 lines
14 KiB
XML
<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">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="290" />
|
|
<ColumnDefinition Width="*" />
|
|
</Grid.ColumnDefinitions>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="*" />
|
|
<RowDefinition Height="Auto" />
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Left panel -->
|
|
<DockPanel Grid.Column="0" Grid.Row="0" Margin="8">
|
|
|
|
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.users]}"
|
|
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
|
|
<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.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Visibility" Value="Collapsed" />
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsSearching}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
<ListBox x:Name="SearchResultsListBox"
|
|
ItemsSource="{Binding SearchResults}" MaxHeight="120"
|
|
ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,0,4"
|
|
SelectionChanged="SearchResultsListBox_SelectionChanged">
|
|
<ListBox.Style>
|
|
<Style TargetType="ListBox">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding SearchResults.Count}" Value="0">
|
|
<Setter Property="Visibility" Value="Collapsed" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ListBox.Style>
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<TextBlock>
|
|
<TextBlock.Text>
|
|
<MultiBinding StringFormat="{}{0} ({1})">
|
|
<Binding Path="DisplayName" />
|
|
<Binding Path="Mail" />
|
|
</MultiBinding>
|
|
</TextBlock.Text>
|
|
</TextBlock>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</ListBox>
|
|
<ItemsControl ItemsSource="{Binding SelectedUsers}" Margin="0,0,0,4">
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate>
|
|
<Border Background="#EBF5FB" BorderBrush="#2980B9" BorderThickness="1"
|
|
CornerRadius="4" Padding="6,2" Margin="0,1">
|
|
<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" />
|
|
</DockPanel>
|
|
</Border>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
<TextBlock Text="{Binding SelectedUsersLabel}" FontStyle="Italic" Foreground="Gray" FontSize="10" />
|
|
</StackPanel>
|
|
</GroupBox>
|
|
|
|
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.options]}"
|
|
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
|
|
<StackPanel>
|
|
<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.scan.folders]}"
|
|
IsChecked="{Binding ScanFolders}" Margin="0,0,0,4" />
|
|
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.recursive]}"
|
|
IsChecked="{Binding IncludeSubsites}" Margin="0,0,0,0" />
|
|
</StackPanel>
|
|
</GroupBox>
|
|
|
|
<StackPanel DockPanel.Dock="Top">
|
|
<Grid Margin="0,0,0,4">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*" />
|
|
<ColumnDefinition Width="*" />
|
|
</Grid.ColumnDefinitions>
|
|
<Button Grid.Column="0"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.btn.run]}"
|
|
Command="{Binding RunCommand}" Margin="0,0,4,0" 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,0" 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=[audit.btn.exportCsv]}"
|
|
Command="{Binding ExportCsvCommand}" Margin="0,0,4,0" Padding="6,3" />
|
|
<Button Grid.Column="1"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.btn.exportHtml]}"
|
|
Command="{Binding ExportHtmlCommand}" Margin="0,0,0,0" Padding="6,3" />
|
|
</Grid>
|
|
</StackPanel>
|
|
|
|
</DockPanel>
|
|
|
|
<!-- Right panel -->
|
|
<Grid Grid.Column="1" Grid.Row="0" Margin="0,8,8,0">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="*" />
|
|
</Grid.RowDefinitions>
|
|
|
|
<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">
|
|
<StackPanel HorizontalAlignment="Center">
|
|
<TextBlock Text="{Binding TotalAccessCount}" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" />
|
|
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.summary.total]}"
|
|
FontSize="10" Foreground="Gray" HorizontalAlignment="Center" />
|
|
</StackPanel>
|
|
</Border>
|
|
<Border Background="#EAFAF1" BorderBrush="#27AE60" BorderThickness="1"
|
|
CornerRadius="4" Padding="12,6" Margin="0,0,8,0">
|
|
<StackPanel HorizontalAlignment="Center">
|
|
<TextBlock Text="{Binding SitesCount}" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" />
|
|
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.summary.sites]}"
|
|
FontSize="10" Foreground="Gray" HorizontalAlignment="Center" />
|
|
</StackPanel>
|
|
</Border>
|
|
<Border Background="#FDEDEC" BorderBrush="#E74C3C" BorderThickness="1"
|
|
CornerRadius="4" Padding="12,6">
|
|
<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" />
|
|
</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.Style>
|
|
<Style TargetType="ToggleButton">
|
|
<Setter Property="Content" Value="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.toggle.bySite]}" />
|
|
<Style.Triggers>
|
|
<Trigger Property="IsChecked" Value="True">
|
|
<Setter Property="Content" Value="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.toggle.byUser]}" />
|
|
</Trigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ToggleButton.Style>
|
|
</ToggleButton>
|
|
</StackPanel>
|
|
|
|
<DataGrid Grid.Row="2" ItemsSource="{Binding ResultsView}"
|
|
AutoGenerateColumns="False" IsReadOnly="True"
|
|
VirtualizingPanel.IsVirtualizing="True" EnableRowVirtualization="True">
|
|
<DataGrid.RowStyle>
|
|
<Style TargetType="DataGridRow">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding AccessType}" Value="Direct">
|
|
<Setter Property="Background" Value="#EBF5FB" />
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding AccessType}" Value="Group">
|
|
<Setter Property="Background" Value="#EAFAF1" />
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding AccessType}" Value="Inherited">
|
|
<Setter Property="Background" Value="#F4F6F6" />
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsHighPrivilege}" Value="True">
|
|
<Setter Property="FontWeight" Value="Bold" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</DataGrid.RowStyle>
|
|
<DataGrid.GroupStyle>
|
|
<GroupStyle>
|
|
<GroupStyle.HeaderTemplate>
|
|
<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" />
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</GroupStyle.HeaderTemplate>
|
|
</GroupStyle>
|
|
</DataGrid.GroupStyle>
|
|
<DataGrid.Columns>
|
|
<DataGridTemplateColumn Header="User" Width="180">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<StackPanel Orientation="Horizontal">
|
|
<TextBlock Text="{Binding UserLogin}" VerticalAlignment="Center" />
|
|
<Border Background="#F39C12" CornerRadius="3" Padding="4,1" Margin="6,0,0,0"
|
|
VerticalAlignment="Center">
|
|
<Border.Style>
|
|
<Style TargetType="Border">
|
|
<Setter Property="Visibility" Value="Collapsed" />
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsExternalUser}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<TextBlock Text="Guest" FontSize="10" Foreground="White" FontWeight="SemiBold" />
|
|
</Border>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
<DataGridTextColumn Header="Site" Binding="{Binding SiteTitle}" Width="120" />
|
|
<DataGridTextColumn Header="Object" Binding="{Binding ObjectTitle}" Width="140" />
|
|
<DataGridTextColumn Header="Object Type" Binding="{Binding ObjectType}" Width="90" />
|
|
<DataGridTemplateColumn Header="Permission Level" Width="140">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<StackPanel Orientation="Horizontal">
|
|
<TextBlock Text="⚠" Foreground="#E74C3C" Margin="0,0,4,0"
|
|
FontSize="12" VerticalAlignment="Center">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Visibility" Value="Collapsed" />
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsHighPrivilege}" Value="True">
|
|
<Setter Property="Visibility" Value="Visible" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
<TextBlock Text="{Binding PermissionLevel}" VerticalAlignment="Center" />
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
<DataGridTemplateColumn Header="Access Type" Width="110">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<TextBlock>
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Text" Value="{Binding AccessType}" />
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding AccessType}" Value="Direct">
|
|
<Setter Property="Foreground" Value="#1A5276" />
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding AccessType}" Value="Group">
|
|
<Setter Property="Foreground" Value="#1E8449" />
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding AccessType}" Value="Inherited">
|
|
<Setter Property="Foreground" Value="#626567" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
<DataGridTextColumn Header="Granted Through" Binding="{Binding GrantedThrough}" Width="140" />
|
|
</DataGrid.Columns>
|
|
</DataGrid>
|
|
|
|
</Grid>
|
|
|
|
<!-- 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" />
|
|
</StatusBarItem>
|
|
<StatusBarItem Content="{Binding StatusMessage}" />
|
|
</StatusBar>
|
|
|
|
</Grid>
|
|
</UserControl>
|