Merge branch 'redistributables'
This commit is contained in:
commit
ff9ec5a17b
30 changed files with 4042 additions and 36 deletions
|
@ -50,7 +50,7 @@ namespace LANCommander.PlaynitePlugin
|
||||||
var result = RetryHelper.RetryOnException<ExtractionResult>(10, TimeSpan.FromMilliseconds(500), new ExtractionResult(), () =>
|
var result = RetryHelper.RetryOnException<ExtractionResult>(10, TimeSpan.FromMilliseconds(500), new ExtractionResult(), () =>
|
||||||
{
|
{
|
||||||
Logger.Trace("Attempting to download and extract game...");
|
Logger.Trace("Attempting to download and extract game...");
|
||||||
return DownloadAndExtract(game);
|
return DownloadAndExtractGame(game);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.Success && !result.Canceled)
|
if (!result.Success && !result.Canceled)
|
||||||
|
@ -88,6 +88,12 @@ namespace LANCommander.PlaynitePlugin
|
||||||
SaveScript(game, result.Directory, ScriptType.NameChange);
|
SaveScript(game, result.Directory, ScriptType.NameChange);
|
||||||
SaveScript(game, result.Directory, ScriptType.KeyChange);
|
SaveScript(game, result.Directory, ScriptType.KeyChange);
|
||||||
|
|
||||||
|
if (game.Redistributables != null && game.Redistributables.Count() > 0)
|
||||||
|
{
|
||||||
|
Logger.Trace("Installing required redistributables...");
|
||||||
|
InstallRedistributables(game);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.Install);
|
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.Install);
|
||||||
|
@ -106,7 +112,7 @@ namespace LANCommander.PlaynitePlugin
|
||||||
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
|
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractionResult DownloadAndExtract(LANCommander.SDK.Models.Game game)
|
private ExtractionResult DownloadAndExtractGame(LANCommander.SDK.Models.Game game)
|
||||||
{
|
{
|
||||||
if (game == null)
|
if (game == null)
|
||||||
{
|
{
|
||||||
|
@ -204,6 +210,153 @@ namespace LANCommander.PlaynitePlugin
|
||||||
return extractionResult;
|
return extractionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InstallRedistributables(LANCommander.SDK.Models.Game game)
|
||||||
|
{
|
||||||
|
foreach (var redistributable in game.Redistributables)
|
||||||
|
{
|
||||||
|
string installScriptTempFile = null;
|
||||||
|
string detectionScriptTempFile = null;
|
||||||
|
string extractTempPath = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var installScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.Install);
|
||||||
|
installScriptTempFile = SaveTempScript(installScript);
|
||||||
|
|
||||||
|
var detectionScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.DetectInstall);
|
||||||
|
detectionScriptTempFile = SaveTempScript(detectionScript);
|
||||||
|
|
||||||
|
var detectionResult = PowerShellRuntime.RunScript(detectionScriptTempFile, detectionScript.RequiresAdmin);
|
||||||
|
|
||||||
|
// Redistributable is not installed
|
||||||
|
if (detectionResult == 0)
|
||||||
|
{
|
||||||
|
var extractionResult = DownloadAndExtractRedistributable(redistributable);
|
||||||
|
|
||||||
|
if (extractionResult.Success)
|
||||||
|
{
|
||||||
|
extractTempPath = extractionResult.Directory;
|
||||||
|
|
||||||
|
PowerShellRuntime.RunScript(installScriptTempFile, installScript.RequiresAdmin, null, extractTempPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error(ex, $"Redistributable {redistributable.Name} failed to install");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(installScriptTempFile))
|
||||||
|
File.Delete(installScriptTempFile);
|
||||||
|
|
||||||
|
if (File.Exists(detectionScriptTempFile))
|
||||||
|
File.Delete(detectionScriptTempFile);
|
||||||
|
|
||||||
|
if (Directory.Exists(extractTempPath))
|
||||||
|
Directory.Delete(extractTempPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtractionResult DownloadAndExtractRedistributable(LANCommander.SDK.Models.Redistributable redistributable)
|
||||||
|
{
|
||||||
|
if (redistributable == null)
|
||||||
|
{
|
||||||
|
Logger.Trace("Redistributable failed to download! No redistributable was specified!");
|
||||||
|
|
||||||
|
throw new Exception("Redistributable failed to download!");
|
||||||
|
}
|
||||||
|
|
||||||
|
var destination = Path.Combine(Path.GetTempPath(), redistributable.Name.SanitizeFilename());
|
||||||
|
|
||||||
|
Logger.Trace($"Downloading and extracting \"{redistributable.Name}\" to path {destination}");
|
||||||
|
var result = Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(destination);
|
||||||
|
progress.ProgressMaxValue = 100;
|
||||||
|
progress.CurrentProgressValue = 0;
|
||||||
|
|
||||||
|
using (var redistributableStream = Plugin.LANCommander.StreamRedistributable(redistributable.Id))
|
||||||
|
using (var reader = ReaderFactory.Open(redistributableStream))
|
||||||
|
{
|
||||||
|
progress.ProgressMaxValue = redistributableStream.Length;
|
||||||
|
|
||||||
|
redistributableStream.OnProgress += (pos, len) =>
|
||||||
|
{
|
||||||
|
progress.CurrentProgressValue = pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.EntryExtractionProgress += (object sender, ReaderExtractionEventArgs<IEntry> e) =>
|
||||||
|
{
|
||||||
|
if (progress.CancelToken != null && progress.CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
reader.Cancel();
|
||||||
|
progress.IsIndeterminate = true;
|
||||||
|
|
||||||
|
reader.Dispose();
|
||||||
|
redistributableStream.Dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.WriteAllToDirectory(destination, new ExtractionOptions()
|
||||||
|
{
|
||||||
|
ExtractFullPath = true,
|
||||||
|
Overwrite = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (progress.CancelToken != null && progress.CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Logger.Trace("User cancelled the download");
|
||||||
|
|
||||||
|
if (Directory.Exists(destination))
|
||||||
|
{
|
||||||
|
Logger.Trace("Cleaning up orphaned install files after cancelled install...");
|
||||||
|
|
||||||
|
Directory.Delete(destination, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error(ex, $"Could not extract to path {destination}");
|
||||||
|
|
||||||
|
if (Directory.Exists(destination))
|
||||||
|
{
|
||||||
|
Logger.Trace("Cleaning up orphaned install files after bad install...");
|
||||||
|
|
||||||
|
Directory.Delete(destination, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("The redistributable archive could not be extracted. Please try again or fix the archive!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new GlobalProgressOptions($"Downloading {redistributable.Name}...")
|
||||||
|
{
|
||||||
|
IsIndeterminate = false,
|
||||||
|
Cancelable = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var extractionResult = new ExtractionResult
|
||||||
|
{
|
||||||
|
Canceled = result.Canceled
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!result.Canceled)
|
||||||
|
{
|
||||||
|
extractionResult.Success = true;
|
||||||
|
extractionResult.Directory = destination;
|
||||||
|
Logger.Trace($"Redistributable successfully downloaded and extracted to {destination}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractionResult;
|
||||||
|
}
|
||||||
|
|
||||||
private string Download(LANCommander.SDK.Models.Game game)
|
private string Download(LANCommander.SDK.Models.Game game)
|
||||||
{
|
{
|
||||||
string tempFile = String.Empty;
|
string tempFile = String.Empty;
|
||||||
|
@ -294,6 +447,21 @@ namespace LANCommander.PlaynitePlugin
|
||||||
File.WriteAllText(destination, yaml);
|
File.WriteAllText(destination, yaml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string SaveTempScript(LANCommander.SDK.Models.Script script)
|
||||||
|
{
|
||||||
|
var tempPath = Path.GetTempFileName();
|
||||||
|
|
||||||
|
File.Move(tempPath, tempPath + ".ps1");
|
||||||
|
|
||||||
|
tempPath = tempPath + ".ps1";
|
||||||
|
|
||||||
|
Logger.Trace($"Writing script {script.Name} to {tempPath}");
|
||||||
|
|
||||||
|
File.WriteAllText(tempPath, script.Contents);
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
private void SaveScript(LANCommander.SDK.Models.Game game, string installationDirectory, ScriptType type)
|
private void SaveScript(LANCommander.SDK.Models.Game game, string installationDirectory, ScriptType type)
|
||||||
{
|
{
|
||||||
var script = game.Scripts.FirstOrDefault(s => s.Type == type);
|
var script = game.Scripts.FirstOrDefault(s => s.Type == type);
|
||||||
|
|
|
@ -206,6 +206,11 @@ namespace LANCommander.PlaynitePlugin
|
||||||
return DownloadRequest($"/api/Archives/Download/{id}", progressHandler, completeHandler);
|
return DownloadRequest($"/api/Archives/Download/{id}", progressHandler, completeHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TrackableStream StreamRedistributable(Guid id)
|
||||||
|
{
|
||||||
|
return StreamRequest($"/api/Redistributables/{id}/Download");
|
||||||
|
}
|
||||||
|
|
||||||
public string DownloadSave(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
|
public string DownloadSave(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
|
||||||
{
|
{
|
||||||
return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler);
|
return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler);
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace LANCommander.PlaynitePlugin
|
||||||
File.Delete(tempScript);
|
File.Delete(tempScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunScript(string path, bool asAdmin = false, string arguments = null)
|
public int RunScript(string path, bool asAdmin = false, string arguments = null, string workingDirectory = null)
|
||||||
{
|
{
|
||||||
Logger.Trace($"Executing script at path {path} | Admin: {asAdmin} | Arguments: {arguments}");
|
Logger.Trace($"Executing script at path {path} | Admin: {asAdmin} | Arguments: {arguments}");
|
||||||
|
|
||||||
|
@ -58,6 +58,9 @@ namespace LANCommander.PlaynitePlugin
|
||||||
if (arguments != null)
|
if (arguments != null)
|
||||||
process.StartInfo.Arguments += " " + arguments;
|
process.StartInfo.Arguments += " " + arguments;
|
||||||
|
|
||||||
|
if (workingDirectory != null)
|
||||||
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||||
|
|
||||||
if (asAdmin)
|
if (asAdmin)
|
||||||
{
|
{
|
||||||
process.StartInfo.Verb = "runas";
|
process.StartInfo.Verb = "runas";
|
||||||
|
@ -68,6 +71,8 @@ namespace LANCommander.PlaynitePlugin
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
|
|
||||||
Wow64RevertWow64FsRedirection(ref wow64Value);
|
Wow64RevertWow64FsRedirection(ref wow64Value);
|
||||||
|
|
||||||
|
return process.ExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunScript(Game game, ScriptType type, string arguments = null)
|
public void RunScript(Game game, ScriptType type, string arguments = null)
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
Install,
|
Install,
|
||||||
Uninstall,
|
Uninstall,
|
||||||
NameChange,
|
NameChange,
|
||||||
KeyChange
|
KeyChange,
|
||||||
|
SaveUpload,
|
||||||
|
SaveDownload,
|
||||||
|
DetectInstall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,6 @@ namespace LANCommander.SDK.Models
|
||||||
public virtual Company Developer { get; set; }
|
public virtual Company Developer { get; set; }
|
||||||
public virtual IEnumerable<Archive> Archives { get; set; }
|
public virtual IEnumerable<Archive> Archives { get; set; }
|
||||||
public virtual IEnumerable<Script> Scripts { get; set; }
|
public virtual IEnumerable<Script> Scripts { get; set; }
|
||||||
|
public virtual IEnumerable<Redistributable> Redistributables { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
LANCommander.SDK/Models/Redistributable.cs
Normal file
15
LANCommander.SDK/Models/Redistributable.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LANCommander.SDK.Models
|
||||||
|
{
|
||||||
|
public class Redistributable : BaseModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
public DateTime ReleasedOn { get; set; }
|
||||||
|
public virtual IEnumerable<Archive> Archives { get; set; }
|
||||||
|
public virtual IEnumerable<Script> Scripts { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
<Space Direction="DirectionVHType.Vertical" Style="width: 100%">
|
<Space Direction="DirectionVHType.Vertical" Style="width: 100%">
|
||||||
<SpaceItem>
|
<SpaceItem>
|
||||||
<Table TItem="Archive" DataSource="@Game.Archives.OrderByDescending(a => a.CreatedOn)" HidePagination="true" Responsive>
|
<Table TItem="Archive" DataSource="@Archives.OrderByDescending(a => a.CreatedOn)" HidePagination="true" Responsive>
|
||||||
<PropertyColumn Property="a => a.Version" />
|
<PropertyColumn Property="a => a.Version" />
|
||||||
<PropertyColumn Property="a => a.CompressedSize">
|
<PropertyColumn Property="a => a.CompressedSize">
|
||||||
@ByteSizeLib.ByteSize.FromBytes(context.CompressedSize)
|
@ByteSizeLib.ByteSize.FromBytes(context.CompressedSize)
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<ActionColumn Title="">
|
<ActionColumn Title="">
|
||||||
<Space Style="display: flex; justify-content: end">
|
<Space Style="display: flex; justify-content: end">
|
||||||
<SpaceItem>
|
<SpaceItem>
|
||||||
<a href="/Download/Game/@context.Id" target="_blank" class="ant-btn ant-btn-text ant-btn-icon-only">
|
<a href="/Download/Archive/@context.Id" target="_blank" class="ant-btn ant-btn-text ant-btn-icon-only">
|
||||||
<Icon Type="@IconType.Outline.Download" />
|
<Icon Type="@IconType.Outline.Download" />
|
||||||
</a>
|
</a>
|
||||||
</SpaceItem>
|
</SpaceItem>
|
||||||
|
@ -49,7 +49,10 @@
|
||||||
<ArchiveUploader @ref="Uploader" OnArchiveUploaded="AddArchive" />
|
<ArchiveUploader @ref="Uploader" OnArchiveUploaded="AddArchive" />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public Game Game { get; set; }
|
[Parameter] public Guid GameId { get; set; }
|
||||||
|
[Parameter] public Guid RedistributableId { get; set; }
|
||||||
|
[Parameter] public ICollection<Archive> Archives { get; set; }
|
||||||
|
[Parameter] public EventCallback<ICollection<Archive>> ArchivesChanged { get; set; }
|
||||||
|
|
||||||
Archive Archive;
|
Archive Archive;
|
||||||
ArchiveUploader Uploader;
|
ArchiveUploader Uploader;
|
||||||
|
@ -63,10 +66,7 @@
|
||||||
|
|
||||||
private async Task LoadData()
|
private async Task LoadData()
|
||||||
{
|
{
|
||||||
Game.Archives = await ArchiveService.Get(a => a.GameId == Game.Id).OrderByDescending(a => a.CreatedOn).ToListAsync();
|
Archives = await ArchiveService.Get(a => a.GameId == GameId).OrderByDescending(a => a.CreatedOn).ToListAsync();
|
||||||
|
|
||||||
if (Game.Archives == null)
|
|
||||||
Game.Archives = new List<Archive>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Download(Archive archive)
|
private async Task Download(Archive archive)
|
||||||
|
@ -78,18 +78,18 @@
|
||||||
|
|
||||||
private async Task UploadArchive()
|
private async Task UploadArchive()
|
||||||
{
|
{
|
||||||
Archive = new Archive()
|
if (GameId != Guid.Empty)
|
||||||
{
|
Archive = new Archive() { GameId = GameId, Id = Guid.NewGuid() };
|
||||||
GameId = Game.Id,
|
|
||||||
Id = Guid.NewGuid()
|
if (RedistributableId != Guid.Empty)
|
||||||
};
|
Archive = new Archive() { RedistributableId = RedistributableId, Id = Guid.NewGuid() };
|
||||||
|
|
||||||
await Uploader.Open(Archive);
|
await Uploader.Open(Archive);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddArchive(Archive archive)
|
private async Task AddArchive(Archive archive)
|
||||||
{
|
{
|
||||||
var lastArchive = Game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault();
|
var lastArchive = Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault();
|
||||||
|
|
||||||
Archive = await ArchiveService.Add(archive);
|
Archive = await ArchiveService.Add(archive);
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem Label="Type">
|
<FormItem Label="Type">
|
||||||
<Select @bind-Value="context.Type" TItem="ScriptType" TItemValue="ScriptType" DataSource="Enum.GetValues<ScriptType>()">
|
<Select @bind-Value="context.Type" TItem="ScriptType" TItemValue="ScriptType" DataSource="Enum.GetValues<ScriptType>().Where(st => AllowedTypes == null || AllowedTypes.Contains(st))">
|
||||||
<LabelTemplate Context="Value">@Value.GetDisplayName()</LabelTemplate>
|
<LabelTemplate Context="Value">@Value.GetDisplayName()</LabelTemplate>
|
||||||
<ItemTemplate Context="Value">@Value.GetDisplayName()</ItemTemplate>
|
<ItemTemplate Context="Value">@Value.GetDisplayName()</ItemTemplate>
|
||||||
</Select>
|
</Select>
|
||||||
|
@ -111,11 +111,13 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public Guid GameId { get; set; }
|
[Parameter] public Guid GameId { get; set; }
|
||||||
|
[Parameter] public Guid RedistributableId { get; set; }
|
||||||
[Parameter] public Guid ArchiveId { get; set; }
|
[Parameter] public Guid ArchiveId { get; set; }
|
||||||
[Parameter] public ICollection<Script> Scripts { get; set; }
|
[Parameter] public ICollection<Script> Scripts { get; set; }
|
||||||
[Parameter] public EventCallback<ICollection<Script>> ScriptsChanged { get; set; }
|
[Parameter] public EventCallback<ICollection<Script>> ScriptsChanged { get; set; }
|
||||||
|
[Parameter] public IEnumerable<ScriptType> AllowedTypes { get; set; }
|
||||||
|
|
||||||
Script Script;
|
Script Script;
|
||||||
|
|
||||||
|
@ -150,10 +152,11 @@
|
||||||
private async void Edit(Script script = null)
|
private async void Edit(Script script = null)
|
||||||
{
|
{
|
||||||
if (script == null) {
|
if (script == null) {
|
||||||
Script = new Script()
|
if (GameId != Guid.Empty)
|
||||||
{
|
Script = new Script() { GameId = GameId };
|
||||||
GameId = GameId
|
|
||||||
};
|
if (RedistributableId != Guid.Empty)
|
||||||
|
Script = new Script() { RedistributableId = RedistributableId };
|
||||||
|
|
||||||
if (Editor != null)
|
if (Editor != null)
|
||||||
await Editor.SetValue("");
|
await Editor.SetValue("");
|
37
LANCommander/Components/TransferInput.razor
Normal file
37
LANCommander/Components/TransferInput.razor
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
@typeparam TItem where TItem : BaseModel
|
||||||
|
|
||||||
|
<Transfer DataSource="TransferItems" TargetKeys="TargetKeys" OnChange="OnChange" Titles="new string[] { LeftTitle, RightTitle }" />
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public string LeftTitle { get; set; } = "";
|
||||||
|
[Parameter] public string RightTitle { get; set; } = "";
|
||||||
|
[Parameter] public Func<TItem, string> TitleSelector { get; set; }
|
||||||
|
[Parameter] public IEnumerable<TItem> DataSource { get; set; }
|
||||||
|
[Parameter] public ICollection<TItem> Values { get; set; } = new List<TItem>();
|
||||||
|
[Parameter] public EventCallback<ICollection<TItem>> ValuesChanged { get; set; }
|
||||||
|
|
||||||
|
IEnumerable<TransferItem> TransferItems { get; set; } = new List<TransferItem>();
|
||||||
|
List<string> TargetKeys { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
TransferItems = DataSource.Select(i => new TransferItem()
|
||||||
|
{
|
||||||
|
Key = i.Id.ToString(),
|
||||||
|
Title = TitleSelector.Invoke(i)
|
||||||
|
});
|
||||||
|
|
||||||
|
TargetKeys = Values.Select(i => i.Id.ToString()).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task OnChange(TransferChangeArgs e)
|
||||||
|
{
|
||||||
|
Values = DataSource.Where(i => e.TargetKeys.Contains(i.Id.ToString())).ToList();
|
||||||
|
|
||||||
|
if (ValuesChanged.HasDelegate)
|
||||||
|
await ValuesChanged.InvokeAsync(Values);
|
||||||
|
}
|
||||||
|
}
|
57
LANCommander/Controllers/Api/RedistributableController.cs
Normal file
57
LANCommander/Controllers/Api/RedistributableController.cs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
using LANCommander.Data.Models;
|
||||||
|
using LANCommander.Extensions;
|
||||||
|
using LANCommander.Models;
|
||||||
|
using LANCommander.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace LANCommander.Controllers.Api
|
||||||
|
{
|
||||||
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class RedistributableController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly RedistributableService RedistributableService;
|
||||||
|
private readonly LANCommanderSettings Settings = SettingService.GetSettings();
|
||||||
|
|
||||||
|
public RedistributableController(RedistributableService redistributableService)
|
||||||
|
{
|
||||||
|
|
||||||
|
RedistributableService = redistributableService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IEnumerable<Redistributable>> Get()
|
||||||
|
{
|
||||||
|
return await RedistributableService.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<Redistributable> Get(Guid id)
|
||||||
|
{
|
||||||
|
return await RedistributableService.Get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/Download")]
|
||||||
|
public async Task<IActionResult> Download(Guid id)
|
||||||
|
{
|
||||||
|
var redistributable = await RedistributableService.Get(id);
|
||||||
|
|
||||||
|
if (redistributable == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
if (redistributable.Archives == null || redistributable.Archives.Count == 0)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var archive = redistributable.Archives.OrderByDescending(a => a.CreatedOn).First();
|
||||||
|
|
||||||
|
var filename = Path.Combine(Settings.Archives.StoragePath, archive.ObjectKey);
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(filename))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{redistributable.Name.SanitizeFilename()}.zip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ namespace LANCommander.Controllers
|
||||||
ArchiveService = archiveService;
|
ArchiveService = archiveService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Game(Guid id)
|
public async Task<IActionResult> Archive(Guid id)
|
||||||
{
|
{
|
||||||
var archive = await ArchiveService.Get(id);
|
var archive = await ArchiveService.Get(id);
|
||||||
|
|
||||||
|
|
|
@ -37,13 +37,13 @@ namespace LANCommander.Data
|
||||||
builder.Entity<Game>()
|
builder.Entity<Game>()
|
||||||
.HasMany(g => g.Archives)
|
.HasMany(g => g.Archives)
|
||||||
.WithOne(g => g.Game)
|
.WithOne(g => g.Game)
|
||||||
.IsRequired(true)
|
.IsRequired(false)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
builder.Entity<Game>()
|
builder.Entity<Game>()
|
||||||
.HasMany(g => g.Scripts)
|
.HasMany(g => g.Scripts)
|
||||||
.WithOne(s => s.Game)
|
.WithOne(s => s.Game)
|
||||||
.IsRequired(true)
|
.IsRequired(false)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
builder.Entity<Game>()
|
builder.Entity<Game>()
|
||||||
|
@ -81,6 +81,15 @@ namespace LANCommander.Data
|
||||||
g => g.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
g => g.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
builder.Entity<Game>()
|
||||||
|
.HasMany(g => g.Redistributables)
|
||||||
|
.WithMany(r => r.Games)
|
||||||
|
.UsingEntity<Dictionary<string, object>>(
|
||||||
|
"GameRedistributable",
|
||||||
|
gr => gr.HasOne<Redistributable>().WithMany().HasForeignKey("RedistributableId"),
|
||||||
|
gr => gr.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
||||||
|
);
|
||||||
|
|
||||||
builder.Entity<User>()
|
builder.Entity<User>()
|
||||||
.HasMany(u => u.GameSaves)
|
.HasMany(u => u.GameSaves)
|
||||||
.WithOne(gs => gs.User)
|
.WithOne(gs => gs.User)
|
||||||
|
@ -104,6 +113,18 @@ namespace LANCommander.Data
|
||||||
.WithOne(sl => sl.Server)
|
.WithOne(sl => sl.Server)
|
||||||
.IsRequired(true)
|
.IsRequired(true)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.Entity<Redistributable>()
|
||||||
|
.HasMany(r => r.Archives)
|
||||||
|
.WithOne(a => a.Redistributable)
|
||||||
|
.IsRequired(false)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.Entity<Redistributable>()
|
||||||
|
.HasMany(r => r.Scripts)
|
||||||
|
.WithOne(s => s.Redistributable)
|
||||||
|
.IsRequired(false)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<Game>? Games { get; set; }
|
public DbSet<Game>? Games { get; set; }
|
||||||
|
@ -123,5 +144,7 @@ namespace LANCommander.Data
|
||||||
public DbSet<Server>? Servers { get; set; }
|
public DbSet<Server>? Servers { get; set; }
|
||||||
|
|
||||||
public DbSet<ServerConsole>? ServerConsoles { get; set; }
|
public DbSet<ServerConsole>? ServerConsoles { get; set; }
|
||||||
|
|
||||||
|
public DbSet<Redistributable>? Redistributables { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,8 @@ namespace LANCommander.Data.Enums
|
||||||
[Display(Name = "Save Upload")]
|
[Display(Name = "Save Upload")]
|
||||||
SaveUpload,
|
SaveUpload,
|
||||||
[Display(Name = "Save Download")]
|
[Display(Name = "Save Download")]
|
||||||
SaveDownload
|
SaveDownload,
|
||||||
|
[Display(Name = "Detect Install")]
|
||||||
|
DetectInstall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,18 @@ namespace LANCommander.Data.Models
|
||||||
[Required]
|
[Required]
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
public Guid GameId { get; set; }
|
public Guid? GameId { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
[ForeignKey(nameof(GameId))]
|
[ForeignKey(nameof(GameId))]
|
||||||
[InverseProperty("Archives")]
|
[InverseProperty("Archives")]
|
||||||
public virtual Game? Game { get; set; }
|
public virtual Game? Game { get; set; }
|
||||||
|
|
||||||
|
public Guid? RedistributableId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
[ForeignKey(nameof(RedistributableId))]
|
||||||
|
[InverseProperty("Archives")]
|
||||||
|
public virtual Redistributable? Redistributable { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Last Version")]
|
[Display(Name = "Last Version")]
|
||||||
public virtual Archive? LastVersion { get; set; }
|
public virtual Archive? LastVersion { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ namespace LANCommander.Data.Models
|
||||||
public virtual ICollection<GameSave>? GameSaves { get; set; }
|
public virtual ICollection<GameSave>? GameSaves { get; set; }
|
||||||
public virtual ICollection<SavePath>? SavePaths { get; set; }
|
public virtual ICollection<SavePath>? SavePaths { get; set; }
|
||||||
public virtual ICollection<Server>? Servers { get; set; }
|
public virtual ICollection<Server>? Servers { get; set; }
|
||||||
|
public virtual ICollection<Redistributable>? Redistributables { get; set; }
|
||||||
|
|
||||||
public string? ValidKeyRegex { get; set; }
|
public string? ValidKeyRegex { get; set; }
|
||||||
public virtual ICollection<Key>? Keys { get; set; }
|
public virtual ICollection<Key>? Keys { get; set; }
|
||||||
|
|
16
LANCommander/Data/Models/Redistributable.cs
Normal file
16
LANCommander/Data/Models/Redistributable.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace LANCommander.Data.Models
|
||||||
|
{
|
||||||
|
[Table("Redistributables")]
|
||||||
|
public class Redistributable : BaseModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Notes { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<Archive>? Archives { get; set; }
|
||||||
|
public virtual ICollection<Script>? Scripts { get; set; }
|
||||||
|
public virtual ICollection<Game>? Games { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,5 +18,11 @@ namespace LANCommander.Data.Models
|
||||||
[ForeignKey(nameof(GameId))]
|
[ForeignKey(nameof(GameId))]
|
||||||
[InverseProperty("Scripts")]
|
[InverseProperty("Scripts")]
|
||||||
public virtual Game? Game { get; set; }
|
public virtual Game? Game { get; set; }
|
||||||
|
|
||||||
|
public Guid? RedistributableId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
[ForeignKey(nameof(RedistributableId))]
|
||||||
|
[InverseProperty("Scripts")]
|
||||||
|
public virtual Redistributable? Redistributable { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1524
LANCommander/Migrations/20231018060152_AddRedistributables.Designer.cs
generated
Normal file
1524
LANCommander/Migrations/20231018060152_AddRedistributables.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
158
LANCommander/Migrations/20231018060152_AddRedistributables.cs
Normal file
158
LANCommander/Migrations/20231018060152_AddRedistributables.cs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace LANCommander.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddRedistributables : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "GameId",
|
||||||
|
table: "Scripts",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "TEXT");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "RedistributableId",
|
||||||
|
table: "Scripts",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "GameId",
|
||||||
|
table: "Archive",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "TEXT");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "RedistributableId",
|
||||||
|
table: "Archive",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Redistributables",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Description = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Notes = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
CreatedById = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||||
|
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedById = table.Column<Guid>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Redistributables", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Redistributables_AspNetUsers_CreatedById",
|
||||||
|
column: x => x.CreatedById,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Redistributables_AspNetUsers_UpdatedById",
|
||||||
|
column: x => x.UpdatedById,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Scripts_RedistributableId",
|
||||||
|
table: "Scripts",
|
||||||
|
column: "RedistributableId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Archive_RedistributableId",
|
||||||
|
table: "Archive",
|
||||||
|
column: "RedistributableId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Redistributables_CreatedById",
|
||||||
|
table: "Redistributables",
|
||||||
|
column: "CreatedById");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Redistributables_UpdatedById",
|
||||||
|
table: "Redistributables",
|
||||||
|
column: "UpdatedById");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Archive_Redistributables_RedistributableId",
|
||||||
|
table: "Archive",
|
||||||
|
column: "RedistributableId",
|
||||||
|
principalTable: "Redistributables",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Scripts_Redistributables_RedistributableId",
|
||||||
|
table: "Scripts",
|
||||||
|
column: "RedistributableId",
|
||||||
|
principalTable: "Redistributables",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Archive_Redistributables_RedistributableId",
|
||||||
|
table: "Archive");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Scripts_Redistributables_RedistributableId",
|
||||||
|
table: "Scripts");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Redistributables");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Scripts_RedistributableId",
|
||||||
|
table: "Scripts");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Archive_RedistributableId",
|
||||||
|
table: "Archive");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RedistributableId",
|
||||||
|
table: "Scripts");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RedistributableId",
|
||||||
|
table: "Archive");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "GameId",
|
||||||
|
table: "Scripts",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "TEXT",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "GameId",
|
||||||
|
table: "Archive",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "TEXT",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1554
LANCommander/Migrations/20231019005133_AddGameRedistributableRelationship.Designer.cs
generated
Normal file
1554
LANCommander/Migrations/20231019005133_AddGameRedistributableRelationship.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace LANCommander.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddGameRedistributableRelationship : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GameRedistributable",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
GameId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
RedistributableId = table.Column<Guid>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GameRedistributable", x => new { x.GameId, x.RedistributableId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GameRedistributable_Games_GameId",
|
||||||
|
column: x => x.GameId,
|
||||||
|
principalTable: "Games",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GameRedistributable_Redistributables_RedistributableId",
|
||||||
|
column: x => x.RedistributableId,
|
||||||
|
principalTable: "Redistributables",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GameRedistributable_RedistributableId",
|
||||||
|
table: "GameRedistributable",
|
||||||
|
column: "RedistributableId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GameRedistributable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,6 +81,21 @@ namespace LANCommander.Migrations
|
||||||
b.ToTable("GamePublisher");
|
b.ToTable("GamePublisher");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GameRedistributable", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("GameId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("RedistributableId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("GameId", "RedistributableId");
|
||||||
|
|
||||||
|
b.HasIndex("RedistributableId");
|
||||||
|
|
||||||
|
b.ToTable("GameRedistributable");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("GameTag", b =>
|
modelBuilder.Entity("GameTag", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("GamesId")
|
b.Property<Guid>("GamesId")
|
||||||
|
@ -165,7 +180,7 @@ namespace LANCommander.Migrations
|
||||||
b.Property<DateTime>("CreatedOn")
|
b.Property<DateTime>("CreatedOn")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid>("GameId")
|
b.Property<Guid?>("GameId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid?>("LastVersionId")
|
b.Property<Guid?>("LastVersionId")
|
||||||
|
@ -175,6 +190,9 @@ namespace LANCommander.Migrations
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("RedistributableId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<long>("UncompressedSize")
|
b.Property<long>("UncompressedSize")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
@ -196,6 +214,8 @@ namespace LANCommander.Migrations
|
||||||
|
|
||||||
b.HasIndex("LastVersionId");
|
b.HasIndex("LastVersionId");
|
||||||
|
|
||||||
|
b.HasIndex("RedistributableId");
|
||||||
|
|
||||||
b.HasIndex("UpdatedById");
|
b.HasIndex("UpdatedById");
|
||||||
|
|
||||||
b.ToTable("Archive");
|
b.ToTable("Archive");
|
||||||
|
@ -505,6 +525,43 @@ namespace LANCommander.Migrations
|
||||||
b.ToTable("MultiplayerModes");
|
b.ToTable("MultiplayerModes");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedById")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedById")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedById");
|
||||||
|
|
||||||
|
b.HasIndex("UpdatedById");
|
||||||
|
|
||||||
|
b.ToTable("Redistributables");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LANCommander.Data.Models.Role", b =>
|
modelBuilder.Entity("LANCommander.Data.Models.Role", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
@ -591,13 +648,15 @@ namespace LANCommander.Migrations
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid?>("GameId")
|
b.Property<Guid?>("GameId")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("RedistributableId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<bool>("RequiresAdmin")
|
b.Property<bool>("RequiresAdmin")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
@ -616,6 +675,8 @@ namespace LANCommander.Migrations
|
||||||
|
|
||||||
b.HasIndex("GameId");
|
b.HasIndex("GameId");
|
||||||
|
|
||||||
|
b.HasIndex("RedistributableId");
|
||||||
|
|
||||||
b.HasIndex("UpdatedById");
|
b.HasIndex("UpdatedById");
|
||||||
|
|
||||||
b.ToTable("Scripts");
|
b.ToTable("Scripts");
|
||||||
|
@ -1027,6 +1088,21 @@ namespace LANCommander.Migrations
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("GameRedistributable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("LANCommander.Data.Models.Game", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GameId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.Redistributable", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RedistributableId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("GameTag", b =>
|
modelBuilder.Entity("GameTag", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("LANCommander.Data.Models.Game", null)
|
b.HasOne("LANCommander.Data.Models.Game", null)
|
||||||
|
@ -1074,13 +1150,17 @@ namespace LANCommander.Migrations
|
||||||
b.HasOne("LANCommander.Data.Models.Game", "Game")
|
b.HasOne("LANCommander.Data.Models.Game", "Game")
|
||||||
.WithMany("Archives")
|
.WithMany("Archives")
|
||||||
.HasForeignKey("GameId")
|
.HasForeignKey("GameId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("LANCommander.Data.Models.Archive", "LastVersion")
|
b.HasOne("LANCommander.Data.Models.Archive", "LastVersion")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("LastVersionId");
|
.HasForeignKey("LastVersionId");
|
||||||
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.Redistributable", "Redistributable")
|
||||||
|
.WithMany("Archives")
|
||||||
|
.HasForeignKey("RedistributableId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UpdatedById");
|
.HasForeignKey("UpdatedById");
|
||||||
|
@ -1091,6 +1171,8 @@ namespace LANCommander.Migrations
|
||||||
|
|
||||||
b.Navigation("LastVersion");
|
b.Navigation("LastVersion");
|
||||||
|
|
||||||
|
b.Navigation("Redistributable");
|
||||||
|
|
||||||
b.Navigation("UpdatedBy");
|
b.Navigation("UpdatedBy");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1243,6 +1325,21 @@ namespace LANCommander.Migrations
|
||||||
b.Navigation("UpdatedBy");
|
b.Navigation("UpdatedBy");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedById");
|
||||||
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UpdatedById");
|
||||||
|
|
||||||
|
b.Navigation("CreatedBy");
|
||||||
|
|
||||||
|
b.Navigation("UpdatedBy");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LANCommander.Data.Models.SavePath", b =>
|
modelBuilder.Entity("LANCommander.Data.Models.SavePath", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||||
|
@ -1273,8 +1370,12 @@ namespace LANCommander.Migrations
|
||||||
b.HasOne("LANCommander.Data.Models.Game", "Game")
|
b.HasOne("LANCommander.Data.Models.Game", "Game")
|
||||||
.WithMany("Scripts")
|
.WithMany("Scripts")
|
||||||
.HasForeignKey("GameId")
|
.HasForeignKey("GameId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
.IsRequired();
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.Redistributable", "Redistributable")
|
||||||
|
.WithMany("Scripts")
|
||||||
|
.HasForeignKey("RedistributableId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
|
@ -1284,6 +1385,8 @@ namespace LANCommander.Migrations
|
||||||
|
|
||||||
b.Navigation("Game");
|
b.Navigation("Game");
|
||||||
|
|
||||||
|
b.Navigation("Redistributable");
|
||||||
|
|
||||||
b.Navigation("UpdatedBy");
|
b.Navigation("UpdatedBy");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1426,6 +1529,13 @@ namespace LANCommander.Migrations
|
||||||
b.Navigation("Servers");
|
b.Navigation("Servers");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Archives");
|
||||||
|
|
||||||
|
b.Navigation("Scripts");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
|
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ServerConsoles");
|
b.Navigation("ServerConsoles");
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@page "/Games/{id:guid}/{panel}"
|
@page "/Games/{id:guid}/{panel}"
|
||||||
@page "/Games/Add"
|
@page "/Games/Add"
|
||||||
@using LANCommander.Components.FileManagerComponents;
|
@using LANCommander.Components.FileManagerComponents;
|
||||||
|
@using LANCommander.Data.Enums;
|
||||||
@using LANCommander.Models;
|
@using LANCommander.Models;
|
||||||
@using LANCommander.Pages.Games.Components
|
@using LANCommander.Pages.Games.Components
|
||||||
@using System.IO.Compression;
|
@using System.IO.Compression;
|
||||||
|
@ -12,6 +13,7 @@
|
||||||
@inject TagService TagService
|
@inject TagService TagService
|
||||||
@inject ArchiveService ArchiveService
|
@inject ArchiveService ArchiveService
|
||||||
@inject ScriptService ScriptService
|
@inject ScriptService ScriptService
|
||||||
|
@inject RedistributableService RedistributableService
|
||||||
@inject IMessageService MessageService
|
@inject IMessageService MessageService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject ModalService ModalService
|
@inject ModalService ModalService
|
||||||
|
@ -97,6 +99,9 @@
|
||||||
<FormItem Label="Tags">
|
<FormItem Label="Tags">
|
||||||
<TagsInput Entities="Tags" @bind-Values="Game.Tags" OptionLabelSelector="c => c.Name" TItem="Data.Models.Tag" />
|
<TagsInput Entities="Tags" @bind-Values="Game.Tags" OptionLabelSelector="c => c.Name" TItem="Data.Models.Tag" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem Label="Redistributables">
|
||||||
|
<TransferInput LeftTitle="Available" RightTitle="Selected" DataSource="Redistributables" TitleSelector="r => r.Name" @bind-Values="Game.Redistributables" />
|
||||||
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -121,11 +126,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-panel="Scripts">
|
<div data-panel="Scripts">
|
||||||
<ScriptEditor @bind-Scripts="Game.Scripts" GameId="Game.Id" ArchiveId="@LatestArchiveId" />
|
<ScriptEditor @bind-Scripts="Game.Scripts" GameId="Game.Id" ArchiveId="@LatestArchiveId" AllowedTypes="new ScriptType[] { ScriptType.Install, ScriptType.Uninstall, ScriptType.NameChange, ScriptType.KeyChange }" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-panel="Archives">
|
<div data-panel="Archives">
|
||||||
<ArchiveEditor Game="Game" />
|
<ArchiveEditor @bind-Archives="Game.Archives" GameId="Game.Id" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +165,9 @@ else
|
||||||
IEnumerable<Company> Companies;
|
IEnumerable<Company> Companies;
|
||||||
IEnumerable<Genre> Genres;
|
IEnumerable<Genre> Genres;
|
||||||
IEnumerable<Data.Models.Tag> Tags;
|
IEnumerable<Data.Models.Tag> Tags;
|
||||||
|
IEnumerable<Redistributable> Redistributables = new List<Redistributable>();
|
||||||
|
IEnumerable<TransferItem> RedistributableTargetItems = new List<TransferItem>();
|
||||||
|
IEnumerable<string> TargetRedistributables = new List<string>();
|
||||||
|
|
||||||
FilePickerDialog ArchiveFilePickerDialog;
|
FilePickerDialog ArchiveFilePickerDialog;
|
||||||
|
|
||||||
|
@ -206,6 +214,13 @@ else
|
||||||
Companies = await CompanyService.Get();
|
Companies = await CompanyService.Get();
|
||||||
Genres = await GenreService.Get();
|
Genres = await GenreService.Get();
|
||||||
Tags = await TagService.Get();
|
Tags = await TagService.Get();
|
||||||
|
Redistributables = await RedistributableService.Get();
|
||||||
|
RedistributableTargetItems = Redistributables.Select(r => new TransferItem
|
||||||
|
{
|
||||||
|
Title = r.Name,
|
||||||
|
Description = r.Description,
|
||||||
|
Key = r.Id.ToString()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Save()
|
private async Task Save()
|
||||||
|
|
117
LANCommander/Pages/Redistributables/Edit.razor
Normal file
117
LANCommander/Pages/Redistributables/Edit.razor
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
@page "/Redistributables/{id:guid}"
|
||||||
|
@page "/Redistributables/{id:guid}/{panel}"
|
||||||
|
@page "/Redistributables/Add"
|
||||||
|
@using LANCommander.Data.Enums;
|
||||||
|
@inject RedistributableService RedistributableService
|
||||||
|
@inject IMessageService MessageService
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
<Layout Class="panel-layout" Style="padding: 24px 0;">
|
||||||
|
<Sider Width="200">
|
||||||
|
<Menu Mode="@MenuMode.Inline" Style="height: 100%;">
|
||||||
|
<MenuItem RouterLink="@($"/Redistributables/{Redistributable.Id}/General")">General</MenuItem>
|
||||||
|
|
||||||
|
@if (Redistributable.Id != Guid.Empty)
|
||||||
|
{
|
||||||
|
<MenuItem RouterLink="@($"/Redistributables/{Redistributable.Id}/Scripts")">Scripts</MenuItem>
|
||||||
|
<MenuItem RouterLink="@($"/Redistributables/{Redistributable.Id}/Archives")">Archives</MenuItem>
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
</Sider>
|
||||||
|
|
||||||
|
<Content>
|
||||||
|
<PageHeader>
|
||||||
|
<PageHeaderTitle>@Panel</PageHeaderTitle>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="panel-layout-content">
|
||||||
|
@if (Panel == "General" || String.IsNullOrWhiteSpace(Panel))
|
||||||
|
{
|
||||||
|
<Form Model="@Redistributable" Layout="@FormLayout.Vertical">
|
||||||
|
<FormItem Label="Name">
|
||||||
|
<Input @bind-Value="@context.Name" />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem Label="Notes">
|
||||||
|
<TextArea @bind-Value="@context.Notes" MaxLength=2000 ShowCount />
|
||||||
|
</FormItem>
|
||||||
|
<FormItem Label="Description">
|
||||||
|
<TextArea @bind-Value="@context.Description" MaxLength=500 ShowCount />
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem>
|
||||||
|
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Panel == "Scripts")
|
||||||
|
{
|
||||||
|
<ScriptEditor @bind-Scripts="Redistributable.Scripts" RedistributableId="Redistributable.Id" ArchiveId="@LatestArchiveId" AllowedTypes="new ScriptType[] { ScriptType.Install, ScriptType.DetectInstall }" />
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Panel == "Archives")
|
||||||
|
{
|
||||||
|
<ArchiveEditor @bind-Archives="Redistributable.Archives" RedistributableId="Redistributable.Id" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public Guid Id { get; set; }
|
||||||
|
[Parameter] public string Panel { get; set; }
|
||||||
|
|
||||||
|
Redistributable Redistributable;
|
||||||
|
|
||||||
|
private Guid LatestArchiveId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Redistributable != null && Redistributable.Archives != null && Redistributable.Archives.Count > 0)
|
||||||
|
return Redistributable.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Id;
|
||||||
|
else
|
||||||
|
return Guid.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
if (Id == Guid.Empty)
|
||||||
|
Redistributable = new Redistributable();
|
||||||
|
else
|
||||||
|
Redistributable = await RedistributableService.Get(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Redistributable.Id != Guid.Empty)
|
||||||
|
{
|
||||||
|
Redistributable = await RedistributableService.Update(Redistributable);
|
||||||
|
|
||||||
|
await MessageService.Success("Redistributable updated!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Redistributable = await RedistributableService.Add(Redistributable);
|
||||||
|
|
||||||
|
NavigationManager.LocationChanged += NotifyRedistributableAdded;
|
||||||
|
|
||||||
|
NavigationManager.NavigateTo($"/Redistributables/{Redistributable.Id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await MessageService.Error("Could not save!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyRedistributableAdded(object? sender, LocationChangedEventArgs e)
|
||||||
|
{
|
||||||
|
NavigationManager.LocationChanged -= NotifyRedistributableAdded;
|
||||||
|
|
||||||
|
MessageService.Success("Redistributable added!");
|
||||||
|
}
|
||||||
|
}
|
115
LANCommander/Pages/Redistributables/Index.razor
Normal file
115
LANCommander/Pages/Redistributables/Index.razor
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
@page "/Redistributables"
|
||||||
|
@using Microsoft.EntityFrameworkCore;
|
||||||
|
@attribute [Authorize]
|
||||||
|
@inject RedistributableService RedistributableService
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject IMessageService MessageService
|
||||||
|
|
||||||
|
<PageHeader Title="Redistributables">
|
||||||
|
<PageHeaderExtra>
|
||||||
|
<Space Direction="DirectionVHType.Horizontal">
|
||||||
|
<SpaceItem>
|
||||||
|
<Search Placeholder="Search" @bind-Value="Search" BindOnInput DebounceMilliseconds="150" OnChange="() => LoadData()" />
|
||||||
|
</SpaceItem>
|
||||||
|
<SpaceItem>
|
||||||
|
<Button OnClick="() => Add()" Type="@ButtonType.Primary">Add Redistributable</Button>
|
||||||
|
</SpaceItem>
|
||||||
|
</Space>
|
||||||
|
</PageHeaderExtra>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
|
<TableColumnPicker @ref="Picker" Key="Redistributables" @bind-Visible="ColumnPickerVisible" />
|
||||||
|
|
||||||
|
<Table TItem="Redistributable" DataSource="@Redistributables" Loading="@Loading" PageSize="25" Responsive>
|
||||||
|
<PropertyColumn Property="r => r.Name" Sortable Hidden="@(Picker.IsColumnHidden("Name"))" />
|
||||||
|
<PropertyColumn Property="s => s.CreatedOn" Format="MM/dd/yyyy hh:mm tt" Sortable Hidden="@(Picker.IsColumnHidden("Created On"))" />
|
||||||
|
<PropertyColumn Property="s => s.CreatedBy" Sortable Hidden="@(Picker.IsColumnHidden("Created By"))">
|
||||||
|
@context.CreatedBy?.UserName
|
||||||
|
</PropertyColumn>
|
||||||
|
<PropertyColumn Property="g => g.UpdatedOn" Format="MM/dd/yyyy hh:mm tt" Sortable Hidden="@(Picker.IsColumnHidden("Updated On"))" />
|
||||||
|
<PropertyColumn Property="g => g.UpdatedBy" Sortable Hidden="@(Picker.IsColumnHidden("Updated By"))">
|
||||||
|
@context.UpdatedBy?.UserName
|
||||||
|
</PropertyColumn>
|
||||||
|
<ActionColumn Title="" Style="text-align: right; white-space: nowrap">
|
||||||
|
<TitleTemplate>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<Button Icon="@IconType.Outline.Edit" Type="@ButtonType.Text" OnClick="() => OpenColumnPicker()" />
|
||||||
|
</div>
|
||||||
|
</TitleTemplate>
|
||||||
|
<ChildContent>
|
||||||
|
<Space Direction="DirectionVHType.Horizontal">
|
||||||
|
<SpaceItem>
|
||||||
|
<Button OnClick="() => Edit(context)">Edit</Button>
|
||||||
|
</SpaceItem>
|
||||||
|
<SpaceItem>
|
||||||
|
<Popconfirm OnConfirm="() => Delete(context)" Title="Are you sure you want to delete this redistributable?">
|
||||||
|
<Button Icon="@IconType.Outline.Close" Type="@ButtonType.Text" Danger />
|
||||||
|
</Popconfirm>
|
||||||
|
</SpaceItem>
|
||||||
|
</Space>
|
||||||
|
</ChildContent>
|
||||||
|
</ActionColumn>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
IEnumerable<Redistributable> Redistributables { get; set; } = new List<Redistributable>();
|
||||||
|
|
||||||
|
bool Loading = true;
|
||||||
|
|
||||||
|
string Search = "";
|
||||||
|
|
||||||
|
TableColumnPicker Picker;
|
||||||
|
bool ColumnPickerVisible = false;
|
||||||
|
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
LoadData();
|
||||||
|
|
||||||
|
Loading = false;
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadData()
|
||||||
|
{
|
||||||
|
var fuzzySearch = Search.ToLower().Trim();
|
||||||
|
|
||||||
|
Redistributables = await RedistributableService.Get(r => r.Name.ToLower().Contains(fuzzySearch)).OrderBy(r => r.Name).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Add()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo("/Redistributables/Add");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Edit(Redistributable redistributable)
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo($"/Redistributables/{redistributable.Id}/General");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Delete(Redistributable redistributable)
|
||||||
|
{
|
||||||
|
Redistributables = new List<Redistributable>();
|
||||||
|
|
||||||
|
Loading = true;
|
||||||
|
|
||||||
|
await RedistributableService.Delete(redistributable);
|
||||||
|
|
||||||
|
Redistributables = await RedistributableService.Get(x => true).OrderBy(r => r.Name).ToListAsync();
|
||||||
|
|
||||||
|
Loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenColumnPicker()
|
||||||
|
{
|
||||||
|
ColumnPickerVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CloseColumnPicker()
|
||||||
|
{
|
||||||
|
ColumnPickerVisible = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -141,6 +141,7 @@ namespace LANCommander
|
||||||
builder.Services.AddScoped<ServerService>();
|
builder.Services.AddScoped<ServerService>();
|
||||||
builder.Services.AddScoped<ServerConsoleService>();
|
builder.Services.AddScoped<ServerConsoleService>();
|
||||||
builder.Services.AddScoped<GameSaveService>();
|
builder.Services.AddScoped<GameSaveService>();
|
||||||
|
builder.Services.AddScoped<RedistributableService>();
|
||||||
|
|
||||||
builder.Services.AddSingleton<ServerProcessService>();
|
builder.Services.AddSingleton<ServerProcessService>();
|
||||||
builder.Services.AddSingleton<IPXRelayService>();
|
builder.Services.AddSingleton<IPXRelayService>();
|
||||||
|
|
12
LANCommander/Services/RedistributableService.cs
Normal file
12
LANCommander/Services/RedistributableService.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using LANCommander.Data;
|
||||||
|
using LANCommander.Data.Models;
|
||||||
|
|
||||||
|
namespace LANCommander.Services
|
||||||
|
{
|
||||||
|
public class RedistributableService : BaseDatabaseService<Redistributable>
|
||||||
|
{
|
||||||
|
public RedistributableService(DatabaseContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
@if (User != null && User.IsInRole("Administrator"))
|
@if (User != null && User.IsInRole("Administrator"))
|
||||||
{
|
{
|
||||||
<MenuItem RouterLink="/Games">Games</MenuItem>
|
<MenuItem RouterLink="/Games">Games</MenuItem>
|
||||||
|
<MenuItem RouterLink="/Redistributables">Redistributables</MenuItem>
|
||||||
<MenuItem RouterLink="/Servers">Servers</MenuItem>
|
<MenuItem RouterLink="/Servers">Servers</MenuItem>
|
||||||
<MenuItem RouterLink="/Files">Files</MenuItem>
|
<MenuItem RouterLink="/Files">Files</MenuItem>
|
||||||
<MenuItem RouterLink="/Settings">Settings</MenuItem>
|
<MenuItem RouterLink="/Settings">Settings</MenuItem>
|
||||||
|
|
Loading…
Add table
Reference in a new issue