Orchard.Layouts: Upgrading YamlDotNet to 5.3.0 and dynamic Snippet Manifest parsing replaced with static typing (#8149)

This commit is contained in:
Lombiq
2018-12-17 21:30:26 +01:00
parent 998946ba00
commit bb8e109582
6 changed files with 85 additions and 91 deletions

View File

@@ -1,6 +1,5 @@
using Orchard.Environment.Extensions;
using Orchard.Layouts.Framework.Elements;
using Orchard.Localization;
namespace Orchard.Layouts.Elements {
[OrchardFeature("Orchard.Layouts.Snippets")]

View File

@@ -1,11 +1,19 @@
using System.Collections.Generic;
using Orchard.Localization;
namespace Orchard.Layouts.Models {
public class SnippetDescriptor {
public string Category { get; set; }
public LocalizedString Description { get; set; }
public LocalizedString DisplayName { get; set; }
public string ToolboxIcon { get; set; }
public SnippetDescriptor() {
Fields = new List<SnippetFieldDescriptor>();
}
public IList<SnippetFieldDescriptor> Fields { get; set; }
}
}

View File

@@ -6,5 +6,7 @@ namespace Orchard.Layouts.Models {
public string Name { get; set; }
public LocalizedString DisplayName { get; set; }
public LocalizedString Description { get; set; }
public bool IsValid => !(string.IsNullOrEmpty(Name) || string.IsNullOrEmpty(Type));
}
}

View File

@@ -108,12 +108,8 @@
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Xml.Linq" />
<Reference Include="YamlDotNet, Version=3.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\YamlDotNet.3.8.0\lib\net35\YamlDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="YamlDotNet.Dynamic, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\YamlDotNet.Dynamic.3.2.3\lib\net40\YamlDotNet.Dynamic.dll</HintPath>
<Reference Include="YamlDotNet, Version=5.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\YamlDotNet.5.3.0\lib\net45\YamlDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.Environment;
@@ -21,7 +20,7 @@ using Orchard.Localization;
using Orchard.Themes.Services;
using Orchard.Tokens;
using Orchard.Utility.Extensions;
using YamlDotNet.Dynamic;
using YamlDotNet.RepresentationModel;
namespace Orchard.Layouts.Providers {
[OrchardFeature("Orchard.Layouts.Snippets")]
@@ -36,6 +35,7 @@ namespace Orchard.Layouts.Providers {
private readonly Work<ITokenizer> _tokenizer;
private readonly IWorkContextAccessor _wca;
public SnippetElementHarvester(
IWorkContextAccessor workContextAccessor,
Work<IShapeFactory> shapeFactory,
@@ -56,32 +56,35 @@ namespace Orchard.Layouts.Providers {
_wca = workContextAccessor;
}
public IEnumerable<ElementDescriptor> HarvestElements(HarvestElementsContext context) {
var currentThemeName = _siteThemeService.Value.GetCurrentThemeName();
var shapeTable = _shapeTableLocator.Value.Lookup(currentThemeName);
var shapeDescriptors = shapeTable.Bindings.Where(x => !String.Equals(x.Key, "Elements_Snippet", StringComparison.OrdinalIgnoreCase) && x.Key.EndsWith(SnippetShapeSuffix, StringComparison.OrdinalIgnoreCase)).ToDictionary(x => x.Key, x => x.Value.ShapeDescriptor);
var shapeDescriptors = shapeTable.Bindings
.Where(x => !string.Equals(x.Key, "Elements_Snippet", StringComparison.OrdinalIgnoreCase) &&
x.Key.EndsWith(SnippetShapeSuffix, StringComparison.OrdinalIgnoreCase))
.ToDictionary(x => x.Key, x => x.Value.ShapeDescriptor);
var elementType = typeof(Snippet);
var snippetElement = (Snippet)_elementFactory.Value.Activate(elementType);
foreach (var shapeDescriptor in shapeDescriptors) {
var snippetManifest = ParseSnippetManifest(shapeDescriptor.Value.BindingSource);
var snippetDescriptor = ParseSnippetManifest(shapeDescriptor.Value, snippetElement);
if (snippetDescriptor == null) continue;
var shapeType = shapeDescriptor.Value.ShapeType;
var elementName = GetDisplayName(snippetManifest, shapeDescriptor.Value.BindingSource);
var toolboxIcon = GetToolboxIcon(snippetManifest, snippetElement);
var description = GetDescription(snippetManifest, shapeType);
var category = GetCategory(snippetManifest, snippetElement);
var closureDescriptor = shapeDescriptor;
var snippetDescriptor = ParseSnippetDescriptor(snippetManifest);
yield return new ElementDescriptor(elementType, shapeType, new LocalizedString(elementName), description, category) {
Displaying = displayContext => Displaying(displayContext, closureDescriptor.Value, snippetDescriptor),
ToolboxIcon = toolboxIcon,
EnableEditorDialog = snippetDescriptor != null || HasSnippetFields(shapeDescriptor.Value),
yield return new ElementDescriptor(elementType, shapeType, snippetDescriptor.DisplayName, snippetDescriptor.Description, snippetDescriptor.Category) {
Displaying = displayContext => Displaying(displayContext, shapeDescriptor.Value, snippetDescriptor),
ToolboxIcon = snippetDescriptor.ToolboxIcon,
EnableEditorDialog = snippetDescriptor.Fields.Any(),
Editor = ctx => Editor(snippetDescriptor ?? DescribeSnippet(shapeType, snippetElement), ctx),
UpdateEditor = ctx => UpdateEditor(snippetDescriptor ?? DescribeSnippet(shapeType, snippetElement), ctx)
};
}
}
private void Editor(SnippetDescriptor descriptor, ElementEditorContext context) {
UpdateEditor(descriptor, context);
}
@@ -138,73 +141,74 @@ namespace Orchard.Layouts.Providers {
context.ElementShape.Snippet = shape;
}
private dynamic ParseSnippetManifest(string bindingSource) {
var physicalSourcePath = _wca.GetContext().HttpContext.Server.MapPath(bindingSource);
var paramsFileName = Path.Combine(Path.GetDirectoryName(physicalSourcePath) ?? "", Path.GetFileNameWithoutExtension(physicalSourcePath) + ".txt");
private SnippetDescriptor ParseSnippetManifest(ShapeDescriptor shape, Snippet snippetElement) {
// Initializing and checking access to the Snippet manifest file.
var physicalSourcePath = _wca.GetContext().HttpContext.Server.MapPath(shape.BindingSource);
var fullPath = Path.Combine(
Path.GetDirectoryName(physicalSourcePath) ?? "",
Path.GetFileNameWithoutExtension(physicalSourcePath) + ".txt");
if (!File.Exists(paramsFileName))
return null;
if (!File.Exists(fullPath)) return null;
var yaml = File.ReadAllText(paramsFileName);
var snippetConfig = Deserialize(yaml);
// Reading and parsing the manifest.
var manifestText = File.ReadAllText(fullPath);
var yaml = new YamlStream();
return snippetConfig;
}
yaml.Load(new StringReader(manifestText));
var manifest = (YamlMappingNode)yaml.Documents?[0]?.RootNode;
private string GetDisplayName(dynamic snippetManifest, string bindingSource) {
if (snippetManifest != null && (string)snippetManifest.DisplayName != null) {
return (string)snippetManifest.DisplayName;
if (!manifest?.Children?.Any() ?? true) return null;
// Extracting the main properties of the manifest.
var category = GetYamlMappingNodeChildByName(manifest, nameof(SnippetDescriptor.Category))?.ToString();
if (string.IsNullOrEmpty(category)) category = snippetElement.Category;
var description = GetYamlMappingNodeChildByName(manifest, nameof(SnippetDescriptor.Description))?.ToString();
if (string.IsNullOrEmpty(description)) description = $"An element that renders the {shape.ShapeType} shape.";
var displayName = GetYamlMappingNodeChildByName(manifest, nameof(SnippetDescriptor.DisplayName))?.ToString();
if (string.IsNullOrEmpty(displayName)) {
var fileName = Path.GetFileNameWithoutExtension(shape.BindingSource) ?? "";
var lastIndex = fileName.IndexOf(SnippetShapeSuffix, StringComparison.OrdinalIgnoreCase);
displayName = fileName.Substring(0, lastIndex).CamelFriendly();
}
var fileName = Path.GetFileNameWithoutExtension(bindingSource) ?? "";
var lastIndex = fileName.IndexOf(SnippetShapeSuffix, StringComparison.OrdinalIgnoreCase);
return fileName.Substring(0, lastIndex).CamelFriendly();
}
var toolboxIcon = GetYamlMappingNodeChildByName(manifest, nameof(SnippetDescriptor.ToolboxIcon))?.ToString();
if (string.IsNullOrEmpty(toolboxIcon)) toolboxIcon = snippetElement.ToolboxIcon;
private string GetToolboxIcon(dynamic snippetManifest, Snippet snippetElement) {
if (snippetManifest != null && (string)snippetManifest.ToolboxIcon != null) {
return Regex.Unescape((string)snippetManifest.ToolboxIcon);
}
var descriptor = new SnippetDescriptor {
Category = category,
Description = new LocalizedString(description),
DisplayName = new LocalizedString(displayName),
ToolboxIcon = toolboxIcon
};
return snippetElement.ToolboxIcon;
}
// Extracting the editor fields from the manifest.
var fields = (YamlSequenceNode)GetYamlMappingNodeChildByName(manifest, nameof(SnippetDescriptor.Fields));
private LocalizedString GetDescription(dynamic snippetManifest, string shapeType) {
if (snippetManifest != null && (string)snippetManifest.Description != null) {
return new LocalizedString((string)snippetManifest.Description);
}
if (fields?.Any() ?? false) {
foreach (YamlMappingNode field in fields) {
descriptor.Fields.Add(new SnippetFieldDescriptor {
Description = new LocalizedString(GetYamlMappingNodeChildByName(field, nameof(SnippetFieldDescriptor.Description))?.ToString() ?? ""),
DisplayName = new LocalizedString(GetYamlMappingNodeChildByName(field, nameof(SnippetFieldDescriptor.DisplayName))?.ToString() ?? ""),
Name = GetYamlMappingNodeChildByName(field, nameof(SnippetFieldDescriptor.Name))?.ToString(),
Type = GetYamlMappingNodeChildByName(field, nameof(SnippetFieldDescriptor.Type))?.ToString()
});
}
return new LocalizedString(String.Format("An element that renders the {0} shape.", shapeType));
}
private string GetCategory(dynamic snippetManifest, Snippet snippetElement) {
if (snippetManifest != null && (string)snippetManifest.Category != null) {
return (string)snippetManifest.Category;
}
return snippetElement.Category;
}
private SnippetDescriptor ParseSnippetDescriptor(dynamic snippetManifest) {
if(snippetManifest == null || snippetManifest.Fields.Count == 0) {
return null;
}
var fieldsConfig = snippetManifest.Fields.Children;
var descriptor = new SnippetDescriptor();
foreach (var fieldConfig in fieldsConfig) {
descriptor.Fields.Add(new SnippetFieldDescriptor {
Name = (string)fieldConfig.Name,
Type = (string)fieldConfig.Type,
DisplayName = new LocalizedString((string)fieldConfig.DisplayName),
Description = new LocalizedString((string)fieldConfig.Description)
});
descriptor.Fields = descriptor.Fields.Where(field => field.IsValid).ToList();
}
return descriptor;
}
private YamlNode GetYamlMappingNodeChildByName(YamlMappingNode parent, string childNodeName) {
YamlNode yamlNode = null;
parent?.Children?.TryGetValue(new YamlScalarNode(childNodeName), out yamlNode);
return yamlNode;
}
private SnippetDescriptor DescribeSnippet(string shapeType, Snippet element) {
var shape = (dynamic)_shapeFactory.Value.Create(shapeType);
shape.Element = element;
@@ -215,7 +219,8 @@ namespace Orchard.Layouts.Providers {
// Execute the shape and intercept calls to the Html.SnippetField method.
var descriptor = new SnippetDescriptor();
shape.DescriptorRegistrationCallback = (Action<SnippetFieldDescriptor>)(fieldDescriptor => {
var existingDescriptor = descriptor.Fields.SingleOrDefault(x => x.Name == fieldDescriptor.Name); // Not using Dictionary, as that will break rendering the view for some obscure reason.
// Not using Dictionary, as that will break rendering the view for some obscure reason.
var existingDescriptor = descriptor.Fields.SingleOrDefault(x => x.Name == fieldDescriptor.Name);
if (existingDescriptor == null)
descriptor.Fields.Add(fieldDescriptor);
@@ -231,20 +236,5 @@ namespace Orchard.Layouts.Providers {
shape.SnippetDescriptor = descriptor;
return descriptor;
}
private bool HasSnippetFields(ShapeDescriptor shapeDescriptor) {
var bindingSource = shapeDescriptor.BindingSource;
var localFileName = _wca.GetContext().HttpContext.Server.MapPath(bindingSource);
if (!File.Exists(localFileName))
return false;
var markup = File.ReadAllText(localFileName);
return markup.Contains("@Html.SnippetField");
}
private dynamic Deserialize(string yaml) {
return new DynamicYaml(yaml);
}
}
}

View File

@@ -8,6 +8,5 @@
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net452" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" />
<package id="YamlDotNet" version="3.8.0" targetFramework="net452" />
<package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net452" />
<package id="YamlDotNet" version="5.3.0" targetFramework="net452" />
</packages>