Moved code from Razor components to code-behind to fix Identity UI scaffolding.

This commit is contained in:
Jared Goodwin 2023-07-31 11:56:19 -07:00
parent 57b86763a4
commit 1a033fb0e7
20 changed files with 439 additions and 304 deletions

View File

@ -24,18 +24,18 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="7.0.7" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.9" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.7" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.4" />
<PackageReference Include="Microsoft.WSMan.Management" Version="7.3.4" />
<PackageReference Include="Microsoft.WSMan.Runtime" Version="7.3.4" />
<PackageReference Include="System.Management.Automation" Version="7.3.4" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.9" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.6" />
<PackageReference Include="Microsoft.WSMan.Management" Version="7.3.6" />
<PackageReference Include="Microsoft.WSMan.Runtime" Version="7.3.6" />
<PackageReference Include="System.Management.Automation" Version="7.3.6" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="7.0.1" />
</ItemGroup>

View File

@ -1,49 +1,123 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Remotely.Server.Areas.Identity.Pages.Account.Manage;
public static class ManageNavPages
namespace Remotely.Server.Areas.Identity.Pages.Account.Manage
{
public static string Index => "Index";
public static string Email => "Email";
public static string ChangePassword => "ChangePassword";
public static string DownloadPersonalData => "DownloadPersonalData";
public static string DeletePersonalData => "DeletePersonalData";
public static string ExternalLogins => "ExternalLogins";
public static string PersonalData => "PersonalData";
public static string TwoFactorAuthentication => "TwoFactorAuthentication";
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email);
public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData);
public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData);
public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
private static string PageNavClass(ViewContext viewContext, string page)
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static class ManageNavPages
{
var activePage = viewContext.ViewData["ActivePage"] as string
?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName);
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : "";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string Index => "Index";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string Email => "Email";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string ChangePassword => "ChangePassword";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string DownloadPersonalData => "DownloadPersonalData";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string DeletePersonalData => "DeletePersonalData";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string ExternalLogins => "ExternalLogins";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string PersonalData => "PersonalData";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string TwoFactorAuthentication => "TwoFactorAuthentication";
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static string PageNavClass(ViewContext viewContext, string page)
{
var activePage = viewContext.ViewData["ActivePage"] as string
?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName);
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
}
}
}

View File

@ -0,0 +1,18 @@
<environment include="Development">
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha384-R3vNCHsZ+A2Lo3d5A6XNP7fdQkeswQWTIPfiYwSpEP3YV079R+93YzTeZRah7f/F">
</script>
</environment>

View File

@ -1,7 +1,5 @@
@attribute [Authorize]
@inherits AuthComponentBase
@inject IDataService DataService
@inject IToastService ToastService
@inherits AuthComponentBase
<h5 class="text-info">
Editing @EditUser?.UserName
@ -10,55 +8,12 @@
@foreach (var group in DeviceGroups ?? Array.Empty<DeviceGroup>())
{
<div @key="group.ID">
<input id="@group.ID" type="checkbox" class="align-middle mr-2" checked="@(DoesGroupContainUser(group))" @onchange="ev => GroupCheckChanged(ev, group)" />
<input
id="@group.ID"
type="checkbox"
class="align-middle mr-2"
checked="@(DoesGroupContainUser(group))"
@onchange="ev => GroupCheckChanged(ev, group)" />
<label class="align-middle mb-0" for="@group.ID">@group.Name</label>
</div>
}
@code {
public static string EditUserPropName => nameof(EditUser);
public static string DeviceGroupsPropName => nameof(DeviceGroups);
[Parameter]
public required RemotelyUser EditUser { get; set; }
[Parameter]
public required DeviceGroup[] DeviceGroups { get; set; }
private bool DoesGroupContainUser(DeviceGroup group)
{
return group.Users.Any(x => x.Id == EditUser.Id);
}
private async Task GroupCheckChanged(ChangeEventArgs args, DeviceGroup group)
{
if (!string.IsNullOrWhiteSpace(EditUser.UserName) &&
args.Value is bool boolValue &&
boolValue)
{
if (!DataService.AddUserToDeviceGroup(EditUser.OrganizationID, group.ID, EditUser.UserName, out var result))
{
ToastService.ShowToast(result, classString: "bg-warning");
}
else
{
ToastService.ShowToast("User added to group.");
}
}
else
{
var result = await DataService.RemoveUserFromDeviceGroup(EditUser.OrganizationID, group.ID, EditUser.Id);
if (!result)
{
ToastService.ShowToast("Failed to remove from group.", classString: "bg-warning");
}
else
{
ToastService.ShowToast("Removed user from group.");
}
}
}
}

View File

@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Remotely.Server.Services;
using Remotely.Shared.Entities;
using Remotely.Shared.Models;
using System.Linq;
using System.Threading.Tasks;
namespace Remotely.Server.Components.ModalContents;
[Authorize]
public partial class EditDeviceGroup : AuthComponentBase
{
public static string DeviceGroupsPropName => nameof(DeviceGroups);
public static string EditUserPropName => nameof(EditUser);
[Parameter]
public required DeviceGroup[] DeviceGroups { get; set; }
[Parameter]
public required RemotelyUser EditUser { get; set; }
[Inject]
private IDataService DataService { get; init; } = null!;
[Inject]
private IToastService ToastService { get; init; } = null!;
private bool DoesGroupContainUser(DeviceGroup group)
{
return group.Users.Any(x => x.Id == EditUser.Id);
}
private async Task GroupCheckChanged(ChangeEventArgs args, DeviceGroup group)
{
if (!string.IsNullOrWhiteSpace(EditUser.UserName) &&
args.Value is bool boolValue &&
boolValue)
{
if (!DataService.AddUserToDeviceGroup(EditUser.OrganizationID, group.ID, EditUser.UserName, out var result))
{
ToastService.ShowToast(result, classString: "bg-warning");
}
else
{
ToastService.ShowToast("User added to group.");
}
}
else
{
var result = await DataService.RemoveUserFromDeviceGroup(EditUser.OrganizationID, group.ID, EditUser.Id);
if (!result)
{
ToastService.ShowToast("Failed to remove from group.", classString: "bg-warning");
}
else
{
ToastService.ShowToast("Removed user from group.");
}
}
}
}

View File

@ -0,0 +1,7 @@
using Microsoft.AspNetCore.Components;
namespace Remotely.Server.Components.ModalContents;
public partial class QuickScriptsSelector : ComponentBase
{
}

View File

@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Remotely.Shared.Entities;
using Remotely.Shared.Models;
@ -23,6 +25,13 @@ public class AppDb : IdentityDbContext
c => c.Aggregate(0, (a, b) => HashCode.Combine(a, b.GetHashCode())),
c => c.ToArray());
private readonly IWebHostEnvironment _hostEnv;
public AppDb(IWebHostEnvironment hostEnvironment)
{
_hostEnv = hostEnvironment;
}
public DbSet<Alert> Alerts { get; set; }
public DbSet<ApiToken> ApiTokens { get; set; }
@ -31,10 +40,10 @@ public class AppDb : IdentityDbContext
public DbSet<Device> Devices { get; set; }
public DbSet<InviteLink> InviteLinks { get; set; }
public DbSet<Organization> Organizations { get; set; }
public DbSet<ScriptRun> ScriptRuns { get; set; }
public DbSet<SavedScript> SavedScripts { get; set; }
public DbSet<ScriptSchedule> ScriptSchedules { get; set; }
public DbSet<ScriptResult> ScriptResults { get; set; }
public DbSet<ScriptRun> ScriptRuns { get; set; }
public DbSet<ScriptSchedule> ScriptSchedules { get; set; }
public DbSet<SharedFile> SharedFiles { get; set; }
public new DbSet<RemotelyUser> Users { get; set; }
@ -43,6 +52,12 @@ public class AppDb : IdentityDbContext
{
options.ConfigureWarnings(x => x.Ignore(RelationalEventId.MultipleCollectionIncludeWarning));
options.LogTo((message) => System.Diagnostics.Debug.Write(message));
if (_hostEnv.IsDevelopment())
{
options.EnableDetailedErrors();
options.EnableSensitiveDataLogging();
}
}
protected override void OnModelCreating(ModelBuilder builder)

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Remotely.Server.Services;
using System;
@ -13,21 +14,26 @@ public class AppDbFactory : IAppDbFactory
{
private readonly IApplicationConfig _appConfig;
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _hostEnv;
public AppDbFactory(IApplicationConfig appConfig, IConfiguration configuration)
public AppDbFactory(
IApplicationConfig appConfig,
IConfiguration configuration,
IWebHostEnvironment hostEnv)
{
_appConfig = appConfig;
_configuration = configuration;
_hostEnv = hostEnv;
}
public AppDb GetContext()
{
return _appConfig.DBProvider.ToLower() switch
{
"sqlite" => new SqliteDbContext(_configuration),
"sqlserver" => new SqlServerDbContext(_configuration),
"postgresql" => new PostgreSqlDbContext(_configuration),
"inmemory" => new TestingDbContext(),
"sqlite" => new SqliteDbContext(_configuration, _hostEnv),
"sqlserver" => new SqlServerDbContext(_configuration, _hostEnv),
"postgresql" => new PostgreSqlDbContext(_configuration, _hostEnv),
"inmemory" => new TestingDbContext(_hostEnv),
_ => throw new ArgumentException("Unknown DB provider."),
};
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
@ -14,7 +15,8 @@ public class PostgreSqlDbContext : AppDb
{
private readonly IConfiguration _configuration;
public PostgreSqlDbContext(IConfiguration configuration)
public PostgreSqlDbContext(IConfiguration configuration, IWebHostEnvironment hostEnv)
: base(hostEnv)
{
_configuration = configuration;
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
@ -13,7 +14,8 @@ public class SqlServerDbContext : AppDb
{
private readonly IConfiguration _configuration;
public SqlServerDbContext(IConfiguration configuration)
public SqlServerDbContext(IConfiguration configuration, IWebHostEnvironment hostEnv)
: base(hostEnv)
{
_configuration = configuration;
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
@ -13,7 +14,8 @@ public class SqliteDbContext : AppDb
{
private readonly IConfiguration _configuration;
public SqliteDbContext(IConfiguration configuration)
public SqliteDbContext(IConfiguration configuration, IWebHostEnvironment hostEnv)
: base(hostEnv)
{
_configuration = configuration;
}

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Remotely.Server.Data;
using System;
@ -11,6 +12,11 @@ namespace Remotely.Server.Data;
public class TestingDbContext : AppDb
{
public TestingDbContext(IWebHostEnvironment hostEnvironment)
: base(hostEnvironment)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseInMemoryDatabase("Remotely");

View File

@ -1,8 +1,6 @@
@page "/scripts/{activeTab?}"
@attribute [Authorize]
@inherits AuthComponentBase
@using System.Collections
@inject IDataService DataService
<CascadingValue Value="this">
<TabControl InitialActiveTab="@(ActiveTab ?? "saved-scripts")">
@ -30,135 +28,3 @@
</TabContents>
</TabControl>
</CascadingValue>
@code {
private readonly List<ScriptTreeNode> _treeNodes = new();
private IEnumerable<SavedScript> _allScripts = Enumerable.Empty<SavedScript>();
private bool _showOnlyMyScripts = true;
[Parameter]
public string? ActiveTab { get; set; }
public bool ShowOnlyMyScripts
{
get => _showOnlyMyScripts;
set
{
_showOnlyMyScripts = value;
_treeNodes.Clear();
}
}
public IEnumerable<ScriptTreeNode> TreeNodes
{
get
{
if (_treeNodes.Any() == true)
{
return _treeNodes;
}
RefreshTreeNodes();
return _treeNodes;
}
}
public string GetItemIconCss(ScriptTreeNode viewModel)
{
if (viewModel.ItemType == TreeItemType.Folder)
{
return "oi oi-folder text-warning";
}
return "oi oi-script text-success";
}
public async Task RefreshScripts()
{
_treeNodes.Clear();
_allScripts = await DataService.GetSavedScriptsWithoutContent(User.Id, User.OrganizationID);
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await RefreshScripts();
}
private void CreateTreeNode(SavedScript script)
{
var root = _treeNodes;
ScriptTreeNode? targetParent = null;
if (!string.IsNullOrWhiteSpace(script.FolderPath))
{
var paths = script.FolderPath.Split("/", StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < paths.Length; i++)
{
var existingParent = root.Find(x => x.Name == paths[i]);
if (existingParent is null)
{
var newItem = new ScriptTreeNode()
{
Name = paths[i],
ItemType = TreeItemType.Folder,
ParentNode = existingParent
};
root.Add(newItem);
root = newItem.ChildItems;
targetParent = newItem;
}
else
{
root = existingParent.ChildItems;
targetParent = existingParent;
}
}
}
var scriptNode = new ScriptTreeNode()
{
Name = script.Name,
Script = script,
ItemType = TreeItemType.Item,
ParentNode = targetParent
};
root.Add(scriptNode);
}
private void RefreshTreeNodes()
{
_treeNodes.Clear();
foreach (var script in _allScripts)
{
var showScript = ShowOnlyMyScripts ?
script.CreatorId == User.Id :
script.CreatorId == User.Id || script.IsPublic;
if (!showScript)
{
continue;
}
CreateTreeNode(script);
}
_treeNodes.Sort((a, b) =>
{
if (a.ItemType != b.ItemType)
{
return Comparer.Default.Compare(a.ItemType, b.ItemType);
}
return Comparer.Default.Compare(a.Name, b.Name);
});
}
}

View File

@ -0,0 +1,148 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Remotely.Server.Components;
using Remotely.Server.Components.Scripts;
using Remotely.Server.Components.TreeView;
using Remotely.Server.Services;
using Remotely.Shared.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using System.Collections;
namespace Remotely.Server.Pages;
[Authorize]
public partial class ScriptsPage : AuthComponentBase
{
private readonly List<ScriptTreeNode> _treeNodes = new();
private IEnumerable<SavedScript> _allScripts = Enumerable.Empty<SavedScript>();
private bool _showOnlyMyScripts = true;
[Parameter]
public string? ActiveTab { get; set; }
public bool ShowOnlyMyScripts
{
get => _showOnlyMyScripts;
set
{
_showOnlyMyScripts = value;
_treeNodes.Clear();
}
}
public IEnumerable<ScriptTreeNode> TreeNodes
{
get
{
if (_treeNodes.Any() == true)
{
return _treeNodes;
}
RefreshTreeNodes();
return _treeNodes;
}
}
[Inject]
private IDataService DataService { get; init; } = null!;
public string GetItemIconCss(ScriptTreeNode viewModel)
{
if (viewModel.ItemType == TreeItemType.Folder)
{
return "oi oi-folder text-warning";
}
return "oi oi-script text-success";
}
public async Task RefreshScripts()
{
_treeNodes.Clear();
_allScripts = await DataService.GetSavedScriptsWithoutContent(User.Id, User.OrganizationID);
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await RefreshScripts();
}
private void CreateTreeNode(SavedScript script)
{
var root = _treeNodes;
ScriptTreeNode? targetParent = null;
if (!string.IsNullOrWhiteSpace(script.FolderPath))
{
var paths = script.FolderPath.Split("/", StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < paths.Length; i++)
{
var existingParent = root.Find(x => x.Name == paths[i]);
if (existingParent is null)
{
var newItem = new ScriptTreeNode()
{
Name = paths[i],
ItemType = TreeItemType.Folder,
ParentNode = existingParent
};
root.Add(newItem);
root = newItem.ChildItems;
targetParent = newItem;
}
else
{
root = existingParent.ChildItems;
targetParent = existingParent;
}
}
}
var scriptNode = new ScriptTreeNode()
{
Name = script.Name,
Script = script,
ItemType = TreeItemType.Item,
ParentNode = targetParent
};
root.Add(scriptNode);
}
private void RefreshTreeNodes()
{
_treeNodes.Clear();
foreach (var script in _allScripts)
{
var showScript = ShowOnlyMyScripts ?
script.CreatorId == User.Id :
script.CreatorId == User.Id || script.IsPublic;
if (!showScript)
{
continue;
}
CreateTreeNode(script);
}
_treeNodes.Sort((a, b) =>
{
if (a.ItemType != b.ItemType)
{
return Comparer.Default.Compare(a.ItemType, b.ItemType);
}
return Comparer.Default.Compare(a.Name, b.Name);
});
}
}

View File

@ -1,13 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
@ -26,14 +23,10 @@ using Remotely.Server.Services;
using System.IO;
using System.Linq;
using System.Net;
using Remotely.Shared.Utilities;
using Immense.RemoteControl.Server.Extensions;
using Remotely.Server.Services.RcImplementations;
using Immense.RemoteControl.Server.Abstractions;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Remotely.Shared.Services;
using System;
using Immense.RemoteControl.Server.Services;
using Serilog;
using Nihs.SimpleMessenger;
using Microsoft.AspNetCore.RateLimiting;
@ -63,37 +56,15 @@ if (string.IsNullOrWhiteSpace(dbProvider))
if (dbProvider == "sqlite")
{
services.AddDbContext<AppDb, SqliteDbContext>(options =>
{
options.UseSqlite(configuration.GetConnectionString("SQLite"));
});
services.AddDbContext<AppDb, SqliteDbContext>();
}
else if (dbProvider == "sqlserver")
{
services.AddDbContext<AppDb, SqlServerDbContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("SQLServer"));
});
services.AddDbContext<AppDb, SqlServerDbContext>();
}
else if (dbProvider == "postgresql")
{
services.AddDbContext<AppDb, PostgreSqlDbContext>(options =>
{
// Password should be set in User Secrets in dev environment.
// See https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1
if (!string.IsNullOrWhiteSpace(configuration.GetValue<string>("PostgresPassword")))
{
var connectionBuilder = new NpgsqlConnectionStringBuilder(configuration.GetConnectionString("PostgreSQL"))
{
Password = configuration["PostgresPassword"]
};
options.UseNpgsql(connectionBuilder.ConnectionString);
}
else
{
options.UseNpgsql(configuration.GetConnectionString("PostgreSQL"));
}
});
services.AddDbContext<AppDb, PostgreSqlDbContext>();
}
services.AddIdentity<RemotelyUser, IdentityRole>(options =>

View File

@ -17,21 +17,21 @@
<ItemGroup>
<PackageReference Include="MailKit" Version="4.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.7">
<PackageReference Include="MailKit" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="7.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.7" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.8" />
<PackageReference Include="Nihs.SimpleMessenger" Version="1.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />

View File

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="7.0.7" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="7.0.9" />
<PackageReference Include="Nihs.ConcurrentList" Version="1.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />

View File

@ -13,12 +13,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.7" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>
<ItemGroup>

View File

@ -13,9 +13,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

@ -1 +1 @@
Subproject commit d571422a0e282fd009769bf14c7f6adf8867b0d8
Subproject commit 7a548c24cc95c0d1c002672873516a59ecbde2c6