("content");
81 | ```
82 |
83 | If you have the raw JSON string, you can parse it like:
84 |
85 | ```C#
86 | GridDataModel grid = GridDataModel.Deserialize(json);
87 | ```
88 |
89 | But you can also just call an extension method to get the grid model:
90 |
91 | ```C#
92 | GridDataModel grid = Model.Content.GetGridModel("content");
93 | ```
94 |
95 | The benefit of the extension method is that it will always return an instance of `GridDataModel` - even if the property doesn't exists or doesn't have a value, so you don't have to check whether the returned value is `null`. However if you need it, you can use the `IsValid` property to validate that the model is valid (eg. not empty).
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | ## Indexing with Examine
104 |
105 | As of `v2.0`, the `GridDataModel` contains a `GetSearchableText` method that will return a textual representation of the entire grid model - see the example below:
106 |
107 | ```C#
108 | @using Skybrud.Umbraco.GridData
109 | @using Skybrud.Umbraco.GridData.Extensions
110 | @inherits UmbracoTemplatePage
111 | @{
112 |
113 | GridDataModel grid = Model.Content.GetGridModel("content");
114 |
115 | @grid.GetSearchableText()
116 |
117 | }
118 | ```
119 |
120 | The `GetSearchableText` method works by traversing all the controls of the grid, and calling a similar `GetSearchableText` method on each control. The end result will then be a string combined of the returned values from all the controls.
121 |
122 | This of course requires that each control (or the model of it's value, really) can provide a textual representation of it's value.
123 |
124 | If you need further control of the indexing, you can have a look at this example Gist:
125 |
126 | * [Gist: Indexing the Umbraco Grid.md](https://gist.github.com/abjerner/bdd89e0788d274ec5a33)
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | ## Rendering the grid
135 |
136 | The package supports a number of different ways to render the grid. If we start out with the entire grid model, you can do something like (`Fanoe` is the framework/view that should be used for rendering the grid):
137 |
138 | ```C#
139 | @using Skybrud.Umbraco.GridData
140 | @using Skybrud.Umbraco.GridData.Extensions
141 | @inherits UmbracoTemplatePage
142 | @{
143 |
144 | GridDataModel grid = Model.Content.GetGridModel("content");
145 |
146 | @Html.GetTypedGridHtml(grid, "Fanoe")
147 |
148 | }
149 | ```
150 |
151 | This works by first getting the grid value, and then rendering the model into the current view. This can also be done in a single line instead (`Model.Content` as specified for the first parameter is an instance of `IPublishedContent`):
152 |
153 | ```C#
154 | @using Skybrud.Umbraco.GridData.Extensions
155 | @inherits UmbracoTemplatePage
156 |
157 | @Html.GetTypedGridHtml(Model.Content, "content", "Fanoe")
158 | ```
159 |
160 | Since both examples specifies the `Fanoe` view, the package will look for a partial view located at `~/Views/Partials/TypedGrid/Fanoe.cshtml` and with an instance of `GridDataModel` as the model. You can find an example of this partial view at the link below:
161 |
162 | https://github.com/abjerner/UmbracoGridDataDemo/blob/master/dev/web/Views/Partials/TypedGrid/Fanoe.cshtml
163 |
164 | You can also have a look at an example partial view for rendering the individual rows of the grid:
165 |
166 | https://github.com/abjerner/UmbracoGridDataDemo/blob/master/dev/web/Views/Partials/TypedGrid/Rows/Default.cshtml
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | ## Extending the grid
175 |
176 | The package will only provide models for the grid editors thats comes with Umbraco by default (as well as the editors from the Fanoe starter kit), but it is also possible to create your own models for custom controls.
177 |
178 | This process might however be a bit complex, so I've written an article for [**Skrift.io**](http://skrift.io/) that describes this a bit further:
179 |
180 | http://skrift.io/articles/archive/strongly-typed-models-in-the-umbraco-grid/
181 |
--------------------------------------------------------------------------------
/debug.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | dotnet build src/Skybrud.Umbraco.GridData --configuration Debug /t:rebuild /t:pack -p:PackageOutputPath=c:\nuget\Umbraco13
--------------------------------------------------------------------------------
/release.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | dotnet build src/Skybrud.Umbraco.GridData --configuration Release /t:rebuild /t:pack -p:PackageOutputPath=../../releases/nuget
--------------------------------------------------------------------------------
/releases/nuget/Skybrud.Umbraco.GridData.13.0.0.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skybrud/Skybrud.Umbraco.GridData/24e85ae1fc414cc56ea5550da9e1073eeab0e6ac/releases/nuget/Skybrud.Umbraco.GridData.13.0.0.nupkg
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | # Version 1.0.7
2 |
3 | # Remove the line below if you want to inherit .editorconfig settings from higher directories
4 | root = true
5 |
6 | # C# files
7 | [*.cs]
8 |
9 | #### Core EditorConfig Options ####
10 |
11 | # Indentation and spacing
12 | indent_size = 4
13 | indent_style = space
14 | tab_width = 4
15 | trim_trailing_whitespace = true
16 |
17 | # New line preferences
18 | end_of_line = crlf
19 | insert_final_newline = false
20 |
21 | #### .NET Coding Conventions ####
22 |
23 | # Organize usings
24 | dotnet_separate_import_directive_groups = false
25 | dotnet_sort_system_directives_first = true
26 |
27 | # this. and Me. preferences
28 | dotnet_style_qualification_for_event = false:silent
29 | dotnet_style_qualification_for_field = false:silent
30 | dotnet_style_qualification_for_method = false:silent
31 | dotnet_style_qualification_for_property = false:silent
32 |
33 | # Language keywords vs BCL types preferences
34 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning
35 | dotnet_style_predefined_type_for_member_access = true:warning
36 |
37 | # Parentheses preferences
38 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
39 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
40 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
41 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
42 |
43 | # Modifier preferences
44 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
45 |
46 | # Expression-level preferences
47 | dotnet_style_coalesce_expression = true:suggestion
48 | dotnet_style_collection_initializer = true:suggestion
49 | dotnet_style_explicit_tuple_names = true:suggestion
50 | dotnet_style_null_propagation = true:suggestion
51 | dotnet_style_object_initializer = true:suggestion
52 | dotnet_style_prefer_auto_properties = true:silent
53 | dotnet_style_prefer_compound_assignment = true:suggestion
54 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
55 | dotnet_style_prefer_conditional_expression_over_return = true:silent
56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
57 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
58 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
59 |
60 | # Field preferences
61 | dotnet_style_readonly_field = true:suggestion
62 |
63 | # Parameter preferences
64 | dotnet_code_quality_unused_parameters = all:suggestion
65 |
66 | #### C# Coding Conventions ####
67 |
68 | # var preferences
69 | csharp_style_var_elsewhere = false:silent
70 | csharp_style_var_for_built_in_types = false:silent
71 | csharp_style_var_when_type_is_apparent = false:silent
72 |
73 | # Expression-bodied members
74 | csharp_style_expression_bodied_accessors = true:silent
75 | csharp_style_expression_bodied_constructors = false:silent
76 | csharp_style_expression_bodied_indexers = true:silent
77 | csharp_style_expression_bodied_lambdas = true:silent
78 | csharp_style_expression_bodied_local_functions = false:silent
79 | csharp_style_expression_bodied_methods = false:silent
80 | csharp_style_expression_bodied_operators = false:silent
81 | csharp_style_expression_bodied_properties = true:silent
82 |
83 | # Pattern matching preferences
84 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
85 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
86 | csharp_style_prefer_switch_expression = true:suggestion
87 |
88 | # Null-checking preferences
89 | csharp_style_conditional_delegate_call = true:suggestion
90 |
91 | # Modifier preferences
92 | csharp_prefer_static_local_function = true:suggestion
93 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
94 |
95 | # Code-block preferences
96 | csharp_prefer_braces = true:silent
97 | csharp_prefer_simple_using_statement = true:suggestion
98 |
99 | # Expression-level preferences
100 | csharp_prefer_simple_default_expression = true:suggestion
101 | csharp_style_deconstructed_variable_declaration = true:suggestion
102 | csharp_style_inlined_variable_declaration = true:suggestion
103 | csharp_style_pattern_local_over_anonymous_function = true:suggestion
104 | csharp_style_prefer_index_operator = true:suggestion
105 | csharp_style_prefer_range_operator = true:suggestion
106 | csharp_style_throw_expression = true:suggestion
107 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion
108 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent
109 |
110 | # 'using' directive preferences
111 | csharp_using_directive_placement = outside_namespace:warning
112 |
113 | #### C# Formatting Rules ####
114 |
115 | # New line preferences
116 | csharp_new_line_before_catch = false
117 | csharp_new_line_before_else = false
118 | csharp_new_line_before_finally = false
119 | csharp_new_line_before_members_in_anonymous_types = true
120 | csharp_new_line_before_members_in_object_initializers = true
121 | csharp_new_line_before_open_brace = none
122 | csharp_new_line_between_query_expression_clauses = true
123 |
124 | # Indentation preferences
125 | csharp_indent_block_contents = true
126 | csharp_indent_braces = false
127 | csharp_indent_case_contents = true
128 | csharp_indent_case_contents_when_block = true
129 | csharp_indent_labels = no_change
130 | csharp_indent_switch_labels = true
131 |
132 | # Space preferences
133 | csharp_space_after_cast = true
134 | csharp_space_after_colon_in_inheritance_clause = true
135 | csharp_space_after_comma = true
136 | csharp_space_after_dot = false
137 | csharp_space_after_keywords_in_control_flow_statements = true
138 | csharp_space_after_semicolon_in_for_statement = true
139 | csharp_space_around_binary_operators = before_and_after
140 | csharp_space_around_declaration_statements = false
141 | csharp_space_before_colon_in_inheritance_clause = true
142 | csharp_space_before_comma = false
143 | csharp_space_before_dot = false
144 | csharp_space_before_open_square_brackets = false
145 | csharp_space_before_semicolon_in_for_statement = false
146 | csharp_space_between_empty_square_brackets = false
147 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
148 | csharp_space_between_method_call_name_and_opening_parenthesis = false
149 | csharp_space_between_method_call_parameter_list_parentheses = false
150 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
151 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
152 | csharp_space_between_method_declaration_parameter_list_parentheses = false
153 | csharp_space_between_parentheses = false
154 | csharp_space_between_square_brackets = false
155 |
156 | # Wrapping preferences
157 | csharp_preserve_single_line_blocks = true
158 | csharp_preserve_single_line_statements = true
159 |
160 | #### Naming styles ####
161 |
162 | # Naming rules
163 |
164 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
165 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
166 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
167 |
168 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
169 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
170 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
171 |
172 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
173 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
174 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
175 |
176 | # Symbol specifications
177 |
178 | dotnet_naming_symbols.interface.applicable_kinds = interface
179 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
180 | dotnet_naming_symbols.interface.required_modifiers =
181 |
182 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
183 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
184 | dotnet_naming_symbols.types.required_modifiers =
185 |
186 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
187 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
188 | dotnet_naming_symbols.non_field_members.required_modifiers =
189 |
190 | # Naming styles
191 |
192 | dotnet_naming_style.pascal_case.required_prefix =
193 | dotnet_naming_style.pascal_case.required_suffix =
194 | dotnet_naming_style.pascal_case.word_separator =
195 | dotnet_naming_style.pascal_case.capitalization = pascal_case
196 |
197 | dotnet_naming_style.begins_with_i.required_prefix = I
198 | dotnet_naming_style.begins_with_i.required_suffix =
199 | dotnet_naming_style.begins_with_i.word_separator =
200 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
201 |
202 | # Define what we will treat as private fields
203 | dotnet_naming_symbols.private_fields.applicable_kinds = field
204 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private
205 |
206 | # Define rule that something must begin with an underscore and be in camel case
207 | dotnet_naming_style.require_underscore_prefix_and_camel_case.required_prefix = _
208 | dotnet_naming_style.require_underscore_prefix_and_camel_case.capitalization = camel_case
209 |
210 | # Apply our rule to private fields
211 | dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.symbols = private_fields
212 | dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.style = require_underscore_prefix_and_camel_case
213 | dotnet_naming_rule.private_fields_must_begin_with_underscore_and_be_in_camel_case.severity = warning
214 |
215 | # Prefer file-scoped namespace declarations
216 | csharp_style_namespace_declarations = file_scoped:warning
217 |
218 | # Don't prefer or suggest primary constructors
219 | csharp_style_prefer_primary_constructors = false
220 |
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31112.23
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Skybrud.Umbraco.GridData", "Skybrud.Umbraco.GridData\Skybrud.Umbraco.GridData.csproj", "{99DC24EA-4933-4BB9-B6A1-BF565E9E93C0}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {99DC24EA-4933-4BB9-B6A1-BF565E9E93C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {99DC24EA-4933-4BB9-B6A1-BF565E9E93C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {99DC24EA-4933-4BB9-B6A1-BF565E9E93C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {99DC24EA-4933-4BB9-B6A1-BF565E9E93C0}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {57B0500C-D959-4699-AB7D-481ACDEC9F0A}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Composers/GridComposer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Skybrud.Umbraco.GridData.Converters.Umbraco;
3 | using Skybrud.Umbraco.GridData.Factories;
4 | using Skybrud.Umbraco.GridData.Manifests;
5 | using Umbraco.Cms.Core.Composing;
6 | using Umbraco.Cms.Core.DependencyInjection;
7 | using Umbraco.Extensions;
8 |
9 | namespace Skybrud.Umbraco.GridData.Composers;
10 |
11 | internal class GridComposer : IComposer {
12 |
13 | public void Compose(IUmbracoBuilder builder) {
14 |
15 | builder.Services.AddSingleton();
16 | builder.Services.AddUnique();
17 |
18 | builder.GridConverters().Append();
19 |
20 | builder.ManifestFilters().Append();
21 |
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Composers/GridComposerExtensions.cs:
--------------------------------------------------------------------------------
1 | using Skybrud.Umbraco.GridData.Converters;
2 | using Umbraco.Cms.Core.DependencyInjection;
3 |
4 | // ReSharper disable InconsistentNaming
5 |
6 | namespace Skybrud.Umbraco.GridData.Composers;
7 |
8 | ///
9 | /// Static class with extension methods for composing the grid.
10 | ///
11 | public static class GridComposerExtensions {
12 |
13 | ///
14 | /// Returns the current instance of .
15 | ///
16 | /// The current .
17 | /// An instance of .
18 | public static GridConverterCollectionBuilder GridConverters(this IUmbracoBuilder builder) {
19 | return builder.WithCollectionBuilder();
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Converters/GridConverterBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Globalization;
4 | using System.IO;
5 | using Newtonsoft.Json.Linq;
6 | using Skybrud.Umbraco.GridData.Models;
7 | using Skybrud.Umbraco.GridData.Models.Config;
8 | using Skybrud.Umbraco.GridData.Models.Values;
9 | using Umbraco.Cms.Core.Models.PublishedContent;
10 |
11 | namespace Skybrud.Umbraco.GridData.Converters;
12 |
13 | ///
14 | /// Abstract base implementation of .
15 | ///
16 | public abstract class GridConverterBase : IGridConverter {
17 |
18 | ///
19 | /// Attemtps to get the type of the configuration object of the specified .
20 | ///
21 | /// The editor.
22 | /// When this method returns, holds an instance of representing the type if successful; otherwise, .
23 | /// if successful; otherwise, .
24 | public virtual bool TryGetConfigType(GridEditor editor, [NotNullWhen(true)] out Type? type) {
25 | type = null;
26 | return false;
27 | }
28 |
29 | ///
30 | /// Attempts to get the type of the value of the specified .
31 | ///
32 | /// The control.
33 | /// When this method returns, holds an instance of representing the type if successful; otherwise, .
34 | /// if successful; otherwise, .
35 | public virtual bool TryGetValueType(GridControl control, [NotNullWhen(true)] out Type? type) {
36 | type = null;
37 | return false;
38 | }
39 |
40 | ///
41 | /// Converts the specified into an instance of .
42 | ///
43 | /// A reference to the parent .
44 | /// The instance of representing the control value.
45 | /// The converted control value.
46 | public virtual bool TryConvertControlValue(GridControl control, JToken token, [NotNullWhen(true)] out IGridControlValue? value) {
47 | value = null;
48 | return false;
49 | }
50 |
51 | ///
52 | /// Converts the specified into an instance of .
53 | ///
54 | /// A reference to the parent .
55 | /// The instance of representing the editor config.
56 | /// The converted editor config.
57 | public virtual bool TryConvertEditorConfig(GridEditor editor, JToken token, [NotNullWhen(true)] out IGridEditorConfig? config) {
58 | config = null;
59 | return false;
60 | }
61 |
62 | ///
63 | /// Writes a string representation of to .
64 | ///
65 | /// The current grid context.
66 | /// The element.
67 | /// The writer.
68 | /// if successful; otherwise, .
69 | public virtual bool TryWriteSearchableText(GridContext context, IPublishedElement element, TextWriter writer) {
70 | return false;
71 | }
72 |
73 | ///
74 | /// Attempts to check whether the specified represents a valid grid control value.
75 | ///
76 | /// The value to check.
77 | /// When this method returns, holds a boolean value indicating whether is valid if successful; otherwise, .
78 | /// if successful; otherwise, .
79 | public virtual bool TryGetValid(IGridControlValue value, out bool result) {
80 | result = false;
81 | return false;
82 | }
83 |
84 | ///
85 | /// Attempts to check whether the specified represents a valid element.
86 | ///
87 | /// The element.
88 | /// When this method returns, holds a boolean value indicating whether is valid if successful; otherwise, .
89 | /// if successful; otherwise, .
90 | public virtual bool TryGetValid(IPublishedElement element, out bool result) {
91 | result = false;
92 | return false;
93 | }
94 |
95 | ///
96 | /// Returns whether is contained in (case insensitive).
97 | ///
98 | /// The source string.
99 | /// The value to search for.
100 | /// true if contains ; otherwise false.
101 | protected bool ContainsIgnoreCase(string source, string value) {
102 | if (string.IsNullOrWhiteSpace(source)) return false;
103 | if (string.IsNullOrWhiteSpace(value)) return false;
104 | return CultureInfo.InvariantCulture.CompareInfo.IndexOf(source, value, CompareOptions.IgnoreCase) >= 0;
105 | }
106 |
107 | ///
108 | /// Returns whether is equal (case insensitive).
109 | ///
110 | /// The source string.
111 | /// The value to search for.
112 | /// true if equal to ; otherwise false.
113 | protected bool EqualsIgnoreCase(string source, string value) {
114 | if (string.IsNullOrWhiteSpace(source)) return false;
115 | if (string.IsNullOrWhiteSpace(value)) return false;
116 | return source.Equals(value, StringComparison.InvariantCultureIgnoreCase);
117 | }
118 |
119 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Converters/GridConverterCollection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Umbraco.Cms.Core.Composing;
4 |
5 | namespace Skybrud.Umbraco.GridData.Converters;
6 |
7 | ///
8 | /// Collection of .
9 | ///
10 | public class GridConverterCollection : BuilderCollectionBase {
11 |
12 | ///
13 | /// Initializes a new converter collection based on the specified .
14 | ///
15 | /// The items to make up the collection.
16 | public GridConverterCollection(Func> items) : base(items) { }
17 |
18 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Converters/GridConverterCollectionBuilder.cs:
--------------------------------------------------------------------------------
1 | using Umbraco.Cms.Core.Composing;
2 |
3 | namespace Skybrud.Umbraco.GridData.Converters;
4 |
5 | ///
6 | public class GridConverterCollectionBuilder : OrderedCollectionBuilderBase {
7 |
8 | ///
9 | protected override GridConverterCollectionBuilder This => this;
10 |
11 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Converters/IGridConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.IO;
4 | using Newtonsoft.Json.Linq;
5 | using Skybrud.Umbraco.GridData.Models;
6 | using Skybrud.Umbraco.GridData.Models.Config;
7 | using Skybrud.Umbraco.GridData.Models.Values;
8 | using Umbraco.Cms.Core.Models.PublishedContent;
9 |
10 | namespace Skybrud.Umbraco.GridData.Converters;
11 |
12 | ///
13 | /// Interface describing a Grid converter.
14 | ///
15 | public interface IGridConverter {
16 |
17 | ///
18 | /// Attemtps to get the type of the configuration object of the specified .
19 | ///
20 | /// The editor.
21 | /// When this method returns, holds an instance of representing the type if successful; otherwise, .
22 | /// if successful; otherwise, .
23 | bool TryGetConfigType(GridEditor editor, [NotNullWhen(true)] out Type? type);
24 |
25 | ///
26 | /// Attempts to get the type of the value of the specified .
27 | ///
28 | /// The control.
29 | /// When this method returns, holds an instance of representing the type if successful; otherwise, .
30 | /// if successful; otherwise, .
31 | bool TryGetValueType(GridControl control, [NotNullWhen(true)] out Type? type);
32 |
33 | ///
34 | /// Attempts to convert the specified into an instance of .
35 | ///
36 | /// The parent control.
37 | /// The instance of representing the control value.
38 | /// The converted value.
39 | /// if successful; otherwise, .
40 | bool TryConvertControlValue(GridControl control, JToken token, [NotNullWhen(true)] out IGridControlValue? value);
41 |
42 | ///
43 | /// Attempts to convert the specified into an instance of .
44 | ///
45 | ///
46 | /// The instance of representing the editor config.
47 | /// The converted config.
48 | /// if successful; otherwise, .
49 | bool TryConvertEditorConfig(GridEditor editor, JToken token, [NotNullWhen(true)] out IGridEditorConfig? config);
50 |
51 | ///
52 | /// Attempts to write a string representation of to .
53 | ///
54 | /// The current grid context.
55 | /// The element.
56 | /// The writer.
57 | /// if successful; otherwise, .
58 | bool TryWriteSearchableText(GridContext context, IPublishedElement element, TextWriter writer);
59 |
60 | ///
61 | /// Attempts to check whether the specified represents a valid grid control value.
62 | ///
63 | /// The value to check.
64 | /// When this method returns, holds a boolean value indicating whether is valid if successful; otherwise, .
65 | /// if successful; otherwise, .
66 | bool TryGetValid(IGridControlValue value, out bool result);
67 |
68 | ///
69 | /// Attempts to check whether the specified represents a valid element.
70 | ///
71 | /// The element.
72 | /// When this method returns, holds a boolean value indicating whether is valid if successful; otherwise, .
73 | /// if successful; otherwise, .
74 | bool TryGetValid(IPublishedElement element, out bool result);
75 |
76 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Converters/Umbraco/UmbracoGridConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using Newtonsoft.Json.Linq;
4 | using Skybrud.Umbraco.GridData.Models;
5 | using Skybrud.Umbraco.GridData.Models.Config;
6 | using Skybrud.Umbraco.GridData.Models.Values;
7 | using Umbraco.Cms.Core.Web;
8 |
9 | #pragma warning disable CS1591
10 |
11 | namespace Skybrud.Umbraco.GridData.Converters.Umbraco;
12 |
13 | ///
14 | /// Converter for handling the default editors (and their values and configs) of Umbraco.
15 | ///
16 | public class UmbracoGridConverter : GridConverterBase {
17 |
18 | private readonly IUmbracoContextAccessor _umbracoContextAccessor;
19 |
20 | public UmbracoGridConverter(IUmbracoContextAccessor umbracoContextAccessor) {
21 | _umbracoContextAccessor = umbracoContextAccessor;
22 | }
23 |
24 | public override bool TryGetConfigType(GridEditor editor, [NotNullWhen(true)] out Type? type) {
25 |
26 | type = null;
27 |
28 | if (IsMediaEditor(editor)) {
29 | type = typeof(GridEditorMediaConfig);
30 | } else if (IsTextStringEditor(editor)) {
31 | type = typeof(GridEditorTextConfig);
32 | }
33 |
34 | return type != null;
35 |
36 | }
37 |
38 | public override bool TryGetValueType(GridControl control, [NotNullWhen(true)] out Type? type) {
39 |
40 | type = null;
41 |
42 | if (IsEmbedEditor(control.Editor)) {
43 | type = typeof(GridControlEmbedValue);
44 | } else if (IsMacroEditor(control.Editor)) {
45 | type = typeof(GridControlMacroValue);
46 | } else if (IsMediaEditor(control.Editor)) {
47 | type = typeof(GridControlMediaValue);
48 | } else if (IsRichTextEditor(control.Editor)) {
49 | type = typeof(GridControlRichTextValue);
50 | } else if (IsTextStringEditor(control.Editor)) {
51 | type = typeof(GridControlTextValue);
52 | }
53 |
54 | return type != null;
55 |
56 | }
57 |
58 | ///
59 | /// Converts the specified into an instance of .
60 | ///
61 | /// The parent control.
62 | /// The instance of representing the control value.
63 | /// The converted value.
64 | public override bool TryConvertControlValue(GridControl control, JToken token, [NotNullWhen(true)] out IGridControlValue? value) {
65 |
66 | value = null;
67 |
68 | if (IsEmbedEditor(control.Editor)) {
69 | value = new GridControlEmbedValue(control);
70 | } else if (IsMacroEditor(control.Editor)) {
71 | value = new GridControlMacroValue(control);
72 | } else if (IsMediaEditor(control.Editor)) {
73 | value = ParseGridControlMediaValue(control);
74 | } else if (IsRichTextEditor(control.Editor)) {
75 | value = new GridControlRichTextValue(control);
76 | } else if (IsTextStringEditor(control.Editor)) {
77 | value = new GridControlTextValue(control);
78 | }
79 |
80 | return value != null;
81 |
82 | }
83 |
84 | protected virtual IGridControlValue ParseGridControlMediaValue(GridControl control) {
85 |
86 | GridControlMediaValue value = new(control);
87 |
88 | if (value.Id > 0 && _umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? context)) {
89 | value.PublishedImage = context.Media?.GetById(value.Id);
90 | }
91 |
92 | return value;
93 |
94 | }
95 |
96 | ///
97 | /// Converts the specified into an instance of .
98 | ///
99 | ///
100 | /// The instance of representing the editor config.
101 | /// The converted config.
102 | public override bool TryConvertEditorConfig(GridEditor editor, JToken token, [NotNullWhen(true)] out IGridEditorConfig? config) {
103 |
104 | config = null;
105 |
106 | if (IsMediaEditor(editor)) {
107 | config = new GridEditorMediaConfig((JObject) token, editor);
108 | } else if (IsTextStringEditor(editor)) {
109 | config = new GridEditorTextConfig((JObject) token, editor);
110 | }
111 |
112 | return config != null;
113 |
114 | }
115 |
116 | protected static bool IsEmbedEditor(GridEditor? editor) {
117 | return editor?.View == "embed";
118 | }
119 |
120 | protected static bool IsTextStringEditor(GridEditor? editor) {
121 | return editor?.View == "textstring";
122 | }
123 |
124 | protected static bool IsMediaEditor(GridEditor? editor) {
125 | return editor?.View == "media";
126 | }
127 |
128 | protected static bool IsMacroEditor(GridEditor? editor) {
129 | return editor?.View == "macro";
130 | }
131 |
132 | protected static bool IsRichTextEditor(GridEditor? editor) {
133 | return editor?.View == "rte";
134 | }
135 |
136 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Extensions/TypedGridExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using Skybrud.Umbraco.GridData.Models;
2 | using Umbraco.Cms.Core.Models.PublishedContent;
3 | using Umbraco.Extensions;
4 |
5 | namespace Skybrud.Umbraco.GridData.Extensions;
6 |
7 | ///
8 | /// Class holding various extension methods for using the typed Grid.
9 | ///
10 | public static class TypedGridExtensionMethods {
11 |
12 | #region Constants
13 |
14 | ///
15 | /// Gets the default framework for rendering the Grid.
16 | ///
17 | public const string DefaultFramework = "bootstrap3";
18 |
19 | #endregion
20 |
21 | #region Static methods
22 |
23 | ///
24 | /// Returns a instance representing the value of the property with the specified
25 | /// . If the property doesn't exist, or it's value doesn't match a
26 | /// instance, a instance representing an empty grid
27 | /// model is returned instead.
28 | ///
29 | /// The parent content item.
30 | /// The alias of the property.
31 | public static GridDataModel GetGridModel(this IPublishedContent? content, string propertyAlias) {
32 | return content?.Value(propertyAlias) ?? GridDataModel.GetEmptyModel();
33 | }
34 |
35 | ///
36 | /// Returns a instance representing the value of the property with the specified
37 | /// . If the property doesn't exist, or it's value doesn't match a
38 | /// instance, is returned instead.
39 | ///
40 | /// The parent content item.
41 | /// The alias of the property.
42 | public static GridDataModel? GetGridModelorNull(this IPublishedContent? content, string propertyAlias) {
43 | return content?.Value(propertyAlias);
44 | }
45 |
46 | ///
47 | /// Attempts to get a instance from the property with the specified .
48 | ///
49 | /// The parent content item.
50 | /// The alias of the property.
51 | /// When this method returns, holds the instance if successful; otherwise, .
52 | /// if successful; otherwise, .
53 | public static bool TryGetGridModel(this IPublishedContent? content, string propertyAlias, out GridDataModel? result) {
54 | result = content?.Value(propertyAlias);
55 | return result != null;
56 | }
57 |
58 | #endregion
59 |
60 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Factories/DefaultGridFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Microsoft.Extensions.Logging;
4 | using Newtonsoft.Json.Linq;
5 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
6 | using Skybrud.Umbraco.GridData.Converters;
7 | using Skybrud.Umbraco.GridData.Models;
8 | using Skybrud.Umbraco.GridData.Models.Values;
9 | using Umbraco.Cms.Core.Configuration.Grid;
10 | using Umbraco.Cms.Core.Models.PublishedContent;
11 | using IGridEditorConfig = Skybrud.Umbraco.GridData.Models.Config.IGridEditorConfig;
12 |
13 | #pragma warning disable CS1591
14 |
15 | namespace Skybrud.Umbraco.GridData.Factories;
16 |
17 | public class DefaultGridFactory : IGridFactory {
18 |
19 | private readonly ILogger _logger;
20 | private readonly IGridConfig _gridConfig;
21 | private readonly GridConverterCollection _converters;
22 |
23 | #region Constructors
24 |
25 | public DefaultGridFactory(ILogger logger, IGridConfig gridConfig, GridConverterCollection converters) {
26 | _logger = logger;
27 | _gridConfig = gridConfig;
28 | _converters = converters;
29 | }
30 |
31 | #endregion
32 |
33 | #region Member methods
34 |
35 | ///
36 | public virtual GridDataModel CreateGridModel(IPublishedElement owner, IPublishedPropertyType propertyType, JObject json, bool preview) {
37 | return new GridDataModel(owner, propertyType, json, this);
38 | }
39 |
40 | ///
41 | public virtual GridSection CreateGridSection(JObject json, GridDataModel grid) {
42 | return new GridSection(json, grid, this);
43 | }
44 |
45 | ///
46 | public virtual GridRow CreateGridRow(JObject json, GridSection section) {
47 | return new GridRow(json, section, this);
48 | }
49 |
50 | ///
51 | public virtual GridArea CreateGridArea(JObject json, GridRow row) {
52 | return new GridArea(json, row, this);
53 | }
54 |
55 | ///
56 | public virtual GridControl CreateGridControl(JObject json, GridArea area) {
57 |
58 | // The saved JSON for the editor only contains the alias of the editor as other information may change over
59 | // time. As a result of this, we need to inject a new editor object into the JSON.
60 | ReplaceEditorObjectFromConfig(json);
61 |
62 | // Parse the Grid editor (undelrying type may be generic ... or not)
63 | GridEditor editor = json.GetObject("editor", CreateGridEditor)!;
64 |
65 | // Initialize a new Grid control
66 | GridControl control = new(json, area) {
67 | // Make sure to set the editor before we parse the control value
68 | Editor = editor
69 | };
70 |
71 | // Parse the control value
72 | control.Value = ParseGridControlValue(control);
73 |
74 | // Get the type of the editor config (it may not have a config)
75 | Type? configType = control.Editor.Config?.GetType();
76 |
77 | // Determine the value type
78 | Type? valueType = null;
79 | foreach (IGridConverter converter in _converters) {
80 | if (converter.TryGetValueType(control, out valueType)) break;
81 | }
82 |
83 | // If no converters specify a value type, we just return the control right away
84 | if (valueType == null) return control;
85 |
86 | // If the editor doesn't have a configuration, we can create a new generic type from just the value type.
87 | // If we both have a value type and config type, we create a new generic type from both types
88 | if (configType == null) {
89 | Type genericType = typeof(GridControl<>).MakeGenericType(valueType);
90 | control = (GridControl) Activator.CreateInstance(genericType, control)!;
91 | } else {
92 | Type genericType = typeof(GridControl<,>).MakeGenericType(valueType, configType);
93 | control = (GridControl) Activator.CreateInstance(genericType, control, editor)!;
94 | }
95 |
96 | // Return the control
97 | return control;
98 |
99 | }
100 |
101 | ///
102 | public virtual GridEditor CreateGridEditor(JObject json) {
103 |
104 | Type? configType = null;
105 |
106 | // Initialize a new Grid editor
107 | GridEditor editor = new(json);
108 |
109 | foreach (var converter in _converters) {
110 |
111 | if (converter.TryGetConfigType(editor, out configType)) break;
112 |
113 | }
114 |
115 | if (configType != null) {
116 |
117 | Type genericType = typeof(GridEditor<>).MakeGenericType(configType);
118 |
119 | editor = (GridEditor) Activator.CreateInstance(genericType, editor)!;
120 |
121 | }
122 |
123 | // Parse the grid editor configuration
124 | editor.Config = ParseGridEditorConfig(editor);
125 |
126 | // Return the editor
127 | return editor;
128 |
129 | }
130 |
131 | protected virtual IGridControlValue? ParseGridControlValue(GridControl control) {
132 |
133 | // Parse the control value
134 | JToken? value = control.JObject.GetValue("value");
135 | if (value is null) return null;
136 |
137 | foreach (IGridConverter converter in _converters) {
138 | try {
139 | if (!converter.TryConvertControlValue(control, value, out IGridControlValue? converted)) continue;
140 | return converted;
141 | } catch (Exception ex) {
142 | _logger.LogError(ex, $"Converter of type {converter} failed for ConvertControlValue()");
143 | }
144 | }
145 |
146 | return null;
147 |
148 | }
149 |
150 | protected virtual IGridEditorConfig? ParseGridEditorConfig(GridEditor editor) {
151 |
152 | // Parse the editor configuration
153 | JToken? config = editor.JObject.GetValue("config");
154 | if (config is null) return null;
155 |
156 | foreach (IGridConverter converter in _converters) {
157 | try {
158 | if (!converter.TryConvertEditorConfig(editor, config, out IGridEditorConfig? converted)) continue;
159 | return converted;
160 | } catch (Exception ex) {
161 | _logger.LogError(ex, $"Converter of type {converter} failed for ConvertEditorConfig()");
162 | }
163 | }
164 |
165 | return null;
166 |
167 | }
168 |
169 | protected virtual void ReplaceEditorObjectFromConfig(JObject json) {
170 |
171 | // Get the "editor" object from the JSON
172 | JObject? editor = json.GetObject("editor");
173 | if (editor is null) return;
174 |
175 | // Get the alias of the editor
176 | string? alias = editor.GetString("alias");
177 |
178 | // Skip if we dont have an alias
179 | if (string.IsNullOrWhiteSpace(alias)) return;
180 |
181 | // Find the editor in the configuration
182 | var found = _gridConfig.EditorsConfig.Editors.FirstOrDefault(x => x.Alias == alias);
183 |
184 | // Skip if not found
185 | if (found == null) return;
186 |
187 | // Set a new editor object with the updated config
188 | json["editor"] = new JObject {
189 | {"name", found.Name},
190 | {"alias", found.Alias},
191 | {"view", found.View},
192 | {"render", found.Render},
193 | {"icon", found.Icon},
194 | {"config", JObject.FromObject(found.Config)}
195 | };
196 |
197 | }
198 |
199 | #endregion
200 |
201 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Factories/IGridFactory.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Linq;
2 | using Skybrud.Umbraco.GridData.Models;
3 | using Umbraco.Cms.Core.Models.PublishedContent;
4 |
5 | namespace Skybrud.Umbraco.GridData.Factories;
6 |
7 | ///
8 | /// Interface describing a factory for initializing the various parts of the Grid.
9 | ///
10 | public interface IGridFactory {
11 |
12 | ///
13 | /// Returns a new from the specified object.
14 | ///
15 | /// and may be specified if the grid value comes directly from a property value. If either aren't available, it's fine to specify null for both of them.
16 | ///
17 | /// An instance of representing the owner holding the grid value.
18 | /// An instance of representing the property holding the grid value.
19 | /// The instance of representing the grid model.
20 | ///
21 | /// An instance of representing the grid model.
22 | GridDataModel CreateGridModel(IPublishedElement owner, IPublishedPropertyType propertyType, JObject json, bool preview);
23 |
24 | ///
25 | /// Returns a new instance of from the specified object.
26 | ///
27 | /// The instance of representing the grid section.
28 | /// An instance of representing the parent grid model.
29 | /// An instance of representing the grid section.
30 | GridSection CreateGridSection(JObject json, GridDataModel grid);
31 |
32 | ///
33 | /// Creates a new based on the specified object.
34 | ///
35 | /// An instance of representing the row.
36 | /// A reference to the parent .
37 | /// An instance of .
38 | GridRow CreateGridRow(JObject json, GridSection section);
39 |
40 | ///
41 | /// Creates a new based on the specified object.
42 | ///
43 | /// An instance of representing the area.
44 | /// A reference to the parent .
45 | /// An instance of .
46 | GridArea CreateGridArea(JObject json, GridRow row);
47 |
48 | ///
49 | /// Creates a new based on the specified object.
50 | ///
51 | /// An instance of representing the control.
52 | /// A reference to the parent .
53 | /// An instance of .
54 | GridControl CreateGridControl(JObject json, GridArea area);
55 |
56 | ///
57 | /// Creates a new based on the specified object.
58 | ///
59 | /// An instance of representing the editor.
60 | /// An instance of .
61 | GridEditor CreateGridEditor(JObject json);
62 |
63 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/GridContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using Microsoft.Extensions.Logging;
5 | using Skybrud.Umbraco.GridData.Converters;
6 | using Umbraco.Cms.Core.Models.PublishedContent;
7 |
8 | namespace Skybrud.Umbraco.GridData;
9 |
10 | ///
11 | /// Singleton class used for configuring and using the grid.
12 | ///
13 | public class GridContext {
14 |
15 | private readonly ILogger _logger;
16 | private readonly GridConverterCollection _converterCollection;
17 |
18 | #region Constructors
19 |
20 | ///
21 | /// Initializes a new instance based on the specified dependencies.
22 | ///
23 | /// A reference to the current logger.
24 | /// A reference to the current .
25 | public GridContext(ILogger logger, GridConverterCollection converterCollection) {
26 | _logger = logger;
27 | _converterCollection = converterCollection;
28 | }
29 |
30 | #endregion
31 |
32 | #region Member methods
33 |
34 | ///
35 | /// Writes a string representation of to .
36 | ///
37 | /// The element.
38 | /// The writer.
39 | public virtual void WriteSearchableText(IPublishedElement element, TextWriter writer) {
40 | foreach (IGridConverter converter in _converterCollection) {
41 | try {
42 | if (converter.TryWriteSearchableText(this, element, writer)) return;
43 | } catch (Exception ex) {
44 | _logger.LogError(ex, $"Converter of type {converter} failed for WriteSearchableText()");
45 | }
46 | }
47 | }
48 |
49 | ///
50 | /// Returns a string representation of the specified .
51 | ///
52 | /// The element.
53 | /// A string representation of .
54 | public virtual string GetSearchableText(IPublishedElement element) {
55 | StringBuilder sb = new();
56 | using TextWriter writer = new StringWriter(sb);
57 | WriteSearchableText(element, writer);
58 | return sb.ToString();
59 | }
60 |
61 | #endregion
62 |
63 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/GridPackage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using Umbraco.Cms.Core.Semver;
4 |
5 | namespace Skybrud.Umbraco.GridData;
6 |
7 | ///
8 | /// Static class with various information and constants about the package.
9 | ///
10 | public class GridPackage {
11 |
12 | ///
13 | /// Gets the alias of the package.
14 | ///
15 | public const string Alias = "Skybrud.Umbraco.GridData";
16 |
17 | ///
18 | /// Gets the friendly name of the package.
19 | ///
20 | public const string Name = "Skybrud Grid Data";
21 |
22 | ///
23 | /// Gets the version of the package.
24 | ///
25 | public static readonly Version Version = typeof(GridPackage).Assembly.GetName().Version!;
26 |
27 | ///
28 | /// Gets the informational version of the package.
29 | ///
30 | public static readonly string InformationalVersion = FileVersionInfo.GetVersionInfo(typeof(GridPackage).Assembly.Location).ProductVersion!;
31 |
32 | ///
33 | /// Gets the semantic version of the package.
34 | ///
35 | public static readonly SemVersion SemVersion = InformationalVersion;
36 |
37 | ///
38 | /// Gets the URL of the GitHub repository for this package.
39 | ///
40 | public const string GitHubUrl = "https://github.com/skybrud/Skybrud.Umbraco.GridData/";
41 |
42 | ///
43 | /// Gets the URL of the issue tracker for this package.
44 | ///
45 | public const string IssuesUrl = "https://github.com/skybrud/Skybrud.Umbraco.GridData/issues";
46 |
47 | ///
48 | /// Gets the website URL of the package.
49 | ///
50 | public const string WebsiteUrl = "https://packages.skybrud.dk/skybrud.umbraco.griddata/v5/";
51 |
52 | ///
53 | /// Gets the URL of the documentation for this package.
54 | ///
55 | public const string DocumentationUrl = "https://packages.skybrud.dk/skybrud.umbraco.griddata/v5/docs/";
56 |
57 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/GridUtils.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Reflection;
3 | using System.Text.RegularExpressions;
4 | using Skybrud.Essentials.Reflection;
5 |
6 | namespace Skybrud.Umbraco.GridData;
7 |
8 | ///
9 | /// Various utility methods for the grid.
10 | ///
11 | public static class GridUtils {
12 |
13 | #region Version specific methods
14 |
15 | ///
16 | /// Gets the assembly version as a string.
17 | ///
18 | public static string GetVersion() {
19 | return ReflectionUtils.GetVersion(typeof(GridUtils).Assembly);
20 | }
21 |
22 | ///
23 | /// Gets the file version as a string.
24 | ///
25 | ///
26 | public static string GetFileVersion() {
27 | Assembly assembly = typeof(GridUtils).Assembly;
28 | return FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion!;
29 | }
30 |
31 | #endregion
32 |
33 | #region Rendering
34 |
35 | ///
36 | /// Gets whether the specified is a valid partial name.
37 | ///
38 | /// The name of the partial.
39 | /// true if is valid; otherwise false.
40 | public static bool IsValidPartialName(string? name) {
41 | return name is not null && Regex.IsMatch(name, "^[a-zA-Z0-9-\\._ ]+$");
42 | }
43 |
44 | #endregion
45 |
46 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Json/Converters/GridControlValueStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Skybrud.Umbraco.GridData.Models.Values;
4 |
5 | namespace Skybrud.Umbraco.GridData.Json.Converters;
6 |
7 | ///
8 | /// Converter for text based grid control values.
9 | ///
10 | public class GridControlValueStringConverter : JsonConverter {
11 |
12 | ///
13 | /// Writes the JSON representation of the object.
14 | ///
15 | /// The to write to.
16 | /// The value.
17 | /// The calling serializer.
18 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) {
19 |
20 | if (value is GridControlTextValue text) {
21 | writer.WriteValue(text.Value);
22 | return;
23 | }
24 |
25 | serializer.Serialize(writer, value);
26 |
27 | }
28 |
29 | ///
30 | /// Reads the JSON representation of the object.
31 | ///
32 | /// The to read from.
33 | /// Type of the object.
34 | /// The existing value of object being read.
35 | /// The calling serializer.
36 | /// The object value.
37 | public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) {
38 | throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
39 | }
40 |
41 | ///
42 | /// Gets a value indicating whether this Newtonsoft.Json.JsonConverter can read JSON.
43 | ///
44 | public override bool CanRead => false;
45 |
46 | ///
47 | /// Determines whether this instance can convert the specified object type.
48 | ///
49 | /// Type of the object.
50 | /// true if this instance can convert the specified object type; otherwise false.
51 | public override bool CanConvert(Type type) {
52 | return false;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Json/Converters/GridJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Skybrud.Umbraco.GridData.Models;
4 |
5 | namespace Skybrud.Umbraco.GridData.Json.Converters;
6 |
7 | ///
8 | /// Converter for dictionary based values in the grid.
9 | ///
10 | public class GridJsonConverter : JsonConverter {
11 |
12 | ///
13 | /// Writes the JSON representation of the object.
14 | ///
15 | /// The to write to.
16 | /// The value.
17 | /// The calling serializer.
18 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) {
19 |
20 | if (value is GridJsonObject obj) {
21 | serializer.Serialize(writer, obj.JObject);
22 | return;
23 | }
24 |
25 | serializer.Serialize(writer, value);
26 |
27 | }
28 |
29 | ///
30 | /// Reads the JSON representation of the object.
31 | ///
32 | /// The to read from.
33 | /// Type of the object.
34 | /// The existing value of object being read.
35 | /// The calling serializer.
36 | /// The object value.
37 | public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) {
38 | throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
39 | }
40 |
41 | ///
42 | /// Gets a value indicating whether this Newtonsoft.Json.JsonConverter can read JSON.
43 | ///
44 | public override bool CanRead => false;
45 |
46 | ///
47 | /// Determines whether this instance can convert the specified object type.
48 | ///
49 | /// Type of the object.
50 | /// true if this instance can convert the specified object type; otherwise false.
51 | public override bool CanConvert(Type type) {
52 | return false;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Manifests/GridManifestFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Umbraco.Cms.Core.Manifest;
3 |
4 | namespace Skybrud.Umbraco.GridData.Manifests;
5 |
6 | ///
7 | public class GridManifestFilter : IManifestFilter {
8 |
9 | ///
10 | public void Filter(List manifests) {
11 | manifests.Add(new PackageManifest {
12 | AllowPackageTelemetry = true,
13 | PackageId = GridPackage.Alias,
14 | PackageName = GridPackage.Name,
15 | Version = GridPackage.InformationalVersion
16 | });
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Config/GridEditorConfigBase.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using Skybrud.Umbraco.GridData.Models.Values;
4 |
5 | namespace Skybrud.Umbraco.GridData.Models.Config;
6 |
7 | ///
8 | /// Abstract class with a basic implementation of the interface.
9 | ///
10 | public abstract class GridEditorConfigBase : GridJsonObject, IGridEditorConfig {
11 |
12 | #region Properties
13 |
14 | ///
15 | /// Gets a reference to the parent editor of the configuration.
16 | ///
17 | [JsonIgnore]
18 | public GridEditor Editor { get; }
19 |
20 | #endregion
21 |
22 | #region Constructors
23 |
24 | ///
25 | /// Initializes a new instance based on the specified object.
26 | ///
27 | /// An instance of representing the configuration of the editor.
28 | /// An instance of representing the parent editor.
29 | protected GridEditorConfigBase(JObject json, GridEditor editor) : base(json) {
30 | Editor = editor;
31 | }
32 |
33 | #endregion
34 |
35 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Config/GridEditorMediaConfig.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
4 |
5 | namespace Skybrud.Umbraco.GridData.Models.Config;
6 |
7 | ///
8 | /// Class representing the configuration of a media editor.
9 | ///
10 | public class GridEditorMediaConfig : GridEditorConfigBase {
11 |
12 | #region Properties
13 |
14 | ///
15 | /// Gets an object describing the desired size of the media.
16 | ///
17 | [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
18 | public GridEditorMediaConfigSize Size { get; }
19 |
20 | #endregion
21 |
22 | #region Constructors
23 |
24 | ///
25 | /// Initializes a new instance based on the specified object.
26 | ///
27 | /// An instance of representing the configuration of the editor.
28 | /// An instance of representing the parent editor.
29 | public GridEditorMediaConfig(JObject json, GridEditor editor) : base(json, editor) {
30 | Size = json.GetObject("size", GridEditorMediaConfigSize.Parse)!;
31 | }
32 |
33 | #endregion
34 |
35 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Config/GridEditorMediaConfigSize.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
5 |
6 | namespace Skybrud.Umbraco.GridData.Models.Config;
7 |
8 | ///
9 | /// Class describing the desired size of a media (image).
10 | ///
11 | public class GridEditorMediaConfigSize : GridJsonObject {
12 |
13 | #region Properties
14 |
15 | ///
16 | /// Gets the desired width of the media.
17 | ///
18 | [JsonProperty("width")]
19 | public int Width { get; }
20 |
21 | ///
22 | /// Gets the desired height of the media.
23 | ///
24 | [JsonProperty("height")]
25 | public int Height { get; }
26 |
27 | #endregion
28 |
29 | #region Constructors
30 |
31 | private GridEditorMediaConfigSize(JObject json) : base(json) {
32 | Width = json.GetInt32("width");
33 | Height = json.GetInt32("height");
34 | }
35 |
36 | #endregion
37 |
38 | #region Static methods
39 |
40 | ///
41 | /// Gets an instance of from the specified object.
42 | ///
43 | /// The instance of to be parsed.
44 | [return: NotNullIfNotNull("json")]
45 | public static GridEditorMediaConfigSize? Parse(JObject? json) {
46 | return json == null ? null : new GridEditorMediaConfigSize(json);
47 | }
48 |
49 | #endregion
50 |
51 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Config/GridEditorTextConfig.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
4 |
5 | namespace Skybrud.Umbraco.GridData.Models.Config;
6 |
7 | ///
8 | /// Class representing the configuration of a text editor.
9 | ///
10 | public class GridEditorTextConfig : GridEditorConfigBase {
11 |
12 | #region Properties
13 |
14 | ///
15 | /// Gets the style properties for the text.
16 | ///
17 | [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
18 | public string Style { get; private set; }
19 |
20 | ///
21 | /// Gets whether the property has a value.
22 | ///
23 | public bool HasStyle => !string.IsNullOrWhiteSpace(Style);
24 |
25 | ///
26 | /// Gets the markup for the text.
27 | ///
28 | [JsonProperty("markup", NullValueHandling = NullValueHandling.Ignore)]
29 | public string Markup { get; private set; }
30 |
31 | ///
32 | /// Gets whether the property has a value.
33 | ///
34 | public bool HasMarkup => !string.IsNullOrWhiteSpace(Markup);
35 |
36 | #endregion
37 |
38 | #region Constructors
39 |
40 | ///
41 | /// Initializes a new instance based on the specified object.
42 | ///
43 | /// An instance of representing the configuration of the editor.
44 | /// An instance of representing the parent editor.
45 | public GridEditorTextConfig(JObject json, GridEditor editor) : base(json, editor) {
46 | Style = json.GetString("style")!;
47 | Markup = json.GetString("markup")!;
48 | }
49 |
50 | #endregion
51 |
52 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Config/IGridEditorConfig.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace Skybrud.Umbraco.GridData.Models.Config;
4 |
5 | ///
6 | /// Interface describing a grid editor config.
7 | ///
8 | public interface IGridEditorConfig {
9 |
10 | ///
11 | /// Gets a reference to the parent editor of the configuration.
12 | ///
13 | [JsonIgnore]
14 | GridEditor Editor { get; }
15 |
16 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridArea.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Linq;
7 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
8 | using Skybrud.Umbraco.GridData.Factories;
9 |
10 | namespace Skybrud.Umbraco.GridData.Models;
11 |
12 | ///
13 | /// Class representing an area in an Umbraco Grid.
14 | ///
15 | public class GridArea : GridElement {
16 |
17 | #region Properties
18 |
19 | ///
20 | /// Gets a reference to the entire .
21 | ///
22 | [JsonIgnore]
23 | public GridDataModel Model => Section.Model;
24 |
25 | ///
26 | /// Gets a reference to the parent .
27 | ///
28 | [JsonIgnore]
29 | public GridSection Section => Row.Section;
30 |
31 | ///
32 | /// Gets a reference to the parent .
33 | ///
34 | [JsonIgnore]
35 | public GridRow Row { get; }
36 |
37 | ///
38 | /// Gets the column width of the area.
39 | ///
40 | public int Grid { get; }
41 |
42 | ///
43 | /// Gets wether all editors are allowed for this area.
44 | ///
45 | public bool AllowAll { get; }
46 |
47 | ///
48 | /// Gets an array of all editors allowed for this area. If is true, this
49 | /// array may be empty.
50 | ///
51 | public string[] Allowed { get; }
52 |
53 | ///
54 | /// Gets an array of all controls added to this area.
55 | ///
56 | public IReadOnlyList Controls { get; }
57 |
58 | ///
59 | /// Gets a reference to the previous area.
60 | ///
61 | public GridArea? PreviousArea { get; internal set; }
62 |
63 | ///
64 | /// Gets a reference to the next area.
65 | ///
66 | public GridArea? NextArea { get; internal set; }
67 |
68 | ///
69 | /// Gets whether the area has any controls.
70 | ///
71 | public bool HasControls => Controls.Count > 0;
72 |
73 | ///
74 | /// Gets the first control of the area. If the area doesn't contain
75 | /// any controls, this property will return null.
76 | ///
77 | public GridControl? FirstControl => Controls.FirstOrDefault();
78 |
79 | ///
80 | /// Gets the last control of the area. If the area doesn't contain
81 | /// any controls, this property will return null.
82 | ///
83 | public GridControl? LastControl => Controls.LastOrDefault();
84 |
85 | ///
86 | /// Gets whether at least one control within the area is valid.
87 | ///
88 | public override bool IsValid {
89 | get { return Controls.Any(x => x.IsValid); }
90 | }
91 |
92 | #endregion
93 |
94 | #region Constructors
95 |
96 | ///
97 | /// Initializes a new instance based on the specified object, and .
98 | ///
99 | /// An instance of representing the section.
100 | /// The parent row.
101 | /// The factory used for parsing subsequent parts of the grid.
102 | public GridArea(JObject json, GridRow row, IGridFactory factory) : base(json) {
103 |
104 | Row = row;
105 | Grid = json.GetInt32("grid");
106 | AllowAll = json.GetBoolean("allowAll");
107 | Allowed = json.GetStringArray("allowed");
108 | Controls = json.GetArray("controls", x => factory.CreateGridControl(x, this)) ?? [];
109 |
110 | // Update "PreviousControl" and "NextControl" properties
111 | for (int i = 1; i < Controls.Count; i++) {
112 | Controls[i - 1].NextControl = Controls[i];
113 | Controls[i].PreviousControl = Controls[i - 1];
114 | }
115 |
116 | }
117 |
118 | #endregion
119 |
120 | #region Member methods
121 |
122 | ///
123 | /// Writes a string representation of the area to .
124 | ///
125 | /// The current grid context.
126 | /// The writer.
127 | public override void WriteSearchableText(GridContext context, TextWriter writer) {
128 | foreach (GridControl control in Controls) control.WriteSearchableText(context, writer);
129 | }
130 |
131 | #endregion
132 |
133 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridControl.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Newtonsoft.Json;
4 | using Newtonsoft.Json.Linq;
5 | using Skybrud.Umbraco.GridData.Models.Config;
6 | using Skybrud.Umbraco.GridData.Models.Values;
7 |
8 | namespace Skybrud.Umbraco.GridData.Models;
9 |
10 | ///
11 | /// Class representing a control in an Umbraco Grid.
12 | ///
13 | public class GridControl : GridJsonObject {
14 |
15 | #region Properties
16 |
17 | ///
18 | /// Gets a reference to the entire .
19 | ///
20 | [JsonIgnore]
21 | public GridDataModel Model => Section.Model;
22 |
23 | ///
24 | /// Gets a reference to the parent .
25 | ///
26 | [JsonIgnore]
27 | public GridSection Section => Row.Section;
28 |
29 | ///
30 | /// Gets a reference to the parent .
31 | ///
32 | [JsonIgnore]
33 | public GridRow Row => Area.Row;
34 |
35 | ///
36 | /// Gets a reference to the parent .
37 | ///
38 | [JsonIgnore]
39 | public GridArea Area { get; }
40 |
41 | ///
42 | /// Gets the value of the control. Alternately use the method to get the type safe value.
43 | ///
44 | [JsonProperty("value")]
45 | public IGridControlValue? Value { get; internal set; }
46 |
47 | ///
48 | /// Gets a reference to the editor of the control.
49 | ///
50 | [JsonProperty("editor")]
51 | public GridEditor Editor { get; internal set; }
52 |
53 | ///
54 | /// Gets a reference to the previous control.
55 | ///
56 | public GridControl? PreviousControl { get; internal set; }
57 |
58 | ///
59 | /// Gets a reference to the next control.
60 | ///
61 | public GridControl? NextControl { get; internal set; }
62 |
63 | ///
64 | /// Gets whether the control and it's value is valid.
65 | ///
66 | [JsonIgnore]
67 | public bool IsValid => Value is { IsValid: true };
68 |
69 | #endregion
70 |
71 | #region Constructors
72 |
73 | ///
74 | /// Initializes a new instance from the specified object and .
75 | ///
76 | /// The instance of representing the control.
77 | /// An instance of representing the parent area.
78 | internal GridControl(JObject json, GridArea area) : base(json) {
79 | Area = area;
80 | Value = null!;
81 | Editor = null!;
82 | }
83 |
84 | internal GridControl(GridControl control) : base(control.JObject) {
85 | Area = control.Area;
86 | Value = control.Value;
87 | Editor = control.Editor;
88 | PreviousControl = control.PreviousControl;
89 | NextControl = control.NextControl;
90 | }
91 |
92 | #endregion
93 |
94 | #region Member methods
95 |
96 | ///
97 | /// Returns the value of the control cast to the type of .
98 | ///
99 | /// The type of the value to be returned.
100 | public T? GetValue() where T : IGridControlValue {
101 | return Value is T value ? value : default;
102 | }
103 |
104 | ///
105 | /// Writes a string representation of the control to .
106 | ///
107 | /// The current grid context.
108 | /// The writer.
109 | public void WriteSearchableText(GridContext context, TextWriter writer) {
110 | Value?.WriteSearchableText(context, writer);
111 | }
112 |
113 | ///
114 | /// Returns the value of the control as a searchable text - e.g. to be used in Examine.
115 | ///
116 | /// The current grid context.
117 | /// An instance of with the value as a searchable text.
118 | public string GetSearchableText(GridContext context) {
119 | StringBuilder sb = new();
120 | using TextWriter writer = new StringWriter(sb);
121 | WriteSearchableText(context, writer);
122 | return sb.ToString();
123 | }
124 |
125 | #endregion
126 |
127 | }
128 |
129 | ///
130 | /// Class representing a grid control where the value is of type .
131 | ///
132 | /// The type of the value.
133 | public class GridControl : GridControl where TValue : IGridControlValue {
134 |
135 | ///
136 | /// Gets the value of the control.
137 | ///
138 | [JsonProperty("value")]
139 | public new TValue Value => (TValue) base.Value!;
140 |
141 | ///
142 | /// Initializes a new instance based on the specified .
143 | ///
144 | /// The control.
145 | public GridControl(GridControl control) : base(control) { }
146 |
147 | }
148 |
149 | ///
150 | /// Class representing a grid control where the value is of type and an editor with a config of type .
151 | ///
152 | /// The type of the value.
153 | /// The type of the editor config.
154 | public class GridControl : GridControl where TValue : IGridControlValue where TConfig : IGridEditorConfig {
155 |
156 | ///
157 | /// Gets a reference to the editor of the control.
158 | ///
159 | [JsonProperty("editor")]
160 | public new GridEditor Editor { get; }
161 |
162 | ///
163 | /// Initializes a new instance based on the specified and .
164 | ///
165 | /// The control.
166 | /// The editor.
167 | public GridControl(GridControl control, GridEditor editor) : base(control) {
168 | Editor = editor;
169 | }
170 |
171 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridDataModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using Newtonsoft.Json;
8 | using Newtonsoft.Json.Linq;
9 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
10 | using Skybrud.Umbraco.GridData.Factories;
11 | using Umbraco.Cms.Core.Models.PublishedContent;
12 |
13 | namespace Skybrud.Umbraco.GridData.Models;
14 |
15 | ///
16 | /// Class representing the value/model saved by an Umbraco Grid property.
17 | ///
18 | public class GridDataModel : GridJsonObject {
19 |
20 | #region Properties
21 |
22 | ///
23 | /// Gets whether the model is valid. The model is considered valid if it has been parsed from a JSON value and
24 | /// has at least one valid control.
25 | ///
26 | public bool IsValid {
27 | get { return JObject != null! && GetAllControls().Any(x => x.IsValid); }
28 | }
29 |
30 | ///
31 | /// Gets the name of the selected layout.
32 | ///
33 | public string Name { get; }
34 |
35 | ///
36 | /// Gets an array of the columns in the grid.
37 | ///
38 | public IReadOnlyList Sections { get; }
39 |
40 | ///
41 | /// Gets a reference to the parent , if the Grid model was loaded directly from a property value.
42 | ///
43 | [JsonIgnore]
44 | public IPublishedElement? Owner { get; }
45 |
46 | ///
47 | /// Gets whether the grid model has a reference to it's owner.
48 | ///
49 | [JsonIgnore]
50 | [MemberNotNullWhen(true, "Owner")]
51 | public bool HasOwner => Owner != null;
52 |
53 | ///
54 | /// Gets a reference to the parent property type, if the Grid model was loaded directly from a property value.
55 | ///
56 | public IPublishedPropertyType? PropertyType { get; }
57 |
58 | ///
59 | /// Gets whether a property type has been specified for the model.
60 | ///
61 | [MemberNotNullWhen(true, "PropertyType")]
62 | public bool HasPropertyType => PropertyType != null;
63 |
64 | #endregion
65 |
66 | #region Constructors
67 |
68 | ///
69 | /// Initializes a new instance based on the specified object.
70 | ///
71 | /// and may be specified if the grid value comes directly from a property value. If either aren't available, it's fine to specify null for both of them.
72 | ///
73 | /// An instance of representing the owner holding the grid value.
74 | /// An instance of representing the property holding the grid value.
75 | /// An instance of representing the grid model.
76 | /// The factory used for parsing subsequent parts of the grid.
77 | public GridDataModel(IPublishedElement? owner, IPublishedPropertyType? propertyType, JObject? json, IGridFactory? factory) : base(json ?? new JObject()) {
78 |
79 | Owner = owner;
80 | PropertyType = propertyType;
81 | Name = json.GetString("name")!;
82 |
83 | if (factory is null) {
84 | Sections = [];
85 | } else {
86 | Sections = json.GetArray("sections", x => factory.CreateGridSection(x, this)) ?? [];
87 | }
88 |
89 | }
90 |
91 | #endregion
92 |
93 | #region Member methods
94 |
95 | ///
96 | /// Returns a list of all nested controls.
97 | ///
98 | public IReadOnlyList GetAllControls() {
99 | return (
100 | from section in Sections
101 | from row in section.Rows
102 | from area in row.Areas
103 | from control in area.Controls
104 | select control
105 | ).ToArray();
106 | }
107 |
108 | ///
109 | /// Returns a list of all nested controls with the specified editor .
110 | ///
111 | /// The editor alias of controls to be returned.
112 | public IReadOnlyList GetAllControls(string alias) {
113 | return GetAllControls(x => x.Editor.Alias == alias);
114 | }
115 |
116 | ///
117 | /// Returns a list of all nested controls matching the specified .
118 | ///
119 | /// The predicate (callback function) used for comparison.
120 | public IReadOnlyList GetAllControls(Func predicate) {
121 | return (
122 | from section in Sections
123 | from row in section.Rows
124 | from area in row.Areas
125 | from control in area.Controls
126 | where predicate(control)
127 | select control
128 | ).ToArray();
129 | }
130 |
131 | ///
132 | /// Writes a string representation of the grid data model to .
133 | ///
134 | /// The current grid context.
135 | /// The writer.
136 | public void WriteSearchableText(GridContext context, TextWriter writer) {
137 | foreach (GridSection section in Sections) section.WriteSearchableText(context, writer);
138 | }
139 |
140 | ///
141 | /// Returns a textual representation of the grid model - e.g. to be used in Examine.
142 | ///
143 | /// The current grid context.
144 | /// An instance of representing the value of the element.
145 | public string GetSearchableText(GridContext context) {
146 | StringBuilder sb = new();
147 | using TextWriter writer = new StringWriter(sb);
148 | WriteSearchableText(context, writer);
149 | return sb.ToString();
150 | }
151 |
152 | #endregion
153 |
154 | #region Static methods
155 |
156 | ///
157 | /// Returns an empty (and invalid) model. This method can be used to get a fallback value for when an actual Grid model isn't available.
158 | ///
159 | public static GridDataModel GetEmptyModel() {
160 | return new GridDataModel(null, null, null, null);
161 | }
162 |
163 | ///
164 | /// Returns an empty (and invalid) model. This method can be used to get a fallback value for when an actual Grid model isn't available.
165 | ///
166 | /// An instance of representing the owner holding the grid value.
167 | /// An instance of representing the property holding the grid value.
168 | public static GridDataModel GetEmptyModel(IPublishedElement owner, IPublishedPropertyType propertyType) {
169 | return new GridDataModel(owner, propertyType, null, null);
170 | }
171 |
172 | #endregion
173 |
174 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridDictionary.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Globalization;
5 | using System.Linq;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Linq;
8 |
9 | namespace Skybrud.Umbraco.GridData.Models;
10 |
11 | ///
12 | /// Dictionary representing a configuration for an element in the Umbraco Grid.
13 | ///
14 | public class GridDictionary : GridJsonObject, IEnumerable {
15 |
16 | #region Private fields
17 |
18 | private readonly Dictionary _dictionary;
19 |
20 | #endregion
21 |
22 | #region Properties
23 |
24 | ///
25 | /// Gets the keys of the underlying dictionary.
26 | ///
27 | [JsonIgnore]
28 | public string[] Keys => [.. _dictionary.Keys];
29 |
30 | ///
31 | /// Gets the keys of the underlying dictionary.
32 | ///
33 | [JsonIgnore]
34 | public string[] Values => [.. _dictionary.Keys];
35 |
36 | ///
37 | /// Gets the amount of items in the dictionary.
38 | ///
39 | public int Count => _dictionary.Count;
40 |
41 | ///
42 | /// Gets the value of an item with the specified .
43 | ///
44 | /// The key of the dictionary item.
45 | public string this[string key] => _dictionary[key];
46 |
47 | #endregion
48 |
49 | #region Constructors
50 |
51 | private GridDictionary(Dictionary config, JObject json) : base(json) {
52 | _dictionary = config;
53 | }
54 |
55 | #endregion
56 |
57 | #region Member methods
58 |
59 | ///
60 | /// Gets whether the specified is contained in the dictionary.
61 | ///
62 | /// The key.
63 | public bool ContainsKey(string key) {
64 | return _dictionary.ContainsKey(key);
65 | }
66 |
67 | ///
68 | /// Gets the value associated with the specified .
69 | ///
70 | /// The key of the value to get.
71 | /// When this method returns, contains the value associated with the specified key, if the
72 | /// key is found; otherwise, the default value for the type of the value parameter. This parameter is passed
73 | /// uninitialized.
74 | /// true if the dictionary contains an element with the specified key; otherwise, false.
75 | public bool TryGetValue(string key, [NotNullWhen(true)] out string? value) {
76 | return _dictionary.TryGetValue(key, out value);
77 | }
78 |
79 | ///
80 | /// Returns an enumerator that iterates through the collection.
81 | ///
82 | /// An enumerator that can be used to iterate through the collection.
83 | public IEnumerator GetEnumerator() {
84 | return _dictionary.Select(x => new GridDictionaryItem(x.Key, x.Value)).GetEnumerator();
85 | }
86 |
87 | IEnumerator IEnumerable.GetEnumerator() {
88 | return GetEnumerator();
89 | }
90 |
91 | #endregion
92 |
93 | #region Static methods
94 |
95 | ///
96 | /// Parses the specified into an instance of .
97 | ///
98 | /// The instance of to be parsed.
99 | /// An instance of .
100 | public static GridDictionary Parse(JObject? json) {
101 |
102 | // Initialize an empty dictionary
103 | Dictionary config = new();
104 |
105 | // Add all properties to the dictionary
106 | if (json != null) {
107 | foreach (JProperty property in json.Properties()) {
108 | config.Add(property.Name, string.Format(CultureInfo.InvariantCulture, "{0}", property.Value));
109 | }
110 | }
111 |
112 | // Return the instance
113 | return new GridDictionary(config, json ?? new JObject());
114 |
115 | }
116 |
117 | #endregion
118 |
119 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridDictionaryItem.cs:
--------------------------------------------------------------------------------
1 | namespace Skybrud.Umbraco.GridData.Models;
2 |
3 | ///
4 | /// Class representing an item of .
5 | ///
6 | public class GridDictionaryItem {
7 |
8 | #region Properties
9 |
10 | ///
11 | /// Gets the key of the item.
12 | ///
13 | public string Key { get; }
14 |
15 | ///
16 | /// Gets the value of the item.
17 | ///
18 | public string Value { get; }
19 |
20 | #endregion
21 |
22 | #region Constructors
23 |
24 | ///
25 | /// Initializes a new item based on the specified and .
26 | ///
27 | /// The key of the item.
28 | /// The value of the item.
29 | public GridDictionaryItem(string key, string value) {
30 | Key = key;
31 | Value = value;
32 | }
33 |
34 | #endregion
35 |
36 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridEditor.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
4 | using Skybrud.Umbraco.GridData.Models.Config;
5 |
6 | namespace Skybrud.Umbraco.GridData.Models;
7 |
8 | ///
9 | /// Class representing an editor of a control in an Umbraco Grid.
10 | ///
11 | public class GridEditor : GridJsonObject {
12 |
13 | #region Properties
14 |
15 | ///
16 | /// Gets the name of the editor.
17 | ///
18 | [JsonProperty("name")]
19 | public string? Name { get; }
20 |
21 | ///
22 | /// Gets the alias of the editor.
23 | ///
24 | [JsonProperty("alias")]
25 | public string Alias { get; }
26 |
27 | ///
28 | /// Gets the view of the editor.
29 | ///
30 | [JsonProperty("view")]
31 | public string? View { get; }
32 |
33 | ///
34 | /// Gets renderer for the control/editor. If specified, the renderer refers to a partial
35 | /// view that should be used for rendering the control.
36 | ///
37 | [JsonProperty("render")]
38 | public string? Render { get; }
39 |
40 | ///
41 | /// Gets the icon of the editor.
42 | ///
43 | [JsonProperty("icon")]
44 | public string? Icon { get; }
45 |
46 | ///
47 | /// Gets the configuration object for the editor. This property will return null if the
48 | /// corresponding property in the underlying JSON is also null.
49 | ///
50 | [JsonProperty("config", NullValueHandling = NullValueHandling.Ignore)]
51 | public IGridEditorConfig? Config { get; internal set; }
52 |
53 | #endregion
54 |
55 | #region Constructors
56 |
57 | ///
58 | /// Initializes a new instance with the specified .
59 | ///
60 | /// The alias of the editor.
61 | public GridEditor(string alias) : base(null ?? new JObject()) {
62 | Alias = alias;
63 | }
64 |
65 | ///
66 | /// Initializes a new instance based on the specified object.
67 | ///
68 | /// The instance of representing the control.
69 | public GridEditor(JObject json) : base(json) {
70 |
71 | // Parse basic properties
72 | Name = json.GetString("name")!;
73 | Alias = json.GetString("alias")!;
74 | View = json.GetString("view")!;
75 | Render = json.GetString("render");
76 | Icon = json.GetString("icon")!;
77 |
78 | }
79 |
80 | ///
81 | /// Initializes a new instance based on the specified .
82 | ///
83 | /// The editor to be wrapped.
84 | public GridEditor(GridEditor editor) : base(editor.JObject) {
85 | Name = editor.Name;
86 | Alias = editor.Alias;
87 | View = editor.View;
88 | Render = editor.Render;
89 | Icon = editor.Icon;
90 | }
91 |
92 | #endregion
93 |
94 | #region Member methods
95 |
96 | ///
97 | /// Returns the config of the editor cast to the type of .
98 | ///
99 | /// The type of the config to be returned.
100 | public T? GetConfig() where T : IGridEditorConfig {
101 | return Config is T value ? value : default;
102 | }
103 |
104 | #endregion
105 |
106 | }
107 |
108 | ///
109 | /// Class representing an editor where the config is of type .
110 | ///
111 | /// The type of the editor config.
112 | public class GridEditor : GridEditor where TConfig : IGridEditorConfig {
113 |
114 | ///
115 | /// Gets the editor config.
116 | ///
117 | public new TConfig Config => (TConfig) base.Config!;
118 |
119 | ///
120 | /// Initializes a new instance based on the specified .
121 | ///
122 | /// The editor to be wrapped.
123 | public GridEditor(GridEditor editor) : base(editor) { }
124 |
125 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridElement.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Newtonsoft.Json.Linq;
4 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
5 |
6 | namespace Skybrud.Umbraco.GridData.Models;
7 |
8 | ///
9 | /// Class representing a generic element in an Umbraco Grid.
10 | ///
11 | public abstract class GridElement : GridJsonObject {
12 |
13 | #region Properties
14 |
15 | ///
16 | /// Gets a dictionary representing the configuration (called Settings in the backoffice) of the element.
17 | ///
18 | public GridDictionary Config { get; }
19 |
20 | ///
21 | /// Gets whetehr the element has one or more config values.
22 | ///
23 | public bool HasConfig => Config is { Count: > 0 };
24 |
25 | ///
26 | /// Gets a dictionary representing the styles of the element.
27 | ///
28 | public GridDictionary Styles { get; }
29 |
30 | ///
31 | /// Gets whetehr the element has one or more style values.
32 | ///
33 | public bool HasStyles => Styles is { Count: > 0 };
34 |
35 | ///
36 | /// Gets whether at least one control within the element is valid.
37 | ///
38 | public abstract bool IsValid { get; }
39 |
40 | #endregion
41 |
42 | #region Constructors
43 |
44 | ///
45 | /// Initializes a new instance based on the specified .
46 | ///
47 | /// An instance of representing the area.
48 | protected GridElement(JObject json) : base(json) {
49 | Styles = json.GetObject("styles", GridDictionary.Parse)!;
50 | Config = json.GetObject("config", GridDictionary.Parse)!;
51 | }
52 |
53 | #endregion
54 |
55 | #region Member methods
56 |
57 | ///
58 | /// Writes a string representation of the element to .
59 | ///
60 | /// The current grid context.
61 | /// The writer.
62 | public abstract void WriteSearchableText(GridContext context, TextWriter writer);
63 |
64 | ///
65 | /// Gets a textual representation of the element - e.g. to be used in Examine.
66 | ///
67 | /// The current grid context.
68 | /// An instance of representing the value of the element.
69 | public string GetSearchableText(GridContext context) {
70 | StringBuilder sb = new();
71 | using TextWriter writer = new StringWriter(sb);
72 | WriteSearchableText(context, writer);
73 | return sb.ToString();
74 | }
75 |
76 | #endregion
77 |
78 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridJsonObject.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using Skybrud.Umbraco.GridData.Json.Converters;
4 |
5 | namespace Skybrud.Umbraco.GridData.Models;
6 |
7 | ///
8 | /// Class representing an object derived from an instance of .
9 | ///
10 | [JsonConverter(typeof(GridJsonConverter))]
11 | public class GridJsonObject {
12 |
13 | #region Properties
14 |
15 | ///
16 | /// Gets a reference to the underlying instance of .
17 | ///
18 | [JsonIgnore]
19 | public JObject JObject { get; }
20 |
21 | #endregion
22 |
23 | #region Constructors
24 |
25 | /// The underlying instance of .
26 | public GridJsonObject(JObject json) {
27 | JObject = json;
28 | }
29 |
30 | #endregion
31 |
32 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridRow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using Newtonsoft.Json.Linq;
6 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
7 | using Skybrud.Umbraco.GridData.Factories;
8 |
9 | namespace Skybrud.Umbraco.GridData.Models;
10 |
11 | ///
12 | /// Class representing a row in an Umbraco Grid.
13 | ///
14 | public class GridRow : GridElement {
15 |
16 | #region Properties
17 |
18 | ///
19 | /// Gets a reference to the parent .
20 | ///
21 | public GridSection Section { get; }
22 |
23 | ///
24 | /// Gets the unique ID of the row.
25 | ///
26 | public string Id { get; }
27 |
28 | ///
29 | /// Gets the label of the row. Use to check whether a label has been specified.
30 | ///
31 | public string? Label { get; }
32 |
33 | ///
34 | /// Gets whether a label has been specified for the definition of this row.
35 | ///
36 | public bool HasLabel => string.IsNullOrWhiteSpace(Label) == false;
37 |
38 | ///
39 | /// Gets the name of the row.
40 | ///
41 | public string Name { get; }
42 |
43 | ///
44 | /// Gets an array of all areas in the row.
45 | ///
46 | public IReadOnlyList Areas { get; }
47 |
48 | ///
49 | /// Gets a reference to the previous row.
50 | ///
51 | public GridRow? PreviousRow { get; internal set; }
52 |
53 | ///
54 | /// Gets a reference to the next row.
55 | ///
56 | public GridRow? NextRow { get; internal set; }
57 |
58 | ///
59 | /// Gets whether the row has any areas.
60 | ///
61 | public bool HasAreas => Areas.Count > 0;
62 |
63 | ///
64 | /// Gets the first area of the row. If the row doesn't contain any areas, this property will return null.
65 | ///
66 | public GridArea? FirstArea => Areas.FirstOrDefault();
67 |
68 | ///
69 | /// Gets the last area of the row. If the row doesn't contain any areas, this property will return null.
70 | ///
71 | public GridArea? LastArea => Areas.LastOrDefault();
72 |
73 | ///
74 | /// Gets whether at least one area or control within the row is valid.
75 | ///
76 | public override bool IsValid {
77 | get { return Areas.Any(x => x.IsValid); }
78 | }
79 |
80 | #endregion
81 |
82 | #region Constructors
83 |
84 | ///
85 | /// Initializes a new instance based on the specified object, and .
86 | ///
87 | /// An instance of representing the section.
88 | /// The parent section.
89 | /// The factory used for parsing subsequent parts of the grid.
90 | public GridRow(JObject json, GridSection section, IGridFactory factory) : base(json) {
91 |
92 | Section = section;
93 | Id = json.GetString("id")!;
94 | Label = json.GetString("label");
95 | Name = json.GetString("name")!;
96 |
97 | Areas = json.GetArray("areas", x => factory.CreateGridArea(x, this)) ?? [];
98 |
99 | // Update "PreviousArea" and "NextArea" properties
100 | for (int i = 1; i < Areas.Count; i++) {
101 | Areas[i - 1].NextArea = Areas[i];
102 | Areas[i].PreviousArea = Areas[i - 1];
103 | }
104 |
105 | }
106 |
107 | #endregion
108 |
109 | #region Member methods
110 |
111 | ///
112 | /// Returns a list of all nested controls.
113 | ///
114 | public IReadOnlyList GetAllControls() {
115 | return (
116 | from area in Areas
117 | from control in area.Controls
118 | select control
119 | ).ToArray();
120 | }
121 |
122 | ///
123 | /// Returns a list of all nested controls with the specified editor .
124 | ///
125 | /// The editor alias of controls to be returned.
126 | public IReadOnlyList GetAllControls(string alias) {
127 | return GetAllControls(x => x.Editor.Alias == alias);
128 | }
129 |
130 | ///
131 | /// Returns a list of all nested controls matching the specified .
132 | ///
133 | /// The predicate (callback function) used for comparison.
134 | public IReadOnlyList GetAllControls(Func predicate) {
135 | return (
136 | from area in Areas
137 | from control in area.Controls
138 | where predicate(control)
139 | select control
140 | ).ToArray();
141 | }
142 |
143 | ///
144 | /// Writes a string representation of the row to .
145 | ///
146 | /// The current grid context.
147 | /// The writer.
148 | public override void WriteSearchableText(GridContext context, TextWriter writer) {
149 | foreach (GridArea area in Areas) area.WriteSearchableText(context, writer);
150 | }
151 |
152 | #endregion
153 |
154 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/GridSection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 | using Newtonsoft.Json.Linq;
6 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
7 | using Skybrud.Umbraco.GridData.Factories;
8 |
9 | namespace Skybrud.Umbraco.GridData.Models;
10 |
11 | ///
12 | /// Class representing a section in an Umbraco Grid.
13 | ///
14 | public class GridSection : GridJsonObject {
15 |
16 | #region Properties
17 |
18 | ///
19 | /// Gets the section name.
20 | ///
21 | public string Name { get; }
22 |
23 | ///
24 | /// Gets a reference to the parent .
25 | ///
26 | public GridDataModel Model { get; }
27 |
28 | ///
29 | /// Gets the overall column width of the section.
30 | ///
31 | public int Grid { get; }
32 |
33 | ///
34 | /// Gets an array of all rows in the sections.
35 | ///
36 | public IReadOnlyList Rows { get; }
37 |
38 | ///
39 | /// Gets whether the section has any rows.
40 | ///
41 | public bool HasRows => Rows.Count > 0;
42 |
43 | ///
44 | /// Gets the first row of the section. If the section doesn't contain any rows, this property will return null.
45 | ///
46 | public GridRow? FirstRow => Rows.FirstOrDefault();
47 |
48 | ///
49 | /// Gets the last row of the section. If the section doesn't contain any rows, this property will return null.
50 | ///
51 | public GridRow? LastRow => Rows.LastOrDefault();
52 |
53 | #endregion
54 |
55 | #region Constructors
56 |
57 | ///
58 | /// Initializes a new instance based on the specified object, model and .
59 | ///
60 | /// An instance of representing the section.
61 | /// The parent grid model.
62 | /// The factory used for parsing subsequent parts of the grid.
63 | public GridSection(JObject json, GridDataModel grid, IGridFactory factory) : base(json) {
64 |
65 | Model = grid;
66 | Grid = json.GetInt32("grid");
67 | Name = grid.Name;
68 | Rows = json.GetArray("rows", x => factory.CreateGridRow(x, this)) ?? [];
69 |
70 | // Update "PreviousRow" and "NextRow" properties
71 | for (int i = 1; i < Rows.Count; i++) {
72 | Rows[i - 1].NextRow = Rows[i];
73 | Rows[i].PreviousRow = Rows[i - 1];
74 | }
75 |
76 | }
77 |
78 | #endregion
79 |
80 | #region Member methods
81 |
82 | ///
83 | /// Writes a string representation of the section to .
84 | ///
85 | /// The current grid context.
86 | /// The writer.
87 | public void WriteSearchableText(GridContext context, TextWriter writer) {
88 | foreach (GridRow row in Rows) row.WriteSearchableText(context, writer);
89 | }
90 |
91 | ///
92 | /// Returns a textual representation of the section - e.g. to be used in Examine.
93 | ///
94 | /// The current grid context.
95 | /// An instance of representing the value of the element.
96 | public string GetSearchableText(GridContext context) {
97 | StringBuilder sb = new();
98 | using TextWriter writer = new StringWriter(sb);
99 | WriteSearchableText(context, writer);
100 | return sb.ToString();
101 | }
102 |
103 | #endregion
104 |
105 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlEmbedValue.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Microsoft.AspNetCore.Html;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
7 | using Skybrud.Umbraco.GridData.Json.Converters;
8 | using Umbraco.Extensions;
9 |
10 | namespace Skybrud.Umbraco.GridData.Models.Values;
11 |
12 | ///
13 | /// Class representing the embed value of a control.
14 | ///
15 | [JsonConverter(typeof(GridControlValueStringConverter))]
16 | public class GridControlEmbedValue : GridControlValueBase {
17 |
18 | #region Properties
19 |
20 | ///
21 | /// Gets a string representing the value.
22 | ///
23 | public string Value { get; protected set; }
24 |
25 | ///
26 | /// Gets an instance of representing the text value.
27 | ///
28 | [JsonIgnore]
29 | public IHtmlContent HtmlValue { get; protected set; }
30 |
31 | ///
32 | /// Gets whether the value is valid. For an instance of , this means
33 | /// checking whether the property has a value.
34 | ///
35 | [JsonIgnore]
36 | public override bool IsValid => string.IsNullOrWhiteSpace(Value) == false;
37 |
38 | ///
39 | /// Gets the width of the embed.
40 | ///
41 | [JsonProperty("width")]
42 | public int Width { get; set; }
43 |
44 | ///
45 | /// Gets the height of the embed.
46 | ///
47 | [JsonProperty("height")]
48 | public int Height { get; set; }
49 |
50 | ///
51 | /// Gets the url of the embed.
52 | ///
53 | [JsonProperty("url")]
54 | public string Url { get; set; }
55 |
56 | ///
57 | /// Gets the info of the embed.
58 | ///
59 | [JsonProperty("info")]
60 | public string Info { get; set; }
61 |
62 | ///
63 | /// Gets the preview html of the embed.
64 | ///
65 | [JsonProperty("preview")]
66 | public IHtmlContent Preview => HtmlValue;
67 |
68 | #endregion
69 |
70 | #region Constructors
71 |
72 | ///
73 | /// Initializes a new instance based on the value of the specified grid .
74 | ///
75 | /// An instance of representing the parent grid control.
76 | public GridControlEmbedValue( GridControl control) : base(control) {
77 |
78 | Value = Json.GetString("preview")!;
79 | HtmlValue = new HtmlString(Value);
80 |
81 | Width = Json.GetInt32("width");
82 | Height = Json.GetInt32("height");
83 | Url = Json.GetString("url")!;
84 | Info = Json.GetString("info") ?? string.Empty;
85 |
86 | }
87 |
88 | #endregion
89 |
90 | #region Member methods
91 |
92 | ///
93 | /// Gets a string representing the raw value of the control.
94 | ///
95 | /// An instance of .
96 | public override string ToString() {
97 | return HtmlValue.ToHtmlString();
98 | }
99 |
100 | ///
101 | /// Gets an HTML representing the value of the control.
102 | ///
103 | /// An instance of .
104 | public string ToHtmlString() {
105 | return Value;
106 | }
107 |
108 | ///
109 | /// Writes a string representation of this value to .
110 | ///
111 | /// The current grid context.
112 | /// The writer.
113 | public override void WriteSearchableText(GridContext context, TextWriter writer) { }
114 |
115 | ///
116 | /// Returns a string representation of this value.
117 | ///
118 | /// The current grid context.
119 | /// A string representation of this value.
120 | public override string GetSearchableText(GridContext context) {
121 | StringBuilder sb = new();
122 | using TextWriter writer = new StringWriter(sb);
123 | WriteSearchableText(context, writer);
124 | return sb.ToString();
125 | }
126 |
127 | #endregion
128 |
129 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlHtmlValue.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text.RegularExpressions;
3 | using Microsoft.AspNetCore.Html;
4 | using Newtonsoft.Json;
5 | using Skybrud.Umbraco.GridData.Json.Converters;
6 |
7 | namespace Skybrud.Umbraco.GridData.Models.Values;
8 |
9 | ///
10 | /// Class representing the HTML value of a control.
11 | ///
12 | [JsonConverter(typeof(GridControlValueStringConverter))]
13 | public class GridControlHtmlValue : GridControlTextValue {
14 |
15 | #region Properties
16 |
17 | ///
18 | /// Gets an instance of representing the text value.
19 | ///
20 | [JsonIgnore]
21 | public IHtmlContent HtmlValue { get; }
22 |
23 | ///
24 | /// Gets whether the value is valid. For an instance of , this means
25 | /// checking whether the specified text is not an empty string.
26 | ///
27 | [JsonIgnore]
28 | public override bool IsValid => !string.IsNullOrWhiteSpace(Regex.Replace(Value, "<(p|/p)>", string.Empty));
29 |
30 | #endregion
31 |
32 | #region Constructors
33 |
34 | ///
35 | /// Initializes a new instance based on the value of the specified grid .
36 | ///
37 | /// An instance of representing the parent grid control.
38 | public GridControlHtmlValue(GridControl control) : base(control) {
39 | HtmlValue = new HtmlString(Value);
40 | }
41 |
42 | #endregion
43 |
44 | #region Member methods
45 |
46 | ///
47 | /// Writes a string representation of this value to .
48 | ///
49 | /// The current grid context.
50 | /// The writer.
51 | public override void WriteSearchableText(GridContext context, TextWriter writer) {
52 | writer.WriteLine(Regex.Replace(Value, "<.*?>", " "));
53 | }
54 |
55 | #endregion
56 |
57 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlMacroValue.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
5 |
6 | namespace Skybrud.Umbraco.GridData.Models.Values;
7 |
8 | ///
9 | /// Class representing the macro value of a control.
10 | ///
11 | public class GridControlMacroValue : GridControlValueBase {
12 |
13 | ///
14 | /// Gets the syntax of the macro.
15 | ///
16 | [JsonProperty("syntax")]
17 | public string Syntax { get; set; }
18 |
19 | ///
20 | /// Gets the alias of the macro.
21 | ///
22 | [JsonProperty("macroAlias")]
23 | public string MacroAlias { get; set; }
24 |
25 | ///
26 | /// Gets a dictionary containing the macro parameters.
27 | ///
28 | [JsonProperty("macroParamsDictionary")]
29 | public Dictionary Parameters { get; set; }
30 |
31 | ///
32 | /// Gets whether the value is valid. For an instance of , this means
33 | /// checking whether a macro alias has been specified.
34 | ///
35 | [JsonIgnore]
36 | public override bool IsValid => !string.IsNullOrWhiteSpace(MacroAlias);
37 |
38 | #region Constructors
39 |
40 | ///
41 | /// Initializes a new instance based on the value of the specified grid .
42 | ///
43 | /// An instance of representing the parent grid control.
44 | public GridControlMacroValue(GridControl control) : base(control) {
45 | Syntax = Json.GetString("syntax")!;
46 | MacroAlias = Json.GetString("macroAlias")!;
47 | Parameters = Json.GetObject("macroParamsDictionary")?.ToObject>() ?? new Dictionary();
48 | }
49 |
50 | #endregion
51 |
52 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlMediaFocalPoint.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
5 |
6 | namespace Skybrud.Umbraco.GridData.Models.Values;
7 |
8 | ///
9 | /// Class representing the focal point of a media.
10 | ///
11 | public class GridControlMediaFocalPoint : GridJsonObject {
12 |
13 | #region Properties
14 |
15 | ///
16 | /// The horizontal (X-axis) coordinate of the focal point.
17 | ///
18 | [JsonProperty("left")]
19 | public float Left { get; }
20 |
21 | ///
22 | /// The vertical (Y-axis) coordinate of the focal point.
23 | ///
24 | [JsonProperty("top")]
25 | public float Top { get; }
26 |
27 | #endregion
28 |
29 | #region Constructors
30 |
31 | ///
32 | /// Initializes a new instance based on the specified .
33 | ///
34 | /// An instance of representing the focal point.
35 | protected GridControlMediaFocalPoint(JObject json) : base(json) {
36 | Left = json.GetFloat("left");
37 | Top = json.GetFloat("top");
38 | }
39 |
40 | #endregion
41 |
42 | #region Static methods
43 |
44 | ///
45 | /// Gets a focal point from the specified .
46 | ///
47 | /// The instance of to be parsed.
48 | [return: NotNullIfNotNull("json")]
49 | public static GridControlMediaFocalPoint? Parse(JObject? json) {
50 | return json == null ? null : new GridControlMediaFocalPoint(json);
51 | }
52 |
53 | #endregion
54 |
55 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlMediaValue.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using Skybrud.Essentials.Json.Newtonsoft.Extensions;
5 | using Umbraco.Cms.Core.Models.PublishedContent;
6 |
7 | namespace Skybrud.Umbraco.GridData.Models.Values;
8 |
9 | ///
10 | /// Class representing the media value of a control.
11 | ///
12 | public class GridControlMediaValue : GridControlValueBase {
13 |
14 | #region Properties
15 |
16 | ///
17 | /// Gets the focal point with information on how the iamge should be cropped.
18 | ///
19 | [JsonProperty("focalPoint")]
20 | public GridControlMediaFocalPoint? FocalPoint { get; protected set; }
21 |
22 | ///
23 | /// Gets the ID of the image.
24 | ///
25 | [JsonProperty("id")]
26 | public int Id { get; protected set; }
27 |
28 | ///
29 | /// Gets the URL of the media.
30 | ///
31 | [JsonProperty("image")]
32 | public string? Image { get; protected set; }
33 |
34 | ///
35 | /// Gets the alt text of the media.
36 | ///
37 | [JsonProperty("altText", NullValueHandling = NullValueHandling.Ignore)]
38 | public string? AlternativeText { get; protected set; }
39 |
40 | ///
41 | /// Gets whether the property has a value.
42 | ///
43 | [JsonIgnore]
44 | [MemberNotNullWhen(true, nameof(AlternativeText))]
45 | public bool HasAlternativeText => !string.IsNullOrWhiteSpace(AlternativeText);
46 |
47 | ///
48 | /// Gets the caption of the media.
49 | ///
50 | [JsonProperty("caption", NullValueHandling = NullValueHandling.Ignore)]
51 | public string? Caption { get; protected set; }
52 |
53 | ///
54 | /// Gets whether the property has a value.
55 | ///
56 | [JsonIgnore]
57 | [MemberNotNullWhen(true, nameof(Caption))]
58 | public bool HasCaption => !string.IsNullOrWhiteSpace(Caption);
59 |
60 | ///
61 | /// Gets whether the value is valid. For an instance of , this means
62 | /// checking whether an image has been selected. The property will however not validate the image against the
63 | /// media cache.
64 | ///
65 | [JsonIgnore]
66 | public override bool IsValid => Id > 0;
67 |
68 | ///
69 | /// Gets a reference to the underlying representing the selected image.
70 | ///
71 | [JsonIgnore]
72 | public IPublishedContent? PublishedImage { get; internal set; }
73 |
74 | #endregion
75 |
76 | #region Constructors
77 |
78 | ///
79 | /// Initializes a new instance based on the value of the specified grid .
80 | ///
81 | /// An instance of representing the parent grid control.
82 | public GridControlMediaValue(GridControl control) : base(control) {
83 | FocalPoint = Json.GetObject("focalPoint", GridControlMediaFocalPoint.Parse);
84 | Id = Json.GetInt32("id");
85 | Image = Json.GetString("image");
86 | AlternativeText = Json.GetString("altText");
87 | Caption = Json.GetString("caption");
88 | }
89 |
90 | #endregion
91 |
92 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlRichTextValue.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Skybrud.Umbraco.GridData.Json.Converters;
3 |
4 | namespace Skybrud.Umbraco.GridData.Models.Values;
5 |
6 | ///
7 | /// Class representing the rich text value of a control.
8 | ///
9 | [JsonConverter(typeof(GridControlValueStringConverter))]
10 | public class GridControlRichTextValue : GridControlHtmlValue {
11 |
12 | ///
13 | /// Initializes a new instance based on the value of the specified grid .
14 | ///
15 | /// An instance of representing the parent grid control.
16 | public GridControlRichTextValue(GridControl control) : base(control) { }
17 |
18 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlTextValue.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Newtonsoft.Json;
4 | using Newtonsoft.Json.Linq;
5 | using Skybrud.Umbraco.GridData.Json.Converters;
6 |
7 | namespace Skybrud.Umbraco.GridData.Models.Values;
8 |
9 | ///
10 | /// Class representing the text value of a control.
11 | ///
12 | [JsonConverter(typeof(GridControlValueStringConverter))]
13 | public class GridControlTextValue : GridControlValueBase {
14 |
15 | #region Properties
16 |
17 | ///
18 | /// Gets a string representing the value.
19 | ///
20 | public string Value { get; protected set; }
21 |
22 | ///
23 | /// Gets whether the value is valid. For an instance of , this means
24 | /// checking whether the specified text is not an empty string (using ).
25 | ///
26 | public override bool IsValid => !string.IsNullOrWhiteSpace(Value);
27 |
28 | #endregion
29 |
30 | #region Constructors
31 |
32 | ///
33 | /// Initializes a new instance based on the value of the specified grid .
34 | ///
35 | /// An instance of representing the parent grid control.
36 | public GridControlTextValue(GridControl control) : base(control) {
37 | Value = Json.Value() ?? string.Empty;
38 | }
39 |
40 | #endregion
41 |
42 | #region Member methods
43 |
44 | ///
45 | /// Writes a string representation of this value to .
46 | ///
47 | /// The current grid context.
48 | /// The writer.
49 | public override void WriteSearchableText(GridContext context, TextWriter writer) {
50 | writer.WriteLine(Value);
51 | }
52 |
53 | ///
54 | /// Returns a string representation of this value.
55 | ///
56 | /// The current grid context.
57 | /// A string representation of this value.
58 | public override string GetSearchableText(GridContext context) {
59 | StringBuilder sb = new();
60 | using TextWriter writer = new StringWriter(sb);
61 | WriteSearchableText(context, writer);
62 | return sb.ToString();
63 | }
64 |
65 | ///
66 | /// Gets a string representing the raw value of the control.
67 | ///
68 | /// An instance of .
69 | public override string ToString() {
70 | return Value;
71 | }
72 |
73 | #endregion
74 |
75 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/GridControlValueBase.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Newtonsoft.Json;
4 | using Newtonsoft.Json.Linq;
5 |
6 | namespace Skybrud.Umbraco.GridData.Models.Values;
7 |
8 | ///
9 | /// Abstract class with a basic implementation of the interface.
10 | ///
11 | public abstract class GridControlValueBase : IGridControlValue {
12 |
13 | #region Properties
14 |
15 | ///
16 | /// Gets a reference to the underlying instance of the value was parsed from.
17 | ///
18 | [JsonIgnore]
19 | public JToken Json { get; }
20 |
21 | ///
22 | /// Gets a reference to the parent .
23 | ///
24 | [JsonIgnore]
25 | public GridControl Control { get; }
26 |
27 | ///
28 | /// Gets whether the control is valid (e.g. whether it has a value).
29 | ///
30 | [JsonIgnore]
31 | public virtual bool IsValid => true;
32 |
33 | #endregion
34 |
35 | #region Constructors
36 |
37 | ///
38 | /// Initializes a new instance based on the specified object.
39 | ///
40 | /// An instance of representing the value of the control.
41 | /// An instance of representing the parent grid control.
42 | protected GridControlValueBase(JToken json, GridControl control) {
43 | Json = json;
44 | Control = control;
45 | }
46 |
47 | ///
48 | /// Initializes a new instance based on the specified object.
49 | ///
50 | /// An instance of representing the parent grid control.
51 | protected GridControlValueBase(GridControl control) {
52 | Json = control.JObject.GetValue("value")!;
53 | Control = control;
54 | }
55 |
56 | #endregion
57 |
58 | #region Member methods
59 |
60 | ///
61 | /// Writes a string representation of this value to .
62 | ///
63 | /// The current grid context.
64 | /// The writer.
65 | public virtual void WriteSearchableText(GridContext context, TextWriter writer) { }
66 |
67 | ///
68 | /// Returns a string representation of this value.
69 | ///
70 | /// The current grid context.
71 | /// A string representation of this value.
72 | public virtual string GetSearchableText(GridContext context) {
73 | StringBuilder sb = new();
74 | using TextWriter writer = new StringWriter(sb);
75 | WriteSearchableText(context, writer);
76 | return sb.ToString();
77 | }
78 |
79 | #endregion
80 |
81 | }
82 |
83 | ///
84 | /// Abstract class with a basic implementation of the interface.
85 | ///
86 | public abstract class GridControlValueBase : GridControlValueBase where TJson : JToken {
87 |
88 | ///
89 | /// Gets a reference to the underlying instance of the value was parsed from.
90 | ///
91 | [JsonIgnore]
92 | public new TJson Json => (TJson) base.Json;
93 |
94 | ///
95 | /// Initializes a new instance based on the specified object.
96 | ///
97 | /// An instance of representing the value of the control.
98 | /// An instance of representing the parent grid control.
99 | protected GridControlValueBase(TJson json, GridControl control) : base(json, control) { }
100 |
101 | ///
102 | /// Initializes a new instance based on the specified object.
103 | ///
104 | /// An instance of representing the parent grid control.
105 | protected GridControlValueBase(GridControl control) : base(control) { }
106 |
107 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Models/Values/IGridControlValue.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Newtonsoft.Json;
3 |
4 | namespace Skybrud.Umbraco.GridData.Models.Values;
5 |
6 | ///
7 | /// Interface describing a grid control value.
8 | ///
9 | public interface IGridControlValue {
10 |
11 | ///
12 | /// Gets a reference to the parent .
13 | ///
14 | [JsonIgnore]
15 | GridControl Control { get; }
16 |
17 | ///
18 | /// Gets whether the value of the control is valid.
19 | ///
20 | [JsonIgnore]
21 | bool IsValid { get; }
22 |
23 | ///
24 | /// Writes a textual representation of this control value to the specified .
25 | ///
26 | /// The current .
27 | /// The writer to write to.
28 | void WriteSearchableText(GridContext context, TextWriter writer);
29 |
30 | ///
31 | /// Gets the value of the control as a searchable text - e.g. to be used in Examine.
32 | ///
33 | /// The current grid context.
34 | /// An instance of with the value as a searchable text.
35 | string GetSearchableText(GridContext context);
36 |
37 | }
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/Skybrud.Umbraco.GridData.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 12.0
7 | net8.0
8 | enable
9 |
10 |
11 |
12 | 13.0.0
13 | build$([System.DateTime]::UtcNow.ToString(`yyyyMMddHHmm`))
14 | Limbo
15 | Anders Bjerner, René Pjengaard
16 | Copyright © $([System.DateTime]::UtcNow.ToString(`yyyy`))
17 | Skybrud Grid Data
18 | Strongly typed models for the grid in Umbraco.
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Skybrud.Umbraco.GridData
32 | Skybrud, Limbo, Umbraco, Grid, JSON, Umbraco-Marketplace
33 | MIT
34 | https://packages.skybrud.dk/skybrud.umbraco.griddata/
35 | Limbo.png
36 | git
37 | https://github.com/skybrud/Skybrud.Umbraco.GridData/
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/Skybrud.Umbraco.GridData/ValueConverters/GridValueConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json.Linq;
3 | using Skybrud.Essentials.Json.Newtonsoft;
4 | using Skybrud.Umbraco.GridData.Factories;
5 | using Skybrud.Umbraco.GridData.Models;
6 | using Umbraco.Cms.Core.Models.PublishedContent;
7 | using Umbraco.Cms.Core.PropertyEditors;
8 |
9 | namespace Skybrud.Umbraco.GridData.ValueConverters;
10 |
11 | ///
12 | /// Property value converter for the Umbraco Grid.
13 | ///
14 | class GridValueConverter : PropertyValueConverterBase {
15 |
16 | private readonly IGridFactory _factory;
17 |
18 | public GridValueConverter(IGridFactory factory) {
19 | _factory = factory;
20 | }
21 |
22 | ///
23 | /// Gets a value indicating whether the converter supports a property type.
24 | ///
25 | /// The property type.
26 | /// A value indicating whether the converter supports a property type.
27 | public override bool IsConverter(IPublishedPropertyType propertyType) {
28 | return propertyType.EditorAlias == "Umbraco.Grid";
29 | }
30 |
31 | public override object? ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview) {
32 |
33 | if (source is string { Length: > 0 } json && json[0] == '{') {
34 | return JsonUtils.ParseJsonObject(json);
35 | }
36 |
37 | return null;
38 |
39 | }
40 |
41 | public override object? ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) {
42 | return inter is JObject json ? _factory.CreateGridModel(owner, propertyType, json, preview) : null;
43 | }
44 |
45 | public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) {
46 | return PropertyCacheLevel.Snapshot;
47 | }
48 |
49 | ///
50 | /// Gets the type of values returned by the converter.
51 | ///
52 | /// The property type.
53 | /// The CLR type of values returned by the converter.
54 | public override Type GetPropertyValueType(IPublishedPropertyType propertyType) {
55 | return typeof(GridDataModel);
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/src/build/Limbo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skybrud/Skybrud.Umbraco.GridData/24e85ae1fc414cc56ea5550da9e1073eeab0e6ac/src/build/Limbo.png
--------------------------------------------------------------------------------