using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Primitives; namespace BTCPayServer.Models { public abstract class BasePagingViewModel { public const int CountDefault = 50; public int Skip { get; set; } = 0; public int Count { get; set; } = CountDefault; public int? Total { get; set; } /// /// SearchTerm is the part of the SearchString that isn't shown explicitly /// in the search text input. /// [DisplayFormat(ConvertEmptyStringToNull = false)] public string SearchTerm { get; set; } [BindingBehavior(BindingBehavior.Never)] [ValidateNever] public SearchString Search { get; set; } /// /// SearchText is the part of the SearchString that is shown explicitly in the search text input. /// public string SearchText { get; set; } public string FilterCommand { get; set; } public SearchString GetSearch() { var search = SearchString.Combine([SearchTerm, SearchText]); search.RemoveDups("daterange"); if (FilterCommand is not null) RunFilterCommand(search); AddUIFilters(search); SearchTerm = search.ToString(SearchStringFormat.OnlyUIFilters); SearchText = search.ToString(SearchStringFormat.ExceptUIFilters); Search = search; return search; } protected virtual void AddUIFilters(SearchString search) { } protected virtual void RunFilterCommand(SearchString search) { if (Regex.Match(FilterCommand, @"^(set|set-multi):") is { Success: true } match) { var kv = FilterCommand.Substring(match.Value.Length).Split('=', 2); if (kv.Length == 2) { search.SetFilter(kv[0], kv[1], true, "set-multi:" == match.Value); } } if (FilterCommand.StartsWith("set-timezone:")) { var tz = FilterCommand.Substring("set-timezone:".Length); if (TimeZones.TryGet(tz) is { } tzo) search.SetFilter("timezone", tzo.Id); } if (FilterCommand.StartsWith("unset:")) { var k = FilterCommand.Substring("unset:".Length); search.SetFilter(k); } if (FilterCommand is "reset") { var tz = search.GetFilterString("timezone"); search.Filters.Clear(); if (tz != null) search.SetFilter("timezone", tz); search.TextSearch = ""; return; } if (FilterCommand is "alltime") { search.Filters.Remove("daterange"); search.Filters.Remove("startdate"); search.Filters.Remove("enddate"); } if (FilterCommand.StartsWith("set-daterange:")) { var dateRange = FilterCommand.Substring("set-daterange:".Length); if (SearchString.IsValidDateRange(dateRange)) search.SetDateRange(dateRange, true); } } public IActionResult Redirect(HttpRequest request) { var query = QueryHelpers.ParseQuery(request.QueryString.Value); var newQuery = query.ToDictionary( x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase); newQuery.Remove("SearchTerm"); newQuery.Remove("SearchText"); newQuery.Remove("Skip"); if (!string.IsNullOrEmpty(SearchTerm)) newQuery["SearchTerm"] = new StringValues(SearchTerm); if (!string.IsNullOrEmpty(SearchText)) newQuery["SearchText"] = new StringValues(SearchText); newQuery.Remove("FilterCommand"); var path = (request.PathBase + request.Path).ToString(); var newUrl = QueryHelpers.AddQueryString(path, newQuery); return new LocalRedirectResult(newUrl); } public Dictionary PaginationQuery { get; set; } [ValidateNever] [BindingBehavior(BindingBehavior.Never)] public abstract int CurrentPageCount { get; } } }