diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Base/AbstractSortablePageModel.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Base/AbstractSortablePageModel.cs new file mode 100644 index 00000000..1eaad5c0 --- /dev/null +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Base/AbstractSortablePageModel.cs @@ -0,0 +1,16 @@ +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Base; + +public abstract class AbstractSortablePageModel : PageModel +{ + public string SortColumn { get; set; } + public SortDirection? SortDirection { get; set; } + + public void ApplySort(string sortColumn, SortDirection? sortDirection) + { + SortColumn = sortColumn; + SortDirection = sortDirection; + } +} diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/Default.cshtml b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/Default.cshtml new file mode 100644 index 00000000..7f3dc27c --- /dev/null +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/Default.cshtml @@ -0,0 +1,13 @@ +@using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models +@model Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Components.SortableHeaderCell.SortableHeaderCellViewModel + + + @Model.DisplayName + + @if (Model.IsActive() && Model.GetSortDirection() != null) + { + + + } + \ No newline at end of file diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/SortableHeaderCellViewComponent.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/SortableHeaderCellViewComponent.cs new file mode 100644 index 00000000..94b2a566 --- /dev/null +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/SortableHeaderCellViewComponent.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Components.SortableHeaderCell; + +public class SortableHeaderCellViewComponent : ViewComponent +{ + private readonly IHttpContextAccessor _contextAccessor; + + public SortableHeaderCellViewComponent(IHttpContextAccessor contextAccessor) + { + _contextAccessor = contextAccessor; + } + + public IViewComponentResult Invoke(string key, string displayName) + { + var context = _contextAccessor.HttpContext; + + return View(new SortableHeaderCellViewModel + { + QueryString = context?.Request.QueryString.ToString(), + Key = key, + DisplayName = displayName + }); + } +} diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/SortableHeaderCellViewModel.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/SortableHeaderCellViewModel.cs new file mode 100644 index 00000000..61075bde --- /dev/null +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Components/SortableHeaderCell/SortableHeaderCellViewModel.cs @@ -0,0 +1,63 @@ +using System; +using System.Web; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Base; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; + +namespace Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Components.SortableHeaderCell; + +public class SortableHeaderCellViewModel +{ + public string DisplayName { get; set; } + public string Key { get; set; } + public string QueryString { get; set; } + + public SortDirection? GetNextSortDirection() + { + return GetSortDirection() switch + { + null => SortDirection.Ascending, + SortDirection.Ascending => SortDirection.Descending, + SortDirection.Descending => null, + _ => throw new ArgumentOutOfRangeException() + }; + } + + public string GetSortColumn() + { + if (GetNextSortDirection() == null) + { + return null; + } + + return Key; + } + + public string GetSortUrl() + { + var qs = HttpUtility + .ParseQueryString(QueryString); + + qs[nameof(AbstractSortablePageModel.SortColumn)] = GetSortColumn(); + qs[nameof(AbstractSortablePageModel.SortDirection)] = GetNextSortDirection().ToString(); + + return $"?{qs}"; + } + + public bool IsActive() + { + var sortColumn = HttpUtility + .ParseQueryString(QueryString) + .Get(nameof(AbstractSortablePageModel.SortColumn)); + + return !string.IsNullOrEmpty(sortColumn) && sortColumn == Key; + } + + public SortDirection? GetSortDirection() + { + var sortDirection = HttpUtility + .ParseQueryString(QueryString) + .Get(nameof(AbstractSortablePageModel.SortDirection)); + + return Enum.TryParse(sortDirection, out SortDirection sort) ? sort : null; + } +} diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml index c247027b..d869f36d 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml @@ -1,4 +1,7 @@ @page "{handler?}" +@using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Components.SortableHeaderCell +@using Geta.NotFoundHandler.Core.Providers.RegexRedirects +@using Microsoft.AspNetCore.Mvc.TagHelpers @model Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.DeletedModel @await Component.InvokeAsync("Card", new { message = Model.Message }) @@ -7,42 +10,44 @@
- - - - + + + + - - + + + + + @foreach (var item in Model.Items) + { + + - @foreach (var item in Model.Items) - { - - - - - } + }
URL
+ +
- - -
+ + + +
+ +
+
@item.OldUrl
-
@item.OldUrl -
- -
-
@await Component.InvokeAsync(typeof(Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Components.Pager.PagerViewComponent), new { Model.Items })
- + \ No newline at end of file diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml.cs index 6cd5cbe5..ce319cac 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml.cs +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Deleted.cshtml.cs @@ -1,16 +1,19 @@ +using System.Collections.Generic; using System.Linq; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Base; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Extensions; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; using Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Models; using Geta.NotFoundHandler.Core.Redirects; using Geta.NotFoundHandler.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; using X.PagedList; namespace Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin; [Authorize(Constants.PolicyName)] -public class DeletedModel : PageModel +public class DeletedModel : AbstractSortablePageModel { private readonly IRedirectsService _redirectsService; @@ -29,8 +32,10 @@ public DeletedModel(IRedirectsService redirectsService) [BindProperty(SupportsGet = true)] public Paging Paging { get; set; } - public void OnGet() + public void OnGet(string sortColumn, SortDirection? sortDirection) { + ApplySort(sortColumn, sortDirection); + Load(); } @@ -55,9 +60,16 @@ public IActionResult OnPostDelete(string oldUrl) private void Load() { - var items = _redirectsService.GetDeleted().ToPagedList(Paging.PageNumber, Paging.PageSize); + var items = FindRedirects().ToPagedList(Paging.PageNumber, Paging.PageSize); Message = $"There are currently {items.TotalItemCount} URLs that return a Deleted response. This tells crawlers to remove these URLs from their index."; Items = items; } + + private IEnumerable FindRedirects() + { + return _redirectsService + .GetDeleted() + .Sort(SortColumn, SortDirection); + } } diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Extensions/EnumerableExtensions.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..49c3956c --- /dev/null +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Extensions/EnumerableExtensions.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; + +namespace Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Extensions; + +public static class EnumerableExtensions +{ + public static IEnumerable Sort(this IEnumerable list, string propertyName, SortDirection? sortDirection) + { + if (!string.IsNullOrEmpty(propertyName)) + { + var prop = typeof(T).GetProperty(propertyName); + + if (prop != null && sortDirection != null) + { + if (sortDirection == SortDirection.Ascending) + { + list = list.OrderBy(x => GetValue(prop, x)); + } + else + { + list = list.OrderByDescending(x => GetValue(prop, x)); + } + } + } + + return list; + } + + private static object GetValue(PropertyInfo prop, T element) + { + var value = prop.GetValue(element); + + // Value should be IComparable + if (value is Regex regex) + { + return regex.ToString(); + } + + return value; + } +} diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml index 7bdd6aab..588a140c 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml @@ -1,4 +1,6 @@ @page "{handler?}" +@using Geta.NotFoundHandler.Core.Redirects +@using Microsoft.AspNetCore.Mvc.TagHelpers @model Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.IgnoredModel @await Component.InvokeAsync("Card", new { message = Model.Message }) @@ -8,7 +10,9 @@ - + diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml.cs index f7a6cf42..ff6495c7 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml.cs +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Ignored.cshtml.cs @@ -1,16 +1,19 @@ +using System.Collections.Generic; using System.Linq; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Base; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Extensions; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; using Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Models; using Geta.NotFoundHandler.Core.Redirects; using Geta.NotFoundHandler.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; using X.PagedList; namespace Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin; [Authorize(Constants.PolicyName)] -public class IgnoredModel : PageModel +public class IgnoredModel : AbstractSortablePageModel { private readonly IRedirectsService _redirectsService; @@ -26,8 +29,10 @@ public IgnoredModel(IRedirectsService redirectsService) [BindProperty(SupportsGet = true)] public Paging Paging { get; set; } - public void OnGet() + public void OnGet(string sortColumn, SortDirection? sortDirection) { + ApplySort(sortColumn, sortDirection); + Load(); } @@ -40,8 +45,15 @@ public IActionResult OnPostUnignore(string oldUrl) private void Load() { - var items = _redirectsService.GetIgnored().ToPagedList(Paging.PageNumber, Paging.PageSize); + var items = FindRedirects().ToPagedList(Paging.PageNumber, Paging.PageSize); Message = $"There are currently {items.TotalItemCount} ignored suggestions stored."; Items = items; } + + private IEnumerable FindRedirects() + { + return _redirectsService + .GetIgnored() + .Sort(SortColumn, SortDirection); + } } diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml index 33ad790e..6979a4ac 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml @@ -27,10 +27,18 @@
URL + +
- - - - + + + + diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml.cs index c4aa129e..789c7b82 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml.cs +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Index.cshtml.cs @@ -1,17 +1,19 @@ using System.Collections.Generic; using System.Linq; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Base; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Extensions; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; using Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Models; using Geta.NotFoundHandler.Core.Redirects; using Geta.NotFoundHandler.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; using X.PagedList; namespace Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin; [Authorize(Constants.PolicyName)] -public class IndexModel : PageModel +public class IndexModel : AbstractSortablePageModel { private readonly IRedirectsService _redirectsService; @@ -35,8 +37,10 @@ public IndexModel(IRedirectsService redirectsService) public bool HasQuery => !string.IsNullOrEmpty(Query); - public void OnGet() + public void OnGet(string sortColumn, SortDirection? sortDirection) { + ApplySort(sortColumn, sortDirection); + Load(); } @@ -73,6 +77,9 @@ private void Load() private IEnumerable FindRedirects() { - return HasQuery ? _redirectsService.Search(Query) : _redirectsService.GetSaved(); + var result = HasQuery ? _redirectsService.Search(Query) : _redirectsService.GetSaved(); + + return result + .Sort(SortColumn, SortDirection); } } diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Models/SortDirection.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Models/SortDirection.cs new file mode 100644 index 00000000..1cad2fcf --- /dev/null +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Models/SortDirection.cs @@ -0,0 +1,7 @@ +namespace Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; + +public enum SortDirection +{ + Ascending, + Descending +} diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml index 63635870..37fd5daf 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml @@ -1,4 +1,5 @@ @page "{handler?}" +@using Geta.NotFoundHandler.Core.Providers.RegexRedirects @using Microsoft.AspNetCore.Mvc.TagHelpers @model Geta.NotFoundHandler.Admin.Areas.Geta.NotFoundHandler.Admin.RegexModel @@ -9,9 +10,15 @@
Old URLNew URLWildcardRedirect Type + + + + + + + +
- - - + + + diff --git a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml.cs b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml.cs index 544ae5e5..b4904702 100644 --- a/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml.cs +++ b/src/Geta.NotFoundHandler.Admin/Areas/GetaNotFoundHandlerAdmin/Pages/Regex.cshtml.cs @@ -1,18 +1,20 @@ using System; using System.Collections.Generic; using System.Linq; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Base; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Extensions; +using Geta.NotFoundHandler.Admin.Areas.GetaNotFoundHandlerAdmin.Pages.Models; using Geta.NotFoundHandler.Admin.Pages.Geta.NotFoundHandler.Admin.Models; using Geta.NotFoundHandler.Core.Providers.RegexRedirects; using Geta.NotFoundHandler.Data; using Geta.NotFoundHandler.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; namespace Geta.NotFoundHandler.Admin.Areas.Geta.NotFoundHandler.Admin; [Authorize(Constants.PolicyName)] -public class RegexModel : PageModel +public class RegexModel : AbstractSortablePageModel { private readonly IRegexRedirectLoader _redirectLoader; private readonly IRegexRedirectsService _regexRedirectsService; @@ -34,8 +36,10 @@ public RegexModel( [BindProperty] public RegexRedirectModel RegexRedirect { get; set; } - public void OnGet() + public void OnGet(string sortColumn, SortDirection? sortDirection) { + ApplySort(sortColumn, sortDirection); + Load(); } @@ -105,9 +109,11 @@ private void Load() RegexRedirect = new RegexRedirectModel { OrderNumber = items.Select(x => x.OrderNumber).DefaultIfEmpty().Max() + 1 }; } - private IList FindRedirects() + private IEnumerable FindRedirects() { - return _redirectLoader.GetAll().ToList(); + return _redirectLoader + .GetAll() + .Sort(SortColumn, SortDirection); } public bool IsEditing(Guid? id)
Order NumberOld URL RegexNew URL Format + + + + + +