mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-25 19:17:13 +08:00
866 lines
37 KiB
C#
866 lines
37 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Web;
|
|
using System.Web.Mvc;
|
|
using System.Web.Mvc.Html;
|
|
using System.Web.Routing;
|
|
using Orchard.ContentManagement;
|
|
using Orchard.DisplayManagement;
|
|
using Orchard.DisplayManagement.Descriptors;
|
|
using Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy;
|
|
using Orchard.Environment;
|
|
using Orchard.Localization;
|
|
using Orchard.Mvc;
|
|
using Orchard.Settings;
|
|
using Orchard.UI;
|
|
using Orchard.UI.Resources;
|
|
using Orchard.UI.Zones;
|
|
using Orchard.Utility.Extensions;
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
|
|
namespace Orchard.Core.Shapes {
|
|
public class CoreShapes : IShapeTableProvider {
|
|
private readonly Work<WorkContext> _workContext;
|
|
private readonly Work<IResourceManager> _resourceManager;
|
|
private readonly Work<IHttpContextAccessor> _httpContextAccessor;
|
|
private readonly Work<IShapeFactory> _shapeFactory;
|
|
|
|
public CoreShapes(
|
|
Work<WorkContext> workContext,
|
|
Work<IResourceManager> resourceManager,
|
|
Work<IHttpContextAccessor> httpContextAccessor,
|
|
Work<IShapeFactory> shapeFactory
|
|
) {
|
|
_workContext = workContext;
|
|
_resourceManager = resourceManager;
|
|
_httpContextAccessor = httpContextAccessor;
|
|
_shapeFactory = shapeFactory;
|
|
|
|
T = NullLocalizer.Instance;
|
|
}
|
|
|
|
public Localizer T { get; set; }
|
|
public dynamic New { get { return _shapeFactory.Value; } }
|
|
|
|
public void Discover(ShapeTableBuilder builder) {
|
|
// the root page shape named 'Layout' is wrapped with 'Document'
|
|
// and has an automatic zone creating behavior
|
|
builder.Describe("Layout")
|
|
.Configure(descriptor => descriptor.Wrappers.Add("Document"))
|
|
.OnCreating(creating => creating.Create = () => new ZoneHolding(() => creating.New.Zone()))
|
|
.OnCreated(created => {
|
|
var layout = created.Shape;
|
|
|
|
layout.Head = created.New.DocumentZone(ZoneName: "Head");
|
|
layout.Body = created.New.DocumentZone(ZoneName: "Body");
|
|
layout.Tail = created.New.DocumentZone(ZoneName: "Tail");
|
|
|
|
layout.Body.Add(created.New.PlaceChildContent(Source: layout));
|
|
|
|
layout.Content = created.New.Zone();
|
|
layout.Content.ZoneName = "Content";
|
|
layout.Content.Add(created.New.PlaceChildContent(Source: layout));
|
|
|
|
});
|
|
|
|
// 'Zone' shapes are built on the Zone base class
|
|
// They have class="zone zone-{name}"
|
|
// and the template can be specialized with "Zone-{Name}" base file name
|
|
builder.Describe("Zone")
|
|
.OnCreating(creating => creating.Create = () => new Zone())
|
|
.OnDisplaying(displaying => {
|
|
var zone = displaying.Shape;
|
|
string zoneName = zone.ZoneName;
|
|
zone.Classes.Add("zone-" + zoneName.HtmlClassify());
|
|
zone.Classes.Add("zone");
|
|
|
|
// Zone__[ZoneName] e.g. Zone-SideBar
|
|
zone.Metadata.Alternates.Add("Zone__" + zoneName);
|
|
});
|
|
|
|
builder.Describe("Menu")
|
|
.OnDisplaying(displaying => {
|
|
var menu = displaying.Shape;
|
|
string menuName = menu.MenuName;
|
|
menu.Classes.Add("menu-" + menuName.HtmlClassify());
|
|
menu.Classes.Add("menu");
|
|
menu.Metadata.Alternates.Add("Menu__" + EncodeAlternateElement(menuName));
|
|
});
|
|
|
|
builder.Describe("MenuItem")
|
|
.OnDisplaying(displaying => {
|
|
var menuItem = displaying.Shape;
|
|
var menu = menuItem.Menu;
|
|
int level = menuItem.Level;
|
|
|
|
menuItem.Metadata.Alternates.Add("MenuItem__level__" + level);
|
|
menuItem.Metadata.Alternates.Add("MenuItem__" + EncodeAlternateElement(menu.MenuName));
|
|
menuItem.Metadata.Alternates.Add("MenuItem__" + EncodeAlternateElement(menu.MenuName) + "__level__" + level);
|
|
});
|
|
|
|
builder.Describe("MenuItemLink")
|
|
.OnDisplaying(displaying => {
|
|
var menuItem = displaying.Shape;
|
|
string menuName = menuItem.Menu.MenuName;
|
|
string contentType = null;
|
|
int level = menuItem.Level;
|
|
if (menuItem.Content != null) {
|
|
contentType = ((IContent) menuItem.Content).ContentItem.ContentType;
|
|
}
|
|
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__level__" + level);
|
|
|
|
// MenuItemLink__[ContentType] e.g. MenuItemLink-HtmlMenuItem
|
|
// MenuItemLink__[ContentType]__level__[level] e.g. MenuItemLink-HtmlMenuItem-level-2
|
|
if (contentType != null) {
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(contentType));
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(contentType) + "__level__" + level);
|
|
}
|
|
|
|
// MenuItemLink__[MenuName] e.g. MenuItemLink-Main-Menu
|
|
// MenuItemLink__[MenuName]__level__[level] e.g. MenuItemLink-Main-Menu-level-2
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(menuName));
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(menuName) + "__level__" + level);
|
|
|
|
// MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-Main-Menu-HtmlMenuItem
|
|
// MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-Main-Menu-HtmlMenuItem-level-2
|
|
if (contentType != null) {
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(menuName) + "__" + EncodeAlternateElement(contentType));
|
|
menuItem.Metadata.Alternates.Add("MenuItemLink__" + EncodeAlternateElement(menuName) + "__" + EncodeAlternateElement(contentType) + "__level__" + level);
|
|
}
|
|
});
|
|
|
|
builder.Describe("LocalMenu")
|
|
.OnDisplaying(displaying => {
|
|
var menu = displaying.Shape;
|
|
string menuName = menu.MenuName;
|
|
menu.Classes.Add("localmenu-" + menuName.HtmlClassify());
|
|
menu.Classes.Add("localmenu");
|
|
menu.Metadata.Alternates.Add("LocalMenu__" + EncodeAlternateElement(menuName));
|
|
});
|
|
|
|
builder.Describe("LocalMenuItem")
|
|
.OnDisplaying(displaying => {
|
|
var menuItem = displaying.Shape;
|
|
var menu = menuItem.Menu;
|
|
menuItem.Metadata.Alternates.Add("LocalMenuItem__" + EncodeAlternateElement(menu.MenuName));
|
|
});
|
|
|
|
#region Pager alternates
|
|
builder.Describe("Pager")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_Gap")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape.Pager;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
pager.Metadata.Alternates.Add("Pager_Gap__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_First")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape.Pager;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager_First__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_Previous")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape.Pager;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager_Previous__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_Next")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape.Pager;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager_Next__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_Last")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape.Pager;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager_Last__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_CurrentPage")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape.Pager;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager_CurrentPage__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
builder.Describe("Pager_Links")
|
|
.OnDisplaying(displaying => {
|
|
var pager = displaying.Shape;
|
|
string pagerId = pager.PagerId;
|
|
if (!String.IsNullOrWhiteSpace(pagerId))
|
|
displaying.Shape.Metadata.Alternates.Add("Pager_Links__" + EncodeAlternateElement(pagerId));
|
|
});
|
|
|
|
#endregion
|
|
|
|
// 'List' shapes start with several empty collections
|
|
builder.Describe("List")
|
|
.OnCreated(created => {
|
|
var list = created.Shape;
|
|
list.Tag = "ul";
|
|
list.ItemClasses = new List<string>();
|
|
list.ItemAttributes = new Dictionary<string, string>();
|
|
});
|
|
|
|
builder.Describe("Style")
|
|
.OnDisplaying(displaying => {
|
|
var resource = displaying.Shape;
|
|
string url = resource.Url;
|
|
var fileName = url != null ? StaticFileBindingStrategy.GetAlternateShapeNameFromFileName(url) : default(string);
|
|
if (!string.IsNullOrEmpty(fileName)) {
|
|
resource.Metadata.Alternates.Add("Style__" + fileName);
|
|
}
|
|
});
|
|
|
|
builder.Describe("Script")
|
|
.OnDisplaying(displaying => {
|
|
var resource = displaying.Shape;
|
|
string url = resource.Url;
|
|
var fileName = url != null ? StaticFileBindingStrategy.GetAlternateShapeNameFromFileName(url) : default(string);
|
|
if (!string.IsNullOrEmpty(fileName)) {
|
|
resource.Metadata.Alternates.Add("Script__" + fileName);
|
|
}
|
|
});
|
|
|
|
builder.Describe("Resource")
|
|
.OnDisplaying(displaying => {
|
|
var resource = displaying.Shape;
|
|
string url = resource.Url;
|
|
var fileName = url != null ? StaticFileBindingStrategy.GetAlternateShapeNameFromFileName(url) : default(string);
|
|
if (!string.IsNullOrEmpty(fileName)) {
|
|
resource.Metadata.Alternates.Add("Resource__" + fileName);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable<string> classes, IDictionary<string, string> attributes) {
|
|
var tagBuilder = new TagBuilder(tagName);
|
|
tagBuilder.MergeAttributes(attributes, false);
|
|
foreach (var cssClass in classes ?? Enumerable.Empty<string>())
|
|
tagBuilder.AddCssClass(cssClass);
|
|
if (!string.IsNullOrWhiteSpace(id))
|
|
tagBuilder.GenerateId(id);
|
|
return tagBuilder;
|
|
}
|
|
|
|
[Shape]
|
|
public void Zone(dynamic Display, dynamic Shape, TextWriter Output) {
|
|
string id = Shape.Id;
|
|
IEnumerable<string> classes = Shape.Classes;
|
|
IDictionary<string, string> attributes = Shape.Attributes;
|
|
var zoneWrapper = GetTagBuilder("div", id, classes, attributes);
|
|
Output.Write(zoneWrapper.ToString(TagRenderMode.StartTag));
|
|
foreach (var item in Order(Shape))
|
|
Output.Write(Display(item));
|
|
Output.Write(zoneWrapper.ToString(TagRenderMode.EndTag));
|
|
}
|
|
|
|
[Shape]
|
|
public void ContentZone(dynamic Display, dynamic Shape, TextWriter Output) {
|
|
var unordered = ((IEnumerable<dynamic>)Shape).ToArray();
|
|
var tabbed = unordered.GroupBy(x => (string)x.Metadata.Tab);
|
|
|
|
if (tabbed.Count() > 1) {
|
|
foreach (var tab in tabbed) {
|
|
var tabName = String.IsNullOrWhiteSpace(tab.Key) ? "Content" : tab.Key;
|
|
var tabBuilder = new TagBuilder("div");
|
|
tabBuilder.Attributes["id"] = "tab-" + tabName.HtmlClassify();
|
|
tabBuilder.Attributes["data-tab"] = tabName;
|
|
Output.Write(tabBuilder.ToString(TagRenderMode.StartTag));
|
|
foreach (var item in Order(tab))
|
|
Output.Write(Display(item));
|
|
|
|
Output.Write(tabBuilder.ToString(TagRenderMode.EndTag));
|
|
}
|
|
}
|
|
else {
|
|
foreach (var item in Order(unordered))
|
|
Output.Write(Display(item));
|
|
}
|
|
}
|
|
|
|
[Shape]
|
|
public void DocumentZone(dynamic Display, dynamic Shape, TextWriter Output) {
|
|
foreach (var item in Order(Shape))
|
|
Output.Write(Display(item));
|
|
}
|
|
|
|
public static IEnumerable<dynamic> Order(dynamic shape) {
|
|
IEnumerable<dynamic> unordered = shape;
|
|
if (unordered == null || unordered.Count() < 2)
|
|
return shape;
|
|
|
|
var i = 1;
|
|
var progress = 1;
|
|
var flatPositionComparer = new FlatPositionComparer();
|
|
var ordering = unordered.Select(item => {
|
|
string position = null;
|
|
var itemPosition = item as IPositioned;
|
|
if (itemPosition != null) {
|
|
position = itemPosition.Position;
|
|
}
|
|
|
|
return new { item, position };
|
|
}).ToList();
|
|
|
|
// since this isn't sticking around (hence, the "hack" in the name), throwing (in) a gnome
|
|
while (i < ordering.Count()) {
|
|
if (flatPositionComparer.Compare(ordering[i].position, ordering[i - 1].position) > -1) {
|
|
if (i == progress)
|
|
progress = ++i;
|
|
else
|
|
i = progress;
|
|
}
|
|
else {
|
|
var higherThanItShouldBe = ordering[i];
|
|
ordering[i] = ordering[i - 1];
|
|
ordering[i - 1] = higherThanItShouldBe;
|
|
if (i > 1)
|
|
--i;
|
|
}
|
|
}
|
|
|
|
return ordering.Select(ordered => ordered.item).ToList();
|
|
}
|
|
|
|
public static IEnumerable<string> HarvestAndSortTabs(IEnumerable<dynamic> shapes) {
|
|
var orderedShapes = Order(shapes).ToArray();
|
|
var tabs = new List<string>();
|
|
|
|
foreach (var shape in orderedShapes) {
|
|
var tab = (string)shape.Metadata.Tab;
|
|
|
|
if (String.IsNullOrEmpty(tab))
|
|
continue;
|
|
|
|
if(!tabs.Contains(tab))
|
|
tabs.Add(tab);
|
|
}
|
|
|
|
// If we have any tabs, make sure we have at least the Content tab and that it is the first one,
|
|
// since that's where we will put anything else not part of a tab.
|
|
if (tabs.Any()) {
|
|
tabs.Remove("Content");
|
|
tabs.Insert(0, "Content");
|
|
}
|
|
|
|
return tabs;
|
|
}
|
|
|
|
[Shape]
|
|
public void HeadScripts(dynamic Display, TextWriter Output) {
|
|
WriteResources(Display, Output, "script", ResourceLocation.Head, null);
|
|
WriteLiteralScripts(Output, _resourceManager.Value.GetRegisteredHeadScripts());
|
|
}
|
|
|
|
[Shape]
|
|
public void FootScripts(dynamic Display, TextWriter Output) {
|
|
WriteResources(Display, Output, "script", null, ResourceLocation.Head);
|
|
WriteLiteralScripts(Output, _resourceManager.Value.GetRegisteredFootScripts());
|
|
}
|
|
|
|
[Shape]
|
|
public void Metas(TextWriter Output) {
|
|
foreach (var meta in _resourceManager.Value.GetRegisteredMetas() ) {
|
|
Output.WriteLine(meta.GetTag());
|
|
}
|
|
}
|
|
|
|
[Shape]
|
|
public void HeadLinks(TextWriter Output) {
|
|
foreach (var link in _resourceManager.Value.GetRegisteredLinks() ) {
|
|
Output.WriteLine(link.GetTag());
|
|
}
|
|
}
|
|
|
|
[Shape]
|
|
public void StylesheetLinks(dynamic Display, TextWriter Output) {
|
|
WriteResources(Display, Output, "stylesheet", null, null);
|
|
}
|
|
|
|
[Shape]
|
|
public void Style(dynamic Display, HtmlHelper Html, TextWriter Output, ResourceDefinition Resource, string Url, string Condition, Dictionary<string, string> TagAttributes) {
|
|
// do not write to Output directly as Styles are rendered in Zones
|
|
ResourceManager.WriteResource(Html.ViewContext.Writer, Resource, Url, Condition, TagAttributes);
|
|
}
|
|
|
|
[Shape]
|
|
public void Script(HtmlHelper Html, TextWriter Output, ResourceDefinition Resource, string Url, string Condition, Dictionary<string, string> TagAttributes) {
|
|
// do not write to Output directly as Styles are rendered in Zones
|
|
ResourceManager.WriteResource(Html.ViewContext.Writer, Resource, Url, Condition, TagAttributes);
|
|
}
|
|
|
|
[Shape]
|
|
public void Resource(TextWriter Output, ResourceDefinition Resource, string Url, string Condition, Dictionary<string, string> TagAttributes) {
|
|
ResourceManager.WriteResource(Output, Resource, Url, Condition, TagAttributes);
|
|
}
|
|
|
|
private static void WriteLiteralScripts(TextWriter output, IEnumerable<string> scripts) {
|
|
if (scripts == null) {
|
|
return;
|
|
}
|
|
foreach (string script in scripts) {
|
|
output.WriteLine(script);
|
|
}
|
|
}
|
|
|
|
private void WriteResources(dynamic Display, TextWriter Output, string resourceType, ResourceLocation? includeLocation, ResourceLocation? excludeLocation) {
|
|
bool debugMode;
|
|
var site = _workContext.Value.CurrentSite;
|
|
switch (site.ResourceDebugMode) {
|
|
case ResourceDebugMode.Enabled:
|
|
debugMode = true;
|
|
break;
|
|
case ResourceDebugMode.Disabled:
|
|
debugMode = false;
|
|
break;
|
|
default:
|
|
Debug.Assert(site.ResourceDebugMode == ResourceDebugMode.FromAppSetting, "Unknown ResourceDebugMode value.");
|
|
var context = _httpContextAccessor.Value.Current();
|
|
debugMode = context != null && context.IsDebuggingEnabled;
|
|
break;
|
|
}
|
|
var defaultSettings = new RequireSettings {
|
|
DebugMode = debugMode,
|
|
CdnMode = site.UseCdn,
|
|
Culture = _workContext.Value.CurrentCulture,
|
|
};
|
|
var requiredResources = _resourceManager.Value.BuildRequiredResources(resourceType);
|
|
var httpContext = _httpContextAccessor.Value.Current();
|
|
var appPath = httpContext == null || httpContext.Request == null
|
|
? null
|
|
: httpContext.Request.ApplicationPath;
|
|
var ssl = httpContext?.Request?.IsSecureConnection ?? false;
|
|
foreach (var context in requiredResources.Where(r =>
|
|
(includeLocation.HasValue ? r.Settings.Location == includeLocation.Value : true) &&
|
|
(excludeLocation.HasValue ? r.Settings.Location != excludeLocation.Value : true))) {
|
|
|
|
var path = context.GetResourceUrl(defaultSettings, appPath, ssl);
|
|
var condition = context.Settings.Condition;
|
|
var attributes = context.Settings.HasAttributes ? context.Settings.Attributes : null;
|
|
IHtmlString result;
|
|
if (resourceType == "stylesheet") {
|
|
result = Display.Style(Url: path, Condition: condition, Resource: context.Resource, TagAttributes: attributes);
|
|
}
|
|
else if (resourceType == "script") {
|
|
result = Display.Script(Url: path, Condition: condition, Resource: context.Resource, TagAttributes: attributes);
|
|
}
|
|
else {
|
|
result = Display.Resource(Url: path, Condition: condition, Resource: context.Resource, TagAttributes: attributes);
|
|
}
|
|
Output.Write(result);
|
|
}
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_Links(dynamic Shape, dynamic Display,
|
|
HtmlHelper Html,
|
|
int Page,
|
|
int PageSize,
|
|
double TotalItemCount,
|
|
int? Quantity,
|
|
object FirstText,
|
|
object PreviousText,
|
|
object NextText,
|
|
object LastText,
|
|
object GapText,
|
|
string PagerId
|
|
// parameter omitted to workaround an issue where a NullRef is thrown
|
|
// when an anonymous object is bound to an object shape parameter
|
|
/*object RouteValues*/) {
|
|
|
|
var currentPage = Page;
|
|
if (currentPage < 1)
|
|
currentPage = 1;
|
|
|
|
var pageSize = PageSize;
|
|
|
|
var numberOfPagesToShow = Quantity ?? 0;
|
|
if (Quantity == null || Quantity < 0)
|
|
numberOfPagesToShow = 7;
|
|
|
|
var totalPageCount = pageSize > 0 ? (int)Math.Ceiling(TotalItemCount / pageSize) : 1;
|
|
|
|
var firstText = FirstText ?? T("<<");
|
|
var previousText = PreviousText ?? T("<");
|
|
var nextText = NextText ?? T(">");
|
|
var lastText = LastText ?? T(">>");
|
|
var gapText = GapText ?? T("...");
|
|
|
|
var routeData = new RouteValueDictionary(Html.ViewContext.RouteData.Values);
|
|
var queryString = _workContext.Value.HttpContext.Request.QueryString;
|
|
if (queryString != null) {
|
|
foreach (var key in from string key in queryString.Keys where key != null && !routeData.ContainsKey(key) let value = queryString[key] select key) {
|
|
routeData[key] = queryString[key];
|
|
}
|
|
}
|
|
|
|
// specific cross-requests route data can be passed to the shape directly (e.g., Orchard.Users)
|
|
var shapeRoute = (object)Shape.RouteData;
|
|
|
|
if (shapeRoute != null) {
|
|
var shapeRouteData = shapeRoute as RouteValueDictionary;
|
|
if (shapeRouteData == null) {
|
|
var route = shapeRoute as RouteData;
|
|
if (route != null) {
|
|
shapeRouteData = (route).Values;
|
|
}
|
|
}
|
|
|
|
if (shapeRouteData != null) {
|
|
foreach (var rd in shapeRouteData) {
|
|
routeData[rd.Key] = rd.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// HACK: MVC 3 is adding a specific value in System.Web.Mvc.Html.ChildActionExtensions.ActionHelper
|
|
// when a content item is set as home page, it is rendered by using Html.RenderAction, and the routeData is altered
|
|
// This code removes this extra route value
|
|
var removedKeys = routeData.Keys.Where(key => routeData[key] is DictionaryValueProvider<object>).ToList();
|
|
foreach (var key in removedKeys) {
|
|
routeData.Remove(key);
|
|
}
|
|
|
|
int firstPage = Math.Max(1, Page - (numberOfPagesToShow / 2));
|
|
int lastPage = Math.Min(totalPageCount, Page + (int)(numberOfPagesToShow / 2));
|
|
|
|
var pageKey = String.IsNullOrEmpty(PagerId) ? "page" : PagerId;
|
|
|
|
Shape.Classes.Add("pager");
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "List";
|
|
|
|
// first and previous pages
|
|
if (Page > 1) {
|
|
if (routeData.ContainsKey(pageKey)) {
|
|
routeData.Remove(pageKey); // to keep from having "page=1" in the query string
|
|
}
|
|
// first
|
|
Shape.Add(New.Pager_First(Value: firstText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
|
|
|
|
// previous
|
|
if (currentPage > 2) { // also to keep from having "page=1" in the query string
|
|
routeData[pageKey] = currentPage - 1;
|
|
}
|
|
Shape.Add(New.Pager_Previous(Value: previousText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
|
|
}
|
|
|
|
// gap at the beginning of the pager
|
|
if (firstPage > 1 && numberOfPagesToShow > 0) {
|
|
Shape.Add(New.Pager_Gap(Value: gapText, Pager: Shape));
|
|
}
|
|
|
|
// page numbers
|
|
if (numberOfPagesToShow > 0 && lastPage > 1) {
|
|
for (var p = firstPage; p <= lastPage; p++) {
|
|
if (p == currentPage) {
|
|
Shape.Add(New.Pager_CurrentPage(Value: p, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
|
|
}
|
|
else {
|
|
if (p == 1)
|
|
routeData.Remove(pageKey);
|
|
else
|
|
routeData[pageKey] = p;
|
|
Shape.Add(New.Pager_Link(Value: p, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
|
|
}
|
|
}
|
|
}
|
|
|
|
// gap at the end of the pager
|
|
if (lastPage < totalPageCount && numberOfPagesToShow > 0) {
|
|
Shape.Add(New.Pager_Gap(Value: gapText, Pager: Shape));
|
|
}
|
|
|
|
// next and last pages
|
|
if (Page < totalPageCount) {
|
|
// next
|
|
routeData[pageKey] = Page + 1;
|
|
Shape.Add(New.Pager_Next(Value: nextText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
|
|
// last
|
|
routeData[pageKey] = totalPageCount;
|
|
Shape.Add(New.Pager_Last(Value: lastText, RouteValues: new RouteValueDictionary(routeData), Pager: Shape));
|
|
}
|
|
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager(dynamic Shape, dynamic Display) {
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "Pager_Links";
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_First(dynamic Shape, dynamic Display) {
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "Pager_Link";
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_Previous(dynamic Shape, dynamic Display) {
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "Pager_Link";
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_CurrentPage(HtmlHelper Html, dynamic Display, object Value) {
|
|
var tagBuilder = new TagBuilder("span");
|
|
tagBuilder.InnerHtml = EncodeOrDisplay(Value, Display, Html).ToString();
|
|
|
|
return MvcHtmlString.Create(tagBuilder.ToString());
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_Next(dynamic Shape, dynamic Display) {
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "Pager_Link";
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_Last(dynamic Shape, dynamic Display) {
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "Pager_Link";
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_Link(HtmlHelper Html, dynamic Shape, dynamic Display, object Value) {
|
|
Shape.Metadata.Alternates.Clear();
|
|
Shape.Metadata.Type = "ActionLink";
|
|
return Display(Shape);
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString ActionLink(HtmlHelper Html, UrlHelper Url, dynamic Shape, dynamic Display, object Value) {
|
|
var RouteValues = (object)Shape.RouteValues;
|
|
RouteValueDictionary rvd;
|
|
if (RouteValues == null) {
|
|
rvd = new RouteValueDictionary();
|
|
}
|
|
else {
|
|
rvd = RouteValues is RouteValueDictionary ? (RouteValueDictionary)RouteValues : new RouteValueDictionary(RouteValues);
|
|
}
|
|
|
|
var action = Url.Action((string)rvd["action"], (string)rvd["controller"], rvd);
|
|
|
|
IEnumerable<string> classes = Shape.Classes;
|
|
IDictionary<string, string> attributes = Shape.Attributes;
|
|
attributes["href"] = action;
|
|
string id = Shape.Id;
|
|
var tag = GetTagBuilder("a", id, classes, attributes);
|
|
tag.InnerHtml = EncodeOrDisplay(Value, Display, Html).ToString();
|
|
|
|
return Html.Raw(tag.ToString());
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString Pager_Gap(HtmlHelper Html, dynamic Display, object Value) {
|
|
var tagBuilder = new TagBuilder("span");
|
|
tagBuilder.InnerHtml = EncodeOrDisplay(Value, Display, Html).ToString();
|
|
|
|
return MvcHtmlString.Create(tagBuilder.ToString());
|
|
}
|
|
|
|
[Shape]
|
|
public void List(
|
|
dynamic Display,
|
|
TextWriter Output,
|
|
IEnumerable<dynamic> Items,
|
|
string Tag,
|
|
string Id,
|
|
IEnumerable<string> Classes,
|
|
IDictionary<string, string> Attributes,
|
|
string ItemTag,
|
|
IEnumerable<string> ItemClasses,
|
|
IDictionary<string, string> ItemAttributes) {
|
|
|
|
if (Items == null)
|
|
return;
|
|
|
|
// prevent multiple enumerations
|
|
var items = Items.ToList();
|
|
|
|
// var itemDisplayOutputs = Items.Select(item => Display(item)).Where(output => !string.IsNullOrWhiteSpace(output.ToHtmlString())).ToList();
|
|
var count = items.Count();
|
|
if (count < 1)
|
|
return;
|
|
|
|
string listTagName = null;
|
|
|
|
if (Tag != "-") {
|
|
listTagName = string.IsNullOrEmpty(Tag) ? "ul" : Tag;
|
|
}
|
|
|
|
var listTag = String.IsNullOrEmpty(listTagName) ? null : GetTagBuilder(listTagName, Id, Classes, Attributes);
|
|
|
|
string itemTagName = null;
|
|
if (ItemTag != "-") {
|
|
itemTagName = string.IsNullOrEmpty(ItemTag) ? "li" : ItemTag;
|
|
}
|
|
|
|
if (listTag != null) {
|
|
Output.Write(listTag.ToString(TagRenderMode.StartTag));
|
|
}
|
|
|
|
var itemTags = new List<TagBuilder>();
|
|
var itemOutputs = new List<string>();
|
|
|
|
// give the item shape the possibility to alter its container tag
|
|
var index = 0;
|
|
foreach (var item in items) {
|
|
|
|
var itemTag = String.IsNullOrEmpty(itemTagName) ? null : GetTagBuilder(itemTagName, null, ItemClasses, ItemAttributes);
|
|
|
|
if (item is IShape) {
|
|
item.Tag = itemTag;
|
|
}
|
|
|
|
var itemOutput = Display(item).ToHtmlString();
|
|
|
|
if (!String.IsNullOrWhiteSpace(itemOutput)) {
|
|
itemTags.Add(itemTag);
|
|
itemOutputs.Add(itemOutput);
|
|
}
|
|
else {
|
|
count--;
|
|
}
|
|
|
|
++index;
|
|
}
|
|
|
|
index = 0;
|
|
foreach(var itemOutput in itemOutputs) {
|
|
var itemTag = itemTags[index];
|
|
|
|
if (itemTag != null) {
|
|
if (index == 0)
|
|
itemTag.AddCssClass("first");
|
|
if (index == count - 1)
|
|
itemTag.AddCssClass("last");
|
|
Output.Write(itemTag.ToString(TagRenderMode.StartTag));
|
|
}
|
|
|
|
Output.Write(itemOutput);
|
|
|
|
if (itemTag != null) {
|
|
Output.WriteLine(itemTag.ToString(TagRenderMode.EndTag));
|
|
}
|
|
|
|
++index;
|
|
}
|
|
|
|
if (listTag != null) {
|
|
Output.WriteLine(listTag.ToString(TagRenderMode.EndTag));
|
|
}
|
|
}
|
|
|
|
[Shape]
|
|
public IHtmlString PlaceChildContent(dynamic Source) {
|
|
return Source.Metadata.ChildContent;
|
|
}
|
|
|
|
[Shape]
|
|
public void Partial(HtmlHelper Html, TextWriter Output, string TemplateName, object Model, string Prefix) {
|
|
RenderInternal(Html, Output, TemplateName, Model, Prefix);
|
|
}
|
|
|
|
[Shape]
|
|
public void DisplayTemplate(HtmlHelper Html, TextWriter Output, string TemplateName, object Model, string Prefix) {
|
|
RenderInternal(Html, Output, "DisplayTemplates/" + TemplateName, Model, Prefix);
|
|
}
|
|
|
|
[Shape]
|
|
public void EditorTemplate(HtmlHelper Html, TextWriter Output, string TemplateName, object Model, string Prefix) {
|
|
RenderInternal(Html, Output, "EditorTemplates/" + TemplateName, Model, Prefix);
|
|
}
|
|
|
|
[Shape]
|
|
public void DefinitionTemplate(HtmlHelper Html, TextWriter Output, string TemplateName, object Model, string Prefix) {
|
|
RenderInternal(Html, Output, "DefinitionTemplates/" + TemplateName, Model, Prefix);
|
|
}
|
|
|
|
static void RenderInternal(HtmlHelper Html, TextWriter Output, string TemplateName, object Model, string Prefix) {
|
|
var adjustedViewData = new ViewDataDictionary(Html.ViewDataContainer.ViewData) {
|
|
Model = DetermineModel(Html, Model),
|
|
TemplateInfo = new TemplateInfo {
|
|
HtmlFieldPrefix = DeterminePrefix(Html, Prefix)
|
|
}
|
|
};
|
|
var adjustedViewContext = new ViewContext(Html.ViewContext, Html.ViewContext.View, adjustedViewData, Html.ViewContext.TempData, Output);
|
|
var adjustedHtml = new HtmlHelper(adjustedViewContext, new ViewDataContainer(adjustedViewData));
|
|
adjustedHtml.RenderPartial(TemplateName);
|
|
}
|
|
|
|
static object DetermineModel(HtmlHelper Html, object Model) {
|
|
bool isNull = ((dynamic)Model) == null;
|
|
return isNull ? Html.ViewData.Model : Model;
|
|
}
|
|
|
|
static string DeterminePrefix(HtmlHelper Html, string Prefix) {
|
|
var actualPrefix = string.IsNullOrEmpty(Prefix)
|
|
? Html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix
|
|
: Html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(Prefix);
|
|
return actualPrefix;
|
|
}
|
|
|
|
private class ViewDataContainer : IViewDataContainer {
|
|
public ViewDataContainer(ViewDataDictionary viewData) { ViewData = viewData; }
|
|
public ViewDataDictionary ViewData { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encodes dashed and dots so that they don't conflict in filenames
|
|
/// </summary>
|
|
/// <param name="alternateElement"></param>
|
|
/// <returns></returns>
|
|
private string EncodeAlternateElement(string alternateElement) {
|
|
return alternateElement.Replace("-", "__").Replace(".", "_");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encode a value if it's a string, or render it if it's a Shape
|
|
/// </summary>
|
|
private IHtmlString EncodeOrDisplay(dynamic Value, dynamic Display, HtmlHelper Html) {
|
|
if (Value is IHtmlString) {
|
|
return Value;
|
|
}
|
|
|
|
if (Value is IShape) {
|
|
return Display(Value).ToString();
|
|
}
|
|
|
|
return Html.Raw(Html.Encode(Value.ToString()));
|
|
}
|
|
}
|
|
}
|