243 lines
8.3 KiB
C#
243 lines
8.3 KiB
C#
using AsterNET.Manager;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using PhoneToolMX.Data;
|
|
using PhoneToolMX.Helpers;
|
|
using PhoneToolMX.Models;
|
|
using PhoneToolMX.Models.ViewModels;
|
|
using PhoneToolMX.Services;
|
|
using System.Security.Authentication;
|
|
using System.Security.Claims;
|
|
|
|
namespace PhoneToolMX.Controllers
|
|
{
|
|
[Authorize]
|
|
[Route("[controller]")]
|
|
public abstract class BaseController<T, TViewModel> : Controller
|
|
where T: OwnedBase, IModel
|
|
where TViewModel : IViewModel, new()
|
|
{
|
|
protected private readonly PTMXContext _context;
|
|
private readonly UserManager<User> _userManager;
|
|
|
|
protected BaseController(UserManager<User> mgr, PTMXContext ctx)
|
|
{
|
|
_context = ctx;
|
|
_userManager = mgr;
|
|
}
|
|
|
|
protected private virtual Task PreForm(TViewModel vm)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
#region Helper methods
|
|
|
|
/// <summary>
|
|
/// Gets the "friendly" name for an owned model object.
|
|
/// </summary>
|
|
/// <param name="vm">The model object to inspect</param>
|
|
/// <returns>The friendly name, or its ID if none was defined</returns>
|
|
private static string GetFriendlyName(IViewModel vm)
|
|
{
|
|
if (vm is not TViewModel model) return vm.Id.ToString();
|
|
return typeof(TViewModel).GetProperties().Where(pi =>
|
|
{
|
|
var headers = pi.GetCustomAttributes(typeof(HeaderAttribute), false);
|
|
return headers.Length != 0 && ((HeaderAttribute)headers[0]).Primary;
|
|
}).Select(pi => pi.GetValue(model)?.ToString()).FirstOrDefault() ?? model.Id.ToString();
|
|
}
|
|
|
|
private static string GetFriendlyName(IOwnedModel model)
|
|
{
|
|
var vm = new TViewModel().FromEntity(model);
|
|
return GetFriendlyName(vm);
|
|
}
|
|
|
|
protected private async Task<User> CurrentUser()
|
|
{
|
|
var claim = HttpContext.User.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier));
|
|
if (claim == null) {
|
|
throw new InvalidCredentialException("fuck this noise, nameid claim missing");
|
|
}
|
|
var user = await _userManager.FindByIdAsync(claim.Value);
|
|
return user;
|
|
}
|
|
|
|
private async Task<IActionResult> FormView(TViewModel vm)
|
|
{
|
|
await PreForm(vm);
|
|
return View("_Form", vm);
|
|
}
|
|
|
|
private void SetMessage(FormMessage msg)
|
|
{
|
|
TempData["Message"] = msg.ToString();
|
|
}
|
|
|
|
private void SetMessage(FormMessageType type, string msg)
|
|
{
|
|
SetMessage(new FormMessage
|
|
{
|
|
Type = type,
|
|
Message = msg,
|
|
});
|
|
}
|
|
|
|
private async Task<IActionResult> ValidationError(TViewModel vm)
|
|
{
|
|
var plural = ModelState.ErrorCount > 1 ? "s" : string.Empty;
|
|
SetMessage(
|
|
FormMessageType.Error,
|
|
$"Error{plural} occurred in validation. Make the changes required and click Submit to try again."
|
|
);
|
|
return await FormView(vm);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Create
|
|
|
|
[HttpGet("New")]
|
|
public async Task<IActionResult> New()
|
|
{
|
|
ViewData["Action"] = "add";
|
|
ViewData["Title"] = $"Add new {typeof(T).Name.ToLower()}";
|
|
return await FormView(default);
|
|
}
|
|
|
|
[HttpPost("New")]
|
|
public async Task<IActionResult> NewPost(TViewModel vm)
|
|
{
|
|
ViewData["Title"] = $"New {typeof(T).Name}";
|
|
|
|
if (!ModelState.IsValid) return await ValidationError(vm);
|
|
|
|
if (vm.ToEntity(_context) is not T model) throw new InvalidOperationException($"{typeof(TViewModel).FullName}.ToEntity() somehow produced something other than a {typeof(T).FullName}");
|
|
var entity = await _context.AddOwnable(await CurrentUser(), model);
|
|
await _context.SaveChangesAsync();
|
|
|
|
SetMessage(
|
|
FormMessageType.Success,
|
|
$"{typeof(T).Name} {GetFriendlyName(entity.Entity)} was created."
|
|
);
|
|
|
|
return RedirectToAction("Edit", new { id = entity.Entity.Id });
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Read
|
|
|
|
[HttpGet]
|
|
public async Task<IActionResult> Index()
|
|
{
|
|
ViewData["Title"] = $"My {typeof(T).Name}s";
|
|
return View("Index", _context
|
|
.GetOwned<T>(await CurrentUser())
|
|
.Select(m => (TViewModel)new TViewModel().FromEntity(m))
|
|
.ToList());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update
|
|
|
|
[HttpGet("Edit")]
|
|
public async Task<IActionResult> Edit(int id)
|
|
{
|
|
ViewData["Action"] = "update";
|
|
var model = _context.GetOwned<T>(await CurrentUser()).FirstOrDefault(o => o.Id == id);
|
|
if (model == null) return NotFound();
|
|
ViewData["Title"] = $"Editing {typeof(T).Name.ToLower()} {GetFriendlyName(new TViewModel().FromEntity(model))}";
|
|
return await FormView((TViewModel)new TViewModel().FromEntity(model));
|
|
}
|
|
|
|
[HttpPost("Edit")]
|
|
public async Task<IActionResult> EditPost(TViewModel vm)
|
|
{
|
|
if (vm?.Id == null) {
|
|
return BadRequest();
|
|
}
|
|
|
|
if (!ModelState.IsValid) return await ValidationError(vm);
|
|
|
|
// merge VM's changes with DbModel
|
|
var currentModel = _context.GetEntityById<T>(vm.Id);
|
|
if (vm.ToEntity(_context, currentModel) is not T entity) throw new InvalidOperationException($"{typeof(TViewModel).FullName}.ToEntity() somehow produced something other than a {typeof(T).FullName}");
|
|
currentModel.Commit(entity);
|
|
|
|
// and commit back
|
|
_context.Set<T>().Update(currentModel);
|
|
await _context.SaveChangesAsync();
|
|
|
|
// Try to notify the relevant endpoint to update its config
|
|
var amiStatus = "";
|
|
if (HttpContext.RequestServices.GetService<IAsteriskManager>() is {} ami) {
|
|
try { await ami.SendNotifyAsync(vm.NotifyOnChange());
|
|
amiStatus = "Your phone's configuration should be updated shortly.";
|
|
}
|
|
catch (BadResponseException e) { amiStatus = $"You'll need to manually update your phone settings (AMI Response: {e.Message})."; }
|
|
catch (ManagerException e) { amiStatus = $"You'll need to manually update your phone settings (AMI Client Error: {e.Message})."; }
|
|
catch (Exception e) {
|
|
amiStatus = $"You'll need to manually update your phone settings (Unknown Error: {e.Message}).";
|
|
}
|
|
}
|
|
|
|
SetMessage(
|
|
FormMessageType.Success,
|
|
$"{typeof(T).Name} {GetFriendlyName(currentModel)} was updated.{" " + amiStatus}"
|
|
);
|
|
|
|
return RedirectToAction("Edit", new { id = entity.Id });
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Delete
|
|
|
|
[HttpGet("Delete")]
|
|
public async Task<IActionResult> Delete(int id)
|
|
{
|
|
var model = _context.GetEntityById<T>(id);
|
|
|
|
if (model?.IsOwnedBy(await CurrentUser()) != true) {
|
|
return NotFound();
|
|
}
|
|
|
|
ViewData["Title"] = $"Delete {GetFriendlyName(model)}";
|
|
TempData["ModelDelete"] = model.Id;
|
|
return View("DeleteConfirm", model);
|
|
}
|
|
|
|
[HttpPost("Delete")]
|
|
public async Task<IActionResult> PostDelete(int id)
|
|
{
|
|
if ((int?)TempData["ModelDelete"] != id) {
|
|
return BadRequest();
|
|
}
|
|
|
|
var model = _context.GetEntityById<T>(id);
|
|
|
|
if (model?.IsOwnedBy(await CurrentUser()) != true) {
|
|
return NotFound();
|
|
}
|
|
|
|
_context.Set<T>().Remove(model);
|
|
await _context.SaveChangesAsync();
|
|
SetMessage(
|
|
FormMessageType.Success,
|
|
$"{typeof(T).Name} {GetFriendlyName(new TViewModel().FromEntity(model))} was successfully deleted."
|
|
);
|
|
|
|
return RedirectToAction("Index");
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|