From 652981b835777bff26856e5012890ecfc56a6b6e Mon Sep 17 00:00:00 2001 From: David Stone Date: Mon, 11 Apr 2016 23:42:56 +0100 Subject: [PATCH 1/3] Fixing typos Fixes #6724 --- README.md | 2 +- .../DefinitionTemplates/NumericFieldSettings.cshtml | 4 ++-- .../ImportActions/ExecuteRecipe.cshtml | 4 ++-- .../EditorTemplates/Fields/TaxonomyField.cshtml | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 49b30df48..3e61a0bcd 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ There are many ways you can [contribute to Orchard](http://orchardproject.net/co * [Participate in our gitter.im chatroom](https://gitter.im/OrchardCMS/Orchard) * [Participate in forum discussions](http://orchard.codeplex.com/discussions) * [Submit a pull request](http://docs.orchardproject.net/Documentation/Contributing-patches) -* [Translate Orchard](http://orchardproject.net/localize) +* [Translate Orchard](http://orchardproject.net/localization) * [Contribute modules and themes to our gallery](http://gallery.orchardproject.net/) * [Send us feedback](mailto:ofeedbk@microsoft.com) diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Views/DefinitionTemplates/NumericFieldSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Fields/Views/DefinitionTemplates/NumericFieldSettings.cshtml index 09cedb61a..e98d3137a 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Views/DefinitionTemplates/NumericFieldSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Fields/Views/DefinitionTemplates/NumericFieldSettings.cshtml @@ -43,7 +43,7 @@
@Html.TextBoxFor(m => m.DefaultValue, new { @class = "text large tokenized ui-autocomplete-input" }) - @T("The default value for the field. It must be a number, and if not it will not shown. Make sure to set the Scale property if the value is not an integer. You can use tokens in this field. (optional)") + @T("The default value for the field. It must be a number, and if not it will not be shown. Make sure to set the Scale property if the value is not an integer. You can use tokens in this field. (optional)") @Html.ValidationMessageFor(m => m.DefaultValue)
- \ No newline at end of file + diff --git a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ExecuteRecipe.cshtml b/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ExecuteRecipe.cshtml index f4a8a4852..5033819e4 100644 --- a/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ExecuteRecipe.cshtml +++ b/src/Orchard.Web/Modules/Orchard.ImportExport/Views/EditorTemplates/ImportActions/ExecuteRecipe.cshtml @@ -29,7 +29,7 @@ } - @Html.Hint(T("Select wether you want to import the selected recipe into the current site or if you want to do a full site reset first.")) + @Html.Hint(T("Select whether you want to import the selected recipe into the current site or if you want to do a full site reset first."))
m.ResetSite), "true")" style="display: none;">
@T("This will delete your database tables. Please consider creating a backup first.")
@@ -63,4 +63,4 @@ stepIndex++; } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/EditorTemplates/Fields/TaxonomyField.cshtml b/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/EditorTemplates/Fields/TaxonomyField.cshtml index bdd8b2678..02d9d615b 100644 --- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/EditorTemplates/Fields/TaxonomyField.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Views/EditorTemplates/Fields/TaxonomyField.cshtml @@ -5,7 +5,7 @@ @{ Style.Include("admin-taxonomy.css"); - Script.Require("jQuery"); + Script.Require("jQuery"); Script.Include("~/Themes/TheAdmin/scripts/admin.js").AtFoot(); Script.Include("admin-taxonomy-expando.js").AtFoot(); @@ -15,12 +15,12 @@
class="required" }>@Model.DisplayName - +
@if (!String.IsNullOrWhiteSpace(Model.Settings.Hint)) { @Model.Settings.Hint } - +
    @foreach (var entry in Model.Terms) { var ti = termIndex; @@ -41,14 +41,14 @@ termIndex++; }
- + @if (Model.TaxonomyId == 0) { -

@T("Your haven't specified a taxonomy for {0}", Model.DisplayName)

+

@T("You haven't specified a taxonomy for {0}", Model.DisplayName)

}else if (!Model.Terms.Any() && AuthorizedFor(Orchard.Taxonomies.Permissions.CreateTerm)) {
@T("There are no terms defined for {0} yet.", Model.DisplayName) @T("Create some terms") -
+
}
@Html.HiddenFor(m => m.TaxonomyId) From 38bf36075a400a076f8238001ef11419f562c4e9 Mon Sep 17 00:00:00 2001 From: Hannan Azam Khan Date: Tue, 12 Apr 2016 04:08:08 +0500 Subject: [PATCH 2/3] Prevent double-click on Publish/Save Fixes #6686 --- src/Orchard.Web/Themes/TheAdmin/Scripts/admin.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Orchard.Web/Themes/TheAdmin/Scripts/admin.js b/src/Orchard.Web/Themes/TheAdmin/Scripts/admin.js index 53519f096..8fcae3ab7 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Scripts/admin.js +++ b/src/Orchard.Web/Themes/TheAdmin/Scripts/admin.js @@ -81,6 +81,16 @@ $("input[type=checkbox]:not(:disabled)").prop('checked', $(this).prop("checked")) }); + //Prevent double-click on buttons of type "submit" + $("form button[type='submit'], form input[type='submit']").click(function (e) { + var form = $(this).closest("form")[0]; + if (typeof(form.formSubmitted) != "undefined") { + e.preventDefault(); + return; + } + form.formSubmitted = true; + }); + // Handle keypress events in bulk action fieldsets that are part of a single form. // This will make sure the expected action executes when pressing "enter" on a text field. $("form .bulk-actions").on("keypress", "input[type='text']", function (e) { From a5605b7fbdf108881f75fc935b6778aad2f50a47 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 12 Apr 2016 01:08:44 +0200 Subject: [PATCH 3/3] Added support for snippet manifests Fixes #6672 --- .../Orchard.Framework.Tests.csproj | 1 + src/Orchard.Tests/Services/YamlParserTests.cs | 70 +++++++++++++++++++ .../Providers/SnippetElementHarvester.cs | 54 +++++++++++--- src/Orchard.Web/Web.config | 6 +- src/Orchard/Orchard.Framework.csproj | 10 +++ src/Orchard/Services/IYamlParser.cs | 21 ++++++ src/Orchard/Services/YamlParser.cs | 19 +++++ src/Orchard/packages.config | 2 + 8 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 src/Orchard.Tests/Services/YamlParserTests.cs create mode 100644 src/Orchard/Services/IYamlParser.cs create mode 100644 src/Orchard/Services/YamlParser.cs diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index c433d1879..7b3df98cf 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -320,6 +320,7 @@ + diff --git a/src/Orchard.Tests/Services/YamlParserTests.cs b/src/Orchard.Tests/Services/YamlParserTests.cs new file mode 100644 index 000000000..19ece43fa --- /dev/null +++ b/src/Orchard.Tests/Services/YamlParserTests.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using Orchard.Services; + +namespace Orchard.Tests.Services { + + [TestFixture] + public class YamlParserTests { + + [Test] + public void ShouldConvertYamlToWellknowType() { + var parser = new YamlParser(); + var yaml = SampleYamlDocument; + + var order = parser.Deserialize(yaml); + + Assert.AreEqual("Nikola", order.Customer.FirstName); + Assert.AreEqual(2, order.Items.Count); + } + + [Test] + public void ShouldConvertYamlToDynamic() + { + var parser = new YamlParser(); + var yaml = SampleYamlDocument; + + var order = parser.Deserialize(yaml); + + Assert.AreEqual("Nikola", (string)order.Customer.FirstName); + Assert.AreEqual(2, (int)order.Items.Count); + } + + public class Order { + public DateTime Date { get; set; } + public Customer Customer { get; set; } + public IList Items { get; set; } + } + + public class OrderItem { + public string Product { get; set; } + public int Quantity { get; set; } + public decimal Price { get; set; } + } + + public class Customer { + public string FirstName { get; set; } + public string LastName { get; set; } + + } + + private const string SampleYamlDocument = +@" +Date: 1916-04-01 +Customer: + FirstName: Nikola + LastName: Tesla +Items: + - Product: Bulb + Quantity: 1 + Price: 1.46 + + - Product: Wire + Quantity: 1 + Price: 0.32 +"; + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Providers/SnippetElementHarvester.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Providers/SnippetElementHarvester.cs index 8cab0b828..1333ef9c5 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Providers/SnippetElementHarvester.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Providers/SnippetElementHarvester.cs @@ -17,13 +17,14 @@ using Orchard.Layouts.Services; using Orchard.Layouts.Shapes; using Orchard.Layouts.ViewModels; using Orchard.Localization; +using Orchard.Services; using Orchard.Themes.Services; using Orchard.Tokens; using Orchard.Utility.Extensions; namespace Orchard.Layouts.Providers { [OrchardFeature("Orchard.Layouts.Snippets")] - public class SnippetElementHarvester : Component, IElementHarvester { + public class SnippetElementHarvester : IElementHarvester { private const string SnippetShapeSuffix = "Snippet"; private readonly Work _shapeFactory; private readonly Work _siteThemeService; @@ -33,6 +34,7 @@ namespace Orchard.Layouts.Providers { private readonly Work _currentThemeShapeBindingResolver; private readonly Work _tokenizer; private readonly IWorkContextAccessor _wca; + private readonly Work _yamlParser; public SnippetElementHarvester( IWorkContextAccessor workContextAccessor, @@ -42,7 +44,8 @@ namespace Orchard.Layouts.Providers { Work elementFactory, Work shapeDisplay, Work tokenizer, - Work currentThemeShapeBindingResolver) { + Work currentThemeShapeBindingResolver, + Work yamlParser) { _shapeFactory = shapeFactory; _siteThemeService = siteThemeService; @@ -51,6 +54,7 @@ namespace Orchard.Layouts.Providers { _shapeDisplay = shapeDisplay; _tokenizer = tokenizer; _currentThemeShapeBindingResolver = currentThemeShapeBindingResolver; + _yamlParser = yamlParser; _wca = workContextAccessor; } @@ -65,12 +69,13 @@ namespace Orchard.Layouts.Providers { var shapeType = shapeDescriptor.Value.ShapeType; var elementName = GetDisplayName(shapeDescriptor.Value.BindingSource); var closureDescriptor = shapeDescriptor; - yield return new ElementDescriptor(elementType, shapeType, T(elementName), T("An element that renders the {0} shape.", shapeType), snippetElement.Category) { - Displaying = displayContext => Displaying(displayContext, closureDescriptor.Value), + var snippetDescriptor = ParseSnippetDescriptor(shapeDescriptor.Value.BindingSource); + yield return new ElementDescriptor(elementType, shapeType, new LocalizedString(elementName), new LocalizedString(String.Format("An element that renders the {0} shape.", shapeType)), snippetElement.Category) { + Displaying = displayContext => Displaying(displayContext, closureDescriptor.Value, snippetDescriptor), ToolboxIcon = "\uf10c", - EnableEditorDialog = HasSnippetFields(shapeDescriptor.Value), - Editor = ctx => Editor(DescribeSnippet(shapeType, snippetElement), ctx), - UpdateEditor = ctx => UpdateEditor(DescribeSnippet(shapeType, snippetElement), ctx) + EnableEditorDialog = snippetDescriptor != null || HasSnippetFields(shapeDescriptor.Value), + Editor = ctx => Editor(snippetDescriptor ?? DescribeSnippet(shapeType, snippetElement), ctx), + UpdateEditor = ctx => UpdateEditor(snippetDescriptor ?? DescribeSnippet(shapeType, snippetElement), ctx) }; } } @@ -113,22 +118,53 @@ namespace Orchard.Layouts.Providers { context.EditorResult.Add(snippetEditorShape); } - private void Displaying(ElementDisplayingContext context, ShapeDescriptor shapeDescriptor) { + private void Displaying(ElementDisplayingContext context, ShapeDescriptor shapeDescriptor, SnippetDescriptor snippetDescriptor) { var shapeType = shapeDescriptor.ShapeType; var shape = (dynamic)_shapeFactory.Value.Create(shapeType); shape.Element = context.Element; + if (snippetDescriptor != null) { + foreach (var fieldDescriptor in snippetDescriptor.Fields) { + var value = context.Element.Data.Get(fieldDescriptor.Name); + shape.Properties[fieldDescriptor.Name] = value; + } + } + ElementShapes.AddTokenizers(shape, _tokenizer.Value); context.ElementShape.Snippet = shape; } private string GetDisplayName(string bindingSource) { - var fileName = Path.GetFileNameWithoutExtension(bindingSource); + var fileName = Path.GetFileNameWithoutExtension(bindingSource) ?? ""; var lastIndex = fileName.IndexOf(SnippetShapeSuffix, StringComparison.OrdinalIgnoreCase); return fileName.Substring(0, lastIndex).CamelFriendly(); } + private SnippetDescriptor ParseSnippetDescriptor(string bindingSource) { + var physicalSourcePath = _wca.GetContext().HttpContext.Server.MapPath(bindingSource); + var paramsFileName = Path.Combine(Path.GetDirectoryName(physicalSourcePath) ?? "", Path.GetFileNameWithoutExtension(physicalSourcePath) + ".txt"); + + if (!File.Exists(paramsFileName)) + return null; + + var yaml = File.ReadAllText(paramsFileName); + var snippetConfig = _yamlParser.Value.Deserialize(yaml); + var fieldsConfig = snippetConfig.Fields != null ? snippetConfig.Fields.Children : new dynamic[0]; + 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) + }); + } + + return descriptor; + } + private SnippetDescriptor DescribeSnippet(string shapeType, Snippet element) { var shape = (dynamic)_shapeFactory.Value.Create(shapeType); shape.Element = element; diff --git a/src/Orchard.Web/Web.config b/src/Orchard.Web/Web.config index 2106f9674..cc10d9dab 100644 --- a/src/Orchard.Web/Web.config +++ b/src/Orchard.Web/Web.config @@ -225,7 +225,11 @@ - + + + + + diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 5cb38eb1d..be276337b 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -166,6 +166,14 @@ + + ..\packages\YamlDotNet.3.8.0\lib\net35\YamlDotNet.dll + True + + + ..\packages\YamlDotNet.Dynamic.3.2.3\lib\net40\YamlDotNet.Dynamic.dll + True + @@ -419,7 +427,9 @@ + + diff --git a/src/Orchard/Services/IYamlParser.cs b/src/Orchard/Services/IYamlParser.cs new file mode 100644 index 000000000..cf9ea5f2c --- /dev/null +++ b/src/Orchard/Services/IYamlParser.cs @@ -0,0 +1,21 @@ +namespace Orchard.Services { + /// + /// Provides methods to deserialize objects from YAML documents. + /// + public interface IYamlParser : IDependency { + /// + /// Deserializes a YAML document to a dynamic object. + /// + /// The YAML document to deserialize. + /// The deserialized object. + dynamic Deserialize(string yaml); + + /// + /// Deserializes a YAML document to a specific object. + /// + /// The type of the object to deserialize. + /// The YAML document to deserialize. + /// The deserialized object. + T Deserialize(string yaml); + } +} diff --git a/src/Orchard/Services/YamlParser.cs b/src/Orchard/Services/YamlParser.cs new file mode 100644 index 000000000..b783aad5c --- /dev/null +++ b/src/Orchard/Services/YamlParser.cs @@ -0,0 +1,19 @@ +using System.IO; +using YamlDotNet.Dynamic; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Orchard.Services { + public class YamlParser : IYamlParser { + public dynamic Deserialize(string yaml) { + return new DynamicYaml(yaml); + } + + public T Deserialize(string yaml) { + var deserializer = new Deserializer(namingConvention: new PascalCaseNamingConvention(), ignoreUnmatched: true); + using (var reader = new StringReader(yaml)) { + return deserializer.Deserialize(reader); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/packages.config b/src/Orchard/packages.config index 2583dd2b6..f5f57f9e4 100644 --- a/src/Orchard/packages.config +++ b/src/Orchard/packages.config @@ -17,4 +17,6 @@ + + \ No newline at end of file