- Added Export Options GroupBox after Scan Options in UserAccessAuditView.xaml - Added Export Options GroupBox after Display Options in PermissionsView.xaml - Both checkboxes bind to MergePermissions with localized labels via TranslationSource
425 lines
21 KiB
XML
425 lines
21 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">
|
|
|
|
<!-- Mode toggle -->
|
|
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,8">
|
|
<RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.mode.search]}"
|
|
IsChecked="{Binding IsBrowseMode, Converter={StaticResource InverseBoolConverter}}"
|
|
Margin="0,0,12,0" />
|
|
<RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.mode.browse]}"
|
|
IsChecked="{Binding IsBrowseMode}" />
|
|
</StackPanel>
|
|
|
|
<!-- SEARCH MODE PANEL (visible when IsBrowseMode=false) -->
|
|
<GroupBox DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
|
|
<GroupBox.Header>
|
|
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.users]}" />
|
|
</GroupBox.Header>
|
|
<GroupBox.Style>
|
|
<Style TargetType="GroupBox">
|
|
<Setter Property="Visibility" Value="Visible" />
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsBrowseMode}" Value="True">
|
|
<Setter Property="Visibility" Value="Collapsed" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</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.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>
|
|
</StackPanel>
|
|
</GroupBox>
|
|
|
|
<!-- BROWSE MODE PANEL (visible when IsBrowseMode=true) -->
|
|
<GroupBox DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8"
|
|
Visibility="{Binding IsBrowseMode, Converter={StaticResource BoolToVisibilityConverter}}">
|
|
<GroupBox.Header>
|
|
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.grp.browse]}" />
|
|
</GroupBox.Header>
|
|
<DockPanel>
|
|
<!-- Load/Cancel buttons -->
|
|
<Grid DockPanel.Dock="Top" 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=[directory.btn.load]}"
|
|
Command="{Binding LoadDirectoryCommand}" Margin="0,0,4,0" Padding="6,3" />
|
|
<Button Grid.Column="1"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.btn.cancel]}"
|
|
Command="{Binding CancelDirectoryLoadCommand}" Padding="6,3" />
|
|
</Grid>
|
|
|
|
<!-- Include guests checkbox -->
|
|
<CheckBox DockPanel.Dock="Top"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.chk.guests]}"
|
|
IsChecked="{Binding IncludeGuests}" Margin="0,0,0,4" />
|
|
|
|
<!-- Filter text -->
|
|
<TextBox DockPanel.Dock="Top"
|
|
Text="{Binding DirectoryFilterText, UpdateSourceTrigger=PropertyChanged}"
|
|
Margin="0,0,0,4" />
|
|
|
|
<!-- 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"
|
|
Margin="0,0,8,0" />
|
|
<TextBlock FontSize="10" Foreground="Gray">
|
|
<TextBlock.Text>
|
|
<MultiBinding StringFormat="{}{0} {1}">
|
|
<Binding Path="DirectoryUserCount" />
|
|
<Binding Source="{x:Static loc:TranslationSource.Instance}" Path="[directory.status.count]" />
|
|
</MultiBinding>
|
|
</TextBlock.Text>
|
|
</TextBlock>
|
|
</StackPanel>
|
|
|
|
<!-- 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"
|
|
TextWrapping="Wrap" />
|
|
|
|
<!-- Directory DataGrid -->
|
|
<DataGrid x:Name="DirectoryDataGrid"
|
|
ItemsSource="{Binding DirectoryUsersView}"
|
|
AutoGenerateColumns="False" IsReadOnly="True"
|
|
VirtualizingPanel.IsVirtualizing="True" EnableRowVirtualization="True"
|
|
MouseDoubleClick="DirectoryDataGrid_MouseDoubleClick"
|
|
CanUserSortColumns="True"
|
|
SelectionMode="Single" SelectionUnit="FullRow"
|
|
HeadersVisibility="Column" GridLinesVisibility="Horizontal"
|
|
BorderThickness="1" BorderBrush="#DDDDDD">
|
|
<DataGrid.Columns>
|
|
<DataGridTextColumn Header="Name"
|
|
Binding="{Binding DisplayName}" Width="120" />
|
|
<DataGridTextColumn Header="Email"
|
|
Binding="{Binding UserPrincipalName}" Width="140" />
|
|
<DataGridTextColumn Header="Department"
|
|
Binding="{Binding Department}" Width="90" />
|
|
<DataGridTextColumn Header="Job Title"
|
|
Binding="{Binding JobTitle}" Width="90" />
|
|
<DataGridTemplateColumn Header="Type" Width="60">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<TextBlock Text="{Binding UserType}" VerticalAlignment="Center">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding UserType}" Value="Guest">
|
|
<Setter Property="Foreground" Value="#F39C12" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
</DataGrid.Columns>
|
|
</DataGrid>
|
|
</DockPanel>
|
|
</GroupBox>
|
|
|
|
<!-- SHARED: Selected users (visible in both modes) -->
|
|
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,8">
|
|
<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>
|
|
|
|
<!-- Scan Options (always visible) -->
|
|
<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>
|
|
|
|
<!-- Export Options (always visible) -->
|
|
<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>
|
|
|
|
<!-- Run/Export buttons (always visible) -->
|
|
<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>
|