diff --git a/Desktop.Core/Interfaces/IRemoteControlAccessService.cs b/Desktop.Core/Interfaces/IRemoteControlAccessService.cs index 9cd70033..9ed65de0 100644 --- a/Desktop.Core/Interfaces/IRemoteControlAccessService.cs +++ b/Desktop.Core/Interfaces/IRemoteControlAccessService.cs @@ -7,6 +7,6 @@ namespace Remotely.Desktop.Core.Interfaces { public interface IRemoteControlAccessService { - Task PromptForAccess(); + Task PromptForAccess(string requesterName, string organizationName); } } diff --git a/Desktop.Core/Services/CasterSocket.cs b/Desktop.Core/Services/CasterSocket.cs index 9344d6f8..b3562bb1 100644 --- a/Desktop.Core/Services/CasterSocket.cs +++ b/Desktop.Core/Services/CasterSocket.cs @@ -159,13 +159,18 @@ namespace Remotely.Desktop.Core.Services await DisconnectAllViewers(); }); - Connection.On("GetScreenCast", async (string viewerID, string requesterName, bool notifyUser, bool enforceAttendedAccess) => + Connection.On("GetScreenCast", async ( + string viewerID, + string requesterName, + bool notifyUser, + bool enforceAttendedAccess, + string organizationName) => { try { if (enforceAttendedAccess) { - var result = await RemoteControlAccessService.PromptForAccess(); + var result = await RemoteControlAccessService.PromptForAccess(requesterName, organizationName); if (!result) { await SendConnectionRequestDenied(viewerID); diff --git a/Desktop.Linux/Services/RemoteControlAccessServiceLinux.cs b/Desktop.Linux/Services/RemoteControlAccessServiceLinux.cs index 7fca20f9..8885d9ef 100644 --- a/Desktop.Linux/Services/RemoteControlAccessServiceLinux.cs +++ b/Desktop.Linux/Services/RemoteControlAccessServiceLinux.cs @@ -1,6 +1,11 @@ -using Remotely.Desktop.Core.Interfaces; +using Avalonia.Threading; +using Remotely.Desktop.Core.Interfaces; +using Remotely.Desktop.Linux.ViewModels; +using Remotely.Desktop.Linux.Views; +using Remotely.Shared.Helpers; using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; @@ -8,9 +13,29 @@ namespace Remotely.Desktop.Linux.Services { public class RemoteControlAccessServiceLinux : IRemoteControlAccessService { - public Task PromptForAccess() + public async Task PromptForAccess(string requesterName, string organizationName) { - throw new NotImplementedException(); + var result = await Dispatcher.UIThread.InvokeAsync(async () => + { + var promptWindow = new PromptForAccessWindow(); + var viewModel = promptWindow.DataContext as PromptForAccessWindowViewModel; + if (!string.IsNullOrWhiteSpace(requesterName)) + { + viewModel.RequesterName = requesterName; + } + if (!string.IsNullOrWhiteSpace(organizationName)) + { + viewModel.OrganizationName = organizationName; + } + + promptWindow.Show(); + await TaskHelper.DelayUntilAsync(() => !promptWindow.IsVisible, TimeSpan.MaxValue); + + return viewModel.PromptResult; + }); + + + return result; } } } diff --git a/Desktop.Linux/ViewModels/ChatWindowViewModel.cs b/Desktop.Linux/ViewModels/ChatWindowViewModel.cs index aacc098a..4ff4aa75 100644 --- a/Desktop.Linux/ViewModels/ChatWindowViewModel.cs +++ b/Desktop.Linux/ViewModels/ChatWindowViewModel.cs @@ -12,9 +12,9 @@ namespace Remotely.Desktop.Linux.ViewModels { public class ChatWindowViewModel : ReactiveViewModel { - private string inputText; - private string organizationName = "your IT provider"; - private string senderName = "a technician"; + private string _inputText; + private string _organizationName = "your IT provider"; + private string _senderName = "a technician"; public ObservableCollection ChatMessages { get; } = new ObservableCollection(); public string ChatSessionHeader => $"Chat session with {OrganizationName}"; @@ -25,8 +25,8 @@ namespace Remotely.Desktop.Linux.ViewModels }); public string InputText { - get => inputText; - set => this.RaiseAndSetIfChanged(ref inputText, value); + get => _inputText; + set => this.RaiseAndSetIfChanged(ref _inputText, value); } public ICommand MinimizeCommand => new Executor((param) => @@ -36,10 +36,10 @@ namespace Remotely.Desktop.Linux.ViewModels public string OrganizationName { - get => organizationName; + get => _organizationName; set { - this.RaiseAndSetIfChanged(ref organizationName, value); + this.RaiseAndSetIfChanged(ref _organizationName, value); this.RaisePropertyChanged(nameof(ChatSessionHeader)); } } @@ -48,8 +48,8 @@ namespace Remotely.Desktop.Linux.ViewModels public string SenderName { - get => senderName; - set => this.RaiseAndSetIfChanged(ref senderName, value); + get => _senderName; + set => this.RaiseAndSetIfChanged(ref _senderName, value); } public async Task SendChatMessage() diff --git a/Desktop.Linux/ViewModels/PromptForAccessWindowViewModel.cs b/Desktop.Linux/ViewModels/PromptForAccessWindowViewModel.cs new file mode 100644 index 00000000..bbd1dcd5 --- /dev/null +++ b/Desktop.Linux/ViewModels/PromptForAccessWindowViewModel.cs @@ -0,0 +1,56 @@ +using Avalonia.Controls; +using ReactiveUI; +using Remotely.Desktop.Linux.Services; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Input; + +namespace Remotely.Desktop.Linux.ViewModels +{ + public class PromptForAccessWindowViewModel : ReactiveViewModel + { + private string _organizationName = "your IT provider"; + private string _requesterName = "a technician"; + public ICommand CloseCommand => new Executor((param) => + { + (param as Window)?.Close(); + }); + + public ICommand MinimizeCommand => new Executor((param) => + { + (param as Window).WindowState = WindowState.Minimized; + }); + + public string OrganizationName + { + get => _organizationName; + set => this.RaiseAndSetIfChanged(ref _organizationName, value); + } + + public bool PromptResult { get; set; } + + public string RequesterName + { + get => _requesterName; + set => this.RaiseAndSetIfChanged(ref _requesterName, value); + } + public ICommand SetResultNo => new Executor(param => + { + if (param is Window promptWindow) + { + PromptResult = false; + promptWindow.Close(); + } + }); + + public ICommand SetResultYes => new Executor(param => + { + if (param is Window promptWindow) + { + PromptResult = true; + promptWindow.Close(); + } + }); + } +} diff --git a/Desktop.Linux/Views/ChatWindow.axaml b/Desktop.Linux/Views/ChatWindow.axaml index f2fed410..919ae316 100644 --- a/Desktop.Linux/Views/ChatWindow.axaml +++ b/Desktop.Linux/Views/ChatWindow.axaml @@ -38,7 +38,7 @@ + + + + + diff --git a/Desktop.Linux/Views/PromptForAccessWindow.axaml.cs b/Desktop.Linux/Views/PromptForAccessWindow.axaml.cs new file mode 100644 index 00000000..064fc906 --- /dev/null +++ b/Desktop.Linux/Views/PromptForAccessWindow.axaml.cs @@ -0,0 +1,39 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using System; + +namespace Remotely.Desktop.Linux.Views +{ + public class PromptForAccessWindow : Window + { + public PromptForAccessWindow() + { + this.InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + + Opened += Window_Opened; + this.FindControl("TitleBanner").PointerPressed += TitleBanner_PointerPressed; + } + + private void Window_Opened(object sender, EventArgs e) + { + Topmost = false; + } + + private void TitleBanner_PointerPressed(object sender, Avalonia.Input.PointerPressedEventArgs e) + { + if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == Avalonia.Input.PointerUpdateKind.LeftButtonPressed) + { + BeginMoveDrag(e); + } + } + } +} diff --git a/Desktop.Win/Services/RemoteControlAccessServiceWin.cs b/Desktop.Win/Services/RemoteControlAccessServiceWin.cs index dd53c9fa..cb89d3ab 100644 --- a/Desktop.Win/Services/RemoteControlAccessServiceWin.cs +++ b/Desktop.Win/Services/RemoteControlAccessServiceWin.cs @@ -1,4 +1,6 @@ using Remotely.Desktop.Core.Interfaces; +using Remotely.Desktop.Win.ViewModels; +using Remotely.Desktop.Win.Views; using System; using System.Collections.Generic; using System.Text; @@ -8,9 +10,26 @@ namespace Remotely.Desktop.Win.Services { public class RemoteControlAccessServiceWin : IRemoteControlAccessService { - public Task PromptForAccess() + public Task PromptForAccess(string requesterName, string organizationName) { - throw new NotImplementedException(); + var result = App.Current.Dispatcher.Invoke(() => + { + var promptWindow = new PromptForAccessWindow(); + var viewModel = promptWindow.DataContext as PromptForAccessWindowViewModel; + if (!string.IsNullOrWhiteSpace(requesterName)) + { + viewModel.RequesterName = requesterName; + } + if (!string.IsNullOrWhiteSpace(organizationName)) + { + viewModel.OrganizationName = organizationName; + } + promptWindow.ShowDialog(); + + return viewModel.PromptResult; + }); + + return Task.FromResult(result); } } } diff --git a/Desktop.Win/ViewModels/PromptForAccessWindowViewModel.cs b/Desktop.Win/ViewModels/PromptForAccessWindowViewModel.cs new file mode 100644 index 00000000..89fbcab6 --- /dev/null +++ b/Desktop.Win/ViewModels/PromptForAccessWindowViewModel.cs @@ -0,0 +1,55 @@ +using Remotely.Desktop.Core.ViewModels; +using Remotely.Desktop.Win.Services; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Remotely.Desktop.Win.ViewModels +{ + public class PromptForAccessWindowViewModel : ViewModelBase + { + private string _organizationName = "your IT provider"; + private string _requesterName = "a technician"; + public string OrganizationName + { + get => _organizationName; + set + { + _organizationName = value; + FirePropertyChanged(nameof(OrganizationName)); + } + } + + public bool PromptResult { get; set; } + + public string RequesterName + { + get => _requesterName; + set + { + _requesterName = value; + FirePropertyChanged(nameof(RequesterName)); + } + } + + public ICommand SetResultNo => new Executor(param => + { + if (param is Window promptWindow) + { + PromptResult = false; + promptWindow.Close(); + } + }); + + public ICommand SetResultYes => new Executor(param => + { + if (param is Window promptWindow) + { + PromptResult = true; + promptWindow.Close(); + } + }); + } +} diff --git a/Desktop.Win/Views/PromptForAccessWindow.xaml b/Desktop.Win/Views/PromptForAccessWindow.xaml new file mode 100644 index 00000000..2d27acdf --- /dev/null +++ b/Desktop.Win/Views/PromptForAccessWindow.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + Remote Control Request + + + + + + + + + diff --git a/Desktop.Win/Views/PromptForAccessWindow.xaml.cs b/Desktop.Win/Views/PromptForAccessWindow.xaml.cs new file mode 100644 index 00000000..b9a18b83 --- /dev/null +++ b/Desktop.Win/Views/PromptForAccessWindow.xaml.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +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; + +namespace Remotely.Desktop.Win.Views +{ + /// + /// Interaction logic for PromptForAccessWindow.xaml + /// + public partial class PromptForAccessWindow : Window + { + public PromptForAccessWindow() + { + InitializeComponent(); + } + + private void CloseButton_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + private void MinimizeButton_Click(object sender, RoutedEventArgs e) + { + this.WindowState = WindowState.Minimized; + } + + private void Window_ContentRendered(object sender, EventArgs e) + { + Topmost = false; + } + + private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + DragMove(); + } + } +} diff --git a/Server/Hubs/ViewerHub.cs b/Server/Hubs/ViewerHub.cs index 5891560c..33ca3c1b 100644 --- a/Server/Hubs/ViewerHub.cs +++ b/Server/Hubs/ViewerHub.cs @@ -199,11 +199,13 @@ namespace Remotely.Server.Hubs (Context.User.Identity.IsAuthenticated && DataService.DoesUserHaveAccessToDevice(deviceID, Context.UserIdentifier))) { + var orgName = DataService.GetOrganizationNameById(orgId); return CasterHubContext.Clients.Client(screenCasterID).SendAsync("GetScreenCast", Context.ConnectionId, RequesterName, AppConfig.RemoteControlNotifyUser, - AppConfig.EnforceAttendedAccess); + AppConfig.EnforceAttendedAccess, + orgName); } else { diff --git a/Server/Services/ApplicationConfig.cs b/Server/Services/ApplicationConfig.cs index def5feb7..1ec1c005 100644 --- a/Server/Services/ApplicationConfig.cs +++ b/Server/Services/ApplicationConfig.cs @@ -21,7 +21,7 @@ namespace Remotely.Server.Services public string DBProvider => Config["ApplicationOptions:DBProvider"] ?? "SQLite"; public string DefaultPrompt => Config["ApplicationOptions:DefaultPrompt"] ?? "~>"; public bool EnableWindowsEventLog => bool.Parse(Config["ApplicationOptions:EnableWindowsEventLog"]); - public bool EnforceAttendedAccess => bool.Parse(Config["EnforceAttendedAccess"] ?? "false"); + public bool EnforceAttendedAccess => bool.Parse(Config["ApplicationOptions:EnforceAttendedAccess"] ?? "false"); public IceServerModel[] IceServers => Config.GetSection("ApplicationOptions:IceServers").Get() ?? fallbackIceServers; public string[] KnownProxies => Config.GetSection("ApplicationOptions:KnownProxies").Get(); public int MaxConcurrentUpdates => int.Parse(Config["ApplicationOptions:MaxConcurrentUpdates"] ?? "10"); diff --git a/Server/appsettings.json b/Server/appsettings.json index 2a606e54..84c5d860 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -15,7 +15,7 @@ "DBProvider": "SQLite", "DefaultPrompt": "~>", "EnableWindowsEventLog": false, - "EnforceAttendedAccess": false, + "EnforceAttendedAccess": false, "IceServers": [ { "Url": "stun: stun.l.google.com:19302",