Added Main Project

This commit is contained in:
2025-03-25 22:43:13 +01:00
parent 3272781a12
commit 724c410f36
30 changed files with 3849 additions and 0 deletions
@@ -0,0 +1,263 @@
<Window x:Class="TeamsNetphoneLink.WPF.DashboardWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TeamsNetphoneLink.WPF.MVVM"
Title="TeamsNetphoneLink Dashboard" Height="554" Width="800"
Background="#FF1E1E1E" FontFamily="Segoe UI" ResizeMode="CanMinimize"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:DashboardViewModel />
</Window.DataContext>
<Window.Resources>
<!-- Button Style -->
<Style TargetType="Button">
<Setter Property="Background" Value="#FF0078D4"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#0063B1"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- Status Border Style -->
<Style TargetType="Border" x:Key="StatusBorder">
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="MinWidth" Value="100"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
<!-- Status Text Style -->
<Style TargetType="TextBlock" x:Key="StatusText">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
<!-- ListView Styling -->
<Style TargetType="ListView">
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
</Style>
<!-- GridViewColumnHeader Style -->
<Style TargetType="GridViewColumnHeader">
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Height" Value="32"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<!-- Header -->
<RowDefinition Height="Auto"/>
<!-- Status Panels -->
<RowDefinition Height="*"/>
<!-- Log Section -->
<RowDefinition Height="Auto"/>
<!-- Footer -->
</Grid.RowDefinitions>
<!-- Header -->
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,15">
<TextBlock FontSize="24" FontWeight="Bold" Text="{Binding AppTitle2}" Foreground="White"/>
<Border Visibility="{Binding VersionVisibility}" Background="{Binding VersionBackground}" CornerRadius="12" Padding="12,4" Margin="15,0">
<TextBlock Text="{Binding VersionText}" Foreground="White" FontSize="16"/>
</Border>
</StackPanel>
<!-- Status Indicators -->
<Grid Grid.Row="1" Margin="15,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Left Status Column -->
<StackPanel Grid.Column="0">
<!-- Teams Local API Status -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<TextBlock Text="Teams Local API Status" Width="180" FontWeight="Bold" Foreground="White"/>
<Border Style="{StaticResource StatusBorder}"
Background="{Binding TeamsLocalAPIStatusBackground}"
MouseDown="TeamsLocalAPIBorder_MouseDown">
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsLocalAPIStatusText}"/>
</Border>
</StackPanel>
<!-- Teams Graph API Status -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<TextBlock Text="Teams Graph API Status" Width="180" FontWeight="Bold" Foreground="White"/>
<Border Style="{StaticResource StatusBorder}"
Background="{Binding TeamsGraphAPIStatusBackground}"
MouseDown="TeamsGraphAPIBorder_MouseDown">
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsGraphAPIStatusText}"/>
</Border>
</StackPanel>
<!-- NetPhone CLMGR Status -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<TextBlock Text="NetPhone CLMGR Status" Width="180" FontWeight="Bold" Foreground="White"/>
<Border Style="{StaticResource StatusBorder}"
Background="{Binding NetphoneCLMGRStatusBackground}">
<TextBlock Style="{StaticResource StatusText}" Text="{Binding NetphoneCLMGRStatusText}"/>
</Border>
</StackPanel>
</StackPanel>
<!-- Right Status Column -->
<StackPanel Grid.Column="1">
<!-- Teams Meeting Status -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<TextBlock Text="Teams Meeting" Width="180" FontWeight="Bold" Foreground="White"/>
<Border Style="{StaticResource StatusBorder}"
Background="{Binding TeamsMeetingStatusBackground}">
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsMeetingStatusText}"/>
</Border>
</StackPanel>
<!-- NetPhone Call Status -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<TextBlock Text="NetPhone Call" Width="180" FontWeight="Bold" Foreground="White"/>
<Border Style="{StaticResource StatusBorder}"
Background="{Binding NetphoneCallStatusBackground}">
<TextBlock Style="{StaticResource StatusText}" Text="{Binding NetphoneCallStatusText}"/>
</Border>
</StackPanel>
</StackPanel>
</Grid>
<!-- Log Section -->
<Border Grid.Row="2" Margin="15,0,15,10" Background="#FF252526" CornerRadius="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Activity Log"
FontSize="18"
FontWeight="Bold"
Foreground="White"
Margin="15,10"/>
<ListView x:Name="Log" Grid.Row="1"
ItemsSource="{Binding LogEntries}"
Margin="10,0,10,10"
VirtualizingPanel.IsVirtualizing="True"
IsHitTestVisible="False">
<ListView.View>
<GridView>
<GridViewColumn Width="80" Header="Time">
<GridViewColumn.DisplayMemberBinding>
<Binding Path="Time" StringFormat="{}{0:HH:mm:ss}"/>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
<GridViewColumn Width="60" Header="Type">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}" Foreground="White">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#FF0078D4"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="Warn">
<Setter Property="Foreground" Value="Orange"/>
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="Error">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="120" Header="Subsystem">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Subsystem}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Subsystem}" Value="Netphone">
<Setter Property="Foreground" Value="#E20074"/>
</DataTrigger>
<DataTrigger Binding="{Binding Subsystem}" Value="TeamsGraphAPI">
<Setter Property="Foreground" Value="#4E5FBF"/>
</DataTrigger>
<DataTrigger Binding="{Binding Subsystem}" Value="TeamsLocalAPI">
<Setter Property="Foreground" Value="#4E5FBF"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto" Header="Message"
DisplayMemberBinding="{Binding Message}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Border>
<!-- Footer -->
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="15,0,0,8">
<Button Click="SettingsButton_Click"
Padding="8"
FontSize="18"
VerticalAlignment="Center"
ToolTip="Settings">
<Button.Content>
<Path Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
Fill="White" Stretch="Uniform"/>
</Button.Content>
</Button>
<TextBlock Text="Entwickelt von Jan Krampitz"
VerticalAlignment="Center"
Foreground="White"
FontSize="10"
Margin="15,0"/>
</StackPanel>
</Grid>
</Window>
@@ -0,0 +1,95 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using TeamsNetphoneLink.WPF.MVVM;
namespace TeamsNetphoneLink.WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class DashboardWindow : Window
{
private bool isDarkMode = true;
private Syncronisation sync;
private Task<bool> graphAuthenticationTask;
public DashboardViewModel _dashboardViewModel { get; }
public DashboardWindow(Syncronisation _syncronisation)
{
InitializeComponent();
sync = _syncronisation;
_dashboardViewModel = sync._dashboardViewModel;
DataContext = _dashboardViewModel;
// Set the ViewModel instance & Set the DataContext to the ViewModel
//Set the DataContext to the ViewModel
Log.DataContext = sync.LogEntries;
}
private void ToggleDarkMode(object sender, RoutedEventArgs e)
{
isDarkMode = !isDarkMode;
if (isDarkMode)
{
this.Style = (Style)FindResource("DarkModeStyle");
}
else
{
this.Style = (Style)FindResource("LightModeStyle");
}
}
private void SettingsButton_Click(object sender, RoutedEventArgs e)
{
// Einstellungsfenster öffnen
SettingsWindow settingsWindow = new SettingsWindow(sync);
settingsWindow.ShowDialog();
}
private async void TeamsGraphAPIBorder_MouseDown(object sender, MouseButtonEventArgs e)
{
if (!Teams.TeamsGraph.IsGuid(Settings.Default.AppID) || !Teams.TeamsGraph.IsGuid(Settings.Default.TenantID))
{
SettingsWindow settingsWindow = new SettingsWindow(sync);
settingsWindow.ShowDialog();
return;
};
if (sync.TeamsGraphAuthenticationSemaphore.CurrentCount == 0)
return;
bool saveCredentials = Settings.Default.SaveEntraCredentials;
if (!sync.TeamsGraph.Authenticated)
{
await sync.TeamsGraphAuthenticationSemaphore.WaitAsync();
_ = await sync.GraphAuthenticate(saveCredentials);
sync.TeamsGraphAuthenticationSemaphore.Release();
}
}
private void TeamsLocalAPIBorder_MouseDown(object sender, MouseButtonEventArgs e)
{
if (!Settings.Default.TeamsPermission)
{
var setupLocalTeams = new WPF.SetupLocalTeamsAPI(sync);
setupLocalTeams.ShowDialog();
}
}
private void LogListView_UpdateColumnsWidth(object sender, SizeChangedEventArgs e)
{
ListView _ListView = sender as ListView;
GridView _GridView = _ListView.View as GridView;
var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
for (Int32 i = 1; i < _GridView.Columns.Count; i++)
{
_ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
}
_GridView.Columns[0].Width = _ActualWidth;
}
}
}
@@ -0,0 +1,123 @@
<Window x:Class="TeamsNetphoneLink.WPF.ExceptionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Fehlerbericht" Height="400" Width="600"
Background="#FF1E1E1E" FontFamily="Segoe UI"
WindowStyle="ToolWindow" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" Topmost="True">
<!-- Dark Mode Resources -->
<Window.Resources>
<!-- Button Style -->
<Style TargetType="Button">
<Setter Property="Background" Value="#FF0078D4"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True"/>
</Style>
<!-- TextBox Style -->
<Style TargetType="TextBox">
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="3,0,0,0"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TextBlock Style -->
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Window.Resources>
<Grid Margin="10">
<!-- Fehlertext -->
<TextBlock x:Name="ErrorText"
Text="Es ist ein Fehler aufgetreten!"
FontSize="18"
FontWeight="Bold"
Margin="10,10,10,20"
VerticalAlignment="Top"
Foreground="White"/>
<!-- Modul Feld -->
<StackPanel Orientation="Horizontal" Margin="10,50,10,0">
<TextBlock Text="Modul:" VerticalAlignment="Top" Width="60" Foreground="White" Margin="0,15,0,0"/>
<TextBox x:Name="ModuleText"
FontSize="14"
VerticalAlignment="Top"
Width="480"
Margin="5,5,5,0"
IsEnabled="False"
Background="#FF252526"
Foreground="White" Text="tetet" Height="37"/>
</StackPanel>
<!-- Scrollbare Fehlerdetails -->
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
Margin="10,100,10,50">
<TextBox x:Name="TraceText"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
IsReadOnly="True"
FontSize="14"
Padding="3,3,0,0"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Top"
Height="214"
Background="#FF252526"
Foreground="White" Text="tttt">
<!-- Beispiel für Fehlerdetails -->
</TextBox>
</ScrollViewer>
<!-- Button zum Schließen -->
<Button Content="Schließen"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Width="100"
Margin="10,0,0,10"
Click="CloseButton_Click"/>
<!-- Button zum Issue melden -->
<Button Content="Issue melden"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Width="120"
Margin="0,0,10,10"
Click="ReportButton_Click"/>
</Grid>
</Window>
@@ -0,0 +1,67 @@
using System.Diagnostics;
using System.Media;
using System.Windows;
namespace TeamsNetphoneLink.WPF
{
/// <summary>
/// Interaktionslogik für ExceptionWindow.xaml
/// </summary>
public partial class ExceptionWindow : Window
{
public ExceptionWindow(string module, string trace)
{
InitializeComponent();
ModuleText.Text = module;
TraceText.Text = trace;
SystemSounds.Hand.Play();
}
// Button zum Schließen
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
// Button zum Issue melden
private void ReportButton_Click(object sender, RoutedEventArgs e)
{
// Fehlerdetails aus der TextBox holen
string moduleDetails = ModuleText.Text;
string errorDetails = TraceText.Text;
// URL für das GitHub Issue, Fehlerdetails werden als Parameter angehängt
string url = $"https://git.jan.sx/krjan02/TeamsNetphoneLink/issues/new?body={Uri.EscapeDataString(String.Format("Modul: {0}\n\nTrace:\n{1}", moduleDetails, errorDetails))}";
// GitHub im Standardbrowser öffnen
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
}
public static class ExceptionWindowHelper
{
public static void Show(string classname, string method, string message, string trace)
{
var modulestr = String.Format("{0} - {1}", classname, method);
var errorstr = String.Format("{0}\n{1}", message, trace);
// Use the Dispatcher to show the window on the UI thread
Application.Current.Dispatcher.InvokeAsync(() =>
{
new ExceptionWindow(modulestr, errorstr).Show();
});
}
public static void Show(string classname, string method, string message, string tracemessage, string trace)
{
var modulestr = String.Format("{0} - {1}", classname, method);
var errorstr = String.Format("{0}\n{1}\n{2}", message, tracemessage, trace);
// Use the Dispatcher to show the window on the UI thread
Application.Current.Dispatcher.InvokeAsync(() =>
{
new ExceptionWindow(modulestr, errorstr).Show();
});
}
}
}
@@ -0,0 +1,250 @@
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
namespace TeamsNetphoneLink.WPF.MVVM
{
public static class StatusPanelNames
{
public const string TeamsLocalAPIStatus = nameof(TeamsLocalAPIStatus);
public const string TeamsGraphAPIStatus = nameof(TeamsGraphAPIStatus);
public const string NetphoneCLMGRStatus = nameof(NetphoneCLMGRStatus);
public const string TeamsMeetingStatus = nameof(TeamsMeetingStatus);
public const string NetphoneCallStatus = nameof(NetphoneCallStatus);
}
public class DashboardViewModel : INotifyPropertyChanged
{
// Parameterloser Konstruktor
public DashboardViewModel()
{
// Initialisieren Sie hier Standardwerte, falls erforderlich
TeamsLocalAPIStatusText = "Nicht verbunden";
TeamsLocalAPIStatusBackground = new SolidColorBrush(Colors.Red);
TeamsGraphAPIStatusText = "Konfiguration fehlt";
TeamsGraphAPIStatusBackground = new SolidColorBrush(Colors.Red);
NetphoneCLMGRStatusText = "Nicht verbunden";
NetphoneCLMGRStatusBackground = new SolidColorBrush(Colors.Red);
TeamsMeetingStatusText = "Not Active";
TeamsMeetingStatusBackground = new SolidColorBrush(Colors.DarkOrange);
NetphoneCallStatusText = "Not Active";
NetphoneCallStatusBackground = new SolidColorBrush(Colors.DarkOrange);
AppTitle2 = "Teams Netphone Link";
VersionText = "Aktuell";
VersionVisibility = Visibility.Visible;
VersionBackground = new SolidColorBrush(Colors.Green);
}
private string _appTitle;
private string _versionText;
private Visibility _versionVisibility;
private Brush _versionBackground;
private string _teamsLocalAPIStatusText;
private Brush _teamsLocalAPIStatusBackground;
private string _teamsGraphAPIStatusText;
private Brush _teamsGraphAPIStatusBackground;
private string _netphoneCLMGRStatusText;
private Brush _netphoneCLMGRStatusBackground;
private string _teamsMeetingStatusText;
private Brush _teamsMeetingStatusBackground;
private string _netphoneCallStatusText;
private Brush _netphoneCallStatusBackground;
public string TeamsLocalAPIStatusText
{
get => _teamsLocalAPIStatusText;
set
{
_teamsLocalAPIStatusText = value;
OnPropertyChanged();
}
}
public Brush TeamsLocalAPIStatusBackground
{
get => _teamsLocalAPIStatusBackground;
set
{
_teamsLocalAPIStatusBackground = value;
OnPropertyChanged();
}
}
public string TeamsGraphAPIStatusText
{
get => _teamsGraphAPIStatusText;
set
{
_teamsGraphAPIStatusText = value;
OnPropertyChanged();
}
}
public Brush TeamsGraphAPIStatusBackground
{
get => _teamsGraphAPIStatusBackground;
set
{
_teamsGraphAPIStatusBackground = value;
OnPropertyChanged();
}
}
public string NetphoneCLMGRStatusText
{
get => _netphoneCLMGRStatusText;
set
{
_netphoneCLMGRStatusText = value;
OnPropertyChanged();
}
}
public Brush NetphoneCLMGRStatusBackground
{
get => _netphoneCLMGRStatusBackground;
set
{
_netphoneCLMGRStatusBackground = value;
OnPropertyChanged();
}
}
public string TeamsMeetingStatusText
{
get => _teamsMeetingStatusText;
set
{
_teamsMeetingStatusText = value;
OnPropertyChanged();
}
}
public Brush TeamsMeetingStatusBackground
{
get => _teamsMeetingStatusBackground;
set
{
_teamsMeetingStatusBackground = value;
OnPropertyChanged();
}
}
public string NetphoneCallStatusText
{
get => _netphoneCallStatusText;
set
{
_netphoneCallStatusText = value;
OnPropertyChanged();
}
}
public Brush NetphoneCallStatusBackground
{
get => _netphoneCallStatusBackground;
set
{
_netphoneCallStatusBackground = value;
OnPropertyChanged();
}
}
public string AppTitle2
{
get => _appTitle;
set
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
_appTitle = value;
OnPropertyChanged();
});
}
}
public string VersionText
{
get => _versionText;
set
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
_versionText = value;
OnPropertyChanged();
});
}
}
public Visibility VersionVisibility
{
get => _versionVisibility;
set
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
_versionVisibility = value;
OnPropertyChanged();
});
}
}
public Brush VersionBackground
{
get => _versionBackground;
set
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
_versionBackground = value;
OnPropertyChanged();
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// Method to update status panel
public void UpdateStatusPanel(string statusPanelName, Color color, string text)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
// Get the type of the ViewModel
var type = GetType();
// Construct property names for Text and Background
string textPropertyName = $"{statusPanelName}Text";
string backgroundPropertyName = $"{statusPanelName}Background";
// Get the properties using reflection
PropertyInfo textProperty = type.GetProperty(textPropertyName); //CS8600
PropertyInfo backgroundProperty = type.GetProperty(backgroundPropertyName); //CS8600
if (textProperty != null && backgroundProperty != null)
{
// Update the Text property
textProperty.SetValue(this, text);
// Update the Background property
backgroundProperty.SetValue(this, new SolidColorBrush(color));
}
else
{
throw new ArgumentException($"Invalid status panel name: {statusPanelName}");
}
});
}
}
}
@@ -0,0 +1,94 @@
using Emoji.Wpf;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Threading;
namespace TeamsNetphoneLink.WPF.MVVM
{
public class FinishPanelViewModel : INotifyPropertyChanged
{
// Parameterloser Konstruktor
public FinishPanelViewModel()
{
// Initialisieren Sie hier Standardwerte, falls erforderlich
FinishPanelEnabled = false;
FinishPanelFinishTextColor = new SolidColorBrush(Colors.DarkOrange);
FinishPanelFinishTextText = "Authentifizierung ausstehend...";
FinishPanelEffect = new TintEffect { Tint = Colors.DarkGray };
}
private bool _finishPanelEnabled;
private Brush _finishPanelFinishTextColor;
private string _finishPanelFinishTextText;
private Effect _finishPanelEffect;
public bool FinishPanelEnabled
{
get => _finishPanelEnabled;
set
{
System.Windows.Application.Current.Dispatcher.Invoke(delegate
{
_finishPanelEnabled = value;
OnPropertyChanged();
});
}
}
public Brush FinishPanelFinishTextColor
{
get => _finishPanelFinishTextColor;
set
{
System.Windows.Application.Current.Dispatcher.Invoke(delegate
{
_finishPanelFinishTextColor = value;
OnPropertyChanged();
});
}
}
public string FinishPanelFinishTextText
{
get => _finishPanelFinishTextText;
set
{
System.Windows.Application.Current.Dispatcher.Invoke(delegate
{
_finishPanelFinishTextText = value;
OnPropertyChanged();
});
}
}
public Effect FinishPanelEffect
{
get => _finishPanelEffect;
set
{
System.Windows.Application.Current.Dispatcher.Invoke(delegate
{
_finishPanelEffect = value;
OnPropertyChanged();
});
}
}
public void SetFinishPanelFinishTextColor(Color color)
{
System.Windows.Application.Current.Dispatcher.Invoke(delegate
{
FinishPanelFinishTextColor = new SolidColorBrush((Color)color);
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
@@ -0,0 +1,75 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace TeamsNetphoneLink.WPF.MVVM
{
public class LogEntry
{
public DateTime Time { get; set; }
public string Subsystem { get; set; }
public string Type { get; set; }
public string Message { get; set; }
}
public class LogViewModel : INotifyPropertyChanged
{
// Collection to hold log entries
public ObservableCollection<LogEntry> LogEntries { get; } = new ObservableCollection<LogEntry>();
// Add a log entry
public void AddLogEntry(string entry)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = "Info", Subsystem = "NONE", Message = entry });
});
}
// Add a log entry
public void AddLogEntry(string subsystem, string entry)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = "Info", Subsystem = subsystem, Message = entry });
});
}
// Add a log entry
public void AddLogEntry(string type, string subsystem, string entry)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = type, Subsystem = subsystem, Message = entry });
});
}
public void AddLogEntry(DateTime time, string subsystem, string message)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
LogEntries.Add(new LogEntry { Time = time, Type = "Info", Subsystem = subsystem, Message = message });
});
}
// Clear all log entries
public void ClearLogEntries()
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
LogEntries.Clear();
});
}
// Implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
@@ -0,0 +1,143 @@
<Window x:Class="TeamsNetphoneLink.WPF.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Einstellungen" Height="373" Width="500"
WindowStyle="ToolWindow" ResizeMode="NoResize"
Background="#FF1E1E1E" FontFamily="Segoe UI"
WindowStartupLocation="CenterScreen">
<!-- Dark Mode Resources -->
<Window.Resources>
<!-- Button Style -->
<Style TargetType="Button">
<Setter Property="Background" Value="#FF0078D4"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}"
Opacity="1">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<!-- Hover Effect -->
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#0066B3"/>
</Trigger>
<!-- Pressed Effect -->
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="#004D86"/>
<Setter TargetName="border" Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.98" ScaleY="0.98"/>
</Setter.Value>
</Setter>
</Trigger>
<!-- Disabled State -->
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Background" Value="#3A3A3A"/>
<Setter TargetName="border" Property="Opacity" Value="0.7"/>
<Setter Property="Foreground" Value="#A0A0A0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True"/>
</Style>
<!-- TextBox Style -->
<Style TargetType="TextBox">
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="3,0,0,0"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="0,0,0,0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- CheckBox Style -->
<Style TargetType="CheckBox">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- TextBlock Style -->
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Window.Resources>
<Grid Margin="20">
<StackPanel Orientation="Vertical">
<!-- Title -->
<TextBlock Text="Einstellungen" FontSize="24" FontWeight="SemiBold" Margin="0,0,0,20"/>
<!-- Microsoft 365 Tenant ID -->
<StackPanel Orientation="Horizontal" Margin="0,10">
<TextBlock Text="Tenant ID" VerticalAlignment="Center" Width="70" FontSize="14"/>
<TextBox x:Name="TenantIDTextBox" Width="372" Height="30" FontSize="14" TextChanged="TextBox_TextChanged" Text="dddddd"/>
</StackPanel>
<!-- Microsoft 365 App ID -->
<StackPanel Orientation="Horizontal" Margin="0,10">
<TextBlock Text="App ID" VerticalAlignment="Center" Width="70" FontSize="14"/>
<TextBox x:Name="AppIDTextBox" Width="372" Height="30" TextChanged="TextBox_TextChanged"/>
</StackPanel>
<!-- Toggle SaveEntraCredentials -->
<StackPanel Orientation="Horizontal" Margin="0,10">
<CheckBox x:Name="SaveEntraCredentialsCheck" VerticalAlignment="Center"/>
<TextBlock Text="Entra Login speichern" Width="137" FontSize="14" Margin="10,0,0,0"/>
</StackPanel>
<!-- Toggle UseGraphForMeetingState -->
<StackPanel Orientation="Horizontal" Margin="0,10" ToolTip="Nutzt statt der lokalen Teams API die Graph API um festzustellen das ein Meeting läuft">
<CheckBox x:Name="UseGraphForMeetingStateCheck"/>
<TextBlock Text="Graph für den Meeting Status verwenden" VerticalAlignment="Center" Width="259" FontSize="14" Margin="10,0,0,0"/>
</StackPanel>
<!-- Save and Reset Buttons -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,30,0,0">
<Button x:Name="ResetButton" Background="DarkRed" Content="Alles zurücksetzen" Width="140" Height="35" Margin="0,0,10,0" Click="ResetButton_Click"/>
<Button x:Name="SaveButton" Content="Speichern" Width="100" Height="35" Click="SaveButton_Click" IsEnabled="False"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
@@ -0,0 +1,132 @@
using Microsoft.Identity.Client.NativeInterop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using TeamsNetphoneLink;
namespace TeamsNetphoneLink.WPF
{
/// <summary>
/// Interaktionslogik für SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow : Window
{
Syncronisation _sync;
public SettingsWindow(Syncronisation sync)
{
_sync = sync;
InitializeComponent();
TenantIDTextBox.Text = Settings.Default.TenantID;
AppIDTextBox.Text = Settings.Default.AppID;
SaveEntraCredentialsCheck.IsChecked = Settings.Default.SaveEntraCredentials;
UseGraphForMeetingStateCheck.IsChecked = Settings.Default.UseGraphForMeetingState;
}
private void RestartApplication()
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
var startInfo = new ProcessStartInfo
{
FileName = exePath,
UseShellExecute = true
};
Process.Start(startInfo);
Application.Current.Shutdown();
}
// Speichern der Einstellungen
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
string tenantId = TenantIDTextBox.Text.Replace(" ", "");
string appId = AppIDTextBox.Text.Replace(" ", "");
bool SaveEntraCredentials = SaveEntraCredentialsCheck.IsChecked.HasValue ? SaveEntraCredentialsCheck.IsChecked.Value : false;
bool UseGraphForMeetingState = UseGraphForMeetingStateCheck.IsChecked.HasValue ? UseGraphForMeetingStateCheck.IsChecked.Value : false;
if (SaveEntraCredentials && !ShowSecurityWarningMessage())
return;
Settings.Default.TenantID = tenantId;
Settings.Default.AppID = appId;
Settings.Default.SaveEntraCredentials = SaveEntraCredentials;
Settings.Default.UseGraphForMeetingState = UseGraphForMeetingState;
Settings.Default.Save();
RestartApplication();
}
// Reset der Einstellungen
private void ResetButton_Click(object sender, RoutedEventArgs e)
{
Settings.Default.Reset();
RestartApplication();
}
private void SaveEntraCredentialsCheck_Checked(object sender, RoutedEventArgs e)
{
//ShowSecurityWarningMessage();
}
public bool ShowSecurityWarningMessage()
{
string message =
"Die Anmeldedaten werden mit dem MSAL Cache unter Verwendung von DPAPI (Data Protection API) gespeichert. " +
"DPAPI bietet eine sichere Verschlüsselung der Daten auf Benutzerebene, sodass die Anmeldedaten nur für das Benutzerkonto zugänglich sind, " +
"unter dem sie gespeichert wurden.\n\n" +
"Allerdings ist zu beachten, dass theoretisch jede Anwendung mit derselben App-ID auf den MSAL Cache zugreifen könnte. " +
"Das liegt daran, dass der Cache anhand der App-ID identifiziert wird. Wenn eine andere Anwendung dieselbe App-ID verwendet, " +
"könnte sie versuchen, auf die gespeicherten Anmeldedaten zuzugreifen.\n\n" +
"Bewertung des Sicherheitsrisikos:\n" +
"- In einer vertrauenswürdigen Umgebung ist das Risiko gering.\n" +
"- Die App-ID sollte geheim gehalten werden, um unbefugten Zugriff zu verhindern.\n" +
"- DPAPI bietet eine starke Verschlüsselung, solange das Benutzerkonto nicht kompromittiert ist.\n\n" +
"Unter normalen Umständen stellt die Verwendung des MSAL Caches kein signifikantes Sicherheitsrisiko dar. " +
"Es ist jedoch wichtig, die App-ID geheim zu halten und sicherzustellen, dass nur vertrauenswürdige Anwendungen dieselbe App-ID verwenden.\n\n" +
"Sollen die Einstellungen gespeichert werden?.\n\n";
var result = MessageBox.Show(
message,
"Sicherheitshinweis zur Speicherung von Anmeldedaten",
MessageBoxButton.YesNo,
MessageBoxImage.Warning
);
if (result == MessageBoxResult.Yes)
return true;
return false;
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
ValidateInput();
}
private void ValidateInput()
{
if (this.IsInitialized)
{
bool isAppIDValid = Teams.TeamsGraph.IsGuid(AppIDTextBox.Text);
bool isTenantIDValid = Teams.TeamsGraph.IsGuid(TenantIDTextBox.Text);
// Set border color based on validation
AppIDTextBox.BorderBrush = isAppIDValid ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
TenantIDTextBox.BorderBrush = isTenantIDValid ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
// Enable Save button only if both fields are valid
SaveButton.IsEnabled = isAppIDValid && isTenantIDValid;
}
}
}
}
@@ -0,0 +1,83 @@
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Wpf="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:local="clr-namespace:TeamsNetphoneLink.WPF.MVVM" x:Class="TeamsNetphoneLink.WPF.SetupLocalTeamsAPI"
WindowStyle="ToolWindow" ResizeMode="NoResize"
Background="#FF1E1E1E" FontFamily="Segoe UI"
Title="Teams API Integration" Height="497" Width="525">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="#FF0078D4"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True"/>
</Style>
<!-- TextBlock Style -->
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<TextBlock Text="Bitte folgende Schritte durchführen, um die Teams API zu aktivieren und damit die Anwendung zu genehmigen." Margin="10" Height="45" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Bold" FontSize="16"/>
<StackPanel x:Name="Step1Panel" Margin="10">
<TextBlock Text="1. Aktivieren der Teams Drittanbieter API:"/>
<!-- Hyperlink in einem TextBlock unterbringen -->
<TextBlock>
<Hyperlink x:Name="Step1Link" NavigateUri="https://support.microsoft.com/de-de/office/herstellen-einer-verbindung-mit-drittanbieterger%C3%A4ten-in-microsoft-teams-aabca9f2-47bb-407f-9f9b-81a104a883d6" RequestNavigate="Step1Link_RequestNavigate">
<Run Text="Anleitung für die Aktivierung der Drittanbieter API"/>
</Hyperlink>
</TextBlock>
<Button x:Name="Step1Button" Content="Drittanbieter-API aktiviert" Width="200" Margin="10" Click="Step1Button_Click"/>
</StackPanel>
<StackPanel x:Name="Step2Panel" Margin="10" Height="61" IsEnabled="False" >
<StackPanel.Effect >
<Wpf:TintEffect Tint="#FFA4A0A0"/>
</StackPanel.Effect>
<TextBlock Text="2. Starten Sie ein Teams Meeting mit sich selbst:"/>
<Button x:Name="Step2Button" Content="Teams Meeting ist gestartet" Width="200" Margin="10" Click="Step2Button_Click"/>
<TextBlock x:Name="Step2Status" Margin="10"/>
</StackPanel>
<!-- Schritt 3: Authentifizieren -->
<StackPanel x:Name="Step3Panel" Margin="10" IsEnabled="False" >
<StackPanel.Effect >
<Wpf:TintEffect Tint="#FFA4A0A0"/>
</StackPanel.Effect>
<TextBlock Text="3. Zulassen der Verbindungsanforderung:"/>
<TextBlock Margin="10,0,10,0" Height="34" TextWrapping="Wrap" TextAlignment="Center" FontSize="12"><Run Text="Nach dem Klick auf dem Button wird in Teams eine Anforderung sichtbar, diese bitte zulassen"/></TextBlock>
<Button x:Name="Step3Button" Content="Verbindungsanforderung senden" Width="227" Margin="10" Click="Step3Button_Click"/>
</StackPanel>
<!-- Schritt 3: Authentifizieren -->
<StackPanel x:Name="FinishPanel" Margin="10" IsEnabled="{Binding FinishPanelEnabled}" Effect="{Binding FinishPanelEffect}" >
<TextBlock Foreground="{Binding FinishPanelFinishTextColor}" Text="{Binding FinishPanelFinishTextText}" FontWeight="Bold" HorizontalAlignment="Center"/>
<Button x:Name="FinishButton" Content="Abschließen" Width="200" Margin="10" Click="FinishButton_Click"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
@@ -0,0 +1,83 @@
using Emoji.Wpf;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TeamsNetphoneLink.WPF.MVVM;
namespace TeamsNetphoneLink.WPF
{
/// <summary>
/// Interaktionslogik für SetupLocalTeamsAPI.xaml
/// </summary>
public partial class SetupLocalTeamsAPI : Window
{
private Teams.TeamsLocalAPI teamsLocalAPI;
public FinishPanelViewModel finishPanelViewModel { get; }
public SetupLocalTeamsAPI(Syncronisation sync)
{
teamsLocalAPI = sync.TeamsEvents;
finishPanelViewModel = sync.FinishPanelViewModel;
DataContext = finishPanelViewModel;
InitializeComponent();
}
private void Step1Link_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
// Öffnet den Link, um die Teams API zu aktivieren
Process.Start(new ProcessStartInfo(e.Uri.ToString().Replace("&", "^&")) { UseShellExecute = true });
e.Handled = true;
}
private void Step1Button_Click(object sender, RoutedEventArgs e)
{
// Schritt 1 abgeschlossen - Schalte auf den nächsten Schritt
Step1Panel.IsEnabled = false;
Step2Panel.IsEnabled = true;
Step1Panel.Effect = new TintEffect { Tint = Colors.DarkGray };
Step2Panel.Effect = null;
}
// Schritt 2: Teams Meeting starten
private void Step2Button_Click(object sender, RoutedEventArgs e)
{
// Öffne die URL, um ein Meeting zu starten (Hier muss der genaue Link oder die API-Integration erfolgen)
Step2Panel.IsEnabled = false;
Step3Panel.IsEnabled = true;
Step2Panel.Effect = new TintEffect { Tint = Colors.DarkGray };
Step3Panel.Effect = null;
}
// Schritt 3: Authentifizierung durchführen
private async void Step3Button_Click(object sender, RoutedEventArgs e)
{
// Simuliere den Authentifizierungsprozess
Step3Button.Content = "Authentifizierung gesendet...";
Step3Button.IsEnabled = false;
this.finishPanelViewModel.FinishPanelFinishTextText = "Authentifizierung läuft...";
this.finishPanelViewModel.FinishPanelFinishTextColor = new SolidColorBrush(Colors.Orange);
teamsLocalAPI.SendDummyCommand();
}
private void FinishButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}
+183
View File
@@ -0,0 +1,183 @@
<Window x:Class="TeamsNetphoneLink.WPF.UpdateWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Update verfügbar" Height="514" Width="833"
Background="#FF1E1E1E" FontFamily="Segoe UI"
WindowStyle="ToolWindow" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" Closing="Window_Closing">
<!-- Dark Mode Resources -->
<Window.Resources>
<!-- Button Style -->
<Style TargetType="Button">
<Setter Property="Background" Value="#FF0078D4"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True"/>
</Style>
<!-- Special Style for Markdown TextBox -->
<Style x:Key="MarkdownTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="Foreground" Value="#FFD4D4D4"/>
<Setter Property="Background" Value="#FF1E1E1E"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
</Style>
<!-- TextBox Style -->
<Style TargetType="TextBox">
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ProgressBar Style -->
<Style TargetType="ProgressBar">
<Setter Property="Background" Value="#FF252526"/>
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="#FF0078D4"/>
<Setter Property="Height" Value="10"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Grid x:Name="Root">
<Border x:Name="PART_Track"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="5"/>
<Border x:Name="PART_Indicator"
CornerRadius="5"
HorizontalAlignment="Left">
<Border.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="#FF0078D4" Offset="0"/>
<GradientStop Color="#FF00B4FF" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TextBlock Style -->
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- Title TextBlock Style -->
<Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Margin" Value="10,10,10,20"/>
</Style>
<!-- Status TextBlock Style -->
<Style x:Key="StatusTextStyle" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Margin" Value="10,0,10,5"/>
<Setter Property="Foreground" Value="#FFD4D4D4"/>
</Style>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Title -->
<TextBlock x:Name="TitleText"
Text="Es ist ein neues Update verfügbar"
Style="{StaticResource TitleTextStyle}"
Grid.Row="0"/>
<!-- Markdown Release Notes -->
<Border Grid.Row="1"
Margin="10,0,10,10"
Background="#FF252526"
CornerRadius="4"
BorderBrush="#FF3E3E40"
BorderThickness="1">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Padding="5">
<TextBox x:Name="ReleaseNotesTextBox"
Style="{StaticResource MarkdownTextBoxStyle}"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Top"
Text="Lade Release Notes..."/>
</ScrollViewer>
</Border>
<!-- Progress Status -->
<TextBlock x:Name="ProgressStatusText"
Text=""
Style="{StaticResource StatusTextStyle}"
Grid.Row="2"/>
<!-- Progress Bar -->
<ProgressBar x:Name="UpdateProgress"
Margin="10,0,10,10"
Grid.Row="3"/>
<!-- Buttons -->
<StackPanel Grid.Row="4"
HorizontalAlignment="Right"
Orientation="Horizontal"
Margin="0,10,0,0">
<Button x:Name="CloseButton"
Content="Schließen"
Width="100"
Margin="0,0,10,0"
Click="CloseButton_Click"/>
<Button x:Name="UpdateButton"
Content="Update durchführen"
Width="150"
Background="Green"
Click="UpdateButton_Click"/>
</StackPanel>
</Grid>
</Window>
@@ -0,0 +1,229 @@
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Windows;
namespace TeamsNetphoneLink.WPF
{
/// <summary>
/// Interaktionslogik für ExceptionWindow.xaml
/// </summary>
public partial class UpdateWindow : Window
{
private bool _updateRunning = false;
private string _currentVersion;
private string _latestVersion;
public UpdateWindow(string currentVersion, string latestVersion)
{
InitializeComponent();
// Step 1: Check for updates
_currentVersion = currentVersion;
_latestVersion = latestVersion;
TitleText.Text = $"Es ist ein Update von {currentVersion} auf {latestVersion} verfügbar";
LoadReleaseNotesAsync();
}
private async void LoadReleaseNotesAsync()
{
try
{
var notes = await GetReleaseNotes();
Dispatcher.Invoke(() =>
{
ReleaseNotesTextBox.Text = notes;
});
}
catch (Exception ex)
{
Dispatcher.Invoke(() =>
{
ReleaseNotesTextBox.Text = $"Failed to load release notes: {ex.Message}";
});
}
}
// Button zum Schließen
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
// Button zum Updaten
private void UpdateButton_Click(object sender, RoutedEventArgs e)
{
this._updateRunning = true;
this.UpdateButton.IsEnabled = false;
this.CloseButton.IsEnabled = false;
_ = UpdateApplicationAsync();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (this._updateRunning)
{
e.Cancel = true;
}
}
private void StartUpdater(string tempExtractPath)
{
string appPath = AppDomain.CurrentDomain.BaseDirectory;
string updaterPathEXE = Path.Combine(appPath, "TeamsNetphoneLinkUpdater.exe");
string updaterPathDLL = Path.Combine(appPath, "TeamsNetphoneLinkUpdater.dll");
string newUpdaterPathEXE = Path.Combine(tempExtractPath, "TeamsNetphoneLinkUpdater.exe");
string newUpdaterPathDLL = Path.Combine(tempExtractPath, "TeamsNetphoneLinkUpdater.dll");
//Replace the updater if it exists in the new release
if (File.Exists(newUpdaterPathEXE))
{
File.Copy(newUpdaterPathEXE, updaterPathEXE, true);
}
if (File.Exists(newUpdaterPathDLL))
{
File.Copy(newUpdaterPathDLL, updaterPathDLL, true);
}
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = updaterPathEXE;
p.StartInfo.ArgumentList.Add(appPath);
p.StartInfo.ArgumentList.Add(tempExtractPath);
p.StartInfo.UseShellExecute = true; // This will start the process in a new shell
p.Start();
// Exit the application
Application.Current.Shutdown();
}
private static void UnzipRelease(string zipPath, string extractPath)
{
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
private async Task UpdateApplicationAsync()
{
try
{
if (_latestVersion != _currentVersion)
{
UpdateProgress.Value = 0;
// Step 2: Download the new release
string downloadUrl = $"https://git.jan.sx/krjan02/TeamsNetphoneLink/releases/download/latest/NetphoneTeamsLink{_latestVersion}.zip";
var tempZipPath = Path.GetTempFileName();
tempZipPath = tempZipPath + ".TEAMSNETPHONEUPDATE";
Console.WriteLine(tempZipPath);
await DownloadReleaseAsync(downloadUrl, tempZipPath);
// Step 3: Unzip the release
var tempExtractPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + "TEAMSNETPHONEUPDATE");
Directory.CreateDirectory(tempExtractPath);
UnzipRelease(tempZipPath, tempExtractPath);
File.Delete(tempZipPath);
// Step 4: Replace Updater.exe and run the updater
StartUpdater(tempExtractPath);
}
}
catch (Exception ex)
{
Dispatcher.Invoke(() => ProgressStatusText.Text = $"Error: {ex.Message}");
}
}
private async Task<string> GetReleaseNotes()
{
string apiUrl = $"https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/tags/{_latestVersion}";
using (HttpClient client = new HttpClient())
{
// Send a GET request to the Gitea API
HttpResponseMessage response = await client.GetAsync(apiUrl);
response.EnsureSuccessStatusCode();
// Parse the JSON response
string jsonResponse = await response.Content.ReadAsStringAsync();
using (JsonDocument doc = JsonDocument.Parse(jsonResponse))
{
JsonElement root = doc.RootElement;
JsonElement body = root.GetProperty("body");
return body.GetString();
}
}
}
private async Task<long> GetFileSize()
{
string apiUrl = $"https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/tags/{_latestVersion}";
using (HttpClient client = new HttpClient())
{
// Send a GET request to the Gitea API
HttpResponseMessage response = await client.GetAsync(apiUrl);
response.EnsureSuccessStatusCode();
// Parse the JSON response
string jsonResponse = await response.Content.ReadAsStringAsync();
using (JsonDocument doc = JsonDocument.Parse(jsonResponse))
{
JsonElement root = doc.RootElement;
JsonElement assets = root.GetProperty("assets");
// Iterate through the assets to find the file
foreach (JsonElement asset in assets.EnumerateArray())
{
string name = asset.GetProperty("name").GetString();
if (name == $"NetphoneTeamsLink{_latestVersion}.zip")
{
long size = asset.GetProperty("size").GetInt64();
return size;
}
}
}
}
return -1;
}
private async Task DownloadReleaseAsync(string downloadUrl, string destinationPath)
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var totalBytes = await GetFileSize();
var downloadedBytes = 0L;
var buffer = new byte[8192];
using (var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
using (var stream = await response.Content.ReadAsStreamAsync())
{
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
fileStream.Write(buffer, 0, bytesRead);
downloadedBytes += bytesRead;
// Update progress in the UI
Dispatcher.Invoke(() =>
{
ProgressStatusText.Text = $"Update wird heruntergeladen... ({downloadedBytes/1000}kB/{totalBytes/1000}kB)";
UpdateProgress.Value = (double)downloadedBytes / totalBytes * 100;
//StatusText.Text = $"Downloading... {DownloadProgress.Value:F2}%";
});
}
}
}
}
}
}