From b8066ab3aa6869e56581618f7403cf1be3c2485f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sun, 10 Apr 2022 03:35:33 +0200 Subject: [PATCH 1/6] update gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index d7c444cb..176a458f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -* -crlf \ No newline at end of file +* text=auto From 883c84082cc3a1443cd599d9dfd7bdc3bb895d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sun, 10 Apr 2022 04:20:35 +0200 Subject: [PATCH 2/6] renormalized --- .github/workflows/publish.yml | 24 +- .gitignore | 14 +- .../CodeGenerationDisplayDriver.cs | 504 +++++++++--------- .../ContentTypeMigrationsViewModel.cs | 18 +- Extensions/CodeGeneration/Startup.cs | 40 +- Extensions/ContentTypes/ContentTypes.cs | 12 +- Extensions/ContentTypes/Migrations.cs | 98 ++-- Extensions/ContentTypes/Startup.cs | 38 +- .../EmailSenderShellScopeExtensions.cs | 80 +-- Extensions/Emails/Models/EmailParameters.cs | 28 +- .../Emails/Services/IEmailTemplateService.cs | 34 +- .../ShapeBasedEmailTemplateService.cs | 54 +- Extensions/Emails/Startup.cs | 48 +- .../Drivers/AdditionalStylingPartDisplay.cs | 84 +-- .../Flows/FlowPartShapeTableProvider.cs | 20 +- .../Handlers/AdditionalStylingPartHandler.cs | 56 +- .../Flows/Models/AdditionalStylingPart.cs | 18 +- Extensions/Flows/Startup.cs | 62 +-- .../StrictSecuritySettingsDisplayDriver.cs | 70 +-- .../Security/Models/StrictSecuritySettings.cs | 12 +- ...tSecurityPermissionAuthorizationHandler.cs | 148 ++--- Extensions/Security/Startup.cs | 40 +- .../StrictSecuritySettingsViewModel.cs | 12 +- .../ShapeTracing/ShapeTracingShapeEvents.cs | 162 +++--- Extensions/ShapeTracing/Startup.cs | 24 +- Extensions/Widgets/Migrations.cs | 186 +++---- Extensions/Widgets/Startup.cs | 40 +- .../Widgets/ViewModels/MenuWidgetViewModel.cs | 44 +- Extensions/Widgets/WidgetTypes.cs | 20 +- FeatureIds.cs | 28 +- Licence.md | 28 +- Lombiq.HelpfulExtensions.csproj | 106 ++-- Manifest.cs | 164 +++--- Models/BootstrapAccordionItem.cs | 20 +- Readme.md | 246 ++++----- Views/AdditionalStylingPart.Edit.cshtml | 32 +- Views/BootstrapAccordion.cshtml | 106 ++-- Views/ContentTypeMigrations.Edit.cshtml | 58 +- Views/Layout-EmailTemplate.cshtml | 88 +-- ...iq.HelpfulExtensions.Flows.FlowPart.cshtml | 86 +-- Views/MenuWidget.cshtml | 178 +++---- Views/StrictSecuritySetting.Edit.cshtml | 46 +- Views/_ViewImports.cshtml | 10 +- placement.json | 16 +- 44 files changed, 1601 insertions(+), 1601 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7d2e014f..f64e9da9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,12 +1,12 @@ -name: publish - -on: - push: - tags: - - v* - -jobs: - call-publish-workflow: - uses: Lombiq/GitHub-Actions/.github/workflows/publish.yml@dev - secrets: - API_KEY: ${{ secrets.DEFAULT_NUGET_PUBLISH_API_KEY }} +name: publish + +on: + push: + tags: + - v* + +jobs: + call-publish-workflow: + uses: Lombiq/GitHub-Actions/.github/workflows/publish.yml@dev + secrets: + API_KEY: ${{ secrets.DEFAULT_NUGET_PUBLISH_API_KEY }} diff --git a/.gitignore b/.gitignore index dad29bbe..7f30a0ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -.vs/ -obj/ -bin/ -artifacts/ -wwwroot/ -node_modules/ -*.user +.vs/ +obj/ +bin/ +artifacts/ +wwwroot/ +node_modules/ +*.user .pnpm-debug.log \ No newline at end of file diff --git a/Extensions/CodeGeneration/CodeGenerationDisplayDriver.cs b/Extensions/CodeGeneration/CodeGenerationDisplayDriver.cs index 2c251df1..3bad3d7b 100644 --- a/Extensions/CodeGeneration/CodeGenerationDisplayDriver.cs +++ b/Extensions/CodeGeneration/CodeGenerationDisplayDriver.cs @@ -1,252 +1,252 @@ -using Microsoft.Extensions.Localization; -using Newtonsoft.Json.Linq; -using OrchardCore.ContentManagement.Metadata.Models; -using OrchardCore.ContentManagement.Metadata.Settings; -using OrchardCore.ContentTypes.Editors; -using OrchardCore.DisplayManagement.Views; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace Lombiq.HelpfulExtensions.Extensions.CodeGeneration; - -public class CodeGenerationDisplayDriver : ContentTypeDefinitionDisplayDriver -{ - private readonly IStringLocalizer T; - - public CodeGenerationDisplayDriver(IStringLocalizer stringLocalizer) => - T = stringLocalizer; - - public override IDisplayResult Edit(ContentTypeDefinition model) => - Initialize( - "ContentTypeMigrations_Edit", - viewModel => viewModel.MigrationCodeLazy = new Lazy(() => - { - var codeBuilder = new StringBuilder(); - - // Building the code for the type. - var name = model.Name; - - // This would be great in a Helpful Libraries extension method but unless we construct and manage an - // StringBuilder.AppendInterpolatedStringHandler instance by hand (to be able to use pass on a - // FormattableString received from here to StringBuilder.AppendLine(IFormatProvider? provider, ref - // AppendInterpolatedStringHandler handler)) it won't work. - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"_contentDefinitionManager.AlterTypeDefinition(\"{name}\", type => type"); - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .DisplayedAs(\"{model.DisplayName}\")"); - - GenerateCodeForSettings(codeBuilder, model.GetSettings()); - AddSettingsWithout(codeBuilder, model.Settings, 4); - GenerateCodeForParts(codeBuilder, model.Parts); - codeBuilder.AppendLine(");"); - - GenerateCodeForPartsWithFields(codeBuilder, model.Parts); - - return codeBuilder.ToString(); - })) - .Location("Content:7"); - - private void GenerateCodeForParts(StringBuilder codeBuilder, IEnumerable parts) - { - foreach (var part in parts) - { - var partSettings = part.GetSettings(); - - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .WithPart(\"{part.Name}\", part => part"); - - var partStartingLength = codeBuilder.Length; - - AddWithLine(codeBuilder, nameof(partSettings.DisplayName), partSettings.DisplayName); - AddWithLine(codeBuilder, nameof(partSettings.Description), partSettings.Description); - AddWithLine(codeBuilder, nameof(partSettings.Position), partSettings.Position); - AddWithLine(codeBuilder, nameof(partSettings.DisplayMode), partSettings.DisplayMode); - AddWithLine(codeBuilder, nameof(partSettings.Editor), partSettings.Editor); - - AddSettingsWithout(codeBuilder, part.Settings, 8); - - // Checking if anything was added to the part's settings. - if (codeBuilder.Length == partStartingLength) - { - // Remove ", part => part" and the line break. - codeBuilder.Length -= 16; - codeBuilder.Append(")" + Environment.NewLine); - } - else - { - codeBuilder.AppendLine(" )"); - } - } - } - - /// - /// Building those parts that have fields separately (fields can't be configured inline in types). - /// - private void GenerateCodeForPartsWithFields( - StringBuilder codeBuilder, - IEnumerable parts) - { - var partDefinitions = parts - .Where(part => part.PartDefinition.Fields.Any()) - .Select(part => part.PartDefinition); - foreach (var part in partDefinitions) - { - codeBuilder.AppendLine(); - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"_contentDefinitionManager.AlterPartDefinition(\"{part.Name}\", part => part"); - - var partSettings = part.GetSettings(); - if (partSettings.Attachable) codeBuilder.AppendLine(" .Attachable()"); - if (partSettings.Reusable) codeBuilder.AppendLine(" .Reusable()"); - - AddWithLine(codeBuilder, nameof(partSettings.DisplayName), partSettings.DisplayName); - AddWithLine(codeBuilder, nameof(partSettings.Description), partSettings.Description); - AddWithLine(codeBuilder, nameof(partSettings.DefaultPosition), partSettings.DefaultPosition); - - AddSettingsWithout(codeBuilder, part.Settings, 4); - - foreach (var field in part.Fields) - { - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .WithField(\"{field.Name}\", field => field"); - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .OfType(\"{field.FieldDefinition.Name}\")"); - - var fieldSettings = field.GetSettings(); - AddWithLine(codeBuilder, nameof(fieldSettings.DisplayName), fieldSettings.DisplayName); - AddWithLine(codeBuilder, nameof(fieldSettings.Description), fieldSettings.Description); - AddWithLine(codeBuilder, nameof(fieldSettings.Editor), fieldSettings.Editor); - AddWithLine(codeBuilder, nameof(fieldSettings.DisplayMode), fieldSettings.DisplayMode); - AddWithLine(codeBuilder, nameof(fieldSettings.Position), fieldSettings.Position); - - AddSettingsWithout(codeBuilder, field.Settings, 8); - - codeBuilder.AppendLine(" )"); - } - - codeBuilder.AppendLine(");"); - } - } - - private string ConvertJToken(JToken jToken, int indentationDepth) - { - switch (jToken) - { - case JValue jValue: - var value = jValue.Value; - return value switch - { - bool boolValue => boolValue ? "true" : "false", - string => $"\"{value}\"", - _ => value?.ToString()?.Replace(',', '.'), // Replace decimal commas. - }; - case JArray jArray: - return ConvertJArray(jArray, indentationDepth); - case JObject jObject: - return ConvertJObject(jObject, indentationDepth); - default: - throw new NotSupportedException($"Settings values of type {jToken.GetType()} are not supported."); - } - } - - private string ConvertJArray(JArray jArray, int indentationDepth) - { - var indentation = new string(' ', indentationDepth + 4); - - var items = jArray.Select(item => ConvertJToken(item, indentationDepth + 8)).ToList(); - - // If the items are formatted (for ListValueOption) then don't inject line-by-line formatting. - if (items.Any(item => item.ContainsOrdinalIgnoreCase(Environment.NewLine))) - { - var token = string.Join(string.Empty, items); - return $"new[]\n{indentation}{{\n{token}{indentation}}}"; - } - - // Otherwise, make sure that we have proper formatting for string arrays. - var stringArrayCodeBuilder = new StringBuilder("new[]"); - stringArrayCodeBuilder.AppendLine(); - stringArrayCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{indentation}{{"); - - var itemIndentation = new string(' ', indentationDepth + 8); - - foreach (var item in items) - { - stringArrayCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{itemIndentation}{item},"); - } - - stringArrayCodeBuilder.Append(CultureInfo.InvariantCulture, $"{indentation}}}"); - - return stringArrayCodeBuilder.ToString(); - } - - private string ConvertJObject(JObject jObject, int indentationDepth) - { - var braceIndentation = new string(' ', indentationDepth); - var propertyIndentation = new string(' ', indentationDepth + 4); - if (jObject["name"] != null && jObject["value"] != null) - { - var objectCodeBuilder = new StringBuilder(); - objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{braceIndentation}new ListValueOption"); - objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{braceIndentation}{{"); - objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{propertyIndentation}Name = \"{jObject["name"]}\","); - objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{propertyIndentation}Value = \"{jObject["value"]}\","); - objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{braceIndentation}}},"); - - return objectCodeBuilder.ToString(); - } - - // Using a quoted string so it doesn't mess up the syntax highlighting of the rest of the code. - return T["\"FIX ME! Couldn't determine the actual type to instantiate.\" {0}", jObject.ToString()]; - } - - private void AddSettingsWithout(StringBuilder codeBuilder, JObject settings, int indentationDepth) - { - var indentation = new string(' ', indentationDepth); - - var filteredSettings = ((IEnumerable>)settings) - .Where(setting => setting.Key != typeof(T).Name); - - foreach (var setting in filteredSettings) - { - var properties = setting.Value.Where(property => property is JProperty).Cast().ToArray(); - - if (properties.Length == 0) continue; - - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{indentation}.WithSettings(new {setting.Key}"); - codeBuilder.AppendLine(indentation + "{"); - - // This doesn't support multi-level object hierarchies for settings but come on, who uses complex settings - // objects? - for (int i = 0; i < properties.Length; i++) - { - var property = properties[i]; - - var propertyValue = ConvertJToken(property.Value, indentationDepth); - - propertyValue ??= "\"\""; - - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{indentation} {property.Name} = {propertyValue},"); - } - - codeBuilder.AppendLine(indentation + "})"); - } - } - - private static void GenerateCodeForSettings(StringBuilder codeBuilder, ContentTypeSettings contentTypeSettings) - { - if (contentTypeSettings.Creatable) codeBuilder.AppendLine(" .Creatable()"); - if (contentTypeSettings.Listable) codeBuilder.AppendLine(" .Listable()"); - if (contentTypeSettings.Draftable) codeBuilder.AppendLine(" .Draftable()"); - if (contentTypeSettings.Versionable) codeBuilder.AppendLine(" .Versionable()"); - if (contentTypeSettings.Securable) codeBuilder.AppendLine(" .Securable()"); - if (!string.IsNullOrEmpty(contentTypeSettings.Stereotype)) - { - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .Stereotype(\"{contentTypeSettings.Stereotype}\")"); - } - } - - private static void AddWithLine(StringBuilder codeBuilder, string name, string value) - { - if (!string.IsNullOrEmpty(value)) - { - codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .With{name}(\"{value}\")"); - } - } -} +using Microsoft.Extensions.Localization; +using Newtonsoft.Json.Linq; +using OrchardCore.ContentManagement.Metadata.Models; +using OrchardCore.ContentManagement.Metadata.Settings; +using OrchardCore.ContentTypes.Editors; +using OrchardCore.DisplayManagement.Views; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Lombiq.HelpfulExtensions.Extensions.CodeGeneration; + +public class CodeGenerationDisplayDriver : ContentTypeDefinitionDisplayDriver +{ + private readonly IStringLocalizer T; + + public CodeGenerationDisplayDriver(IStringLocalizer stringLocalizer) => + T = stringLocalizer; + + public override IDisplayResult Edit(ContentTypeDefinition model) => + Initialize( + "ContentTypeMigrations_Edit", + viewModel => viewModel.MigrationCodeLazy = new Lazy(() => + { + var codeBuilder = new StringBuilder(); + + // Building the code for the type. + var name = model.Name; + + // This would be great in a Helpful Libraries extension method but unless we construct and manage an + // StringBuilder.AppendInterpolatedStringHandler instance by hand (to be able to use pass on a + // FormattableString received from here to StringBuilder.AppendLine(IFormatProvider? provider, ref + // AppendInterpolatedStringHandler handler)) it won't work. + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"_contentDefinitionManager.AlterTypeDefinition(\"{name}\", type => type"); + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .DisplayedAs(\"{model.DisplayName}\")"); + + GenerateCodeForSettings(codeBuilder, model.GetSettings()); + AddSettingsWithout(codeBuilder, model.Settings, 4); + GenerateCodeForParts(codeBuilder, model.Parts); + codeBuilder.AppendLine(");"); + + GenerateCodeForPartsWithFields(codeBuilder, model.Parts); + + return codeBuilder.ToString(); + })) + .Location("Content:7"); + + private void GenerateCodeForParts(StringBuilder codeBuilder, IEnumerable parts) + { + foreach (var part in parts) + { + var partSettings = part.GetSettings(); + + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .WithPart(\"{part.Name}\", part => part"); + + var partStartingLength = codeBuilder.Length; + + AddWithLine(codeBuilder, nameof(partSettings.DisplayName), partSettings.DisplayName); + AddWithLine(codeBuilder, nameof(partSettings.Description), partSettings.Description); + AddWithLine(codeBuilder, nameof(partSettings.Position), partSettings.Position); + AddWithLine(codeBuilder, nameof(partSettings.DisplayMode), partSettings.DisplayMode); + AddWithLine(codeBuilder, nameof(partSettings.Editor), partSettings.Editor); + + AddSettingsWithout(codeBuilder, part.Settings, 8); + + // Checking if anything was added to the part's settings. + if (codeBuilder.Length == partStartingLength) + { + // Remove ", part => part" and the line break. + codeBuilder.Length -= 16; + codeBuilder.Append(")" + Environment.NewLine); + } + else + { + codeBuilder.AppendLine(" )"); + } + } + } + + /// + /// Building those parts that have fields separately (fields can't be configured inline in types). + /// + private void GenerateCodeForPartsWithFields( + StringBuilder codeBuilder, + IEnumerable parts) + { + var partDefinitions = parts + .Where(part => part.PartDefinition.Fields.Any()) + .Select(part => part.PartDefinition); + foreach (var part in partDefinitions) + { + codeBuilder.AppendLine(); + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"_contentDefinitionManager.AlterPartDefinition(\"{part.Name}\", part => part"); + + var partSettings = part.GetSettings(); + if (partSettings.Attachable) codeBuilder.AppendLine(" .Attachable()"); + if (partSettings.Reusable) codeBuilder.AppendLine(" .Reusable()"); + + AddWithLine(codeBuilder, nameof(partSettings.DisplayName), partSettings.DisplayName); + AddWithLine(codeBuilder, nameof(partSettings.Description), partSettings.Description); + AddWithLine(codeBuilder, nameof(partSettings.DefaultPosition), partSettings.DefaultPosition); + + AddSettingsWithout(codeBuilder, part.Settings, 4); + + foreach (var field in part.Fields) + { + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .WithField(\"{field.Name}\", field => field"); + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .OfType(\"{field.FieldDefinition.Name}\")"); + + var fieldSettings = field.GetSettings(); + AddWithLine(codeBuilder, nameof(fieldSettings.DisplayName), fieldSettings.DisplayName); + AddWithLine(codeBuilder, nameof(fieldSettings.Description), fieldSettings.Description); + AddWithLine(codeBuilder, nameof(fieldSettings.Editor), fieldSettings.Editor); + AddWithLine(codeBuilder, nameof(fieldSettings.DisplayMode), fieldSettings.DisplayMode); + AddWithLine(codeBuilder, nameof(fieldSettings.Position), fieldSettings.Position); + + AddSettingsWithout(codeBuilder, field.Settings, 8); + + codeBuilder.AppendLine(" )"); + } + + codeBuilder.AppendLine(");"); + } + } + + private string ConvertJToken(JToken jToken, int indentationDepth) + { + switch (jToken) + { + case JValue jValue: + var value = jValue.Value; + return value switch + { + bool boolValue => boolValue ? "true" : "false", + string => $"\"{value}\"", + _ => value?.ToString()?.Replace(',', '.'), // Replace decimal commas. + }; + case JArray jArray: + return ConvertJArray(jArray, indentationDepth); + case JObject jObject: + return ConvertJObject(jObject, indentationDepth); + default: + throw new NotSupportedException($"Settings values of type {jToken.GetType()} are not supported."); + } + } + + private string ConvertJArray(JArray jArray, int indentationDepth) + { + var indentation = new string(' ', indentationDepth + 4); + + var items = jArray.Select(item => ConvertJToken(item, indentationDepth + 8)).ToList(); + + // If the items are formatted (for ListValueOption) then don't inject line-by-line formatting. + if (items.Any(item => item.ContainsOrdinalIgnoreCase(Environment.NewLine))) + { + var token = string.Join(string.Empty, items); + return $"new[]\n{indentation}{{\n{token}{indentation}}}"; + } + + // Otherwise, make sure that we have proper formatting for string arrays. + var stringArrayCodeBuilder = new StringBuilder("new[]"); + stringArrayCodeBuilder.AppendLine(); + stringArrayCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{indentation}{{"); + + var itemIndentation = new string(' ', indentationDepth + 8); + + foreach (var item in items) + { + stringArrayCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{itemIndentation}{item},"); + } + + stringArrayCodeBuilder.Append(CultureInfo.InvariantCulture, $"{indentation}}}"); + + return stringArrayCodeBuilder.ToString(); + } + + private string ConvertJObject(JObject jObject, int indentationDepth) + { + var braceIndentation = new string(' ', indentationDepth); + var propertyIndentation = new string(' ', indentationDepth + 4); + if (jObject["name"] != null && jObject["value"] != null) + { + var objectCodeBuilder = new StringBuilder(); + objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{braceIndentation}new ListValueOption"); + objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{braceIndentation}{{"); + objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{propertyIndentation}Name = \"{jObject["name"]}\","); + objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{propertyIndentation}Value = \"{jObject["value"]}\","); + objectCodeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{braceIndentation}}},"); + + return objectCodeBuilder.ToString(); + } + + // Using a quoted string so it doesn't mess up the syntax highlighting of the rest of the code. + return T["\"FIX ME! Couldn't determine the actual type to instantiate.\" {0}", jObject.ToString()]; + } + + private void AddSettingsWithout(StringBuilder codeBuilder, JObject settings, int indentationDepth) + { + var indentation = new string(' ', indentationDepth); + + var filteredSettings = ((IEnumerable>)settings) + .Where(setting => setting.Key != typeof(T).Name); + + foreach (var setting in filteredSettings) + { + var properties = setting.Value.Where(property => property is JProperty).Cast().ToArray(); + + if (properties.Length == 0) continue; + + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{indentation}.WithSettings(new {setting.Key}"); + codeBuilder.AppendLine(indentation + "{"); + + // This doesn't support multi-level object hierarchies for settings but come on, who uses complex settings + // objects? + for (int i = 0; i < properties.Length; i++) + { + var property = properties[i]; + + var propertyValue = ConvertJToken(property.Value, indentationDepth); + + propertyValue ??= "\"\""; + + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $"{indentation} {property.Name} = {propertyValue},"); + } + + codeBuilder.AppendLine(indentation + "})"); + } + } + + private static void GenerateCodeForSettings(StringBuilder codeBuilder, ContentTypeSettings contentTypeSettings) + { + if (contentTypeSettings.Creatable) codeBuilder.AppendLine(" .Creatable()"); + if (contentTypeSettings.Listable) codeBuilder.AppendLine(" .Listable()"); + if (contentTypeSettings.Draftable) codeBuilder.AppendLine(" .Draftable()"); + if (contentTypeSettings.Versionable) codeBuilder.AppendLine(" .Versionable()"); + if (contentTypeSettings.Securable) codeBuilder.AppendLine(" .Securable()"); + if (!string.IsNullOrEmpty(contentTypeSettings.Stereotype)) + { + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .Stereotype(\"{contentTypeSettings.Stereotype}\")"); + } + } + + private static void AddWithLine(StringBuilder codeBuilder, string name, string value) + { + if (!string.IsNullOrEmpty(value)) + { + codeBuilder.AppendLine(CultureInfo.InvariantCulture, $" .With{name}(\"{value}\")"); + } + } +} diff --git a/Extensions/CodeGeneration/ContentTypeMigrationsViewModel.cs b/Extensions/CodeGeneration/ContentTypeMigrationsViewModel.cs index 8fe760b5..ac7e4518 100644 --- a/Extensions/CodeGeneration/ContentTypeMigrationsViewModel.cs +++ b/Extensions/CodeGeneration/ContentTypeMigrationsViewModel.cs @@ -1,9 +1,9 @@ -using System; - -namespace Lombiq.HelpfulExtensions.Extensions.CodeGeneration; - -public class ContentTypeMigrationsViewModel -{ - internal Lazy MigrationCodeLazy { get; set; } - public string MigrationCode => MigrationCodeLazy.Value; -} +using System; + +namespace Lombiq.HelpfulExtensions.Extensions.CodeGeneration; + +public class ContentTypeMigrationsViewModel +{ + internal Lazy MigrationCodeLazy { get; set; } + public string MigrationCode => MigrationCodeLazy.Value; +} diff --git a/Extensions/CodeGeneration/Startup.cs b/Extensions/CodeGeneration/Startup.cs index e3ad74d3..7bc42fed 100644 --- a/Extensions/CodeGeneration/Startup.cs +++ b/Extensions/CodeGeneration/Startup.cs @@ -1,20 +1,20 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.ContentTypes.Editors; -using OrchardCore.Modules; -using System; - -namespace Lombiq.HelpfulExtensions.Extensions.CodeGeneration; - -[Feature(FeatureIds.CodeGeneration)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) => - services.AddScoped(); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // No need for anything here yet. - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.ContentTypes.Editors; +using OrchardCore.Modules; +using System; + +namespace Lombiq.HelpfulExtensions.Extensions.CodeGeneration; + +[Feature(FeatureIds.CodeGeneration)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) => + services.AddScoped(); + + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // No need for anything here yet. + } +} diff --git a/Extensions/ContentTypes/ContentTypes.cs b/Extensions/ContentTypes/ContentTypes.cs index aa6f2fbd..8bd14ca9 100644 --- a/Extensions/ContentTypes/ContentTypes.cs +++ b/Extensions/ContentTypes/ContentTypes.cs @@ -1,6 +1,6 @@ -namespace Lombiq.HelpfulExtensions.Extensions.ContentTypes; - -public static class ContentTypes -{ - public const string Page = nameof(Page); -} +namespace Lombiq.HelpfulExtensions.Extensions.ContentTypes; + +public static class ContentTypes +{ + public const string Page = nameof(Page); +} diff --git a/Extensions/ContentTypes/Migrations.cs b/Extensions/ContentTypes/Migrations.cs index 9674e661..9df320a2 100644 --- a/Extensions/ContentTypes/Migrations.cs +++ b/Extensions/ContentTypes/Migrations.cs @@ -1,49 +1,49 @@ -using OrchardCore.Autoroute.Models; -using OrchardCore.ContentManagement.Metadata; -using OrchardCore.ContentManagement.Metadata.Settings; -using OrchardCore.Data.Migration; -using static Lombiq.HelpfulExtensions.Extensions.ContentTypes.ContentTypes; - -namespace Lombiq.HelpfulExtensions.Extensions.ContentTypes; - -public class Migrations : DataMigration -{ - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations(IContentDefinitionManager contentDefinitionManager) => - _contentDefinitionManager = contentDefinitionManager; - - public int Create() - { - _contentDefinitionManager.AlterTypeDefinition(Page, builder => builder - .Creatable() - .Securable() - .Draftable() - .Listable() - .Versionable() - .WithPart("TitlePart", part => part.WithPosition("0")) - .WithPart("AutoroutePart", part => part - .WithPosition("1") - .WithSettings(new AutoroutePartSettings - { - ShowHomepageOption = true, - AllowCustomPath = true, - }) - ) - .WithPart("FlowPart", part => part.WithPosition("2")) - ); - - return 2; - } - - public int UpdateFrom1() - { - _contentDefinitionManager.AlterTypeDefinition(Page, builder => builder - .WithPart("TitlePart", part => part.WithPosition("0")) - .WithPart("AutoroutePart", part => part.WithPosition("1")) - .WithPart("FlowPart", part => part.WithPosition("2")) - ); - - return 2; - } -} +using OrchardCore.Autoroute.Models; +using OrchardCore.ContentManagement.Metadata; +using OrchardCore.ContentManagement.Metadata.Settings; +using OrchardCore.Data.Migration; +using static Lombiq.HelpfulExtensions.Extensions.ContentTypes.ContentTypes; + +namespace Lombiq.HelpfulExtensions.Extensions.ContentTypes; + +public class Migrations : DataMigration +{ + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) => + _contentDefinitionManager = contentDefinitionManager; + + public int Create() + { + _contentDefinitionManager.AlterTypeDefinition(Page, builder => builder + .Creatable() + .Securable() + .Draftable() + .Listable() + .Versionable() + .WithPart("TitlePart", part => part.WithPosition("0")) + .WithPart("AutoroutePart", part => part + .WithPosition("1") + .WithSettings(new AutoroutePartSettings + { + ShowHomepageOption = true, + AllowCustomPath = true, + }) + ) + .WithPart("FlowPart", part => part.WithPosition("2")) + ); + + return 2; + } + + public int UpdateFrom1() + { + _contentDefinitionManager.AlterTypeDefinition(Page, builder => builder + .WithPart("TitlePart", part => part.WithPosition("0")) + .WithPart("AutoroutePart", part => part.WithPosition("1")) + .WithPart("FlowPart", part => part.WithPosition("2")) + ); + + return 2; + } +} diff --git a/Extensions/ContentTypes/Startup.cs b/Extensions/ContentTypes/Startup.cs index 36033547..d3d219b5 100644 --- a/Extensions/ContentTypes/Startup.cs +++ b/Extensions/ContentTypes/Startup.cs @@ -1,19 +1,19 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.Data.Migration; -using OrchardCore.Modules; -using System; - -namespace Lombiq.HelpfulExtensions.Extensions.ContentTypes; - -[Feature(FeatureIds.ContentTypes)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) => services.AddScoped(); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // No need for anything here yet. - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Data.Migration; +using OrchardCore.Modules; +using System; + +namespace Lombiq.HelpfulExtensions.Extensions.ContentTypes; + +[Feature(FeatureIds.ContentTypes)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) => services.AddScoped(); + + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // No need for anything here yet. + } +} diff --git a/Extensions/Emails/Extensions/EmailSenderShellScopeExtensions.cs b/Extensions/Emails/Extensions/EmailSenderShellScopeExtensions.cs index 06b58528..27f7136d 100644 --- a/Extensions/Emails/Extensions/EmailSenderShellScopeExtensions.cs +++ b/Extensions/Emails/Extensions/EmailSenderShellScopeExtensions.cs @@ -1,40 +1,40 @@ -using Lombiq.HelpfulExtensions.Extensions.Emails.Models; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using OrchardCore.Email; -using OrchardCore.Environment.Shell.Scope; -using System.Collections.Generic; -using System.Linq; - -namespace Lombiq.HelpfulExtensions.Extensions.Emails.Extensions; - -public static class EmailSenderShellScopeExtensions -{ - /// - /// Sends an HTML email after the current shell scope has ended. If any errors occur during the process they will be - /// logged. - /// - /// Parameters required for sending emails (e.g., recipients, subject, CC). - public static void SendEmailDeferred(this ShellScope shellScope, EmailParameters parameters) => - shellScope.AddDeferredTask(async scope => - { - var smtpService = scope.ServiceProvider.GetRequiredService(); - var result = await smtpService.SendAsync(new MailMessage - { - Sender = parameters.Sender, - To = parameters.To?.Join(","), - Cc = parameters.Cc?.Join(","), - Bcc = parameters.Bcc?.Join(","), - Subject = parameters.Subject, - ReplyTo = parameters.ReplyTo, - Body = parameters.Body, - IsBodyHtml = true, - }); - - if (!result.Succeeded) - { - var logger = scope.ServiceProvider.GetRequiredService>(); - logger.LogError("Email sending was unsuccessful: {Error}", result.Errors.Select(error => error.ToString()).Join()); - } - }); -} +using Lombiq.HelpfulExtensions.Extensions.Emails.Models; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OrchardCore.Email; +using OrchardCore.Environment.Shell.Scope; +using System.Collections.Generic; +using System.Linq; + +namespace Lombiq.HelpfulExtensions.Extensions.Emails.Extensions; + +public static class EmailSenderShellScopeExtensions +{ + /// + /// Sends an HTML email after the current shell scope has ended. If any errors occur during the process they will be + /// logged. + /// + /// Parameters required for sending emails (e.g., recipients, subject, CC). + public static void SendEmailDeferred(this ShellScope shellScope, EmailParameters parameters) => + shellScope.AddDeferredTask(async scope => + { + var smtpService = scope.ServiceProvider.GetRequiredService(); + var result = await smtpService.SendAsync(new MailMessage + { + Sender = parameters.Sender, + To = parameters.To?.Join(","), + Cc = parameters.Cc?.Join(","), + Bcc = parameters.Bcc?.Join(","), + Subject = parameters.Subject, + ReplyTo = parameters.ReplyTo, + Body = parameters.Body, + IsBodyHtml = true, + }); + + if (!result.Succeeded) + { + var logger = scope.ServiceProvider.GetRequiredService>(); + logger.LogError("Email sending was unsuccessful: {Error}", result.Errors.Select(error => error.ToString()).Join()); + } + }); +} diff --git a/Extensions/Emails/Models/EmailParameters.cs b/Extensions/Emails/Models/EmailParameters.cs index bcb02b7c..a1a05c69 100644 --- a/Extensions/Emails/Models/EmailParameters.cs +++ b/Extensions/Emails/Models/EmailParameters.cs @@ -1,14 +1,14 @@ -using System.Collections.Generic; - -namespace Lombiq.HelpfulExtensions.Extensions.Emails.Models; - -public class EmailParameters -{ - public string Sender { get; set; } - public IEnumerable To { get; set; } - public IEnumerable Cc { get; set; } - public IEnumerable Bcc { get; set; } - public string Subject { get; set; } - public string Body { get; set; } - public string ReplyTo { get; set; } -} +using System.Collections.Generic; + +namespace Lombiq.HelpfulExtensions.Extensions.Emails.Models; + +public class EmailParameters +{ + public string Sender { get; set; } + public IEnumerable To { get; set; } + public IEnumerable Cc { get; set; } + public IEnumerable Bcc { get; set; } + public string Subject { get; set; } + public string Body { get; set; } + public string ReplyTo { get; set; } +} diff --git a/Extensions/Emails/Services/IEmailTemplateService.cs b/Extensions/Emails/Services/IEmailTemplateService.cs index a3930dfa..2e770606 100644 --- a/Extensions/Emails/Services/IEmailTemplateService.cs +++ b/Extensions/Emails/Services/IEmailTemplateService.cs @@ -1,17 +1,17 @@ -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.Emails.Services; - -/// -/// Service for managing email templates. -/// -public interface IEmailTemplateService -{ - /// - /// Renders an email template content identified by its ID. - /// - /// ID of the email template. - /// Optional model used as replacements in the email template. - /// Rendered email template. - Task RenderEmailTemplateAsync(string emailTemplateId, object model = null); -} +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.Emails.Services; + +/// +/// Service for managing email templates. +/// +public interface IEmailTemplateService +{ + /// + /// Renders an email template content identified by its ID. + /// + /// ID of the email template. + /// Optional model used as replacements in the email template. + /// Rendered email template. + Task RenderEmailTemplateAsync(string emailTemplateId, object model = null); +} diff --git a/Extensions/Emails/Services/ShapeBasedEmailTemplateService.cs b/Extensions/Emails/Services/ShapeBasedEmailTemplateService.cs index fb68acef..57d3dd92 100644 --- a/Extensions/Emails/Services/ShapeBasedEmailTemplateService.cs +++ b/Extensions/Emails/Services/ShapeBasedEmailTemplateService.cs @@ -1,27 +1,27 @@ -using Lombiq.HelpfulLibraries.Common.Utilities; -using Lombiq.HelpfulLibraries.OrchardCore.Shapes; -using OrchardCore.DisplayManagement; -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.Emails.Services; - -public class ShapeBasedEmailTemplateService : IEmailTemplateService -{ - private readonly IShapeFactory _shapeFactory; - private readonly IShapeRenderer _shapeRenderer; - - public ShapeBasedEmailTemplateService(IShapeFactory shapeFactory, IShapeRenderer shapeRenderer) - { - _shapeFactory = shapeFactory; - _shapeRenderer = shapeRenderer; - } - - public async Task RenderEmailTemplateAsync(string emailTemplateId, object model = null) - { - ExceptionHelpers.ThrowIfNull(emailTemplateId, nameof(emailTemplateId)); - - var shape = await _shapeFactory.CreateAsync($"EmailTemplate__{emailTemplateId}", Arguments.From(model ?? new { })); - - return await _shapeRenderer.RenderAsync(shape); - } -} +using Lombiq.HelpfulLibraries.Common.Utilities; +using Lombiq.HelpfulLibraries.OrchardCore.Shapes; +using OrchardCore.DisplayManagement; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.Emails.Services; + +public class ShapeBasedEmailTemplateService : IEmailTemplateService +{ + private readonly IShapeFactory _shapeFactory; + private readonly IShapeRenderer _shapeRenderer; + + public ShapeBasedEmailTemplateService(IShapeFactory shapeFactory, IShapeRenderer shapeRenderer) + { + _shapeFactory = shapeFactory; + _shapeRenderer = shapeRenderer; + } + + public async Task RenderEmailTemplateAsync(string emailTemplateId, object model = null) + { + ExceptionHelpers.ThrowIfNull(emailTemplateId, nameof(emailTemplateId)); + + var shape = await _shapeFactory.CreateAsync($"EmailTemplate__{emailTemplateId}", Arguments.From(model ?? new { })); + + return await _shapeRenderer.RenderAsync(shape); + } +} diff --git a/Extensions/Emails/Startup.cs b/Extensions/Emails/Startup.cs index 26bea85b..6af711be 100644 --- a/Extensions/Emails/Startup.cs +++ b/Extensions/Emails/Startup.cs @@ -1,24 +1,24 @@ -using Lombiq.HelpfulExtensions.Extensions.Emails.Services; -using Lombiq.HelpfulLibraries.OrchardCore.Shapes; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.Modules; -using System; - -namespace Lombiq.HelpfulExtensions.Extensions.Emails; - -[Feature(FeatureIds.Emails)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) - { - services.AddShapeRenderer(); - services.AddScoped(); - } - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // No need for anything here yet. - } -} +using Lombiq.HelpfulExtensions.Extensions.Emails.Services; +using Lombiq.HelpfulLibraries.OrchardCore.Shapes; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Modules; +using System; + +namespace Lombiq.HelpfulExtensions.Extensions.Emails; + +[Feature(FeatureIds.Emails)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddShapeRenderer(); + services.AddScoped(); + } + + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // No need for anything here yet. + } +} diff --git a/Extensions/Flows/Drivers/AdditionalStylingPartDisplay.cs b/Extensions/Flows/Drivers/AdditionalStylingPartDisplay.cs index b585f8dd..2bac368c 100644 --- a/Extensions/Flows/Drivers/AdditionalStylingPartDisplay.cs +++ b/Extensions/Flows/Drivers/AdditionalStylingPartDisplay.cs @@ -1,42 +1,42 @@ -using Lombiq.HelpfulExtensions.Extensions.Flows.Models; -using OrchardCore.ContentManagement; -using OrchardCore.ContentManagement.Display.ContentDisplay; -using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Views; -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.Flows.Drivers; - -public class AdditionalStylingPartDisplay : ContentDisplayDriver -{ - public override IDisplayResult Edit(ContentItem model, IUpdateModel updater) => - Initialize( - $"{nameof(AdditionalStylingPart)}_Edit", - viewModel => PopulateViewModel(model, viewModel)) - .Location("Footer:3"); - - public override async Task UpdateAsync(ContentItem model, IUpdateModel updater) - { - var additionalStylingPart = model.As(); - - if (additionalStylingPart == null) - { - return null; - } - - await model.AlterAsync(model => updater.TryUpdateModelAsync(model, Prefix)); - - return await EditAsync(model, updater); - } - - private static void PopulateViewModel(ContentItem model, AdditionalStylingPart viewModel) - { - var additionalStylingPart = model.As(); - - if (additionalStylingPart != null) - { - viewModel.CustomClasses = additionalStylingPart.CustomClasses; - viewModel.RemoveGridExtensionClasses = additionalStylingPart.RemoveGridExtensionClasses; - } - } -} +using Lombiq.HelpfulExtensions.Extensions.Flows.Models; +using OrchardCore.ContentManagement; +using OrchardCore.ContentManagement.Display.ContentDisplay; +using OrchardCore.DisplayManagement.ModelBinding; +using OrchardCore.DisplayManagement.Views; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.Flows.Drivers; + +public class AdditionalStylingPartDisplay : ContentDisplayDriver +{ + public override IDisplayResult Edit(ContentItem model, IUpdateModel updater) => + Initialize( + $"{nameof(AdditionalStylingPart)}_Edit", + viewModel => PopulateViewModel(model, viewModel)) + .Location("Footer:3"); + + public override async Task UpdateAsync(ContentItem model, IUpdateModel updater) + { + var additionalStylingPart = model.As(); + + if (additionalStylingPart == null) + { + return null; + } + + await model.AlterAsync(model => updater.TryUpdateModelAsync(model, Prefix)); + + return await EditAsync(model, updater); + } + + private static void PopulateViewModel(ContentItem model, AdditionalStylingPart viewModel) + { + var additionalStylingPart = model.As(); + + if (additionalStylingPart != null) + { + viewModel.CustomClasses = additionalStylingPart.CustomClasses; + viewModel.RemoveGridExtensionClasses = additionalStylingPart.RemoveGridExtensionClasses; + } + } +} diff --git a/Extensions/Flows/FlowPartShapeTableProvider.cs b/Extensions/Flows/FlowPartShapeTableProvider.cs index 3ad62a58..9216a177 100644 --- a/Extensions/Flows/FlowPartShapeTableProvider.cs +++ b/Extensions/Flows/FlowPartShapeTableProvider.cs @@ -1,10 +1,10 @@ -using OrchardCore.DisplayManagement.Descriptors; - -namespace Lombiq.HelpfulExtensions.Extensions.Flows; - -internal class FlowPartShapeTableProvider : IShapeTableProvider -{ - public void Discover(ShapeTableBuilder builder) => builder - .Describe("FlowPart") - .OnDisplaying(displaying => displaying.Shape.Metadata.Alternates.Add("Lombiq_HelpfulExtensions_Flows_FlowPart")); -} +using OrchardCore.DisplayManagement.Descriptors; + +namespace Lombiq.HelpfulExtensions.Extensions.Flows; + +internal class FlowPartShapeTableProvider : IShapeTableProvider +{ + public void Discover(ShapeTableBuilder builder) => builder + .Describe("FlowPart") + .OnDisplaying(displaying => displaying.Shape.Metadata.Alternates.Add("Lombiq_HelpfulExtensions_Flows_FlowPart")); +} diff --git a/Extensions/Flows/Handlers/AdditionalStylingPartHandler.cs b/Extensions/Flows/Handlers/AdditionalStylingPartHandler.cs index 2912f74b..8b43e9a3 100644 --- a/Extensions/Flows/Handlers/AdditionalStylingPartHandler.cs +++ b/Extensions/Flows/Handlers/AdditionalStylingPartHandler.cs @@ -1,28 +1,28 @@ -using Lombiq.HelpfulExtensions.Extensions.Flows.Models; -using OrchardCore.ContentManagement; -using OrchardCore.ContentManagement.Handlers; -using OrchardCore.ContentManagement.Metadata; -using OrchardCore.ContentManagement.Metadata.Settings; -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.Flows.Handlers; - -public class AdditionalStylingPartHandler : ContentHandlerBase -{ - private readonly IContentDefinitionManager _contentDefinitionManager; - - public AdditionalStylingPartHandler(IContentDefinitionManager contentDefinitionManager) => - _contentDefinitionManager = contentDefinitionManager; - - public override Task ActivatedAsync(ActivatedContentContext context) - { - if (!context.ContentItem.Has() && - _contentDefinitionManager.GetTypeDefinition(context.ContentItem.ContentType) - .GetSettings().Stereotype == "Widget") - { - context.ContentItem.Weld(); - } - - return Task.CompletedTask; - } -} +using Lombiq.HelpfulExtensions.Extensions.Flows.Models; +using OrchardCore.ContentManagement; +using OrchardCore.ContentManagement.Handlers; +using OrchardCore.ContentManagement.Metadata; +using OrchardCore.ContentManagement.Metadata.Settings; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.Flows.Handlers; + +public class AdditionalStylingPartHandler : ContentHandlerBase +{ + private readonly IContentDefinitionManager _contentDefinitionManager; + + public AdditionalStylingPartHandler(IContentDefinitionManager contentDefinitionManager) => + _contentDefinitionManager = contentDefinitionManager; + + public override Task ActivatedAsync(ActivatedContentContext context) + { + if (!context.ContentItem.Has() && + _contentDefinitionManager.GetTypeDefinition(context.ContentItem.ContentType) + .GetSettings().Stereotype == "Widget") + { + context.ContentItem.Weld(); + } + + return Task.CompletedTask; + } +} diff --git a/Extensions/Flows/Models/AdditionalStylingPart.cs b/Extensions/Flows/Models/AdditionalStylingPart.cs index 242fc2ab..acd1d1c1 100644 --- a/Extensions/Flows/Models/AdditionalStylingPart.cs +++ b/Extensions/Flows/Models/AdditionalStylingPart.cs @@ -1,9 +1,9 @@ -using OrchardCore.ContentManagement; - -namespace Lombiq.HelpfulExtensions.Extensions.Flows.Models; - -public class AdditionalStylingPart : ContentPart -{ - public string CustomClasses { get; set; } - public bool RemoveGridExtensionClasses { get; set; } -} +using OrchardCore.ContentManagement; + +namespace Lombiq.HelpfulExtensions.Extensions.Flows.Models; + +public class AdditionalStylingPart : ContentPart +{ + public string CustomClasses { get; set; } + public bool RemoveGridExtensionClasses { get; set; } +} diff --git a/Extensions/Flows/Startup.cs b/Extensions/Flows/Startup.cs index 199561ef..3bcc4430 100644 --- a/Extensions/Flows/Startup.cs +++ b/Extensions/Flows/Startup.cs @@ -1,31 +1,31 @@ -using Lombiq.HelpfulExtensions.Extensions.Flows.Drivers; -using Lombiq.HelpfulExtensions.Extensions.Flows.Handlers; -using Lombiq.HelpfulExtensions.Extensions.Flows.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.ContentManagement; -using OrchardCore.ContentManagement.Display.ContentDisplay; -using OrchardCore.ContentManagement.Handlers; -using OrchardCore.DisplayManagement.Descriptors; -using OrchardCore.Modules; -using System; - -namespace Lombiq.HelpfulExtensions.Extensions.Flows; - -[Feature(FeatureIds.Flows)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddContentPart(); - services.AddScoped(); - } - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // No need for anything here yet. - } -} +using Lombiq.HelpfulExtensions.Extensions.Flows.Drivers; +using Lombiq.HelpfulExtensions.Extensions.Flows.Handlers; +using Lombiq.HelpfulExtensions.Extensions.Flows.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.ContentManagement; +using OrchardCore.ContentManagement.Display.ContentDisplay; +using OrchardCore.ContentManagement.Handlers; +using OrchardCore.DisplayManagement.Descriptors; +using OrchardCore.Modules; +using System; + +namespace Lombiq.HelpfulExtensions.Extensions.Flows; + +[Feature(FeatureIds.Flows)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddContentPart(); + services.AddScoped(); + } + + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // No need for anything here yet. + } +} diff --git a/Extensions/Security/Driver/StrictSecuritySettingsDisplayDriver.cs b/Extensions/Security/Driver/StrictSecuritySettingsDisplayDriver.cs index 22ccdb6b..470ad8ad 100644 --- a/Extensions/Security/Driver/StrictSecuritySettingsDisplayDriver.cs +++ b/Extensions/Security/Driver/StrictSecuritySettingsDisplayDriver.cs @@ -1,35 +1,35 @@ -using Lombiq.HelpfulExtensions.Extensions.Security.Models; -using Lombiq.HelpfulExtensions.Extensions.Security.ViewModels; -using OrchardCore.ContentManagement.Metadata.Models; -using OrchardCore.ContentManagement.Metadata.Settings; -using OrchardCore.ContentTypes.Editors; -using OrchardCore.DisplayManagement.Views; -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.Security.Driver; - -public class StrictSecuritySettingsDisplayDriver : ContentTypeDefinitionDisplayDriver -{ - public override IDisplayResult Edit(ContentTypeDefinition model) => - Initialize("StrictSecuritySetting_Edit", viewModel => - { - var settings = model.GetSettings(); - - viewModel.Enabled = settings?.Enabled == true; - }).Location("Content:5"); - - public override async Task UpdateAsync(ContentTypeDefinition model, UpdateTypeEditorContext context) - { - var viewModel = new StrictSecuritySettingsViewModel(); - - if (await context.Updater.TryUpdateModelAsync(viewModel, Prefix)) - { - // Securable must be enabled for Strict Securable to make sense. Also checked on the client side too. - if (model.GetSettings()?.Securable != true) viewModel.Enabled = false; - - context.Builder.MergeSettings(settings => settings.Enabled = viewModel.Enabled); - } - - return Edit(model); - } -} +using Lombiq.HelpfulExtensions.Extensions.Security.Models; +using Lombiq.HelpfulExtensions.Extensions.Security.ViewModels; +using OrchardCore.ContentManagement.Metadata.Models; +using OrchardCore.ContentManagement.Metadata.Settings; +using OrchardCore.ContentTypes.Editors; +using OrchardCore.DisplayManagement.Views; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.Security.Driver; + +public class StrictSecuritySettingsDisplayDriver : ContentTypeDefinitionDisplayDriver +{ + public override IDisplayResult Edit(ContentTypeDefinition model) => + Initialize("StrictSecuritySetting_Edit", viewModel => + { + var settings = model.GetSettings(); + + viewModel.Enabled = settings?.Enabled == true; + }).Location("Content:5"); + + public override async Task UpdateAsync(ContentTypeDefinition model, UpdateTypeEditorContext context) + { + var viewModel = new StrictSecuritySettingsViewModel(); + + if (await context.Updater.TryUpdateModelAsync(viewModel, Prefix)) + { + // Securable must be enabled for Strict Securable to make sense. Also checked on the client side too. + if (model.GetSettings()?.Securable != true) viewModel.Enabled = false; + + context.Builder.MergeSettings(settings => settings.Enabled = viewModel.Enabled); + } + + return Edit(model); + } +} diff --git a/Extensions/Security/Models/StrictSecuritySettings.cs b/Extensions/Security/Models/StrictSecuritySettings.cs index 5f03f9d9..766d5711 100644 --- a/Extensions/Security/Models/StrictSecuritySettings.cs +++ b/Extensions/Security/Models/StrictSecuritySettings.cs @@ -1,6 +1,6 @@ -namespace Lombiq.HelpfulExtensions.Extensions.Security.Models; - -public class StrictSecuritySettings -{ - public bool Enabled { get; set; } -} +namespace Lombiq.HelpfulExtensions.Extensions.Security.Models; + +public class StrictSecuritySettings +{ + public bool Enabled { get; set; } +} diff --git a/Extensions/Security/Services/StrictSecurityPermissionAuthorizationHandler.cs b/Extensions/Security/Services/StrictSecurityPermissionAuthorizationHandler.cs index e7a9e00c..97af29b4 100644 --- a/Extensions/Security/Services/StrictSecurityPermissionAuthorizationHandler.cs +++ b/Extensions/Security/Services/StrictSecurityPermissionAuthorizationHandler.cs @@ -1,74 +1,74 @@ -using Lombiq.HelpfulExtensions.Extensions.Security.Models; -using Microsoft.AspNetCore.Authorization; -using OrchardCore.ContentManagement; -using OrchardCore.ContentManagement.Metadata; -using OrchardCore.Contents.Security; -using OrchardCore.Modules; -using OrchardCore.Security; -using OrchardCore.Security.Permissions; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.Security.Services; - -[RequireFeatures(FeatureIds.Security)] -public class StrictSecurityPermissionAuthorizationHandler : AuthorizationHandler -{ - private static readonly Dictionary> _permissionTemplates = ContentTypePermissionsHelper - .PermissionTemplates - .ToDictionary( - pair => pair.Key, - pair => GetPermissionTemplates(pair.Value, new List())); - - private readonly IContentDefinitionManager _contentDefinitionManager; - - public StrictSecurityPermissionAuthorizationHandler(IContentDefinitionManager contentDefinitionManager) => - _contentDefinitionManager = contentDefinitionManager; - - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) - { - if ((context.Resource as IContent)?.ContentItem is not { } contentItem || - !_permissionTemplates.TryGetValue(requirement.Permission.Name, out var claims) || - _contentDefinitionManager.GetTypeDefinition(contentItem.ContentType) is not { } definition || - definition.GetSettings()?.Enabled != true) - { - return Task.CompletedTask; - } - - if (!context.User.Identity.IsAuthenticated) - { - context.Fail(); - return Task.CompletedTask; - } - - var contentType = contentItem.ContentType; - claims = claims - .Select(template => string.Format(CultureInfo.InvariantCulture, template, contentType)) - .ToList(); - var permissionNames = context - .User - .Claims - .Where(claim => claim.Type == nameof(Permission)) - .Select(claim => claim.Value); - - if (!permissionNames.Any(claims.Contains)) context.Fail(); - return Task.CompletedTask; - } - - private static IList GetPermissionTemplates(Permission permission, IList templates) - { - templates.Add(permission.Name); - - if (permission.ImpliedBy is { } impliedBy) - { - foreach (var impliedPermission in impliedBy.Where(impliedPermission => impliedPermission.Name.Contains("{0}"))) - { - GetPermissionTemplates(impliedPermission, templates); - } - } - - return templates; - } -} +using Lombiq.HelpfulExtensions.Extensions.Security.Models; +using Microsoft.AspNetCore.Authorization; +using OrchardCore.ContentManagement; +using OrchardCore.ContentManagement.Metadata; +using OrchardCore.Contents.Security; +using OrchardCore.Modules; +using OrchardCore.Security; +using OrchardCore.Security.Permissions; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.Security.Services; + +[RequireFeatures(FeatureIds.Security)] +public class StrictSecurityPermissionAuthorizationHandler : AuthorizationHandler +{ + private static readonly Dictionary> _permissionTemplates = ContentTypePermissionsHelper + .PermissionTemplates + .ToDictionary( + pair => pair.Key, + pair => GetPermissionTemplates(pair.Value, new List())); + + private readonly IContentDefinitionManager _contentDefinitionManager; + + public StrictSecurityPermissionAuthorizationHandler(IContentDefinitionManager contentDefinitionManager) => + _contentDefinitionManager = contentDefinitionManager; + + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if ((context.Resource as IContent)?.ContentItem is not { } contentItem || + !_permissionTemplates.TryGetValue(requirement.Permission.Name, out var claims) || + _contentDefinitionManager.GetTypeDefinition(contentItem.ContentType) is not { } definition || + definition.GetSettings()?.Enabled != true) + { + return Task.CompletedTask; + } + + if (!context.User.Identity.IsAuthenticated) + { + context.Fail(); + return Task.CompletedTask; + } + + var contentType = contentItem.ContentType; + claims = claims + .Select(template => string.Format(CultureInfo.InvariantCulture, template, contentType)) + .ToList(); + var permissionNames = context + .User + .Claims + .Where(claim => claim.Type == nameof(Permission)) + .Select(claim => claim.Value); + + if (!permissionNames.Any(claims.Contains)) context.Fail(); + return Task.CompletedTask; + } + + private static IList GetPermissionTemplates(Permission permission, IList templates) + { + templates.Add(permission.Name); + + if (permission.ImpliedBy is { } impliedBy) + { + foreach (var impliedPermission in impliedBy.Where(impliedPermission => impliedPermission.Name.Contains("{0}"))) + { + GetPermissionTemplates(impliedPermission, templates); + } + } + + return templates; + } +} diff --git a/Extensions/Security/Startup.cs b/Extensions/Security/Startup.cs index 753d8c6e..2afd37e9 100644 --- a/Extensions/Security/Startup.cs +++ b/Extensions/Security/Startup.cs @@ -1,20 +1,20 @@ -using Lombiq.HelpfulExtensions.Extensions.Security.Driver; -using Lombiq.HelpfulExtensions.Extensions.Security.Services; -using Lombiq.HelpfulLibraries.Common.DependencyInjection; -using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.ContentTypes.Editors; -using OrchardCore.Modules; - -namespace Lombiq.HelpfulExtensions.Extensions.Security; - -[Feature(FeatureIds.Security)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) - { - services.AddLazyInjectionSupport(); - services.AddScoped(); - services.AddScoped(); - } -} +using Lombiq.HelpfulExtensions.Extensions.Security.Driver; +using Lombiq.HelpfulExtensions.Extensions.Security.Services; +using Lombiq.HelpfulLibraries.Common.DependencyInjection; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.ContentTypes.Editors; +using OrchardCore.Modules; + +namespace Lombiq.HelpfulExtensions.Extensions.Security; + +[Feature(FeatureIds.Security)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddLazyInjectionSupport(); + services.AddScoped(); + services.AddScoped(); + } +} diff --git a/Extensions/Security/ViewModels/StrictSecuritySettingsViewModel.cs b/Extensions/Security/ViewModels/StrictSecuritySettingsViewModel.cs index f907829d..110a516a 100644 --- a/Extensions/Security/ViewModels/StrictSecuritySettingsViewModel.cs +++ b/Extensions/Security/ViewModels/StrictSecuritySettingsViewModel.cs @@ -1,6 +1,6 @@ -namespace Lombiq.HelpfulExtensions.Extensions.Security.ViewModels; - -public class StrictSecuritySettingsViewModel -{ - public bool Enabled { get; set; } -} +namespace Lombiq.HelpfulExtensions.Extensions.Security.ViewModels; + +public class StrictSecuritySettingsViewModel +{ + public bool Enabled { get; set; } +} diff --git a/Extensions/ShapeTracing/ShapeTracingShapeEvents.cs b/Extensions/ShapeTracing/ShapeTracingShapeEvents.cs index b66c6d0e..e5819256 100644 --- a/Extensions/ShapeTracing/ShapeTracingShapeEvents.cs +++ b/Extensions/ShapeTracing/ShapeTracingShapeEvents.cs @@ -1,81 +1,81 @@ -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Http; -using OrchardCore.DisplayManagement.Implementation; -using OrchardCore.DisplayManagement.Shapes; -using System.Linq; -using System.Threading.Tasks; - -namespace Lombiq.HelpfulExtensions.Extensions.ShapeTracing; - -internal class ShapeTracingShapeEvents : IShapeDisplayEvents -{ - private readonly IHttpContextAccessor _hca; - - public ShapeTracingShapeEvents(IHttpContextAccessor hca) => _hca = hca; - - public Task DisplayedAsync(ShapeDisplayContext context) - { - if (!_hca.HttpContext.IsDevelopment()) return Task.CompletedTask; - - // We could also use _orchardHelper.ConsoleLog(context.Shape) here but that causes an OutOfMemoryException. - - var builder = new HtmlContentBuilder(6); - var shapeMetadata = context.Shape.Metadata; - - builder.AppendLine(); - builder.AppendHtmlLine(""); - - builder.AppendHtml(context.ChildContent); - - context.ChildContent = builder; - - return Task.CompletedTask; - } - - public Task DisplayingAsync(ShapeDisplayContext context) => Task.CompletedTask; - - public Task DisplayingFinalizedAsync(ShapeDisplayContext context) => Task.CompletedTask; -} +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Http; +using OrchardCore.DisplayManagement.Implementation; +using OrchardCore.DisplayManagement.Shapes; +using System.Linq; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulExtensions.Extensions.ShapeTracing; + +internal class ShapeTracingShapeEvents : IShapeDisplayEvents +{ + private readonly IHttpContextAccessor _hca; + + public ShapeTracingShapeEvents(IHttpContextAccessor hca) => _hca = hca; + + public Task DisplayedAsync(ShapeDisplayContext context) + { + if (!_hca.HttpContext.IsDevelopment()) return Task.CompletedTask; + + // We could also use _orchardHelper.ConsoleLog(context.Shape) here but that causes an OutOfMemoryException. + + var builder = new HtmlContentBuilder(6); + var shapeMetadata = context.Shape.Metadata; + + builder.AppendLine(); + builder.AppendHtmlLine(""); + + builder.AppendHtml(context.ChildContent); + + context.ChildContent = builder; + + return Task.CompletedTask; + } + + public Task DisplayingAsync(ShapeDisplayContext context) => Task.CompletedTask; + + public Task DisplayingFinalizedAsync(ShapeDisplayContext context) => Task.CompletedTask; +} diff --git a/Extensions/ShapeTracing/Startup.cs b/Extensions/ShapeTracing/Startup.cs index 853a7472..288ae47c 100644 --- a/Extensions/ShapeTracing/Startup.cs +++ b/Extensions/ShapeTracing/Startup.cs @@ -1,12 +1,12 @@ -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.DisplayManagement.Implementation; -using OrchardCore.Modules; - -namespace Lombiq.HelpfulExtensions.Extensions.ShapeTracing; - -[Feature(FeatureIds.ShapeTracing)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) => - services.AddScoped(); -} +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.DisplayManagement.Implementation; +using OrchardCore.Modules; + +namespace Lombiq.HelpfulExtensions.Extensions.ShapeTracing; + +[Feature(FeatureIds.ShapeTracing)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) => + services.AddScoped(); +} diff --git a/Extensions/Widgets/Migrations.cs b/Extensions/Widgets/Migrations.cs index c19febbe..b7794824 100644 --- a/Extensions/Widgets/Migrations.cs +++ b/Extensions/Widgets/Migrations.cs @@ -1,93 +1,93 @@ -using Lombiq.HelpfulLibraries.OrchardCore.Contents; -using OrchardCore.ContentManagement.Metadata; -using OrchardCore.ContentManagement.Metadata.Settings; -using OrchardCore.Data.Migration; -using static Lombiq.HelpfulExtensions.Extensions.Widgets.WidgetTypes; - -namespace Lombiq.HelpfulExtensions.Extensions.Widgets; - -public class Migrations : DataMigration -{ - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations(IContentDefinitionManager contentDefinitionManager) => - _contentDefinitionManager = contentDefinitionManager; - - public int Create() - { - _contentDefinitionManager.AlterTypeDefinition(ContainerWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - .WithPart("TitlePart", part => part.WithPosition("0")) - .WithPart("FlowPart", part => part.WithPosition("1")) - ); - - _contentDefinitionManager.AlterTypeDefinition(HtmlWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - .WithPart("HtmlBodyPart", part => part - .WithDisplayName("HTML Body") - .WithSettings(new ContentTypePartSettings - { - Editor = "Trumbowyg", - }) - ) - ); - - _contentDefinitionManager.AlterTypeDefinition(LiquidWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - .WithPart("LiquidPart", part => part - .WithDisplayName("Liquid Part") - ) - ); - - _contentDefinitionManager.AlterTypeDefinition(MenuWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - ); - - _contentDefinitionManager.AlterTypeDefinition(MarkdownWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - .WithPart("MarkdownBodyPart", part => part - .WithDisplayName("Markdown Part") - ) - ); - - return 4; - } - - public int UpdateFrom1() - { - _contentDefinitionManager.AlterTypeDefinition(ContainerWidget, builder => builder - .WithPart("TitlePart", part => part.WithPosition("0")) - .WithPart("FlowPart", part => part.WithPosition("1")) - ); - - return 2; - } - - public int UpdateFrom2() - { - _contentDefinitionManager.AlterTypeDefinition(MenuWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - ); - - return 3; - } - - public int UpdateFrom3() - { - _contentDefinitionManager.AlterTypeDefinition(MarkdownWidget, builder => builder - .Securable() - .Stereotype(CommonStereotypes.Widget) - .WithPart("MarkdownBodyPart", part => part - .WithDisplayName("Markdown Part") - ) - ); - - return 4; - } -} +using Lombiq.HelpfulLibraries.OrchardCore.Contents; +using OrchardCore.ContentManagement.Metadata; +using OrchardCore.ContentManagement.Metadata.Settings; +using OrchardCore.Data.Migration; +using static Lombiq.HelpfulExtensions.Extensions.Widgets.WidgetTypes; + +namespace Lombiq.HelpfulExtensions.Extensions.Widgets; + +public class Migrations : DataMigration +{ + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) => + _contentDefinitionManager = contentDefinitionManager; + + public int Create() + { + _contentDefinitionManager.AlterTypeDefinition(ContainerWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + .WithPart("TitlePart", part => part.WithPosition("0")) + .WithPart("FlowPart", part => part.WithPosition("1")) + ); + + _contentDefinitionManager.AlterTypeDefinition(HtmlWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + .WithPart("HtmlBodyPart", part => part + .WithDisplayName("HTML Body") + .WithSettings(new ContentTypePartSettings + { + Editor = "Trumbowyg", + }) + ) + ); + + _contentDefinitionManager.AlterTypeDefinition(LiquidWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + .WithPart("LiquidPart", part => part + .WithDisplayName("Liquid Part") + ) + ); + + _contentDefinitionManager.AlterTypeDefinition(MenuWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + ); + + _contentDefinitionManager.AlterTypeDefinition(MarkdownWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + .WithPart("MarkdownBodyPart", part => part + .WithDisplayName("Markdown Part") + ) + ); + + return 4; + } + + public int UpdateFrom1() + { + _contentDefinitionManager.AlterTypeDefinition(ContainerWidget, builder => builder + .WithPart("TitlePart", part => part.WithPosition("0")) + .WithPart("FlowPart", part => part.WithPosition("1")) + ); + + return 2; + } + + public int UpdateFrom2() + { + _contentDefinitionManager.AlterTypeDefinition(MenuWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + ); + + return 3; + } + + public int UpdateFrom3() + { + _contentDefinitionManager.AlterTypeDefinition(MarkdownWidget, builder => builder + .Securable() + .Stereotype(CommonStereotypes.Widget) + .WithPart("MarkdownBodyPart", part => part + .WithDisplayName("Markdown Part") + ) + ); + + return 4; + } +} diff --git a/Extensions/Widgets/Startup.cs b/Extensions/Widgets/Startup.cs index 7e58a1b8..6bc8246d 100644 --- a/Extensions/Widgets/Startup.cs +++ b/Extensions/Widgets/Startup.cs @@ -1,20 +1,20 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using OrchardCore.Data.Migration; -using OrchardCore.Modules; -using System; - -namespace Lombiq.HelpfulExtensions.Extensions.Widgets; - -[Feature(FeatureIds.Widgets)] -public class Startup : StartupBase -{ - public override void ConfigureServices(IServiceCollection services) => - services.AddScoped(); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // No need for anything here yet. - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Data.Migration; +using OrchardCore.Modules; +using System; + +namespace Lombiq.HelpfulExtensions.Extensions.Widgets; + +[Feature(FeatureIds.Widgets)] +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) => + services.AddScoped(); + + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // No need for anything here yet. + } +} diff --git a/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs b/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs index d2197381..842bcd39 100644 --- a/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs +++ b/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs @@ -1,22 +1,22 @@ -using OrchardCore.Navigation; -using System.Collections.Generic; -using System.Linq; - -namespace Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels; - -public class MenuWidgetViewModel -{ - public bool NoWrapper { get; set; } - public IEnumerable MenuItems { get; set; } - - public MenuWidgetViewModel(bool noWrapper = false, IEnumerable menuItems = null) - { - NoWrapper = noWrapper; - MenuItems = menuItems ?? Enumerable.Empty(); - } - - public MenuWidgetViewModel(dynamic model) - : this((model.NoWrapper as bool?) == true, model.MenuItems as IEnumerable) - { - } -} +using OrchardCore.Navigation; +using System.Collections.Generic; +using System.Linq; + +namespace Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels; + +public class MenuWidgetViewModel +{ + public bool NoWrapper { get; set; } + public IEnumerable MenuItems { get; set; } + + public MenuWidgetViewModel(bool noWrapper = false, IEnumerable menuItems = null) + { + NoWrapper = noWrapper; + MenuItems = menuItems ?? Enumerable.Empty(); + } + + public MenuWidgetViewModel(dynamic model) + : this((model.NoWrapper as bool?) == true, model.MenuItems as IEnumerable) + { + } +} diff --git a/Extensions/Widgets/WidgetTypes.cs b/Extensions/Widgets/WidgetTypes.cs index c7643c0b..931bf581 100644 --- a/Extensions/Widgets/WidgetTypes.cs +++ b/Extensions/Widgets/WidgetTypes.cs @@ -1,10 +1,10 @@ -namespace Lombiq.HelpfulExtensions.Extensions.Widgets; - -public static class WidgetTypes -{ - public const string ContainerWidget = nameof(ContainerWidget); - public const string HtmlWidget = nameof(HtmlWidget); - public const string LiquidWidget = nameof(LiquidWidget); - public const string MenuWidget = nameof(MenuWidget); - public const string MarkdownWidget = nameof(MarkdownWidget); -} +namespace Lombiq.HelpfulExtensions.Extensions.Widgets; + +public static class WidgetTypes +{ + public const string ContainerWidget = nameof(ContainerWidget); + public const string HtmlWidget = nameof(HtmlWidget); + public const string LiquidWidget = nameof(LiquidWidget); + public const string MenuWidget = nameof(MenuWidget); + public const string MarkdownWidget = nameof(MarkdownWidget); +} diff --git a/FeatureIds.cs b/FeatureIds.cs index ff1218ce..2364f6bf 100644 --- a/FeatureIds.cs +++ b/FeatureIds.cs @@ -1,14 +1,14 @@ -namespace Lombiq.HelpfulExtensions; - -public static class FeatureIds -{ - private const string FeatureIdPrefix = "Lombiq.HelpfulExtensions."; - - public const string CodeGeneration = FeatureIdPrefix + nameof(CodeGeneration); - public const string ContentTypes = FeatureIdPrefix + nameof(ContentTypes); - public const string Flows = FeatureIdPrefix + nameof(Flows); - public const string ShapeTracing = FeatureIdPrefix + nameof(ShapeTracing); - public const string Widgets = FeatureIdPrefix + nameof(Widgets); - public const string Emails = FeatureIdPrefix + nameof(Emails); - public const string Security = FeatureIdPrefix + nameof(Security); -} +namespace Lombiq.HelpfulExtensions; + +public static class FeatureIds +{ + private const string FeatureIdPrefix = "Lombiq.HelpfulExtensions."; + + public const string CodeGeneration = FeatureIdPrefix + nameof(CodeGeneration); + public const string ContentTypes = FeatureIdPrefix + nameof(ContentTypes); + public const string Flows = FeatureIdPrefix + nameof(Flows); + public const string ShapeTracing = FeatureIdPrefix + nameof(ShapeTracing); + public const string Widgets = FeatureIdPrefix + nameof(Widgets); + public const string Emails = FeatureIdPrefix + nameof(Emails); + public const string Security = FeatureIdPrefix + nameof(Security); +} diff --git a/Licence.md b/Licence.md index ba0d6ed3..f59bfb26 100644 --- a/Licence.md +++ b/Licence.md @@ -1,15 +1,15 @@ -Copyright © 2013, [Lombiq Technologies Ltd.](https://lombiq.com) - -All rights reserved. - -For more information and requests about licensing please [contact us through our website](https://lombiq.com/contact-us). - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - +Copyright © 2013, [Lombiq Technologies Ltd.](https://lombiq.com) + +All rights reserved. + +For more information and requests about licensing please [contact us through our website](https://lombiq.com/contact-us). + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Lombiq.HelpfulExtensions.csproj b/Lombiq.HelpfulExtensions.csproj index 1f7e97e0..35db273e 100644 --- a/Lombiq.HelpfulExtensions.csproj +++ b/Lombiq.HelpfulExtensions.csproj @@ -1,53 +1,53 @@ - - - - net6.0 - true - $(DefaultItemExcludes);.git*;node_modules\** - - - - Lombiq Helpful Extensions for Orchard Core - Lombiq Technologies - Copyright © 2013, Lombiq Technologies Ltd. - Lombiq Helpful Extensions for Orchard Core: Orchard Core module containing some handy extensions (e.g. useful content types and widgets). It's also available on all sites of DotNest, the Orchard SaaS. See the project website for detailed documentation. - NuGetIcon.png - OrchardCore;Lombiq;AspNetCore;CodeGeneration;ShapeTracing;Widgets - https://github.com/Lombiq/Helpful-Extensions - https://github.com/Lombiq/Helpful-Extensions - Licence.md - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + net6.0 + true + $(DefaultItemExcludes);.git*;node_modules\** + + + + Lombiq Helpful Extensions for Orchard Core + Lombiq Technologies + Copyright © 2013, Lombiq Technologies Ltd. + Lombiq Helpful Extensions for Orchard Core: Orchard Core module containing some handy extensions (e.g. useful content types and widgets). It's also available on all sites of DotNest, the Orchard SaaS. See the project website for detailed documentation. + NuGetIcon.png + OrchardCore;Lombiq;AspNetCore;CodeGeneration;ShapeTracing;Widgets + https://github.com/Lombiq/Helpful-Extensions + https://github.com/Lombiq/Helpful-Extensions + Licence.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Manifest.cs b/Manifest.cs index 00032188..04793f8c 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -1,82 +1,82 @@ -using OrchardCore.Modules.Manifest; -using static Lombiq.HelpfulExtensions.FeatureIds; - -[assembly: Module( - Name = "Lombiq Helpful Extensions", - Author = "Lombiq Technologies", - Version = "3.0.1", - Website = "https://github.com/Lombiq/Helpful-Extensions" -)] - -[assembly: Feature( - Id = CodeGeneration, - Name = "Lombiq Helpful Extensions - Code Generation Helpful Extensions", - Category = "Development", - Description = "Generates migrations from content type definitions.", - Dependencies = new[] - { - "OrchardCore.Resources", - } -)] - -[assembly: Feature( - Id = Flows, - Name = "Lombiq Helpful Extensions - Flows Helpful Extensions", - Category = "Content", - Description = "Adds additional styling capabilities to Flow Part.", - Dependencies = new[] - { - "OrchardCore.Flows", - } -)] - -[assembly: Feature( - Id = Widgets, - Name = "Lombiq Helpful Extensions - Helpful Widgets", - Category = "Content", - Description = "Adds helpful widgets such as Container or Liquid widgets.", - Dependencies = new[] - { - "OrchardCore.Html", - "OrchardCore.Liquid", - "OrchardCore.Title", - } -)] - -[assembly: Feature( - Id = ContentTypes, - Name = "Lombiq Helpful Extensions - Helpful Content Types", - Category = "Content", - Description = "Adds helpful content types such as Page.", - Dependencies = new[] - { - "OrchardCore.Autoroute", - "OrchardCore.Flows", - "OrchardCore.Title", - } -)] - -[assembly: Feature( - Id = ShapeTracing, - Name = "Lombiq Helpful Extensions - Shape Tracing Helpful Extensions", - Category = "Development", - Description = "Adds a dump of metadata to the output about every shape." -)] - -[assembly: Feature( - Id = Security, - Name = "Lombiq Helpful Extensions - Security Helpful Extensions", - Category = "Security", - Description = "Adds a content type definition setting and authorization handler for richer security options." -)] - -[assembly: Feature( - Id = Emails, - Name = "Lombiq Helpful Extensions - Emails", - Category = "Messaging", - Description = "Adds shape-based email template rendering and helpful email sending services.", - Dependencies = new[] - { - "OrchardCore.Email", - } -)] +using OrchardCore.Modules.Manifest; +using static Lombiq.HelpfulExtensions.FeatureIds; + +[assembly: Module( + Name = "Lombiq Helpful Extensions", + Author = "Lombiq Technologies", + Version = "3.0.1", + Website = "https://github.com/Lombiq/Helpful-Extensions" +)] + +[assembly: Feature( + Id = CodeGeneration, + Name = "Lombiq Helpful Extensions - Code Generation Helpful Extensions", + Category = "Development", + Description = "Generates migrations from content type definitions.", + Dependencies = new[] + { + "OrchardCore.Resources", + } +)] + +[assembly: Feature( + Id = Flows, + Name = "Lombiq Helpful Extensions - Flows Helpful Extensions", + Category = "Content", + Description = "Adds additional styling capabilities to Flow Part.", + Dependencies = new[] + { + "OrchardCore.Flows", + } +)] + +[assembly: Feature( + Id = Widgets, + Name = "Lombiq Helpful Extensions - Helpful Widgets", + Category = "Content", + Description = "Adds helpful widgets such as Container or Liquid widgets.", + Dependencies = new[] + { + "OrchardCore.Html", + "OrchardCore.Liquid", + "OrchardCore.Title", + } +)] + +[assembly: Feature( + Id = ContentTypes, + Name = "Lombiq Helpful Extensions - Helpful Content Types", + Category = "Content", + Description = "Adds helpful content types such as Page.", + Dependencies = new[] + { + "OrchardCore.Autoroute", + "OrchardCore.Flows", + "OrchardCore.Title", + } +)] + +[assembly: Feature( + Id = ShapeTracing, + Name = "Lombiq Helpful Extensions - Shape Tracing Helpful Extensions", + Category = "Development", + Description = "Adds a dump of metadata to the output about every shape." +)] + +[assembly: Feature( + Id = Security, + Name = "Lombiq Helpful Extensions - Security Helpful Extensions", + Category = "Security", + Description = "Adds a content type definition setting and authorization handler for richer security options." +)] + +[assembly: Feature( + Id = Emails, + Name = "Lombiq Helpful Extensions - Emails", + Category = "Messaging", + Description = "Adds shape-based email template rendering and helpful email sending services.", + Dependencies = new[] + { + "OrchardCore.Email", + } +)] diff --git a/Models/BootstrapAccordionItem.cs b/Models/BootstrapAccordionItem.cs index f3924f4a..9741ac15 100644 --- a/Models/BootstrapAccordionItem.cs +++ b/Models/BootstrapAccordionItem.cs @@ -1,10 +1,10 @@ -using Microsoft.AspNetCore.Mvc.Localization; -using OrchardCore.DisplayManagement; - -namespace Lombiq.HelpfulExtensions.Models; - -public class BootstrapAccordionItem -{ - public LocalizedHtmlString Title { get; set; } - public IShape Shape { get; set; } -} +using Microsoft.AspNetCore.Mvc.Localization; +using OrchardCore.DisplayManagement; + +namespace Lombiq.HelpfulExtensions.Models; + +public class BootstrapAccordionItem +{ + public LocalizedHtmlString Title { get; set; } + public IShape Shape { get; set; } +} diff --git a/Readme.md b/Readme.md index dd9276df..00c1fb47 100644 --- a/Readme.md +++ b/Readme.md @@ -1,123 +1,123 @@ -# Lombiq Helpful Extensions for Orchard Core - - - -[![Lombiq.HelpfulExtensions NuGet](https://img.shields.io/nuget/v/Lombiq.HelpfulExtensions?label=Lombiq.HelpfulExtensions)](https://www.nuget.org/packages/Lombiq.HelpfulExtensions/) - - -## About - -Orchard Core module containing some handy extensions (e.g. useful content types and widgets). It's also available on all sites of [DotNest, the Orchard SaaS](https://dotnest.com/). - -Do you want to quickly try out this project and see it in action? Check it out in our [Open-Source Orchard Core Extensions](https://github.com/Lombiq/Open-Source-Orchard-Core-Extensions) full Orchard Core solution and also see our other useful Orchard Core-related open-source projects! - -Note that this module has an Orchard 1 version in the [dev-orchard-1 branch](https://github.com/Lombiq/Helpful-Extensions/tree/dev-orchard-1). - - -## Extensions - -The module consists of the following independent extensions (all in their own features): - -### Code Generation Helpful Extensions - -#### Content definition code generation -Generates migration code from content definitions. You can use this to create (or edit) a content type on the admin and then move its creation to a migration class. Generated migration code is displayed under the content types' editors, just enable the feature. Check out [this demo video](https://www.youtube.com/watch?v=KOlsLaIzgm8) to see this in action. - -![Content definition code generation textbox on the admin, showing generated migration code for the Page content type.](Docs/Attachments/ContentTypeCodeGeneration.png) - -### Flows Helpful Extensions - -Adds additional styling capabilities to the OrchardCore.Flows feature by making it possible to add classes to widgets in the Flow Part editor. Just add `AdditionalStylingPart` to the content type using `FlowPart`. - -![Custom classes editor on a widget contained in Flow Part.](Docs/Attachments/FlowPartCustomClasses.png) - -### Helpful Widgets - -Adds multiple helpful widget content types. These are basic widgets that are added by built-in Orchard Core recipes though in case of using a custom setup recipe these can be added by this feature too. - -Includes: - -- ContainerWidget: Works as a container for further widgets. It has a FlowPart attached to it so it can contain additional widgets as well. -- HtmlWidget: Adds HTML editing and displaying capabilities using a WYSIWYG editor. -- LiquidWidget: Adds Liquid code editing and rendering capabilities. -- MenuWidget: Renders a Bootstrap navigation menu as a widget using the provided `MenuItem`s. - -### Helpful Content Types - -Includes basic content types that are added by built-in Orchard Core recipes though in case of using a custom setup recipe these can be added by this feature too. - -Includes: - -- Page: Highly customizable page content type with FlowPart and AutoroutePart. - -### Shape Tracing Helpful Extensions - -Adds a dump of metadata to the output about every shape. This will help you understand how a shape is displayed and how you can override it. Just check out the HTML output. You can see a video demo of this feature in action [on YouTube](https://www.youtube.com/watch?v=WI4TEKVc9SA). - -### Helpful Shapes - -Adds common constructs you can use in your views. - -#### BootstrapAccordion - -It displays an [accordion powered by Bootstrap](https://getbootstrap.com/docs/4.0/components/collapse/#accordion-example). It displays shapes as content. The `Children` property is required. - -```html - -``` - -### Security Extensions - - -#### Strict Security - -When applied to a content type definition, `StrictSecuritySetting` requires the user to have the exact Securable permission for that content type. For example if you apply it to Page, then just having the common ViewContent permission won't be enough and you must explicitly have the View_Page permission too. Don't worry, the normal implications such as ViewOwn beig fulfilled by View still apply within the content type, they just no longer imply their common counterparts. - -Make content type use strict security in migration: -```csharp -_contentDefinitionManager.AlterTypeDefinition("Page", type => type - .Securable() - .WithSettings(new StrictSecuritySettings { Enabled = true })); -``` - -You can also enable it by going to the content type editor on the admin side and checking the _Strict Securable_ checkbox. - - -### Emails and Email Templates - -#### Email Templates - -Provides a shape-based email template rendering service. The email templates are represented by email template IDs that are also used to identify the corresponding shape using the following pattern: `EmailTemplate__{EmailTemplateID}`. E.g., for the `ContactUs` email template you need to create a shape with the `EmailTemplate__ContactUs` shape type. - -In the email template shapes use the `Layout__EmailTemplate` as the `ViewLayout` to wrap it with a simple HTML layout. - -To extend the layout you can override the `EmailTemplate_LayoutInjections` shape and inject content to the specific zones provided by the layout to activate it in every email template. E.g., - -```html - - Best,
- My Awesome Team -
-``` -To add inline styles include: -```html - - - -``` - -#### Deferred email sending - -Use the `ShellScope.Current.SendEmailDeferred()` for sending emails. It'll send emails after the shell scope has ended without blocking the request. - - - -## Contributing and support - -Bug reports, feature requests, comments, questions, code contributions, and love letters are warmly welcome, please do so via GitHub issues and pull requests. Please adhere to our [open-source guidelines](https://lombiq.com/open-source-guidelines) while doing so. - -This project is developed by [Lombiq Technologies](https://lombiq.com/). Commercial-grade support is available through Lombiq. +# Lombiq Helpful Extensions for Orchard Core + + + +[![Lombiq.HelpfulExtensions NuGet](https://img.shields.io/nuget/v/Lombiq.HelpfulExtensions?label=Lombiq.HelpfulExtensions)](https://www.nuget.org/packages/Lombiq.HelpfulExtensions/) + + +## About + +Orchard Core module containing some handy extensions (e.g. useful content types and widgets). It's also available on all sites of [DotNest, the Orchard SaaS](https://dotnest.com/). + +Do you want to quickly try out this project and see it in action? Check it out in our [Open-Source Orchard Core Extensions](https://github.com/Lombiq/Open-Source-Orchard-Core-Extensions) full Orchard Core solution and also see our other useful Orchard Core-related open-source projects! + +Note that this module has an Orchard 1 version in the [dev-orchard-1 branch](https://github.com/Lombiq/Helpful-Extensions/tree/dev-orchard-1). + + +## Extensions + +The module consists of the following independent extensions (all in their own features): + +### Code Generation Helpful Extensions + +#### Content definition code generation +Generates migration code from content definitions. You can use this to create (or edit) a content type on the admin and then move its creation to a migration class. Generated migration code is displayed under the content types' editors, just enable the feature. Check out [this demo video](https://www.youtube.com/watch?v=KOlsLaIzgm8) to see this in action. + +![Content definition code generation textbox on the admin, showing generated migration code for the Page content type.](Docs/Attachments/ContentTypeCodeGeneration.png) + +### Flows Helpful Extensions + +Adds additional styling capabilities to the OrchardCore.Flows feature by making it possible to add classes to widgets in the Flow Part editor. Just add `AdditionalStylingPart` to the content type using `FlowPart`. + +![Custom classes editor on a widget contained in Flow Part.](Docs/Attachments/FlowPartCustomClasses.png) + +### Helpful Widgets + +Adds multiple helpful widget content types. These are basic widgets that are added by built-in Orchard Core recipes though in case of using a custom setup recipe these can be added by this feature too. + +Includes: + +- ContainerWidget: Works as a container for further widgets. It has a FlowPart attached to it so it can contain additional widgets as well. +- HtmlWidget: Adds HTML editing and displaying capabilities using a WYSIWYG editor. +- LiquidWidget: Adds Liquid code editing and rendering capabilities. +- MenuWidget: Renders a Bootstrap navigation menu as a widget using the provided `MenuItem`s. + +### Helpful Content Types + +Includes basic content types that are added by built-in Orchard Core recipes though in case of using a custom setup recipe these can be added by this feature too. + +Includes: + +- Page: Highly customizable page content type with FlowPart and AutoroutePart. + +### Shape Tracing Helpful Extensions + +Adds a dump of metadata to the output about every shape. This will help you understand how a shape is displayed and how you can override it. Just check out the HTML output. You can see a video demo of this feature in action [on YouTube](https://www.youtube.com/watch?v=WI4TEKVc9SA). + +### Helpful Shapes + +Adds common constructs you can use in your views. + +#### BootstrapAccordion + +It displays an [accordion powered by Bootstrap](https://getbootstrap.com/docs/4.0/components/collapse/#accordion-example). It displays shapes as content. The `Children` property is required. + +```html + +``` + +### Security Extensions + + +#### Strict Security + +When applied to a content type definition, `StrictSecuritySetting` requires the user to have the exact Securable permission for that content type. For example if you apply it to Page, then just having the common ViewContent permission won't be enough and you must explicitly have the View_Page permission too. Don't worry, the normal implications such as ViewOwn beig fulfilled by View still apply within the content type, they just no longer imply their common counterparts. + +Make content type use strict security in migration: +```csharp +_contentDefinitionManager.AlterTypeDefinition("Page", type => type + .Securable() + .WithSettings(new StrictSecuritySettings { Enabled = true })); +``` + +You can also enable it by going to the content type editor on the admin side and checking the _Strict Securable_ checkbox. + + +### Emails and Email Templates + +#### Email Templates + +Provides a shape-based email template rendering service. The email templates are represented by email template IDs that are also used to identify the corresponding shape using the following pattern: `EmailTemplate__{EmailTemplateID}`. E.g., for the `ContactUs` email template you need to create a shape with the `EmailTemplate__ContactUs` shape type. + +In the email template shapes use the `Layout__EmailTemplate` as the `ViewLayout` to wrap it with a simple HTML layout. + +To extend the layout you can override the `EmailTemplate_LayoutInjections` shape and inject content to the specific zones provided by the layout to activate it in every email template. E.g., + +```html + + Best,
+ My Awesome Team +
+``` +To add inline styles include: +```html + + + +``` + +#### Deferred email sending + +Use the `ShellScope.Current.SendEmailDeferred()` for sending emails. It'll send emails after the shell scope has ended without blocking the request. + + + +## Contributing and support + +Bug reports, feature requests, comments, questions, code contributions, and love letters are warmly welcome, please do so via GitHub issues and pull requests. Please adhere to our [open-source guidelines](https://lombiq.com/open-source-guidelines) while doing so. + +This project is developed by [Lombiq Technologies](https://lombiq.com/). Commercial-grade support is available through Lombiq. diff --git a/Views/AdditionalStylingPart.Edit.cshtml b/Views/AdditionalStylingPart.Edit.cshtml index 5d77a804..919cb9fa 100644 --- a/Views/AdditionalStylingPart.Edit.cshtml +++ b/Views/AdditionalStylingPart.Edit.cshtml @@ -1,17 +1,17 @@ -@model Lombiq.HelpfulExtensions.Extensions.Flows.Models.AdditionalStylingPart - -
- - +@model Lombiq.HelpfulExtensions.Extensions.Flows.Models.AdditionalStylingPart + +
+ +
\ No newline at end of file diff --git a/Views/BootstrapAccordion.cshtml b/Views/BootstrapAccordion.cshtml index d8e60eed..e75f8ad0 100644 --- a/Views/BootstrapAccordion.cshtml +++ b/Views/BootstrapAccordion.cshtml @@ -1,53 +1,53 @@ -@using Lombiq.HelpfulExtensions.Models -@using OrchardCore.Mvc.Utilities - -@{ - var additionalClasses = Model.AdditionalClasses?.ToString() ?? string.Empty; -} - -
- @foreach (BootstrapAccordionItem items in Model.Children) - { - var id = items.Title.Value.HtmlClassify(); - -
-
-
@items.Title
-
- -
-
- -
-
- -
-
- @await DisplayAsync(items.Shape) -
-
-
- } -
- - +@using Lombiq.HelpfulExtensions.Models +@using OrchardCore.Mvc.Utilities + +@{ + var additionalClasses = Model.AdditionalClasses?.ToString() ?? string.Empty; +} + +
+ @foreach (BootstrapAccordionItem items in Model.Children) + { + var id = items.Title.Value.HtmlClassify(); + +
+
+
@items.Title
+
+ +
+
+ +
+
+ +
+
+ @await DisplayAsync(items.Shape) +
+
+
+ } +
+ + diff --git a/Views/ContentTypeMigrations.Edit.cshtml b/Views/ContentTypeMigrations.Edit.cshtml index e0568dea..8b077aa1 100644 --- a/Views/ContentTypeMigrations.Edit.cshtml +++ b/Views/ContentTypeMigrations.Edit.cshtml @@ -1,29 +1,29 @@ -@model Lombiq.HelpfulExtensions.Extensions.CodeGeneration.ContentTypeMigrationsViewModel - - - - -
- - -
- -
-
- - +@model Lombiq.HelpfulExtensions.Extensions.CodeGeneration.ContentTypeMigrationsViewModel + + + + +
+ + +
+ +
+
+ + diff --git a/Views/Layout-EmailTemplate.cshtml b/Views/Layout-EmailTemplate.cshtml index f21a5736..32a9148e 100644 --- a/Views/Layout-EmailTemplate.cshtml +++ b/Views/Layout-EmailTemplate.cshtml @@ -1,44 +1,44 @@ -@{ - const string blockName = "emailTemplate"; -} - - - - - - - - - <shape type="EmailTemplate_Title"></shape> - - - @await RenderSectionAsync("Head", required: false) - - - - - - - -
-
- @await RenderSectionAsync("Header", required: false) -
- -
- @await RenderSectionAsync("BeforeContent", required: false) - @await RenderBodyAsync() - @await RenderSectionAsync("AfterContent", required: false) -
- - -
- - - +@{ + const string blockName = "emailTemplate"; +} + + + + + + + + + <shape type="EmailTemplate_Title"></shape> + + + @await RenderSectionAsync("Head", required: false) + + + + + + + +
+
+ @await RenderSectionAsync("Header", required: false) +
+ +
+ @await RenderSectionAsync("BeforeContent", required: false) + @await RenderBodyAsync() + @await RenderSectionAsync("AfterContent", required: false) +
+ + +
+ + + diff --git a/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml b/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml index 8541d283..b0f73d92 100644 --- a/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml +++ b/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml @@ -1,44 +1,44 @@ -@using Lombiq.HelpfulExtensions.Extensions.Flows.Models -@using OrchardCore.ContentManagement -@using OrchardCore.ContentManagement.Display -@using OrchardCore.Flows.Models -@using OrchardCore.Mvc.Utilities -@model OrchardCore.Flows.ViewModels.FlowPartViewModel -@inject IContentItemDisplayManager ContentItemDisplayManager - -
- @foreach (var widget in Model.FlowPart.Widgets) - { - var widgetContent = await ContentItemDisplayManager.BuildDisplayAsync( - widget, Model.BuildPartDisplayContext.Updater, Model.BuildPartDisplayContext.DisplayType, Model.BuildPartDisplayContext.GroupId); - var flowMetadata = widget.As(); - - if (flowMetadata != null) - { - var additionalStyling = widget.As(); - - bool removeGridExtensionClasses = false; - if (additionalStyling != null) - { - - if (!string.IsNullOrEmpty(additionalStyling.CustomClasses)) - { - widgetContent.Classes.Add(additionalStyling.CustomClasses?.Trim()); - } - - removeGridExtensionClasses = additionalStyling.RemoveGridExtensionClasses; - } - - if (!removeGridExtensionClasses) - { - widgetContent.Classes.Add("widget-align-" + flowMetadata.Alignment.ToString().ToLowerInvariant()); - widgetContent.Classes.Add("widget-size-" + flowMetadata.Size); - } - - widgetContent.Classes.Add("widget"); - widgetContent.Classes.Add("widget-" + widget.ContentItem.ContentType.HtmlClassify()); - } - - @await DisplayAsync(widgetContent) - } +@using Lombiq.HelpfulExtensions.Extensions.Flows.Models +@using OrchardCore.ContentManagement +@using OrchardCore.ContentManagement.Display +@using OrchardCore.Flows.Models +@using OrchardCore.Mvc.Utilities +@model OrchardCore.Flows.ViewModels.FlowPartViewModel +@inject IContentItemDisplayManager ContentItemDisplayManager + +
+ @foreach (var widget in Model.FlowPart.Widgets) + { + var widgetContent = await ContentItemDisplayManager.BuildDisplayAsync( + widget, Model.BuildPartDisplayContext.Updater, Model.BuildPartDisplayContext.DisplayType, Model.BuildPartDisplayContext.GroupId); + var flowMetadata = widget.As(); + + if (flowMetadata != null) + { + var additionalStyling = widget.As(); + + bool removeGridExtensionClasses = false; + if (additionalStyling != null) + { + + if (!string.IsNullOrEmpty(additionalStyling.CustomClasses)) + { + widgetContent.Classes.Add(additionalStyling.CustomClasses?.Trim()); + } + + removeGridExtensionClasses = additionalStyling.RemoveGridExtensionClasses; + } + + if (!removeGridExtensionClasses) + { + widgetContent.Classes.Add("widget-align-" + flowMetadata.Alignment.ToString().ToLowerInvariant()); + widgetContent.Classes.Add("widget-size-" + flowMetadata.Size); + } + + widgetContent.Classes.Add("widget"); + widgetContent.Classes.Add("widget-" + widget.ContentItem.ContentType.HtmlClassify()); + } + + @await DisplayAsync(widgetContent) + }
\ No newline at end of file diff --git a/Views/MenuWidget.cshtml b/Views/MenuWidget.cshtml index dd8869d3..cb31b8ea 100644 --- a/Views/MenuWidget.cshtml +++ b/Views/MenuWidget.cshtml @@ -1,89 +1,89 @@ -@using Lombiq.HelpfulExtensions.Extensions.Widgets -@using Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels - - - -@{ - var viewModel = new MenuWidgetViewModel(Model); - - const string blockName = "menuWidget"; - const string contentElementName = blockName + "__content"; - const string togglerElementName = blockName + "__toggler"; - const string dropdownElementName = blockName + "__dropdown"; - const string dropdownItemElementName = blockName + "__dropdownItem"; - - var contentIdName = FormattableString.Invariant($"{contentElementName}_{Guid.NewGuid()}"); - var dropdownIdName = FormattableString.Invariant($"{dropdownElementName}_{Guid.NewGuid()}"); -} - -@if (!viewModel.NoWrapper) -{ - @: -} +@using Lombiq.HelpfulExtensions.Extensions.Widgets +@using Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels + + + +@{ + var viewModel = new MenuWidgetViewModel(Model); + + const string blockName = "menuWidget"; + const string contentElementName = blockName + "__content"; + const string togglerElementName = blockName + "__toggler"; + const string dropdownElementName = blockName + "__dropdown"; + const string dropdownItemElementName = blockName + "__dropdownItem"; + + var contentIdName = FormattableString.Invariant($"{contentElementName}_{Guid.NewGuid()}"); + var dropdownIdName = FormattableString.Invariant($"{dropdownElementName}_{Guid.NewGuid()}"); +} + +@if (!viewModel.NoWrapper) +{ + @: +} diff --git a/Views/StrictSecuritySetting.Edit.cshtml b/Views/StrictSecuritySetting.Edit.cshtml index c54fa2aa..d590018f 100644 --- a/Views/StrictSecuritySetting.Edit.cshtml +++ b/Views/StrictSecuritySetting.Edit.cshtml @@ -1,23 +1,23 @@ -@model Lombiq.HelpfulExtensions.Extensions.Security.ViewModels.StrictSecuritySettingsViewModel - -@* Template is to be kept identically formatted as ContentTypeSettings.Edit.cshtml. *@ -
-
- - - - @T["Enable along with Securable to ensure the user must have the content type version of the permission."] - -
-
- - +@model Lombiq.HelpfulExtensions.Extensions.Security.ViewModels.StrictSecuritySettingsViewModel + +@* Template is to be kept identically formatted as ContentTypeSettings.Edit.cshtml. *@ +
+
+ + + + @T["Enable along with Securable to ensure the user must have the content type version of the permission."] + +
+
+ + diff --git a/Views/_ViewImports.cshtml b/Views/_ViewImports.cshtml index a7977c43..8b01c2f9 100644 --- a/Views/_ViewImports.cshtml +++ b/Views/_ViewImports.cshtml @@ -1,6 +1,6 @@ -@inherits OrchardCore.DisplayManagement.Razor.RazorPage - -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *, OrchardCore.DisplayManagement -@addTagHelper *, OrchardCore.ResourceManagement +@inherits OrchardCore.DisplayManagement.Razor.RazorPage + +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, OrchardCore.DisplayManagement +@addTagHelper *, OrchardCore.ResourceManagement @addTagHelper *, OrchardCore.Contents \ No newline at end of file diff --git a/placement.json b/placement.json index ca114b92..7d42c481 100644 --- a/placement.json +++ b/placement.json @@ -1,8 +1,8 @@ -{ - "TitlePart": [ - { - "contentType": [ "ContainerWidget" ], - "place": "-" - } - ] -} +{ + "TitlePart": [ + { + "contentType": [ "ContainerWidget" ], + "place": "-" + } + ] +} From 7d7a966a2805e1d037ecb0be74f6285978dc5947 Mon Sep 17 00:00:00 2001 From: Oliver Friedrich Date: Sat, 23 Apr 2022 00:49:21 +0200 Subject: [PATCH 3/6] Add trailing empty line --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7f30a0ce..fd70fd24 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ artifacts/ wwwroot/ node_modules/ *.user -.pnpm-debug.log \ No newline at end of file +.pnpm-debug.log From 5504b9b31a0d62b8d7f8724b5e11aff9442b829a Mon Sep 17 00:00:00 2001 From: Oliver Friedrich Date: Sat, 23 Apr 2022 00:51:03 +0200 Subject: [PATCH 4/6] Add trailing empty line to View files --- Views/AdditionalStylingPart.Edit.cshtml | 2 +- Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml | 2 +- Views/_ViewImports.cshtml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Views/AdditionalStylingPart.Edit.cshtml b/Views/AdditionalStylingPart.Edit.cshtml index 919cb9fa..1e9d1a61 100644 --- a/Views/AdditionalStylingPart.Edit.cshtml +++ b/Views/AdditionalStylingPart.Edit.cshtml @@ -14,4 +14,4 @@
- \ No newline at end of file + diff --git a/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml b/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml index b0f73d92..e17dae2b 100644 --- a/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml +++ b/Views/Lombiq.HelpfulExtensions.Flows.FlowPart.cshtml @@ -41,4 +41,4 @@ @await DisplayAsync(widgetContent) } - \ No newline at end of file + diff --git a/Views/_ViewImports.cshtml b/Views/_ViewImports.cshtml index 8b01c2f9..8dc399d4 100644 --- a/Views/_ViewImports.cshtml +++ b/Views/_ViewImports.cshtml @@ -3,4 +3,4 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, OrchardCore.DisplayManagement @addTagHelper *, OrchardCore.ResourceManagement -@addTagHelper *, OrchardCore.Contents \ No newline at end of file +@addTagHelper *, OrchardCore.Contents From 85b365ee93c76352c938c3c337c27338b0bead48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sat, 23 Apr 2022 02:07:25 +0200 Subject: [PATCH 5/6] Add file trailing newline. --- Views/EmailTemplate.LayoutInjections.cshtml | 2 +- Views/EmailTemplate.Title.cshtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/EmailTemplate.LayoutInjections.cshtml b/Views/EmailTemplate.LayoutInjections.cshtml index 54c4561c..97cfdfff 100644 --- a/Views/EmailTemplate.LayoutInjections.cshtml +++ b/Views/EmailTemplate.LayoutInjections.cshtml @@ -1 +1 @@ -@* Override this shape to add generic injections email templates (e.g., header, footer, etc.). *@ \ No newline at end of file +@* Override this shape to add generic injections email templates (e.g., header, footer, etc.). *@ diff --git a/Views/EmailTemplate.Title.cshtml b/Views/EmailTemplate.Title.cshtml index 4850e54d..43e850bc 100644 --- a/Views/EmailTemplate.Title.cshtml +++ b/Views/EmailTemplate.Title.cshtml @@ -1 +1 @@ -@* Override this shape to add a title to the email templates. *@ \ No newline at end of file +@* Override this shape to add a title to the email templates. *@ From f035bc8c07d3fb9f0b14ec5df05fcdd02918a9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sat, 23 Apr 2022 17:10:32 +0200 Subject: [PATCH 6/6] Update gitignore file to that in the repo template. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fd70fd24..0d410d77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .vs/ +.idea/ +.vscode/ obj/ bin/ artifacts/