566 lines
17 KiB
C#
566 lines
17 KiB
C#
//Contributor : MVCContrib
|
|
|
|
using System.Text;
|
|
using System.Text.Encodings.Web;
|
|
using Microsoft.AspNetCore.Html;
|
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Nop.Core;
|
|
using Nop.Core.Infrastructure;
|
|
using Nop.Services.Localization;
|
|
using Nop.Web.Framework.Extensions;
|
|
|
|
namespace Nop.Web.Framework.UI.Paging;
|
|
|
|
/// <summary>
|
|
/// Renders a pager component from an IPageableModel datasource.
|
|
/// </summary>
|
|
public partial class Pager : IHtmlContent
|
|
{
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// ViewContext
|
|
/// </summary>
|
|
public ViewContext ViewContext { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region Fields
|
|
|
|
/// <summary>
|
|
/// Page query string prameter name
|
|
/// </summary>
|
|
protected string _queryParam = "page";
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to show Total summary
|
|
/// </summary>
|
|
protected bool _showTotalSummary = false;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to show pager items
|
|
/// </summary>
|
|
protected bool _showPagerItems = true;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to show the first item
|
|
/// </summary>
|
|
protected bool _showFirst = true;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to the previous item
|
|
/// </summary>
|
|
protected bool _showPrevious = true;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to show the next item
|
|
/// </summary>
|
|
protected bool _showNext = true;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to show the last item
|
|
/// </summary>
|
|
protected bool _showLast = true;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to show individual page
|
|
/// </summary>
|
|
protected bool _showIndividualPages = true;
|
|
|
|
/// <summary>
|
|
/// A value indicating whether to render empty query string parameters (without values)
|
|
/// </summary>
|
|
protected bool _renderEmptyParameters = true;
|
|
|
|
/// <summary>
|
|
/// Number of individual page items to display
|
|
/// </summary>
|
|
protected int _individualPagesDisplayedCount = 5;
|
|
|
|
/// <summary>
|
|
/// Boolean parameter names
|
|
/// </summary>
|
|
protected IList<string> _booleanParameterNames = new List<string>();
|
|
|
|
/// <summary>
|
|
/// First page css class name
|
|
/// </summary>
|
|
protected string _firstPageCssClass = "first-page";
|
|
|
|
/// <summary>
|
|
/// Previous page css class name
|
|
/// </summary>
|
|
protected string _previousPageCssClass = "previous-page";
|
|
|
|
/// <summary>
|
|
/// Current page css class name
|
|
/// </summary>
|
|
protected string _currentPageCssClass = "current-page";
|
|
|
|
/// <summary>
|
|
/// Individual page css class name
|
|
/// </summary>
|
|
protected string _individualPageCssClass = "individual-page";
|
|
|
|
/// <summary>
|
|
/// Next page css class name
|
|
/// </summary>
|
|
protected string _nextPageCssClass = "next-page";
|
|
|
|
/// <summary>
|
|
/// Last page css class name
|
|
/// </summary>
|
|
protected string _lastPageCssClass = "last-page";
|
|
|
|
/// <summary>
|
|
/// Main ul css class name
|
|
/// </summary>
|
|
protected string _mainUlCssClass = string.Empty;
|
|
|
|
protected readonly IPageableModel _model;
|
|
|
|
#endregion
|
|
|
|
#region Ctor
|
|
|
|
public Pager(IPageableModel model, ViewContext context)
|
|
{
|
|
_model = model;
|
|
ViewContext = context;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
#region Fields setters
|
|
|
|
/// <summary>
|
|
/// Set
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager QueryParam(string value)
|
|
{
|
|
_queryParam = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to show Total summary
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowTotalSummary(bool value)
|
|
{
|
|
_showTotalSummary = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to show pager items
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowPagerItems(bool value)
|
|
{
|
|
_showPagerItems = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to show the first item
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowFirst(bool value)
|
|
{
|
|
_showFirst = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to the previous item
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowPrevious(bool value)
|
|
{
|
|
_showPrevious = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to show the next item
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowNext(bool value)
|
|
{
|
|
_showNext = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to show the last item
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowLast(bool value)
|
|
{
|
|
_showLast = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set number of individual page items to display
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager ShowIndividualPages(bool value)
|
|
{
|
|
_showIndividualPages = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value indicating whether to render empty query string parameters (without values)
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager RenderEmptyParameters(bool value)
|
|
{
|
|
_renderEmptyParameters = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set number of individual page items to display
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager IndividualPagesDisplayedCount(int value)
|
|
{
|
|
_individualPagesDisplayedCount = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// little hack here due to ugly MVC implementation
|
|
/// find more info here: http://www.mindstorminteractive.com/topics/jquery-fix-asp-net-mvc-checkbox-truefalse-value/
|
|
/// </summary>
|
|
/// <param name="paramName">Parameter name</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager BooleanParameterName(string paramName)
|
|
{
|
|
_booleanParameterNames.Add(paramName);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set first page pager css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager FirstPageCssClass(string value)
|
|
{
|
|
_firstPageCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set previous page pager css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager PreviousPageCssClass(string value)
|
|
{
|
|
_previousPageCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set current page pager css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager CurrentPageCssClass(string value)
|
|
{
|
|
_currentPageCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set individual page pager css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager IndividualPageCssClass(string value)
|
|
{
|
|
_individualPageCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set next page pager css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager NextPageCssClass(string value)
|
|
{
|
|
_nextPageCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set last page pager css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager LastPageCssClass(string value)
|
|
{
|
|
_lastPageCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set main ul css class name
|
|
/// </summary>
|
|
/// <param name="value">Value</param>
|
|
/// <returns>Pager</returns>
|
|
public Pager MainUlCssClass(string value)
|
|
{
|
|
_mainUlCssClass = value;
|
|
return this;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Write control
|
|
/// </summary>
|
|
/// <param name="writer">Writer</param>
|
|
/// <param name="encoder">Encoder</param>
|
|
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
|
|
{
|
|
writer.Write(ToString());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate HTML control
|
|
/// </summary>
|
|
/// <returns>HTML control</returns>
|
|
public override string ToString()
|
|
{
|
|
return GenerateHtmlStringAsync().Result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is pager empty (only one page)?
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the result
|
|
/// </returns>
|
|
public virtual async Task<bool> IsEmpty()
|
|
{
|
|
return string.IsNullOrEmpty(await GenerateHtmlStringAsync());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Utilities
|
|
|
|
/// <summary>
|
|
/// Generate HTML control
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the hTML control
|
|
/// </returns>
|
|
protected virtual async Task<string> GenerateHtmlStringAsync()
|
|
{
|
|
if (_model.TotalItems == 0)
|
|
return null;
|
|
|
|
var localizationService = EngineContext.Current.Resolve<ILocalizationService>();
|
|
|
|
var links = new StringBuilder();
|
|
if (_showTotalSummary && (_model.TotalPages > 0))
|
|
{
|
|
links.Append("<li class=\"total-summary\">");
|
|
|
|
links.Append(string.Format(await localizationService.GetResourceAsync("Pager.CurrentPage"),
|
|
_model.PageIndex + 1, _model.TotalPages, _model.TotalItems));
|
|
|
|
links.Append("</li>");
|
|
}
|
|
|
|
if (_showPagerItems && (_model.TotalPages > 1))
|
|
{
|
|
if (_showFirst)
|
|
{
|
|
//first page
|
|
if ((_model.PageIndex >= 3) && (_model.TotalPages > _individualPagesDisplayedCount))
|
|
links.Append(await CreatePageLinkAsync(1, await localizationService.GetResourceAsync("Pager.First"), _firstPageCssClass));
|
|
}
|
|
|
|
if (_showPrevious)
|
|
{
|
|
//previous page
|
|
if (_model.PageIndex > 0)
|
|
links.Append(await CreatePageLinkAsync(_model.PageIndex, await localizationService.GetResourceAsync("Pager.Previous"), _previousPageCssClass));
|
|
}
|
|
|
|
if (_showIndividualPages)
|
|
{
|
|
//individual pages
|
|
var firstIndividualPageIndex = GetFirstIndividualPageIndex();
|
|
var lastIndividualPageIndex = GetLastIndividualPageIndex();
|
|
for (var i = firstIndividualPageIndex; i <= lastIndividualPageIndex; i++)
|
|
{
|
|
if (_model.PageIndex == i)
|
|
links.AppendFormat("<li class=\"" + _currentPageCssClass + "\"><span>{0}</span></li>", (i + 1));
|
|
else
|
|
links.Append(await CreatePageLinkAsync(i + 1, (i + 1).ToString(), _individualPageCssClass));
|
|
}
|
|
}
|
|
|
|
if (_showNext)
|
|
{
|
|
//next page
|
|
if ((_model.PageIndex + 1) < _model.TotalPages)
|
|
links.Append(await CreatePageLinkAsync(_model.PageIndex + 2, await localizationService.GetResourceAsync("Pager.Next"), _nextPageCssClass));
|
|
}
|
|
|
|
if (_showLast)
|
|
{
|
|
//last page
|
|
if (((_model.PageIndex + 3) < _model.TotalPages) && (_model.TotalPages > _individualPagesDisplayedCount))
|
|
links.Append(await CreatePageLinkAsync(_model.TotalPages, await localizationService.GetResourceAsync("Pager.Last"), _lastPageCssClass));
|
|
}
|
|
}
|
|
|
|
var result = links.ToString();
|
|
if (!string.IsNullOrEmpty(result))
|
|
result = string.Format("<ul{0}>", string.IsNullOrEmpty(_mainUlCssClass) ? "" : " class=\"" + _mainUlCssClass + "\"") + result + "</ul>";
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get first individual page index
|
|
/// </summary>
|
|
/// <returns>Page index</returns>
|
|
protected virtual int GetFirstIndividualPageIndex()
|
|
{
|
|
if ((_model.TotalPages < _individualPagesDisplayedCount) || ((_model.PageIndex - (_individualPagesDisplayedCount / 2)) < 0))
|
|
return 0;
|
|
|
|
if ((_model.PageIndex + (_individualPagesDisplayedCount / 2)) >= _model.TotalPages)
|
|
return _model.TotalPages - _individualPagesDisplayedCount;
|
|
|
|
return _model.PageIndex - (_individualPagesDisplayedCount / 2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get last individual page index
|
|
/// </summary>
|
|
/// <returns>Page index</returns>
|
|
protected virtual int GetLastIndividualPageIndex()
|
|
{
|
|
var num = _individualPagesDisplayedCount / 2;
|
|
if ((_individualPagesDisplayedCount % 2) == 0)
|
|
num--;
|
|
|
|
if ((_model.TotalPages < _individualPagesDisplayedCount) || ((_model.PageIndex + num) >= _model.TotalPages))
|
|
return _model.TotalPages - 1;
|
|
|
|
if ((_model.PageIndex - (_individualPagesDisplayedCount / 2)) < 0)
|
|
return _individualPagesDisplayedCount - 1;
|
|
|
|
return _model.PageIndex + num;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create page link
|
|
/// </summary>
|
|
/// <param name="pageNumber">Page number</param>
|
|
/// <param name="text">Text</param>
|
|
/// <param name="cssClass">CSS class</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the link
|
|
/// </returns>
|
|
protected virtual async Task<string> CreatePageLinkAsync(int pageNumber, string text, string cssClass)
|
|
{
|
|
var liBuilder = new TagBuilder("li");
|
|
if (!string.IsNullOrWhiteSpace(cssClass))
|
|
liBuilder.AddCssClass(cssClass);
|
|
|
|
var aBuilder = new TagBuilder("a");
|
|
aBuilder.InnerHtml.AppendHtml(text);
|
|
aBuilder.MergeAttribute("data-page", pageNumber.ToString());
|
|
aBuilder.MergeAttribute("href", CreateDefaultUrl(pageNumber));
|
|
|
|
liBuilder.InnerHtml.AppendHtml(aBuilder);
|
|
return await liBuilder.RenderHtmlContentAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create default URL
|
|
/// </summary>
|
|
/// <param name="pageNumber">Page number</param>
|
|
/// <returns>URL</returns>
|
|
protected virtual string CreateDefaultUrl(int pageNumber)
|
|
{
|
|
var routeValues = new RouteValueDictionary();
|
|
|
|
var parametersWithEmptyValues = new List<string>();
|
|
foreach (var key in ViewContext.HttpContext.Request.Query.Keys.Where(key => key != null))
|
|
{
|
|
var value = ViewContext.HttpContext.Request.Query[key].ToString();
|
|
if (_renderEmptyParameters && string.IsNullOrEmpty(value))
|
|
{
|
|
//we store query string parameters with empty values separately
|
|
//we need to do it because they are not properly processed in the UrlHelper.GenerateUrl method (dropped for some reasons)
|
|
parametersWithEmptyValues.Add(key);
|
|
}
|
|
else
|
|
{
|
|
if (_booleanParameterNames.Contains(key, StringComparer.InvariantCultureIgnoreCase))
|
|
{
|
|
//little hack here due to ugly MVC implementation
|
|
//find more info here: http://www.mindstorminteractive.com/topics/jquery-fix-asp-net-mvc-checkbox-truefalse-value/
|
|
if (!string.IsNullOrEmpty(value) && value.Equals("true,false", StringComparison.InvariantCultureIgnoreCase))
|
|
value = "true";
|
|
}
|
|
|
|
routeValues[key] = value;
|
|
}
|
|
}
|
|
|
|
if (pageNumber > 1)
|
|
routeValues[_queryParam] = pageNumber;
|
|
else
|
|
{
|
|
//SEO. we do not render pageindex query string parameter for the first page
|
|
if (routeValues.ContainsKey(_queryParam))
|
|
routeValues.Remove(_queryParam);
|
|
}
|
|
|
|
var webHelper = EngineContext.Current.Resolve<IWebHelper>();
|
|
var url = webHelper.GetThisPageUrl(false);
|
|
foreach (var routeValue in routeValues)
|
|
url = webHelper.ModifyQueryString(url, routeValue.Key, routeValue.Value?.ToString());
|
|
|
|
if (_renderEmptyParameters && parametersWithEmptyValues.Any())
|
|
foreach (var key in parametersWithEmptyValues)
|
|
url = webHelper.ModifyQueryString(url, key);
|
|
|
|
return url;
|
|
}
|
|
|
|
#endregion
|
|
} |