diff --git a/.github/README.md b/.github/README.md index 3e6ec3c7..227c46db 100644 --- a/.github/README.md +++ b/.github/README.md @@ -24,6 +24,7 @@ Let's take a look inside... ##### Property Editors - [Bytes](../docs/editors/bytes.md) - a read-only label to display file sizes in relative bytes. +- [Code Editor](../docs/editors/code-editor.md) - a code snippet editor, _(using the ACE library that is bundled with Umbraco)._ - [Content Blocks](../docs/editors/content-blocks.md) - a block editor, configurable using element types. - [Data List](../docs/editors/data-list.md) - an editor that combines a custom data source with a custom list editor. - [Icon Picker](../docs/editors/icon-picker.md) - an editor to select an icon (from the Umbraco icon library). diff --git a/.github/ROADMAP.md b/.github/ROADMAP.md index 386e080d..7ae3b38e 100644 --- a/.github/ROADMAP.md +++ b/.github/ROADMAP.md @@ -35,10 +35,15 @@ Property Editors are: ### v1.3 -- Code Editor _(using ACE bundled with Umbraco)_ +- [Code Editor](../docs/editors/code-editor.md) _(using ACE bundled with Umbraco)_ - Data List: Preview _(a real time preview of the configured Data Source and List Editor)_ +- Data List: Buttons _(list editor, similar to what folk see used in Umbraco Uno)_ - Data List: Tags _(list editor, visually similar to Umbraco Tags editor)_ +### v1.4 + +- 🤫 + ## v2 diff --git a/VERSION b/VERSION index cb174d58..589268e6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.1 \ No newline at end of file +1.3.0 \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 259cb34e..8c44072d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,6 +13,7 @@ Here is the documentation for the Contentment property-editors... - [Bytes](../docs/editors/bytes.md) - a read-only label to display file sizes in relative bytes. +- [Code Editor](../docs/editors/code-editor.md) - a code snippet editor, _(using the ACE library that is bundled with Umbraco)._ - [Content Blocks](../docs/editors/content-blocks.md) - a block editor, configurable using element types. - [Data List](../docs/editors/data-list.md) - an editor that combines a custom data source with a custom list editor. - [Icon Picker](../docs/editors/icon-picker.md) - an editor to select an icon (from the Umbraco icon library). diff --git a/docs/editors/code-editor--configuration-editor.png b/docs/editors/code-editor--configuration-editor.png new file mode 100644 index 00000000..57d3c351 Binary files /dev/null and b/docs/editors/code-editor--configuration-editor.png differ diff --git a/docs/editors/code-editor--property-editor-01.png b/docs/editors/code-editor--property-editor-01.png new file mode 100644 index 00000000..57d3c351 Binary files /dev/null and b/docs/editors/code-editor--property-editor-01.png differ diff --git a/docs/editors/code-editor.md b/docs/editors/code-editor.md new file mode 100644 index 00000000..8f8a6f9c --- /dev/null +++ b/docs/editors/code-editor.md @@ -0,0 +1,72 @@ + + +## Contentment for Umbraco + +### Code Editor + +Code Editor is a property-editor that is used to enter code snippets (as content), makes use of [AWS Cloud 9's Ace editor](https://ace.c9.io/) library that is distributed with Umbraco. + + +### How to configure the editor? + +In your new Data Type, selected the "[Contentment] Code Editor" option. You will see the following configuration fields. + +![Configuration Editor for Code Editor](code-editor--configuration-editor.png) + +The first field is **Language mode**, this is used to select the programming language mode, for code syntax highlighting. The default mode is "Razor", meaning that you can use a combination of HTML, CSS, JavaScript and Razor syntax. + +The next field is **Theme**, which is used to set the visual appearance of the code editor. The default _(and only)_ theme is "Chrome". + +> **Please note,** by default, Umbraco ships a streamlined set of programming language modes and themes. +> +> If you would like to add more modes and themes, you can do this by [downloading the latest pre-packaged version of the Ace editor](https://github.com/ajaxorg/ace-builds/releases) and copy any of the `mode-*` or `theme-*` files from the `src-min-noconflict` folder over to the `~/umbraco/lib/ace-builds/src-min-noconflict/` folder in you Umbraco installation. +> +> Once you've done this, you can reload the Data Type screen, and the new programming language modes and themes will appear in the dropdown options for the fields above. + + +The **Font size** field is used to set the font size, the value must be a [valid CSS font-size value](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size), e.g. `14px`, `80%`, `0.8em`, etc. The default size is "`small`". + +The **Word wrapping** option can enable the code editor to wrap the text around to the following line. + +The next two fields, **Minimum lines** and **Maximum lines**, are used to set the default height size of the code editor. If you would like the height to auto-scale forever, then set the maximum number to something ridiculously high. If left empty, the height of the code editor will remain at a fixed height and not auto-scale. + + +### How to use the editor? + +Once you have added the configured Data Type to your Document Type, the Code Editor editor will be displayed on the content page's property panel. + +![Code Editor property-editor](code-editor--property-editor-01.png) + + +### How to get the value? + +The value for the Code Editor is a `string`. + +Programmatically, you would access the value exactly the same as Umbraco's Textarea editor, [see Umbraco's documentation for code snippet examples](https://our.umbraco.com/Documentation/Getting-Started/Backoffice/Property-Editors/Built-in-Property-Editors/Textarea/#mvc-view-example). + +If you are wanting to display the code content as a pre-formatted code snippet, I would recommend using the `
` and `` tags.
+
+Using Umbraco's Models Builder...
+
+```cshtml
+@Html.Raw(Model.CodeEditor)
+```
+
+Without ModelsBuilder...
+
+Weakly-typed...
+
+```cshtml
+@Html.Raw(Model.Value("codeEditor"))
+```
+
+Strongly-typed...
+
+```cshtml
+@Html.Raw(Model.Value("codeEditor"))
+```
+
+For code syntax highlighting, the following JavaScript libraries are quite popular:
+
+- [Prism.js](https://prismjs.com/)
+- [highlight.js](https://highlightjs.org/)
diff --git a/docs/telemetry.md b/docs/telemetry.md
index 26f996e3..220cfdd4 100644
--- a/docs/telemetry.md
+++ b/docs/telemetry.md
@@ -15,8 +15,12 @@ Here is an example of the JSON data that is sent.
"dataType": "4E7D6B3A-F959-42E4-921E-081BC0E9E7EE",
"editorAlias": "DataList",
"umbracoId": "0403E47E-EFE7-4CF2-8E97-148681DAFC10",
- "umbracoVersion": "8.6.6",
- "contentmentVersion": "1.2.0",
+ "umbracoVersion": "8.6.8",
+ "contentmentVersion": "1.3.0",
+ "dataTypeConfig": {
+ "dataSource": "EnumDataListSource",
+ "listEditor": "CheckboxListDataListEditor",
+ }
}
```
diff --git a/src/Umbraco.Community.Contentment.sln b/src/Umbraco.Community.Contentment.sln
index b485ab86..d03ab422 100644
--- a/src/Umbraco.Community.Contentment.sln
+++ b/src/Umbraco.Community.Contentment.sln
@@ -41,6 +41,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Editors", "Editors", "{A5294B30-2ED5-4BBA-A2DE-A07103DAE78F}"
ProjectSection(SolutionItems) = preProject
..\docs\editors\bytes.md = ..\docs\editors\bytes.md
+ ..\docs\editors\code-editor.md = ..\docs\editors\code-editor.md
..\docs\editors\content-blocks.md = ..\docs\editors\content-blocks.md
..\docs\editors\data-list.md = ..\docs\editors\data-list.md
..\docs\editors\icon-picker.md = ..\docs\editors\icon-picker.md
diff --git a/src/Umbraco.Community.Contentment/Core/DictionaryExtensions.cs b/src/Umbraco.Community.Contentment/Core/DictionaryExtensions.cs
index ca9589fa..e9ffae3f 100644
--- a/src/Umbraco.Community.Contentment/Core/DictionaryExtensions.cs
+++ b/src/Umbraco.Community.Contentment/Core/DictionaryExtensions.cs
@@ -11,7 +11,7 @@ internal static class DictionaryExtensions
{
public static TValueOut GetValueAs(this IDictionary config, TKey key, TValueOut defaultValue = default)
{
- if (config.TryGetValue(key, out var tmp))
+ if (config.TryGetValue(key, out var tmp) == true)
{
if (tmp is TValueOut value)
{
@@ -19,7 +19,7 @@ public static TValueOut GetValueAs(this IDictionary();
- if (attempt.Success)
+ if (attempt.Success == true)
{
return attempt.Result;
}
@@ -30,7 +30,7 @@ public static TValueOut GetValueAs(this IDictionary(this IDictionary config, TKey key, out TValueOut value)
{
- if (config.TryGetValue(key, out var tmp1))
+ if (config.TryGetValue(key, out var tmp1) == true)
{
if (tmp1 is TValueOut tmp2)
{
@@ -39,7 +39,7 @@ public static bool TryGetValueAs(this IDictionary();
- if (attempt.Success)
+ if (attempt.Success == true)
{
value = attempt.Result;
return attempt.Success;
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Buttons/ButtonsDataListEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/Buttons/ButtonsDataListEditor.cs
new file mode 100644
index 00000000..4fe0c840
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Buttons/ButtonsDataListEditor.cs
@@ -0,0 +1,95 @@
+/* Copyright © 2020 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+using System.Collections.Generic;
+using Umbraco.Core;
+using Umbraco.Core.IO;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Community.Contentment.DataEditors
+{
+ public sealed class ButtonsDataListEditor : IDataListEditor
+ {
+ internal const string DataEditorViewPath = Constants.Internals.EditorsPathRoot + "buttons.html";
+
+ public string Name => "Buttons";
+
+ public string Description => "Select multiple values from a group of buttons.";
+
+ public string Icon => "icon-tab";
+
+ public IEnumerable Fields => new ConfigurationField[]
+ {
+ new ConfigurationField
+ {
+ Key = "defaultIcon",
+ Name = "Default icon",
+ Description = "Select an icon to be displayed as the default icon, (for when no icon is available).",
+ View = IOHelper.ResolveUrl("~/umbraco/views/propertyeditors/listview/icon.prevalues.html"),
+ },
+ new ConfigurationField
+ {
+ Key = "size",
+ Name = "Size",
+ Description = "Select the button size. By default this is set to 'medium'.",
+ View = IOHelper.ResolveUrl(RadioButtonListDataListEditor.DataEditorViewPath),
+ Config = new Dictionary
+ {
+ { Constants.Conventions.ConfigurationFieldAliases.Items, new[]
+ {
+ new DataListItem { Name = "Small", Value = "s" },
+ new DataListItem { Name = "Medium", Value = "m" },
+ new DataListItem { Name = "Large", Value = "l" },
+ }
+ },
+ { Constants.Conventions.ConfigurationFieldAliases.DefaultValue, "m" }
+ }
+ },
+ new ConfigurationField
+ {
+ Key = "labelStyle",
+ Name = "Label style",
+ Description = "Select the style of the button's label.",
+ View = IOHelper.ResolveUrl(RadioButtonListDataListEditor.DataEditorViewPath),
+ Config = new Dictionary
+ {
+ { Constants.Conventions.ConfigurationFieldAliases.Items, new[]
+ {
+ new DataListItem { Name = "Icon and Text", Value = "both", Description = "Displays both the item's icon and name." },
+ new DataListItem { Name = "Icon only", Value = "icon", Description = "Hides the item's name and only displays the icon." },
+ new DataListItem { Name = "Text only", Value = "text", Description = "Hides the item's icon and only displays the name." },
+ }
+ },
+ { Constants.Conventions.ConfigurationFieldAliases.DefaultValue, "both" },
+ { ShowDescriptionsConfigurationField.ShowDescriptions, Constants.Values.True },
+ }
+ },
+ new ConfigurationField
+ {
+ Key = "enableMultiple",
+ Name = "Multiple selection?",
+ Description = "Select to enable picking multiple items.",
+ View = "boolean",
+ },
+ };
+
+ public Dictionary DefaultValues => new Dictionary
+ {
+ { "defaultIcon", Core.Constants.Icons.DefaultIcon },
+ { "labelStyle", "both" },
+ };
+
+ public Dictionary DefaultConfig => default;
+
+ public bool HasMultipleValues(Dictionary config)
+ {
+ return config.TryGetValue("enableMultiple", out var tmp) && tmp.TryConvertTo().Result;
+ }
+
+ public OverlaySize OverlaySize => OverlaySize.Small;
+
+ public string View => DataEditorViewPath;
+ }
+}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.css b/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.css
new file mode 100644
index 00000000..550d4892
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.css
@@ -0,0 +1,21 @@
+/* Copyright © 2020 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+.contentment.lk-buttons .umb-button {
+ margin-bottom: 5px;
+}
+
+.contentment.lk-buttons .umb-button--selected .umb-button__button {
+ background-color: #f5c1bc;
+}
+
+.contentment.lk-buttons .umb-button__button {
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+ .contentment.lk-buttons .umb-button__button[disabled] {
+ opacity: 0.5;
+ }
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.html b/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.html
new file mode 100644
index 00000000..122b583d
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.html
@@ -0,0 +1,17 @@
+
+
+ ToValueEditor(object configuration)
{
config.Add(Filter, "formatBytes");
- if (config.ContainsKey(Format) == false && config.ContainsKey(Kilo) && config.ContainsKey(Decimals))
+ if (config.ContainsKey(Format) == false && config.ContainsKey(Kilo) == true && config.ContainsKey(Decimals) == true)
{
config.Add(Format, new
{
diff --git a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorConfigurationEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorConfigurationEditor.cs
index 77e85714..3885be21 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorConfigurationEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorConfigurationEditor.cs
@@ -3,13 +3,139 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+using System.Collections.Generic;
+using System.IO;
+using Umbraco.Core;
+using Umbraco.Core.IO;
+using Umbraco.Core.PropertyEditors;
+
namespace Umbraco.Community.Contentment.DataEditors
{
- internal sealed class CodeEditorConfigurationEditor
+ internal sealed class CodeEditorConfigurationEditor : ConfigurationEditor
{
internal const string FontSize = "fontSize";
internal const string Mode = "mode";
internal const string Theme = "theme";
internal const string UseWrapMode = "useWrapMode";
+
+ public CodeEditorConfigurationEditor()
+ : base()
+ {
+ var targetPath = "~/umbraco/lib/ace-builds/src-min-noconflict/";
+ var aceEditorPath = IOHelper.MapPath(targetPath);
+
+ if (Directory.Exists(aceEditorPath) == true)
+ {
+ var aceEditorFiles = Directory.GetFiles(aceEditorPath, "*.js");
+ if (aceEditorFiles != null && aceEditorFiles.Length > 0)
+ {
+ var modes = new List();
+ var themes = new List();
+
+ foreach (var file in aceEditorFiles)
+ {
+ var filename = Path.GetFileNameWithoutExtension(file);
+ if (filename.StartsWith("mode-") == true)
+ {
+ var mode = filename.Replace("mode-", string.Empty).ToLower();
+ modes.Add(new DataListItem { Name = mode.ToFirstUpperInvariant(), Value = mode });
+ }
+
+ if (filename.StartsWith("theme-") == true)
+ {
+ var theme = filename.Replace("theme-", string.Empty).ToLower();
+ themes.Add(new DataListItem { Name = theme.ToFirstUpperInvariant(), Value = theme });
+ }
+ }
+
+ if (modes.Count > 0)
+ {
+ DefaultConfiguration.Add(Mode, "razor");
+ Fields.Add(
+ Mode,
+ "Language mode",
+ "Select the programming language mode. The default mode is 'Razor'.",
+ IOHelper.ResolveUrl(DropdownListDataListEditor.DataEditorViewPath),
+ new Dictionary
+ {
+ { DropdownListDataListEditor.AllowEmpty, Constants.Values.False },
+ { Constants.Conventions.ConfigurationFieldAliases.Items, modes },
+ });
+ }
+
+ if (themes.Count > 0)
+ {
+ DefaultConfiguration.Add(Theme, "chrome");
+ Fields.Add(
+ Theme,
+ nameof(Theme),
+ "Set the theme for the code editor. The default theme is 'Chrome'.",
+ IOHelper.ResolveUrl(DropdownListDataListEditor.DataEditorViewPath),
+ new Dictionary
+ {
+ { DropdownListDataListEditor.AllowEmpty, Constants.Values.False },
+ { Constants.Conventions.ConfigurationFieldAliases.Items, themes },
+ });
+ }
+
+ if (modes.Count > 0 || themes.Count > 0)
+ {
+ Fields.Add(new NotesConfigurationField($@"
+Would you like to add more language modes and themes?
+
+This property editor makes use of AWS Cloud 9's Ace editor library that is distributed with Umbraco. By default, Umbraco ships a streamlined set of programming language modes and themes.
+If you would like to add more modes and themes, you can do this by downloading the latest pre-packaged version of the Ace editor and copy any of the mode-*
or theme-*
files from the src-min-noconflict
folder over to the {targetPath}
folder in this Umbraco installation.
+When you reload this screen, the new programming language modes and themes will appear in the dropdown options above.
+
+", true));
+ }
+ }
+ }
+
+ DefaultConfiguration.Add(FontSize, "small");
+ Fields.Add(
+ FontSize,
+ "Font size",
+ @"Set the font size. The value must be a valid CSS font-size value. The default size is 'small'.",
+ IOHelper.ResolveUrl(TextInputDataEditor.DataEditorViewPath),
+ new Dictionary
+ {
+ { Constants.Conventions.ConfigurationFieldAliases.Items, new[] {
+ new DataListItem { Name = "Extra extra small", Value = "xx-small" },
+ new DataListItem { Name = "Extra small", Value = "x-small" },
+ new DataListItem { Name = "Small", Value = "small" },
+ new DataListItem { Name = "Medium", Value = "medium" },
+ new DataListItem { Name = "Large", Value = "large" },
+ new DataListItem { Name = "Extra large", Value = "x-large" },
+ new DataListItem { Name = "Extra extra large", Value = "xx-large" },
+ new DataListItem { Name = "Extra extra extra large", Value = "xxx-large" },
+ new DataListItem { Name = "Use pixels?", Value = "14px" },
+ new DataListItem { Name = "Use percentage?", Value = "80%" },
+ new DataListItem { Name = "Use ems?", Value = "0.8em" },
+ new DataListItem { Name = "Use rems?", Value = "1.2rem" },
+ } },
+ });
+
+ Fields.Add(UseWrapMode, "Word wrapping", "Select to enable word wrapping.", "boolean");
+
+ // NOTE: [LK:2019-06-07] Hidden the advanced options (for now), need to review.
+ //Fields.Add("showGutter", "Show gutter?", "Select to show the left-hand side gutter in the code editor.", "boolean");
+ //Fields.Add("firstLineNumber", "First Line Number", "[A friendly description]", "number");
+ //Fields.Add("showInvisibles", "showInvisibles", "[A friendly description]", "boolean");// showInvisibles: 0,
+ //Fields.Add("showIndentGuides", "showIndentGuides", "[A friendly description]", "boolean");// showIndentGuides: 0,
+ //Fields.Add("useSoftTabs", "useSoftTabs", "[A friendly description]", "boolean");// useSoftTabs: 1,
+ //Fields.Add("showPrintMargin", "showPrintMargin", "[A friendly description]", "boolean");// showPrintMargin: 0,
+ //Fields.Add("disableSearch", "disableSearch", "[A friendly description]", "boolean");// disableSearch: 0,
+ //Fields.Add("enableSnippets", "enableSnippets", "[A friendly description]", "boolean");// enableSnippets: 0,
+ //Fields.Add("enableBasicAutocompletion", "enableBasicAutocompletion", "[A friendly description]", "boolean");// enableBasicAutocompletion: 0,
+ //Fields.Add("enableLiveAutocompletion", "enableLiveAutocompletion", "[A friendly description]", "boolean");// enableLiveAutocompletion: 0,
+ //Fields.Add("readonly", "readonly", "[A friendly description]", "boolean");// readonly: 0,
+
+ DefaultConfiguration.Add("minLines", 12);
+ Fields.Add("minLines", "Minimum lines", "Set the minimum number of lines that the editor will be. The default is 12 lines.", IOHelper.ResolveUrl(NumberInputDataEditor.DataEditorViewPath));
+
+ DefaultConfiguration.Add("maxLines", 30);
+ Fields.Add("maxLines", "Maximum lines", "Set the maximum number of lines that the editor can be. If left empty, the editor will not auto-scale.", IOHelper.ResolveUrl(NumberInputDataEditor.DataEditorViewPath));
+ }
}
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorDataEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorDataEditor.cs
index 43c3822e..bc0331de 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorDataEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorDataEditor.cs
@@ -3,13 +3,33 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+using Umbraco.Core.Logging;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Web.PropertyEditors;
+
namespace Umbraco.Community.Contentment.DataEditors
{
- internal sealed class CodeEditorDataEditor
+ [DataEditor(
+ DataEditorAlias,
+ EditorType.PropertyValue,
+ DataEditorName,
+ DataEditorViewPath,
+ ValueType = ValueTypes.Text,
+ Group = Constants.Conventions.PropertyGroups.Code,
+ Icon = DataEditorIcon)]
+ internal sealed class CodeEditorDataEditor : DataEditor
{
internal const string DataEditorAlias = Constants.Internals.DataEditorAliasPrefix + "CodeEditor";
internal const string DataEditorName = Constants.Internals.DataEditorNamePrefix + "Code Editor";
internal const string DataEditorViewPath = Constants.Internals.EditorsPathRoot + "code-editor.html";
- internal const string DataEditorIcon = "icon-code";
+ internal const string DataEditorIcon = "icon-fa fa-code";
+
+ public CodeEditorDataEditor(ILogger logger)
+ : base(logger)
+ { }
+
+ protected override IConfigurationEditor CreateConfigurationEditor() => new CodeEditorConfigurationEditor();
+
+ protected override IDataValueEditor CreateValueEditor() => new TextOnlyValueEditor(Attribute);
}
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorValueConverter.cs b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorValueConverter.cs
new file mode 100644
index 00000000..2c331f35
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/CodeEditorValueConverter.cs
@@ -0,0 +1,19 @@
+/* Copyright © 2020 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+using System;
+using Umbraco.Core;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Community.Contentment.DataEditors
+{
+ internal sealed class CodeEditorValueConverter : PropertyValueConverterBase
+ {
+ public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(CodeEditorDataEditor.DataEditorAlias);
+
+ public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof(string);
+ }
+}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/README.md b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/README.md
index 9844960d..730e2bf7 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/README.md
+++ b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/README.md
@@ -1,10 +1,7 @@
## Code Editor
+Code Editor is a property-editor that uses Umbraco's ACE editor implementation as a input for code (as content).
+
### Used interally by
Code Editor is used in the configuration editors of a couple of Data List providers, namely SQL data source and Templated List editor.
-
-
-### Future scope
-
-It has future scope to be a standalone property-editor.
diff --git a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.html b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.html
index eb2d54d8..6b86223a 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.html
+++ b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.html
@@ -3,6 +3,6 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/. -->
-
+
diff --git a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.js b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.js
index 2da7a0d1..406cb867 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.js
+++ b/src/Umbraco.Community.Contentment/DataEditors/CodeEditor/code-editor.js
@@ -20,7 +20,7 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
theme: "chrome",
mode: "javascript",
firstLineNumber: 1,
- fontSize: "14px",
+ fontSize: "small",
enableSnippets: 0,
enableBasicAutocompletion: 0,
enableLiveAutocompletion: 0,
@@ -35,6 +35,7 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
function init() {
vm.readonly = Object.toBoolean(config.readonly);
+
vm.options = {
autoFocus: false,
showGutter: Object.toBoolean(config.showGutter),
diff --git a/src/Umbraco.Community.Contentment/DataEditors/ConfigurationEditor/configuration-editor.js b/src/Umbraco.Community.Contentment/DataEditors/ConfigurationEditor/configuration-editor.js
index f98dbda0..61160f8d 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/ConfigurationEditor/configuration-editor.js
+++ b/src/Umbraco.Community.Contentment/DataEditors/ConfigurationEditor/configuration-editor.js
@@ -6,11 +6,13 @@
angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.ConfigurationEditor.Controller", [
"$scope",
"$interpolate",
+ "$timeout",
"editorService",
+ "eventsService",
"localizationService",
"overlayService",
"Umbraco.Community.Contentment.Services.DevMode",
- function ($scope, $interpolate, editorService, localizationService, overlayService, devModeService) {
+ function ($scope, $interpolate, $timeout, editorService, eventsService, localizationService, overlayService, devModeService) {
// console.log("config-editor.model", $scope.model);
@@ -102,6 +104,8 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
});
}
+ // NOTE: Must wait, otherwise the preview may not be ready to receive the event.
+ $timeout(function () { emit(); }, 100);
};
function add() {
@@ -167,6 +171,10 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
});
};
+ function emit() {
+ eventsService.emit("contentment.config-editor.model", $scope.model);
+ };
+
function populate(item, $index, propertyName) {
var label = "";
@@ -232,6 +240,7 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
if ($scope.propertyForm) {
$scope.propertyForm.$setDirty();
}
+ emit();
};
init();
diff --git a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlockPreviewView.cs b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlockPreviewView.cs
index 459afab7..516564d2 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlockPreviewView.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlockPreviewView.cs
@@ -24,7 +24,7 @@ protected override void SetViewData(ViewDataDictionary viewData)
{
void setProperty(string key, Action action)
{
- if (viewData.TryGetValueAs(key, out T tmp))
+ if (viewData.TryGetValueAs(key, out T tmp) == true)
{
action(tmp);
diff --git a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksConfigurationEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksConfigurationEditor.cs
index 52d7b46a..b6da9cd4 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksConfigurationEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksConfigurationEditor.cs
@@ -127,13 +127,13 @@ public override IDictionary ToValueEditor(object configuration)
var item = (JObject)array2[i];
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`. [LK:2020-04-03]
- if (item.ContainsKey("key") == false && item.ContainsKey("type"))
+ if (item.ContainsKey("key") == false && item.ContainsKey("type") == true)
{
item.Add("key", item["type"]);
item.Remove("type");
}
- if (Guid.TryParse(item.Value("key"), out var guid) && _elementTypes.ContainsKey(guid))
+ if (Guid.TryParse(item.Value("key"), out var guid) && _elementTypes.ContainsKey(guid) == true)
{
var elementType = _elementTypes[guid];
diff --git a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksDataValueEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksDataValueEditor.cs
index a297f55b..643969b4 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksDataValueEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksDataValueEditor.cs
@@ -43,7 +43,7 @@ public ContentBlocksDataValueEditor(
public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
var value = property.GetValue(culture, segment)?.ToString();
- if (string.IsNullOrWhiteSpace(value))
+ if (string.IsNullOrWhiteSpace(value) == true)
{
return base.ToEditor(property, dataTypeService, culture, segment);
}
@@ -105,7 +105,7 @@ public override object ToEditor(Property property, IDataTypeService dataTypeServ
public override object FromEditor(ContentPropertyData editorValue, object currentValue)
{
var value = editorValue?.Value?.ToString();
- if (string.IsNullOrWhiteSpace(value))
+ if (string.IsNullOrWhiteSpace(value) == true)
{
return base.FromEditor(editorValue, currentValue);
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksValueConverter.cs b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksValueConverter.cs
index faba7bc0..bb093bb3 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksValueConverter.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentBlocksValueConverter.cs
@@ -54,7 +54,7 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub
foreach (var item in items)
{
- if (item == null || item.ElementType.Equals(Guid.Empty))
+ if (item == null || item.ElementType == Guid.Empty)
continue;
// NOTE: [LK:2019-09-03] Why `IPublishedCache` doesn't support Guids or UDIs, I do not know!?
diff --git a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentTypeCacheHelper.cs b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentTypeCacheHelper.cs
index 557c413d..baddc58f 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentTypeCacheHelper.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/ContentBlocks/ContentTypeCacheHelper.cs
@@ -42,7 +42,7 @@ public static void TryAdd(Guid guid, string alias, string icon = null)
public static bool TryGetAlias(Guid key, out string alias, IContentTypeService contentTypeService = null)
{
- if (_forward.TryGetValue(key, out alias))
+ if (_forward.TryGetValue(key, out alias) == true)
return true;
// The alias isn't cached, we can attempt to get it via the content-type service, using the GUID.
@@ -62,7 +62,7 @@ public static bool TryGetAlias(Guid key, out string alias, IContentTypeService c
public static bool TryGetIcon(string alias, out string icon, IContentTypeService contentTypeService = null)
{
- if (_icons.TryGetValue(alias, out icon))
+ if (_icons.TryGetValue(alias, out icon) == true)
return true;
// The icon isn't cached, we can attempt to get it via the content-type service, using the alias.
@@ -82,7 +82,7 @@ public static bool TryGetIcon(string alias, out string icon, IContentTypeService
public static bool TryGetGuid(string alias, out Guid key, IContentTypeService contentTypeService = null)
{
- if (_reverse.TryGetValue(alias, out key))
+ if (_reverse.TryGetValue(alias, out key) == true)
return true;
// The GUID isn't cached, we can attempt to get it via the content-type service, using the alias.
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListApiController.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListApiController.cs
new file mode 100644
index 00000000..36d4b406
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListApiController.cs
@@ -0,0 +1,47 @@
+/* Copyright © 2020 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Web.Http;
+using Newtonsoft.Json.Linq;
+using Umbraco.Core;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Web.Editors;
+using Umbraco.Web.Mvc;
+using Umbraco.Web.WebApi;
+
+namespace Umbraco.Community.Contentment.DataEditors
+{
+ [PluginController(Constants.Internals.PluginControllerName), IsBackOffice]
+ public sealed class DataListApiController : UmbracoAuthorizedJsonController
+ {
+ private readonly PropertyEditorCollection _propertyEditors;
+
+ public DataListApiController(PropertyEditorCollection propertyEditors)
+ {
+ _propertyEditors = propertyEditors;
+ }
+
+ [HttpPost]
+ public HttpResponseMessage GetPreview([FromBody] JObject data)
+ {
+ var config = data.ToObject>();
+
+ if (_propertyEditors.TryGet(DataListDataEditor.DataEditorAlias, out var propertyEditor) == true)
+ {
+ var alias = config.GetValueAs("alias", "preview");
+ var configurationEditor = propertyEditor.GetConfigurationEditor();
+ var valueEditorConfig = configurationEditor.ToValueEditor(config);
+ var valueEditor = propertyEditor.GetValueEditor(config);
+
+ return Request.CreateResponse(HttpStatusCode.OK, new { config = valueEditorConfig, view = valueEditor.View, alias });
+ }
+
+ return Request.CreateResponse(HttpStatusCode.NotFound);
+ }
+ }
+}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListConfigurationEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListConfigurationEditor.cs
index beaec048..809ac1c2 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListConfigurationEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListConfigurationEditor.cs
@@ -36,8 +36,8 @@ public DataListConfigurationEditor(ConfigurationEditorUtility utility)
{ EnableDevModeConfigurationField.EnableDevMode, Constants.Values.True },
};
- var dataSources = utility.GetConfigurationEditorModels();
- var listEditors = utility.GetConfigurationEditorModels();
+ var dataSources = utility.GetConfigurationEditorModels().ToList();
+ var listEditors = utility.GetConfigurationEditorModels().ToList();
Fields.Add(
DataSource,
@@ -47,7 +47,8 @@ public DataListConfigurationEditor(ConfigurationEditorUtility utility)
new Dictionary(defaultConfigEditorConfig)
{
{ Constants.Conventions.ConfigurationFieldAliases.AddButtonLabelKey, "contentment_configureDataSource" },
- { Constants.Conventions.ConfigurationFieldAliases.Items, dataSources }
+ { EnableFilterConfigurationField.EnableFilter, dataSources.Count > 10 ? Constants.Values.True : Constants.Values.False },
+ { Constants.Conventions.ConfigurationFieldAliases.Items, dataSources },
});
Fields.Add(
@@ -58,8 +59,15 @@ public DataListConfigurationEditor(ConfigurationEditorUtility utility)
new Dictionary(defaultConfigEditorConfig)
{
{ Constants.Conventions.ConfigurationFieldAliases.AddButtonLabelKey, "contentment_configureListEditor" },
- { Constants.Conventions.ConfigurationFieldAliases.Items, listEditors }
+ { EnableFilterConfigurationField.EnableFilter, dataSources.Count > 10 ? Constants.Values.True : Constants.Values.False },
+ { Constants.Conventions.ConfigurationFieldAliases.Items, listEditors },
});
+
+ Fields.Add(
+ "preview",
+ "Preview",
+ null,
+ IOHelper.ResolveUrl(DataListDataEditor.DataEditorPreviewViewPath));
}
public override IDictionary ToValueEditor(object configuration)
@@ -68,10 +76,10 @@ public override IDictionary ToValueEditor(object configuration)
var toValueEditor = new Dictionary();
- if (config.TryGetValueAs(DataSource, out JArray array1) && array1.Count > 0 && array1[0] is JObject item1)
+ if (config.TryGetValueAs(DataSource, out JArray array1) == true && array1.Count > 0 && array1[0] is JObject item1)
{
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`. [LK:2020-04-03]
- if (item1.ContainsKey("key") == false && item1.ContainsKey("type"))
+ if (item1.ContainsKey("key") == false && item1.ContainsKey("type") == true)
{
item1.Add("key", item1["type"]);
item1.Remove("type");
@@ -87,10 +95,10 @@ public override IDictionary ToValueEditor(object configuration)
}
}
- if (config.TryGetValueAs(ListEditor, out JArray array2) && array2.Count > 0 && array2[0] is JObject item2)
+ if (config.TryGetValueAs(ListEditor, out JArray array2) == true && array2.Count > 0 && array2[0] is JObject item2)
{
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`. [LK:2020-04-03]
- if (item2.ContainsKey("key") == false && item2.ContainsKey("type"))
+ if (item2.ContainsKey("key") == false && item2.ContainsKey("type") == true)
{
item2.Add("key", item2["type"]);
item2.Remove("type");
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListDataEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListDataEditor.cs
index 22137f86..3de04412 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListDataEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListDataEditor.cs
@@ -15,6 +15,7 @@ public sealed class DataListDataEditor : IDataEditor
internal const string DataEditorAlias = Constants.Internals.DataEditorAliasPrefix + "DataList";
internal const string DataEditorName = Constants.Internals.DataEditorNamePrefix + "Data List";
internal const string DataEditorViewPath = Constants.Internals.EditorsPathRoot + "_empty.html";
+ internal const string DataEditorPreviewViewPath = Constants.Internals.EditorsPathRoot + "data-list.preview.html";
internal const string DataEditorIcon = "icon-fa fa-list-ul";
private readonly ConfigurationEditorUtility _utility;
@@ -53,12 +54,12 @@ public IDataValueEditor GetValueEditor(object configuration)
var view = default(string);
if (configuration is Dictionary config &&
- config.TryGetValueAs(DataListConfigurationEditor.ListEditor, out JArray array) &&
+ config.TryGetValueAs(DataListConfigurationEditor.ListEditor, out JArray array) == true &&
array.Count > 0 &&
array[0] is JObject item)
{
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`. [LK:2020-04-03]
- if (item.ContainsKey("key") == false && item.ContainsKey("type"))
+ if (item.ContainsKey("key") == false && item.ContainsKey("type") == true)
{
item.Add("key", item["type"]);
item.Remove("type");
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListValueConverter.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListValueConverter.cs
index 119d12b5..03956a4d 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListValueConverter.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataListValueConverter.cs
@@ -125,13 +125,13 @@ private void TryGetPropertyTypeConfiguration(IPublishedPropertyType propertyType
converter = default;
if (propertyType.DataType.Configuration is Dictionary configuration &&
- configuration.TryGetValue(DataListConfigurationEditor.DataSource, out var tmp1) &&
+ configuration.TryGetValue(DataListConfigurationEditor.DataSource, out var tmp1) == true &&
tmp1 is JArray array1 && array1.Count > 0 && array1[0] is JObject obj1 &&
- configuration.TryGetValue(DataListConfigurationEditor.ListEditor, out var tmp2) &&
+ configuration.TryGetValue(DataListConfigurationEditor.ListEditor, out var tmp2) == true &&
tmp2 is JArray array2 && array2.Count > 0 && array2[0] is JObject obj2)
{
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`. [LK:2020-04-03]
- if (obj1.ContainsKey("key") == false && obj1.ContainsKey("type"))
+ if (obj1.ContainsKey("key") == false && obj1.ContainsKey("type") == true)
{
obj1.Add("key", obj1["type"]);
obj1.Remove("type");
@@ -146,7 +146,7 @@ tmp1 is JArray array1 && array1.Count > 0 && array1[0] is JObject obj1 &&
}
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`. [LK:2020-04-03]
- if (obj2.ContainsKey("key") == false && obj2.ContainsKey("type"))
+ if (obj2.ContainsKey("key") == false && obj2.ContainsKey("type") == true)
{
obj2.Add("key", obj2["type"]);
obj2.Remove("type");
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/EnumDataListSource.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/EnumDataListSource.cs
index 58e50bd8..614d2f3f 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/EnumDataListSource.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/EnumDataListSource.cs
@@ -93,7 +93,7 @@ public IEnumerable GetItems(Dictionary config)
});
}
- if (config.TryGetValueAs("sortAlphabetically", out string boolean) && boolean.Equals("1"))
+ if (config.TryGetValueAs("sortAlphabetically", out string boolean) == true && boolean == "1")
{
return items.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase);
}
@@ -103,7 +103,7 @@ public IEnumerable GetItems(Dictionary config)
public Type GetValueType(Dictionary config)
{
- if (config.TryGetValueAs("enumType", out JArray array))
+ if (config.TryGetValueAs("enumType", out JArray array) == true)
{
var enumType = array.ToObject();
if (enumType?.Length > 1)
@@ -114,7 +114,7 @@ public Type GetValueType(Dictionary config)
{
var type = default(Type);
try { type = assembly.GetType(enumType[1]); } catch (Exception ex) { _logger.Error(ex); }
- if (type != null && type.IsEnum)
+ if (type != null && type.IsEnum == true)
{
return type;
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/JsonDataListSource.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/JsonDataListSource.cs
index ffedc9d7..4d38a1ae 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/JsonDataListSource.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/JsonDataListSource.cs
@@ -104,7 +104,7 @@ public IEnumerable GetItems(Dictionary config)
var url = config.GetValueAs("url", string.Empty);
- if (string.IsNullOrWhiteSpace(url))
+ if (string.IsNullOrWhiteSpace(url) == true)
{
return items;
}
@@ -118,7 +118,7 @@ public IEnumerable GetItems(Dictionary config)
var itemsJsonPath = config.GetValueAs("itemsJsonPath", string.Empty);
- if (string.IsNullOrWhiteSpace(itemsJsonPath))
+ if (string.IsNullOrWhiteSpace(itemsJsonPath) == true)
{
return items;
}
@@ -189,7 +189,7 @@ private JToken GetJson(string url)
{
var content = string.Empty;
- if (url.StartsWith("http", StringComparison.InvariantCultureIgnoreCase))
+ if (url.StartsWith("http", StringComparison.InvariantCultureIgnoreCase) == true)
{
try
{
@@ -207,7 +207,7 @@ private JToken GetJson(string url)
{
// assume local file
var path = IOHelper.MapPath(url);
- if (File.Exists(path))
+ if (File.Exists(path) == true)
{
content = File.ReadAllText(path);
}
@@ -218,7 +218,7 @@ private JToken GetJson(string url)
}
}
- if (string.IsNullOrWhiteSpace(content))
+ if (string.IsNullOrWhiteSpace(content) == true)
{
_logger.Warn($"The contents of '{url}' was empty. Unable to process JSON data.");
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/SqlDataListSource.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/SqlDataListSource.cs
index 958899e5..6368ad8a 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/SqlDataListSource.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/SqlDataListSource.cs
@@ -99,7 +99,7 @@ public IEnumerable GetItems(Dictionary config)
var query = config.GetValueAs("query", string.Empty);
var connectionString = config.GetValueAs("connectionString", string.Empty);
- if (string.IsNullOrWhiteSpace(query) || string.IsNullOrWhiteSpace(connectionString))
+ if (string.IsNullOrWhiteSpace(query) == true || string.IsNullOrWhiteSpace(connectionString) == true)
return items;
var settings = ConfigurationManager.ConnectionStrings[connectionString];
@@ -107,7 +107,7 @@ public IEnumerable GetItems(Dictionary config)
return items;
// NOTE: SQLCE uses a different connection/command. I'm trying to keep this as generic as possible, without resorting to using NPoco. [LK]
- if (settings.ProviderName.InvariantEquals(Core.Constants.DatabaseProviders.SqlCe))
+ if (settings.ProviderName.InvariantEquals(Core.Constants.DatabaseProviders.SqlCe) == true)
{
items.AddRange(GetSqlItems(query, settings.ConnectionString));
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/TextDelimitedDataListSource.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/TextDelimitedDataListSource.cs
index 20db7ad4..17d6d40d 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/TextDelimitedDataListSource.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/TextDelimitedDataListSource.cs
@@ -112,7 +112,7 @@ public IEnumerable GetItems(Dictionary config)
var iconIndex = config.GetValueAs("iconIndex", -1);
var descriptionIndex = config.GetValueAs("descriptionIndex", -1);
- if (string.IsNullOrWhiteSpace(url))
+ if (string.IsNullOrWhiteSpace(url) == true)
{
return items;
}
@@ -129,7 +129,7 @@ public IEnumerable GetItems(Dictionary config)
for (var i = 0; i < lines.Length; i++)
{
- if (i == 0 && ignoreFirstLine)
+ if (i == 0 && ignoreFirstLine == true)
{
continue;
}
@@ -174,7 +174,7 @@ public IEnumerable GetItems(Dictionary config)
private string[] GetTextLines(string url)
{
- if (url.InvariantStartsWith("http"))
+ if (url.InvariantStartsWith("http") == true)
{
try
{
@@ -192,7 +192,7 @@ private string[] GetTextLines(string url)
{
// assume local file
var path = IOHelper.MapPath(url);
- if (File.Exists(path))
+ if (File.Exists(path) == true)
{
return File.ReadAllLines(path);
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/UmbracoContentDataListSource.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/UmbracoContentDataListSource.cs
index 46b14b21..03520903 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/UmbracoContentDataListSource.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/UmbracoContentDataListSource.cs
@@ -81,7 +81,7 @@ public IEnumerable GetItems(Dictionary config)
startNode = umbracoContext.Content.GetSingleByXPath(preview, parsed);
}
}
- else if (GuidUdi.TryParse(parentNode, out var udi) && udi.Guid.Equals(Guid.Empty) == false)
+ else if (GuidUdi.TryParse(parentNode, out var udi) == true && udi.Guid != Guid.Empty)
{
startNode = _umbracoContextAccessor.UmbracoContext.Content.GetById(preview, udi.Guid);
}
@@ -106,7 +106,7 @@ public Type GetValueType(Dictionary config)
public object ConvertValue(Type type, string value)
{
- if (type == typeof(IPublishedContent) && Udi.TryParse(value, out var udi))
+ if (type == typeof(IPublishedContent) && Udi.TryParse(value, out var udi) == true)
{
return _umbracoContextAccessor.UmbracoContext.Content.GetById(udi);
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/XmlDataListSource.cs b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/XmlDataListSource.cs
index e5e9cfcc..10250209 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/XmlDataListSource.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/XmlDataListSource.cs
@@ -103,7 +103,7 @@ public IEnumerable GetItems(Dictionary config)
var url = config.GetValueAs("url", string.Empty);
- if (string.IsNullOrWhiteSpace(url))
+ if (string.IsNullOrWhiteSpace(url) == true)
return items;
var path = url.InvariantStartsWith("http") == false
@@ -133,7 +133,7 @@ public IEnumerable GetItems(Dictionary config)
var itemsXPath = config.GetValueAs("itemsXPath", string.Empty);
- if (string.IsNullOrWhiteSpace(itemsXPath))
+ if (string.IsNullOrWhiteSpace(itemsXPath) == true)
{
return items;
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.css b/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.css
new file mode 100644
index 00000000..17c8a8e5
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.css
@@ -0,0 +1,17 @@
+/* Copyright © 2021 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/* "Well" container - limiting the width, to match property width limit. */
+.contentment .well.well--limit-width {
+ max-width: 760px;
+}
+
+.contentment .well.well-small.well--limit-width {
+ max-width: 780px;
+}
+
+.contentment .well.well-large.well--limit-width {
+ max-width: 760px;
+}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.html b/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.html
new file mode 100644
index 00000000..483eefbf
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Please select and configure a data source and list editor.
+
+
+ Please select and configure a list editor.
+
+
+ Please select and configure a data source.
+
+
+
+
+
+ The data source returned no items to preview.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name
+ Value
+ Description
+ Enabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.js b/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.js
new file mode 100644
index 00000000..772dceab
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/DataList/data-list.preview.js
@@ -0,0 +1,87 @@
+/* Copyright © 2020 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.DataList.Preview.Controller", [
+ "$scope",
+ "$http",
+ "eventsService",
+ "umbRequestHelper",
+ function ($scope, $http, eventsService, umbRequestHelper) {
+
+ //console.log("data-list.preview.model", $scope.model);
+
+ var config = Object.assign({ alias: $scope.model.alias }, $scope.model.config);
+ var vm = this;
+
+ function init() {
+
+ vm.property = {};
+ vm.state = "loading";
+
+ vm.tabs = [{
+ label: "Editor preview",
+ alias: "listEditor",
+ active: false,
+ }, {
+ label: "Data source items",
+ alias: "dataSource",
+ active: false,
+ }];
+
+ // set the first tab to active
+ vm.tabs[0].active = true;
+ vm.activeTab = vm.tabs[0].alias;
+
+ vm.changeTab = function (tab) {
+ vm.tabs.forEach(function (x) { x.active = false; });
+ vm.activeTab = tab.alias;
+ tab.active = true;
+ };
+
+ var events = [];
+
+ events.push(eventsService.on("contentment.config-editor.model", function (event, args) {
+ config[args.alias] = args.value;
+ fetch();
+ }));
+
+ $scope.$on("$destroy", function () {
+ for (var event in events) {
+ eventsService.unsubscribe(events[event]);
+ }
+ });
+
+ };
+
+ function fetch() {
+ if (_.isEmpty(config.dataSource) === false && _.isEmpty(config.listEditor) === false) {
+
+ vm.state = "loading";
+ vm.property = null;
+
+ umbRequestHelper.resourcePromise(
+ $http.post("backoffice/Contentment/DataListApi/GetPreview", config),
+ "Failed to retrieve data list preview.")
+ .then(function (result) {
+
+ vm.property = result;
+ vm.state = result.config.items.length > 0 ? "loaded" : "noItems";
+
+ });
+ }
+ else if (_.isEmpty(config.dataSource) === false) {
+ vm.state = "dataSourceConfigured";
+ }
+ else if (_.isEmpty(config.listEditor) === false) {
+ vm.state = "listEditorConfigured";
+ }
+ else {
+ vm.state = "init";
+ }
+ };
+
+ init();
+ }
+]);
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DropdownList/DropdownListDataListEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/DropdownList/DropdownListDataListEditor.cs
index 0bea99d7..38545921 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DropdownList/DropdownListDataListEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/DropdownList/DropdownListDataListEditor.cs
@@ -25,7 +25,7 @@ public sealed class DropdownListDataListEditor : IDataListEditor
{
Key = AllowEmpty,
Name = "Allow empty?",
- Description = "Enable to allow an empty option at the top of the dropdown list.",
+ Description = "Enable to allow an empty option at the top of the dropdown list.
When disabled, the default value will be set to the first option.",
View = "views/propertyeditors/boolean/boolean.html",
Config = new Dictionary
{
@@ -35,7 +35,10 @@ public sealed class DropdownListDataListEditor : IDataListEditor
new HtmlAttributesConfigurationField(),
};
- public Dictionary DefaultValues => default;
+ public Dictionary DefaultValues => new Dictionary
+ {
+ { AllowEmpty, Constants.Values.True }
+ };
public Dictionary DefaultConfig => default;
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.html b/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.html
index 2e32e0a9..49fe1c15 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.html
+++ b/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.html
@@ -10,6 +10,6 @@
ng-model="model.value"
ng-change="vm.change()"
ng-options="item.value as item.name disable when item.disabled for item in vm.items">
-
+
diff --git a/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.js b/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.js
index 25f96990..d0c423cd 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.js
+++ b/src/Umbraco.Community.Contentment/DataEditors/DropdownList/dropdown-list.js
@@ -20,15 +20,21 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
var vm = this;
function init() {
+
+ config.showEmpty = Object.toBoolean(config.allowEmpty);
+
+ vm.items = config.items.slice();
+
$scope.model.value = $scope.model.value || config.defaultValue;
if (Array.isArray($scope.model.value)) {
$scope.model.value = $scope.model.value[0];
}
+ else if (config.showEmpty === false && $scope.model.value === '' && vm.items.length > 0) {
+ $scope.model.value = vm.items[0].value;
+ }
- vm.items = config.items.slice();
-
- vm.allowEmpty = Object.toBoolean(config.allowEmpty) && vm.items.some(x => x.value === $scope.model.value);
+ vm.showEmpty = config.showEmpty === true && vm.items.some(x => x.value === $scope.model.value);
vm.htmlAttributes = config.htmlAttributes;
@@ -40,7 +46,7 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.
};
function change() {
- vm.allowEmpty = Object.toBoolean(config.allowEmpty) && vm.items.some(x => x.value === $scope.model.value);
+ vm.showEmpty = config.showEmpty === true && vm.items.some(x => x.value === $scope.model.value);
};
init();
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Notes/NotesDataEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/Notes/NotesDataEditor.cs
index 2bf191ed..463d4a0e 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/Notes/NotesDataEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/Notes/NotesDataEditor.cs
@@ -47,7 +47,7 @@ public IDataValueEditor GetValueEditor(object configuration)
{
var hideLabel = false;
- if (configuration is Dictionary config && config.ContainsKey(HideLabelConfigurationField.HideLabelAlias))
+ if (configuration is Dictionary config && config.ContainsKey(HideLabelConfigurationField.HideLabelAlias) == true)
{
hideLabel = config[HideLabelConfigurationField.HideLabelAlias].TryConvertTo().Result;
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Notes/notes.css b/src/Umbraco.Community.Contentment/DataEditors/Notes/notes.css
index 0e5012ab..c546d6db 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/Notes/notes.css
+++ b/src/Umbraco.Community.Contentment/DataEditors/Notes/notes.css
@@ -11,6 +11,11 @@
margin-bottom: 10px;
}
+ .umb-readonlyvalue .well blockquote p {
+ font-size: 16px;
+ margin-bottom: 10px;
+ }
+
.umb-readonlyvalue .well hr {
border-bottom: 1px solid #e0e0e5;
margin: 15px 0;
diff --git a/src/Umbraco.Community.Contentment/DataEditors/NumberInput/NumberInputValueConverter.cs b/src/Umbraco.Community.Contentment/DataEditors/NumberInput/NumberInputValueConverter.cs
index d41c0fdd..66425714 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/NumberInput/NumberInputValueConverter.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/NumberInput/NumberInputValueConverter.cs
@@ -19,7 +19,7 @@ internal sealed class NumberInputValueConverter : PropertyValueConverterBase
// TODO: [LK:2020-12-11] Commented out the value-type feature for the time being. Adds additional complexity that I don't currently need.
//public override Type GetPropertyValueType(IPublishedPropertyType propertyType)
//{
- // if (propertyType.DataType.Configuration is Dictionary config && config.TryGetValue(UmbConfigurationKeys.DataValueType, out var tmp) && tmp is string valueType)
+ // if (propertyType.DataType.Configuration is Dictionary config && config.TryGetValueAs(UmbConfigurationKeys.DataValueType, out string valueType) == true)
// {
// switch (valueType)
// {
diff --git a/src/Umbraco.Community.Contentment/DataEditors/RenderMacro/RenderMacroDataEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/RenderMacro/RenderMacroDataEditor.cs
index 0e0f3e0b..5858f4a1 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/RenderMacro/RenderMacroDataEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/RenderMacro/RenderMacroDataEditor.cs
@@ -47,7 +47,7 @@ public IDataValueEditor GetValueEditor(object configuration)
{
var hideLabel = false;
- if (configuration is Dictionary config && config.ContainsKey(HideLabelConfigurationField.HideLabelAlias))
+ if (configuration is Dictionary config && config.ContainsKey(HideLabelConfigurationField.HideLabelAlias) == true)
{
hideLabel = config[HideLabelConfigurationField.HideLabelAlias].TryConvertTo().Result;
}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Tags/TagsDataListEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/Tags/TagsDataListEditor.cs
new file mode 100644
index 00000000..cdd0fa49
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Tags/TagsDataListEditor.cs
@@ -0,0 +1,43 @@
+/* Copyright © 2021 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+using System.Collections.Generic;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Community.Contentment.DataEditors
+{
+ public sealed class TagsDataListEditor : IDataListEditor
+ {
+ internal const string DataEditorViewPath = Constants.Internals.EditorsPathRoot + "tags.html";
+
+ public string Name => "Tags";
+
+ public string Description => "Select items from an Umbraco Tags-like interface.";
+
+ public string Icon => "icon-fa fa-tags";
+
+ public IEnumerable Fields => new ConfigurationField[]
+ {
+ new ShowIconsConfigurationField(),
+ new ConfigurationField
+ {
+ Key ="confirmRemoval",
+ Name = "Confirm removals?",
+ Description = "Select to enable a confirmation prompt when removing an item.",
+ View = "boolean",
+ }
+ };
+
+ public Dictionary DefaultValues => default;
+
+ public Dictionary DefaultConfig => default;
+
+ public bool HasMultipleValues(Dictionary config) => true;
+
+ public OverlaySize OverlaySize => OverlaySize.Small;
+
+ public string View => DataEditorViewPath;
+ }
+}
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.css b/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.css
new file mode 100644
index 00000000..7621e854
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.css
@@ -0,0 +1,20 @@
+/* Copyright © 2021 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+.contentment .umb-tags .tag {
+ user-select: text;
+}
+
+ .contentment .umb-tags .tag .icon {
+ color: white !important;
+ }
+
+ .contentment .umb-tags .tag .btn-reset {
+ line-height: 14px;
+ }
+
+ .contentment .umb-tags .tag .icon-trash {
+ bottom: 0;
+ }
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.html b/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.html
new file mode 100644
index 00000000..cf76f598
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.html
@@ -0,0 +1,31 @@
+
+
+
+
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.js b/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.js
new file mode 100644
index 00000000..bd176a19
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Tags/tags.js
@@ -0,0 +1,174 @@
+/* Copyright © 2021 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.Tags.Controller", [
+ "$rootScope",
+ "$scope",
+ "$element",
+ "angularHelper",
+ "assetsService",
+ "localizationService",
+ "overlayService",
+ function ($rootScope, $scope, $element, angularHelper, assetsService, localizationService, overlayService) {
+
+ //console.log("tags.model", $scope.model);
+
+ var defaultConfig = {
+ confirmRemoval: 0,
+ defaultValue: [],
+ items: [],
+ showIcons: 0,
+ };
+ var config = Object.assign({}, defaultConfig, $scope.model.config);
+
+ var vm = this;
+
+ function init() {
+
+ $scope.model.value = $scope.model.value || config.defaultValue;
+
+ if (Array.isArray($scope.model.value) === false) {
+ $scope.model.value = [$scope.model.value];
+ }
+
+ config.confirmRemoval = Object.toBoolean(config.confirmRemoval);
+
+ vm.items = [];
+
+ vm.showIcons = Object.toBoolean(config.showIcons);
+
+ vm.uniqueId = $scope.model.hasOwnProperty("dataTypeKey")
+ ? ["tags", $scope.model.alias, $scope.model.dataTypeKey.substring(0, 8)].join("-")
+ : ["tags", $scope.model.alias].join("-");
+
+ vm.add = add;
+ vm.keyDown = keyDown;
+ vm.keyUp = keyUp;
+ vm.remove = remove;
+
+ vm.loading = true;
+
+ assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js").then(function () {
+
+ $scope.model.value.forEach(function (v) {
+ var item = config.items.find(x => x.value === v);
+ if (item) {
+ vm.items.push(Object.assign({}, item));
+ }
+ });
+
+ vm.loading = false;
+
+ var engine = new Bloodhound({
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace("name", "value"),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ initialize: false,
+ local: config.items.filter(x => !x.disabled),
+ identify: d => d.value,
+ });
+
+ engine.initialize().then(function () {
+
+ var opts = {
+ hint: true,
+ highlight: true,
+ minLength: 1
+ };
+
+ var sources = {
+ display: "name",
+ minLength: 0,
+ source: function (q, sync) {
+ if (q && q.length > 0) {
+ engine.search(q, sync);
+ } else {
+ sync(engine.all());
+ }
+ }
+ };
+
+ vm.editor = $element.find("input.typeahead")
+ .typeahead(opts, sources)
+ .bind("typeahead:select", add)
+ .bind("typeahead:autocomplete", add);
+ });
+
+ });
+
+ };
+
+ function add($event, item) {
+ angularHelper.safeApply($rootScope, function () {
+
+ vm.items.push(Object.assign({}, item));
+
+ $scope.model.value.push(item.value);
+
+ vm.editor.typeahead("val", "");
+
+ setDirty();
+ });
+ };
+
+ function keyDown($event) {
+ if ($event.keyCode == 13) {
+ var tt = vm.editor.data("ttTypeahead");
+ if (tt && tt.menu && tt.menu.isOpen() === true) {
+ var selection = tt.menu.getActiveSelectable() || tt.menu.getTopSelectable();
+ tt.menu.trigger("selectableClicked", selection);
+ $event.preventDefault();
+ }
+ }
+ };
+
+ function keyUp($event, $index) {
+ console.log("keyUp", $event.keyCode, $index);
+ if ($event.keyCode === 8 || $event.keyCode === 46) {
+ remove($index);
+ }
+ };
+
+ function remove($index) {
+ if (config.confirmRemoval === true) {
+ var keys = ["contentment_removeItemMessage", "general_remove", "general_cancel", "contentment_removeItemButton"];
+ localizationService.localizeMany(keys).then(function (data) {
+ overlayService.open({
+ title: data[1],
+ content: data[0],
+ closeButtonLabel: data[2],
+ submitButtonLabel: data[3],
+ submitButtonStyle: "danger",
+ submit: function () {
+ removeItem($index);
+ overlayService.close();
+ },
+ close: function () {
+ overlayService.close();
+ }
+ });
+ });
+ } else {
+ removeItem($index);
+ }
+ };
+
+ function removeItem($index) {
+
+ vm.items.splice($index, 1);
+
+ $scope.model.value.splice($index, 1);
+
+ setDirty();
+ };
+
+ function setDirty() {
+ if ($scope.propertyForm) {
+ $scope.propertyForm.$setDirty();
+ }
+ };
+
+ init();
+ }
+]);
diff --git a/src/Umbraco.Community.Contentment/DataEditors/TextInput/TextInputConfigurationEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/TextInput/TextInputConfigurationEditor.cs
index e013651b..8ffb59b0 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/TextInput/TextInputConfigurationEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/TextInput/TextInputConfigurationEditor.cs
@@ -21,7 +21,7 @@ public TextInputConfigurationEditor(ConfigurationEditorUtility utility)
{
_utility = utility;
- var dataSources = _utility.GetConfigurationEditorModels
+();
+ var dataSources = _utility.GetConfigurationEditorModels().ToList();
Fields.Add(new ConfigurationField
{
@@ -43,6 +43,7 @@ public TextInputConfigurationEditor(ConfigurationEditorUtility utility)
{ DisableSortingConfigurationField.DisableSorting, Constants.Values.True },
{ Constants.Conventions.ConfigurationFieldAliases.OverlayView, IOHelper.ResolveUrl(ConfigurationEditorDataEditor.DataEditorOverlayViewPath) },
{ EnableDevModeConfigurationField.EnableDevMode, Constants.Values.True },
+ { EnableFilterConfigurationField.EnableFilter, dataSources.Count > 10 ? Constants.Values.True : Constants.Values.False },
{ Constants.Conventions.ConfigurationFieldAliases.Items, dataSources },
});
@@ -75,10 +76,10 @@ public override IDictionary ToValueEditor(object configuration)
{
var config = base.ToValueEditor(configuration);
- if (config.TryGetValueAs(Constants.Conventions.ConfigurationFieldAliases.Items, out JArray array) && array.Count > 0 && array[0] is JObject item)
+ if (config.TryGetValueAs(Constants.Conventions.ConfigurationFieldAliases.Items, out JArray array) == true && array.Count > 0 && array[0] is JObject item)
{
// NOTE: Patches a breaking-change. I'd renamed `type` to become `key`.
- if (item.ContainsKey("key") == false && item.ContainsKey("type"))
+ if (item.ContainsKey("key") == false && item.ContainsKey("type") == true)
{
item.Add("key", item["type"]);
item.Remove("type");
diff --git a/src/Umbraco.Community.Contentment/Properties/VersionInfo.cs b/src/Umbraco.Community.Contentment/Properties/VersionInfo.cs
index f9eea8fd..0ebad52c 100644
--- a/src/Umbraco.Community.Contentment/Properties/VersionInfo.cs
+++ b/src/Umbraco.Community.Contentment/Properties/VersionInfo.cs
@@ -1,5 +1,5 @@
using System.Reflection;
-[assembly: AssemblyVersion("1.2")]
-[assembly: AssemblyFileVersion("1.2.1")]
-[assembly: AssemblyInformationalVersion("1.2.1-develop")]
+[assembly: AssemblyVersion("1.3")]
+[assembly: AssemblyFileVersion("1.3.0")]
+[assembly: AssemblyInformationalVersion("1.3.0")]
diff --git a/src/Umbraco.Community.Contentment/Telemetry/ContentmentTelemetryComponent.cs b/src/Umbraco.Community.Contentment/Telemetry/ContentmentTelemetryComponent.cs
index 3d4e26c4..0fe9aeca 100644
--- a/src/Umbraco.Community.Contentment/Telemetry/ContentmentTelemetryComponent.cs
+++ b/src/Umbraco.Community.Contentment/Telemetry/ContentmentTelemetryComponent.cs
@@ -4,13 +4,16 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using System;
+using System.Collections.Generic;
using System.Net;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using ClientDependency.Core;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using Umbraco.Community.Contentment.Configuration;
+using Umbraco.Community.Contentment.DataEditors;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
@@ -56,6 +59,51 @@ private void DataTypeService_Saved(IDataTypeService sender, SaveEventArgs();
+
+ if (entity.Configuration is Dictionary config)
+ {
+ void AddConfigurationEditorKey(string alias)
+ {
+ if (config.ContainsKey(alias) == true &&
+ config.TryGetValueAs(alias, out JArray array) == true &&
+ array.Count > 0 &&
+ array[0] is JObject item &&
+ item.ContainsKey("key") == true)
+ {
+ var key = item.Value("key");
+
+ if (key.InvariantStartsWith(Constants.Internals.ProjectNamespace) == true && key.Length > 73)
+ {
+ // Strips off the namespace and assembly.
+ // e.g. "Umbraco.Community.Contentment.DataEditors.[DataSourceName], Umbraco.Community.Contentment"
+ key = key.Substring(42, key.Length - 73);
+ }
+
+ dataTypeConfig.Add(alias, key);
+ }
+ };
+
+ switch (entity.EditorAlias)
+ {
+ case DataListDataEditor.DataEditorAlias:
+ AddConfigurationEditorKey(DataListConfigurationEditor.DataSource);
+ AddConfigurationEditorKey(DataListConfigurationEditor.ListEditor);
+ break;
+
+ case ContentBlocksDataEditor.DataEditorAlias:
+ AddConfigurationEditorKey(ContentBlocksConfigurationEditor.DisplayMode);
+ break;
+
+ case TextInputDataEditor.DataEditorAlias:
+ AddConfigurationEditorKey(Constants.Conventions.ConfigurationFieldAliases.Items);
+ break;
+
+ default:
+ break;
+ }
+ }
+
// TODO: [LK] After v8.10.0 bump, switch this to use `IUmbracoSettingsSection.BackOffice.Id`.
var umbracoId = _umbracoContextAccessor.UmbracoContext?.HttpContext?.Server != null
? new Guid(_umbracoContextAccessor.UmbracoContext.HttpContext.Server.MachineName.GenerateMd5())
@@ -69,6 +117,7 @@ private void DataTypeService_Saved(IDataTypeService sender, SaveEventArgs
+
+
@@ -311,6 +313,7 @@
+
@@ -321,6 +324,7 @@
+
@@ -415,6 +419,9 @@
+
+
+
@@ -431,6 +438,9 @@
+
+
+
@@ -445,6 +455,9 @@
+
+
+
diff --git a/src/Umbraco.Community.Contentment/Web/Controllers/EnumDataListSourceApiController.cs b/src/Umbraco.Community.Contentment/Web/Controllers/EnumDataListSourceApiController.cs
index b3b6dfb2..fe8c3872 100644
--- a/src/Umbraco.Community.Contentment/Web/Controllers/EnumDataListSourceApiController.cs
+++ b/src/Umbraco.Community.Contentment/Web/Controllers/EnumDataListSourceApiController.cs
@@ -33,7 +33,7 @@ public IEnumerable GetAssemblies()
{
foreach (var assembly in assemblies)
{
- if (options.ContainsKey(assembly.FullName) || assembly.IsDynamic)
+ if (options.ContainsKey(assembly.FullName) == true || assembly.IsDynamic == true)
continue;
var hasEnums = false;
@@ -41,7 +41,7 @@ public IEnumerable GetAssemblies()
{
foreach (var exportedType in assembly.ExportedTypes)
{
- if (exportedType.IsEnum)
+ if (exportedType.IsEnum == true)
{
hasEnums = true;
break;
@@ -52,7 +52,7 @@ public IEnumerable GetAssemblies()
if (hasEnums == false)
continue;
- if (assembly.FullName.StartsWith(App_Code) && options.ContainsKey(App_Code) == false)
+ if (assembly.FullName.StartsWith(App_Code) == true && options.ContainsKey(App_Code) == false)
{
options.Add(App_Code, new DataListItem { Name = App_Code, Value = App_Code });
}
diff --git a/src/Umbraco.Community.Contentment/Web/Serialization/PublishedContentContractResolver.cs b/src/Umbraco.Community.Contentment/Web/Serialization/PublishedContentContractResolver.cs
index 33175758..67776567 100644
--- a/src/Umbraco.Community.Contentment/Web/Serialization/PublishedContentContractResolver.cs
+++ b/src/Umbraco.Community.Contentment/Web/Serialization/PublishedContentContractResolver.cs
@@ -2,16 +2,17 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-/* This code has been derived from Rasmus Fjord's code, supplied on an
- * Our Umbraco forum post:
+/* This code was originally based on code by Rasmus Fjord,
+ * supplied on an Our Umbraco forum post:
* https://our.umbraco.com/forum/umbraco-8/98381-serializing-an-publishedcontentmodel-modelsbuilder-model-in-v8#comment-310148
* From searching GitHub, I also found René Pjengaard's code:
* https://github.com/rpjengaard/merchelloshop/blob/master/dev/code/Json/Resolvers/PublishedContentContractResolver.cs
* It is unknown (to me) who the original author is, and how the code
- * has been licensed. I'll assume it's available under MIT license. */
+ * has been licensed. My assumption is under the MIT license. */
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
@@ -20,26 +21,43 @@
namespace Umbraco.Community.Contentment.Web.Serialization
{
- public sealed class PublishedContentContractResolver : CamelCasePropertyNamesContractResolver
+ public sealed class PublishedContentContractResolver : DefaultContractResolver
{
public static readonly PublishedContentContractResolver Instance = new PublishedContentContractResolver();
- private readonly Dictionary _converterLookup;
+ private readonly Dictionary _converterLookup;
+ private readonly HashSet _ignoreFromCustom;
+ private readonly HashSet _ignoreFromElement;
private readonly HashSet _ignoreFromContent;
private readonly HashSet _ignoreFromProperty;
+ private readonly HashSet _systemProperties;
public PublishedContentContractResolver()
+ : base()
{
- _converterLookup = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ NamingStrategy = new CamelCaseNamingStrategy
{
- { nameof(IPublishedContent.ItemType), new StringEnumConverter() },
+ OverrideSpecifiedNames = true,
+ ProcessDictionaryKeys = true,
};
- _ignoreFromContent = new HashSet(StringComparer.OrdinalIgnoreCase)
+ _converterLookup = new Dictionary()
+ {
+ { typeof(PublishedItemType), new StringEnumConverter() }
+ };
+
+ _ignoreFromCustom = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ _ignoreFromElement = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ nameof(IPublishedElement.ContentType),
+ nameof(IPublishedElement.Properties),
+ };
+
+ _ignoreFromContent = new HashSet(_ignoreFromElement, StringComparer.OrdinalIgnoreCase)
{
nameof(IPublishedContent.Children),
nameof(IPublishedContent.ChildrenForAllCultures),
- nameof(IPublishedContent.ContentType),
nameof(IPublishedContent.CreatorId),
nameof(IPublishedContent.Cultures),
nameof(IPublishedContent.Parent),
@@ -51,24 +69,80 @@ public PublishedContentContractResolver()
{
nameof(IPublishedProperty.PropertyType),
};
+
+ _systemProperties = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ nameof(IPublishedContent.CreateDate),
+ nameof(IPublishedContent.CreatorName),
+ nameof(IPublishedContent.Id),
+ nameof(IPublishedContent.ItemType),
+ nameof(IPublishedElement.Key),
+ nameof(IPublishedContent.Level),
+ nameof(IPublishedContent.Name),
+ nameof(IPublishedContent.Path),
+ nameof(IPublishedContent.SortOrder),
+ nameof(IPublishedContent.UpdateDate),
+ nameof(IPublishedContent.Url),
+ nameof(IPublishedContent.UrlSegment),
+ nameof(IPublishedContent.WriterName),
+ };
+ }
+
+ public string[] PropertiesToIgnore
+ {
+ set => _ignoreFromCustom.UnionWith(value);
+ }
+
+ public Dictionary PropertyTypeConverters
+ {
+ set
+ {
+ foreach (var item in value)
+ {
+ if (_converterLookup.ContainsKey(item.Key) == false)
+ {
+ _converterLookup.Add(item.Key, item.Value);
+ }
+ }
+ }
+ }
+
+ public bool PrefixSystemPropertyNamesWithUnderscore { private get; set; }
+
+ protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
+ {
+ return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
- if (typeof(IPublishedContent).IsAssignableFrom(member.DeclaringType))
+ if (_ignoreFromCustom.Contains(member.Name) == true)
{
- property.ShouldSerialize = _ => _ignoreFromContent.Contains(property.PropertyName) == false;
+ property.ShouldSerialize = _ => false;
}
- else if (typeof(IPublishedProperty).IsAssignableFrom(member.DeclaringType))
+ else if (typeof(IPublishedContent).IsAssignableFrom(member.DeclaringType) == true)
+ {
+ property.ShouldSerialize = _ => _ignoreFromContent.Contains(member.Name) == false;
+ }
+ else if (typeof(IPublishedElement).IsAssignableFrom(member.DeclaringType) == true)
+ {
+ property.ShouldSerialize = _ => _ignoreFromElement.Contains(member.Name) == false;
+ }
+ else if (typeof(IPublishedProperty).IsAssignableFrom(member.DeclaringType) == true)
+ {
+ property.ShouldSerialize = _ => _ignoreFromProperty.Contains(member.Name) == false;
+ }
+
+ if (_converterLookup.ContainsKey(property.PropertyType) == true)
{
- property.ShouldSerialize = _ => _ignoreFromProperty.Contains(property.PropertyName) == false;
+ property.Converter = _converterLookup[property.PropertyType];
}
- if (_converterLookup.ContainsKey(property.PropertyName))
+ if (PrefixSystemPropertyNamesWithUnderscore == true && _systemProperties.Contains(member.Name) == true)
{
- property.Converter = _converterLookup[property.PropertyName];
+ property.PropertyName = "_" + property.PropertyName;
}
return property;
diff --git a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/backoffice/contentment/index.html b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/backoffice/contentment/index.html
index cda59ab8..219716a5 100644
--- a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/backoffice/contentment/index.html
+++ b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/backoffice/contentment/index.html
@@ -39,7 +39,7 @@
Tree dashboard
+
+
Telemetry
By default, the package sends telemetry data about which property-editors are being used (from Contentment only). For more details about the data being captured and transparency on the analysis, please visit leekelleher.com/umbraco/contentment/telemetry.
diff --git a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/da.xml b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/da.xml
index e6f693f7..4702eb89 100644
--- a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/da.xml
+++ b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/da.xml
@@ -24,6 +24,11 @@
Vælg og konfigurer en datakilde
Vælg og konfigurer en liste
+ Vælg og konfigurer en datakilde og en listeeditor, tak.
+ Vælg og konfigurer en datakilde, tak.
+ Vælg og konfigurer en liste, tak.
+ Datakilden returnerede ingen elementer til forhåndsvisning.
+ Indtast nøgleord (tryk på Enter efter hvert nøgleord)...
diff --git a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/en.xml b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/en.xml
index 903036df..08152da5 100644
--- a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/en.xml
+++ b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/en.xml
@@ -24,6 +24,11 @@
Select and configure a data source
Select and configure a list editor
+ Please select and configure a data source and list editor.
+ Please select and configure a data source.
+ Please select and configure a list editor.
+ The data source returned no items to preview.
+ Type to select an item...
diff --git a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/pt-br.xml b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/pt-br.xml
index b749c22a..8aa8a340 100644
--- a/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/pt-br.xml
+++ b/src/Umbraco.Community.Contentment/Web/UI/App_Plugins/Contentment/lang/pt-br.xml
@@ -24,6 +24,11 @@
Selecionar e configurar a origem dos dados
Selecionar e configurar o editor de lista
+ Selecionar e configurar a origem dos dados e o editor de lista, por favor.
+ Selecionar e configurar a origem dos dados, por favor.
+ Selecionar e configurar o editor de lista, por favor.
+ A fonte de dados não retornou nenhum item para visualização.
+ Digite para selecionar um item...
diff --git a/src/Umbraco.Community.Contentment/Web/UI/backoffice-ui-shim.css b/src/Umbraco.Community.Contentment/Web/UI/backoffice-ui-shim.css
index 0555aa78..c4d74d22 100644
--- a/src/Umbraco.Community.Contentment/Web/UI/backoffice-ui-shim.css
+++ b/src/Umbraco.Community.Contentment/Web/UI/backoffice-ui-shim.css
@@ -22,7 +22,7 @@
[class].icon-umbraco {
background-image: url(/umbraco/assets/img/application/logo_black.png);
background-repeat: no-repeat;
- background-size: cover;
+ background-size: contain;
}
.umb-action-link .icon-umbraco {
diff --git a/src/Umbraco.Community.Contentment/Web/UI/backoffice.js b/src/Umbraco.Community.Contentment/Web/UI/backoffice.js
index 84fb3d3d..41c5eb03 100644
--- a/src/Umbraco.Community.Contentment/Web/UI/backoffice.js
+++ b/src/Umbraco.Community.Contentment/Web/UI/backoffice.js
@@ -63,7 +63,26 @@ angular.module("umbraco").controller("Umbraco.Community.Contentment.Tree.Control
};
vm.vote = function (x) {
- vm.nggyu = x == false;
+ if (x === false && !vm.nggyu) {
+
+ vm.nggyu = true;
+
+ // Kudos to Mathieu 'p01' Henri for this snippet:
+ // Music SoftSynth https://gist.github.com/p01/1285255
+ var softSynth = function (f) { return eval("for(var t=0,S='RIFF_oO_WAVEfmt " + atob("EAAAAAEAAQBAHwAAQB8AAAEACAA") + "data';++t<3e5;)S+=String.fromCharCode(" + f + ")"); };
+ var formula = "(t<<3)*[8/9,1,9/8,6/5,4/3,3/2,0][[0xd2d2c8,0xce4088,0xca32c8,0x8e4009][t>>14&3]>>(0x3dbe4688>>((t>>10&15)>9?18:t>>10&15)*3&7)*3&7]&255";
+ vm.audio = new Audio("data:audio/wav;base64," + btoa(softSynth(formula)));
+ vm.audio.play();
+
+ } else {
+
+ vm.nggyu = false;
+
+ if (vm.audio) {
+ vm.audio.pause();
+ }
+
+ }
};
vm.csharp = "csharp";
-
+
@@ -104,6 +104,8 @@
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.js b/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.js
new file mode 100644
index 00000000..7c49d787
--- /dev/null
+++ b/src/Umbraco.Community.Contentment/DataEditors/Buttons/buttons.js
@@ -0,0 +1,92 @@
+/* Copyright © 2020 Lee Kelleher.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+angular.module("umbraco").controller("Umbraco.Community.Contentment.DataEditors.Buttons.Controller", [
+ "$scope",
+ function ($scope) {
+
+ // console.log("buttons.model", $scope.model);
+
+ var defaultConfig = {
+ defaultIcon: "icon-science",
+ defaultValue: [],
+ items: [],
+ enableMultiple: 0,
+ labelStyle: "both",
+ size: "m",
+ };
+ var config = Object.assign({}, defaultConfig, $scope.model.config);
+
+ var vm = this;
+
+ function init() {
+ $scope.model.value = $scope.model.value || config.defaultValue;
+
+ if (Array.isArray($scope.model.value) === false) {
+ $scope.model.value = [$scope.model.value];
+ }
+
+ vm.multiple = Object.toBoolean(config.enableMultiple);
+
+ if (vm.multiple === false && $scope.model.value.length > 0) {
+ $scope.model.value.splice(1);
+ }
+
+ vm.items = config.items.slice();
+
+ vm.hideIcon = config.labelStyle === 'text';
+ vm.hideName = config.labelStyle === 'icon';
+
+ vm.uniqueId = $scope.model.hasOwnProperty("dataTypeKey")
+ ? [$scope.model.alias, $scope.model.dataTypeKey.substring(0, 8)].join("-")
+ : $scope.model.alias;
+
+ var sizes = {
+ "s": "small",
+ "m": "medium",
+ "l": "large",
+ };
+
+ vm.size = config.size;
+
+ vm.defaultIcon = config.defaultIcon;
+ vm.iconExtras = sizes[config.size] + (vm.hideName === false ? " mr2 " : " mr0 ");
+
+ vm.items.forEach(function (item) {
+ item.selected = $scope.model.value.indexOf(item.value) > -1;
+ });
+
+ vm.select = select;
+ };
+
+ function select(item) {
+
+ item.selected = item.selected === false;
+ $scope.model.value = [];
+
+ vm.items.forEach(function (x) {
+
+ if (vm.multiple === false) {
+ x.selected = x.value === item.value;
+ }
+
+ if (x.selected) {
+ $scope.model.value.push(x.value);
+ }
+
+ });
+
+ setDirty();
+ };
+
+ function setDirty() {
+ if ($scope.propertyForm) {
+ $scope.propertyForm.$setDirty();
+ }
+ };
+
+ init();
+ }
+]);
diff --git a/src/Umbraco.Community.Contentment/DataEditors/Bytes/BytesConfigurationEditor.cs b/src/Umbraco.Community.Contentment/DataEditors/Bytes/BytesConfigurationEditor.cs
index b561766c..8af6f7fa 100644
--- a/src/Umbraco.Community.Contentment/DataEditors/Bytes/BytesConfigurationEditor.cs
+++ b/src/Umbraco.Community.Contentment/DataEditors/Bytes/BytesConfigurationEditor.cs
@@ -57,7 +57,7 @@ public override IDictionary