Remove deprecated MVC routes, views, and models

This commit is contained in:
Pat Hartl 2023-02-17 00:41:39 -06:00
parent 4efbbe8a4e
commit 1158886c52
41 changed files with 0 additions and 3477 deletions

View file

@ -1,51 +0,0 @@
using LANCommander.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LANCommander.Controllers.Api
{
[Route("api/[controller]")]
[ApiController]
public class UploadController : ControllerBase
{
private const string UploadDirectory = "Upload";
[HttpPost("Init")]
public string Init()
{
var key = Guid.NewGuid().ToString();
if (!Directory.Exists(UploadDirectory))
Directory.CreateDirectory(UploadDirectory);
if (!System.IO.File.Exists(Path.Combine(UploadDirectory, key)))
System.IO.File.Create(Path.Combine(UploadDirectory, key)).Close();
return key;
}
[HttpPost("Chunk")]
public async Task Chunk([FromForm] ChunkUpload chunk)
{
var filePath = Path.Combine(UploadDirectory, chunk.Key.ToString());
if (!System.IO.File.Exists(filePath))
throw new Exception("Destination file not initialized.");
Request.EnableBuffering();
using (var ms = new MemoryStream())
{
await chunk.File.CopyToAsync(ms);
var data = ms.ToArray();
using (var fs = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None))
{
fs.Position = chunk.Start;
fs.Write(data, 0, data.Length);
}
}
}
}
}

View file

@ -1,140 +0,0 @@
using LANCommander.Data;
using LANCommander.Data.Models;
using LANCommander.Extensions;
using LANCommander.Models;
using LANCommander.SDK;
using LANCommander.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.IO.Compression;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class ArchivesController : Controller
{
private readonly GameService GameService;
private readonly ArchiveService ArchiveService;
public ArchivesController(GameService gameService, ArchiveService archiveService)
{
GameService = gameService;
ArchiveService = archiveService;
}
public async Task<IActionResult> Add(Guid? id)
{
if (id == null)
return NotFound();
var game = await GameService.Get(id.GetValueOrDefault());
if (game == null)
return NotFound();
Archive lastVersion = null;
if (game.Archives != null && game.Archives.Count > 0)
lastVersion = game.Archives.OrderByDescending(a => a.CreatedOn).First();
return View(new Archive()
{
Game = game,
GameId = game.Id,
LastVersion = lastVersion,
});
}
[HttpPost]
public async Task<IActionResult> Add(Guid? id, Archive archive)
{
archive.Id = Guid.Empty;
var game = await GameService.Get(id.GetValueOrDefault());
if (game == null)
return NotFound();
archive.Game = game;
archive.GameId = game.Id;
if (game.Archives != null && game.Archives.Any(a => a.Version == archive.Version))
ModelState.AddModelError("Version", "An archive for this game is already using that version.");
if (ModelState.IsValid)
{
await ArchiveService.Update(archive);
return RedirectToAction("Edit", "Games", new { id = id });
}
return View(archive);
}
public async Task<IActionResult> Download(Guid id)
{
var archive = await ArchiveService.Get(id);
var content = new FileStream($"Upload/{archive.ObjectKey}".ToPath(), FileMode.Open, FileAccess.Read, FileShare.Read);
return File(content, "application/octet-stream", $"{archive.Game.Title.SanitizeFilename()}.zip");
}
public async Task<IActionResult> Delete(Guid? id)
{
var archive = await ArchiveService.Get(id.GetValueOrDefault());
var gameId = archive.Game.Id;
await ArchiveService.Delete(archive);
return RedirectToAction("Edit", "Games", new { id = gameId });
}
public async Task<IActionResult> Browse(Guid id)
{
var archive = await ArchiveService.Get(id);
return View(archive);
}
public async Task<IActionResult> Validate(Guid id, Archive archive)
{
var path = $"Upload/{id}".ToPath();
string manifestContents = String.Empty;
long compressedSize = 0;
long uncompressedSize = 0;
if (!System.IO.File.Exists(path))
return BadRequest("Specified object does not exist");
var game = await GameService.Get(archive.GameId);
if (game == null)
return BadRequest("The related game is missing or corrupt.");
archive.GameId = game.Id;
archive.Id = Guid.Empty;
archive.CompressedSize = compressedSize;
archive.UncompressedSize = uncompressedSize;
archive.ObjectKey = id.ToString();
try
{
archive = await ArchiveService.Add(archive);
}
catch (Exception ex)
{
}
return Json(new
{
Id = archive.Id,
ObjectKey = archive.ObjectKey,
});
}
}
}

View file

@ -1,166 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using LANCommander.Data;
using LANCommander.Data.Models;
using Microsoft.AspNetCore.Authorization;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class CompaniesController : Controller
{
private readonly DatabaseContext _context;
public CompaniesController(DatabaseContext context)
{
_context = context;
}
// GET: Companies
public async Task<IActionResult> Index()
{
return _context.Companies != null ?
View(await _context.Companies.ToListAsync()) :
Problem("Entity set 'DatabaseContext.Companies' is null.");
}
// GET: Companies/Details/5
public async Task<IActionResult> Details(Guid? id)
{
if (id == null || _context.Companies == null)
{
return NotFound();
}
var company = await _context.Companies
.FirstOrDefaultAsync(m => m.Id == id);
if (company == null)
{
return NotFound();
}
return View(company);
}
// GET: Companies/Create
public IActionResult Create()
{
return View();
}
// POST: Companies/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Name,Id,CreatedOn,CreatedById,UpdatedOn,UpdatedById")] Company company)
{
if (ModelState.IsValid)
{
company.Id = Guid.NewGuid();
_context.Add(company);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(company);
}
// GET: Companies/Edit/5
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null || _context.Companies == null)
{
return NotFound();
}
var company = await _context.Companies.FindAsync(id);
if (company == null)
{
return NotFound();
}
return View(company);
}
// POST: Companies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, [Bind("Name,Id,CreatedOn,CreatedById,UpdatedOn,UpdatedById")] Company company)
{
if (id != company.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(company);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CompanyExists(company.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(company);
}
// GET: Companies/Delete/5
public async Task<IActionResult> Delete(Guid? id)
{
if (id == null || _context.Companies == null)
{
return NotFound();
}
var company = await _context.Companies
.FirstOrDefaultAsync(m => m.Id == id);
if (company == null)
{
return NotFound();
}
return View(company);
}
// POST: Companies/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(Guid id)
{
if (_context.Companies == null)
{
return Problem("Entity set 'DatabaseContext.Companies' is null.");
}
var company = await _context.Companies.FindAsync(id);
if (company != null)
{
_context.Companies.Remove(company);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool CompanyExists(Guid id)
{
return (_context.Companies?.Any(e => e.Id == id)).GetValueOrDefault();
}
}
}

View file

@ -1,577 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using LANCommander.Data;
using LANCommander.Data.Models;
using Microsoft.AspNetCore.Authorization;
using LANCommander.Services;
using System.Drawing;
using LANCommander.Models;
using LANCommander.Data.Enums;
using LANCommander.PCGamingWiki;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class GamesController : Controller
{
private readonly GameService GameService;
private readonly ArchiveService ArchiveService;
private readonly CategoryService CategoryService;
private readonly TagService TagService;
private readonly GenreService GenreService;
private readonly CompanyService CompanyService;
private readonly IGDBService IGDBService;
private readonly PCGamingWikiClient PCGamingWikiClient;
public GamesController(GameService gameService, ArchiveService archiveService, CategoryService categoryService, TagService tagService, GenreService genreService, CompanyService companyService, IGDBService igdbService)
{
GameService = gameService;
ArchiveService = archiveService;
CategoryService = categoryService;
TagService = tagService;
GenreService = genreService;
CompanyService = companyService;
IGDBService = igdbService;
PCGamingWikiClient = new PCGamingWikiClient();
}
// GET: Games
public async Task<IActionResult> Index()
{
return View(GameService.Get());
}
public async Task<IActionResult> Add(long? igdbid)
{
var viewModel = new GameViewModel()
{
Game = new Game(),
Developers = new List<SelectListItem>(),
Publishers = new List<SelectListItem>(),
Genres = new List<SelectListItem>(),
Tags = new List<SelectListItem>(),
};
if (igdbid == null)
{
viewModel.Game = new Game()
{
Actions = new List<Data.Models.Action>(),
MultiplayerModes = new List<Data.Models.MultiplayerMode>()
};
viewModel.Developers = CompanyService.Get().OrderBy(c => c.Name).Select(c => new SelectListItem() { Text = c.Name, Value = c.Name }).ToList();
viewModel.Publishers = CompanyService.Get().OrderBy(c => c.Name).Select(c => new SelectListItem() { Text = c.Name, Value = c.Name }).ToList();
viewModel.Genres = GenreService.Get().OrderBy(g => g.Name).Select(g => new SelectListItem() { Text = g.Name, Value = g.Name }).ToList();
viewModel.Tags = TagService.Get().OrderBy(t => t.Name).Select(t => new SelectListItem() { Text = t.Name, Value = t.Name }).ToList();
return View(viewModel);
}
var result = await IGDBService.Get(igdbid.Value, "genres.*", "game_modes.*", "multiplayer_modes.*", "release_dates.*", "platforms.*", "keywords.*", "involved_companies.*", "involved_companies.company.*", "cover.*");
viewModel.Game = new Game()
{
IGDBId = result.Id.GetValueOrDefault(),
Title = result.Name,
Description = result.Summary,
ReleasedOn = result.FirstReleaseDate.GetValueOrDefault().UtcDateTime,
Actions = new List<Data.Models.Action>(),
MultiplayerModes = new List<MultiplayerMode>()
};
var playerCounts = await PCGamingWikiClient.GetMultiplayerPlayerCounts(result.Name);
if (playerCounts != null)
{
foreach (var playerCount in playerCounts)
{
MultiplayerType type;
switch (playerCount.Key)
{
case "Local Play":
type = MultiplayerType.Local;
break;
case "LAN Play":
type = MultiplayerType.Lan;
break;
case "Online Play":
type = MultiplayerType.Online;
break;
default:
continue;
}
viewModel.Game.MultiplayerModes.Add(new MultiplayerMode()
{
Type = type,
MaxPlayers = playerCount.Value,
MinPlayers = 2
});
}
}
if (result.GameModes != null && result.GameModes.Values != null)
viewModel.Game.Singleplayer = result.GameModes.Values.Any(gm => gm.Name == "Singleplayer");
#region Multiplayer Modes
if (result.MultiplayerModes != null && result.MultiplayerModes.Values != null)
{
var lan = result.MultiplayerModes.Values.Where(mm => mm.LanCoop.GetValueOrDefault()).OrderByDescending(mm => mm.OnlineMax).FirstOrDefault();
var online = result.MultiplayerModes.Values.Where(mm => mm.OnlineCoop.GetValueOrDefault()).OrderByDescending(mm => mm.OnlineMax).FirstOrDefault();
var offline = result.MultiplayerModes.Values.Where(mm => mm.OfflineCoop.GetValueOrDefault()).OrderByDescending(mm => mm.OnlineMax).FirstOrDefault();
if (lan != null)
{
viewModel.Game.MultiplayerModes.Add(new MultiplayerMode()
{
Type = MultiplayerType.Lan,
MaxPlayers = lan.OnlineMax.GetValueOrDefault(),
});
}
if (online != null)
{
viewModel.Game.MultiplayerModes.Add(new MultiplayerMode()
{
Type = MultiplayerType.Online,
MaxPlayers = online.OnlineMax.GetValueOrDefault(),
});
}
if (offline != null)
{
viewModel.Game.MultiplayerModes.Add(new MultiplayerMode()
{
Type = MultiplayerType.Local,
MaxPlayers = offline.OfflineMax.GetValueOrDefault(),
});
}
}
#endregion
#region Publishers & Developers
var companies = CompanyService.Get();
if (result.InvolvedCompanies != null && result.InvolvedCompanies.Values != null)
{
// Make sure companie
var developerNames = result.InvolvedCompanies.Values.Where(c => c.Developer.GetValueOrDefault()).Select(c => c.Company.Value.Name);
var publisherNames = result.InvolvedCompanies.Values.Where(c => c.Publisher.GetValueOrDefault()).Select(c => c.Company.Value.Name);
viewModel.Developers.AddRange(companies.Select(c => new SelectListItem()
{
Text = c.Name,
Value = c.Name,
Selected = developerNames.Contains(c.Name),
}));
viewModel.Publishers.AddRange(companies.Select(c => new SelectListItem()
{
Text = c.Name,
Value = c.Name,
Selected = publisherNames.Contains(c.Name),
}));
foreach (var developer in developerNames)
{
if (!viewModel.Developers.Any(d => d.Value == developer))
{
viewModel.Developers.Add(new SelectListItem()
{
Text = developer,
Value = developer,
Selected = true
});
}
}
foreach (var publisher in publisherNames)
{
if (!viewModel.Publishers.Any(d => d.Value == publisher))
{
viewModel.Publishers.Add(new SelectListItem()
{
Text = publisher,
Value = publisher,
Selected = true
});
}
}
viewModel.Developers = viewModel.Developers.OrderBy(d => d.Value).ToList();
viewModel.Publishers = viewModel.Publishers.OrderBy(d => d.Value).ToList();
}
#endregion
#region Genres
var genres = GenreService.Get();
if (result.Genres != null && result.Genres.Values != null)
{
var genreNames = result.Genres.Values.Select(g => g.Name);
viewModel.Genres.AddRange(genres.Select(g => new SelectListItem()
{
Text = g.Name,
Value = g.Name,
Selected = genreNames.Contains(g.Name),
}));
foreach (var genre in genreNames)
{
if (!viewModel.Genres.Any(g => g.Value == genre))
{
viewModel.Genres.Add(new SelectListItem()
{
Text = genre,
Value = genre,
Selected = true
});
}
}
viewModel.Genres = viewModel.Genres.OrderBy(g => g.Value).ToList();
}
#endregion
#region Tags
var tags = TagService.Get();
if (result.Keywords != null && result.Keywords.Values != null)
{
var tagNames = result.Keywords.Values.Select(t => t.Name).Take(20);
viewModel.Tags.AddRange(genres.Select(t => new SelectListItem()
{
Text = t.Name,
Value = t.Name,
Selected = tagNames.Contains(t.Name),
}));
foreach (var tag in tagNames)
{
if (!viewModel.Tags.Any(t => t.Value == tag))
{
viewModel.Tags.Add(new SelectListItem()
{
Text = tag,
Value = tag,
Selected = true
});
}
}
}
#endregion
return View(viewModel);
}
// POST: Games/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Add(GameViewModel viewModel)
{
if (ModelState.IsValid)
{
var game = await GameService.Add(viewModel.Game);
if (viewModel.SelectedDevelopers != null && viewModel.SelectedDevelopers.Length > 0)
game.Developers = viewModel.SelectedDevelopers.Select(async d => await CompanyService.AddMissing(x => x.Name == d, new Company() { Name = d })).Select(t => t.Result).ToList();
if (viewModel.SelectedPublishers != null && viewModel.SelectedPublishers.Length > 0)
game.Publishers = viewModel.SelectedPublishers.Select(async p => await CompanyService.AddMissing(x => x.Name == p, new Company() { Name = p })).Select(t => t.Result).ToList();
if (viewModel.SelectedGenres != null && viewModel.SelectedGenres.Length > 0)
game.Genres = viewModel.SelectedGenres.Select(async g => await GenreService.AddMissing(x => x.Name == g, new Genre() { Name = g })).Select(t => t.Result).ToList();
if (viewModel.SelectedTags != null && viewModel.SelectedTags.Length > 0)
game.Tags = viewModel.SelectedTags.Select(async t => await TagService.AddMissing(x => x.Name == t, new Tag() { Name = t })).Select(t => t.Result).ToList();
await GameService.Update(game);
return RedirectToAction(nameof(Edit), new { id = game.Id });
}
return View(viewModel.Game);
}
// GET: Games/Edit/5
public async Task<IActionResult> Edit(Guid? id)
{
var viewModel = new GameViewModel();
viewModel.Game = await GameService.Get(id.GetValueOrDefault());
if (viewModel.Game == null)
return NotFound();
viewModel.Developers = CompanyService.Get()
.OrderBy(c => c.Name)
.Select(c => new SelectListItem() { Text = c.Name, Value = c.Name, Selected = viewModel.Game.Developers.Any(d => d.Id == c.Id) })
.ToList();
viewModel.Publishers = CompanyService.Get()
.OrderBy(c => c.Name)
.Select(c => new SelectListItem() { Text = c.Name, Value = c.Name, Selected = viewModel.Game.Publishers.Any(d => d.Id == c.Id) })
.ToList();
viewModel.Genres = GenreService.Get()
.OrderBy(g => g.Name)
.Select(g => new SelectListItem() { Text = g.Name, Value = g.Name, Selected = viewModel.Game.Genres.Any(x => x.Id == g.Id) })
.ToList();
viewModel.Tags = TagService.Get()
.OrderBy(t => t.Name)
.Select(t => new SelectListItem() { Text = t.Name, Value = t.Name, Selected = viewModel.Game.Tags.Any(x => x.Id == t.Id) })
.ToList();
return View(viewModel);
}
// POST: Games/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, GameViewModel viewModel)
{
if (id != viewModel.Game.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
var game = GameService.Get(g => g.Id == viewModel.Game.Id).FirstOrDefault();
game.Title = viewModel.Game.Title;
game.SortTitle = viewModel.Game.SortTitle;
game.DirectoryName = viewModel.Game.DirectoryName;
game.Icon = viewModel.Game.Icon;
game.Description = viewModel.Game.Description;
game.ReleasedOn = viewModel.Game.ReleasedOn;
game.Singleplayer = viewModel.Game.Singleplayer;
#region Update Developers
if (viewModel.SelectedDevelopers == null)
viewModel.SelectedDevelopers = new string[0];
foreach (var developer in game.Developers)
{
if (!viewModel.SelectedDevelopers.Any(d => d == developer.Name))
game.Developers.Remove(developer);
}
foreach (var newDeveloper in viewModel.SelectedDevelopers.Where(sd => !game.Developers.Any(d => d.Name == sd)))
{
game.Developers.Add(new Company()
{
Name = newDeveloper
});
}
#endregion
#region Update Publishers
if (viewModel.SelectedPublishers == null)
viewModel.SelectedPublishers = new string[0];
foreach (var publisher in game.Publishers)
{
if (!viewModel.SelectedPublishers.Any(p => p == publisher.Name))
game.Publishers.Remove(publisher);
}
foreach (var newPublisher in viewModel.SelectedPublishers.Where(sp => !game.Publishers.Any(p => p.Name == sp)))
{
game.Publishers.Add(new Company()
{
Name = newPublisher
});
}
#endregion
#region Update Genres
if (viewModel.SelectedGenres == null)
viewModel.SelectedGenres = new string[0];
foreach (var genre in game.Genres)
{
if (!viewModel.SelectedGenres.Any(g => g == genre.Name))
game.Genres.Remove(genre);
}
foreach (var newGenre in viewModel.SelectedGenres.Where(sg => !game.Genres.Any(g => g.Name == sg)))
{
game.Genres.Add(new Genre()
{
Name = newGenre
});
}
#endregion
#region Update Tags
if (viewModel.SelectedTags == null)
viewModel.SelectedTags = new string[0];
foreach (var tag in game.Tags)
{
if (!viewModel.SelectedTags.Any(t => t == tag.Name))
game.Tags.Remove(tag);
}
foreach (var newTag in viewModel.SelectedTags.Where(st => !game.Tags.Any(t => t.Name == st)))
{
game.Tags.Add(new Tag()
{
Name = newTag
});
}
#endregion
#region Update Actions
if (game.Actions != null)
{
game.Actions.Clear();
if (viewModel.Game.Actions != null)
{
foreach (var action in viewModel.Game.Actions)
{
game.Actions.Add(action);
}
}
}
#endregion
#region Update MultiplayerModes
if (game.MultiplayerModes != null)
{
game.MultiplayerModes.Clear();
if (viewModel.Game.MultiplayerModes != null)
{
foreach (var multiplayerMode in viewModel.Game.MultiplayerModes)
{
game.MultiplayerModes.Add(multiplayerMode);
}
}
}
#endregion
await GameService.Update(game);
return RedirectToAction(nameof(Edit), new { id = id });
}
return View(viewModel);
}
// GET: Games/Delete/5
public async Task<IActionResult> Delete(Guid? id)
{
var game = await GameService.Get(id.GetValueOrDefault());
if (game == null)
{
return NotFound();
}
return View(game);
}
// POST: Games/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(Guid id)
{
var game = await GameService.Get(id);
if (game == null)
return NotFound();
await GameService.Delete(game);
return RedirectToAction(nameof(Index));
}
[HttpPost]
public async Task<IActionResult> Lookup(Game game)
{
var viewModel = new GameLookupResultsViewModel()
{
Search = game.Title
};
var results = await IGDBService.Search(game.Title, "involved_companies.*", "involved_companies.company.*");
if (results == null)
return View(new List<Game>());
viewModel.Results = results.Select(r =>
{
var result = new Game()
{
IGDBId = r.Id.GetValueOrDefault(),
Title = r.Name,
ReleasedOn = r.FirstReleaseDate.GetValueOrDefault().UtcDateTime,
Developers = new List<Company>()
};
if (r.InvolvedCompanies != null && r.InvolvedCompanies.Values != null)
{
result.Developers = r.InvolvedCompanies.Values.Where(c => c.Developer.HasValue && c.Developer.GetValueOrDefault() && c.Company != null && c.Company.Value != null).Select(c => new Company()
{
Name = c.Company.Value.Name
}).ToList();
}
return result;
});
return View(viewModel);
}
/// <summary>
/// Provides a list of possible games based on the given name
/// </summary>
/// <param name="name">Name of the game to lookup against IGDB</param>
/// <returns></returns>
public async Task<IActionResult> SearchMetadata(string name)
{
var metadata = await IGDBService.Search(name, "genres.*", "multiplayer_modes.*", "release_dates.*", "platforms.*", "keywords.*", "involved_companies.*", "involved_companies.company.*", "cover.*");
if (metadata == null)
return NotFound();
return Json(metadata);
}
public async Task<IActionResult> GetIcon(Guid id)
{
try
{
var game = await GameService.Get(id);
return File(GameService.GetIcon(game), "image/png");
}
catch (FileNotFoundException ex)
{
return NotFound();
}
}
}
}

View file

@ -1,110 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using LANCommander.Data;
using LANCommander.Data.Models;
using Microsoft.AspNetCore.Authorization;
using LANCommander.Models;
using LANCommander.Services;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class KeysController : Controller
{
private readonly DatabaseContext Context;
private readonly KeyService KeyService;
public KeysController(DatabaseContext context, KeyService keyService)
{
Context = context;
KeyService = keyService;
}
public async Task<IActionResult> Details(Guid? id)
{
using (var repo = new Repository<Game>(Context, HttpContext))
{
var game = await repo.Find(id.GetValueOrDefault());
if (game == null)
return NotFound();
return View(game);
}
}
public async Task<IActionResult> Edit(Guid? id)
{
using (var repo = new Repository<Game>(Context, HttpContext))
{
var game = await repo.Find(id.GetValueOrDefault());
if (game == null)
return NotFound();
var viewModel = new EditKeysViewModel()
{
Game = game,
Keys = String.Join("\n", game.Keys.OrderByDescending(k => k.ClaimedOn).Select(k => k.Value))
};
return View(viewModel);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, EditKeysViewModel viewModel)
{
var keys = viewModel.Keys.Split("\n").Select(k => k.Trim()).Where(k => !String.IsNullOrWhiteSpace(k));
using (var gameRepo = new Repository<Game>(Context, HttpContext))
{
var game = await gameRepo.Find(id);
if (game == null)
return NotFound();
using (var keyRepo = new Repository<Key>(Context, HttpContext))
{
var existingKeys = keyRepo.Get(k => k.Game.Id == id).ToList();
var keysDeleted = existingKeys.Where(k => !keys.Contains(k.Value));
var keysAdded = keys.Where(k => !existingKeys.Any(e => e.Value == k));
foreach (var key in keysDeleted)
keyRepo.Delete(key);
foreach (var key in keysAdded)
await keyRepo.Add(new Key()
{
Game = game,
Value = key,
});
await keyRepo.SaveChanges();
}
}
return RedirectToAction("Edit", "Games", new { id = id });
}
public async Task<IActionResult> Release(Guid id)
{
var existing = await KeyService.Get(id);
if (existing == null)
return NotFound();
await KeyService.Release(id);
return RedirectToAction("Details", "Keys", new { id = existing.Game.Id });
}
}
}

View file

@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using LANCommander.Data;
using LANCommander.Data.Models;
using Microsoft.AspNetCore.Authorization;
using LANCommander.Models;
using LANCommander.Services;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class ScriptsController : BaseController
{
private readonly GameService GameService;
private readonly ScriptService ScriptService;
public ScriptsController(GameService gameService, ScriptService scriptService)
{
GameService = gameService;
ScriptService = scriptService;
}
public async Task<IActionResult> Add(Guid? id)
{
var game = await GameService.Get(id.GetValueOrDefault());
if (game == null)
return NotFound();
var script = new Script()
{
GameId = game.Id,
Game = game
};
return View(script);
}
[HttpPost]
public async Task<IActionResult> Add(Script script)
{
script.Id = Guid.Empty;
if (ModelState.IsValid)
{
script = await ScriptService.Add(script);
return RedirectToAction("Edit", "Games", new { id = script.GameId });
}
return View(script);
}
public async Task<IActionResult> Edit(Guid? id)
{
var script = await ScriptService.Get(id.GetValueOrDefault());
if (script == null)
return NotFound();
return View(script);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, Script script)
{
if (ModelState.IsValid)
{
await ScriptService.Update(script);
Alert("The script has been saved!", "success");
return RedirectToAction("Edit", "Games", new { id = script.GameId });
}
script.Game = await GameService.Get(script.GameId.GetValueOrDefault());
return View(script);
}
public async Task<IActionResult> Delete(Guid? id)
{
var script = await ScriptService.Get(id.GetValueOrDefault());
if (script == null)
return NotFound();
var gameId = script.GameId;
await ScriptService.Delete(script);
return RedirectToAction("Edit", "Games", new { id = gameId });
}
}
}

View file

@ -1,166 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using LANCommander.Data;
using LANCommander.Data.Models;
using Microsoft.AspNetCore.Authorization;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class TagsController : Controller
{
private readonly DatabaseContext _context;
public TagsController(DatabaseContext context)
{
_context = context;
}
// GET: Tags
public async Task<IActionResult> Index()
{
return _context.Tags != null ?
View(await _context.Tags.ToListAsync()) :
Problem("Entity set 'DatabaseContext.Tags' is null.");
}
// GET: Tags/Details/5
public async Task<IActionResult> Details(Guid? id)
{
if (id == null || _context.Tags == null)
{
return NotFound();
}
var tag = await _context.Tags
.FirstOrDefaultAsync(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
return View(tag);
}
// GET: Tags/Create
public IActionResult Create()
{
return View();
}
// POST: Tags/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Name,Id,CreatedOn,CreatedById,UpdatedOn,UpdatedById")] Tag tag)
{
if (ModelState.IsValid)
{
tag.Id = Guid.NewGuid();
_context.Add(tag);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(tag);
}
// GET: Tags/Edit/5
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null || _context.Tags == null)
{
return NotFound();
}
var tag = await _context.Tags.FindAsync(id);
if (tag == null)
{
return NotFound();
}
return View(tag);
}
// POST: Tags/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, [Bind("Name,Id,CreatedOn,CreatedById,UpdatedOn,UpdatedById")] Tag tag)
{
if (id != tag.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(tag);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TagExists(tag.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(tag);
}
// GET: Tags/Delete/5
public async Task<IActionResult> Delete(Guid? id)
{
if (id == null || _context.Tags == null)
{
return NotFound();
}
var tag = await _context.Tags
.FirstOrDefaultAsync(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
return View(tag);
}
// POST: Tags/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(Guid id)
{
if (_context.Tags == null)
{
return Problem("Entity set 'DatabaseContext.Tags' is null.");
}
var tag = await _context.Tags.FindAsync(id);
if (tag != null)
{
_context.Tags.Remove(tag);
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool TagExists(Guid id)
{
return (_context.Tags?.Any(e => e.Id == id)).GetValueOrDefault();
}
}
}

View file

@ -1,56 +0,0 @@
using LANCommander.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LANCommander.Controllers
{
[Authorize(Roles = "Administrator")]
public class UploadController : Controller
{
private const string UploadDirectory = "Upload";
public JsonResult Init()
{
var key = Guid.NewGuid().ToString();
if (!Directory.Exists(UploadDirectory))
Directory.CreateDirectory(UploadDirectory);
if (!System.IO.File.Exists(Path.Combine(UploadDirectory, key)))
System.IO.File.Create(Path.Combine(UploadDirectory, key)).Close();
return Json(new
{
Key = key
});
}
public async Task<IActionResult> Chunk([FromForm] ChunkUpload chunk)
{
var filePath = Path.Combine(UploadDirectory, chunk.Key.ToString());
if (!System.IO.File.Exists(filePath))
return BadRequest("Destination file not initialized.");
Request.EnableBuffering();
using (var ms = new MemoryStream())
{
await chunk.File.CopyToAsync(ms);
var data = ms.ToArray();
using (var fs = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None))
{
fs.Position = chunk.Start;
fs.Write(data, 0, data.Length);
}
}
Thread.Sleep(100);
return Json("Done!");
}
}
}

View file

@ -59,12 +59,6 @@
<ProjectReference Include="..\LANCommander.SDK\LANCommander.SDK.csproj" />
</ItemGroup>
<ItemGroup>
<TypeScriptCompile Update="wwwroot\js\Upload.ts">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</TypeScriptCompile>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
</Project>

View file

@ -1,11 +0,0 @@
namespace LANCommander.Models
{
public class ChunkUpload
{
public long Start { get; set; }
public long End { get; set; }
public long Total { get; set; }
public Guid Key { get; set; }
public IFormFile File { get; set; }
}
}

View file

@ -1,10 +0,0 @@
using LANCommander.Data.Models;
namespace LANCommander.Models
{
public class EditKeysViewModel
{
public Game Game { get; set; }
public string Keys { get; set; }
}
}

View file

@ -1,10 +0,0 @@
using LANCommander.Data.Models;
namespace LANCommander.Models
{
public class GameLookupResultsViewModel
{
public string Search { get; set; }
public IEnumerable<Game> Results { get; set; }
}
}

View file

@ -1,21 +0,0 @@
using LANCommander.Data.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace LANCommander.Models
{
public class GameViewModel
{
public long? IgdbId { get; set; }
public Game Game { get; set; }
public List<SelectListItem>? Genres { get; set; }
public List<SelectListItem>? Tags { get; set; }
public List<SelectListItem>? Categories { get; set; }
public List<SelectListItem>? Developers { get; set; }
public List<SelectListItem>? Publishers { get; set; }
public string[]? SelectedGenres { get; set; }
public string[]? SelectedTags { get; set; }
public string[]? SelectedCategories { get; set; }
public string[]? SelectedDevelopers { get; set; }
public string[]? SelectedPublishers { get; set; }
}
}

View file

@ -1,23 +0,0 @@
@page "/Games/{id:guid}/Browse"
@inject GameService GameService
<Card Title="Archive Browser" SubTitle="@Game.Title">
<Table>
<ArchiveBrowser ArchiveId="@Archive.Id" />
</Table>
</Card>
@code {
[Parameter] public Guid Id { get; set; }
private Game Game { get; set; }
private Archive Archive { get; set; }
protected override async Task OnInitializedAsync()
{
Game = await GameService.Get(Id);
if (Game.Archives != null && Game.Archives.Count > 0)
Archive = Game.Archives.OrderByDescending(a => a.CreatedOn).First();
}
}

View file

@ -1,127 +0,0 @@
@model LANCommander.Data.Models.Archive
@{
ViewData["Title"] = "Add Archive";
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Game.Title</div>
<h2 class="page-title">
Add Archive
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Add" enctype="multipart/form-data" class="card">
<fieldset>
<div class="card-body">
<div class="row">
<div class="col-12">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="mb-3">
<label asp-for="Version" class="control-label"></label>
<input asp-for="Version" class="form-control" />
<span asp-validation-for="Version" class="text-danger"></span>
@if (Model.LastVersion != null && !String.IsNullOrWhiteSpace(Model.LastVersion.Version))
{
<small class="form-hint">Last version: @Model.LastVersion.Version</small>
}
</div>
<div class="mb-3">
<label asp-for="Changelog" class="control-label"></label>
<textarea asp-for="Changelog" class="form-control"></textarea>
<span asp-validation-for="Changelog" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="File" class="control-label">File</label>
<input type="file" id="File" class="form-control" />
</div>
<div>
<div class="progress h-4">
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
</div>
</div>
<input type="hidden" asp-for="GameId" />
<input type="hidden" asp-for="LastVersion.Id" />
<input type="hidden" asp-for="ObjectKey" />
</div>
</div>
</div>
<div class="card-footer">
<div class="d-flex">
<a asp-action="Edit" asp-controller="Games" asp-route-id="@Model.Game.Id" class="btn btn-ghost-primary">Cancel</a>
<button class="btn btn-primary ms-auto" id="UploadButton">Upload</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/js/Upload.js"></script>
<script>
var uploader = new Uploader();
uploader.Init('File', 'UploadButton');
uploader.OnStart = () => {
$('fieldset').prop('disabled', true);
$('.progress-bar')
.css('width', '0%')
.removeClass('bg-success')
.removeClass('bg-danger')
.addClass('progress-bar-striped')
.addClass('progress-bar-animated')
.text('0%');
};
uploader.OnComplete = (id, key) => {
$('#Id').val(id);
$('#ObjectKey').val(key);
$('.progress-bar')
.css('width', '100%')
.removeClass('progress-bar-striped')
.removeClass('progress-bar-animated')
.addClass('bg-success')
.text('Upload Complete!');
setTimeout(() => {
window.location.href = '@Url.Action("Edit", "Games", new { id = Model.Game.Id })';
}, 2000);
};
uploader.OnProgress = (percent) => {
$('.progress-bar')
.css('width', `${percent * 100}%`)
.text(`${Math.round(percent * 100)}%`);
};
uploader.OnError = () => {
$('fieldset').prop('disabled', false);
$('.progress-bar')
.css('width', '100%')
.removeClass('progress-bar-striped')
.removeClass('progress-bar-animated')
.addClass('bg-danger')
.text('Upload Error!');
};
</script>
}

View file

@ -1,32 +0,0 @@
@using LANCommander.Components;
@model LANCommander.Data.Models.Archive
@{
ViewData["Title"] = "Browse Archive";
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Game.Title</div>
<h2 class="page-title">
Browse Archive
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<div class="card">
<component type="typeof(ArchiveBrowser)" render-mode="Server" param-ArchiveId="Model.Id" />
</div>
</div>
</div>
</div>
</div>

View file

@ -1,43 +0,0 @@
@model LANCommander.Data.Models.Company
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Company</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedOn" class="control-label"></label>
<input asp-for="CreatedOn" class="form-control" />
<span asp-validation-for="CreatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UpdatedOn" class="control-label"></label>
<input asp-for="UpdatedOn" class="form-control" />
<span asp-validation-for="UpdatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

View file

@ -1,39 +0,0 @@
@model LANCommander.Data.Models.Company
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Company</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Name)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.CreatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.CreatedOn)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.UpdatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.UpdatedOn)
</dd>
</dl>
<form asp-action="Delete">
<input type="hidden" asp-for="Id" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-action="Index">Back to List</a>
</form>
</div>

View file

@ -1,36 +0,0 @@
@model LANCommander.Data.Models.Company
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Company</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Name)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.CreatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.CreatedOn)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.UpdatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.UpdatedOn)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model?.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>

View file

@ -1,44 +0,0 @@
@model LANCommander.Data.Models.Company
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Company</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="CreatedOn" class="control-label"></label>
<input asp-for="CreatedOn" class="form-control" />
<span asp-validation-for="CreatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UpdatedOn" class="control-label"></label>
<input asp-for="UpdatedOn" class="form-control" />
<span asp-validation-for="UpdatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

View file

@ -1,47 +0,0 @@
@model IEnumerable<LANCommander.Data.Models.Company>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.CreatedOn)
</th>
<th>
@Html.DisplayNameFor(model => model.UpdatedOn)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.CreatedOn)
</td>
<td>
@Html.DisplayFor(modelItem => item.UpdatedOn)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>

View file

@ -1,125 +0,0 @@
@using LANCommander.Components
@model LANCommander.Models.GameViewModel
@{
ViewData["Title"] = "Add Game";
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<h2 class="page-title">
Add Game
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Add" class="card">
<div class="card-body">
<div class="row">
<div class="col-12">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="mb-3">
<label asp-for="Game.Title" class="control-label"></label>
<div class="input-group">
<input asp-for="Game.Title" class="form-control" />
<button class="btn" type="submit" asp-action="Lookup">Lookup</button>
</div>
<span asp-validation-for="Game.Title" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.SortTitle" class="control-label"></label>
<input asp-for="Game.SortTitle" class="form-control" />
<span asp-validation-for="Game.SortTitle" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.Icon" class="control-label"></label>
<input asp-for="Game.Icon" class="form-control" />
<span asp-validation-for="Game.Icon" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.Description" class="control-label"></label>
<textarea asp-for="Game.Description" class="form-control" data-bs-toggle="autosize"></textarea>
<span asp-validation-for="Game.Description" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.ReleasedOn" class="control-label"></label>
<input asp-for="Game.ReleasedOn" class="form-control" />
<span asp-validation-for="Game.ReleasedOn" class="text-danger"></span>
</div>
<div class="mb-3">
<label class="form-check">
<input asp-for="Game.Singleplayer" type="checkbox" class="form-check-input" />
<span class="form-check-label">Singleplayer</span>
<span class="form-check-description">Game has a singleplayer mode</span>
</label>
</div>
<div class="mb-3">
<label asp-for="Developers" class="control-label"></label>
<input type="text" class="developer-select" />
<select asp-for="SelectedDevelopers" class="d-none"></select>
</div>
<div class="mb-3">
<label asp-for="Publishers" class="control-label"></label>
<input type="text" class="publisher-select" />
<select asp-for="SelectedPublishers" class="d-none"></select>
</div>
<div class="mb-3">
<label asp-for="Genres" class="control-label"></label>
<input type="text" class="genre-select" />
<select asp-for="SelectedGenres" class="d-none"></select>
</div>
<div class="mb-3">
<label asp-for="Tags" class="control-label"></label>
<input type="text" class="tag-select" />
<select asp-for="SelectedTags" class="d-none"></select>
</div>
</div>
</div>
</div>
<div class="card-header">
<h3 class="card-title">Actions</h3>
</div>
<component type="typeof(ActionEditor)" render-mode="Server" param-Actions="Model.Game.Actions.ToList()" param-GameId="Model.Game.Id" />
<div class="card-header">
<h3 class="card-title">Multiplayer Modes</h3>
</div>
<component type="typeof(MultiplayerModeEditor)" render-mode="Server" param-MultiplayerModes="Model.Game.MultiplayerModes" param-GameId="Model.Game.Id" />
<div class="card-footer">
<div class="d-flex">
<a asp-action="Index" class="btn btn-ghost-primary">Cancel</a>
<button type="submit" class="btn btn-primary ms-auto">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script>
new Select('.developer-select', @Html.Raw(Json.Serialize(Model.Developers)));
new Select('.publisher-select', @Html.Raw(Json.Serialize(Model.Publishers)));
new Select('.genre-select', @Html.Raw(Json.Serialize(Model.Genres)));
new Select('.tag-select', @Html.Raw(Json.Serialize(Model.Tags)));
</script>
}

View file

@ -1,65 +0,0 @@
@model LANCommander.Data.Models.Game
@{
ViewData["Title"] = "Delete";
}
<div class="container container-tight py-4">
<div class="page-header">
<div class="row align-items-center">
<div class="col">
<h2 class="page-title">Delete @Model.Title?</h2>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<p class="text-muted">Are you sure you want to delete this game?
@if (Model.Archives != null && Model.Archives.Count > 0)
{
<span>It will also delete the following archives:</span>
}
</p>
</div>
@if (Model.Archives != null && Model.Archives.Count > 0)
{
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
<thead>
<tr>
<th>Version</th>
<th>Uploaded By</th>
<th>Uploaded On</th>
<th>Size</th>
</tr>
</thead>
<tbody>
@foreach (var archive in Model.Archives.OrderByDescending(a => a.CreatedOn))
{
<tr>
<td>@Html.DisplayFor(m => archive.Version)</td>
<td>@Html.DisplayFor(m => archive.CreatedBy.UserName)</td>
<td>@Html.DisplayFor(m => archive.CreatedOn)</td>
<td>@ByteSizeLib.ByteSize.FromBytes(archive.CompressedSize)</td>
</tr>
}
</tbody>
</table>
</div>
}
<div class="card-footer">
<div class="d-flex justify-content-between">
<a asp-action="Index" class="btn btn-ghost-primary">Cancel</a>
<form asp-action="Delete">
<input type="hidden" asp-for="Id" />
<button type="submit" class="btn btn-danger ms-auto">Delete</button>
</form>
</div>
</div>
</div>
</div>

View file

@ -1,303 +0,0 @@
@using LANCommander.Components
@using LANCommander.Data.Models
@model LANCommander.Models.GameViewModel
@{
ViewData["Title"] = "Edit";
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<h2 class="page-title">
Edit Game
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Edit" class="card">
<div class="card-body">
<div class="row">
<div class="col-12">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="mb-3">
<label asp-for="Game.Title" class="control-label"></label>
<div class="input-group">
<input asp-for="Game.Title" class="form-control" />
<button class="btn" type="submit" asp-action="Lookup">Lookup</button>
</div>
<span asp-validation-for="Game.Title" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.SortTitle" class="control-label"></label>
<input asp-for="Game.SortTitle" class="form-control" />
<span asp-validation-for="Game.SortTitle" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.Icon" class="control-label"></label>
<input asp-for="Game.Icon" class="form-control" />
<span asp-validation-for="Game.Icon" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.Description" class="control-label"></label>
<textarea asp-for="Game.Description" class="form-control" data-bs-toggle="autosize"></textarea>
<span asp-validation-for="Game.Description" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="Game.ReleasedOn" class="control-label"></label>
<input asp-for="Game.ReleasedOn" class="form-control" />
<span asp-validation-for="Game.ReleasedOn" class="text-danger"></span>
</div>
<div class="mb-3">
<label class="form-check">
<input asp-for="Game.Singleplayer" type="checkbox" class="form-check-input" />
<span class="form-check-label">Singleplayer</span>
<span class="form-check-description">Game has a singleplayer mode</span>
</label>
</div>
<div class="mb-3">
<label asp-for="Developers" class="control-label"></label>
<input type="text" class="developer-select" />
<select asp-for="SelectedDevelopers" class="d-none"></select>
</div>
<div class="mb-3">
<label asp-for="Publishers" class="control-label"></label>
<input type="text" class="publisher-select" />
<select asp-for="SelectedPublishers" class="d-none"></select>
</div>
<div class="mb-3">
<label asp-for="Genres" class="control-label"></label>
<input type="text" class="genre-select" />
<select asp-for="SelectedGenres" class="d-none"></select>
</div>
<div class="mb-3">
<label asp-for="Tags" class="control-label"></label>
<input type="text" class="tag-select" />
<select asp-for="SelectedTags" class="d-none"></select>
</div>
<input type="hidden" asp-for="Game.Id" />
</div>
</div>
</div>
<div class="card-header">
<h3 class="card-title">Actions</h3>
</div>
<component type="typeof(ActionEditor)" render-mode="Server" param-Actions="Model.Game.Actions.ToList()" param-GameId="Model.Game.Id" />
<div class="card-header">
<h3 class="card-title">Multiplayer Modes</h3>
</div>
<component type="typeof(MultiplayerModeEditor)" render-mode="Server" param-MultiplayerModes="Model.Game.MultiplayerModes" param-GameId="Model.Game.Id" />
<div class="card-footer">
<div class="d-flex">
<a asp-action="Index" class="btn btn-ghost-primary">Cancel</a>
<button type="submit" class="btn btn-primary ms-auto">Save</button>
</div>
</div>
</form>
</div>
<div class="col-12">
<div class="card">
@if (Model.Game.Keys != null && Model.Game.Keys.Count > 0)
{
<div class="card-header">
<h3 class="card-title">Keys</h3>
<div class="card-actions">
<a asp-action="Details" asp-controller="Keys" asp-route-id="@Model.Game.Id" class="btn btn-ghost-primary">
Details
</a>
</div>
</div>
var keysAvailable = Model.Game.Keys.Count(k =>
{
return (k.AllocationMethod == KeyAllocationMethod.MacAddress && String.IsNullOrWhiteSpace(k.ClaimedByMacAddress)) ||
(k.AllocationMethod == KeyAllocationMethod.UserAccount && k.ClaimedByUser == null);
});
<div class="card-body">
<div class="datagrid text-center">
<div class="datagrid-item">
<div class="datagrid-title">Available</div>
<div class="datagrid-content">
@keysAvailable
</div>
</div>
<div class="datagrid-item">
<div class="datagrid-title">Claimed</div>
<div class="datagrid-content">
@(Model.Game.Keys.Count - keysAvailable)
</div>
</div>
<div class="datagrid-item">
<div class="datagrid-title">Total</div>
<div class="datagrid-content">
@Model.Game.Keys.Count
</div>
</div>
</div>
</div>
}
else
{
<div class="empty">
<p class="empty-title">No Keys</p>
<p class="empty-subtitle text-muted">There have been no keys added for this game.</p>
<div class="empty-action">
<a asp-action="Edit" asp-controller="Keys" asp-route-id="@Model.Game.Id" class="btn btn-primary">Edit Keys</a>
</div>
</div>
}
</div>
</div>
<div class="col-12">
<div class="card">
@if (Model.Game.Archives != null && Model.Game.Archives.Count > 0)
{
<div class="card-header">
<h3 class="card-title">Archives</h3>
<div class="card-actions">
<a asp-action="Add" asp-controller="Archives" asp-route-id="@Model.Game.Id" class="btn btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
Add
</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
<thead>
<tr>
<th>Version</th>
<th>Uploaded By</th>
<th>Uploaded On</th>
<th>Size</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var archive in Model.Game.Archives.OrderByDescending(a => a.CreatedOn))
{
<tr>
<td>@Html.DisplayFor(m => archive.Version)</td>
<td>@Html.DisplayFor(m => archive.CreatedBy.UserName)</td>
<td>@Html.DisplayFor(m => archive.CreatedOn)</td>
<td>@ByteSizeLib.ByteSize.FromBytes(new FileInfo(System.IO.Path.Combine("Upload", archive.ObjectKey)).Length)</td>
<td>
<div class="btn-list flex-nowrap justify-content-end">
<a asp-action="Download" asp-controller="Archives" asp-route-id="@archive.Id" class="btn">Download</a>
<a asp-action="Browse" asp-controller="Archives" asp-route-id="@archive.Id" class="btn">Browse</a>
<a asp-action="Delete" asp-controller="Archives" asp-route-id="@archive.Id" class="btn btn-danger">Delete</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<div class="empty">
<p class="empty-title">No Archives</p>
<p class="empty-subtitle text-muted">There have been no archives uploaded for this game.</p>
<div class="empty-action">
<a asp-action="Add" asp-controller="Archives" asp-route-id="@Model.Game.Id" class="btn btn-primary">Upload Archive</a>
</div>
</div>
}
</div>
</div>
<div class="col-12">
<div class="card">
@if (Model.Game.Scripts != null && Model.Game.Scripts.Count > 0)
{
<div class="card-header">
<h3 class="card-title">Scripts</h3>
<div class="card-actions">
<a asp-action="Add" asp-controller="Scripts" asp-route-id="@Model.Game.Id" class="btn btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
Add
</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Created On</th>
<th>Created By</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var script in Model.Game.Scripts.OrderBy(s => s.Type).ThenByDescending(s => s.CreatedOn))
{
<tr>
<td>@Html.DisplayFor(m => script.Type)</td>
<td>@Html.DisplayFor(m => script.Name)</td>
<td>@Html.DisplayFor(m => script.CreatedOn)</td>
<td>@Html.DisplayFor(m => script.CreatedBy.UserName)</td>
<td>
<div class="btn-list flex-nowrap justify-content-end">
<a asp-action="Edit" asp-controller="Scripts" asp-route-id="@script.Id" class="btn">Edit</a>
<a asp-action="Delete" asp-controller="Scripts" asp-route-id="@script.Id" class="btn btn-danger">Delete</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<div class="empty">
<p class="empty-title">No Scripts</p>
<p class="empty-subtitle text-muted">There have been no scripts added for this game.</p>
<div class="empty-action">
<a asp-action="Add" asp-controller="Scripts" asp-route-id="@Model.Game.Id" class="btn btn-primary">Add Script</a>
</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script>
new Select('.developer-select', @Html.Raw(Json.Serialize(Model.Developers)));
new Select('.publisher-select', @Html.Raw(Json.Serialize(Model.Publishers)));
new Select('.genre-select', @Html.Raw(Json.Serialize(Model.Genres)));
new Select('.tag-select', @Html.Raw(Json.Serialize(Model.Tags)));
</script>
}

View file

@ -1,110 +0,0 @@
@model IEnumerable<LANCommander.Data.Models.Game>
@{
ViewData["Title"] = "Games";
}
<div class="container-xl">
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<h2 class="page-title">
Games
</h2>
</div>
<div class="col-auto ms-auto">
<div class="btn-list">
<a asp-action="Add" class="btn btn-primary d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
Add
</a>
</div>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<div class="card">
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
<thead>
<tr>
<th></th>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.SortTitle)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleasedOn)
</th>
<th>
@Html.DisplayNameFor(model => model.CreatedOn)
</th>
<th>
@Html.DisplayNameFor(model => model.CreatedBy)
</th>
<th>
@Html.DisplayNameFor(model => model.UpdatedOn)
</th>
<th>
@Html.DisplayNameFor(model => model.UpdatedBy)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.OrderBy(g => !String.IsNullOrWhiteSpace(g.SortTitle) ? g.SortTitle : g.Title))
{
<tr>
<td>
@if (!String.IsNullOrWhiteSpace(item.Icon)) {
<img src="@Url.Action("GetIcon", "Games", new { id = item.Id })" />
}
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.SortTitle)
</td>
<td>
@if (item.ReleasedOn.HasValue)
{
@item.ReleasedOn.Value.ToString("MM/dd/yyyy")
}
</td>
<td>
@Html.DisplayFor(modelItem => item.CreatedOn)
</td>
<td>
@Html.DisplayFor(modelItem => item.CreatedBy.UserName)
</td>
<td>
@Html.DisplayFor(modelItem => item.UpdatedOn)
</td>
<td>
@Html.DisplayFor(modelItem => item.UpdatedBy.UserName)
</td>
<td>
<div class="btn-list flex-nowrap justify-content-end">
<a asp-action="Edit" asp-route-id="@item.Id" class="btn">Edit</a>
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger">Delete</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,99 +0,0 @@
@model LANCommander.Models.GameLookupResultsViewModel
@{
ViewData["Title"] = "Games";
}
<div class="container-xl">
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Search</div>
<h2 class="page-title">
Game Lookup
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Results</h3>
</div>
@if (Model.Results.Count() == 0)
{
<div class="card-body">
<p>No games could be found with the search "@Model.Search".</p>
</div>
}
else
{
<div class="card-body">
@if (Model.Results.Count() > 1)
{
<p>There was a total of @Model.Results.Count() games that matched the search "@Model.Search" in IGDB's database.</p>
}
else
{
<p>Only one game matched the search "@Model.Search" in IGDB's database.</p>
}
</div>
<form>
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
<thead>
<tr>
<th>
Title
</th>
<th>
Release Date
</th>
<th>
Developers
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Results)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleasedOn)
</td>
<td>
@String.Join(", ", item.Developers.Select(d => d.Name))
</td>
<td>
<div class="btn-list flex-nowrap justify-content-end">
<a asp-action="Add" asp-route-igdbid="@item.IGDBId" class="btn btn-ghost-primary">Select</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</form>
}
<div class="card-footer">
<div class="d-flex">
<a class="btn btn-ghost-primary" asp-action="Add" asp-controller="Games">Go Back</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,106 +0,0 @@
@using LANCommander.Data.Models
@model LANCommander.Data.Models.Game
@{
ViewData["Title"] = "Keys | " + Model.Title;
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Title</div>
<h2 class="page-title">
Keys
</h2>
</div>
<div class="col-auto ms-auto">
<div class="btn-list">
<a asp-action="Edit" asp-controller="Games" asp-route-id="@Model.Id" class="btn btn-ghost-primary">Back</a>
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-primary d-none d-sm-inline-block">Edit</a>
</div>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Edit" class="card">
@if (Model.Keys != null && Model.Keys.Count > 0)
{
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
<thead>
<tr>
<th>Key</th>
<th>Allocation Method</th>
<th>Claimed By</th>
<th>Claimed On</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var key in Model.Keys.OrderByDescending(k => k.ClaimedOn))
{
<tr>
<td class="game-key">@Html.DisplayFor(m => key.Value)</td>
<td>@Html.DisplayFor(m => key.AllocationMethod)</td>
<td>
@switch (key.AllocationMethod)
{
case KeyAllocationMethod.MacAddress:
<text>@key.ClaimedByMacAddress</text>
break;
case KeyAllocationMethod.UserAccount:
<text>@key.ClaimedByUser?.UserName</text>
break;
}
</td>
<td>@key.ClaimedOn</td>
<td>
<div class="btn-list flex-nowrap justify-content-end">
@if ((key.AllocationMethod == KeyAllocationMethod.MacAddress && !String.IsNullOrWhiteSpace(key.ClaimedByMacAddress)) || (key.AllocationMethod == KeyAllocationMethod.UserAccount && key.ClaimedByUser != null))
{
<a asp-action="Release" asp-controller="Keys" asp-route-id="@key.Id" class="btn btn-sm btn-ghost-dark">Release</a>
}
<a asp-action="Delete" asp-controller="Keys" asp-route-id="@key.Id" class="btn btn-sm btn-ghost-danger">Delete</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<div class="empty">
<p class="empty-title">No Key</p>
<p class="empty-subtitle text-muted">There have been no keys added for this game.</p>
<div class="empty-action">
<a asp-action="Edit" asp-controller="Keys" asp-route-id="@Model.Id" class="btn btn-primary">Edit Keys</a>
</div>
</div>
}
</form>
</div>
</div>
</div>
</div>
<style>
.game-key {
font-family: var(--tblr-font-monospace);
}
</style>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View file

@ -1,73 +0,0 @@
@model LANCommander.Models.EditKeysViewModel
@{
ViewData["Title"] = "Edit Keys | " + Model.Game.Title;
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Game.Title</div>
<h2 class="page-title">
Edit Keys
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Edit" class="card">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Keys" />
<input type="hidden" asp-for="Game.Id" />
<div id="KeyEditor" style="height: 100%; min-height: 600px;"></div>
<div class="card-footer">
<div class="d-flex">
<a asp-action="Details" asp-route-id="@Model.Game.Id" class="btn btn-ghost-primary">Cancel</a>
<button type="submit" class="btn btn-primary ms-auto">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script src="~/lib/monaco-editor/min/vs/loader.js"></script>
<script>
require.config({ paths: { vs: '/lib/monaco-editor/min/vs' } });
require(['vs/editor/editor.main'], function () {
var editor = monaco.editor.create(document.getElementById('KeyEditor'), {
value: $('#Keys').val(),
readOnly: false,
theme: 'vs-dark',
automaticLayout: true
});
editor.onDidChangeModelContent(function (e) {
$('#Keys').val(editor.getModel().getValue());
});
});
document.addEventListener('keydown', e => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
$('form').submit();
}
});
</script>
}

View file

@ -1,125 +0,0 @@
@using LANCommander.Components;
@using LANCommander.Data.Enums
@model LANCommander.Data.Models.Script
@{
ViewData["Title"] = "Add Script | " + Model.Game.Title;
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Game.Title</div>
<h2 class="page-title">
Add Script
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Add" class="card">
<div class="card-body pb-0">
<div class="row">
<div class="col-12">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-3">
<div class="mb-3">
<label asp-for="Type" class="control-label"></label>
<select asp-for="Type" class="form-control" asp-items="Html.GetEnumSelectList<ScriptType>()"></select>
<span asp-validation-for="Type" class="text-danger"></span>
</div>
</div>
<div class="col-9">
<div class="mb-3">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="mb-3">
<label asp-for="Description" class="control-label"></label>
<textarea asp-for="Description" class="form-control"></textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="mb-3">
<label class="form-check">
<input asp-for="RequiresAdmin" type="checkbox" class="form-check-input" />
<span class="form-check-label">Requires Admin Privileges</span>
<span class="form-check-description">Marks the script as needing admin privileges. Recommended for any changes to the system e.g. Windows Registry.</span>
</label>
</div>
<input type="hidden" asp-for="Contents" />
<input type="hidden" asp-for="GameId" />
</div>
</div>
<div class="row">
<div class="col btn-list mb-3">
<component type="typeof(SnippetBar)" render-mode="Server" />
</div>
</div>
</div>
<div id="ScriptEditor" style="height: 100%; min-height: 600px;"></div>
<div class="card-footer">
<div class="d-flex">
<a asp-action="Edit" asp-controller="Games" asp-route-id="@Model.Game.Id" class="btn btn-ghost-primary">Cancel</a>
<button type="submit" class="btn btn-primary ms-auto">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script src="~/lib/monaco-editor/min/vs/loader.js"></script>
<script>
window.Editor = {};
require.config({ paths: { vs: '/lib/monaco-editor/min/vs' } });
require(['vs/editor/editor.main'], function () {
window.Editor = monaco.editor.create(document.getElementById('ScriptEditor'), {
value: $('#Contents').val(),
language: 'powershell',
readOnly: false,
theme: 'vs-dark',
automaticLayout: true
});
window.Editor.onDidChangeModelContent(function (e) {
$('#Contents').val(window.Editor.getModel().getValue());
});
});
document.addEventListener('keydown', e => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
$('form').submit();
}
});
</script>
}

View file

@ -1,126 +0,0 @@
@using LANCommander.Components;
@using LANCommander.Data.Enums
@model LANCommander.Data.Models.Script
@{
ViewData["Title"] = "Edit Script | " + Model.Game.Title;
}
<div class="container-xl">
<!-- Page title -->
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">@Model.Game.Title</div>
<h2 class="page-title">
Edit Script
</h2>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<form asp-action="Edit" class="card">
<div class="card-body pb-0">
<div class="row">
<div class="col-12">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-3">
<div class="mb-3">
<label asp-for="Type" class="control-label"></label>
<select asp-for="Type" class="form-control" asp-items="Html.GetEnumSelectList<ScriptType>()"></select>
<span asp-validation-for="Type" class="text-danger"></span>
</div>
</div>
<div class="col-9">
<div class="mb-3">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="mb-3">
<label asp-for="Description" class="control-label"></label>
<textarea asp-for="Description" class="form-control"></textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="mb-3">
<label class="form-check">
<input asp-for="RequiresAdmin" type="checkbox" class="form-check-input" />
<span class="form-check-label">Requires Admin Privileges</span>
<span class="form-check-description">Marks the script as needing admin privileges. Recommended for any changes to the system e.g. Windows Registry.</span>
</label>
</div>
<input type="hidden" asp-for="Contents" />
<input type="hidden" asp-for="GameId" />
<input type="hidden" asp-for="Id" />
</div>
</div>
<div class="row">
<div class="col btn-list mb-3">
<component type="typeof(SnippetBar)" render-mode="Server" />
</div>
</div>
</div>
<div id="ScriptEditor" style="height: 100%; min-height: 600px;"></div>
<div class="card-footer">
<div class="d-flex">
<a asp-action="Edit" asp-controller="Games" asp-route-id="@Model.Game.Id" class="btn btn-ghost-primary">Cancel</a>
<button type="submit" class="btn btn-primary ms-auto">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script src="~/lib/monaco-editor/min/vs/loader.js"></script>
<script>
window.Editor = {};
require.config({ paths: { vs: '/lib/monaco-editor/min/vs' } });
require(['vs/editor/editor.main'], function () {
window.Editor = monaco.editor.create(document.getElementById('ScriptEditor'), {
value: $('#Contents').val(),
language: 'powershell',
readOnly: false,
theme: 'vs-dark',
automaticLayout: true
});
window.Editor.onDidChangeModelContent(function (e) {
$('#Contents').val(window.Editor.getModel().getValue());
});
});
document.addEventListener('keydown', e => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
$('form').submit();
}
});
</script>
}

View file

@ -1,43 +0,0 @@
@model LANCommander.Data.Models.Tag
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Tag</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedOn" class="control-label"></label>
<input asp-for="CreatedOn" class="form-control" />
<span asp-validation-for="CreatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UpdatedOn" class="control-label"></label>
<input asp-for="UpdatedOn" class="form-control" />
<span asp-validation-for="UpdatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

View file

@ -1,39 +0,0 @@
@model LANCommander.Data.Models.Tag
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Tag</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Name)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.CreatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.CreatedOn)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.UpdatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.UpdatedOn)
</dd>
</dl>
<form asp-action="Delete">
<input type="hidden" asp-for="Id" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-action="Index">Back to List</a>
</form>
</div>

View file

@ -1,36 +0,0 @@
@model LANCommander.Data.Models.Tag
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Tag</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Name)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.CreatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.CreatedOn)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.UpdatedOn)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.UpdatedOn)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model?.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>

View file

@ -1,44 +0,0 @@
@model LANCommander.Data.Models.Tag
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Tag</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="CreatedOn" class="control-label"></label>
<input asp-for="CreatedOn" class="form-control" />
<span asp-validation-for="CreatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UpdatedOn" class="control-label"></label>
<input asp-for="UpdatedOn" class="form-control" />
<span asp-validation-for="UpdatedOn" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

View file

@ -1,47 +0,0 @@
@model IEnumerable<LANCommander.Data.Models.Tag>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.CreatedOn)
</th>
<th>
@Html.DisplayNameFor(model => model.UpdatedOn)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.CreatedOn)
</td>
<td>
@Html.DisplayFor(modelItem => item.UpdatedOn)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>

View file

@ -1,16 +0,0 @@
class Modal {
constructor(id) {
this.ElementId = id;
// @ts-ignore
this.Instance = new bootstrap.Modal(`#${this.ElementId}`, {
keyboard: false
});
}
Show(header, message) {
document.getElementById(`${this.ElementId}Header`).innerText = header;
document.getElementById(`${this.ElementId}Message`).innerText = message;
this.Instance.show();
}
}
const ErrorModal = new Modal('ErrorModal');
//# sourceMappingURL=Modal.js.map

View file

@ -1 +0,0 @@
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["Modal.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK;IAIP,YAAY,EAAU;QAClB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QAEpB,aAAa;QACb,IAAI,CAAC,QAAQ,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE;YACtD,QAAQ,EAAE,KAAK;SAClB,CAAC,CAAC;IACP,CAAC;IAED,IAAI,CAAC,MAAc,EAAE,OAAe;QAChC,QAAQ,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,QAAQ,CAAC,CAAC,SAAS,GAAG,MAAM,CAAC;QACtE,QAAQ,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,SAAS,CAAC,CAAC,SAAS,GAAG,OAAO,CAAC;QAExE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;CACJ;AAED,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC"}

View file

@ -1,22 +0,0 @@
class Modal {
Instance: any;
ElementId: string;
constructor(id: string) {
this.ElementId = id;
// @ts-ignore
this.Instance = new bootstrap.Modal(`#${this.ElementId}`, {
keyboard: false
});
}
Show(header: string, message: string) {
document.getElementById(`${this.ElementId}Header`).innerText = header;
document.getElementById(`${this.ElementId}Message`).innerText = message;
this.Instance.show();
}
}
const ErrorModal = new Modal('ErrorModal');

View file

@ -1,118 +0,0 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class Chunk {
constructor(start, end, index) {
this.Start = start;
this.End = end;
this.Index = index;
}
}
class Uploader {
constructor() {
this.InitRoute = "/Upload/Init";
this.ChunkRoute = "/Upload/Chunk";
this.ValidateRoute = "/Archives/Validate";
this.MaxChunkSize = 1024 * 1024 * 25;
}
Init(fileInputId, uploadButtonId) {
this.FileInput = document.getElementById("File");
this.UploadButton = document.getElementById("UploadButton");
this.VersionInput = document.getElementById("Version");
this.ChangelogTextArea = document.getElementById("Changelog");
this.LastVersionIdInput = document.getElementById("LastVersion_Id");
this.GameIdInput = document.getElementById("GameId");
this.ParentForm = this.FileInput.closest("form");
this.Chunks = [];
this.UploadButton.onclick = (e) => __awaiter(this, void 0, void 0, function* () {
yield this.OnUploadButtonClicked(e);
});
}
OnUploadButtonClicked(e) {
return __awaiter(this, void 0, void 0, function* () {
e.preventDefault();
this.OnStart();
this.File = this.FileInput.files.item(0);
this.TotalChunks = Math.ceil(this.File.size / this.MaxChunkSize);
var response = yield fetch(this.InitRoute, {
method: "POST"
});
const data = yield response.json();
if (response.ok) {
this.Key = data.key;
this.GetChunks();
try {
for (let chunk of this.Chunks) {
yield this.UploadChunk(chunk);
}
var isValid = yield this.Validate();
if (isValid)
this.OnComplete(this.Id, this.Key);
else
this.OnError();
}
catch (ex) {
this.OnError();
}
}
});
}
UploadChunk(chunk) {
return __awaiter(this, void 0, void 0, function* () {
let formData = new FormData();
formData.append('file', this.File.slice(chunk.Start, chunk.End + 1));
formData.append('start', chunk.Start.toString());
formData.append('end', chunk.End.toString());
formData.append('key', this.Key);
formData.append('total', this.File.size.toString());
console.info(`Uploading chunk ${chunk.Index}/${this.TotalChunks}...`);
let chunkResponse = yield fetch(this.ChunkRoute, {
method: "POST",
body: formData
});
if (!chunkResponse)
throw `Error uploading chunk ${chunk.Index}/${this.TotalChunks}`;
this.OnProgress(chunk.Index / this.TotalChunks);
});
}
Validate() {
return __awaiter(this, void 0, void 0, function* () {
let formData = new FormData();
formData.append('Version', this.VersionInput.value);
formData.append('Changelog', this.ChangelogTextArea.value);
formData.append('GameId', this.GameIdInput.value);
formData.append('ObjectKey', this.Key);
let validationResponse = yield fetch(`${this.ValidateRoute}/${this.Key}`, {
method: "POST",
body: formData
});
if (!validationResponse.ok) {
ErrorModal.Show("Archive Invalid", yield validationResponse.text());
return false;
}
let data = yield validationResponse.json();
if (data == null || data.Id === "") {
ErrorModal.Show("Upload Error", "Something interfered with the upload. Try again.");
return false;
}
this.Id = data.Id;
return true;
});
}
GetChunks() {
for (let currentChunk = 1; currentChunk <= this.TotalChunks; currentChunk++) {
let start = (currentChunk - 1) * this.MaxChunkSize;
let end = (currentChunk * this.MaxChunkSize) - 1;
if (currentChunk == this.TotalChunks)
end = this.File.size;
this.Chunks.push(new Chunk(start, end, currentChunk));
}
}
}
//# sourceMappingURL=Upload.js.map

View file

@ -1 +0,0 @@
{"version":3,"file":"Upload.js","sourceRoot":"","sources":["Upload.ts"],"names":[],"mappings":"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"}

View file

@ -1,161 +0,0 @@
class Chunk {
Index: number;
Start: number;
End: number;
constructor(start: number, end: number, index: number) {
this.Start = start;
this.End = end;
this.Index = index;
}
}
class Uploader {
ParentForm: HTMLFormElement;
FileInput: HTMLInputElement;
UploadButton: HTMLButtonElement;
VersionInput: HTMLInputElement;
LastVersionIdInput: HTMLInputElement;
GameIdInput: HTMLInputElement;
ChangelogTextArea: HTMLTextAreaElement;
ObjectKeyInput: HTMLInputElement;
IdInput: HTMLInputElement;
File: File;
InitRoute: string = "/Upload/Init";
ChunkRoute: string = "/Upload/Chunk";
ValidateRoute: string = "/Archives/Validate";
MaxChunkSize: number = 1024 * 1024 * 25;
TotalChunks: number;
CurrentChunk: number;
Chunks: Chunk[];
Key: string;
Id: string;
Init(fileInputId: string, uploadButtonId: string) {
this.FileInput = document.getElementById("File") as HTMLInputElement;
this.UploadButton = document.getElementById("UploadButton") as HTMLButtonElement;
this.VersionInput = document.getElementById("Version") as HTMLInputElement;
this.ChangelogTextArea = document.getElementById("Changelog") as HTMLTextAreaElement;
this.LastVersionIdInput = document.getElementById("LastVersion_Id") as HTMLInputElement;
this.GameIdInput = document.getElementById("GameId") as HTMLInputElement;
this.ParentForm = this.FileInput.closest("form");
this.Chunks = [];
this.UploadButton.onclick = async (e) => {
await this.OnUploadButtonClicked(e);
}
}
async OnUploadButtonClicked(e: MouseEvent) {
e.preventDefault();
this.OnStart();
this.File = this.FileInput.files.item(0);
this.TotalChunks = Math.ceil(this.File.size / this.MaxChunkSize);
var response = await fetch(this.InitRoute, {
method: "POST"
});
const data = await response.json();
if (response.ok) {
this.Key = data.key;
this.GetChunks();
try {
for (let chunk of this.Chunks) {
await this.UploadChunk(chunk);
}
var isValid = await this.Validate();
if (isValid)
this.OnComplete(this.Id, this.Key);
else
this.OnError();
}
catch (ex) {
this.OnError();
}
}
}
async UploadChunk(chunk: Chunk) {
let formData = new FormData();
formData.append('file', this.File.slice(chunk.Start, chunk.End + 1));
formData.append('start', chunk.Start.toString());
formData.append('end', chunk.End.toString());
formData.append('key', this.Key);
formData.append('total', this.File.size.toString());
console.info(`Uploading chunk ${chunk.Index}/${this.TotalChunks}...`);
let chunkResponse = await fetch(this.ChunkRoute, {
method: "POST",
body: formData
});
if (!chunkResponse)
throw `Error uploading chunk ${chunk.Index}/${this.TotalChunks}`;
this.OnProgress(chunk.Index / this.TotalChunks);
}
async Validate(): Promise<boolean> {
let formData = new FormData();
formData.append('Version', this.VersionInput.value);
formData.append('Changelog', this.ChangelogTextArea.value);
formData.append('GameId', this.GameIdInput.value);
formData.append('ObjectKey', this.Key);
let validationResponse = await fetch(`${this.ValidateRoute}/${this.Key}`, {
method: "POST",
body: formData
});
if (!validationResponse.ok) {
ErrorModal.Show("Archive Invalid", await validationResponse.text())
return false;
}
let data = await validationResponse.json();
if (data == null || data.Id === "") {
ErrorModal.Show("Upload Error", "Something interfered with the upload. Try again.");
return false;
}
this.Id = data.Id;
return true;
}
GetChunks() {
for (let currentChunk = 1; currentChunk <= this.TotalChunks; currentChunk++) {
let start = (currentChunk - 1) * this.MaxChunkSize;
let end = (currentChunk * this.MaxChunkSize) - 1;
if (currentChunk == this.TotalChunks)
end = this.File.size;
this.Chunks.push(new Chunk(start, end, currentChunk));
}
}
OnStart: () => void;
OnComplete: (id: string, key: string) => void;
OnProgress: (percent: number) => void;
OnError: () => void;
}